elanous 0.2.0 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1249) hide show
  1. package/.env.example +6 -0
  2. package/README.md +17 -15
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/06ef2eba-d1b99ac6d2ea663c.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/1751.50b96ea9c37b49b6.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +25 -0
  13. package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +5 -0
  21. package/apps/pwa/out/_next/static/chunks/4440-18bb160d95819f4e.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4853-35fdbe6bdbcdc0fc.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{2a79f15a-2013e4d421d6df88.js → 4daa2798-1363d725c79f3593.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5348-bbf838e119661eee.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5391-8793f88b03b30161.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{5927.8378481a12cc8044.js → 7527.943753b6ed829f09.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{6804.8ed6ab492bc5fc0a.js → 8617.465ca22a319c95b3.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/{320-d7013ec072863e5e.js → 8918-482b30ce2a8ebf4c.js} +4 -4
  44. package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{2598.814f64e3268634e7.js → 9238.2cd5f47f4bf7a2c3.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/9704-3221c102623c8d73.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f89e9a303515a033.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/board/page-4f403cd060442c95.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0ac5d053f0444d21.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/control/page-72bd384898d88955.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/morning/page-bccce0babceae30f.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-84cd419494e16b0f.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/f6f3850d-7f324c6566002113.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/webpack-2bf59c720fe9542c.js +1 -0
  92. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
  93. package/apps/pwa/out/_next/static/ktMqfrBbf8BMGac0jqLrb/_buildManifest.js +1 -0
  94. package/apps/pwa/out/approvals/index.html +1 -0
  95. package/apps/pwa/out/approvals/index.txt +32 -0
  96. package/apps/pwa/out/autopilot/index.html +1 -1
  97. package/apps/pwa/out/autopilot/index.txt +29 -26
  98. package/apps/pwa/out/board/index.html +1 -0
  99. package/apps/pwa/out/board/index.txt +32 -0
  100. package/apps/pwa/out/botlab/index.html +1 -1
  101. package/apps/pwa/out/botlab/index.txt +26 -23
  102. package/apps/pwa/out/bots/index.html +1 -1
  103. package/apps/pwa/out/bots/index.txt +26 -23
  104. package/apps/pwa/out/chat/index.html +1 -2
  105. package/apps/pwa/out/chat/index.txt +26 -23
  106. package/apps/pwa/out/control/index.html +1 -1
  107. package/apps/pwa/out/control/index.txt +29 -26
  108. package/apps/pwa/out/design-check/index.html +1 -1
  109. package/apps/pwa/out/design-check/index.txt +29 -26
  110. package/apps/pwa/out/editor/index.html +1 -0
  111. package/apps/pwa/out/editor/index.txt +33 -0
  112. package/apps/pwa/out/healthz +1 -1
  113. package/apps/pwa/out/icons/elanous-16.png +0 -0
  114. package/apps/pwa/out/icons/elanous-180.png +0 -0
  115. package/apps/pwa/out/icons/elanous-192.png +0 -0
  116. package/apps/pwa/out/icons/elanous-32.png +0 -0
  117. package/apps/pwa/out/icons/elanous-512.png +0 -0
  118. package/apps/pwa/out/index.html +1 -1
  119. package/apps/pwa/out/index.txt +26 -23
  120. package/apps/pwa/out/intake/index.html +1 -1
  121. package/apps/pwa/out/intake/index.txt +29 -26
  122. package/apps/pwa/out/live/index.html +1 -0
  123. package/apps/pwa/out/live/index.txt +32 -0
  124. package/apps/pwa/out/manifest.webmanifest +5 -2
  125. package/apps/pwa/out/market/index.html +1 -0
  126. package/apps/pwa/out/market/index.txt +29 -0
  127. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  128. package/apps/pwa/out/missions/placeholder/index.txt +26 -23
  129. package/apps/pwa/out/morning/index.html +1 -1
  130. package/apps/pwa/out/morning/index.txt +29 -26
  131. package/apps/pwa/out/observatory/index.html +1 -1
  132. package/apps/pwa/out/observatory/index.txt +29 -26
  133. package/apps/pwa/out/reflection/index.html +1 -1
  134. package/apps/pwa/out/reflection/index.txt +27 -24
  135. package/apps/pwa/out/scheduler/index.html +1 -1
  136. package/apps/pwa/out/scheduler/index.txt +29 -26
  137. package/apps/pwa/out/sessions/index.html +1 -1
  138. package/apps/pwa/out/sessions/index.txt +26 -23
  139. package/apps/pwa/out/settings/devices/index.html +1 -1
  140. package/apps/pwa/out/settings/devices/index.txt +29 -26
  141. package/apps/pwa/out/settings/index.html +1 -1
  142. package/apps/pwa/out/settings/index.txt +29 -26
  143. package/apps/pwa/out/setup/done/index.html +1 -1
  144. package/apps/pwa/out/setup/done/index.txt +29 -26
  145. package/apps/pwa/out/setup/index.html +1 -1
  146. package/apps/pwa/out/setup/index.txt +29 -26
  147. package/apps/pwa/out/share/index.html +1 -1
  148. package/apps/pwa/out/share/index.txt +29 -26
  149. package/apps/pwa/out/showroom/index.html +1 -1
  150. package/apps/pwa/out/showroom/index.txt +26 -23
  151. package/apps/pwa/out/sw-strategies.js +44 -0
  152. package/apps/pwa/out/sw.js +6 -44
  153. package/apps/pwa/out/tasks/index.html +1 -1
  154. package/apps/pwa/out/tasks/index.txt +29 -26
  155. package/apps/pwa/out/term/index.html +1 -1
  156. package/apps/pwa/out/term/index.txt +29 -26
  157. package/apps/pwa/out/trace/index.html +1 -0
  158. package/apps/pwa/out/trace/index.txt +32 -0
  159. package/apps/pwa/out/vault/index.html +1 -1
  160. package/apps/pwa/out/vault/index.txt +29 -26
  161. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  162. package/apps/pwa/out/workflows/chat-ui/index.txt +29 -26
  163. package/apps/pwa/out/workflows/index.html +1 -1
  164. package/apps/pwa/out/workflows/index.txt +29 -27
  165. package/apps/pwa/out/workspace/index.html +1 -1
  166. package/apps/pwa/out/workspace/index.txt +29 -26
  167. package/apps/pwa/out/worktrees/index.html +1 -1
  168. package/apps/pwa/out/worktrees/index.txt +29 -26
  169. package/catalog/resources.yaml +14 -17
  170. package/docs/design/systems/LICENSE +201 -0
  171. package/docs/design/systems/NOTICE.md +29 -0
  172. package/docs/design/systems/SOURCE.json +291 -0
  173. package/docs/design/systems/application/DESIGN.md +71 -0
  174. package/docs/design/systems/application/manifest.json +54 -0
  175. package/docs/design/systems/application/tokens.css +63 -0
  176. package/docs/design/systems/artistic/DESIGN.md +71 -0
  177. package/docs/design/systems/artistic/manifest.json +54 -0
  178. package/docs/design/systems/artistic/tokens.css +63 -0
  179. package/docs/design/systems/bento/DESIGN.md +71 -0
  180. package/docs/design/systems/bento/manifest.json +54 -0
  181. package/docs/design/systems/bento/tokens.css +63 -0
  182. package/docs/design/systems/bold/DESIGN.md +71 -0
  183. package/docs/design/systems/bold/manifest.json +54 -0
  184. package/docs/design/systems/bold/tokens.css +63 -0
  185. package/docs/design/systems/brutalism/DESIGN.md +71 -0
  186. package/docs/design/systems/brutalism/manifest.json +54 -0
  187. package/docs/design/systems/brutalism/tokens.css +63 -0
  188. package/docs/design/systems/cafe/DESIGN.md +71 -0
  189. package/docs/design/systems/cafe/manifest.json +54 -0
  190. package/docs/design/systems/cafe/tokens.css +63 -0
  191. package/docs/design/systems/claymorphism/DESIGN.md +71 -0
  192. package/docs/design/systems/claymorphism/manifest.json +54 -0
  193. package/docs/design/systems/claymorphism/tokens.css +63 -0
  194. package/docs/design/systems/clean/DESIGN.md +71 -0
  195. package/docs/design/systems/clean/manifest.json +54 -0
  196. package/docs/design/systems/clean/tokens.css +63 -0
  197. package/docs/design/systems/colorful/DESIGN.md +71 -0
  198. package/docs/design/systems/colorful/manifest.json +54 -0
  199. package/docs/design/systems/colorful/tokens.css +63 -0
  200. package/docs/design/systems/contemporary/DESIGN.md +71 -0
  201. package/docs/design/systems/contemporary/manifest.json +54 -0
  202. package/docs/design/systems/contemporary/tokens.css +63 -0
  203. package/docs/design/systems/corporate/DESIGN.md +71 -0
  204. package/docs/design/systems/corporate/manifest.json +54 -0
  205. package/docs/design/systems/corporate/tokens.css +63 -0
  206. package/docs/design/systems/cosmic/DESIGN.md +71 -0
  207. package/docs/design/systems/cosmic/manifest.json +54 -0
  208. package/docs/design/systems/cosmic/tokens.css +63 -0
  209. package/docs/design/systems/creative/DESIGN.md +71 -0
  210. package/docs/design/systems/creative/manifest.json +54 -0
  211. package/docs/design/systems/creative/tokens.css +63 -0
  212. package/docs/design/systems/dashboard/DESIGN.md +71 -0
  213. package/docs/design/systems/dashboard/manifest.json +54 -0
  214. package/docs/design/systems/dashboard/tokens.css +63 -0
  215. package/docs/design/systems/default/DESIGN.md +62 -0
  216. package/docs/design/systems/default/manifest.json +69 -0
  217. package/docs/design/systems/default/tokens.css +200 -0
  218. package/docs/design/systems/dithered/DESIGN.md +71 -0
  219. package/docs/design/systems/dithered/manifest.json +54 -0
  220. package/docs/design/systems/dithered/tokens.css +63 -0
  221. package/docs/design/systems/doodle/DESIGN.md +71 -0
  222. package/docs/design/systems/doodle/manifest.json +54 -0
  223. package/docs/design/systems/doodle/tokens.css +63 -0
  224. package/docs/design/systems/dramatic/DESIGN.md +71 -0
  225. package/docs/design/systems/dramatic/manifest.json +54 -0
  226. package/docs/design/systems/dramatic/tokens.css +63 -0
  227. package/docs/design/systems/editorial/DESIGN.md +71 -0
  228. package/docs/design/systems/editorial/manifest.json +54 -0
  229. package/docs/design/systems/editorial/tokens.css +63 -0
  230. package/docs/design/systems/elegant/DESIGN.md +71 -0
  231. package/docs/design/systems/elegant/manifest.json +54 -0
  232. package/docs/design/systems/elegant/tokens.css +63 -0
  233. package/docs/design/systems/energetic/DESIGN.md +72 -0
  234. package/docs/design/systems/energetic/manifest.json +54 -0
  235. package/docs/design/systems/energetic/tokens.css +63 -0
  236. package/docs/design/systems/enterprise/DESIGN.md +71 -0
  237. package/docs/design/systems/enterprise/manifest.json +54 -0
  238. package/docs/design/systems/enterprise/tokens.css +63 -0
  239. package/docs/design/systems/expressive/DESIGN.md +71 -0
  240. package/docs/design/systems/expressive/manifest.json +54 -0
  241. package/docs/design/systems/expressive/tokens.css +63 -0
  242. package/docs/design/systems/fantasy/DESIGN.md +71 -0
  243. package/docs/design/systems/fantasy/manifest.json +54 -0
  244. package/docs/design/systems/fantasy/tokens.css +63 -0
  245. package/docs/design/systems/flat/DESIGN.md +71 -0
  246. package/docs/design/systems/flat/manifest.json +54 -0
  247. package/docs/design/systems/flat/tokens.css +63 -0
  248. package/docs/design/systems/friendly/DESIGN.md +71 -0
  249. package/docs/design/systems/friendly/manifest.json +54 -0
  250. package/docs/design/systems/friendly/tokens.css +63 -0
  251. package/docs/design/systems/glassmorphism/DESIGN.md +71 -0
  252. package/docs/design/systems/glassmorphism/manifest.json +54 -0
  253. package/docs/design/systems/glassmorphism/tokens.css +63 -0
  254. package/docs/design/systems/gradient/DESIGN.md +71 -0
  255. package/docs/design/systems/gradient/manifest.json +54 -0
  256. package/docs/design/systems/gradient/tokens.css +63 -0
  257. package/docs/design/systems/luxury/DESIGN.md +71 -0
  258. package/docs/design/systems/luxury/manifest.json +54 -0
  259. package/docs/design/systems/luxury/tokens.css +63 -0
  260. package/docs/design/systems/minimal/DESIGN.md +71 -0
  261. package/docs/design/systems/minimal/manifest.json +54 -0
  262. package/docs/design/systems/minimal/tokens.css +63 -0
  263. package/docs/design/systems/modern/DESIGN.md +71 -0
  264. package/docs/design/systems/modern/manifest.json +54 -0
  265. package/docs/design/systems/modern/tokens.css +63 -0
  266. package/docs/design/systems/mono/DESIGN.md +71 -0
  267. package/docs/design/systems/mono/manifest.json +54 -0
  268. package/docs/design/systems/mono/tokens.css +63 -0
  269. package/docs/design/systems/neobrutalism/DESIGN.md +71 -0
  270. package/docs/design/systems/neobrutalism/manifest.json +54 -0
  271. package/docs/design/systems/neobrutalism/tokens.css +63 -0
  272. package/docs/design/systems/neon/DESIGN.md +71 -0
  273. package/docs/design/systems/neon/manifest.json +54 -0
  274. package/docs/design/systems/neon/tokens.css +63 -0
  275. package/docs/design/systems/neumorphism/DESIGN.md +71 -0
  276. package/docs/design/systems/neumorphism/manifest.json +54 -0
  277. package/docs/design/systems/neumorphism/tokens.css +63 -0
  278. package/docs/design/systems/paper/DESIGN.md +71 -0
  279. package/docs/design/systems/paper/manifest.json +54 -0
  280. package/docs/design/systems/paper/tokens.css +63 -0
  281. package/docs/design/systems/perspective/DESIGN.md +71 -0
  282. package/docs/design/systems/perspective/manifest.json +54 -0
  283. package/docs/design/systems/perspective/tokens.css +63 -0
  284. package/docs/design/systems/premium/DESIGN.md +71 -0
  285. package/docs/design/systems/premium/manifest.json +54 -0
  286. package/docs/design/systems/premium/tokens.css +63 -0
  287. package/docs/design/systems/professional/DESIGN.md +71 -0
  288. package/docs/design/systems/professional/manifest.json +54 -0
  289. package/docs/design/systems/professional/tokens.css +63 -0
  290. package/docs/design/systems/publication/DESIGN.md +71 -0
  291. package/docs/design/systems/publication/manifest.json +54 -0
  292. package/docs/design/systems/publication/tokens.css +63 -0
  293. package/docs/design/systems/refined/DESIGN.md +71 -0
  294. package/docs/design/systems/refined/manifest.json +54 -0
  295. package/docs/design/systems/refined/tokens.css +63 -0
  296. package/docs/design/systems/retro/DESIGN.md +71 -0
  297. package/docs/design/systems/retro/manifest.json +54 -0
  298. package/docs/design/systems/retro/tokens.css +63 -0
  299. package/docs/design/systems/shadcn/DESIGN.md +71 -0
  300. package/docs/design/systems/shadcn/manifest.json +54 -0
  301. package/docs/design/systems/shadcn/tokens.css +255 -0
  302. package/docs/design/systems/simple/DESIGN.md +71 -0
  303. package/docs/design/systems/simple/manifest.json +54 -0
  304. package/docs/design/systems/simple/tokens.css +63 -0
  305. package/docs/design/systems/skeumorphism/DESIGN.md +71 -0
  306. package/docs/design/systems/skeumorphism/manifest.json +54 -0
  307. package/docs/design/systems/skeumorphism/tokens.css +63 -0
  308. package/docs/design/systems/sleek/DESIGN.md +71 -0
  309. package/docs/design/systems/sleek/manifest.json +54 -0
  310. package/docs/design/systems/sleek/tokens.css +63 -0
  311. package/docs/design/systems/spacious/DESIGN.md +71 -0
  312. package/docs/design/systems/spacious/manifest.json +54 -0
  313. package/docs/design/systems/spacious/tokens.css +63 -0
  314. package/docs/design/systems/storytelling/DESIGN.md +71 -0
  315. package/docs/design/systems/storytelling/manifest.json +54 -0
  316. package/docs/design/systems/storytelling/tokens.css +63 -0
  317. package/docs/design/systems/tom-modern/DESIGN.md +361 -0
  318. package/docs/design/systems/tom-modern/manifest.json +16 -0
  319. package/docs/design/systems/tom-modern/tokens.css +99 -0
  320. package/docs/design/systems/vibrant/DESIGN.md +71 -0
  321. package/docs/design/systems/vibrant/manifest.json +54 -0
  322. package/docs/design/systems/vibrant/tokens.css +63 -0
  323. package/docs/design/systems/vintage/DESIGN.md +71 -0
  324. package/docs/design/systems/vintage/manifest.json +54 -0
  325. package/docs/design/systems/vintage/tokens.css +63 -0
  326. package/docs/design/systems/warm-editorial/DESIGN.md +65 -0
  327. package/docs/design/systems/warm-editorial/manifest.json +54 -0
  328. package/docs/design/systems/warm-editorial/tokens.css +63 -0
  329. package/docs/goal-context/01-goal-authoring-checks.md +18 -0
  330. package/docs/goal-context/steward-directives-2026-09-28.md +42 -0
  331. package/graphs/catalog/catalog.yaml +615 -0
  332. package/graphs/default-loop.yaml +1 -1
  333. package/graphs/document-loop.yaml +1 -1
  334. package/graphs/examples/stale-draft-digest/digest.ts +26 -0
  335. package/graphs/examples/stale-draft-digest/recipes.yaml +8 -0
  336. package/graphs/examples/stale-draft-digest/stale-draft-digest.yaml +22 -0
  337. package/graphs/heal/heal-loop.yaml +155 -0
  338. package/graphs/heal/recipes.yaml +19 -0
  339. package/graphs/implement-loop.yaml +2 -2
  340. package/graphs/intake/intake-daily.yaml +55 -0
  341. package/graphs/intake/recipes.yaml +16 -0
  342. package/graphs/landing/landing-heal.yaml +22 -0
  343. package/graphs/landing/recipes.yaml +8 -0
  344. package/graphs/launch/launch-head.yaml +66 -0
  345. package/graphs/launch/recipes.yaml +8 -0
  346. package/graphs/operate-loop.yaml +1 -1
  347. package/graphs/ops/docs-publish.yaml +31 -0
  348. package/graphs/ops/recipes.yaml +8 -0
  349. package/graphs/release/recipes.yaml +45 -0
  350. package/graphs/release/release-loop.yaml +45 -0
  351. package/graphs/research-loop.yaml +1 -1
  352. package/graphs/steward/recipes.yaml +12 -0
  353. package/graphs/steward/steward.yaml +45 -0
  354. package/graphs/video/README.md +1 -0
  355. package/graphs/video/broll-line.yaml +54 -0
  356. package/graphs/video/explainer-line.yaml +58 -0
  357. package/package.json +9 -5
  358. package/plugins/job-coach/.agents/plugins/marketplace.json +12 -0
  359. package/plugins/job-coach/.codex-plugin/plugin.json +23 -0
  360. package/plugins/job-coach/.mcp.json +9 -0
  361. package/plugins/job-coach/README.md +32 -0
  362. package/plugins/job-coach/connectors/ncs/server.ts +90 -0
  363. package/plugins/job-coach/examples/interview-sample.md +11 -0
  364. package/plugins/job-coach/graphs/course-links.ts +46 -0
  365. package/plugins/job-coach/graphs/gap-judge.ts +44 -0
  366. package/plugins/job-coach/graphs/recipes.yaml +45 -0
  367. package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
  368. package/plugins/job-coach/graphs/report.yaml +20 -0
  369. package/plugins/job-coach/graphs/run-step.ts +302 -0
  370. package/plugins/job-coach/plugin.json +26 -0
  371. package/plugins/job-coach/plugin.ts +6 -0
  372. package/plugins/job-coach/skills/career-report/SKILL.md +6 -0
  373. package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
  374. package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
  375. package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
  376. package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
  377. package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
  378. package/plugins/job-coach/skills/interview-to-profile/SKILL.md +23 -0
  379. package/plugins/job-coach/skills/run-report/SKILL.md +15 -0
  380. package/scripts/backup/elanous-backup.sh +143 -14
  381. package/scripts/backup/elanous-restore.sh +122 -0
  382. package/scripts/build-docs-site.ts +4 -3
  383. package/scripts/ci-daemon-port-gate.ts +164 -0
  384. package/scripts/ci-typecheck-changed.ts +15 -1
  385. package/scripts/cli-json-exit-scan.ts +126 -0
  386. package/scripts/codex-quota-alert-format.ts +52 -0
  387. package/scripts/codex-quota-alert.ts +63 -13
  388. package/scripts/continuation-dogfood.ts +2 -1
  389. package/scripts/coord-channel-watch.sh +28 -9
  390. package/scripts/coord-post.sh +49 -12
  391. package/scripts/coord-tracks.json +6 -5
  392. package/scripts/cron-run.ts +37 -16
  393. package/scripts/daemon-port-baseline.txt +9 -0
  394. package/scripts/design/extract-corpus-bench.ts +469 -0
  395. package/scripts/design/extract-corpus-score.ts +510 -0
  396. package/scripts/design/vendor-open-design-systems.ts +100 -0
  397. package/scripts/docs-lint.ts +1 -1
  398. package/scripts/eval-bot-vs-grok.ts +79 -0
  399. package/scripts/eval-grok-live-baseline.ts +36 -0
  400. package/scripts/fixtures/front-route-corpus.sample.json +17 -0
  401. package/scripts/fleet/node-setup.sh +80 -7
  402. package/scripts/heal/run-role.ts +328 -0
  403. package/scripts/install-role-watch.sh +95 -0
  404. package/scripts/install.ps1 +11 -1
  405. package/scripts/install.sh +81 -9
  406. package/scripts/isolation-hardcode-baseline.txt +1 -1
  407. package/scripts/landing-heal/must-fix-match.ts +63 -0
  408. package/scripts/landing-heal/run-mission.ts +195 -0
  409. package/scripts/lib/front-route-measurement.ts +131 -0
  410. package/scripts/lib/pwa-scenarios/acp-replay.ts +79 -0
  411. package/scripts/lib/pwa-scenarios/cdp-driver.ts +67 -0
  412. package/scripts/lib/pwa-scenarios/runner.ts +200 -0
  413. package/scripts/lib/pwa-scenarios/scenarios.ts +202 -0
  414. package/scripts/lib/tui-sim-keys.ts +42 -0
  415. package/scripts/market/db-check.ts +212 -0
  416. package/scripts/market-posture-cycle.ts +63 -4
  417. package/scripts/measure-front-route-classifier.ts +56 -0
  418. package/scripts/media/ocr-leak-scan.sh +21 -0
  419. package/scripts/nightly-docops.ts +1 -1
  420. package/scripts/ops/channel-bot-token-migrate.ts +146 -0
  421. package/scripts/ops-health-check.ts +15 -3
  422. package/scripts/pty-scenarios/run.ts +272 -0
  423. package/scripts/public-export.ts +2 -3
  424. package/scripts/public-import-pr.ts +80 -6
  425. package/scripts/public-leak-baseline.txt +0 -7
  426. package/scripts/publish-internal-dist.ts +118 -0
  427. package/scripts/pwa-scenarios.ts +106 -0
  428. package/scripts/rebrand-elanous.ts +1 -1
  429. package/scripts/red-sweep-diff.ts +1 -1
  430. package/scripts/release-loop/auto-approve-node.ts +72 -0
  431. package/scripts/release-loop/cutoff.ts +139 -0
  432. package/scripts/release-loop/docs-land-node.ts +65 -0
  433. package/scripts/release-loop/docs-node.ts +172 -0
  434. package/scripts/release-loop/gate-diff.ts +29 -0
  435. package/scripts/release-loop/gate-node.ts +641 -0
  436. package/scripts/release-loop/node-verdict.ts +85 -0
  437. package/scripts/release-loop/prepare-node.ts +31 -0
  438. package/scripts/release-loop/publish-node.ts +61 -0
  439. package/scripts/release-loop/pwa-node-verdict.ts +45 -0
  440. package/scripts/release-loop/pwa-scenarios-node.ts +140 -0
  441. package/scripts/release-loop/release-docs.ts +109 -0
  442. package/scripts/release-loop/test-durations.ts +63 -0
  443. package/scripts/release-loop/tui-node-checks.ts +29 -0
  444. package/scripts/release-loop/tui-sim-node.ts +113 -0
  445. package/scripts/release-loop/upgrade-node.ts +26 -0
  446. package/scripts/release-loop/verify-node.ts +21 -0
  447. package/scripts/release-loop/version-node.ts +147 -0
  448. package/scripts/release-upgrade-check.sh +53 -0
  449. package/scripts/se-mission-prepare.ts +9 -15
  450. package/scripts/test-audit-coverage-per-file.sh +16 -0
  451. package/scripts/test-audit-meaningless.ts +88 -0
  452. package/scripts/test-audit-overlap-and-weak.ts +73 -0
  453. package/scripts/test-deterministic.ts +29 -6
  454. package/scripts/tui-sim.ts +59 -14
  455. package/scripts/uninstall.sh +10 -3
  456. package/scripts/ux/bump-installed.sh +20 -0
  457. package/scripts/ux/lib/cdp.ts +72 -0
  458. package/scripts/ux/record-page.ts +50 -0
  459. package/scripts/video-broll-line.ts +89 -0
  460. package/scripts/waitlist-daily-digest.ts +108 -0
  461. package/scripts/window-landings.sh +1 -1
  462. package/skills/explainer-video/SKILL.md +99 -0
  463. package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
  464. package/skills/explainer-video/engine/build.mjs +431 -0
  465. package/skills/explainer-video/engine/vo.ts +37 -0
  466. package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
  467. package/skills/motion-broll/LICENSE +25 -0
  468. package/skills/motion-broll/SKILL.md +150 -0
  469. package/skills/motion-broll/SOURCE.md +6 -0
  470. package/skills/motion-broll/engine/base.css +19 -0
  471. package/skills/motion-broll/engine/beats.js +11 -0
  472. package/skills/motion-broll/engine/build.py +21 -0
  473. package/skills/motion-broll/engine/fonts/Geist-Variable.woff2 +0 -0
  474. package/skills/motion-broll/engine/fonts/GeistMono-Medium.woff2 +0 -0
  475. package/skills/motion-broll/engine/fonts/OFL-Geist.txt +92 -0
  476. package/skills/motion-broll/engine/motion.js +146 -0
  477. package/skills/motion-broll/engine/render.js +22 -0
  478. package/skills/motion-broll/examples/opus-aoe2/01-opus-drop.html +41 -0
  479. package/skills/motion-broll/examples/opus-aoe2/02-pip-builds.html +110 -0
  480. package/skills/motion-broll/examples/opus-aoe2/03-effort-slider.html +38 -0
  481. package/skills/motion-broll/examples/opus-aoe2/04-master-prompt.html +56 -0
  482. package/skills/motion-broll/examples/opus-aoe2/05-low-vs-max.html +49 -0
  483. package/skills/motion-broll/examples/opus-aoe2/06-chapter.html +18 -0
  484. package/skills/motion-broll/examples/opus-aoe2/plan.json +14 -0
  485. package/skills/motion-broll/reference/engine-api.md +106 -0
  486. package/skills/motion-broll/scripts/composite.py +14 -0
  487. package/skills/motion-broll/scripts/inspect_video.py +34 -0
  488. package/skills/motion-broll/scripts/make_pages.py +34 -0
  489. package/skills/motion-broll/scripts/setup.sh +16 -0
  490. package/skills/motion-broll/scripts/words.py +13 -0
  491. package/skills/motion-broll/templates/compare.html +135 -0
  492. package/skills/motion-broll/templates/viewer.html +97 -0
  493. package/skills/omni-digest/SKILL.md +260 -0
  494. package/skills/omni-digest/digest.md +197 -0
  495. package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +32 -0
  496. package/skills/omni-digest/package-lock.json +558 -0
  497. package/skills/omni-digest/package.json +13 -0
  498. package/skills/omni-digest/scripts/main.ts +404 -0
  499. package/skills/omni-digest/src/delegate.ts +34 -0
  500. package/skills/omni-digest/src/detect.ts +46 -0
  501. package/skills/omni-digest/src/diagram.ts +72 -0
  502. package/skills/omni-digest/src/domain-staging.ts +50 -0
  503. package/skills/omni-digest/src/env.ts +38 -0
  504. package/skills/omni-digest/src/fetch.ts +237 -0
  505. package/skills/omni-digest/src/media.ts +180 -0
  506. package/skills/omni-digest/src/obsidian.ts +69 -0
  507. package/skills/omni-digest/src/ocr.ts +149 -0
  508. package/skills/omni-digest/src/router.ts +60 -0
  509. package/skills/omni-digest/src/summarize.ts +87 -0
  510. package/skills/omni-digest/src/types.ts +124 -0
  511. package/skills/omni-digest/src/util.ts +34 -0
  512. package/skills/omni-digest/src/visual.ts +129 -0
  513. package/skills/omni-digest/src/x-api.ts +151 -0
  514. package/skills/omni-digest/tsconfig.json +13 -0
  515. package/src/a2a/agent-card.ts +29 -0
  516. package/src/a2a/server.ts +173 -0
  517. package/src/acp/auto-persist.ts +3 -6
  518. package/src/acp/backend-registry.ts +6 -10
  519. package/src/acp/channel-browser-shell.ts +1 -535
  520. package/src/acp/client.ts +106 -36
  521. package/src/acp/fs-roots.ts +20 -4
  522. package/src/acp/obsidian-tags.ts +6 -3
  523. package/src/acp/server.ts +189 -32
  524. package/src/agent/nested-agent-env.ts +1 -0
  525. package/src/agent/pty-binding.ts +1 -1
  526. package/src/agent/spawn-embodied-agent-in-vw.ts +5 -336
  527. package/src/agent/subagent-callable.ts +39 -1
  528. package/src/agent-mission/browser-login.ts +241 -0
  529. package/src/agent-mission/capabilities-cli.ts +33 -0
  530. package/src/agent-mission/capability-matrix.ts +31 -0
  531. package/src/agent-mission/capability-readers.ts +79 -0
  532. package/src/agent-mission/capability-types.ts +15 -0
  533. package/src/agent-mission/claude-headless.ts +131 -0
  534. package/src/agent-mission/claude-subscription.ts +57 -0
  535. package/src/agent-mission/doc-guided-install.ts +185 -0
  536. package/src/agent-mission/driver.ts +900 -69
  537. package/src/agent-mission/env-profile.ts +68 -0
  538. package/src/agent-mission/handoff.ts +128 -0
  539. package/src/agent-mission/in-agent-plugin.ts +311 -0
  540. package/src/agent-mission/mission-cli.ts +25 -1
  541. package/src/agent-mission/parse.ts +9 -4
  542. package/src/agent-mission/pr-review-watch.ts +62 -13
  543. package/src/agent-mission/resource-ladder.ts +235 -0
  544. package/src/agent-mission/tool-install.ts +249 -0
  545. package/src/agent-mission/tool-ladder.ts +37 -0
  546. package/src/agent-room/room-builder.ts +6 -600
  547. package/src/agent-substrate/review-intent.ts +43 -1
  548. package/src/auth/acp-token.ts +16 -0
  549. package/src/auth/github-app-token.ts +112 -0
  550. package/src/auth/scope.ts +8 -4
  551. package/src/auth/temp-tokens.ts +131 -0
  552. package/src/auto-research/obsidian-bridge.ts +2 -0
  553. package/src/autopilot/build/integrity-gate.ts +27 -4
  554. package/src/autopilot/build/isolated-instance.ts +2 -1
  555. package/src/autopilot/build/llm-conflict-merge.ts +27 -0
  556. package/src/autopilot/gh-rest-fallback.ts +177 -0
  557. package/src/autopilot/llm-control-brain.ts +54 -12
  558. package/src/autopilot/mission-engine.ts +15 -1
  559. package/src/autopilot/mission-notify.ts +4 -2
  560. package/src/autopilot/mission-origin.ts +5 -5
  561. package/src/autopilot/mission-phase-track.ts +52 -0
  562. package/src/autopilot/mission-tool.ts +1 -1
  563. package/src/autopilot/pr-manager.ts +51 -4
  564. package/src/autopilot/pty-control-loop.ts +94 -2
  565. package/src/autopilot/track-agent.ts +138 -0
  566. package/src/autopilot/track-courier.ts +153 -0
  567. package/src/boot/acp-server.ts +18 -18
  568. package/src/boot/check-same-origin.ts +19 -5
  569. package/src/boot/daemon-multi-llm-runtime.ts +14 -8
  570. package/src/boot/daemon-prompt-turn.ts +2 -0
  571. package/src/boot/daemon-runtime.ts +36 -14
  572. package/src/boot/daemon-tools/index.ts +16 -0
  573. package/src/boot/daemon-tools/self-implement.ts +4 -0
  574. package/src/boot/daemon-tools/types.ts +3 -0
  575. package/src/boot/daemon-tools/web-search.ts +2 -1
  576. package/src/boot/ws-bridge.ts +11 -0
  577. package/src/browser-cdp/chrome-flags.ts +7 -0
  578. package/src/browser-cdp/client.ts +2 -0
  579. package/src/browser-pane/mount.ts +1 -246
  580. package/src/budget/codex-reset-credit-state.ts +15 -0
  581. package/src/budget/fetchers/codex.ts +2 -1
  582. package/src/budget/run-usage-rollup.ts +11 -4
  583. package/src/budget/unified-usage.ts +2 -1
  584. package/src/capture/tui-self-report.ts +10 -0
  585. package/src/channel-bot-token.ts +126 -0
  586. package/src/chat/index.ts +57 -52
  587. package/src/chat/input-text-key.ts +2 -2
  588. package/src/chat/log-scroll-key.ts +12 -0
  589. package/src/chat/mode/manual.ts +2 -4
  590. package/src/chat/pickers/state.ts +10 -27
  591. package/src/chat/search/modal.ts +14 -2
  592. package/src/chat/submit-trace.ts +30 -0
  593. package/src/chat/tool-render/types.ts +1 -1
  594. package/src/chat/turn-typeahead.ts +2 -1
  595. package/src/chat/wrap.ts +6 -8
  596. package/src/cli/__fixtures__/agent-help-before/agent.txt +18 -0
  597. package/src/cli/__fixtures__/agent-help-before/registry.txt +13 -0
  598. package/src/cli/__fixtures__/agent-help-before/root.txt +100 -0
  599. package/src/cli/__fixtures__/agent-help-before/tier.txt +15 -0
  600. package/src/cli/a2a-cli.ts +52 -0
  601. package/src/cli/agent-cli.ts +726 -0
  602. package/src/cli/autopilot-cli.ts +739 -0
  603. package/src/cli/bg-launch.ts +20 -4
  604. package/src/cli/claude-plugin-setup.ts +199 -0
  605. package/src/cli/config-test-sync.ts +94 -10
  606. package/src/cli/connector-cli.ts +204 -0
  607. package/src/cli/control-cli.ts +220 -0
  608. package/src/cli/control-join-cli.ts +142 -0
  609. package/src/cli/decisions-cli.ts +94 -0
  610. package/src/cli/docs-cli.ts +38 -0
  611. package/src/cli/doctor-cli.ts +262 -77
  612. package/src/cli/doctor-distro.ts +17 -1
  613. package/src/cli/doctor-fix.ts +145 -17
  614. package/src/cli/doctor-llm-assist.ts +71 -0
  615. package/src/cli/doctor-private-files.ts +66 -0
  616. package/src/cli/doctor-readiness.ts +23 -28
  617. package/src/cli/fleet-cli.ts +63 -0
  618. package/src/cli/g6a-registration-probe.ts +16 -0
  619. package/src/cli/git-install-plan.ts +51 -0
  620. package/src/cli/hooks-cli.ts +44 -0
  621. package/src/cli/ingest-token-cli.ts +21 -0
  622. package/src/cli/intake-cli.ts +416 -0
  623. package/src/cli/labels-cli.ts +74 -0
  624. package/src/cli/leader-cli.ts +10 -159
  625. package/src/cli/live-cli.ts +48 -0
  626. package/src/cli/llm-detect-cli.ts +130 -0
  627. package/src/cli/logs-cli.ts +231 -7
  628. package/src/cli/machine-cli.ts +38 -0
  629. package/src/cli/mcp-call.ts +52 -13
  630. package/src/cli/mcp-gateway-cli.ts +94 -0
  631. package/src/cli/mcp-login.ts +116 -8
  632. package/src/cli/mcp-reload.ts +20 -3
  633. package/src/cli/nexus-dist.ts +18 -13
  634. package/src/cli/nexus-ios-bind.ts +20 -7
  635. package/src/cli/ops-cli.ts +225 -0
  636. package/src/cli/plugin-cli.ts +201 -0
  637. package/src/cli/pod-cli.ts +83 -0
  638. package/src/cli/port-lease-local.ts +124 -0
  639. package/src/cli/pr-cli.ts +12 -1
  640. package/src/cli/pr-lineage.ts +2 -1
  641. package/src/cli/provider-cli.ts +720 -0
  642. package/src/cli/pty-record-cli.ts +63 -0
  643. package/src/cli/pty-takeover-cli.ts +57 -40
  644. package/src/cli/pty-web-address.ts +52 -0
  645. package/src/cli/publish-cli.ts +44 -0
  646. package/src/cli/pwa-build.ts +18 -12
  647. package/src/cli/pwa-dev.ts +13 -8
  648. package/src/cli/pwa-registry.ts +6 -0
  649. package/src/cli/pwa-share.ts +65 -22
  650. package/src/cli/pwa-show.ts +20 -4
  651. package/src/cli/pwa-start.ts +102 -27
  652. package/src/cli/pwa-test.ts +201 -96
  653. package/src/cli/release-cli.ts +177 -7
  654. package/src/cli/remotes-cli.ts +20 -1
  655. package/src/cli/remotes.ts +2 -1
  656. package/src/cli/repo-cli.ts +315 -36
  657. package/src/cli/research-cli.ts +66 -0
  658. package/src/cli/role-cli.ts +320 -0
  659. package/src/cli/schedule-cli.ts +246 -0
  660. package/src/cli/self-update.ts +103 -12
  661. package/src/cli/setup-cli.ts +64 -2
  662. package/src/cli/shadow-cli.ts +188 -0
  663. package/src/cli/share-auto-mount.ts +12 -8
  664. package/src/cli/start-cli.ts +240 -0
  665. package/src/cli/stdout-flush.ts +8 -0
  666. package/src/cli/storage-cli.ts +334 -0
  667. package/src/cli/tailscale-serve.ts +34 -19
  668. package/src/cli/tasks-cli.ts +176 -0
  669. package/src/cli/test-state-dir-flag.ts +1 -1
  670. package/src/cli/usage-cli.ts +15 -4
  671. package/src/cli/where-cli.ts +22 -55
  672. package/src/code-edit/diff-render.ts +6 -32
  673. package/src/code-edit/source-delta-browser.ts +0 -1
  674. package/src/codex/models.ts +1 -1
  675. package/src/config.ts +8 -13
  676. package/src/connectors/event-ledger.ts +41 -0
  677. package/src/connectors/linear-projector.ts +88 -0
  678. package/src/connectors/linear.ts +143 -0
  679. package/src/connectors/types.ts +19 -0
  680. package/src/control-plane/lease-holder.ts +41 -0
  681. package/src/control-plane/ledger.ts +146 -0
  682. package/src/control-plane/light-member.ts +164 -0
  683. package/src/control-plane/member-tokens.ts +88 -0
  684. package/src/control-plane/member.ts +159 -0
  685. package/src/control-plane/port-lease-heartbeat.ts +101 -0
  686. package/src/control-plane/ports.ts +40 -0
  687. package/src/control-plane/primary.ts +169 -0
  688. package/src/control-plane/server.ts +216 -0
  689. package/src/dashboard/acp-boot.ts +14 -38
  690. package/src/dashboard/assistant-render-state-runtime.ts +2 -2
  691. package/src/dashboard/assistant-sample-runtime.ts +2 -2
  692. package/src/dashboard/auto-compact-runtime.ts +11 -16
  693. package/src/dashboard/browser-help-runtime.ts +2 -3
  694. package/src/dashboard/chat/acp-chat.ts +2 -5
  695. package/src/dashboard/code-edit-post-turn-runtime.ts +1 -2
  696. package/src/dashboard/compact-surface-assembly.ts +0 -14
  697. package/src/dashboard/compact-surface-dispatch.ts +1 -38
  698. package/src/dashboard/compact-surface-inventory.ts +0 -8
  699. package/src/dashboard/compact-surface-runtime.ts +0 -14
  700. package/src/dashboard/context-menu-provider-boot.ts +0 -9
  701. package/src/dashboard/context-window-runtime-boot.ts +0 -37
  702. package/src/dashboard/index.ts +331 -2562
  703. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +0 -6
  704. package/src/dashboard/input/chat-main-slash-command.ts +2 -2
  705. package/src/dashboard/input/dashboard-priority-key-route.ts +1 -43
  706. package/src/dashboard/input/mouse-modal-surface-runtime.ts +1 -5
  707. package/src/dashboard/input/mouse-wiring.ts +2 -3
  708. package/src/dashboard/input/no-provider-reply.ts +13 -0
  709. package/src/dashboard/layout-display-runtime-registration.ts +0 -10
  710. package/src/dashboard/modals/pane-multi.ts +1 -1
  711. package/src/dashboard/replay-preview.ts +1 -1
  712. package/src/dashboard/runtime/approvers.ts +0 -1
  713. package/src/dashboard/runtime/state-snapshot.ts +0 -90
  714. package/src/dashboard/scenario-runtime-boot.ts +16 -16
  715. package/src/dashboard/slash-runtime/dashboard-handlers.ts +134 -1456
  716. package/src/dashboard/slash-runtime/design-pick.ts +96 -0
  717. package/src/dashboard/slash-runtime/help-from-registry.ts +77 -0
  718. package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
  719. package/src/dashboard/stale-install-check.ts +77 -0
  720. package/src/dashboard/surface-registry-wiring.ts +2 -15
  721. package/src/dashboard/turn-message-runtime.ts +0 -4
  722. package/src/dashboard/turn-preamble.ts +1 -1
  723. package/src/dashboard/turn-stream-formatter.ts +2 -2
  724. package/src/dashboard/turn-stream-runtime.ts +2 -2
  725. package/src/dashboard-pane-context-menu.ts +0 -14
  726. package/src/debug/log.ts +7 -0
  727. package/src/decide/boundary-shadow.ts +131 -0
  728. package/src/decisions/decision-ledger.ts +404 -0
  729. package/src/design/apply-direction.ts +209 -0
  730. package/src/design/css-root-tokens.ts +89 -0
  731. package/src/design/custom-system-create.ts +230 -0
  732. package/src/design/design-directions.ts +61 -41
  733. package/src/design/design-gate.ts +241 -0
  734. package/src/design/design-library.ts +57 -0
  735. package/src/design/design-previews.ts +138 -0
  736. package/src/design/design-systems.ts +61 -0
  737. package/src/design/lint-artifact-run.ts +46 -6
  738. package/src/design/lint-artifact.ts +5 -1
  739. package/src/design/open-design-client.ts +247 -0
  740. package/src/design/system-from-extract.ts +767 -0
  741. package/src/design/system-schema.ts +123 -0
  742. package/src/discord-slash-wire.ts +2 -1
  743. package/src/discord-test-runner.ts +2 -1
  744. package/src/discord-voice-wire.ts +2 -1
  745. package/src/discord.ts +1 -1
  746. package/src/dispatch/opportunistic-launcher.ts +24 -1
  747. package/src/domains/ops-status.ts +35 -2
  748. package/src/domains/outbound-alert.ts +24 -6
  749. package/src/domains/repeated-failure.ts +89 -0
  750. package/src/domains/schedule-manage-tool.ts +28 -8
  751. package/src/domains/schedule-migrate.ts +22 -4
  752. package/src/domains/schedule-registry.ts +88 -33
  753. package/src/domains/schedule-runs.ts +132 -0
  754. package/src/domains/schedule-state.ts +152 -0
  755. package/src/domains/telegram-kind-route.ts +21 -0
  756. package/src/element-registry/address.ts +1 -3
  757. package/src/execution-loop/dispatch-task.ts +139 -0
  758. package/src/execution-loop/gate-decision-summary.ts +29 -0
  759. package/src/execution-loop/gates.ts +197 -0
  760. package/src/git-fs/gh-cli.ts +47 -17
  761. package/src/git-fs/retry.ts +1 -0
  762. package/src/git-fs/runner.ts +1 -0
  763. package/src/git-fs/worktree.ts +6 -5
  764. package/src/github/pr-labels.ts +68 -0
  765. package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
  766. package/src/graph-kinds/plugin-nodes.ts +68 -0
  767. package/src/graph-kinds/registry.ts +118 -0
  768. package/src/graph-runner/graph-cli.ts +103 -0
  769. package/src/graph-runner/graph-notify.ts +220 -0
  770. package/src/graph-runner/graph-tick.ts +119 -0
  771. package/src/graph-runner/release-manifest.ts +93 -0
  772. package/src/graph-runner/runner.ts +487 -0
  773. package/src/grok/credential.ts +158 -5
  774. package/src/grounding/ensure-grounding.ts +131 -0
  775. package/src/grounding/mirror-export.ts +89 -0
  776. package/src/grounding/mirror-manifest.ts +252 -0
  777. package/src/grounding/sources-cli.ts +209 -0
  778. package/src/grounding/sources.ts +333 -0
  779. package/src/grounding/token.ts +121 -0
  780. package/src/harness/harness-cli-command.ts +415 -23
  781. package/src/harness/harness-pod-dispatch.ts +116 -0
  782. package/src/harness/harness-stop.ts +260 -0
  783. package/src/harness/harness-substrate-default.ts +40 -0
  784. package/src/harness/harness-worktrees.ts +1 -1
  785. package/src/harness/harness-write-boundary.ts +2 -0
  786. package/src/harness/llm-decompose.ts +2 -1
  787. package/src/harness/self-send-decide.ts +233 -0
  788. package/src/harness/self-send-target.ts +22 -1
  789. package/src/harness/shell-relay.ts +5 -0
  790. package/src/harness/worktree-branch-prefix.ts +4 -3
  791. package/src/hooks/providers.ts +77 -0
  792. package/src/hooks/queue.ts +73 -0
  793. package/src/hooks/receiver.ts +133 -0
  794. package/src/index.ts +712 -3409
  795. package/src/input-core/dispatcher.ts +1 -1
  796. package/src/input-core/drag-esc-interceptor.ts +1 -1
  797. package/src/instance/current.ts +5 -5
  798. package/src/instance/leader.ts +10 -326
  799. package/src/instance/nexus-run-refusal.ts +27 -92
  800. package/src/instance/project-local-exclude.ts +27 -0
  801. package/src/instance/resolve.ts +46 -175
  802. package/src/intake-plane/capture.ts +14 -1
  803. package/src/intake-plane/check.ts +34 -14
  804. package/src/intake-plane/collect-github.ts +258 -0
  805. package/src/intake-plane/collect-pod.ts +100 -0
  806. package/src/intake-plane/collect-telegram-saved.ts +91 -0
  807. package/src/intake-plane/digest.ts +111 -0
  808. package/src/intake-plane/enrich-plugins.ts +1 -1
  809. package/src/intake-plane/front-classifier.ts +56 -0
  810. package/src/intake-plane/front-route-classifier.ts +80 -0
  811. package/src/intake-plane/front-route-rules.ts +152 -0
  812. package/src/intake-plane/grounding-docs.ts +185 -0
  813. package/src/intake-plane/harness-target.ts +20 -0
  814. package/src/intake-plane/intake-to-tasks.ts +303 -0
  815. package/src/intake-plane/items.ts +247 -0
  816. package/src/intake-plane/link-ledger.ts +27 -0
  817. package/src/intake-plane/route.ts +104 -0
  818. package/src/intake-plane/runtime-callables.ts +3 -1
  819. package/src/intake-plane/sketch-ledger.ts +34 -0
  820. package/src/intake-plane/slash.ts +30 -2
  821. package/src/intake-plane/submit-intake-work.ts +160 -0
  822. package/src/intake-plane/telegram-work.ts +56 -0
  823. package/src/iul/sidebar-shell.ts +0 -165
  824. package/src/launch-head/capability-check.ts +162 -0
  825. package/src/launch-head/launch-head-cli.ts +242 -0
  826. package/src/launch-head/load-balance.ts +81 -0
  827. package/src/live/detail-switch.ts +180 -0
  828. package/src/llm/provider-detect.ts +247 -0
  829. package/src/llm/provider-probe.ts +57 -0
  830. package/src/llm/usable-llm.ts +32 -0
  831. package/src/llm.ts +55 -23
  832. package/src/log-entry.ts +1 -1
  833. package/src/log-pane/attachment-popup.ts +1 -2
  834. package/src/loops/loop-cli.ts +37 -0
  835. package/src/loops/registry.ts +196 -0
  836. package/src/market/official-keys.ts +5 -0
  837. package/src/market/publish.ts +329 -0
  838. package/src/market/signed-index.ts +156 -0
  839. package/src/market/tgz.ts +121 -0
  840. package/src/mcp/client.ts +1 -1
  841. package/src/mcp/mcp-oauth.ts +40 -5
  842. package/src/mcp/server.ts +75 -7
  843. package/src/mcp-gateway/gateway.ts +128 -0
  844. package/src/mcp-gateway/pat-store.ts +124 -0
  845. package/src/mss/logging/log-store.ts +91 -2
  846. package/src/mss/logging/scoped-level.ts +5 -9
  847. package/src/native-tool-catalog.ts +66 -345
  848. package/src/nexus/api/connect-info.ts +23 -25
  849. package/src/nexus/api/context-url.ts +1 -1
  850. package/src/nexus/api/design-check.ts +90 -59
  851. package/src/nexus/api/design-previews-route.ts +182 -0
  852. package/src/nexus/api/design-system-create-route.ts +123 -0
  853. package/src/nexus/api/graph-approvals.ts +125 -0
  854. package/src/nexus/api/graph-kinds.ts +69 -0
  855. package/src/nexus/api/graphs-api.ts +221 -0
  856. package/src/nexus/api/harness-api.ts +31 -2
  857. package/src/nexus/api/health.ts +15 -0
  858. package/src/nexus/api/http-server.ts +328 -43
  859. package/src/nexus/api/image-generate.ts +3 -0
  860. package/src/nexus/api/ingest-token.ts +175 -0
  861. package/src/nexus/api/intake-ledger-api.ts +54 -0
  862. package/src/nexus/api/intake-route.ts +111 -0
  863. package/src/nexus/api/json-response.ts +33 -0
  864. package/src/nexus/api/live-detail.ts +47 -0
  865. package/src/nexus/api/live-shipped.ts +78 -0
  866. package/src/nexus/api/log-fabric.ts +42 -2
  867. package/src/nexus/api/markdown-publish.ts +3 -2
  868. package/src/nexus/api/mcp-http.ts +14 -9
  869. package/src/nexus/api/mcp-oauth-hint.ts +31 -0
  870. package/src/nexus/api/mcp-public-allowlist.ts +33 -0
  871. package/src/nexus/api/merge-approvals-gate.ts +79 -0
  872. package/src/nexus/api/merge-approvals.ts +307 -0
  873. package/src/nexus/api/meta-api.ts +18 -0
  874. package/src/nexus/api/next-fluent-wiring.ts +7 -9
  875. package/src/nexus/api/plugins-credentials-api.ts +47 -0
  876. package/src/nexus/api/plugins-market.ts +209 -0
  877. package/src/nexus/api/pod-credential-api.ts +187 -0
  878. package/src/nexus/api/public-routes.ts +87 -0
  879. package/src/nexus/api/rest-route-paths.ts +15 -0
  880. package/src/nexus/api/schedules-action.ts +12 -3
  881. package/src/nexus/api/schedules-read.ts +228 -0
  882. package/src/nexus/api/sessions-store.ts +8 -2
  883. package/src/nexus/api/setup-answer-priority.ts +130 -0
  884. package/src/nexus/api/setup-channel-bot.ts +56 -0
  885. package/src/nexus/api/setup-child-llm.ts +228 -0
  886. package/src/nexus/api/setup-obsidian-skills.ts +93 -0
  887. package/src/nexus/api/task-cards-api.ts +34 -0
  888. package/src/nexus/api/tasks-create.ts +136 -0
  889. package/src/nexus/api/telegram-trigger-bot.ts +3 -3
  890. package/src/nexus/api/terminals.ts +204 -18
  891. package/src/nexus/api/trace.ts +405 -0
  892. package/src/nexus/api/vault-api.ts +140 -7
  893. package/src/nexus/api/worktrees.ts +18 -2
  894. package/src/nexus/boot/register-mcp-clients.ts +135 -16
  895. package/src/nexus/daemon-endpoint.ts +85 -0
  896. package/src/nexus/default-port.ts +1 -0
  897. package/src/nexus/index.ts +362 -143
  898. package/src/nexus/onboarding/pwa-share-prompt.ts +31 -8
  899. package/src/nexus/onboarding/tailscale-probe.ts +9 -1
  900. package/src/nexus/outbound/router.ts +1 -1
  901. package/src/nexus/setup-status.ts +58 -0
  902. package/src/nexus/supervisor/lock.ts +8 -0
  903. package/src/nexus/webterm/pty.ts +15 -0
  904. package/src/oauth/codex-account-rotation.ts +122 -16
  905. package/src/oauth/codex-account-store.ts +88 -19
  906. package/src/oauth/codex-quota-policy.ts +55 -0
  907. package/src/oauth/store.ts +28 -25
  908. package/src/obsidian/default-vault.ts +17 -0
  909. package/src/obsidian/vault-write-guard.ts +46 -0
  910. package/src/onboarding/help/llm.md +2 -2
  911. package/src/onboarding.ts +27 -4
  912. package/src/panes/alt-skip-predicate.ts +4 -3
  913. package/src/panes/external-terminal-pane.ts +3 -4
  914. package/src/panes/factory.ts +1 -18
  915. package/src/panes/index.ts +1 -5
  916. package/src/panes/pane-cycle.ts +1 -1
  917. package/src/panes/types.ts +1 -1
  918. package/src/platform/env-literal.ts +4 -0
  919. package/src/plugin-hooks/dispatcher.ts +10 -4
  920. package/src/plugins/core/host.ts +108 -4
  921. package/src/plugins/core/manifest.ts +104 -7
  922. package/src/plugins/install/market-fetch.ts +250 -0
  923. package/src/plugins/install/plugin-credentials.ts +106 -0
  924. package/src/plugins/install/plugin-install.ts +518 -0
  925. package/src/policy/claude-subscription-guard.ts +25 -0
  926. package/src/preview/terminal.ts +24 -1
  927. package/src/preview-pane/mount.ts +1 -142
  928. package/src/prompt/references.ts +4 -51
  929. package/src/prompt-library/universal-preamble.ts +8 -2
  930. package/src/pty-shell/pty-control-ipc.ts +57 -3
  931. package/src/pty-shell/pty-mouse.ts +75 -0
  932. package/src/pty-shell/pty-recording.ts +108 -0
  933. package/src/pty-shell/registry.ts +53 -3
  934. package/src/python/resolve-python.ts +1 -1
  935. package/src/release-loop/checklist.ts +191 -0
  936. package/src/release-loop/manifest.ts +65 -0
  937. package/src/release-loop/release-note.ts +177 -0
  938. package/src/roles/lease-cas.ts +52 -0
  939. package/src/roles/machine-name.ts +48 -0
  940. package/src/roles/machine-profile.ts +65 -0
  941. package/src/roles/role-lease-read.ts +81 -0
  942. package/src/roles/role-lease.ts +63 -0
  943. package/src/roles/role-watch.ts +69 -0
  944. package/src/self-dev/ask-launch-flow.ts +39 -22
  945. package/src/self-dev/ask-launch-io.ts +1 -1
  946. package/src/self-dev/dev-cli.ts +290 -159
  947. package/src/self-dev/dev-pipeline.ts +157 -9
  948. package/src/self-dev/draft-sweep.ts +231 -0
  949. package/src/self-dev/draft-triage-rules.ts +42 -0
  950. package/src/self-dev/entrance-baseline.ts +156 -4
  951. package/src/self-dev/fabric-decompose-adapter.ts +10 -0
  952. package/src/self-dev/launch-preflight.ts +1 -1
  953. package/src/self-dev/orchestrate.ts +297 -20
  954. package/src/self-dev/repair-signals.ts +6 -0
  955. package/src/self-dev/run-store.ts +20 -5
  956. package/src/self-dev/run-supervisor.ts +43 -3
  957. package/src/self-implement/abandoned-classification.ts +15 -1
  958. package/src/self-implement/blocked-draft-policy.ts +6 -0
  959. package/src/self-implement/budget-gate.ts +236 -0
  960. package/src/self-implement/child-llm-preference.ts +84 -0
  961. package/src/self-implement/decision-events.ts +57 -0
  962. package/src/self-implement/decision-signal-press.ts +5 -2
  963. package/src/self-implement/dependency-change-gate.ts +90 -0
  964. package/src/self-implement/gate-baseline.ts +35 -2
  965. package/src/self-implement/gate-cli.ts +29 -7
  966. package/src/self-implement/gate-scope.ts +7 -1
  967. package/src/self-implement/goal-author-decompose.ts +22 -4
  968. package/src/self-implement/goal-author.ts +41 -3
  969. package/src/self-implement/goal-boundary.ts +65 -0
  970. package/src/self-implement/goal-clarification-escalation.ts +1 -0
  971. package/src/self-implement/goal-documents-dir.ts +6 -1
  972. package/src/self-implement/goal-execution-records.ts +58 -0
  973. package/src/self-implement/goal-run-store.ts +42 -24
  974. package/src/self-implement/graph-authority.ts +13 -28
  975. package/src/self-implement/graph-catalog.ts +48 -0
  976. package/src/self-implement/graph-run-contract.ts +127 -0
  977. package/src/self-implement/graph-templates.ts +14 -7
  978. package/src/self-implement/graph-yaml.ts +35 -6
  979. package/src/self-implement/harness-policy-gates.ts +42 -0
  980. package/src/self-implement/headless-elanous-driver.ts +30 -6
  981. package/src/self-implement/heal-triage.ts +229 -0
  982. package/src/self-implement/host-regate.ts +244 -0
  983. package/src/self-implement/non-git-consent.ts +23 -0
  984. package/src/self-implement/orchestrator.ts +488 -134
  985. package/src/self-implement/pipeline-shape.ts +1 -1
  986. package/src/self-implement/pr-evidence-artifact.ts +6 -19
  987. package/src/self-implement/prior-draft-findings.ts +128 -0
  988. package/src/self-implement/pwa-import-graph.ts +65 -0
  989. package/src/self-implement/repo-provision.ts +18 -21
  990. package/src/self-implement/report-origin.ts +39 -0
  991. package/src/self-implement/rework-policy.ts +21 -0
  992. package/src/self-implement/run-ledger-compact.ts +438 -0
  993. package/src/self-implement/run-ledger.ts +212 -29
  994. package/src/self-implement/run-report.ts +24 -0
  995. package/src/self-implement/run-status-mapping.ts +2 -0
  996. package/src/self-implement/running-runs.ts +112 -14
  997. package/src/self-implement/scoped-typecheck-config.ts +49 -0
  998. package/src/self-implement/seams.ts +207 -40
  999. package/src/self-implement/self-implement-cli.ts +17 -2
  1000. package/src/self-implement/shadow-repo.ts +350 -0
  1001. package/src/self-implement/supervision-vocabulary.ts +7 -0
  1002. package/src/shell-runner/dispatch.ts +3 -0
  1003. package/src/shell-runner/index.ts +0 -7
  1004. package/src/skills/runner.ts +0 -69
  1005. package/src/skills/tools/agent-room-slash.ts +2 -1
  1006. package/src/skills/tools/agent-room.ts +8 -140
  1007. package/src/skills/tools/agent.ts +5 -3
  1008. package/src/skills/tools/context.ts +5 -140
  1009. package/src/skills/tools/control.ts +4 -188
  1010. package/src/skills/tools/dashboard-slash.ts +3 -11
  1011. package/src/skills/tools/dashboard-state.ts +3 -5
  1012. package/src/skills/tools/dev-harness.ts +45 -2
  1013. package/src/skills/tools/omni-search.ts +29 -1
  1014. package/src/skills/tools/shell.ts +11 -29
  1015. package/src/skills/tools/spawn-coding-agent-headless.ts +6 -9
  1016. package/src/ssh/ssh-picker-modal.ts +1 -1
  1017. package/src/steward/directive.ts +99 -0
  1018. package/src/steward/steward-cards.ts +45 -0
  1019. package/src/steward/triage.ts +239 -0
  1020. package/src/storage/cli-bin.ts +67 -0
  1021. package/src/storage/object-store-visibility.ts +169 -0
  1022. package/src/storage/object-store.ts +452 -0
  1023. package/src/storage/s3.ts +53 -10
  1024. package/src/storage/storage-detect.ts +337 -0
  1025. package/src/surface/address.ts +3 -5
  1026. package/src/surface/index.ts +0 -6
  1027. package/src/surface/llm-tools.ts +1 -2
  1028. package/src/surface/shell-runner-boot.ts +4 -89
  1029. package/src/task-cards/card-cli.ts +64 -0
  1030. package/src/task-cards/card-store.ts +241 -0
  1031. package/src/task-orchestrator/boot.ts +27 -1
  1032. package/src/task-orchestrator/dispatcher.ts +49 -7
  1033. package/src/task-orchestrator/external-fingerprint.ts +10 -0
  1034. package/src/task-orchestrator/external-policy.ts +86 -0
  1035. package/src/task-orchestrator/feedback-loop.ts +4 -2
  1036. package/src/task-orchestrator/graph.ts +18 -1
  1037. package/src/task-orchestrator/runtimes/create.ts +128 -0
  1038. package/src/task-orchestrator/store.ts +78 -18
  1039. package/src/task-orchestrator/surface-registry.ts +5 -2
  1040. package/src/task-orchestrator/surfaces/external-exec.ts +180 -0
  1041. package/src/task-orchestrator/surfaces/idea-approval-pr.ts +118 -0
  1042. package/src/task-orchestrator/surfaces/index.ts +4 -3
  1043. package/src/task-orchestrator/surfaces/pod-account-broker.ts +60 -5
  1044. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +194 -0
  1045. package/src/task-orchestrator/surfaces/pod-command-job.ts +369 -0
  1046. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +207 -0
  1047. package/src/task-orchestrator/surfaces/pod-pool.ts +128 -12
  1048. package/src/task-orchestrator/surfaces/pod-skills.ts +11 -3
  1049. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +147 -0
  1050. package/src/task-orchestrator/surfaces/pod-source-receive.ts +129 -0
  1051. package/src/task-orchestrator/surfaces/pod-source-spec.ts +77 -0
  1052. package/src/task-orchestrator/surfaces/self-implement-pod.ts +930 -45
  1053. package/src/task-orchestrator/surfaces/self-implement.ts +8 -1
  1054. package/src/task-orchestrator/tox-loop.ts +204 -0
  1055. package/src/task-orchestrator/types.ts +21 -1
  1056. package/src/telegram-commands.ts +8 -0
  1057. package/src/telegram-report.ts +14 -7
  1058. package/src/telegram-run.ts +18 -5
  1059. package/src/telegram.ts +92 -22
  1060. package/src/tool-runtime/acp-session-runtime.ts +28 -7
  1061. package/src/tool-runtime/agent-runtime.ts +1 -0
  1062. package/src/tool-runtime/context-runtime.ts +18 -35
  1063. package/src/tool-runtime/control-runtime.ts +8 -21
  1064. package/src/tool-runtime/dashboard-state-runtime.ts +1 -5
  1065. package/src/tool-runtime/elanous-asana-runtimes.ts +205 -0
  1066. package/src/tool-runtime/elanous-control-runtimes.ts +16 -2
  1067. package/src/tool-runtime/elanous-task-submit-runtime.ts +124 -0
  1068. package/src/tool-runtime/index.ts +4 -1
  1069. package/src/tool-runtime/scenario-runtimes.ts +22 -21
  1070. package/src/tool-runtime/scenario-target-mount.ts +6 -32
  1071. package/src/tool-runtime/types.ts +8 -0
  1072. package/src/tui/chat/attachment-summary.ts +4 -4
  1073. package/src/tui-client/acp-transport-ws-client.ts +72 -5
  1074. package/src/tui-client/headless-core-guard.ts +0 -1
  1075. package/src/tui.ts +11 -7
  1076. package/src/typecheck-ratchet.ts +15 -0
  1077. package/src/ui/hover-presenter.ts +3 -7
  1078. package/src/user-config.ts +453 -190
  1079. package/src/util/spawn-sync-output.ts +15 -0
  1080. package/src/version/code-revision.ts +34 -9
  1081. package/src/version/packed-revision.json +1 -1
  1082. package/src/video-pipeline/broll-agent.ts +102 -0
  1083. package/src/video-pipeline/recipes/broll.ts +263 -0
  1084. package/src/video-pipeline/walk-line.ts +2 -1
  1085. package/src/views/scratch-image-size.ts +10 -0
  1086. package/src/voice/channel-adapters/pwa-voice-adapter.ts +2 -18
  1087. package/src/voice/channel-adapters/telegram-voice-adapter.ts +1 -6
  1088. package/src/voice/voice-tts-singleton.ts +46 -8
  1089. package/src/web-push/vapid-keys.ts +1 -1
  1090. package/src/web-search/free.ts +77 -0
  1091. package/src/web-search/index.ts +7 -5
  1092. package/src/web-terminal/preview-tap-registry.ts +152 -43
  1093. package/src/workflow-runtime/deps-bridge.ts +4 -3
  1094. package/src/workflow-runtime/executor.ts +18 -22
  1095. package/src/workflow-runtime/node-catalog.ts +20 -1
  1096. package/src/workflow-runtime/nodes/bash.ts +1 -0
  1097. package/src/workflow-runtime/nodes/task.ts +74 -0
  1098. package/src/workflow-runtime/nodes/triggers.ts +24 -1
  1099. package/src/workflow-runtime/plugin-kind-node.ts +166 -0
  1100. package/src/workflow-runtime/schema.ts +98 -2
  1101. package/src/workflow-runtime/triggers/webhook-router.ts +43 -3
  1102. package/src/workflow-runtime/triggers/webhook-source.ts +19 -1
  1103. package/src/workflow-runtime/types.ts +76 -5
  1104. package/apps/pwa/out/_next/static/Ksn99fVf2HUWhSyV1-FC8/_buildManifest.js +0 -1
  1105. package/apps/pwa/out/_next/static/chunks/1176-4943bd83b00e53a2.js +0 -25
  1106. package/apps/pwa/out/_next/static/chunks/1380-9c652e1002032c86.js +0 -1
  1107. package/apps/pwa/out/_next/static/chunks/1863-2ea170dfec26b88b.js +0 -1
  1108. package/apps/pwa/out/_next/static/chunks/2091-2d6b7f4f16300f83.js +0 -1
  1109. package/apps/pwa/out/_next/static/chunks/2347-366617ec73babe3b.js +0 -1
  1110. package/apps/pwa/out/_next/static/chunks/3089-e60cc8f5d35ee239.js +0 -1
  1111. package/apps/pwa/out/_next/static/chunks/3158-c77ed9ea34f9cc9f.js +0 -1
  1112. package/apps/pwa/out/_next/static/chunks/3607-f0b5c5ee18fcf56a.js +0 -1
  1113. package/apps/pwa/out/_next/static/chunks/3659-05b2d5d971b42dbc.js +0 -1
  1114. package/apps/pwa/out/_next/static/chunks/3736-b6a7bfdcb8f56ec6.js +0 -1
  1115. package/apps/pwa/out/_next/static/chunks/3839-eebf298e89a7bb77.js +0 -1
  1116. package/apps/pwa/out/_next/static/chunks/4012.832a2824b978e7ee.js +0 -5
  1117. package/apps/pwa/out/_next/static/chunks/4706-d85d7f3345f3ff91.js +0 -1
  1118. package/apps/pwa/out/_next/static/chunks/526-17a6a2e0e6dc4498.js +0 -1
  1119. package/apps/pwa/out/_next/static/chunks/5508-676ac75f2757ad40.js +0 -1
  1120. package/apps/pwa/out/_next/static/chunks/5583-8a0317f4b8e68db9.js +0 -1
  1121. package/apps/pwa/out/_next/static/chunks/6258-b58f722b10e8d31d.js +0 -1
  1122. package/apps/pwa/out/_next/static/chunks/6820-32fd097e6186fdc9.js +0 -1
  1123. package/apps/pwa/out/_next/static/chunks/6852-983db7ca958634b7.js +0 -1
  1124. package/apps/pwa/out/_next/static/chunks/7235-e583e4a015e159b9.js +0 -1
  1125. package/apps/pwa/out/_next/static/chunks/7482-7b417330b24a996a.js +0 -1
  1126. package/apps/pwa/out/_next/static/chunks/7701-99b7d4dedb955bf8.js +0 -1
  1127. package/apps/pwa/out/_next/static/chunks/77f1928d-bd3777589042d3ea.js +0 -1
  1128. package/apps/pwa/out/_next/static/chunks/8366-36956cafc5e33614.js +0 -1
  1129. package/apps/pwa/out/_next/static/chunks/8370-23d0de995be666f8.js +0 -1
  1130. package/apps/pwa/out/_next/static/chunks/8617-307421f3b0e8b2a2.js +0 -1
  1131. package/apps/pwa/out/_next/static/chunks/8837-e45de46a9fedf1f5.js +0 -1
  1132. package/apps/pwa/out/_next/static/chunks/9058-0e8055a5ea3ebdd6.js +0 -1
  1133. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-84b84eca12acada4.js +0 -1
  1134. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1f23568c29e88f7a.js +0 -1
  1135. package/apps/pwa/out/_next/static/chunks/app/botlab/page-bf6fc9aafd6135a7.js +0 -1
  1136. package/apps/pwa/out/_next/static/chunks/app/bots/page-3f8539ae4b719d2e.js +0 -1
  1137. package/apps/pwa/out/_next/static/chunks/app/chat/page-d2900febb74ecf32.js +0 -1
  1138. package/apps/pwa/out/_next/static/chunks/app/control/page-e82dec647dd79c36.js +0 -1
  1139. package/apps/pwa/out/_next/static/chunks/app/dashboard/page-096ec743a8ee955a.js +0 -1
  1140. package/apps/pwa/out/_next/static/chunks/app/design-check/page-255c2ceded4b3493.js +0 -1
  1141. package/apps/pwa/out/_next/static/chunks/app/healthz/route-84b84eca12acada4.js +0 -1
  1142. package/apps/pwa/out/_next/static/chunks/app/intake/page-2db61b2f9efba61f.js +0 -1
  1143. package/apps/pwa/out/_next/static/chunks/app/layout-e11ee17f221aaa9b.js +0 -1
  1144. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e075974e029ce4c4.js +0 -1
  1145. package/apps/pwa/out/_next/static/chunks/app/morning/page-ec5b68a9af3366a0.js +0 -1
  1146. package/apps/pwa/out/_next/static/chunks/app/not-found-986c177afb4adae6.js +0 -1
  1147. package/apps/pwa/out/_next/static/chunks/app/observatory/page-a72781461af2f2a3.js +0 -1
  1148. package/apps/pwa/out/_next/static/chunks/app/page-92781cbaafc487ad.js +0 -1
  1149. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2c14d2a1f2930f33.js +0 -1
  1150. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-22ee4bf8070b2b4d.js +0 -1
  1151. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9c06e4a8297942a8.js +0 -1
  1152. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d0cd32f6d373798f.js +0 -1
  1153. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c1642018d87ebb8.js +0 -1
  1154. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-93e5e97d614df487.js +0 -1
  1155. package/apps/pwa/out/_next/static/chunks/app/setup/layout-84b84eca12acada4.js +0 -1
  1156. package/apps/pwa/out/_next/static/chunks/app/setup/page-b1ce871940d7393e.js +0 -1
  1157. package/apps/pwa/out/_next/static/chunks/app/share/page-ae154addf345febe.js +0 -1
  1158. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90af3aa813d2053c.js +0 -1
  1159. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f3f962fda428a738.js +0 -1
  1160. package/apps/pwa/out/_next/static/chunks/app/term/page-1c1f7cc1d5def06d.js +0 -1
  1161. package/apps/pwa/out/_next/static/chunks/app/vault/page-09d4cbdaaacf06c1.js +0 -1
  1162. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e877b8e8ca90b27f.js +0 -1
  1163. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71d902f463dea526.js +0 -1
  1164. package/apps/pwa/out/_next/static/chunks/app/workspace/page-40ea576cefa25fe6.js +0 -1
  1165. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a56a3f178c3831f6.js +0 -1
  1166. package/apps/pwa/out/_next/static/chunks/bcc499d1-804a270d878e0842.js +0 -1
  1167. package/apps/pwa/out/_next/static/chunks/framework-b25b7c5501c59b6d.js +0 -1
  1168. package/apps/pwa/out/_next/static/chunks/main-a9ec9da43387afff.js +0 -1
  1169. package/apps/pwa/out/_next/static/chunks/main-app-aa2c05954aaa6754.js +0 -1
  1170. package/apps/pwa/out/_next/static/chunks/pages/_app-bb3768fb4b84aebb.js +0 -1
  1171. package/apps/pwa/out/_next/static/chunks/pages/_error-2820ba75b87b2f5e.js +0 -1
  1172. package/apps/pwa/out/_next/static/chunks/webpack-58c73b145f189b1c.js +0 -1
  1173. package/apps/pwa/out/_next/static/css/effec8adf767aa75.css +0 -1
  1174. package/apps/pwa/out/dashboard/index.html +0 -1
  1175. package/apps/pwa/out/dashboard/index.txt +0 -26
  1176. package/src/acp/resident-shell.ts +0 -244
  1177. package/src/agent/spawn-local-llm-in-vw.ts +0 -240
  1178. package/src/dashboard/agent-spawn-tools-boot.ts +0 -14
  1179. package/src/dashboard/dock-picker-inventory.ts +0 -41
  1180. package/src/dashboard/goal-plan-hud-bridge.ts +0 -112
  1181. package/src/dashboard/hud-mirror.ts +0 -207
  1182. package/src/dashboard/input/mouse-dock-runtime.ts +0 -156
  1183. package/src/dashboard/input/mouse-hover-runtime.ts +0 -46
  1184. package/src/dashboard/input/mouse-pill-context-runtime.ts +0 -74
  1185. package/src/dashboard/input/mouse-workspace-restore-runtime.ts +0 -64
  1186. package/src/dashboard/input/scratch-actions.ts +0 -36
  1187. package/src/dashboard/input/virtual-window-key-router.ts +0 -24
  1188. package/src/dashboard/rich-scratch-viewers.ts +0 -77
  1189. package/src/dashboard/scratch-slash-runtime.ts +0 -36
  1190. package/src/dashboard/scratch-state-runtime.ts +0 -42
  1191. package/src/dashboard/scratch-widget-runtime.ts +0 -37
  1192. package/src/dashboard/sim-shell.ts +0 -313
  1193. package/src/dashboard/virtual-window-control-runtime.ts +0 -57
  1194. package/src/dashboard/virtual-window-help-runtime.ts +0 -113
  1195. package/src/dashboard/virtual-window-input-runtime.ts +0 -99
  1196. package/src/dashboard/virtual-window-mutation-runtime.ts +0 -102
  1197. package/src/dashboard/virtual-window-split-runtime.ts +0 -70
  1198. package/src/dashboard/vw-popup-openers.ts +0 -126
  1199. package/src/dashboard/window-picker-popup.ts +0 -74
  1200. package/src/dashboard/window-slash-runtime.ts +0 -108
  1201. package/src/dashboard/windowing/companion-slash.ts +0 -78
  1202. package/src/dashboard/windowing/fast-switch.ts +0 -138
  1203. package/src/dashboard/windowing/lifecycle.ts +0 -185
  1204. package/src/dashboard/windowing/virtual-windows.ts +0 -259
  1205. package/src/dashboard/windowing/visibility-chord.ts +0 -77
  1206. package/src/iul/resident-vw.ts +0 -55
  1207. package/src/panes/content-adapter.ts +0 -162
  1208. package/src/prompt/vw-reference.ts +0 -153
  1209. package/src/shell-runner/external-terminal-pane.ts +0 -157
  1210. package/src/sim/resident-vw.ts +0 -48
  1211. package/src/skills/tools/spawn-coding-agent-vw.ts +0 -181
  1212. package/src/skills/tools/virtual-windows.ts +0 -632
  1213. package/src/surface/adapters/window-surface-adapter.ts +0 -141
  1214. package/src/tool-runtime/layout-runtimes.ts +0 -81
  1215. package/src/tool-runtime/scenario-window-mount.ts +0 -207
  1216. package/src/views/ui-mode.ts +0 -100
  1217. package/src/virtual-window-context-menu.ts +0 -97
  1218. package/src/virtual-windows/addressing.ts +0 -195
  1219. package/src/virtual-windows/benchmark-preset.ts +0 -119
  1220. package/src/virtual-windows/event-bus.ts +0 -202
  1221. package/src/virtual-windows/layout/apply-planner.ts +0 -189
  1222. package/src/virtual-windows/layout/index.ts +0 -68
  1223. package/src/virtual-windows/layout/layout-commands.ts +0 -186
  1224. package/src/virtual-windows/layout/layout-tools.ts +0 -262
  1225. package/src/virtual-windows/layout/persistence.ts +0 -219
  1226. package/src/virtual-windows/layout/presets.ts +0 -114
  1227. package/src/virtual-windows/layout/restore-planner.ts +0 -223
  1228. package/src/virtual-windows/layout/serializer.ts +0 -197
  1229. package/src/virtual-windows/layout/tree.ts +0 -147
  1230. package/src/virtual-windows/layout/types.ts +0 -63
  1231. package/src/virtual-windows/layout-tree.ts +0 -389
  1232. package/src/virtual-windows/navigation.ts +0 -251
  1233. package/src/virtual-windows/pane-capture.ts +0 -142
  1234. package/src/virtual-windows/pane-content.ts +0 -611
  1235. package/src/virtual-windows/pane-spawner.ts +0 -278
  1236. package/src/virtual-windows/pty-tail-content.ts +0 -291
  1237. package/src/virtual-windows/resident-window.ts +0 -59
  1238. package/src/virtual-windows/secondary-action.ts +0 -25
  1239. package/src/virtual-windows/terminal-slot-content.ts +0 -165
  1240. package/src/virtual-windows/virtual-window.ts +0 -1301
  1241. package/src/virtual-windows/vw-local-input-target-popup.ts +0 -110
  1242. package/src/virtual-windows/vw-rename-modal.ts +0 -77
  1243. package/src/virtual-windows/vw-selector-popup.ts +0 -177
  1244. package/src/virtual-windows/window-registry.ts +0 -519
  1245. package/src/voice/channel-adapters/discord-voice-adapter.ts +0 -202
  1246. package/src/voice/voice-pwa-dispatch.ts +0 -245
  1247. package/src/window-picker-modal.ts +0 -135
  1248. /package/apps/pwa/out/_next/static/{Ksn99fVf2HUWhSyV1-FC8 → ktMqfrBbf8BMGac0jqLrb}/_ssgManifest.js +0 -0
  1249. /package/src/dashboard/{companion-slash-runtime.ts → companion-messages-runtime.ts} +0 -0
@@ -5,11 +5,14 @@
5
5
 
6
6
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
7
7
  import { join } from 'path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { createStaleInstallCheck, readCurrentInstalledCommit, readDashboardBootCommit, STALE_INSTALL_CHECK_INTERVAL_MS, versionedDashboardInstallDir } from './stale-install-check.js';
8
10
  import { spawn, spawnSync } from 'node:child_process';
9
11
  import { $ } from 'bun';
10
12
  import { LOCAL_SKILLS_DIR, SERVICE_NAMES, DATA_DIR, OBSIDIAN_VAULT } from '../config.js';
11
13
  import { dispatchDashboardSessionRuntimeTool } from './session-runtime-dispatch.js';
12
14
  import { buildAdRunSetup } from '../ad-pipeline/ad-run-setup.js';
15
+ import { nextChatScrollOffset } from '../chat/log-scroll-key.js';
13
16
  import type { CommandRunner } from '../ad-pipeline/higgsfield-backend.js';
14
17
  import type { ShootRunOptions } from '../ad-pipeline/shoot-run.js';
15
18
  import type { QcThresholds } from '../ad-pipeline/qc.js';
@@ -295,24 +298,10 @@ export interface RegisterDashboardGlobalKeysDeps {
295
298
  register: (binding: DashboardGlobalKeyRegistration) => void;
296
299
  openSurfaceCatalog: () => void;
297
300
  surfaceCatalogWhen?: () => boolean;
298
- dispatchVirtualWindowChord: (event: DisplayKeyEvent) => void;
299
301
  }
300
302
 
301
- /** Registers optional dashboard controls and the core VW controls that must always remain available. */
303
+ /** Registers optional dashboard controls. */
302
304
  export function registerDashboardGlobalKeys(deps: RegisterDashboardGlobalKeysDeps): void {
303
- const registerVwChord = (id: string, key: string, event: DisplayKeyEvent): void => {
304
- deps.register({
305
- id: `dashboard:vw-chord:${id}`,
306
- chordPrefix: 'C-b',
307
- key,
308
- scope: 'global',
309
- handler: () => deps.dispatchVirtualWindowChord(event),
310
- });
311
- };
312
- const vwKey = (name: string, mods: Partial<DisplayKeyEvent> = {}): DisplayKeyEvent => ({
313
- name, ctrl: false, shift: false, alt: false, ...mods,
314
- });
315
-
316
305
  if (deps.enableSupplementalGlobalKeys) {
317
306
  deps.register({
318
307
  id: 'dashboard:open-surface-catalog',
@@ -321,14 +310,7 @@ export function registerDashboardGlobalKeys(deps: RegisterDashboardGlobalKeysDep
321
310
  handler: deps.openSurfaceCatalog,
322
311
  when: deps.surfaceCatalogWhen,
323
312
  });
324
- registerVwChord('picker', '0', vwKey('0'));
325
- registerVwChord('close-window', 'S-x', vwKey('x', { shift: true }));
326
- registerVwChord('last-pane', 'tab', vwKey('tab'));
327
313
  }
328
-
329
- registerVwChord('new-window', 'c', vwKey('c'));
330
- registerVwChord('close-pane', 'x', vwKey('x'));
331
- registerVwChord('zoom-toggle', 'z', vwKey('z'));
332
314
  }
333
315
 
334
316
  export type ForkableSessionTurn = {
@@ -509,13 +491,10 @@ import {
509
491
  } from '../primitives/damage-region/index.js';
510
492
  import {
511
493
  createArtifactStore,
512
- createLegacyLayoutProvider,
513
494
  createLegacyTimelineProvider,
514
495
  } from '../artifact/index.js';
515
496
  import { defaultTimelineBaseDir } from '../tool-runtime/recording-runtimes-paths.js';
516
- import { layoutsDir } from '../virtual-windows/layout/persistence.js';
517
497
  import { createVisualStateStore } from '../panes/visual-state.js';
518
- import { skipWindowWhenStorePredicate } from '../panes/alt-skip-predicate.js';
519
498
  import { composeIdentityHooks } from '../display/modal-identity-wiring.js';
520
499
  import { composeWorkspaceHostHooks } from '../display/workspace-host-wiring.js';
521
500
  import {
@@ -626,6 +605,7 @@ import {
626
605
  import { createDashboardInlineSetupFlow } from './setup-inline.js';
627
606
  import { resolveDashboardChatMainThemeCommand } from './input/chat-main-theme-command.js';
628
607
  import { resolveDashboardChatMainLogCommand } from './input/chat-main-log-command.js';
608
+ import { noProviderReply } from './input/no-provider-reply.js';
629
609
  import {
630
610
  dashboardLogHelpLines,
631
611
  resolveDashboardLogFilterAction,
@@ -650,7 +630,6 @@ import {
650
630
  matchDashboardChordAction,
651
631
  } from './input/dashboard-chord-actions.js';
652
632
  import { createDashboardChordRuntime } from './input/dashboard-chord-runtime.js';
653
- import { createMouseDockRuntime } from './input/mouse-dock-runtime.js';
654
633
  import { handleDashboardDisplayKeyRoute } from './input/dashboard-display-key-route.js';
655
634
  import { routeDashboardPriorityKey } from './input/dashboard-priority-key-route.js';
656
635
  import { shouldPassthroughDashboardHostOwnedInputCoreAction } from './input/input-core-host-action.js';
@@ -915,15 +894,12 @@ import {
915
894
  import { globalVoiceCostTracker } from '../voice/cost-tracker.js';
916
895
  import { createDashboardMouseWiring } from './input/mouse-wiring.js';
917
896
  import { createMouseConversationPopupRuntime } from './input/mouse-conversation-popup-runtime.js';
918
- import { createMouseHoverRuntime } from './input/mouse-hover-runtime.js';
919
897
  import { createMouseModalHitRuntime } from './input/mouse-modal-hit-runtime.js';
920
898
  import { createMouseModalSurfaceRuntime } from './input/mouse-modal-surface-runtime.js';
921
899
  import { createMouseModeSwitchRuntime } from './input/mouse-mode-switch-runtime.js';
922
900
  import { createMousePaneHitRuntime } from './input/mouse-pane-hit-runtime.js';
923
901
  import { createMousePickerRuntime } from './input/mouse-picker-runtime.js';
924
- import { createMousePillContextRuntime } from './input/mouse-pill-context-runtime.js';
925
902
  import { createMouseShellRollupRuntime } from './input/mouse-shell-rollup-runtime.js';
926
- import { createMouseWorkspaceRestoreRuntime } from './input/mouse-workspace-restore-runtime.js';
927
903
  import { createDashboardPluginBaseRuntime } from './plugin-base-runtime.js';
928
904
  import { createDashboardPluginExecutionRuntime } from './plugin-execution-runtime.js';
929
905
  import { createDashboardPluginPaneRuntime } from './plugin-pane-runtime.js';
@@ -987,7 +963,6 @@ import { runDashboardSkillByName } from './skill-runtime.js';
987
963
  import { createDashboardClipboardActions } from './clipboard-actions.js';
988
964
  import {
989
965
  buildDashboardEditorOpenPayload,
990
- buildDashboardScratchDumpLines,
991
966
  } from './clipboard-message-runtime.js';
992
967
  import { createDashboardCopyRuntime } from './copy-runtime.js';
993
968
  import { createDashboardMediaRuntime } from './media-runtime.js';
@@ -998,18 +973,14 @@ import { createDashboardContextMenuFeedbackRuntime } from './context-menu-feedba
998
973
  import { createDashboardStatusFeedbackRuntime } from './status-feedback-runtime.js';
999
974
  import { createDashboardTransferFeedbackRuntime } from './transfer-feedback-runtime.js';
1000
975
  import { createDashboardFinderFeedbackRuntime } from './finder-feedback-runtime.js';
1001
- import { createDashboardWindowSlashRuntime } from './window-slash-runtime.js';
1002
- import { createDashboardBenchSlashRuntime } from './bench-slash-runtime.js';
1003
976
  import { createDashboardTermSlashRuntime } from './term-slash-runtime.js';
1004
977
  import { createDashboardShellSlashRuntime } from './shell-slash-runtime.js';
1005
978
  import { createDashboardRunSkillSlashRuntime } from './run-skill-slash-runtime.js';
1006
979
  import { createDashboardSkillTriggersSlashRuntime } from './skill-triggers-slash-runtime.js';
1007
980
  import { createDashboardProviderSlashRuntime } from './provider-slash-runtime.js';
1008
981
  import { createDashboardPreviewSlashRuntime } from './preview-slash-runtime.js';
1009
- import { createDashboardScratchSlashRuntime } from './scratch-slash-runtime.js';
1010
- import { createDashboardCompanionSlashRuntime } from './companion-slash-runtime.js';
982
+ import { createDashboardCompanionSlashRuntime } from './companion-messages-runtime.js';
1011
983
  import { createDashboardAgentsSlashRuntime } from './agents-slash-runtime.js';
1012
- import { createDashboardViewSlashRuntime } from './view-slash-runtime.js';
1013
984
  import { createDashboardControlSignalSlashRuntime } from './control-signal-slash-runtime.js';
1014
985
  import { createDashboardBrowserCdpSlashRuntime } from './browser-cdp-slash-runtime.js';
1015
986
  import { collectGroundingFactsViaCdp } from '../product-grounding/cdp-collect.js';
@@ -1025,9 +996,6 @@ import { resolveDashboardMediaSlash } from './media-slash-runtime.js';
1025
996
  import { createDashboardMemoCompanionRuntime } from './memo-companion-runtime.js';
1026
997
  import { createDashboardDetailViewerRuntime } from './detail-viewer-runtime.js';
1027
998
  import { createDashboardCompanionWidgetRuntime } from './companion-widget-runtime.js';
1028
- import { createDashboardScratchWidgetRuntime } from './scratch-widget-runtime.js';
1029
- import { createDashboardScratchStateRuntime } from './scratch-state-runtime.js';
1030
- import { createRichScratchViewers } from './rich-scratch-viewers.js';
1031
999
  import { createDashboardClipboardHistoryRuntime } from './clipboard-history-runtime.js';
1032
1000
  import { createDashboardClipboardCompanionRuntime } from './clipboard-companion-runtime.js';
1033
1001
  import { createDashboardMemoWidgetRuntime } from './memo-widget-runtime.js';
@@ -1082,7 +1050,6 @@ import {
1082
1050
  import { createDashboardTerminalPopupRuntime } from './terminal-popup-runtime.js';
1083
1051
  import { toggleDashboardAssistantRenderState } from './assistant-render-state-runtime.js';
1084
1052
  import { bootDashboardAcpBgSweep } from './acp-bg-sweep-boot.js';
1085
- import { bootDashboardAgentSpawnTools } from './agent-spawn-tools-boot.js';
1086
1053
  import { bootDashboardConfigTools } from './config-tools-boot.js';
1087
1054
  import { bootDashboardConversationPopupRuntime } from './conversation-popup-boot.js';
1088
1055
  import { bootDashboardTerminalRuntime } from './terminal-runtime-boot.js';
@@ -1103,20 +1070,16 @@ import { bootDashboardHitl } from './hitl-boot.js';
1103
1070
  import { attachDashboardFilePathToken } from './file-path-attachment.js';
1104
1071
  import { bootDashboardSlashExecutor } from './slash-executor-boot.js';
1105
1072
  import { buildDashboardSlashRegistry, type DashboardSlashContext } from './slash-runtime/index.js';
1073
+ import { unknownSlashReply } from './slash-runtime/unknown-slash-reply.js';
1106
1074
  import { resolveSurfaceUx } from '../agent/surface-ux/build.js';
1107
1075
  import type { SurfaceUx } from '../agent/surface-ux/types.js';
1108
1076
  import { getDefaultQuestionChannels, type QuestionChannel } from '../hitl/question.js';
1109
1077
  import { getDefaultCompactProvider, scheduleArchiveRetentionOnce } from '../compact/index.js';
1110
1078
  import { attachDashboardWorkingDirSelection } from './working-dir-selection-attachment.js';
1111
- import { createDashboardVirtualWindowControlRuntime } from './virtual-window-control-runtime.js';
1112
- import { createDashboardVirtualWindowHelpRuntime } from './virtual-window-help-runtime.js';
1113
1079
  import { createBrowserHelpRuntime } from './browser-help-runtime.js';
1114
1080
  import { createBrowserChordRuntime } from './browser-chord-runtime.js';
1115
1081
  import { summarizeFileWithLLM } from './browser-llm-summary.js';
1116
- import { createDashboardVirtualWindowInputRuntime } from './virtual-window-input-runtime.js';
1117
1082
  import { resolveDashboardHostChromePolicy } from './host-chrome-policy.js';
1118
- import { createDashboardVirtualWindowMutationRuntime } from './virtual-window-mutation-runtime.js';
1119
- import { createDashboardVirtualWindowSplitRuntime } from './virtual-window-split-runtime.js';
1120
1083
  import {
1121
1084
  bootDashboardPlanModeRuntime,
1122
1085
  cleanupDashboardWorktreeSessions,
@@ -1152,11 +1115,7 @@ import {
1152
1115
  import {
1153
1116
  type DashboardPaneTitleMenuPayload,
1154
1117
  } from '../dashboard-pane-context-menu.js';
1155
- import {
1156
- type VirtualWindowTitleMenuPayload,
1157
- } from '../virtual-window-context-menu.js';
1158
1118
  // writeClipboard imported below with other clipboard helpers (line ~185).
1159
- import { registerVwFastSwitchBindings } from './windowing/fast-switch.js';
1160
1119
  import {
1161
1120
  renderPaneNav, paneAtColumn, paneNavLabel,
1162
1121
  type PaneNavHitArea,
@@ -1204,15 +1163,9 @@ import { createInteractiveTerminalModal } from '../interactive-terminal-modal.js
1204
1163
  import { showToast } from './render/toast.js';
1205
1164
  import {
1206
1165
  browserWidgetInstanceIdForView,
1207
- renderPaneModalHint,
1208
1166
  shortcutFor,
1209
- SHORTCUT_TABLE as PANE_SHORTCUT_TABLE,
1210
- PANE_MODAL_CHORD_TIMEOUT_MS,
1211
1167
  } from './modals/pane.js';
1212
- // FU-2 — routeVirtualWindowKey import removed. VW chord 는 coordinator
1213
- // 의 registerKeyBinding chord 경로로 이전. imperative 호출용 파일은
1214
- // dashboard-virtual-window-key-router.ts 에 그대로 유지.
1215
- import { showTransientTerminalModal } from './modals/transient.js';
1168
+ import { showTransientTerminalModal, currentTransientTerminalModal } from './modals/transient.js';
1216
1169
  import { showPaneMultiModal, showLivePaneMultiModal, currentPaneMultiModal, type PaneMultiModalHandle } from './modals/pane-multi.js';
1217
1170
  import {
1218
1171
  resolveCompanionPopupChrome,
@@ -1234,26 +1187,10 @@ import {
1234
1187
  TerminalRegistry,
1235
1188
  BroadcastBus,
1236
1189
  PreviewSlotAdapter,
1237
- VwPlacementAdapter,
1238
1190
  getChannelBus,
1239
1191
  type TerminalPlacement,
1240
1192
  } from '../terminal-matrix/index.js';
1241
- import {
1242
- resolveTerminalMoveDestination,
1243
- resolveVwTerminalInstanceForPane,
1244
- } from '../terminal-matrix/mobility.js';
1245
- import {
1246
- createVwExitPaneCloser,
1247
- installVwLifecycleHandlers,
1248
- } from './windowing/lifecycle.js';
1249
- import { parseWindowCompanionSlash } from './windowing/companion-slash.js';
1250
- import { createVisibilityChordHandler } from './windowing/visibility-chord.js';
1251
- import { createPaneContent } from '../virtual-windows/pane-content.js';
1252
- import type { PaneId } from '../virtual-windows/addressing.js';
1253
- import {
1254
- initDashboardVirtualWindows,
1255
- type DashboardVirtualWindows,
1256
- } from './windowing/virtual-windows.js';
1193
+ import { resolveTerminalMoveDestination } from '../terminal-matrix/mobility.js';
1257
1194
  import { initElementObservability } from '../element-registry/index.js';
1258
1195
  import {
1259
1196
  armSessionQuickControl,
@@ -1278,8 +1215,6 @@ import {
1278
1215
  import {
1279
1216
  initDashboardApprovers,
1280
1217
  createInjectApprover,
1281
- createPaneInjectApprover,
1282
- createBroadcastApprover,
1283
1218
  createConfigSetApprover,
1284
1219
  createAcpPermissionApprover,
1285
1220
  openGenericApproval,
@@ -1303,7 +1238,6 @@ import { createDashboardCompactSurfaceAssembly } from './compact-surface-assembl
1303
1238
  import { openCompactSurfaceCatalogPopup } from './compact-surface-popup.js';
1304
1239
  import { approvalModalRouter, type ApprovalModalRouter } from '../approval-modal.js';
1305
1240
  import { routeApprovalModalKey } from './input/approval-key-router.js';
1306
- import { createWindowPickerModal } from '../window-picker-modal.js';
1307
1241
 
1308
1242
  export function routeStreamingApprovalModalKey(
1309
1243
  key: Parameters<typeof routeApprovalModalKey>[0],
@@ -1320,21 +1254,9 @@ export function routeStreamingApprovalModalKey(
1320
1254
  import {
1321
1255
  attachSurfaceToWorkspace,
1322
1256
  DASHBOARD_MAIN_WORKSPACE_ID,
1323
- workspaceOwnerIdForVirtualWindow,
1324
1257
  } from '../display/workspace-affinity.js';
1325
- import { createVwSelectorPopup } from '../virtual-windows/vw-selector-popup.js';
1326
- import { createVwLocalInputTargetPopup } from '../virtual-windows/vw-local-input-target-popup.js';
1327
- import { createVwRenameModal } from '../virtual-windows/vw-rename-modal.js';
1328
- import { spawnLLMBenchmark, MAX_BENCHMARK_PANES } from '../virtual-windows/benchmark-preset.js';
1329
- import { PROVIDERS as LLM_PROVIDERS } from '../llm.js';
1330
1258
  import { initDashboardHitl, stopDashboardHitl } from './runtime/hitl.js';
1331
- import { openDashboardWindowPickerPopup } from './window-picker-popup.js';
1332
- import { createIulSidebarShellPaneContent } from '../iul/sidebar-shell.js';
1333
1259
  import { openDashboardShellRollupPopup } from './shell-rollup-popup.js';
1334
- import {
1335
- openVwLocalInputTargetPopupLauncher,
1336
- openVwRenameModalLauncher,
1337
- } from './vw-popup-openers.js';
1338
1260
  import { expandPromptReferences } from '../prompt/references.js';
1339
1261
  import { spawnCodingAgent, CodingAgentBinaryMissing } from '../terminal/coding-agent.js';
1340
1262
  import type { TerminalSession } from '../terminal/session-registry.js';
@@ -1367,7 +1289,7 @@ import { enqueuePendingUserInput } from '../session/pending-input.js';
1367
1289
  import { createAttachmentRowMap } from '../log-pane/attachment-row-map.js';
1368
1290
  import { createAttachmentPopup, type AttachmentPopupAction } from '../log-pane/attachment-popup.js';
1369
1291
  import { findLogMatches, type LogSearchResult } from '../log-pane/search.js';
1370
- import { createLogSearchModal } from '../log-pane/search-modal.js';
1292
+ import { createLogSearchModal, type LogSearchModalOpts } from '../log-pane/search-modal.js';
1371
1293
  // U-4.3 · direct imports of tryAttachmentHitAtBodyRow /
1372
1294
  // tryHandleLogAreaClick are gone · wd-log.onMouse owns the hit-test.
1373
1295
  // LogClickDispatchDeps type still needed for buildLogClickDeps +
@@ -1379,7 +1301,7 @@ import {
1379
1301
  import { fileColor, fileIcon, sizeStr, dirColor } from '../panes/file-icons.js';
1380
1302
  import { canPreview } from '../panes/preview-pane.js';
1381
1303
  import { colorLine } from '../panes/syntax-color.js';
1382
- import { createHud, setSegment, clearSegment, renderHud } from '../panes/hud.js';
1304
+ import { createHud, setSegment, clearSegment } from '../panes/hud.js';
1383
1305
  import {
1384
1306
  paneHeightBeforeTargetRow,
1385
1307
  paneHeightForTargetRows,
@@ -1429,11 +1351,9 @@ import { initWidgetInspectorTools } from '../skills/tools/widget-inspector.js';
1429
1351
  import {
1430
1352
  initDashboardViewTools,
1431
1353
  } from '../skills/tools/dashboard-view.js';
1432
- import { initSpawnCodingAgentInVW } from '../skills/tools/spawn-coding-agent-vw.js';
1433
1354
  import {
1434
1355
  findLiveSessionById,
1435
1356
  findLiveSessionByPaneId,
1436
- initSpawnEmbodiedAgentInVW,
1437
1357
  listLiveEmbodiedSessions,
1438
1358
  } from '../agent/spawn-embodied-agent-in-vw.js';
1439
1359
  import { findSessionObserver } from '../agent/observer-registry.js';
@@ -1496,7 +1416,7 @@ import { buildAstGrepHostTool } from '../skills/tools/ast-grep.js';
1496
1416
  import { buildLspHostTool } from '../skills/tools/lsp/index.js';
1497
1417
  import { PluginHost } from '../plugins/core/host.js';
1498
1418
  import { registerDashboardPaneHostTools } from './panes/host-tools.js';
1499
- import { DEFAULT_THEME_TOKENS, mergeThemeTokens, resolveThemeTokens, type ThemeTokenInput } from '../theme/tokens.js';
1419
+ import { DEFAULT_THEME_TOKENS, mergeThemeTokens, resolveThemeTokens, type ThemeTokenInput, type ThemeTokens } from '../theme/tokens.js';
1500
1420
  import { resolveActiveTheme, setActivePresetInConfig } from './render/theme-resolver.js';
1501
1421
  import { getTheme, listThemes } from '../themes/index.js';
1502
1422
  import type { PaneFocus, PreviewSource, WorkingDirView } from '../workspace-types.js';
@@ -1525,9 +1445,8 @@ import {
1525
1445
  resolveBrowserActionContextFromHit,
1526
1446
  } from '../browser-pane/actions.js';
1527
1447
  import { resolveBrowserTransferCapability } from '../browser-pane/capabilities.js';
1528
- import { createBrowserPaneContent, openBrowserPaneModal } from '../browser-pane/mount.js';
1448
+ import { openBrowserPaneModal } from '../browser-pane/mount.js';
1529
1449
  import { openPreviewPaneModal } from '../preview-pane/mount.js';
1530
- import { createPreviewPaneContent } from '../preview-pane/mount.js';
1531
1450
  import {
1532
1451
  cyclePreviewSourceForView,
1533
1452
  formatPreviewSourceLabel,
@@ -1558,7 +1477,6 @@ import {
1558
1477
  pruneSurfaceCatalogForCompactMode,
1559
1478
  productCompactModeForViewport,
1560
1479
  } from '../views/product-compact-mode.js';
1561
- import { isDashboardHeavyFeatureEnabled, resolveDashboardUiMode, type DashboardUiMode } from '../views/ui-mode.js';
1562
1480
  import { readScopedRenderLogs, resolveRenderSuppressed, readScopedDebugLevel, resolveStartupDebugLevel, hotPathGateOpen } from '../mss/logging/scoped-level.js';
1563
1481
  import { resolveCurrentInstance } from '../instance/current.js';
1564
1482
  import { createPushLog } from '../push-log.js';
@@ -1658,34 +1576,16 @@ import {
1658
1576
  TERMINAL_BACKGROUND_STATES,
1659
1577
  withBgApprovalSignals,
1660
1578
  } from '../acp/background-manager.js';
1661
- import { dispatchAcpSessionJoin, dispatchAcpSessionResume } from '../skills/tools/acp-session.js';
1662
- // PLAN-tui-redundancy-cleanup T1 (2026-05-16) — ACP resident window
1663
- // 자산은 사용자 미사용 명시로 trim. `acp-shell` pane kind 자체는
1664
- // resident-shell.ts (ACP Browser · ACP History) 가 의존하므로 보존 ·
1665
- // vw-join-bridge / vw-live-bridge / resident-vw 의 boot init + resident
1666
- // auto-mount 만 제거.
1667
- import { createAcpResidentShellPaneContent } from '../acp/resident-shell.js';
1668
- import { createSimShellPaneContent } from './sim-shell.js';
1579
+ import { dispatchAcpSessionJoin } from '../skills/tools/acp-session.js';
1669
1580
  import {
1670
1581
  listDashboardSimulationScenarios,
1671
1582
  runDashboardSimulationScenario,
1672
1583
  } from './sim-shell-runtime.js';
1673
- import {
1674
- bootSimResidentWindow,
1675
- focusOrSpawnSimResidentWindow,
1676
- isSimResidentEnabled,
1677
- } from '../sim/resident-vw.js';
1678
- import {
1679
- bootIulResidentWindow,
1680
- focusOrSpawnIulResidentWindow,
1681
- isIulForegroundStartupEnabled,
1682
- isIulResidentEnabled,
1683
- } from '../iul/resident-vw.js';
1684
1584
  import { preservesHostChromeInput } from '../display/host-chrome-profile.js';
1685
1585
  // H3 #6 follow-up #1 — auto-persist on turn-end. Subscribes DRM +
1686
1586
  // BG managers to the H2 #5 persistence primitive so
1687
1587
  // `AcpSessionList` returns real records instead of empty arrays.
1688
- // Mode controlled by ELANOUS_ACP_PERSIST_MODE env (default 'auto').
1588
+ // Mode is always 'auto' (the ELANOUS_ACP_PERSIST_MODE off-switch was graduated 2026-09-26).
1689
1589
  import { wireAutoPersist } from '../acp/auto-persist.js';
1690
1590
  import { globalAcpSessionPersistence } from '../acp/session-persistence.js';
1691
1591
  import { AgentStatusStore } from '../agent-status/store.js';
@@ -1752,10 +1652,6 @@ export interface ShowDashboardOptions {
1752
1652
  * input). Useful on tablets where the 3-pane grid is too narrow
1753
1653
  * to read. Toggle at runtime with /chat or the chord in the HUD. */
1754
1654
  chatOnly?: boolean;
1755
- /** TUI 부활 T0 — CLI `--rich`. 이번 실행만 uiMode 를 rich 로 강제
1756
- * (config 무변). essential 이 기본이 된 뒤 기존 full dashboard 로
1757
- * 들어가는 탈출구. 해석은 views/ui-mode.ts resolveDashboardUiMode. */
1758
- rich?: boolean;
1759
1655
  /** Flip code-edit policy to unsupervised at boot — every Edit/Write
1760
1656
  * from the LLM applies without an approval modal. Equivalent to
1761
1657
  * typing `/code-edit policy unsupervised` right after launch.
@@ -1771,15 +1667,8 @@ export interface ShowDashboardOptions {
1771
1667
  * (resolved in `main()`). Tool dispatch + history live on the
1772
1668
  * daemon side; the local TUI is a thin client. */
1773
1669
  remote?: { url: string; token?: string; label?: string };
1774
- /** M1.5 A.3 — local daemon counterpart of `remote`. When set, the
1775
- * dashboard's ACP session attaches to a running local daemon over
1776
- * unix socket instead of booting an in-process server. Driven by
1777
- * `ELANOUS_USE_DAEMON=1` env (resolved in `main()`). `ELANOUS_NO_DAEMON=1`
1778
- * force-disables even when a daemon is alive. `remote` (above)
1779
- * takes precedence when both are somehow set. */
1780
- localDaemon?: { socketPath: string };
1781
- /** Tier 1 daemon-resume — when set together with `remote` /
1782
- * `localDaemon`, the dashboard attaches to this existing
1670
+ /** Tier 1 daemon-resume — when set together with `remote`,
1671
+ * the dashboard attaches to this existing
1783
1672
  * daemon-side sessionId instead of minting a new one. The boot
1784
1673
  * also fetches the session's prior turns via REST (when an HTTP
1785
1674
  * endpoint is reachable, derived from `remote.url`) and replays
@@ -1901,18 +1790,16 @@ export function renderDashboardCoordinatorModalOverlay(display: Pick<DisplayCoor
1901
1790
  * ⭐⭐⭐ **essential 은 커서 값을 Z축에 묻지 않는다**(2026-08-02 · RFC essential-z-axis-off).
1902
1791
  * 입력 루프가 끝나면 `setCursor(null)` 로 소유가 비므로 물어보면 빈손이고, 그래서 턴이 도는
1903
1792
  * 동안 커서가 사라졌다(라이브 14 표본 전부 `visible false`). ⇒ 모달이 **실제로 claim** 했으면
1904
- * 그 값을 쓰고, 아니면 **프롬프트 caret** 을 쓴다. 프롬프트가 감춰졌으면 숨긴다.
1905
- * ⚠️ rich 는 Z축을 실제로 쓰므로 종전 동작(조율자가 정한 값)을 그대로 둔다. */
1793
+ * 그 값을 쓰고, 아니면 **프롬프트 caret** 을 쓴다. 프롬프트가 감춰졌으면 숨긴다. */
1906
1794
  export interface DashboardFrameInput {
1907
1795
  overlay: string;
1908
1796
  force: boolean;
1909
- essential: boolean;
1910
1797
  /** ⭐ Z축이 **실제로 누구에게** 커서를 준 결정. essential 은 `modal`·`terminal` 일 때만
1911
1798
  * 그 값에 양보한다 — `coordinator` fallback 은 소유자가 없다는 뜻이지 claim 이 아니다. */
1912
1799
  cursorOwner: CursorDecision['owner'];
1913
1800
  claimedCursor: CursorState | null;
1914
- /** rich 전용 — `#6528` 까지의 동작을 그대로 보존한다(조율자가 emit 하던 값). */
1915
- coordinatorCursor: CursorState | null;
1801
+ /** essential 결정이 커서를 안 낼 때(null)의 폴백 — 조율자가 emit 하던 값(종전 동작 보존). */
1802
+ coordinatorCursor?: CursorState | null;
1916
1803
  promptCaret: CursorState | null;
1917
1804
  suppressPromptArea: boolean;
1918
1805
  onEssentialFrameCursorDecision?: (observation: EssentialFrameCursorObservation) => void;
@@ -1948,31 +1835,27 @@ export function createDashboardEssentialCursorObserver(deps: DashboardEssentialC
1948
1835
 
1949
1836
  export function renderDashboardFrame(
1950
1837
  lines: string[],
1951
- input: DashboardFrameInput & { onEssentialFrameCursorDecision?: (observation: EssentialFrameCursorObservation) => void },
1838
+ input: DashboardFrameInput,
1952
1839
  renderFrame: DashboardFrameRenderer = render,
1953
1840
  ): void {
1954
1841
  const cursorDecisionExists = input.cursorOwner !== 'none';
1955
- const essentialDecision = input.essential
1956
- ? resolveEssentialFrameCursorDecision({
1957
- claimedCursor: cursorDecisionExists ? input.claimedCursor : null,
1958
- promptCaret: input.promptCaret,
1959
- suppressPromptArea: input.suppressPromptArea,
1960
- })
1961
- : null;
1842
+ const essentialDecision = resolveEssentialFrameCursorDecision({
1843
+ claimedCursor: cursorDecisionExists ? input.claimedCursor : null,
1844
+ promptCaret: input.promptCaret,
1845
+ suppressPromptArea: input.suppressPromptArea,
1846
+ });
1962
1847
  renderFrame(lines, {
1963
1848
  overlay: input.overlay,
1964
1849
  force: input.force,
1965
- cursor: paintCursor(essentialDecision?.cursor ?? input.coordinatorCursor),
1850
+ cursor: paintCursor(essentialDecision.cursor ?? input.coordinatorCursor ?? null),
1851
+ });
1852
+ input.onEssentialFrameCursorDecision?.({
1853
+ caret: essentialDecision.cursor,
1854
+ cursorOwner: input.cursorOwner,
1855
+ reason: essentialDecision.reason,
1856
+ source: essentialDecision.source,
1857
+ suppressPromptArea: input.suppressPromptArea,
1966
1858
  });
1967
- if (essentialDecision) {
1968
- input.onEssentialFrameCursorDecision?.({
1969
- caret: essentialDecision.cursor,
1970
- cursorOwner: input.cursorOwner,
1971
- reason: essentialDecision.reason,
1972
- source: essentialDecision.source,
1973
- suppressPromptArea: input.suppressPromptArea,
1974
- });
1975
- }
1976
1859
  }
1977
1860
 
1978
1861
  /** Production frame input: append coordinator modals after every dashboard-owned overlay. */
@@ -2175,6 +2058,47 @@ export async function dispatchDashboardAdSlash(
2175
2058
  afterRun();
2176
2059
  }
2177
2060
 
2061
+ export function createDashboardLogSearchOpener(deps: {
2062
+ display: () => DisplayCoordinator;
2063
+ linesGetter: LogSearchModalOpts['linesGetter'];
2064
+ initialQuery: () => string;
2065
+ termSize: () => { cols: number; rows: number };
2066
+ computePaneH: (rows: number) => number;
2067
+ currentThemeTokens: () => ThemeTokens;
2068
+ currentWorkspaceOwnerId: () => string;
2069
+ draw: () => void;
2070
+ onJump: LogSearchModalOpts['onJump'];
2071
+ onCancel: NonNullable<LogSearchModalOpts['onCancel']>;
2072
+ }): () => void {
2073
+ return () => {
2074
+ let modalHandle: { dispose(): void } | null = null;
2075
+ const closeLogSearchModal = (): void => { modalHandle?.dispose(); modalHandle = null; };
2076
+ const { cols: tc, rows: tr } = deps.termSize();
2077
+ const anchorRow = Math.max(1, deps.computePaneH(tr) + 2);
2078
+ const anchorCol = Math.max(2, Math.floor(tc / 4));
2079
+ const popup = createLogSearchModal({
2080
+ linesGetter: deps.linesGetter,
2081
+ initialQuery: deps.initialQuery(),
2082
+ termCols: tc,
2083
+ termRows: tr,
2084
+ anchorRow,
2085
+ anchorCol,
2086
+ theme: deps.currentThemeTokens(),
2087
+ onJump: (result, query) => {
2088
+ closeLogSearchModal();
2089
+ deps.onJump(result, query);
2090
+ },
2091
+ onCancel: query => {
2092
+ closeLogSearchModal();
2093
+ deps.onCancel(query);
2094
+ },
2095
+ });
2096
+ attachSurfaceToWorkspace(popup.surface, deps.currentWorkspaceOwnerId());
2097
+ modalHandle = deps.display().pushModal(popup.surface);
2098
+ deps.draw();
2099
+ };
2100
+ }
2101
+
2178
2102
  export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<DashboardAction> {
2179
2103
  const dashboardQuitConfirmationState = createQuitConfirmationState();
2180
2104
  const signalQuitConfirmationState = createQuitConfirmationState();
@@ -2196,7 +2120,6 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2196
2120
  forceQuit: true,
2197
2121
  });
2198
2122
  });
2199
- let virtualWindows!: DashboardVirtualWindows;
2200
2123
  // E3 (§7.4 TS baseline reduction · 2026-05-17) — forward declarations
2201
2124
  // for handler functions that get assigned later via destructure from
2202
2125
  // createDashboardCopyRuntime / createDashboardMediaRuntime (lines
@@ -2404,10 +2327,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2404
2327
  * same file isn't re-loading twice. Repeats inside the same call
2405
2328
  * (same id appearing twice in `added`) are also collapsed.
2406
2329
  *
2407
- * Image attachments push a chafa-rendered preview into the
2408
- * detail viewer (set via setScratchImage) so the user sees what
2409
- * they grabbed without an intrusive modal — only on the first
2410
- * appearance to keep `(already attached)` cases quiet. */
2330
+ * Image attachments stay in the chat log without a scratch preview. */
2411
2331
  const renderAttachmentSummary = (added: { attachment: Attachment; isNew: boolean }[]): void => {
2412
2332
  renderDashboardAttachmentSummary(added, {
2413
2333
  formatNewLine: (attachment) =>
@@ -2419,16 +2339,6 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2419
2339
  return chatLines.length - 1;
2420
2340
  },
2421
2341
  trackRow: (row, attachmentId) => { attachmentRowMap.track(row, attachmentId); },
2422
- // TUI 부활 T4 — attach 시 detail viewer(scratch) 프리뷰 push 는
2423
- // rich 전용. essential 은 chat log 의 attachment summary 줄만
2424
- // (codex 패리티 · dogfood: @픽커 Tab attach 가 프리뷰 팝업을 띄워
2425
- // 흐름을 끊는다는 피드백). 런타임 /ui 전환을 따르도록 호출 시점 게이트.
2426
- setScratchImage: async (...args: Parameters<typeof setScratchImage>) => {
2427
- if (isDashboardHeavyFeatureEnabled(dashboardUiMode, 'scratch-image')) await setScratchImage(...args);
2428
- },
2429
- setScratchFile: (...args: Parameters<typeof setScratchFile>) => {
2430
- if (isDashboardHeavyFeatureEnabled(dashboardUiMode, 'scratch-file')) setScratchFile(...args);
2431
- },
2432
2342
  });
2433
2343
  };
2434
2344
 
@@ -2572,7 +2482,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2572
2482
  });
2573
2483
  },
2574
2484
  foldMode: logFoldMode,
2575
- expandHint: dashboardUiMode === 'rich',
2485
+ expandHint: false,
2576
2486
  });
2577
2487
 
2578
2488
  // Session-scoped skill decline memory (session 21). When the user
@@ -2633,32 +2543,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2633
2543
  pushDebugLine(C.muted(` \u251c\u2500 `) + C.text(att.token) + ' ' + C.subtext('(already attached)'));
2634
2544
  }
2635
2545
  clearSegment(hud, 'clipimg');
2636
- // Push a chafa preview into the detail viewer — only on
2637
- // first registration so repeat Ctrl+V on the same clipboard
2638
- // doesn't churn the pane. TUI 부활 T4 — rich 전용 (essential 은
2639
- // summary 줄 + [Image #N] 토큰만 · codex 패리티).
2640
- if (isNew && isDashboardHeavyFeatureEnabled(dashboardUiMode, 'scratch-image')) void setScratchImage(att.sourcePath, att.filename);
2641
- // P3 — also pop a transient centered modal so the user gets
2642
- // immediate confirmation that the paste landed (scratch pane
2643
- // may be out of view on narrow layouts). 2.5s TTL, replaces
2644
- // any prior image modal in flight.
2645
- //
2646
- // Clipboard confirmation preview: routes through showPreviewModal
2647
- // (previewFile → handler pipeline). Identical result for PNG
2648
- // clips; forward-compat for non-image clipboard drops (PDFs /
2649
- // SVGs / archives) once the extractor grows those types.
2650
- // TUI 부활 T4 — 확인용 transient 모달도 rich 전용. essential 은
2651
- // attach summary 줄이 이미 즉시 확인을 제공한다.
2652
- if (isNew && isDashboardHeavyFeatureEnabled(dashboardUiMode, 'clipboard-preview-modal')) {
2653
- const { rows: tr, cols: tc } = termSize();
2654
- void showPreviewModal(att.sourcePath, {
2655
- coordinator: display,
2656
- termCols: tc,
2657
- termRows: tr,
2658
- title: att.filename,
2659
- ttlMs: 2500,
2660
- }).catch(() => { /* modal render failure is non-fatal */ });
2661
- }
2546
+ // The attachment summary confirms the paste without a scratch preview or modal.
2662
2547
  return `${att.token} `;
2663
2548
  } catch (err: any) {
2664
2549
  // grabClipboardImage now throws with the osascript diagnostic
@@ -2675,19 +2560,21 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2675
2560
  }
2676
2561
  };
2677
2562
 
2678
- const renderContextList = (): void => {
2563
+ const renderContextList = (): string[] => {
2679
2564
  const items = ctxList(contextRegistry);
2565
+ const lines: string[] = [];
2566
+ const output = (line: string): void => { pushDebugLine(line); lines.push(line); };
2680
2567
  pushDebugBlank();
2681
- pushDebugLine(C.accent('\u276f /context'));
2568
+ output(C.accent('\u276f /context'));
2682
2569
  if (!items.length) {
2683
- pushDebugLine(C.muted(' (empty)'));
2684
- return;
2570
+ output(C.muted(' (empty)'));
2571
+ return lines;
2685
2572
  }
2686
2573
  const totalBytes = ctxTotalBytes(contextRegistry);
2687
2574
  const total = fmtBytes(totalBytes);
2688
- pushDebugLine(C.muted(` ${items.length} items, ${total} total`));
2575
+ output(C.muted(` ${items.length} items, ${total} total`));
2689
2576
  if (totalBytes > CTX_WARN_BYTES) {
2690
- pushDebugLine(C.warning(` ⚠ ${total} exceeds 10MB — consider /context clear big`));
2577
+ output(C.warning(` ⚠ ${total} exceeds 10MB — consider /context clear big`));
2691
2578
  }
2692
2579
 
2693
2580
  // Table columns: ✓ id kind filename(trunc) size [source-tail]
@@ -2695,7 +2582,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2695
2582
  // left indent; filename gets the slack column so long paths just clip.
2696
2583
  const { cols } = termSize();
2697
2584
  const filenameMax = Math.max(18, cols - 36);
2698
- pushDebugLine(C.muted(
2585
+ output(C.muted(
2699
2586
  ` ${' '}${'id'.padEnd(4)}${'kind'.padEnd(7)}${'file'.padEnd(filenameMax)}${'size'.padStart(8)}`
2700
2587
  ));
2701
2588
  for (const a of items) {
@@ -2704,8 +2591,9 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2704
2591
  const kindCol = (KIND_DISPLAY[a.kind] ?? a.kind).padEnd(7);
2705
2592
  const file = ellipsize(a.filename, filenameMax).padEnd(filenameMax);
2706
2593
  const size = fmtBytes(a.sizeBytes).padStart(8);
2707
- pushDebugLine(` ${mark} ${C.muted(idCol)}${C.muted(kindCol)}${C.text(file)}${C.subtext(size)}`);
2594
+ output(` ${mark} ${C.muted(idCol)}${C.muted(kindCol)}${C.text(file)}${C.subtext(size)}`);
2708
2595
  }
2596
+ return lines;
2709
2597
  };
2710
2598
 
2711
2599
  const chat = createChatState(
@@ -2863,40 +2751,30 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2863
2751
  const target = logSearchResults[logSearchCursor]!;
2864
2752
  scrollToSearchResult(target);
2865
2753
  };
2866
- const openLogSearchModal = (): void => {
2867
- const { cols: tc, rows: tr } = termSize();
2868
- const pH = computePaneH(tr);
2869
- // Anchor near the top of the log pane for predictable placement.
2870
- const anchorRow = Math.max(1, pH + 2);
2871
- const anchorCol = Math.max(2, Math.floor(tc / 4));
2872
- const popup = createLogSearchModal({
2873
- linesGetter: () => chatLines,
2874
- initialQuery: logSearchQuery,
2875
- termCols: tc,
2876
- termRows: tr,
2877
- anchorRow,
2878
- anchorCol,
2879
- theme: currentThemeTokens(),
2880
- onJump: (result, query) => {
2881
- applyLogSearch(query);
2882
- // Position cursor on the picked result. applyLogSearch reset
2883
- // it to 0; advance to the one the user actually picked.
2884
- const idx = logSearchResults.findIndex(r => r.lineIdx === result.lineIdx);
2885
- logSearchCursor = idx >= 0 ? idx : 0;
2886
- scrollToSearchResult(result);
2887
- draw();
2888
- },
2889
- onCancel: (query) => {
2890
- // Keep the query so n/N still works after cancel — tmux
2891
- // `copy-mode -s` convention.
2892
- if (query.trim()) applyLogSearch(query);
2893
- draw();
2894
- },
2895
- });
2896
- attachSurfaceToWorkspace(popup.surface, currentWorkspaceOwnerId());
2897
- display.pushModal(popup.surface);
2898
- draw();
2899
- };
2754
+ const openLogSearchModal = createDashboardLogSearchOpener({
2755
+ display: () => display,
2756
+ linesGetter: () => chatLines,
2757
+ initialQuery: () => logSearchQuery,
2758
+ termSize,
2759
+ computePaneH: rows => computePaneH(rows),
2760
+ currentThemeTokens: () => currentThemeTokens(),
2761
+ currentWorkspaceOwnerId: () => currentWorkspaceOwnerId(),
2762
+ draw: () => draw(),
2763
+ onJump: (result, query) => {
2764
+ applyLogSearch(query);
2765
+ // Position cursor on the picked result. applyLogSearch reset
2766
+ // it to 0; advance it to the result the user actually picked.
2767
+ const idx = logSearchResults.findIndex(r => r.lineIdx === result.lineIdx);
2768
+ logSearchCursor = idx >= 0 ? idx : 0;
2769
+ scrollToSearchResult(result);
2770
+ draw();
2771
+ },
2772
+ onCancel: query => {
2773
+ // Keep the query so n/N still works after cancel — tmux convention.
2774
+ if (query.trim()) applyLogSearch(query);
2775
+ draw();
2776
+ },
2777
+ });
2900
2778
 
2901
2779
  // TUI 부활 S-a — /resume 세션 픽커 (codex 패리티). chat 세션 스토어를
2902
2780
  // 필터형 모달로 띄우고, 선택 시 기존 `/session load <id>` slash 를
@@ -2920,7 +2798,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2920
2798
  draw();
2921
2799
  return;
2922
2800
  }
2923
- // C-d-1 — essential 은 composer 자리(하단 슬롯) · rich 는 중앙 플로팅.
2801
+ // Pickers share the composer bottom slot.
2924
2802
  const placement = resolveDecisionPickerPlacement({ height: 16 });
2925
2803
  const modalWidth = placement.width;
2926
2804
  const fmtRow = (m: (typeof rows)[number]): string => {
@@ -2979,12 +2857,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2979
2857
  browserMode?: 'all' | 'files' | 'turns',
2980
2858
  ): Promise<boolean> => {
2981
2859
  const diffCfg = getUserConfig().chat.rendering.diff;
2982
- if (!diffCfg.turnBrowser) {
2983
- pushDebugLine(C.muted(' source delta browser disabled by chat.rendering.diff.turnBrowser=false'));
2984
- chatScrollOffset = -1;
2985
- draw();
2986
- return false;
2987
- }
2988
2860
  const { getSourceDeltaManager, createSourceDeltaBrowserPopup } = await import('../code-edit/index.js');
2989
2861
  const turns = mode === 'latest'
2990
2862
  ? getSourceDeltaManager().recentTurns(1)
@@ -3284,7 +3156,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3284
3156
  })
3285
3157
  .reverse(); // 최신 턴 먼저
3286
3158
  const items: SearchItem[] = turnItems;
3287
- // C-d-1 — essential 은 composer 자리(하단 슬롯) · rich 는 중앙 플로팅.
3159
+ // Pickers share the composer bottom slot.
3288
3160
  const placement = resolveDecisionPickerPlacement({ height: 16 });
3289
3161
  const modalWidth = placement.width;
3290
3162
  const handle = createSearchModal({
@@ -3350,7 +3222,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3350
3222
  ...HITL_REVISE_PRESETS.map((p): SearchItem => ({ label: `${p.label} — 재분해`, payload: `preset:${p.action}` })),
3351
3223
  { label: '✏️ 직접입력 — 정정 코멘트 작성', payload: 'custom' },
3352
3224
  ];
3353
- // C-d-1 — essential 은 composer 자리(하단 슬롯) · rich 는 중앙 플로팅.
3225
+ // Pickers share the composer bottom slot.
3354
3226
  const placement = resolveDecisionPickerPlacement({ height: 14 });
3355
3227
  const modalWidth = placement.width;
3356
3228
  const handle = createSearchModal({
@@ -3681,33 +3553,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3681
3553
  let lastAssistantRaw: string | null = null;
3682
3554
  let lastAssistantRange: { start: number; end: number } | null = null;
3683
3555
  let lastAssistantMode: 'rendered' | 'raw' = 'rendered';
3684
- // Vim/tmux-style log pane resize. 0 = default 6:3:1 split; positive
3685
- // grows the log at the expense of the 3-pane grid, negative shrinks
3686
- // the log. Clamped inside computeVLayout so paneH >= 5 and logH >= 3.
3556
+ // Log-pane resize remains available when chatOnlyMode is toggled off.
3687
3557
  let logHeightBias = 0;
3688
- // TUI 부활 T0 (PLAN-tui-revival-essentials-2026-07-12) — 단일 UI 모드
3689
- // 축. essential(기본) = codex/claude-code 패리티 chat-first; rich =
3690
- // 기존 full dashboard. `--rich` CLI > config `dashboard.uiMode` >
3691
- // 레거시 `defaultMode` 매핑 > 기본 essential. T1(chrome 게이트)·
3692
- // T3(VW 게이트)가 이 값을 소비한다. `let` — T2 의 `/ui` 런타임
3693
- // 전환이 재할당.
3694
- let dashboardUiMode = resolveDashboardUiMode({
3695
- cliRich: opts.rich,
3696
- configUiMode: getUserConfig().dashboard.uiMode,
3697
- legacyDefaultMode: getUserConfig().dashboard.defaultMode,
3698
- });
3699
3558
  // Chat-only mode: 3-pane grid collapses, log pane owns all space above
3700
3559
  // the input. Toggled by /chat or /dashboard. Input loop stays alive
3701
3560
  // across Escape while this is true — feels like a dedicated LLM REPL.
3702
- // Seeded from the launch flag so `elanous --chat-only` / `elanous --debug`
3703
- // lands straight in this layout (tablet-friendly — debug events need
3704
- // screen real estate). T0 이후 essential uiMode 가 기본 시드 —
3705
- // essential 이면 chat 전체화면으로 부팅한다 (레거시
3706
- // `dashboard.defaultMode: 'chat'` 은 uiMode 해석에 흡수됨).
3707
- let chatOnlyMode = opts.chatOnly === true
3708
- || opts.debug === true
3709
- || opts.benchmark === true
3710
- || dashboardUiMode === 'essential';
3561
+ // Essential layout starts chat-only; /chat and /dashboard still toggle it.
3562
+ let chatOnlyMode = true;
3711
3563
 
3712
3564
  // Debug log — auto-start forensic file capture so every run has a
3713
3565
  // tail-able JSONL trail without the user thinking about it. Default
@@ -3739,12 +3591,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3739
3591
  debug.setFileEnabled(dbgCfg.file);
3740
3592
  // OH9 — 렌더 로그 무음 시드(레벨과 직교). 우선순위:
3741
3593
  // level.json.render(명시) > config.debug.renderLogs(true=override) >
3742
- // uiMode essential(억제). essential 부팅이면 렌더 노이즈를 자동으로
3594
+ // essential 기본 억제. 부팅 시 렌더 노이즈를 자동으로
3743
3595
  // 끄고, 대표가 diag 로 올려도 진단은 그대로 흐른다.
3744
3596
  debug.setRenderSuppressed(resolveRenderSuppressed({
3745
3597
  scopedRender: readScopedRenderLogs(),
3746
3598
  configRenderLogs: dbgCfg.renderLogs,
3747
- uiModeEssential: dashboardUiMode === 'essential',
3599
+ defaultSuppressed: true,
3748
3600
  }));
3749
3601
  // Announce the path so the user knows where to tail. Prefixed
3750
3602
  // with (muted) so it doesn't compete with real chat output.
@@ -3771,9 +3623,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3771
3623
  debugScrollOffset = -1;
3772
3624
  mirrorDrawThrottle.request();
3773
3625
  });
3774
- // --debug launch flag: force both sinks ON and announce it in the
3775
- // log pane. The caller has already set chatOnlyMode=true above so
3776
- // the mirror feed is actually visible.
3626
+ // --debug launch flag: force both sinks ON and announce it in the log pane.
3777
3627
  if (opts.debug) {
3778
3628
  debug.setFileEnabled(true);
3779
3629
  debug.enable();
@@ -3837,14 +3687,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3837
3687
  // ── Plugin host (Phase 2) ──
3838
3688
  const hud = createHud();
3839
3689
 
3840
- // FU-3 (2026-05-05) — wire goal + plan-mode HUD pills. Subscribes
3841
- // to both registries so the status reflects state changes without
3842
- // dashboard polling. Idempotent on attach (paints current state).
3843
- void (async () => {
3844
- const { wireGoalPlanHudBridge } = await import('./goal-plan-hud-bridge.js');
3845
- wireGoalPlanHudBridge({ hud, draw: () => { try { draw(); } catch { /* draw not yet defined during boot */ } } });
3846
- })();
3847
-
3848
3690
  // Reasoning HUD segment — provider-agnostic emoji + label that
3849
3691
  // sits in the top transient HUD row (next to voice / streaming
3850
3692
  // indicators), NOT the bottom status bar. Capability-driven: pill
@@ -3929,42 +3771,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3929
3771
  const toggleChatOnlyLayout = (opts: { announce?: boolean } = {}): void => {
3930
3772
  setChatOnlyLayout(!chatOnlyMode, opts);
3931
3773
  };
3932
- // TUI 부활 T2 — /ui 런타임 전환. uiMode 재할당 + chat-only 동기 +
3933
- // config persist(dashboard.uiMode). T1 chrome 게이트(·T3 VW 게이트)가
3934
- // draw 마다 dashboardUiMode 를 읽으므로 재할당만으로 다음 프레임 반영.
3935
- // draw 는 아래에서 늦게 바인딩되는 let — 슬래시 시점엔 실체 존재.
3936
- const applyDashboardUiMode = (
3937
- mode: DashboardUiMode,
3938
- applyOpts: { persist?: boolean; announce?: boolean } = {},
3939
- ): void => {
3940
- dashboardUiMode = mode;
3941
- setChatOnlyLayout(mode === 'essential');
3942
- // OH9 — /ui 전환 즉시 렌더 무음 시드 재해석(전환이 바로 반영되게).
3943
- // level.json.render / config.renderLogs 명시가 있으면 uiMode 보다 우선
3944
- // (resolveRenderSuppressed) — /ui essential 이 사용자 override 를 안 덮는다.
3945
- debug.setRenderSuppressed(resolveRenderSuppressed({
3946
- scopedRender: readScopedRenderLogs(),
3947
- configRenderLogs: getUserConfig().debug.renderLogs,
3948
- uiModeEssential: mode === 'essential',
3949
- }));
3950
- if (applyOpts.announce) {
3951
- chatLines.push(C.muted(mode === 'essential'
3952
- ? '-- ui: essential (chat 전체화면) — /ui rich 로 전체 대시보드 --'
3953
- : '-- ui: rich (full dashboard) — /ui essential 로 복귀 --'));
3954
- chatScrollOffset = -1;
3955
- }
3956
- if (applyOpts.persist) {
3957
- try {
3958
- const cfg = getUserConfig();
3959
- cfg.dashboard.uiMode = mode;
3960
- saveUserConfig(cfg);
3961
- } catch {
3962
- chatLines.push(C.warning(' ui mode config persist 실패 — 이번 세션만 적용'));
3963
- }
3964
- }
3965
- resetRenderCache();
3966
- draw();
3967
- };
3968
3774
  // Mirror chatOnly launch flag into the HUD pill (same style /chat
3969
3775
  // toggle uses) so the user sees "mode: chat" from the first frame.
3970
3776
  if (chatOnlyMode) setSegment(hud, 'mode', C.info('mode: chat'), 10);
@@ -4004,25 +3810,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4004
3810
  }
4005
3811
  : {};
4006
3812
  const liveShells = listPty().filter(h => h.isAlive()).length;
4007
- // VW-U1 — foreground VW summary for the 🪟 pill. vwSegment hides
4008
- // itself when there's only one single-pane window (nothing worth
4009
- // showing). When there's no foreground at all we pass null.
4010
- let vwSummary: Parameters<typeof mouseWiring.buildStatusLine>[0]['vw'] = null;
4011
- try {
4012
- const current = virtualWindows.registry.current();
4013
- const list = virtualWindows.registry.list();
4014
- if (current) {
4015
- const panes = current.listPanes();
4016
- const focusedIdx = panes.findIndex(p => p.id === current.focused);
4017
- vwSummary = {
4018
- windowId: current.id,
4019
- paneIdx: focusedIdx >= 0 ? focusedIdx + 1 : 1,
4020
- paneTotal: panes.length,
4021
- windowTotal: list.length,
4022
- zoomed: current.isZoomed(),
4023
- };
4024
- }
4025
- } catch { /* vw not yet initialised */ }
4026
3813
  pruneConversationPopupHost();
4027
3814
  const conversationSnapshot = conversationPopupHost.snapshot();
4028
3815
  const metrics = getSessionMetrics();
@@ -4039,7 +3826,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4039
3826
  controller: process.env.ELANOUS_CONTROLLER?.trim() || undefined,
4040
3827
  shellCount: liveShells,
4041
3828
  shellRollup: latestShellRollup ?? undefined,
4042
- vw: vwSummary,
3829
+ vw: null,
4043
3830
  conversationPopups: {
4044
3831
  liveCount: conversationSnapshot.live.length,
4045
3832
  minimizedCount: conversationSnapshot.minimized.length,
@@ -4368,9 +4155,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4368
4155
  cols: termSize().cols,
4369
4156
  syntax: true,
4370
4157
  colorTier: diffRendering.colorTier,
4371
- adaptiveBg: diffRendering.adaptiveBg,
4372
- syntaxPerHunk: diffRendering.syntaxPerHunk,
4373
- cache: diffRendering.cache,
4374
4158
  headerStyle: diffRendering.headerStyle,
4375
4159
  })
4376
4160
  .then((rows) => {
@@ -4460,6 +4244,36 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4460
4244
  } catch { /* noop */ }
4461
4245
  }, 5_000);
4462
4246
  (gitDirtyTimer as unknown as { unref?: () => void }).unref?.();
4247
+ // The running module's installed version, not the user's working directory, is the baseline.
4248
+ const dashboardCodeRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
4249
+ const versionDir = versionedDashboardInstallDir(dashboardCodeRoot);
4250
+ const staleInstallCheck = createStaleInstallCheck({
4251
+ codeRoot: dashboardCodeRoot,
4252
+ bootCommit: versionDir ? readDashboardBootCommit(versionDir) : undefined,
4253
+ readInstalledCommit: readCurrentInstalledCommit,
4254
+ now: Date.now,
4255
+ });
4256
+ if (versionDir) {
4257
+ const checkInstall = (): void => {
4258
+ const result = staleInstallCheck.check();
4259
+ debug.log('dashboard.version', 'install-check', {
4260
+ state: result.state, bootCommit: result.bootCommit, installedCommit: result.installedCommit,
4261
+ });
4262
+ if (staleInstallCheck.shouldNotify(result)) {
4263
+ chatLines.push(C.warning(`elanous 가 새 판으로 바뀌었다 (${result.bootCommit!.slice(0, 7)} → ${result.installedCommit!.slice(0, 7)}) · TUI 를 다시 띄워라`));
4264
+ chatScrollOffset = -1;
4265
+ try { draw(); } catch { /* TUI torn down */ }
4266
+ }
4267
+ };
4268
+ checkInstall();
4269
+ const installCheckTimer = setInterval(checkInstall, STALE_INSTALL_CHECK_INTERVAL_MS);
4270
+ installCheckTimer.unref?.();
4271
+ } else {
4272
+ const result = staleInstallCheck.check();
4273
+ debug.log('dashboard.version', 'install-check', {
4274
+ state: result.state, bootCommit: result.bootCommit, installedCommit: result.installedCommit,
4275
+ });
4276
+ }
4463
4277
  // Phase T1 — unified terminal matrix. Wraps sessionRegistry during
4464
4278
  // the migration window: spawns flow through `matrix.spawn()` so we
4465
4279
  // get a single `term:<N>` id space, character/transport fields,
@@ -4471,13 +4285,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4471
4285
  // tools) has one place to fan keys + clipboard across groups.
4472
4286
  // Phase T5 — terminal IPC channel bus. Fetched via getChannelBus()
4473
4287
  // here so the /term channel slashes can publish / list.
4474
- // Phase T3b-b — VW slot bookkeeping. `<windowId>/<slotId>` → PaneId
4475
- // so the adapter's removeSlot can find the right pane to close.
4476
- // Populated by installSlot, drained by removeSlot. Slot id equals
4477
- // the PaneId on the split-chord path (paneId is already globally
4478
- // unique); externally-chosen slotIds from /term move are free-form.
4479
- const vwSlotBindings = new Map<string, PaneId>();
4480
- const suppressedVwPaneKills = new Set<PaneId>();
4481
4288
  // P16 — mirror session metadata to
4482
4289
  // ~/.config/monad-agent/terminal-sessions.json on each lifecycle
4483
4290
  // change (debounced 250ms). /term resume later re-spawns from this.
@@ -4508,22 +4315,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4508
4315
  // dispatch so browser Ctrl+W / /wd retargets Bash immediately.
4509
4316
  setBashRuntimeDeps({});
4510
4317
 
4511
- // NT-C1b-2/3 — wire the 4-mode ShellRunner.
4512
- // • registry → auto-bg (15s) + TTL (5m) + ShellList/Poll/Kill backend.
4513
- // • fileEngine → inline/bg mode (spawn+pipe).
4514
- // • ptyHostFactory → NT-C1b-3 (session nt): per-label persistent
4515
- // PreviewTerminal for mode='vw' / 'modal'.
4516
- // Sequential commands in the same label
4517
- // share PTY + scrollback. VW pane binding
4518
- // itself lives in NT-C1b-4.
4318
+ // ShellRunner registry tracks handles; file capture runs inline/bg commands.
4519
4319
  {
4520
4320
  const { initShellRegistry } = await import('../shell-runner/registry.js');
4521
4321
  const { createFileCaptureEngine } = await import('../shell-runner/file-engine.js');
4522
4322
  const { setShellRunnerDeps } = await import('../shell-runner/dispatch.js');
4523
- const { createRunnerHostFactory } = await import('../shell-runner/runner-host-factory.js');
4524
- const { createExternalTerminalPaneContent } = await import('../shell-runner/external-terminal-pane.js');
4525
4323
  const { createBackgroundSurface } = await import('../shell-runner/background-surface.js');
4526
- const { registerPaneContentKind } = await import('../virtual-windows/pane-content.js');
4527
4324
  // The boot seam types the registry as the opaque `ShellRegistryLike`
4528
4325
  // stub (decoupling), while the real `wireShellRunnerSurface` takes a
4529
4326
  // concrete `ShellRegistry`. At runtime `initShellRegistry` returns the
@@ -4538,161 +4335,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4538
4335
  deps: { shellRegistry: unknown },
4539
4336
  ) => void;
4540
4337
  } catch { /* never break boot */ }
4541
- registerPaneContentKind('vw-browser', (spec: any, deps: any) =>
4542
- createBrowserPaneContent(spec, {
4543
- browserPaneRegistry: deps.browserPaneRegistry,
4544
- refreshRemoteBrowserPane: deps.refreshRemoteBrowserPane,
4545
- }),
4546
- );
4547
- registerPaneContentKind('vw-preview', (spec: any, deps: any) =>
4548
- createPreviewPaneContent(spec, {
4549
- previewPaneRegistry: deps.previewPaneRegistry,
4550
- }),
4551
- );
4552
- registerPaneContentKind('iul-shell', (spec: any) =>
4553
- createIulSidebarShellPaneContent(spec, {
4554
- iulThemePreviewControl: {
4555
- getActiveThemeName: () => currentThemeTokens().name,
4556
- previewTheme: (name: string) => {
4557
- iulThemePreviewOverride = name;
4558
- requestDashboardRender();
4559
- },
4560
- revertPreview: () => {
4561
- iulThemePreviewOverride = null;
4562
- requestDashboardRender();
4563
- },
4564
- commitTheme: (name: string) => {
4565
- iulThemePreviewOverride = null;
4566
- const cfg = getUserConfig();
4567
- cfg.dashboard.theme = { ...(cfg.dashboard.theme as object ?? {}), active: name } as never;
4568
- saveUserConfig(cfg);
4569
- requestDashboardRender();
4570
- },
4571
- },
4572
- }),
4573
- );
4574
- registerPaneContentKind('acp-shell', (spec: any) =>
4575
- createAcpResidentShellPaneContent(spec, {
4576
- runPrimaryAction: async (action, stub) => {
4577
- switch (action.id) {
4578
- case 'open-live-client-room':
4579
- case 'open-live-server-room': {
4580
- const opened = await openConversationModal(stub.id);
4581
- return opened
4582
- ? `Opened live ACP room for ${stub.id}`
4583
- : `Live ACP room not mounted for ${stub.id}`;
4584
- }
4585
- case 'promote-background-vw': {
4586
- const result = await dispatchAcpSessionJoin({ backgroundId: stub.id, promoteToVW: true });
4587
- return result.promoted && result.windowId
4588
- ? `Promoted to VW ${result.windowId}`
4589
- : 'Joined background lane';
4590
- }
4591
- case 'join-background-transcript': {
4592
- await dispatchAcpSessionJoin({ backgroundId: stub.id, promoteToVW: false });
4593
- return 'Loaded background transcript';
4594
- }
4595
- case 'resume-persisted-session': {
4596
- await dispatchAcpSessionResume({ sessionId: stub.id });
4597
- return 'Resumed persisted ACP session';
4598
- }
4599
- default:
4600
- return 'No ACP action wired for this lane';
4601
- }
4602
- },
4603
- }),
4604
- );
4605
- registerPaneContentKind('sim-shell', (spec: any) =>
4606
- createSimShellPaneContent(spec, {
4607
- seedAssistantSample: (text) => {
4608
- ({ lastAssistantRaw, lastAssistantRange, lastAssistantMode } = appendDashboardAssistantSampleOutput({
4609
- chatLines,
4610
- text,
4611
- termCols: termSize().cols,
4612
- wrapEnabled: getUserConfig().chat.rendering.wrap,
4613
- formatResponse,
4614
- renderTextLine: C.text,
4615
- }));
4616
- chatScrollOffset = -1;
4617
- draw();
4618
- },
4619
- clearAssistantSample: () => {
4620
- lastAssistantRaw = null;
4621
- lastAssistantRange = null;
4622
- lastAssistantMode = 'rendered';
4623
- chatScrollOffset = -1;
4624
- draw();
4625
- },
4626
- openLastAssistantMediaPreview: async () => {
4627
- await openLastAssistantMediaPreview();
4628
- },
4629
- getBrowserStatusLines: () => browserCdpSlashRuntime.statusLines(),
4630
- getBrowserSmokeLines: async () => browserCdpSlashRuntime.smokeLines(),
4631
- getBrowserStopLines: () => browserCdpSlashRuntime.stopLines(),
4632
- signalBus: dashboardControlSignals,
4633
- signalScope: { surface: 'dashboard-chat-main', channel: 'dashboard' },
4634
- source: { kind: 'keyboard', surface: 'dashboard-chat-main' },
4635
- }),
4636
- );
4637
4338
  bootDashboardShellRunner({
4638
4339
  createBackgroundSurface,
4639
4340
  initShellRegistry,
4640
4341
  wireShellRunnerSurface,
4641
- registerPaneContentKind,
4642
- createExternalTerminalPaneContent,
4643
- createRunnerHostFactory,
4644
4342
  createFileCaptureEngine,
4645
4343
  setShellRunnerDeps,
4646
- getSessionCwd: () => getSessionCwd(),
4647
- termSize: () => termSize(),
4648
4344
  setLatestShellRollup: (rollup) => { latestShellRollup = rollup; },
4649
- onSpawnError: (label, err) => {
4650
- // SP-E — visible failure mode. Factory already degrades to
4651
- // file engine + null return; without this the user only
4652
- // sees "no VW appeared" with no hint as to why.
4653
- const msg = err instanceof Error ? err.message : String(err);
4654
- pushDebugLine(C.warning(` [shell] spawn failed for label=${label}: ${msg}`));
4655
- pushDebugLine(C.muted(' (file engine fallback — output still captured; no VW pane)'));
4656
- chatScrollOffset = -1;
4657
- try { draw(); } catch { /* TUI torn down */ }
4658
- },
4659
- spawnVirtualWindowTerminal: (label, host) => {
4660
- const extSpec = {
4661
- kind: 'external-terminal',
4662
- preview: host,
4663
- title: label,
4664
- label,
4665
- focusPolicy: 'output-only',
4666
- onTerminalMouseIntent: (event: {
4667
- paneId: string;
4668
- paneKind: 'external-terminal';
4669
- mouseType: DisplayMouseEvent['type'];
4670
- row: number;
4671
- col: number;
4672
- exposure: TerminalExposureSnapshot;
4673
- interactionPolicy: TerminalInteractionPolicy;
4674
- }) => {
4675
- emitTerminalMouseIntent({
4676
- surfaceId: event.paneId,
4677
- paneKind: event.paneKind,
4678
- mouseType: event.mouseType,
4679
- row: event.row,
4680
- col: event.col,
4681
- exposure: event.exposure,
4682
- interactionPolicy: event.interactionPolicy,
4683
- });
4684
- },
4685
- } as any;
4686
- virtualWindows.registry.spawn({ title: label, initialContent: extSpec });
4687
- },
4688
- subscribeVirtualWindowClose: (cb) => {
4689
- virtualWindows.registry.subscribe((ev) => {
4690
- cb({
4691
- type: ev.type,
4692
- spawnTitle: 'spawnTitle' in ev ? ev.spawnTitle : undefined,
4693
- });
4694
- });
4695
- },
4696
4345
  });
4697
4346
  }
4698
4347
  const { setShellApprover } = await import('../shell-primitive/index.js');
@@ -4819,9 +4468,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4819
4468
  } catch { return []; }
4820
4469
  });
4821
4470
  setTerminalMouseIntentsGetter(() => [...recentTerminalMouseIntents]);
4822
- // Phase C — wire context.* tools. Window-registry getter is set
4823
- // later once virtualWindows is built (a few lines down); until
4824
- // then ContextWindowsList returns (no windows).
4471
+ // Phase C — wire context.* tools without virtual-window state.
4825
4472
  const { setContextRuntimeDeps } = await import('../tool-runtime/index.js');
4826
4473
  // Surface-unification v2.2 V2.2-5 (2026-05-11) — scheduler store
4827
4474
  // wire retired together with `context.jobs.list` LLM tool.
@@ -4924,103 +4571,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4924
4571
  debug.log(scope, area, details);
4925
4572
  },
4926
4573
  });
4927
- // VW-term-infra Bundle B-1 — Phase 5 symmetry foundation.
4928
- // PaneVisualStateStore singleton + SetFocusPolicy/DescribePane LLM
4929
- // tools. Store is dashboard-local so consumers (Alt+N skip migration
4930
- // landed in B-7-α, `^B !` toggle migration, visibility pill) subscribe
4931
- // via the same instance. Constructed ahead of initDashboardVirtualWindows
4932
- // so the VW init can plumb the store-driven skip predicate (B-7-α).
4933
- // Chord hints `^B p` / `^B d` reserved for Bundle B-2 symmetry registry wire.
4574
+ // PaneVisualStateStore serves dashboard pane tools.
4934
4575
  const paneVisualStateStore = createVisualStateStore();
4935
4576
  let browserPaneRegistry!: BrowserPaneRegistry;
4936
4577
  let previewPaneRegistry!: PreviewPaneRegistry;
4937
4578
 
4938
- // T1-P1 — virtual-windows init. Spins up the AddressBook +
4939
- // VWEventBus + WindowRegistry, wires PaneCapture's pane→content
4940
- // lookup, registers the skill-runner tool singletons, and creates
4941
- // the navigation router. Host UI callbacks (picker / new /
4942
- // split / help / close-window) are intentionally left as no-ops
4943
- // here — they are follow-ups (VW §10.2, §10.6). The chord still
4944
- // works for digits, arrows, n/p, x/X, etc. which don't need UI.
4945
- const vwInputRuntime = createDashboardVirtualWindowInputRuntime({
4946
- getRegistry: () => virtualWindows.registry,
4947
- pushMutedLine: (line) => {
4948
- chatLines.push(C.muted(line));
4949
- chatScrollOffset = -1;
4950
- },
4951
- openLocalInputTargetPopup: openVwLocalInputTargetPopup,
4952
- openSelectorPopup: openVwSelectorPopup,
4953
- routeContextMenu: (req) => ctxMenuWire.onMouse(req),
4954
- skipWindowWhen: skipWindowWhenStorePredicate(paneVisualStateStore),
4955
- resolvePaneVisibility: (windowId, paneId) =>
4956
- paneVisualStateStore.snapshot({ windowId: String(windowId), paneId }).visibility,
4957
- termSize,
4958
- });
4959
- const vwControlRuntime = createDashboardVirtualWindowControlRuntime({
4960
- openWindowPicker,
4961
- getCurrentWindow: () => virtualWindows.registry.current(),
4962
- closeWindow: (windowId) => { virtualWindows.registry.close(windowId); },
4963
- pushMutedLine: (line) => {
4964
- chatLines.push(C.muted(line));
4965
- chatScrollOffset = -1;
4966
- },
4967
- draw,
4968
- });
4969
- const vwMutationRuntime = createDashboardVirtualWindowMutationRuntime({
4970
- spawnWindow: () => virtualWindows.registry.spawn({
4971
- title: 'terminal',
4972
- initialContent: { kind: 'terminal' },
4973
- foreground: true,
4974
- }),
4975
- getForegroundSession: () => sessionRegistry.foreground(),
4976
- detachSession: (id) => {
4977
- sessionRegistry.detach(id);
4978
- },
4979
- getLatestBackgroundSession: () =>
4980
- sessionRegistry.list()
4981
- .filter(s => s.state === 'background')
4982
- .sort((a, b) => b.lastFocusedAt - a.lastFocusedAt)[0] ?? null,
4983
- attachSession: (id, dims) => {
4984
- sessionRegistry.attach(id, dims);
4985
- },
4986
- termSize,
4987
- getCurrentWindow: () => virtualWindows.registry.current(),
4988
- renameWindow: (windowId, next) => virtualWindows.registry.renameWindow(windowId, next),
4989
- renamePane: (windowId, paneId, next) => { virtualWindows.registry.renamePane(windowId, paneId, next); },
4990
- openRenameModal: openVwRenameModal,
4991
- pushMutedLine: (line) => {
4992
- chatLines.push(C.muted(line));
4993
- chatScrollOffset = -1;
4994
- },
4995
- pushWarningLine: (line) => {
4996
- chatLines.push(C.warning(line));
4997
- chatScrollOffset = -1;
4998
- },
4999
- draw,
5000
- });
5001
- const vwHelpRuntime = createDashboardVirtualWindowHelpRuntime({
5002
- termSize,
5003
- registrationState: {
5004
- supplementalGlobalKeys: getUserConfig().dashboard.enableSupplementalGlobalKeys,
5005
- virtualWindowSwitchKeys: getUserConfig().dashboard.enableVirtualWindowSwitchKeys,
5006
- },
5007
- keyLabel: (key, desc) => ` ${C.key(pad(key, 14))} ${C.text(desc)}`,
5008
- sectionLabel: (title) => C.bold(title),
5009
- muted: (text) => C.muted(text),
5010
- showHelpModal: ({ title, lines, termCols, termRows, ttlMs, group }) => {
5011
- showTransientTerminalModal({
5012
- title,
5013
- lines,
5014
- coordinator: display,
5015
- termCols,
5016
- termRows,
5017
- ttlMs,
5018
- group,
5019
- });
5020
- },
5021
- });
5022
- // Yazi-style browser-pane help (`~`). Same shape as vwHelpRuntime —
5023
- // shares the showTransientTerminalModal primitive.
4579
+ // Browser-pane help (`~`) uses the transient modal primitive.
5024
4580
  const browserHelpRuntime = createBrowserHelpRuntime({
5025
4581
  termSize,
5026
4582
  keyLabel: (key, desc) => ` ${C.key(pad(key, 14))} ${C.text(desc)}`,
@@ -5038,243 +4594,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5038
4594
  });
5039
4595
  },
5040
4596
  });
5041
- const vwSplitRuntime = createDashboardVirtualWindowSplitRuntime({
5042
- getCurrentWindow: () => virtualWindows.registry.current(),
5043
- spawnTerminal: ({ title, cwd }) => terminalMatrix.spawn({ title, cwd }),
5044
- cwd: () => workingDir.cwd,
5045
- detachSession: (id) => {
5046
- try { sessionRegistry.detach(id); } catch {}
5047
- },
5048
- getCurrentTerminalModalId: () => terminalModalRouter.current()?.id ?? null,
5049
- clearCurrentTerminalModal: () => { terminalModalRouter.set(null); },
5050
- createTerminalSlotContent: ({ terminalId, title }) => createPaneContent({
5051
- kind: 'terminal-slot',
5052
- terminalId,
5053
- title,
5054
- }),
5055
- bindSlot: (slotKey, paneId) => { vwSlotBindings.set(slotKey, paneId); },
5056
- setPlacement: (terminalId, placement) => { terminalMatrix.setPlacement(terminalId, placement); },
5057
- pushMutedLine: (line) => {
5058
- chatLines.push(C.muted(line));
5059
- chatScrollOffset = -1;
5060
- },
5061
- pushWarningLine: (line) => {
5062
- chatLines.push(C.warning(line));
5063
- chatScrollOffset = -1;
5064
- },
5065
- draw,
5066
- });
5067
-
5068
- // TUI 부활 T3 (2026-07-12) — VW 는 essential 에서 "실질 OFF" 게이트.
5069
- // 설계 노트: init 자체는 유지한다 (빈 레지스트리 + 이벤트버스 뿐 —
5070
- // 부팅 시 무조건적 윈도우 스폰이 없어 타이머/렌더 비용 0 확인).
5071
- // 완전 스킵(no-op registry)을 택하지 않은 이유: WindowRegistry 는
5072
- // private 필드를 가진 concrete class 라 구조적 타입 위조가 불가하고,
5073
- // 부팅 시 registry 참조를 캡처하는 소비처들이 stale no-op 을 물게 돼
5074
- // /ui rich 런타임 전환이 재시작 없이는 불가능해진다. 대신 사용자
5075
- // 노출 표면 3곳을 uiMode 로 게이트: ① ^B chord 깔때기(vwBody) ②
5076
- // draw 의 workspace frame 합성 ③ /workspace slash. essential 에서
5077
- // LLM tool 이 프로그래매틱하게 스폰한 윈도우는 레지스트리에 남고
5078
- // /ui rich 전환 시 그대로 보인다 (의도 — 비파괴).
5079
- virtualWindows = initDashboardVirtualWindows({
5080
- coordinator: display,
5081
- injectApprover: createPaneInjectApprover(),
5082
- broadcastApprover: createBroadcastApprover(),
5083
- // Per-window local composer submit. Route through the pane-level
5084
- // broadcast contract so mixed PTY/chat/markdown lanes share the
5085
- // same submit semantics instead of falling back to raw PTY bytes.
5086
- onLocalInputSubmit: vwInputRuntime.onLocalInputSubmit,
5087
- onOpenLocalInputTargetPicker: vwInputRuntime.onOpenLocalInputTargetPicker,
5088
- onShowSelector: vwInputRuntime.onShowSelector,
5089
- onShowContextMenu: vwInputRuntime.onShowContextMenu,
5090
- // B-7-α — PaneVisualStateStore-driven Alt+N skip predicate.
5091
- // Composed (OR) with the built-in !hasInteractableFocus() default
5092
- // in dashboard-virtual-windows.ts. When every pane in a window is
5093
- // skip-eligible (focusPolicy=skip/no-focus · visibility=hidden/
5094
- // dormant), Alt+N/P cycling bypasses the window. LLM's
5095
- // SetFocusPolicy tool becomes a real effect on cycling.
5096
- extraSkipWindowWhen: vwInputRuntime.extraSkipWindowWhen,
5097
- // B-7-γ — per-window resolver that reads PaneVisualStateStore so
5098
- // paintPane can stamp a visibility badge on the focused pane's
5099
- // label (`·H·` / `·D·` / `·ᴸ·`). The store is written by the LLM
5100
- // `SetFocusPolicy` tool (B-1) and the `^B H` chord (B-7-β);
5101
- // this factory is the consumer that makes those writes visible.
5102
- visibilityResolverFactory: vwInputRuntime.visibilityResolverFactory,
5103
- // T2-P5 — wire llm-chat panes to the same provider set the
5104
- // rest of the app uses. spawnLLMBenchmark now routes to a real
5105
- // backend; unknown providers surface as an error line inside
5106
- // the pane rather than a silent hang.
5107
- paneDeps: {
5108
- onTerminalMouseIntent: (ev, meta) => {
5109
- emitTerminalMouseIntent({
5110
- surfaceId: meta.paneId,
5111
- paneKind: meta.paneKind,
5112
- mouseType: ev.type,
5113
- row: ev.row,
5114
- col: ev.col,
5115
- exposure: meta.exposure,
5116
- interactionPolicy: meta.interactionPolicy,
5117
- });
5118
- },
5119
- llmChatBackend: async function* (req) {
5120
- const provider = LLM_PROVIDERS[req.provider];
5121
- if (!provider) {
5122
- yield `[unknown provider: ${req.provider}]`;
5123
- return;
5124
- }
5125
- if (!provider.chat) {
5126
- yield `[provider ${req.provider} missing chat()]`;
5127
- return;
5128
- }
5129
- try {
5130
- for await (const chunk of provider.chat(req.messages, { model: req.model })) {
5131
- yield chunk;
5132
- }
5133
- } catch (err) {
5134
- yield `\n[error: ${err instanceof Error ? err.message : String(err)}]`;
5135
- }
5136
- },
5137
- get browserPaneRegistry() { return browserPaneRegistry; },
5138
- get previewPaneRegistry() { return previewPaneRegistry; },
5139
- refreshRemoteBrowserPane: async (state: BrowserPaneModel) => {
5140
- await refreshRemoteWorkingDir(
5141
- state as WorkingDirState,
5142
- {},
5143
- { previewPath: null, previewLines: [], previewOffset: 0 },
5144
- );
5145
- },
5146
- },
5147
- defaultBounds: vwInputRuntime.defaultBounds,
5148
- callbacks: {
5149
- // FU-2 — chord help popup. Shows a grouped keybinding cheatsheet
5150
- // as a transient modal instead of a single muted chat line, so
5151
- // the user actually gets to read + reference it while experimenting.
5152
- // Auto-dismisses after 8 s; spawning the popup again while it's
5153
- // open replaces the prior instance (group='vw-chord-help').
5154
- onHelp: vwHelpRuntime.onHelp,
5155
- onPicker: vwControlRuntime.onPicker,
5156
- onCloseWindow: vwControlRuntime.onCloseWindow,
5157
- onNewWindow: vwMutationRuntime.onNewWindow,
5158
- onModalWindowToggle: vwMutationRuntime.onModalWindowToggle,
5159
- onSyncInputBarToggle: vwControlRuntime.onSyncInputBarToggle,
5160
- onZoomToggle: vwControlRuntime.onZoomToggle,
5161
- onLastFocusedPane: vwControlRuntime.onLastFocusedPane,
5162
- onRenameWindow: vwMutationRuntime.onRenameWindow,
5163
- onRenamePane: vwMutationRuntime.onRenamePane,
5164
- onSplit: vwSplitRuntime.onSplit,
5165
- },
5166
- });
5167
- // Phase T3b-b1 — install the VW placement adapter. matrix.move()
5168
- // with kind='vw' now routes through the VW registry's splitFocused
5169
- // / closePaneAt. Uses the vwSlotBindings map so the slot id (which
5170
- // the matrix caller chose) resolves back to the real PaneId.
5171
- const vwPlacementAdapter = new VwPlacementAdapter({
5172
- matrix: terminalMatrix,
5173
- sessionRegistry,
5174
- termSize,
5175
- installSlot: (windowId, slotId, terminalId) => {
5176
- // WindowId is a number in the VW registry but the matrix
5177
- // placement carries it as a string. Convert + guard Naan.
5178
- const winNum = Number(windowId);
5179
- const w = Number.isFinite(winNum) ? virtualWindows.registry.get(winNum) : null;
5180
- if (!w) throw new Error(`window not found: ${windowId}`);
5181
- const content = createPaneContent({
5182
- kind: 'terminal-slot',
5183
- terminalId,
5184
- title: `term:${terminalId}`,
5185
- });
5186
- // Adapter-driven splits default to horizontal. The chord flow
5187
- // chooses axis itself and bypasses the adapter — see onSplit
5188
- // above. This default is fine because users who care about the
5189
- // axis press the chord; users who call `/term move <id> vw:w/s`
5190
- // typically just want a slot.
5191
- const newPaneId = w.splitFocused('h', content);
5192
- vwSlotBindings.set(`${windowId}/${slotId}`, newPaneId);
5193
- },
5194
- removeSlot: (windowId, slotId) => {
5195
- const winNum = Number(windowId);
5196
- const w = Number.isFinite(winNum) ? virtualWindows.registry.get(winNum) : null;
5197
- if (!w) return;
5198
- const key = `${windowId}/${slotId}`;
5199
- const paneId = vwSlotBindings.get(key) ?? slotId; // fallback: slotId == paneId on chord path
5200
- suppressedVwPaneKills.add(paneId);
5201
- try { w.closePaneAt(paneId); } catch { /* already gone */ }
5202
- vwSlotBindings.delete(key);
5203
- },
5204
- });
5205
- vwPlacementAdapter.install();
5206
- // Phase T3b-b1 — drop stale bindings when a pane closes externally
5207
- // (user pressed `x` in the VW, window was destroyed, pane exited).
5208
- // Matrix placement stays as `vw:<w>/<slot>` until the user moves it
5209
- // explicitly; drift is annoying but not dangerous. Future phase:
5210
- // auto-move to background on pane:close.
5211
- installVwLifecycleHandlers({
5212
- bus: virtualWindows.bus,
5213
- terminalMatrix,
5214
- bindings: vwSlotBindings,
5215
- suppressedKills: suppressedVwPaneKills,
5216
- });
5217
- // Symmetric counterpart — when a VW-placed PTY exits or is killed
5218
- // (shell `exit`, external kill), auto-close the hosting VW pane so
5219
- // the user isn't left with a dead shell frame on screen. closePaneAt
5220
- // emits pane:close → the handler above drops the binding; kill is a
5221
- // no-op because the terminal already has exitCode !== null.
5222
- const closeVwPaneForExit = createVwExitPaneCloser({
5223
- getVirtualWindow: (id) => virtualWindows.registry.get(id),
5224
- bindings: vwSlotBindings,
5225
- });
5226
- // Phase T3b-b3 — auto-tag terminals with `_vw:<windowId>` broadcast
5227
- // group as they enter / leave a VW placement. This is the per-VW
5228
- // implicit group that `/term group send _vw:<id> <text>` broadcasts
5229
- // to — a tmux synchronize-panes at the VW level without the user
5230
- // having to manually joinGroup each split terminal.
5231
- terminalMatrix.subscribe((ev) => {
5232
- if (ev.type !== 'placement') return;
5233
- const { instance, prev } = ev;
5234
- if (prev.kind === 'vw') {
5235
- terminalMatrix.leaveGroup(instance.id, `_vw:${prev.windowId}`);
5236
- }
5237
- if (instance.placement.kind === 'vw') {
5238
- terminalMatrix.joinGroup(instance.id, `_vw:${instance.placement.windowId}`);
5239
- }
5240
- });
5241
- virtualWindows.bus.subscribe({ types: ['window:close'] }, (ev) => {
5242
- if (ev.type !== 'window:close') return;
5243
- const ownerId = vwCompanionOwnerId(ev.windowId);
5244
- const host = companionSurfaceHosts.get(ownerId);
5245
- if (!host) return;
5246
- suppressCompanionPopupDispose = true;
5247
- try {
5248
- host.disposeHandles();
5249
- } finally {
5250
- suppressCompanionPopupDispose = false;
5251
- }
5252
- for (const key of ['clipboard', 'memo', 'detail'] as const) {
5253
- host.close(key);
5254
- display.workspaceHostAPI().removeMember(ownerId, companionSurfaceId(ownerId, key));
5255
- }
5256
- });
5257
- // TR-P3: pane-modal chord (Ctrl+M <key>) — pops a deferred pane
5258
- // as a transient modal when the viewport is too narrow to show it
5259
- // inline. Chord state lives with the dashboard; hint line rendered
5260
- // by renderPaneModalHint in the frame loop.
5261
- // FU-1 — pane-modal-chord 상태는 이제 coordinator 의 chordArmed
5262
- // 필드가 소유. createPaneModalChord 인스턴스는 더 이상 필요 없음.
5263
-
5264
- // Phase C/D — now that virtualWindows.registry exists, augment the
5265
- // context.* + control.* deps so ContextWindowsList / WindowDetail /
5266
- // PaneDetail + ControlWindowResize / ControlPaneLayout see live
5267
- // windows. Safe to call again — set*RuntimeDeps replaces the
5268
- // stored record wholesale.
4597
+ // Refresh context runtime deps after the dashboard session registry is ready.
5269
4598
  try {
5270
- const { setContextRuntimeDeps, setControlRuntimeDeps } = await import('../tool-runtime/index.js');
5271
- // Surface-unification v2.2 V2.2-5 (2026-05-11) — scheduler store
5272
- // wire retired (scheduler view + `context.jobs.list` polychord
5273
- // both gone). Workflows now own scheduled work end-to-end.
4599
+ const { setContextRuntimeDeps } = await import('../tool-runtime/index.js');
5274
4600
  bootDashboardContextWindowRuntime({
5275
- setControlRuntimeDeps,
5276
4601
  setContextRuntimeDeps,
5277
- registry: virtualWindows.registry,
5278
4602
  // WD7 — context runtime cwd follows SWD.
5279
4603
  cwd: getSessionCwd(),
5280
4604
  getTerminalSessions: () => sessionRegistry.list().map((s) => ({
@@ -5463,7 +4787,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5463
4787
  wireDashboardSurfaceRegistry({
5464
4788
  surfaceRegistry,
5465
4789
  widgetHost,
5466
- windowRegistry: virtualWindows.registry,
5467
4790
  });
5468
4791
  } catch { /* never break boot */ }
5469
4792
 
@@ -5495,8 +4818,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5495
4818
  try {
5496
4819
  registerDashboardScenarioRuntimes({
5497
4820
  scenarioCatalog,
5498
- registry: virtualWindows.registry,
5499
- createPaneContent,
5500
4821
  spawnWidget: (spec) => widgetHost.spawn(spec),
5501
4822
  disposeWidget: (id) => { widgetHost.dispose(id); },
5502
4823
  getDashboardModals: () => dashboardModals,
@@ -5523,7 +4844,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5523
4844
  const artifactStore = createArtifactStore({
5524
4845
  legacyProviders: [
5525
4846
  createLegacyTimelineProvider({ dir: defaultTimelineBaseDir() }),
5526
- createLegacyLayoutProvider({ dir: layoutsDir() }),
5527
4847
  ],
5528
4848
  });
5529
4849
  try {
@@ -5960,19 +5280,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5960
5280
  afterThemeActiveSaved: draw,
5961
5281
  });
5962
5282
 
5963
- // T6-K6 — wire the SpawnCodingAgentInVW tool. Registry is the
5964
- // virtual-windows WindowRegistry shared across VW-P1..VW-P11.
5965
- // paneDeps already flow into VW tool dispatchers via
5966
- // initVirtualWindowTools so the terminal factory + spawn seam
5967
- // use the same defaults.
5968
- bootDashboardAgentSpawnTools({
5969
- registry: virtualWindows.registry,
5970
- initSpawnCodingAgentInVW,
5971
- initSpawnEmbodiedAgentInVW,
5972
- });
5973
- // H5 P1 Step E — wire the Embodied Agent Bus spawner + register the
5974
- // default codex-pty adapter into `defaultAdapterRegistry`. Legacy
5975
- // `/acp-vw codex` flows through `dispatchSpawnCodingAgentInVW`.
5976
5283
  // H5 P2 bootstrap — register the default channel router patterns
5977
5284
  // + codex-pty adapter hook, then bind the TTY snapshot LLM tools
5978
5285
  // to the live embodied-session registry. Without this wiring:
@@ -6127,15 +5434,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6127
5434
  findLiveSessionById,
6128
5435
  findLiveSessionByPaneId,
6129
5436
  getFocusedConversationSessionId: () => conversationPopupHost.snapshot().focusedSessionId,
6130
- getFocusedVirtualWindowPane: () => {
6131
- const vw = virtualWindows?.registry.current();
6132
- const pane = vw?.getFocusedPane();
6133
- return {
6134
- vwId: vw?.id ?? null,
6135
- paneId: pane?.id ?? null,
6136
- paneKind: pane?.kind ?? null,
6137
- };
6138
- },
5437
+ getFocusedVirtualWindowPane: () => ({ vwId: null, paneId: null, paneKind: null }),
6139
5438
  noteUserSubmit: (sessionId, text) => globalAcpEventRouter().noteUserSubmit(sessionId, text),
6140
5439
  clientSessionSend: (opts) => globalDualRoleManager().clientSessionSend(opts),
6141
5440
  getWorkingFocus: () => workingDir.focus,
@@ -6200,25 +5499,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6200
5499
  '../capture/capture-tools.js'
6201
5500
  );
6202
5501
  pfcScreenshotAttacherRef.current = createPfcScreenshotAttacher({
6203
- // The dashboard tracks shell handle ↔ surface mapping in
6204
- // `previewPaneRegistry` and `virtualWindows.registry`. For now
6205
- // resolve via the registered VW runner label (most common case);
6206
- // bg / inline shells return null and skip capture.
6207
- resolveScreenshotTarget: (shellId) => {
6208
- try {
6209
- const reg = pfcGetShellRegistry();
6210
- const label = reg.getVwLabel(shellId);
6211
- if (!label) return null;
6212
- const win = virtualWindows.registry.list().find(
6213
- (w) => virtualWindows.registry.spawnTitleOf(w.id) === label,
6214
- );
6215
- if (!win) return null;
6216
- return {
6217
- surfaceId: `vw:${win.id}/${label}`,
6218
- args: { windowId: String(win.id), runnerLabel: label },
6219
- };
6220
- } catch { return null; }
6221
- },
5502
+ resolveScreenshotTarget: () => null,
6222
5503
  dispatchScreenshot: async (args) => {
6223
5504
  const out = await pfcDispatchScreenshot(args);
6224
5505
  return {
@@ -6620,25 +5901,19 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6620
5901
  // 페인트를 멈춰 하단 영역의 단일 소유자를 보장(codex mod.rs:1674 · CC
6621
5902
  // REPL.tsx:4894 동형). 키는 기존 blocking-modal 라우팅 그대로 뷰가 소비.
6622
5903
  const bottomSlotModalActive = (): boolean => topBlockingForegroundModal()?.slot === 'bottom';
6623
- // 픽커류의 배치 결정 — essential 은 composer 자리(하단 슬롯), rich 는 기존
6624
- // 중앙 플로팅 유지(/ui 왕복 즉시 반영 — 호출 시점 게이트 패턴).
5904
+ // Pickers occupy the composer bottom slot.
6625
5905
  const resolveDecisionPickerPlacement = (desired: { height: number }): {
6626
5906
  bounds: { row: number; col: number; width: number; height: number };
6627
5907
  slot?: 'bottom';
6628
5908
  width: number;
6629
5909
  } => {
6630
5910
  const { cols: tc, rows: tr } = termSize();
6631
- if (dashboardUiMode !== 'rich') {
6632
- const bounds = resolveBottomSlotBounds({
6633
- termCols: tc, termRows: tr,
6634
- promptFrame: currentPromptFrame(tr),
6635
- height: desired.height,
6636
- });
6637
- return { bounds, slot: 'bottom', width: bounds.width };
6638
- }
6639
- const width = Math.min(84, Math.max(48, tc - 6));
6640
- const col = Math.max(2, Math.floor((tc - width) / 2));
6641
- return { bounds: { row: 3, col, width, height: desired.height }, width };
5911
+ const bounds = resolveBottomSlotBounds({
5912
+ termCols: tc, termRows: tr,
5913
+ promptFrame: currentPromptFrame(tr),
5914
+ height: desired.height,
5915
+ });
5916
+ return { bounds, slot: 'bottom', width: bounds.width };
6642
5917
  };
6643
5918
  const topBottomAreaFreezeModal = (
6644
5919
  termRows = termSize().rows,
@@ -6688,7 +5963,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6688
5963
  }
6689
5964
  return policy.suppressPromptArea || policy.suppressStatusArea;
6690
5965
  };
6691
- const vwCompanionOwnerId = (windowId: string | number): string => `virtual-window:${windowId}`;
6692
5966
  const companionPopupSpec = (
6693
5967
  key: CompanionPopupKey,
6694
5968
  ): {
@@ -6931,189 +6205,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6931
6205
  syncCompanionPopups();
6932
6206
  return next;
6933
6207
  };
6934
- const computeVirtualWindowCompanionBounds = (
6935
- windowId: number,
6936
- slotIndex = 0,
6937
- totalCount = 1,
6938
- ) => {
6939
- const bounds = virtualWindows.registry.get(windowId)?.getBounds();
6940
- if (!bounds) return null;
6941
- const innerCols = Math.max(24, bounds.width - 2);
6942
- const innerRows = Math.max(10, bounds.height - 2);
6943
- const local = computeCompanionPopupBounds({
6944
- termCols: innerCols,
6945
- termRows: innerRows,
6946
- slotIndex,
6947
- totalCount,
6948
- hasBlockingForeground: false,
6949
- });
6950
- return {
6951
- row: bounds.row + Math.max(0, local.row - 1),
6952
- col: bounds.col + Math.max(0, local.col - 1),
6953
- width: Math.min(local.width, innerCols),
6954
- height: Math.min(local.height, innerRows),
6955
- };
6956
- };
6957
- const syncVwCompanions = (windowId: number): void => {
6958
- const ownerId = vwCompanionOwnerId(windowId);
6959
- const host = companionSurfaceHosts.ensure(ownerId, ['clipboard', 'memo', 'detail']);
6960
- const workspaceHost = display.workspaceHostAPI();
6961
- suppressCompanionPopupDispose = true;
6962
- try {
6963
- host.disposeHandles();
6964
- } finally {
6965
- suppressCompanionPopupDispose = false;
6966
- }
6967
- const window = virtualWindows.registry.get(windowId);
6968
- if (!window) {
6969
- for (const key of ['clipboard', 'memo', 'detail'] as const) {
6970
- workspaceHost.removeMember(ownerId, companionSurfaceId(ownerId, key));
6971
- }
6972
- return;
6973
- }
6974
- workspaceHost.setLayoutMode(ownerId, 'desktop');
6975
- (['clipboard', 'memo', 'detail'] as const).forEach((key, index) => {
6976
- const surfaceId = companionSurfaceId(ownerId, key);
6977
- if (!host.isActive(key)) {
6978
- workspaceHost.removeMember(ownerId, surfaceId);
6979
- return;
6980
- }
6981
- workspaceHost.upsertMember(ownerId, {
6982
- surfaceId,
6983
- kind: 'popup',
6984
- label: companionPopupSpec(key).title,
6985
- order: 280 + index,
6986
- });
6987
- if (host.isDocked(key)) {
6988
- workspaceHost.minimizeMember(ownerId, surfaceId, { docked: true });
6989
- return;
6990
- }
6991
- workspaceHost.restoreMember(ownerId, surfaceId);
6992
- const bounds = computeVirtualWindowCompanionBounds(windowId, index, 3);
6993
- if (!bounds) return;
6994
- const spec = companionPopupSpec(key);
6995
- const columns = spec.columns.map((column) => ({ ...column }));
6996
- if (key === 'clipboard' && columns[0]) {
6997
- columns[0].onAfterKey = (action, ev) => {
6998
- syncClipboardCursorFromWidget();
6999
- if (action?.type === 'submit' || ev.name === 'enter' || ev.name === 'y' || ev.name === 'ㅛ') {
7000
- void copyClipboardHistoryEntryAt(clipCursor).then(() => draw());
7001
- return;
7002
- }
7003
- if (ev.name === 'c') {
7004
- clipHistory.clear();
7005
- clipCursor = 0;
7006
- statusFeedbackRuntime.onClipboardHistoryCleared();
7007
- draw();
7008
- return;
7009
- }
7010
- if (ev.name === 'escape' || ev.name === 'l' || ev.name === 'right') {
7011
- host.close('clipboard');
7012
- syncVwCompanions(windowId);
7013
- draw();
7014
- }
7015
- };
7016
- }
7017
- if (key === 'memo' && columns[0]) {
7018
- columns[0].onAfterKey = (_action, ev) => {
7019
- if (ev.name === 'escape') {
7020
- closeVwMemoCompanion(windowId);
7021
- syncVwCompanions(windowId);
7022
- draw();
7023
- return;
7024
- }
7025
- if ((ev as { ctrl?: boolean }).ctrl && (ev.name === 's' || ev.name === 'ㄴ')) {
7026
- commitVwMemoCompanion(windowId);
7027
- syncVwCompanions(windowId);
7028
- draw();
7029
- return;
7030
- }
7031
- if ((ev as { ctrl?: boolean }).ctrl && ev.name === 'a') {
7032
- const memo = widgetHost.get('wd-memo') as any;
7033
- if (memo?.state) memo.state.memoColIdx = 0;
7034
- requestDashboardRender('memo');
7035
- return;
7036
- }
7037
- if ((ev as { ctrl?: boolean }).ctrl && ev.name === 'e') {
7038
- const memo = widgetHost.get('wd-memo') as any;
7039
- if (memo?.state) {
7040
- const lineIdx = memo.state.memoLineIdx ?? 0;
7041
- const line = memo.state.memoLines?.[lineIdx] ?? '';
7042
- memo.state.memoColIdx = line.length;
7043
- }
7044
- requestDashboardRender('memo');
7045
- }
7046
- };
7047
- }
7048
- const handle = showLivePaneMultiModal({
7049
- id: surfaceId,
7050
- title: spec.title,
7051
- columns,
7052
- widgetHost,
7053
- coordinator: display,
7054
- termCols: termSize().cols,
7055
- termRows: termSize().rows,
7056
- bounds,
7057
- ttlMs: 0,
7058
- group: surfaceId,
7059
- interactionClass: 'embedded-overlay',
7060
- windowRole: 'companion',
7061
- chrome: resolveWindowScopedCompanionPopupChrome({
7062
- theme: currentThemeTokens(),
7063
- titleControls: spec.titleControls,
7064
- bottomStatus: spec.status,
7065
- windowId,
7066
- }),
7067
- onChromeAction: (action) => {
7068
- if (action.controlId === 'minimize') {
7069
- host.markDocked(key);
7070
- workspaceHost.minimizeMember(ownerId, surfaceId, { docked: true });
7071
- syncVwCompanions(windowId);
7072
- draw();
7073
- return;
7074
- }
7075
- if (action.controlId === 'close') {
7076
- host.close(key);
7077
- workspaceHost.removeMember(ownerId, surfaceId);
7078
- syncVwCompanions(windowId);
7079
- draw();
7080
- }
7081
- },
7082
- onDispose: () => {
7083
- if (suppressCompanionPopupDispose) return;
7084
- if (host.isDocked(key)) {
7085
- workspaceHost.minimizeMember(ownerId, surfaceId, { docked: true });
7086
- draw();
7087
- return;
7088
- }
7089
- host.close(key);
7090
- workspaceHost.removeMember(ownerId, surfaceId);
7091
- draw();
7092
- },
7093
- });
7094
- host.setHandle(key, handle);
7095
- });
7096
- };
7097
- const toggleVwCompanion = (
7098
- windowId: number,
7099
- key: 'clipboard' | 'memo' | 'detail',
7100
- ): boolean => {
7101
- const host = companionSurfaceHosts.ensure(vwCompanionOwnerId(windowId), ['clipboard', 'memo', 'detail']);
7102
- if (key === 'memo' && !host.isActive('memo')) seedMemoWidget(['']);
7103
- const next = host.toggleOpen(key);
7104
- syncVwCompanions(windowId);
7105
- return next;
7106
- };
7107
- const setVwCompanionOpen = (
7108
- windowId: number,
7109
- key: 'clipboard' | 'memo' | 'detail',
7110
- next: boolean,
7111
- ): void => {
7112
- const host = companionSurfaceHosts.ensure(vwCompanionOwnerId(windowId), ['clipboard', 'memo', 'detail']);
7113
- if (key === 'memo' && next && !host.isActive('memo')) seedMemoWidget(['']);
7114
- host.setOpen(key, next);
7115
- syncVwCompanions(windowId);
7116
- };
7117
6208
  const {
7118
6209
  syncConversationPopupModals,
7119
6210
  setConversationPopupLayoutMode,
@@ -7144,7 +6235,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7144
6235
  createAgentSessionProvider,
7145
6236
  createBrowserCdpProvider,
7146
6237
  registerProvider: (registry, provider) => { registry.registerProvider(provider); },
7147
- getWindows: () => virtualWindows.registry.list(),
6238
+ getWindows: () => [],
7148
6239
  listSessions: () => listLiveEmbodiedSessions(),
7149
6240
  // H6 P5 observer-registry landed · threading through enables
7150
6241
  // the channel-aware snapshot path (falls back to raw snapshot
@@ -7167,7 +6258,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7167
6258
 
7168
6259
  // ACP follow-up #1 — auto-persist on turn-end. DRM end_turn
7169
6260
  // transitions + BG terminal states flow into the H2 #5 persistence
7170
- // primitive automatically. Opt out with ELANOUS_ACP_PERSIST_MODE=off.
6261
+ // primitive automatically (always on — off-switch graduated 2026-09-26).
7171
6262
  wireAutoPersist({
7172
6263
  drm: globalDualRoleManager(),
7173
6264
  bg: globalBackgroundManager(),
@@ -7178,17 +6269,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7178
6269
  // the in-memory BackgroundManager bounded for long-running elanous
7179
6270
  // processes. Auto-persist (#1) already flushed the terminal record
7180
6271
  // to disk, so sweeping doesn't lose history · only the live stub.
7181
- // Opt out with ELANOUS_ACP_BG_SWEEP_MODE=off. Defaults: 24h TTL ·
6272
+ // Always on (ELANOUS_ACP_BG_SWEEP_MODE off-switch graduated 2026-09-26). Defaults: 24h TTL ·
7182
6273
  // 1h tick (matches Warp cloud-agent "stale after a day" heuristic).
7183
6274
  bootDashboardAcpBgSweep({
7184
- enabled: process.env['ELANOUS_ACP_BG_SWEEP_MODE'] !== 'off',
6275
+ enabled: true,
7185
6276
  backgroundManager: globalBackgroundManager(),
7186
6277
  });
7187
6278
 
7188
- // Phase 3a wiring — register SaveLayout/LoadLayout/ApplyLayoutPreset
7189
- // LLM tools against the shared WindowRegistry. Plan-only today; actual
7190
- // restore-to-live-VW lands in a follow-up commit.
7191
-
7192
6279
  // T6-K5 — wire widget + pane control tools. Widgets are reported
7193
6280
  // from widgetHost; pane focus piggybacks on workingDir.focus. The
7194
6281
  // pane name list mirrors the PaneFocus type so the LLM sees valid
@@ -7268,32 +6355,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7268
6355
  openSurfaceCatalogPopup();
7269
6356
  return true;
7270
6357
  },
7271
- openSurfaceInVw: (surface) => {
7272
- if (surface === 'scratch') {
7273
- spawnScratchVirtualWindow();
7274
- return true;
7275
- }
7276
- if (surface === 'browser') {
7277
- spawnBrowserVirtualWindow();
7278
- return true;
7279
- }
7280
- if (surface === 'preview') {
7281
- spawnPreviewVirtualWindow();
7282
- return true;
7283
- }
7284
- if (surface === 'browser-preview') {
7285
- spawnBrowserPreviewVirtualWindow();
7286
- return true;
7287
- }
7288
- return false;
7289
- },
6358
+ openSurfaceInVw: () => false,
7290
6359
  openCompanionSurface: (surface, target) => {
7291
- if (target === 'vw') {
7292
- const windowId = virtualWindows.registry.current()?.id ?? null;
7293
- if (!Number.isInteger(windowId)) return false;
7294
- setVwCompanionOpen(windowId as number, surface, true);
7295
- return true;
7296
- }
6360
+ if (target === 'vw') return false;
7297
6361
  setCompanionPopupOpen(surface, true);
7298
6362
  return true;
7299
6363
  },
@@ -8096,131 +7160,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
8096
7160
  const activeBrowserState = (): BrowserPaneModel => (
8097
7161
  resolveBrowserStateById(activeBrowserWidgetId()) ?? (workingDir as BrowserPaneModel)
8098
7162
  );
8099
- let vwPaneMountSeq = 0;
8100
- const nextVwBrowserPaneId = (): string => `vw-browser:${++vwPaneMountSeq}`;
8101
- const nextVwPreviewPaneId = (): string => `vw-preview:${++vwPaneMountSeq}`;
8102
- const cloneActiveBrowserIntoVw = (): { browserId: string; state: BrowserPaneModel } => {
8103
- const browserId = nextVwBrowserPaneId();
8104
- const state = browserPaneRegistry.cloneInto(activeBrowserWidgetId(), browserId);
8105
- return { browserId, state };
8106
- };
8107
- const cloneDockedPreviewIntoVw = (): { previewId: string } => {
8108
- const previewId = nextVwPreviewPaneId();
8109
- previewPaneRegistry.cloneInto('wd-preview', previewId);
8110
- return { previewId };
8111
- };
8112
- const nextTerminalVwSlotId = (terminalId: string): string => `term-slot:${terminalId}`;
8113
- const spawnScratchVirtualWindow = (title?: string): number => {
8114
- const reg = virtualWindows.registry;
8115
- const nextTitle = title?.trim() || `window ${reg.list().length + 1}`;
8116
- const w = reg.spawn({
8117
- title: nextTitle,
8118
- initialContent: { kind: 'scratch', title: nextTitle },
8119
- });
8120
- return w.id;
8121
- };
8122
- const spawnBrowserVirtualWindow = (title?: string): number => {
8123
- const reg = virtualWindows.registry;
8124
- const nextTitle = title?.trim() || `browser ${reg.list().length + 1}`;
8125
- const { browserId } = cloneActiveBrowserIntoVw();
8126
- const w = reg.spawn({
8127
- title: nextTitle,
8128
- initialContent: { kind: 'vw-browser', browserId, title: 'browser' } as any,
8129
- });
8130
- return w.id;
8131
- };
8132
- const spawnPreviewVirtualWindow = (title?: string): number => {
8133
- const reg = virtualWindows.registry;
8134
- const nextTitle = title?.trim() || `preview ${reg.list().length + 1}`;
8135
- const { previewId } = cloneDockedPreviewIntoVw();
8136
- const w = reg.spawn({
8137
- title: nextTitle,
8138
- initialContent: { kind: 'vw-preview', previewId, title: 'preview' } as any,
8139
- });
8140
- return w.id;
8141
- };
8142
- const spawnBrowserPreviewVirtualWindow = (title?: string): number => {
8143
- const reg = virtualWindows.registry;
8144
- const nextTitle = title?.trim() || `browser+preview ${reg.list().length + 1}`;
8145
- const { browserId } = cloneActiveBrowserIntoVw();
8146
- const { previewId } = cloneDockedPreviewIntoVw();
8147
- const w = reg.spawn({
8148
- title: nextTitle,
8149
- initialContent: { kind: 'vw-browser', browserId, title: 'browser' } as any,
8150
- });
8151
- const previewContent = createPaneContent({
8152
- kind: 'vw-preview',
8153
- previewId,
8154
- title: 'preview',
8155
- } as any, {
8156
- browserPaneRegistry,
8157
- previewPaneRegistry,
8158
- refreshRemoteBrowserPane: async (state: BrowserPaneModel) => {
8159
- await refreshRemoteWorkingDir(
8160
- state as WorkingDirState,
8161
- {},
8162
- { previewPath: null, previewLines: [], previewOffset: 0 },
8163
- );
8164
- },
8165
- });
8166
- w.splitFocused('h', previewContent);
8167
- return w.id;
8168
- };
8169
- const spawnIulVirtualWindow = (title?: string): number => {
8170
- const reg = virtualWindows.registry;
8171
- const trimmed = title?.trim() ?? '';
8172
- if (!trimmed && isIulResidentEnabled(getUserConfig().vw)) {
8173
- return focusOrSpawnIulResidentWindow(reg);
8174
- }
8175
- const nextTitle = trimmed || `IUL UX Lab ${reg.list().length + 1}`;
8176
- const w = reg.spawn({
8177
- title: nextTitle,
8178
- initialContent: { kind: 'iul-shell', title: nextTitle } as any,
8179
- });
8180
- return w.id;
8181
- };
8182
- const spawnAcpVirtualWindow = (title?: string): number => {
8183
- const reg = virtualWindows.registry;
8184
- const trimmed = title?.trim() ?? '';
8185
- // PLAN-tui-redundancy-cleanup T1 (2026-05-16) — resident-vw 의
8186
- // focusOrSpawnAcpResidentWindow + isAcpResidentEnabled 의존 제거.
8187
- // 사용자 slash command 통한 manual spawn 만 유지.
8188
- const nextTitle = trimmed || `ACP channels ${reg.list().length + 1}`;
8189
- const w = reg.spawn({
8190
- title: nextTitle,
8191
- initialContent: { kind: 'acp-shell', title: 'ACP Channels' } as any,
8192
- });
8193
- return w.id;
8194
- };
8195
- const spawnSimVirtualWindow = (title?: string): number => {
8196
- const reg = virtualWindows.registry;
8197
- const trimmed = title?.trim() ?? '';
8198
- if (!trimmed && isSimResidentEnabled(getUserConfig().vw)) {
8199
- return focusOrSpawnSimResidentWindow(reg);
8200
- }
8201
- const nextTitle = trimmed || `Simulator ${reg.list().length + 1}`;
8202
- const w = reg.spawn({
8203
- title: nextTitle,
8204
- initialContent: { kind: 'sim-shell', title: 'Test Simulator' } as any,
8205
- });
8206
- return w.id;
8207
- };
8208
- for (const residentKind of getUserConfig().vw.order) {
8209
- if (residentKind === 'iul' && isIulResidentEnabled(getUserConfig().vw)) {
8210
- bootIulResidentWindow(virtualWindows.registry, {
8211
- foreground: isIulForegroundStartupEnabled(getUserConfig().vw),
8212
- });
8213
- continue;
8214
- }
8215
- // PLAN-tui-redundancy-cleanup T1 (2026-05-16) — ACP resident window
8216
- // boot auto-mount 제거. `vw.order` 에 'acp' 가 남아 있어도 noop ·
8217
- // 사용자 manual slash command (/acp-vw) 통한 spawn 만 유지.
8218
- if (residentKind === 'acp') continue;
8219
- if (residentKind === 'sim' && isSimResidentEnabled(getUserConfig().vw)) {
8220
- bootSimResidentWindow(virtualWindows.registry);
8221
- }
8222
- }
8223
-
8224
7163
  // T7-N1 — ACP chat bridge. Speaks JSON-RPC to claude-code-acp /
8225
7164
  // codex-acp / gemini via the real client in src/acp/*. Subprocess
8226
7165
  // spawn is lazy — no cost until the user runs /acp.
@@ -8255,7 +7194,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
8255
7194
  // Scratch visibility (Phase S5). When closed, the workingDir
8256
7195
  // layouts drop the scratch cell so the remaining panes expand
8257
7196
  // to fill the reclaimed columns — useful on narrow terminals.
8258
- // Reopen via the /scratch open slash or the ^B ^S chord.
7197
+ // Reopen via the ^B ^S chord.
8259
7198
  let scratchClosed = false;
8260
7199
  const dashboardViewRegistryOptions = () => ({
8261
7200
  extraPanes: pluginHost.activeDashboardPanes().map(p => p.paneId),
@@ -8322,7 +7261,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
8322
7261
  // chrome rows (input/status/dock), but that does not mean the
8323
7262
  // main workspace's chat composer remains an active input owner.
8324
7263
  const chatMainAvailable = foregroundWorkspace === null;
8325
- const vwLocalComposerActive = !!virtualWindows.registry.current()?.isLocalComposerActive();
7264
+ const vwLocalComposerActive = false;
8326
7265
  const overlayInputActive = hasOverlayInputOwner({
8327
7266
  focusStack: display.modalStack(),
8328
7267
  surfaceAt: (id) => display.surface(id),
@@ -9579,13 +8518,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
9579
8518
  agentStatusStore.clear(ev.instance.id);
9580
8519
  claudeStatusParser.reset(ev.instance.id);
9581
8520
  codexStatusParser.reset(ev.instance.id);
9582
- closeVwPaneForExit(ev.instance.placement, 'exited');
9583
8521
  } else if (ev.type === 'killed') {
9584
8522
  notificationStore.push(exitToNotification(ev.instance.id, null));
9585
8523
  agentStatusStore.clear(ev.instance.id);
9586
8524
  claudeStatusParser.reset(ev.instance.id);
9587
8525
  codexStatusParser.reset(ev.instance.id);
9588
- closeVwPaneForExit(ev.instance.placement, 'killed');
9589
8526
  } else if (ev.type === 'attention') {
9590
8527
  notificationStore.push(attentionToNotification(ev.instance.id, ev.level));
9591
8528
  }
@@ -9620,27 +8557,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
9620
8557
  try { debug.log('agent-status.mirror.activate-failed', String(err), { level: 'error' }); } catch { /* swallow */ }
9621
8558
  }
9622
8559
  })();
9623
- // PLAN-chat-hud-multi-surface-port-2026-05-13 §4 M3 — TUI HUD strip
9624
- // mirrors to daemon's HudStore via `POST /v1/hud-segment` so PWA
9625
- // `<ChatHud>` (M4) hydrates from dashboard-only writers (token gauge
9626
- // · ssh-remote · agent-activity · reasoning level · variant badge).
9627
- // Same probe-once + fire-and-forget contract as agent-status mirror.
9628
- void (async () => {
9629
- try {
9630
- const { activateHudMirrorIfReachable } = await import('./hud-mirror.js');
9631
- const handle = await activateHudMirrorIfReachable({
9632
- hud,
9633
- log: (m) => { try { debug.log('hud.mirror', m); } catch { /* swallow */ } },
9634
- });
9635
- if (handle.active) {
9636
- process.once('beforeExit', () => {
9637
- try { handle.deactivate(); } catch { /* ignore */ }
9638
- });
9639
- }
9640
- } catch (err) {
9641
- try { debug.log('hud.mirror.activate-failed', String(err), { level: 'error' }); } catch { /* swallow */ }
9642
- }
9643
- })();
9644
8560
  const disposeConversationWidgetLiveBridge = createConversationWidgetLiveBridge({
9645
8561
  widgetHost,
9646
8562
  listSessions: () => listLiveEmbodiedSessions().map((entry) => ({
@@ -10888,24 +9804,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
10888
9804
  });
10889
9805
  }
10890
9806
  }
10891
- const scratch = widgetHost.get('wd-scratch') as any;
10892
- if (scratch) {
10893
- const displayScratch = display.scratchState();
10894
- const focused = workingDir.focus === 'scratch';
10895
- // Arc Z — push dashboard source-of-truth state into the scratch
10896
- // widget. Native scratch modes (memo / preview) hand their
10897
- // structured state over.
10898
- // Surface-unification v2.2 V2.2-5 Part 2 (2026-05-11) — view=4
10899
- // scheduler scratch projection retired (scheduler view 폐기).
10900
- scratchWidgetRuntime.projectPreview(
10901
- scratch,
10902
- focused,
10903
- scratchOffset,
10904
- scratchTitle,
10905
- scratchLines,
10906
- displayScratch && displayScratch.mode !== 'agents' ? displayScratch : null,
10907
- );
10908
- }
10909
9807
  const clipboardWidget = widgetHost.get('wd-clipboard') as any;
10910
9808
  companionWidgetRuntime.projectClipboardWidget(
10911
9809
  clipboardWidget,
@@ -11726,18 +10624,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11726
10624
  getScratchTotalBytes: () => scratchLines.reduce((n, l) => n + l.length, 0),
11727
10625
  isCompanionOpen: (key) => companionPopupHost.isActive(key as CompanionPopupKey),
11728
10626
  hasClosedPanes: () => paneStateSnapshot().panes.some((pane) => pane.closed),
11729
- resolveVirtualWindowPaneKind: ({ windowId, paneId }) => {
11730
- const id = Number.parseInt(windowId, 10);
11731
- if (!Number.isFinite(id)) return null;
11732
- return virtualWindows.registry.get(id)?.getPane(paneId)?.kind ?? null;
11733
- },
11734
- isVirtualWindowCompanionOpen: ({ windowId }, key) => {
11735
- const id = Number.parseInt(windowId, 10);
11736
- if (!Number.isFinite(id)) return false;
11737
- return companionSurfaceHosts
11738
- .ensure(vwCompanionOwnerId(id), ['clipboard', 'memo', 'detail'])
11739
- .isActive(key as 'clipboard' | 'memo' | 'detail');
11740
- },
11741
10627
  getDebugPath: () => debug.path(),
11742
10628
  getDebugLevel: () => debug.level(),
11743
10629
  });
@@ -11915,103 +10801,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11915
10801
  });
11916
10802
  return;
11917
10803
  }
11918
- case 'vw.selector': {
11919
- const payload = result.payload as VirtualWindowTitleMenuPayload | undefined;
11920
- if (!payload) return;
11921
- const windowId = Number.parseInt(payload.windowId, 10);
11922
- if (!Number.isFinite(windowId)) return;
11923
- openVwSelectorPopup(windowId, _ev.col, _ev.row);
11924
- return;
11925
- }
11926
- case 'vw.clipboard-companion': {
11927
- const payload = result.payload as VirtualWindowTitleMenuPayload | undefined;
11928
- if (!payload) return;
11929
- const windowId = Number.parseInt(payload.windowId, 10);
11930
- if (!Number.isFinite(windowId)) return;
11931
- const opened = toggleVwCompanion(windowId, 'clipboard');
11932
- contextMenuFeedbackRuntime.onVwCompanionToggled(windowId, 'clipboard', opened);
11933
- return;
11934
- }
11935
- case 'vw.memo-companion': {
11936
- const payload = result.payload as VirtualWindowTitleMenuPayload | undefined;
11937
- if (!payload) return;
11938
- const windowId = Number.parseInt(payload.windowId, 10);
11939
- if (!Number.isFinite(windowId)) return;
11940
- const opened = toggleVwCompanion(windowId, 'memo');
11941
- contextMenuFeedbackRuntime.onVwCompanionToggled(windowId, 'memo', opened);
11942
- return;
11943
- }
11944
- case 'vw.detail-companion': {
11945
- const payload = result.payload as VirtualWindowTitleMenuPayload | undefined;
11946
- if (!payload) return;
11947
- const windowId = Number.parseInt(payload.windowId, 10);
11948
- if (!Number.isFinite(windowId)) return;
11949
- const opened = toggleVwCompanion(windowId, 'detail');
11950
- contextMenuFeedbackRuntime.onVwCompanionToggled(windowId, 'detail', opened);
11951
- return;
11952
- }
11953
- case 'vw.return-popup': {
11954
- const payload = result.payload as VirtualWindowTitleMenuPayload | undefined;
11955
- if (!payload) return;
11956
- const windowId = Number.parseInt(payload.windowId, 10);
11957
- if (!Number.isFinite(windowId)) return;
11958
- const window = virtualWindows.registry.get(windowId);
11959
- const pane = window?.getPane(payload.paneId);
11960
- if (!window || !pane) return;
11961
- if (pane.kind === 'terminal-slot') {
11962
- const inst = resolveVwTerminalInstanceForPane(
11963
- terminalMatrix.list({ includeExited: true }),
11964
- vwSlotBindings,
11965
- payload.windowId,
11966
- payload.paneId,
11967
- );
11968
- if (!inst) return;
11969
- terminalMatrix.move(inst.id, { kind: 'modal', modalId: inst.id });
11970
- if (inst.legacySessionId) {
11971
- const session = sessionRegistry.get(inst.legacySessionId);
11972
- if (session?.modal) {
11973
- terminalModalRouter.set(session.modal, {
11974
- onClose: () => { sessionRegistry.detach(session.id); draw(); },
11975
- });
11976
- }
11977
- }
11978
- draw();
11979
- return;
11980
- }
11981
- if (pane.kind === 'vw-browser') {
11982
- openBrowserOnlyModal();
11983
- } else if (pane.kind === 'vw-preview') {
11984
- openPreviewOnlyModal();
11985
- } else if (pane.kind === 'scratch') {
11986
- openDashboardPaneModal('scratch');
11987
- } else {
11988
- return;
11989
- }
11990
- try { window.closePaneAt(payload.paneId); } catch { /* already gone */ }
11991
- return;
11992
- }
11993
10804
  case 'dashboard-pane.open-popup': {
11994
10805
  const payload = result.payload as DashboardPaneTitleMenuPayload | undefined;
11995
10806
  if (!payload) return;
11996
10807
  openDashboardPaneModal(payload.pane);
11997
10808
  return;
11998
10809
  }
11999
- case 'dashboard-pane.open-vw': {
12000
- const payload = result.payload as DashboardPaneTitleMenuPayload | undefined;
12001
- if (!payload) return;
12002
- if (payload.pane === 'browser') {
12003
- spawnBrowserVirtualWindow();
12004
- return;
12005
- }
12006
- if (payload.pane === 'preview') {
12007
- spawnPreviewVirtualWindow();
12008
- return;
12009
- }
12010
- if (payload.pane === 'scratch') {
12011
- spawnScratchVirtualWindow();
12012
- }
12013
- return;
12014
- }
12015
10810
  case 'dashboard-pane.clipboard-companion': {
12016
10811
  const opened = toggleCompanionPopup('clipboard');
12017
10812
  contextMenuFeedbackRuntime.onDashboardCompanionToggled('clipboard', opened);
@@ -12071,13 +10866,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12071
10866
  openBrowserPreviewModal: () => openBrowserPreviewModal(),
12072
10867
  openDashboardPaneModal: (pane) => openDashboardPaneModal(pane),
12073
10868
  openCompanionPopup: (key) => setCompanionPopupOpen(key as DashboardCompanionSurfaceKey, true),
12074
- spawnBrowserVirtualWindow: () => spawnBrowserVirtualWindow(),
12075
- spawnPreviewVirtualWindow: () => spawnPreviewVirtualWindow(),
12076
- spawnBrowserPreviewVirtualWindow: () => spawnBrowserPreviewVirtualWindow(),
12077
- spawnScratchVirtualWindow: () => spawnScratchVirtualWindow(),
12078
- spawnSimVirtualWindow: () => spawnSimVirtualWindow(),
12079
- currentVirtualWindowId: () => virtualWindows.registry.current()?.id ?? null,
12080
- openVwCompanion: (windowId, key) => setVwCompanionOpen(windowId, key as DashboardCompanionSurfaceKey, true),
12081
10869
  onWarning: (message) => {
12082
10870
  pushDebugLine(C.warning(` ${message}`));
12083
10871
  chatScrollOffset = -1;
@@ -12131,20 +10919,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12131
10919
  pushChatLine: (line) => { chatLines.push(line); },
12132
10920
  setChatScrollBottom: () => { chatScrollOffset = -1; },
12133
10921
  });
12134
- const windowSlashRuntime = createDashboardWindowSlashRuntime({
12135
- accent: C.accent,
12136
- muted: C.muted,
12137
- success: C.success,
12138
- error: C.error,
12139
- warning: C.warning,
12140
- });
12141
- const benchSlashRuntime = createDashboardBenchSlashRuntime({
12142
- accent: C.accent,
12143
- muted: C.muted,
12144
- warning: C.warning,
12145
- error: C.error,
12146
- maxPanes: MAX_BENCHMARK_PANES,
12147
- });
12148
10922
  const termSlashRuntime = createDashboardTermSlashRuntime({
12149
10923
  accent: C.accent,
12150
10924
  muted: C.muted,
@@ -12182,36 +10956,27 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12182
10956
  muted: C.muted,
12183
10957
  warning: C.warning,
12184
10958
  });
12185
- const scratchSlashRuntime = createDashboardScratchSlashRuntime({
10959
+ const companionSlashRuntime = createDashboardCompanionSlashRuntime({
12186
10960
  muted: C.muted,
12187
10961
  warning: C.warning,
12188
10962
  });
12189
- const companionSlashRuntime = createDashboardCompanionSlashRuntime({
10963
+ const agentsSlashRuntime = createDashboardAgentsSlashRuntime({
12190
10964
  muted: C.muted,
12191
10965
  warning: C.warning,
12192
10966
  });
12193
- const agentsSlashRuntime = createDashboardAgentsSlashRuntime({
10967
+ const controlSignalSlashRuntime = createDashboardControlSignalSlashRuntime({
10968
+ accent: C.accent,
12194
10969
  muted: C.muted,
12195
10970
  warning: C.warning,
10971
+ observer: defaultControlSignalObserver(),
10972
+ signalBus: dashboardControlSignals,
12196
10973
  });
12197
- const viewSlashRuntime = createDashboardViewSlashRuntime({
10974
+ const browserCdpSlashRuntime = createDashboardBrowserCdpSlashRuntime({
10975
+ accent: C.accent,
12198
10976
  muted: C.muted,
12199
- success: C.success,
12200
10977
  warning: C.warning,
10978
+ signalBus: dashboardControlSignals,
12201
10979
  });
12202
- const controlSignalSlashRuntime = createDashboardControlSignalSlashRuntime({
12203
- accent: C.accent,
12204
- muted: C.muted,
12205
- warning: C.warning,
12206
- observer: defaultControlSignalObserver(),
12207
- signalBus: dashboardControlSignals,
12208
- });
12209
- const browserCdpSlashRuntime = createDashboardBrowserCdpSlashRuntime({
12210
- accent: C.accent,
12211
- muted: C.muted,
12212
- warning: C.warning,
12213
- signalBus: dashboardControlSignals,
12214
- });
12215
10980
  const simSlashRuntime = createDashboardSimSlashRuntime({
12216
10981
  accent: C.accent,
12217
10982
  muted: C.muted,
@@ -12252,27 +11017,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12252
11017
  muted: C.muted,
12253
11018
  warning: C.warning,
12254
11019
  });
12255
- const mouseDockRuntime = createMouseDockRuntime({
12256
- listDockWindowPanes: () =>
12257
- panesForDashboardView(activeViewDef()).filter((pane) => pane !== 'input'),
12258
- paneLabel,
12259
- openDockWindow: (pane) => openDashboardPaneModal(pane),
12260
- compactSurfaceHost,
12261
- listVirtualWindows: () =>
12262
- virtualWindows.registry.list().map((window) => ({
12263
- id: window.id,
12264
- title: window.title,
12265
- })),
12266
- getCurrentVirtualWindowId: () => virtualWindows.registry.current()?.id ?? null,
12267
- switchToVirtualWindow: (windowId) => {
12268
- virtualWindows.registry.switchTo(windowId);
12269
- },
12270
- focusDashboardMain: () => {
12271
- virtualWindows.registry.backgroundCurrent();
12272
- setWorkingFocus(firstPaneOfView(workingDir.view), 'dock-vw-mover-main');
12273
- },
12274
- redraw: () => { draw(); },
12275
- });
12276
11020
  const mousePickerRuntime = createMousePickerRuntime({
12277
11021
  getRotation: () => getUserConfig().llm.rotation ?? [],
12278
11022
  getCurrentModelEntry: () => {
@@ -12290,337 +11034,52 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12290
11034
  },
12291
11035
  getRecentWds: () => [...wdHistory],
12292
11036
  applySessionWd: (path) => {
12293
- setSessionCwd(path, 'user');
12294
- rememberWd(path);
12295
- },
12296
- reportModelSwitchError: (message) => {
12297
- chatLines.push(C.error(` model switch failed: ${message}`));
12298
- chatScrollOffset = -1;
12299
- },
12300
- reportWdSwitchError: (message) => {
12301
- chatLines.push(C.error(` cwd switch failed: ${message}`));
12302
- chatScrollOffset = -1;
12303
- },
12304
- });
12305
- const mouseShellRollupRuntime = createMouseShellRollupRuntime({
12306
- resolveVwIdByLabel: (label) => {
12307
- const all = virtualWindows.registry.list();
12308
- const hit = all.find((window) => virtualWindows.registry.spawnTitleOf(window.id) === label);
12309
- return hit ? hit.id : null;
12310
- },
12311
- switchToVirtualWindow: (windowId) => {
12312
- virtualWindows.registry.switchTo(windowId);
12313
- },
12314
- onWarning: (message) => {
12315
- chatLines.push(C.warning(` ${message}`));
12316
- chatScrollOffset = -1;
12317
- },
12318
- });
12319
- const mouseConversationPopupRuntime = createMouseConversationPopupRuntime({
12320
- conversationPopupHost,
12321
- ensureConversationWidgetMounted,
12322
- syncConversationPopupModals,
12323
- redraw: () => { draw(); },
12324
- });
12325
- const mouseWorkspaceRestoreRuntime = createMouseWorkspaceRestoreRuntime({
12326
- workspaceHost: display.workspaceHostAPI(),
12327
- companionPopupHost,
12328
- // Lazy wrapper — `openDebugWindow` is declared as a `const` further
12329
- // down (~line 11521), so a shorthand reference here triggers a TDZ
12330
- // ReferenceError when this deps object is built during boot. Same
12331
- // pattern as the PR #1111 fix on `applyFocusToInputTransition` /
12332
- // `draw` for `createDashboardVoiceRuntime`. The arrow defers the
12333
- // name lookup until the callback actually runs.
12334
- openDebugWindow: () => openDebugWindow(),
12335
- openDebugWorkbenchModal: () => { openDebugWorkbenchModal(); },
12336
- syncCompanionPopups,
12337
- openConversationModal,
12338
- redraw: () => { draw(); },
12339
- });
12340
- const mouseModeSwitchRuntime = createMouseModeSwitchRuntime({
12341
- getActiveMode: () => inputCoreActiveMode(),
12342
- getModeAction: (actionId) => getInputCoreAction(actionId),
12343
- setMode: (next) => inputCoreSetMode(next),
12344
- });
12345
- const mouseHoverRuntime = createMouseHoverRuntime({
12346
- dispatchHoverToWidget: (paneId, event) => {
12347
- widgetHost.dispatchHover(
12348
- paneId,
12349
- event,
12350
- );
12351
- },
12352
- setConversationHoverLabel: (label) => {
12353
- setSegment(hud, 'conv-hover', C.muted(label), 4);
12354
- },
12355
- clearConversationHoverLabel: () => {
12356
- clearSegment(hud, 'conv-hover');
12357
- },
12358
- conversationHoverHudLabel,
12359
- });
12360
- const mouseModalHitRuntime = createMouseModalHitRuntime({
12361
- getTopSurface: () => {
12362
- const stack = display.modalStack();
12363
- const topId = stack[stack.length - 1];
12364
- if (!topId) return null;
12365
- return display.surface(topId) as import('../display/modal-stack.js').ModalSurface | null;
12366
- },
12367
- });
12368
- const mouseModalSurfaceRuntime = createMouseModalSurfaceRuntime({
12369
- openWindowPicker,
12370
- getFocusStack: () => display.modalStack(),
12371
- surfaceAt: (id) =>
12372
- display.surface(id) as import('../display/modal-stack.js').ModalSurface | null | undefined,
12373
- getTopBlockingModalSurface: topBlockingForegroundModal,
12374
- routeModalMouse: (surface, ev) => display.routeMouseToSurface(surface, ev),
12375
- });
12376
- const mousePillContextRuntime = createMousePillContextRuntime({
12377
- handleForPill: (name) => {
12378
- const handle = handleForPill(name as Parameters<typeof handleForPill>[0]);
12379
- // 2026-05-05 — model pill 만: 우클릭 직전에 menu 의 title 을 활성
12380
- // rotation entry 라벨로 갱신해 "어떤 모델이 픽 되었는지" 정확히
12381
- // 보여준다. 라벨도 짧게 (Switch / Remove / Settings…) 해서 메뉴
12382
- // 폭을 status bar pill 인접 폭에 가깝게 유지.
12383
- if (handle && name === 'model') {
12384
- try {
12385
- const reg = getDashboardContextMenuRegistry();
12386
- const current = mousePickerRuntime.getCurrentModelEntry?.() ?? null;
12387
- const activeLabel = current ? rotationEntryLabel(current) : '(none)';
12388
- reg.updateMenu(handle as never, buildPillMenu({
12389
- pillName: 'Model',
12390
- title: `Model · ${activeLabel}`,
12391
- canSwitch: true,
12392
- canRemove: true,
12393
- canOpenSettings: false,
12394
- switchLabel: 'Switch',
12395
- removeLabel: 'Remove',
12396
- }));
12397
- } catch { /* dynamic update is best-effort */ }
12398
- }
12399
- return handle;
12400
- },
12401
- showMenu: (handle, pos) =>
12402
- getDashboardContextMenuRegistry().showMenu(
12403
- handle as Parameters<ReturnType<typeof getDashboardContextMenuRegistry>['showMenu']>[0],
12404
- pos,
12405
- // 2026-05-05 — pill 우클릭 메뉴를 CMX-2 의 일반 context-menu wire
12406
- // 와 같은 path 로 정렬: singleInstance + ownerWorkspaceId. 이전엔
12407
- // 옵션 미지정으로 surface 가 dashboard-main workspace 에 attach 만
12408
- // 됐고 (default), singleInstance 도 false 라 매번 새 surface id.
12409
- // 키보드 ↑/↓ 가 routeKey 까지 도달하지 못하던 사용자 피드백 대응.
12410
- { singleInstance: true, ownerWorkspaceId: 'dashboard-main' },
12411
- ),
12412
- // 2026-05-05 — pill 우클릭 메뉴 결과 wire. 현재 model pill 만 액션
12413
- // 매핑되어 있다 (`Switch Model…` · `Remove from rotation`). 좌클릭은
12414
- // 그대로 cycle (mouse-wiring 의 pill click branch). 다른 pill 은
12415
- // legacy 동작 — 추후 wire 추가 시 여기 분기 늘리기.
12416
- onPillMenuPick: async (name, value, pos) => {
12417
- if (debug.enabled) {
12418
- debug.log('mouse.pill.context.dispatch', 'enter', { name, value, pos });
12419
- }
12420
- if (name !== 'model') return;
12421
- // Pill 우클릭 위치 → vw picker 의 PopupPlacement. status row 는
12422
- // pos.y+1 (mouse-pill-context-runtime 가 0-indexed y 로 ev.row-1
12423
- // 보냈음 — 1-indexed 로 복귀). anchor col 도 동일.
12424
- const pillPickerPlacement = (origin: { x: number; y: number }): PopupPlacement => {
12425
- const { rows: termRows, cols: termCols } = termSize();
12426
- const anchorCol = origin.x + 1;
12427
- return {
12428
- anchorStartCol: anchorCol,
12429
- anchorEndCol: anchorCol,
12430
- statusRow: origin.y + 1,
12431
- termCols,
12432
- termRows,
12433
- };
12434
- };
12435
- if (value === 'pill.switch') {
12436
- // 좌클릭 cycle 과 동일 — 다음 rotation entry 로 switch.
12437
- const ring = mousePickerRuntime.getRotation();
12438
- if (ring.length === 0) {
12439
- pushModeToast?.('No rotation configured · /provider add', 'warning');
12440
- return;
12441
- }
12442
- const current = mousePickerRuntime.getCurrentModelEntry?.() ?? null;
12443
- let nextIdx = 0;
12444
- if (current) {
12445
- const cIdx = ring.findIndex((e) =>
12446
- e.provider === current.provider
12447
- && (e.model ?? null) === (current.model ?? null),
12448
- );
12449
- nextIdx = cIdx < 0 ? 0 : (cIdx + 1) % ring.length;
12450
- }
12451
- const nextEntry = ring[nextIdx]!;
12452
- await Promise.resolve(mousePickerRuntime.setActiveModel(nextEntry));
12453
- pushModeToast?.(`▸ ${rotationEntryLabel(nextEntry)}`, 'success');
12454
- draw();
12455
- return;
12456
- }
12457
- if (value === 'pill.remove') {
12458
- const current = mousePickerRuntime.getCurrentModelEntry?.() ?? null;
12459
- if (debug.enabled) {
12460
- debug.log('mouse.pill.context.dispatch', 'remove.start', {
12461
- hasCurrent: !!current,
12462
- currentLabel: current ? rotationEntryLabel(current) : null,
12463
- });
12464
- }
12465
- // 2026-05-05 — `currentRotationIndex(cfg)` 사용. 이전 PR 들은
12466
- // (1) `removeRotationEntry(needle)` — 합성 라벨이 단일 필드와
12467
- // 매칭 안 됨
12468
- // (2) 직접 `(provider, model)` 튜플 — 사용자 지적: "디스플레이
12469
- // 시 provider 이름 생략 루틴" 때문에 cfg.llm.provider/model
12470
- // 이 entry 와 그대로 매칭 안 됨
12471
- // currentRotationIndex 는 이미 검증된 active-entry 매칭 로직을
12472
- // 가짐 — provider 동일 + (양쪽 model 다 있고 다르면 skip · 한쪽
12473
- // 만 있으면 같은 provider 의 entry 면 OK). cfg.llm 으로부터 직접
12474
- // 인덱스 산출하므로 derived label 변환 영향 받지 않음.
12475
- const cfg = getUserConfig();
12476
- const ring = cfg.llm?.rotation ?? [];
12477
- const idx = currentRotationIndex(cfg);
12478
- if (debug.enabled) {
12479
- debug.log('mouse.pill.context.dispatch', 'remove.result', {
12480
- matchedIndex: idx,
12481
- rotationLengthBefore: ring.length,
12482
- cfgProvider: cfg.llm?.provider ?? null,
12483
- cfgModel: cfg.llm?.model ?? null,
12484
- });
12485
- }
12486
- if (idx < 0) {
12487
- pushModeToast?.(
12488
- `Active model not in rotation: ${current ? rotationEntryLabel(current) : '(none)'}`,
12489
- 'warning',
12490
- );
12491
- return;
12492
- }
12493
- // 2026-05-05 — Confirm popup (vw picker 스타일). 사용자 요청:
12494
- // "테마가 vw picker 스타일로 깔끔하게 될것 같은데" — context-
12495
- // menu widget 대신 createActionPickerRecipe 를 사용해 model
12496
- // picker / window picker 같은 정돈된 chrome 으로 통일.
12497
- const targetLabel = rotationEntryLabel(ring[idx]!);
12498
- const placement = pillPickerPlacement(pos);
12499
- const confirmValue = await new Promise<string | null>((resolve) => {
12500
- let resolved = false;
12501
- let handle: ReturnType<typeof createActionPickerRecipe> | null = null;
12502
- handle = createActionPickerRecipe<string>({
12503
- id: 'pill-remove-confirm',
12504
- title: `Remove from rotation?`,
12505
- // 단일 preview row — primary CTA 가 이 entry 의 value 로 fire.
12506
- items: [
12507
- { value: 'confirm.remove', label: targetLabel },
12508
- ],
12509
- placement,
12510
- // CTA 버튼 명시 (사용자 요청). primary='Remove' · cancel='Cancel'.
12511
- actionButtons: true,
12512
- primaryActionLabel: 'Remove',
12513
- cancelActionLabel: 'Cancel',
12514
- filterable: false,
12515
- initialIndex: 0,
12516
- // 2026-05-06 — dock submenu 와 동일 row look 차용. parent
12517
- // role = highlight 색상으로 cursor/selected row · 사용자 요청:
12518
- // "dock area 매뉴 subitem 들의 UI/UX (포커스시와 아닐시 look)
12519
- // 그것을 차용".
12520
- theme: deriveSubmenuPopupRoleTheme(currentThemeTokens(), 'parent'),
12521
- shadow: { theme: currentThemeTokens() },
12522
- onPick: (value) => {
12523
- if (resolved) return;
12524
- resolved = true;
12525
- try { handle?.dispose(); } catch { /* swallow */ }
12526
- resolve(value);
12527
- },
12528
- onCancel: () => {
12529
- if (resolved) return;
12530
- resolved = true;
12531
- try { handle?.dispose(); } catch { /* swallow */ }
12532
- resolve(null);
12533
- },
12534
- });
12535
- display.pushModal(handle.surface);
12536
- draw();
12537
- });
12538
- if (debug.enabled) {
12539
- debug.log('mouse.pill.context.dispatch', 'remove.confirm', {
12540
- value: confirmValue ?? '(cancelled)',
12541
- });
12542
- }
12543
- if (confirmValue !== 'confirm.remove') {
12544
- // Cancelled / escaped — toast 으로 회복 신호. 사용자 피드백:
12545
- // "Remove Confirm 팝업에서 Cancel 을 선택하면 아래 회복 방법
12546
- // 이 없음" — 액션이 안 됐다는 visible feedback 가 필요.
12547
- pushModeToast?.(
12548
- `Cancelled · '${targetLabel}' still in rotation`,
12549
- 'info',
12550
- );
12551
- return;
12552
- }
12553
- const removed = ring[idx]!;
12554
- const nextRing = ring.slice(0, idx).concat(ring.slice(idx + 1));
12555
- const nextCfg = {
12556
- ...cfg,
12557
- llm: {
12558
- ...cfg.llm,
12559
- rotation: nextRing.length > 0 ? nextRing : undefined,
12560
- },
12561
- };
12562
- try {
12563
- saveUserConfig(nextCfg);
12564
- reloadUserConfig();
12565
- if (debug.enabled) {
12566
- debug.log('mouse.pill.context.dispatch', 'remove.persisted', {
12567
- rotationLength: nextRing.length,
12568
- });
12569
- }
12570
- } catch (err) {
12571
- if (debug.enabled) {
12572
- debug.log('mouse.pill.context.dispatch', 'remove.persist-failed', {
12573
- err: err instanceof Error ? err.message : String(err),
12574
- }, { level: 'error' });
12575
- }
12576
- pushModeToast?.('Save failed', 'warning');
12577
- return;
12578
- }
12579
- // 후속: 새 rotation 의 첫 entry 로 switch (있으면).
12580
- const liveRing = mousePickerRuntime.getRotation();
12581
- if (liveRing.length > 0) {
12582
- await Promise.resolve(mousePickerRuntime.setActiveModel(liveRing[0]!));
12583
- }
12584
- pushModeToast?.(
12585
- `🗑 Removed ${rotationEntryLabel(removed)} · ${liveRing.length} left`,
12586
- 'success',
12587
- );
12588
- draw();
12589
- // 2026-05-05 — 정보 popup (vw picker 스타일). 사용자 요청:
12590
- // "실제 지웠을때 인포 팝업도 만들어주세요" + "테마 vw picker
12591
- // 스타일". confirm 과 동일한 createActionPickerRecipe 패턴 ·
12592
- // 단일 'OK' 항목 (Enter/Esc 로 close).
12593
- let infoHandle: ReturnType<typeof createActionPickerRecipe> | null = null;
12594
- const closeInfo = (): void => {
12595
- try { infoHandle?.dispose(); } catch { /* swallow */ }
12596
- };
12597
- infoHandle = createActionPickerRecipe<string>({
12598
- id: 'pill-remove-info',
12599
- title: '🗑 Removed from rotation',
12600
- // 두 정보 row · primary CTA = 'OK'. items value 는 onPick callback
12601
- // 으로 들어오지만 모두 동일 closeInfo 로 라우팅.
12602
- items: [
12603
- { value: 'info.removed', label: rotationEntryLabel(removed) },
12604
- { value: 'info.remaining', label: `${liveRing.length} entries left` },
12605
- ],
12606
- placement: pillPickerPlacement(pos),
12607
- // CTA 버튼 명시. info popup 은 primary='OK' · cancel='Close'.
12608
- actionButtons: true,
12609
- primaryActionLabel: 'OK',
12610
- cancelActionLabel: 'Close',
12611
- filterable: false,
12612
- initialIndex: 0,
12613
- theme: currentThemeTokens(),
12614
- shadow: { theme: currentThemeTokens() },
12615
- onPick: closeInfo,
12616
- onCancel: closeInfo,
12617
- });
12618
- display.pushModal(infoHandle.surface);
12619
- draw();
12620
- return;
12621
- }
11037
+ setSessionCwd(path, 'user');
11038
+ rememberWd(path);
11039
+ },
11040
+ reportModelSwitchError: (message) => {
11041
+ chatLines.push(C.error(` model switch failed: ${message}`));
11042
+ chatScrollOffset = -1;
11043
+ },
11044
+ reportWdSwitchError: (message) => {
11045
+ chatLines.push(C.error(` cwd switch failed: ${message}`));
11046
+ chatScrollOffset = -1;
11047
+ },
11048
+ });
11049
+ const mouseShellRollupRuntime = createMouseShellRollupRuntime({
11050
+ resolveVwIdByLabel: () => null,
11051
+ switchToVirtualWindow: () => { throw new Error('Virtual windows are unavailable in the essential dashboard'); },
11052
+ onWarning: (message) => {
11053
+ chatLines.push(C.warning(` ${message}`));
11054
+ chatScrollOffset = -1;
11055
+ },
11056
+ });
11057
+ const mouseConversationPopupRuntime = createMouseConversationPopupRuntime({
11058
+ conversationPopupHost,
11059
+ ensureConversationWidgetMounted,
11060
+ syncConversationPopupModals,
11061
+ redraw: () => { draw(); },
11062
+ });
11063
+ const mouseModeSwitchRuntime = createMouseModeSwitchRuntime({
11064
+ getActiveMode: () => inputCoreActiveMode(),
11065
+ getModeAction: (actionId) => getInputCoreAction(actionId),
11066
+ setMode: (next) => inputCoreSetMode(next),
11067
+ });
11068
+ const mouseModalHitRuntime = createMouseModalHitRuntime({
11069
+ getTopSurface: () => {
11070
+ const stack = display.modalStack();
11071
+ const topId = stack[stack.length - 1];
11072
+ if (!topId) return null;
11073
+ return display.surface(topId) as import('../display/modal-stack.js').ModalSurface | null;
12622
11074
  },
12623
11075
  });
11076
+ const mouseModalSurfaceRuntime = createMouseModalSurfaceRuntime({
11077
+ getFocusStack: () => display.modalStack(),
11078
+ surfaceAt: (id) =>
11079
+ display.surface(id) as import('../display/modal-stack.js').ModalSurface | null | undefined,
11080
+ getTopBlockingModalSurface: topBlockingForegroundModal,
11081
+ routeModalMouse: (surface, ev) => display.routeMouseToSurface(surface, ev),
11082
+ });
12624
11083
  const mousePaneHitRuntime = createMousePaneHitRuntime({
12625
11084
  getPaneNavRow: () => paneNavRow,
12626
11085
  paneAtColumn: (col0) => {
@@ -12649,29 +11108,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12649
11108
 
12650
11109
  const mouseWiring = createDashboardMouseWiring({
12651
11110
  termSize: () => termSize(),
12652
- // TUI 부활 T4 — hover 팝업(툴팁 자동 표시)은 rich UI 전용.
12653
- // /ui 런타임 전환을 따르도록 predicate 로 배선.
12654
- hoverPopupsEnabled: () => isDashboardHeavyFeatureEnabled(dashboardUiMode, 'hover-popups'),
11111
+ hoverPopupsEnabled: () => false,
12655
11112
  getRotation: mousePickerRuntime.getRotation,
12656
11113
  getCurrentModelEntry: mousePickerRuntime.getCurrentModelEntry,
12657
11114
  setActiveModel: mousePickerRuntime.setActiveModel,
12658
11115
  getRecentWds: mousePickerRuntime.getRecentWds,
12659
11116
  setSessionWd: mousePickerRuntime.setSessionWd,
12660
- getDockMenuWindowTargets: mouseDockRuntime.getDockMenuWindowTargets,
12661
- onOpenDockWindow: mouseDockRuntime.onOpenDockWindow,
12662
- getDockMenuSurfaceTargets: mouseDockRuntime.getDockMenuSurfaceTargets,
12663
- onOpenDockSurface: mouseDockRuntime.onOpenDockSurface,
12664
- onToggleChatOnly: mouseDockRuntime.onToggleChatOnly,
12665
11117
  onExitProgram: async () => {
12666
11118
  exitDashboardTui();
12667
11119
  setTimeout(() => process.exit(0), 0);
12668
11120
  },
12669
- getDashboardViews: mouseDockRuntime.getDashboardViews,
12670
- onApplyDashboardView: mouseDockRuntime.onApplyDashboardView,
12671
- getVirtualWindows: mouseDockRuntime.getVirtualWindows,
12672
- onSwitchVirtualWindow: mouseDockRuntime.onSwitchVirtualWindow,
12673
- getVirtualWindowMover: mouseDockRuntime.getVirtualWindowMover,
12674
- onMoveVirtualWindow: mouseDockRuntime.onMoveVirtualWindow,
12675
11121
  pushModalSurface: surface => display.pushModal(surface),
12676
11122
  // DS-2b (2026-04-21) — drag-session routing hook. Adapter lives
12677
11123
  // in src/display/drag-dispatch.ts (PR #319); this single line is
@@ -12680,10 +11126,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12680
11126
  // the existing chain (0 overhead path). See PLAN-drag-session-
12681
11127
  // primitive.md §3.3 and PR #319 for the contract.
12682
11128
  dragDispatch: (ev) => dragDispatch(ev, display.dragManagerAPI()),
12683
- // CMX-2 (2026-04-22) — non-pill right-click dispatch. Pill path
12684
- // (line ~7590-ish onPillRightClick) keeps the legacy direct
12685
- // handleForPill lookup; this hook serves pane-body / pane-title
12686
- // / input / VW right-clicks via MenuProviderRegistry. Unconsumed
11129
+ // CMX-2 (2026-04-22) — non-pill right-click dispatch. This hook
11130
+ // serves pane-body / pane-title / input / VW right-clicks via
11131
+ // MenuProviderRegistry. Unconsumed
12687
11132
  // (no provider matches) falls through to modal forwarding.
12688
11133
  contextMenuDispatch: (ev) => ctxMenuWire.onMouse(ev),
12689
11134
  // DS-3a (2026-04-21) — chat input hit classifier. Routes clicks
@@ -12703,18 +11148,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12703
11148
  updateModalBounds: (id, bounds) => display.updateModalBounds(id, bounds),
12704
11149
  redraw: () => { draw(); },
12705
11150
  requestRender: () => { requestDashboardRender(); },
12706
- // IDX-F5d Phase 2 (2026-04-22) — pane-body hover dispatcher.
12707
- // Mouse wiring subscribes to its internal HoverTracker; we bridge
12708
- // pane-body hover events to `widgetHost.dispatchHover` so each
12709
- // widget's `onHover` override fires when the pointer enters /
12710
- // leaves / hovers stable over one of its items. `paneId` matches
12711
- // `widgetInstanceId` in the current grid (getPaneHitTarget wires
12712
- // them 1:1 in dashboard.ts:7852).
12713
- dispatchHoverToWidget: mouseHoverRuntime.dispatchHoverToWidget,
12714
- onPaneHoverEvent: mouseHoverRuntime.onPaneHoverEvent,
12715
11151
  conversationPopupHost,
12716
11152
  onConversationPopupPick: mouseConversationPopupRuntime.onConversationPopupPick,
12717
- onWindowPillClick: mouseModalSurfaceRuntime.onWindowPillClick,
12718
11153
  // SRF-4 — shell rollup pill click → popup listing live handles.
12719
11154
  // Handles are fetched at click time so the popup always reflects
12720
11155
  // the current ShellRegistry state (not a frozen snapshot).
@@ -12742,13 +11177,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12742
11177
  getTheme: () => currentThemeTokens(),
12743
11178
  suppressHoverHints: true,
12744
11179
  workspaceHost: display.workspaceHostAPI(),
12745
- onWorkspaceRestore: mouseWorkspaceRestoreRuntime.onWorkspaceRestore,
12746
- // IDX-5 Phase 2 — right-click on a pill → context-menu registry.
12747
- // Registry is initialised just below (after mouseWiring is built
12748
- // so the presenter can use `display.pushModal`). The handler is a
12749
- // closure that resolves the right registry handle at call time so
12750
- // the one-off bootstrap order works out.
12751
- onPillRightClick: mousePillContextRuntime.onPillRightClick,
12752
11180
  // IDX-5 Phase 3 B-4 — classify clicks outside the status-bar pill
12753
11181
  // row into broader pane regions so lastClickHitKind can carry
12754
11182
  // 'pane-nav' / 'pane-title' / 'pane-body' values (previously only
@@ -12781,14 +11209,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12781
11209
  getTheme: () => currentThemeTokens(),
12782
11210
  });
12783
11211
 
12784
- // Phase 3a + F6 / S1.D — layout + display-control runtime
12785
- // registration. Both need late boot state now available here:
12786
- // artifactStore/windowRegistry for Layout* and mouse/context-menu
12787
- // deps for display-control. Registration is idempotent.
11212
+ // F6 / S1.D — display-control runtime registration needs late-boot mouse/context-menu state.
12788
11213
  try {
12789
11214
  registerDashboardLayoutDisplayRuntimes({
12790
- windowRegistry: virtualWindows.registry,
12791
- artifactStore,
12792
11215
  coordinator: display,
12793
11216
  mouseDispatch: (ev) => mouseWiring.handleMouse(ev),
12794
11217
  menuProviderRegistry: ctxMenuProviders,
@@ -12815,16 +11238,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12815
11238
  // values are cheap no-ops (one field comparison per key).
12816
11239
  publishFocusContextKeysFromService(workingDir.focus);
12817
11240
 
12818
- // TR-P3: refresh the pane-modal hint segment each draw. When the
12819
- // viewport is at tabletMini/tabletTwo and there are deferred
12820
- // panes, a "Ctrl+M p=preview l=log" segment appears on the HUD.
12821
- // Clears on wider viewports so the hint doesn't stick around.
12822
- {
12823
- const vis = activePaneVisibility();
12824
- const hint = renderPaneModalHint(vis, cols, effectiveTabletMode());
12825
- if (hint) setSegment(hud, 'tr-pane-modal', C.muted(hint), 4);
12826
- else clearSegment(hud, 'tr-pane-modal');
12827
- }
12828
11241
 
12829
11242
  // T3-B1: agent-activity HUD segment. Pulsing glyph + running
12830
11243
  // count drives awareness that sub-agent work is in flight —
@@ -12962,9 +11375,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12962
11375
  }
12963
11376
  const suppressPromptArea = hostChromePolicy.suppressPromptArea;
12964
11377
  const suppressStatusArea = hostChromePolicy.suppressStatusArea;
12965
- const suppressHudArea = hostChromePolicy.suppressHudArea;
12966
11378
  const suppressDashboardBackground = hostChromePolicy.suppressDashboardBackground;
12967
- const suppressDockArea = hostChromePolicy.suppressDockArea;
12968
11379
  if (suppressPromptArea) display.setCursor(null);
12969
11380
  // ── Zone list (top-to-bottom) ──
12970
11381
  // Replaces the old inline `termRows - N` arithmetic and hand-rolled
@@ -13034,18 +11445,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13034
11445
  });
13035
11446
  }
13036
11447
 
13037
- // HUD — reserves 1 row in rich mode. Empty string when no active
13038
- // segments. TUI 부활 T1: essential 모드는 HUD pill 행 자체를 생략
13039
- // (codex 패리티 — 하단 chrome 은 status line 1줄만). zoneRows 소비처
13040
- // (`zoneRows.get('hud')` 류)는 missing zone 을 안전 처리하는 설계
13041
- // (§MX11b 코멘트)라 생략이 안전하다.
13042
- if (isDashboardHeavyFeatureEnabled(dashboardUiMode, 'hud')) {
13043
- zones.push({
13044
- id: 'hud',
13045
- height: 1,
13046
- render: (_h, c) => [suppressHudArea ? '' : (renderHud(hud, c) || '')],
13047
- });
13048
- }
11448
+ // Essential chrome uses only the status line, not a HUD row.
13049
11449
 
13050
11450
  // Input block.
13051
11451
  let promptCaret: CursorState | null = null;
@@ -13075,24 +11475,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13075
11475
  render: (_h, c) => [suppressPromptArea ? ' '.repeat(c) : hLine(c)],
13076
11476
  });
13077
11477
 
13078
- // Bottom areas: 1-row gap → status area → 1-row gap → dock area.
13079
- // TUI 부활 T1: essential 은 status line 1줄만 유지(대표 확정 ①) —
13080
- // gap-mid·dock 행 생략으로 chat log 가 2행 더 얻는다. dock 클릭
13081
- // 라우팅은 setDockRow(null) 경로로 자연 비활성.
11478
+ // Essential bottom area: status line only; no dock row.
13082
11479
  zones.push({ id: 'status-gap-top', height: 1, render: () => [''] });
13083
11480
  zones.push({
13084
11481
  id: 'status',
13085
11482
  height: 1,
13086
11483
  render: () => [suppressStatusArea ? '' : buildStatusLine()],
13087
11484
  });
13088
- if (isDashboardHeavyFeatureEnabled(dashboardUiMode, 'dock')) {
13089
- zones.push({ id: 'status-gap-mid', height: 1, render: () => [''] });
13090
- zones.push({
13091
- id: 'dock',
13092
- height: 1,
13093
- render: () => [suppressDockArea ? '' : mouseWiring.buildDockLine()],
13094
- });
13095
- }
13096
11485
 
13097
11486
  // Compose.
13098
11487
  const composed = composeVertical(zones, { rows: termRows, cols: totalW });
@@ -13151,11 +11540,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13151
11540
  // is one of the easiest ways to see a flash under tmux.
13152
11541
  let modalFrame = '';
13153
11542
  let modalKey = '';
13154
- // TUI 부활 T3 — essential 은 VW frame 합성 스킵 (rich 에서 스폰된
13155
- // 윈도우가 남아 있어도 essential 로 전환하면 그리지 않는다).
13156
- const foregroundWorkspaceFrame = isDashboardHeavyFeatureEnabled(dashboardUiMode, 'workspace-frame')
13157
- ? (virtualWindows.registry.current()?.render() ?? '')
13158
- : '';
13159
11543
  if (effectiveLayout && effectiveLayout.modals.length > 0) {
13160
11544
  const m = effectiveLayout.modals[0]!;
13161
11545
  modalKey = `${m.id}:${m.widgetInstanceId}:${JSON.stringify(m.position)}:${m.size?.width ?? ''}x${m.size?.height ?? ''}`;
@@ -13163,15 +11547,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13163
11547
  termRows, termCols: cols, theme: currentThemeTokens(),
13164
11548
  });
13165
11549
  }
13166
- if (foregroundWorkspaceFrame.length > 0) {
13167
- modalFrame += foregroundWorkspaceFrame;
13168
- }
13169
11550
  if (debug.isKeyTraceEnabled() && blockingForegroundModal) {
13170
11551
  debug.log('dashboard.frame-compose', 'blocking-modal', {
13171
11552
  modalId: blockingForegroundModal.id,
13172
11553
  baseLineCount: baseLines.length,
13173
11554
  overlayLen: modalFrame.length,
13174
- hasForegroundWorkspaceFrame: foregroundWorkspaceFrame.length > 0,
11555
+ hasForegroundWorkspaceFrame: false,
13175
11556
  });
13176
11557
  }
13177
11558
  try {
@@ -13183,15 +11564,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13183
11564
  // propagated request.force (e.g. popModal / closeSurface on a
13184
11565
  // coordinator-owned modal). Before V3 only the layout-modal signal
13185
11566
  // was honored, so coordinator modal closes left residual pixels.
13186
- // ⭐ 단일 flush 지점 — essential 은 프롬프트 caret 을 스스로 정하고, rich 는 종전 그대로다.
11567
+ // ⭐ Single flush: resolve the prompt caret after overlays.
13187
11568
  const frameCursorDecision = display.cursorDecision();
13188
11569
  renderDashboardFrame(baseLines, composeDashboardFrameWithCoordinatorModal(display, {
13189
11570
  overlay: modalFrame,
13190
11571
  force: forceFromCoordinator || modalKey !== lastModalKey,
13191
- essential: dashboardUiMode === 'essential',
13192
11572
  cursorOwner: frameCursorDecision.owner,
13193
11573
  claimedCursor: frameCursorDecision.cursor,
13194
- // ⛔ rich 는 #6528 까지의 값을 그대로 쓴다 — 이 PR 은 essential 축만 바꾼다.
13195
11574
  coordinatorCursor: display.getCursor(),
13196
11575
  promptCaret,
13197
11576
  suppressPromptArea,
@@ -14109,42 +12488,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14109
12488
  when: () => agentSearchModal === null,
14110
12489
  });
14111
12490
 
14112
- // FU-1 — pane-modal-chord port. Each pane letter from SHORTCUT_TABLE
14113
- // registers as a chord binding under `Ctrl+M,Ctrl+ㅡ`. The `when`
14114
- // guard keeps a pane's binding inactive unless it's currently in
14115
- // the deferred list, so mistyped letters still fall through.
14116
- const openDeferredPane = (pane: PaneFocus): void => {
14117
- const { cols: tc, rows: tr } = termSize();
14118
- const captured = captureDeferredPane(pane);
14119
- showTransientTerminalModal({
14120
- title: `${pane} (modal)`,
14121
- lines: captured.split('\n'),
14122
- coordinator: display,
14123
- termCols: tc,
14124
- termRows: tr,
14125
- ttlMs: 8000,
14126
- group: 'pane-modal',
14127
- });
14128
- };
14129
- for (const [pane, letter] of Object.entries(PANE_SHORTCUT_TABLE) as Array<[PaneFocus, string]>) {
14130
- display.registerKeyBinding({
14131
- id: `dashboard:pane-modal-chord:${pane}`,
14132
- chordPrefix: 'C-m',
14133
- key: letter,
14134
- chordTimeoutMs: PANE_MODAL_CHORD_TIMEOUT_MS,
14135
- scope: 'global',
14136
- handler: () => openDeferredPane(pane),
14137
- when: () => activePaneVisibility().modalDeferred.includes(pane),
14138
- });
14139
- }
14140
-
14141
- // Task 2 · T-3 / T-4 — `Ctrl+M B` (shift) opens the 2×1 modal with
14142
- // browser + preview side-by-side. Complementary to the lowercase
14143
- // single-pane chord above. T-4 saves the pre-modal focus and
14144
- // restores it on dispose (→ log when tablet mode is active, the
14145
- // default "keep user where they were" otherwise). narrow terminals
14146
- // get a 1-column fallback automatically via showPaneMultiModal's
14147
- // MIN_WIDE_WIDTH gate.
14148
12491
  let debugWorkbenchHandle: PaneMultiModalHandle | null = null;
14149
12492
  let debugWindowHandle: PaneMultiModalHandle | null = null;
14150
12493
  let debugWindowHoverActive = false;
@@ -14469,14 +12812,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14469
12812
  onContextMenuMouse: (ev) => ctxMenuWire.onMouse(ev),
14470
12813
  });
14471
12814
  };
14472
- display.registerKeyBinding({
14473
- id: 'dashboard:pane-modal-chord:browser+preview',
14474
- chordPrefix: 'C-m',
14475
- key: 'S-b',
14476
- chordTimeoutMs: PANE_MODAL_CHORD_TIMEOUT_MS,
14477
- scope: 'global',
14478
- handler: openBrowserPreviewModal,
14479
- });
14480
12815
  const openBrowserOnlyModal = (): void => {
14481
12816
  const { cols: tc, rows: tr } = termSize();
14482
12817
  const liveMode = resolveBrowserPreviewModalLiveMode(process.env.ELANOUS_PANE_MODAL_LIVE);
@@ -14497,14 +12832,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14497
12832
  onCancel: () => { draw(); },
14498
12833
  });
14499
12834
  };
14500
- display.registerKeyBinding({
14501
- id: 'dashboard:pane-modal-chord:browser-only',
14502
- chordPrefix: 'C-m',
14503
- key: 'S-w',
14504
- chordTimeoutMs: PANE_MODAL_CHORD_TIMEOUT_MS,
14505
- scope: 'global',
14506
- handler: openBrowserOnlyModal,
14507
- });
14508
12835
  const openPreviewOnlyModal = (): void => {
14509
12836
  const { cols: tc, rows: tr } = termSize();
14510
12837
  const liveMode = (process.env.ELANOUS_PANE_MODAL_LIVE ?? 'on').toLowerCase() !== 'off';
@@ -14522,237 +12849,22 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14522
12849
  onCancel: () => { draw(); },
14523
12850
  });
14524
12851
  };
14525
- display.registerKeyBinding({
14526
- id: 'dashboard:pane-modal-chord:preview-only',
14527
- chordPrefix: 'C-m',
14528
- key: 'S-p',
14529
- chordTimeoutMs: PANE_MODAL_CHORD_TIMEOUT_MS,
14530
- scope: 'global',
14531
- handler: openPreviewOnlyModal,
14532
- });
14533
-
14534
- // FU-2 — virtual-window chord port (last remaining hard branch).
14535
- // Each body key of the Ctrl+B chord registers as its own coordinator
14536
- // binding; the handler synthesises a KeyEvent and forwards it to
14537
- // NavigationRouter.dispatchArmed so the actual routing logic stays
14538
- // in one place (tests still target NavigationRouter directly).
14539
- const VW_CHORD_PREFIX = 'C-b';
14540
- // TUI 부활 T3 — essential 은 VW 키 표면 OFF. 모든 ^B chord body 가
14541
- // 이 단일 깔때기를 지나므로 여기 한 곳 게이트로 비가시 윈도우
14542
- // 생성/조작(^B c 등)을 차단한다. /ui rich 런타임 전환 즉시 복원.
14543
- const vwBody = (ev: DisplayKeyEvent): void => {
14544
- if (dashboardUiMode !== 'rich') {
14545
- chatLines.push(C.muted(' (virtual workspace 는 rich 모드 전용 — /ui rich 로 전환)'));
14546
- chatScrollOffset = -1;
14547
- draw();
14548
- return;
14549
- }
14550
- virtualWindows.router.dispatchArmed(ev);
14551
- };
14552
- const vwKey = (name: string, mods: Partial<DisplayKeyEvent> = {}): DisplayKeyEvent => ({
14553
- name, ctrl: false, shift: false, alt: false, ...mods,
14554
- });
14555
- const registerVwChord = (id: string, key: string, ev: DisplayKeyEvent): void => {
14556
- display.registerKeyBinding({
14557
- id: `dashboard:vw-chord:${id}`,
14558
- chordPrefix: VW_CHORD_PREFIX,
14559
- key,
14560
- scope: 'global',
14561
- handler: () => vwBody(ev),
14562
- });
14563
- };
14564
12852
  registerDashboardGlobalKeys({
14565
12853
  enableSupplementalGlobalKeys: getUserConfig().dashboard.enableSupplementalGlobalKeys,
14566
12854
  register: (binding) => { display.registerKeyBinding(binding); },
14567
12855
  openSurfaceCatalog: () => openSurfaceCatalogPopup(),
14568
12856
  surfaceCatalogWhen: () => agentSearchModal === null,
14569
- dispatchVirtualWindowChord: vwBody,
14570
- });
14571
- // 1-9 / C-1..C-9 → switch to Nth window (router
14572
-
14573
- // does the registry lookup internally).
14574
- if (getUserConfig().dashboard.enableVirtualWindowSwitchKeys) {
14575
- for (let n = 1; n <= 9; n++) {
14576
- // Q4 (substrate Occam): no per-binding pipe alias. Plain digit
14577
- // and Ctrl+digit are distinct chord bodies — register each.
14578
- registerVwChord(`digit-${n}`, `${n}`, vwKey(`${n}`));
14579
- registerVwChord(`ctrl-digit-${n}`, `C-${n}`, vwKey(`${n}`));
14580
- }
14581
- // Cycle next / previous — multiple aliases share a body action.
14582
- registerVwChord('next-n', 'n', vwKey('n'));
14583
- registerVwChord('next-dot', '.', vwKey('.'));
14584
- registerVwChord('next-angle', '>', vwKey('>'));
14585
- registerVwChord('prev-p', 'p', vwKey('p'));
14586
- registerVwChord('prev-comma', ',', vwKey(','));
14587
- registerVwChord('prev-angle', '<', vwKey('<'));
14588
- }
14589
- // Window lifecycle + toggles + splits + help + sync.
14590
- registerVwChord('modal-toggle', 't', vwKey('t'));
14591
- registerVwChord('hsplit-pct', '%', vwKey('%'));
14592
- registerVwChord('hsplit-s', 's', vwKey('s'));
14593
- registerVwChord('vsplit-quote', '"', vwKey('"'));
14594
- registerVwChord('vsplit-v', 'v', vwKey('v'));
14595
- registerVwChord('help', '?', vwKey('?'));
14596
- registerVwChord('sync-input-bar', 'i', vwKey('i'));
14597
- // Focus direction (arrow + vim).
14598
- registerVwChord('focus-up', 'up|k', vwKey('up'));
14599
- registerVwChord('focus-down', 'down|j', vwKey('down'));
14600
- registerVwChord('focus-left', 'left|h', vwKey('left'));
14601
- registerVwChord('focus-right', 'right|l', vwKey('right'));
14602
- // VW-U5 — last-focused pane is registered by registerDashboardGlobalKeys.
14603
- // VW-B1/B2 — rename chord bodies. Shift qualifier is accepted for
14604
- // muscle-memory (`^B R` = `^B Shift+r` in many layouts).
14605
- registerVwChord('rename-window', 'r|S-r', vwKey('r'));
14606
- registerVwChord('rename-pane', 'S-a', vwKey('a', { shift: true }));
14607
-
14608
- // N2 — `^B S` opens the shell-runner rollup popup (keyboard parity
14609
- // with the mouse-clickable 🐚 pill). Shift-qualified to avoid
14610
- // collision with `^B s` (hsplit); this convention matches VW-B1's
14611
- // `^B R` rename-window.
14612
- display.registerKeyBinding({
14613
- id: 'dashboard:vw-chord:shell-rollup',
14614
- chordPrefix: VW_CHORD_PREFIX,
14615
- key: 'S-s',
14616
- scope: 'global',
14617
- handler: () => { void openShellRollupPopup(); },
14618
- });
14619
-
14620
- // Bundle B-9 — `^B h` toggles the focused pane's visibility in the
14621
- // shared PaneVisualStateStore between `visible` and `hidden`. First
14622
- // user-writable path into the store (the LLM `SetFocusPolicy` tool
14623
- // from B-1 was the only writer before this). Alt+N window-skip
14624
- // (B-7-α) already consumes the store, so hiding every pane in a
14625
- // window removes it from the cycle. PLAN: PLAN-vw-term-bundle-b9-
14626
- // visibility-user-toggle.md. Handler factory lives in
14627
- // src/dashboard-vw-visibility-chord.ts for unit testability.
14628
- display.registerKeyBinding({
14629
- id: 'dashboard:vw-chord:visibility-toggle',
14630
- chordPrefix: VW_CHORD_PREFIX,
14631
- // `h` is already taken as the vim alias for focus-left
14632
- // (`registerVwChord('focus-left', 'left|h', ...)`); use Shift+H
14633
- // instead so the "H for Hide" mnemonic survives without collision.
14634
- key: 'S-h',
14635
- scope: 'global',
14636
- handler: createVisibilityChordHandler({
14637
- getCurrentWindow: () => {
14638
- const w = virtualWindows.registry.current();
14639
- return w ? { id: w.id, focused: w.focused } : null;
14640
- },
14641
- store: paneVisualStateStore,
14642
- showToast: (title, lines) => {
14643
- const { cols: tc, rows: tr } = termSize();
14644
- showToast({ title, lines, coordinator: display, termCols: tc, termRows: tr, ttlMs: 1500 });
14645
- try { draw(); } catch { /* TUI torn down */ }
14646
- },
14647
- }),
14648
- });
14649
-
14650
- // SP-D — `^B !` toggles the focused pane's focusPolicy between
14651
- // 'output-only' and 'interactive'. Doesn't go through NavigationRouter
14652
- // because the operation is pane-local, not window-level. Silent no-op
14653
- // when the focused pane doesn't advertise a focusPolicy (plain chat/
14654
- // markdown panes).
14655
- display.registerKeyBinding({
14656
- id: 'dashboard:vw-chord:focus-policy-toggle',
14657
- chordPrefix: VW_CHORD_PREFIX,
14658
- key: '!',
14659
- scope: 'global',
14660
- handler: () => {
14661
- const { cols: tc, rows: tr } = termSize();
14662
- const win = virtualWindows.registry.current();
14663
- if (!win) {
14664
- showToast({ title: 'focus policy', lines: ['no foreground window'], coordinator: display, termCols: tc, termRows: tr, ttlMs: 1500 });
14665
- return;
14666
- }
14667
- const pane = win.getFocusedPane();
14668
- type Togglable = {
14669
- focusPolicy?: 'output-only' | 'interactive';
14670
- setFocusPolicy?: (p: 'output-only' | 'interactive') => unknown;
14671
- };
14672
- const t = (pane ?? {}) as Togglable;
14673
- if (!t.setFocusPolicy || !t.focusPolicy) {
14674
- showToast({ title: 'focus policy', lines: ['pane has no focus policy'], coordinator: display, termCols: tc, termRows: tr, ttlMs: 1500 });
14675
- return;
14676
- }
14677
- const next = t.focusPolicy === 'output-only' ? 'interactive' : 'output-only';
14678
- t.setFocusPolicy(next);
14679
- const label = pane ? win.getPaneDisplayTitle(pane.id) : '';
14680
- showToast({
14681
- title: 'focus policy',
14682
- lines: [`${label || 'pane'} → ${next}`],
14683
- coordinator: display,
14684
- termCols: tc,
14685
- termRows: tr,
14686
- ttlMs: 1500,
14687
- });
14688
- try { draw(); } catch { /* TUI torn down */ }
14689
- },
14690
- });
14691
-
14692
- // VW-U2 — chord-free Alt+N/P/1..9/0 fast-switch (helper in
14693
- // src/dashboard-vw-fast-switch.ts so tests can exercise the
14694
- // bindings without spinning up the whole dashboard).
14695
- registerVwFastSwitchBindings({
14696
- display,
14697
- registry: virtualWindows.registry,
14698
- openPicker: () => openWindowPicker(),
14699
- enableWindowSwitchKeys: getUserConfig().dashboard.enableVirtualWindowSwitchKeys,
14700
- // B-7-δ — expose the same PaneVisualStateStore the chord writer
14701
- // (B-7-β) and badge reader (B-7-γ) consume, so Alt+o/Alt+O can
14702
- // skip hidden/dormant panes within the current window.
14703
- store: paneVisualStateStore,
14704
12857
  });
14705
12858
 
14706
- /** T2-P4 / T2-P5 — open the VW window picker. Called from the
14707
- * NavigationRouter onPicker callback (^B 0) and from /window
14708
- * picker. Empty-list short-circuit writes a hint to chatLines
14709
- * so the user knows why nothing popped. Uses the same
14710
- * agentSearchModal routing path as the agent-roster search
14711
- * modal so the readKey loop forwards keys into it unchanged. */
14712
- function openWindowPicker(): void {
14713
- const currentWindow = virtualWindows.registry.current();
14714
- const ownerWorkspaceId = currentWindow
14715
- ? workspaceOwnerIdForVirtualWindow(currentWindow.id)
14716
- : DASHBOARD_MAIN_WORKSPACE_ID;
14717
- openDashboardWindowPickerPopup({
14718
- registry: virtualWindows.registry,
14719
- ownerWorkspaceId,
14720
- termSize,
14721
- getTheme: () => currentThemeTokens(),
14722
- pushModalSurface: (surface) => display.pushModal(surface),
14723
- setAgentSearchModal: (modal) => {
14724
- agentSearchModal = modal;
14725
- },
14726
- onEmptyWindows: () => {
14727
- pushDebugLine(C.muted('No virtual windows. Use /window new (or WindowCreate tool) first.'));
14728
- chatScrollOffset = -1;
14729
- },
14730
- onAcceptMain: () => {
14731
- virtualWindows.registry.backgroundCurrent();
14732
- setWorkingFocus(firstPaneOfView(workingDir.view), 'window-picker-main');
14733
- },
14734
- onAcceptWindow: (w) => {
14735
- virtualWindows.registry.switchTo(w.id);
14736
- },
14737
- redraw: draw,
14738
- });
14739
- }
14740
-
14741
12859
  function currentWorkspaceOwnerId(): string {
14742
- const currentWindow = virtualWindows.registry.current();
14743
- return currentWindow
14744
- ? workspaceOwnerIdForVirtualWindow(currentWindow.id)
14745
- : DASHBOARD_MAIN_WORKSPACE_ID;
12860
+ return DASHBOARD_MAIN_WORKSPACE_ID;
14746
12861
  }
14747
12862
 
14748
12863
  function openSurfaceCatalogPopup(): void {
14749
- const currentWindow = virtualWindows.registry.current();
14750
12864
  void openCompactSurfaceCatalogPopup({
14751
12865
  getTargets: () => compactSurfaceRuntime.buildTargets(),
14752
12866
  openTarget: (surfaceId) => compactSurfaceRuntime.openTarget(surfaceId),
14753
- ownerWorkspaceId: currentWindow
14754
- ? workspaceOwnerIdForVirtualWindow(currentWindow.id)
14755
- : DASHBOARD_MAIN_WORKSPACE_ID,
12867
+ ownerWorkspaceId: DASHBOARD_MAIN_WORKSPACE_ID,
14756
12868
  termSize,
14757
12869
  getTheme: () => currentThemeTokens(),
14758
12870
  pushModalSurface: (surface) => display.pushModal(surface),
@@ -14773,24 +12885,15 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14773
12885
  let shellRollupDispose: (() => void) | null = null;
14774
12886
  async function openShellRollupPopup(): Promise<void> {
14775
12887
  const { getShellRegistry } = await import('../shell-runner/registry.js');
14776
- const currentWindow = virtualWindows.registry.current();
14777
12888
  await openDashboardShellRollupPopup({
14778
12889
  getShellRegistry,
14779
12890
  getCurrentDispose: () => shellRollupDispose,
14780
12891
  setCurrentDispose: (dispose) => {
14781
12892
  shellRollupDispose = dispose;
14782
12893
  },
14783
- resolveVwIdByLabel: (label) => {
14784
- const all = virtualWindows.registry.list();
14785
- const hit = all.find(w => virtualWindows.registry.spawnTitleOf(w.id) === label);
14786
- return hit ? hit.id : null;
14787
- },
14788
- switchVirtualWindow: (windowId) => {
14789
- virtualWindows.registry.switchTo(windowId);
14790
- },
14791
- ownerWorkspaceId: currentWindow
14792
- ? workspaceOwnerIdForVirtualWindow(currentWindow.id)
14793
- : DASHBOARD_MAIN_WORKSPACE_ID,
12894
+ resolveVwIdByLabel: () => null,
12895
+ switchVirtualWindow: () => { throw new Error('Virtual windows are unavailable in the essential dashboard'); },
12896
+ ownerWorkspaceId: DASHBOARD_MAIN_WORKSPACE_ID,
14794
12897
  termSize,
14795
12898
  getTheme: () => currentThemeTokens(),
14796
12899
  pushModalSurface: (surface) => display.pushModal(surface),
@@ -14806,108 +12909,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14806
12909
  });
14807
12910
  }
14808
12911
 
14809
- /** VW-U4 — open the right-click pane/window selector popup. Called
14810
- * from virtualWindows.registry's onShowSelector bridge. The popup
14811
- * covers panes of the source VW + every other window; Enter switches,
14812
- * Esc cancels. Uses the mouseWiring active-popup protocol (setting
14813
- * `surface.onKey = handle.handleKey`) so coordinator.routeKey feeds
14814
- * keys straight to the SelectView wrapper. */
14815
- let vwSelectorDispose: (() => void) | null = null;
14816
- function openVwSelectorPopup(windowId: number, col: number, row: number): void {
14817
- // Close any previous popup so rapid right-clicks don't stack.
14818
- if (vwSelectorDispose) {
14819
- try { vwSelectorDispose(); } catch { /* ignore */ }
14820
- vwSelectorDispose = null;
14821
- }
14822
- const { cols: tc, rows: tr } = termSize();
14823
- const handle = createVwSelectorPopup({
14824
- registry: virtualWindows.registry,
14825
- sourceWindowId: windowId,
14826
- col, row,
14827
- termCols: tc,
14828
- termRows: tr,
14829
- onFocusPane: (w, p) => {
14830
- const win = virtualWindows.registry.get(w);
14831
- if (win) win.setFocus(p);
14832
- draw();
14833
- },
14834
- onSwitchWindow: (w) => {
14835
- virtualWindows.registry.switchTo(w);
14836
- },
14837
- onCancel: () => { draw(); },
14838
- });
14839
- if (!handle) return;
14840
- handle.surface.onKey = (ev) => handle.handleKey(ev);
14841
- const modalHandle = display.pushModal(handle.surface);
14842
- vwSelectorDispose = () => {
14843
- try { handle.dispose(); } catch { /* ignore */ }
14844
- try { modalHandle.dispose(); } catch { /* ignore */ }
14845
- };
14846
- draw();
14847
- }
14848
-
14849
- let vwLocalInputTargetDispose: (() => void) | null = null;
14850
- const vwLocalInputTargetState = new Map<number, { query: string; cursor: number }>();
14851
- function openVwLocalInputTargetPopup(windowId: number, seedQuery?: string): void {
14852
- openVwLocalInputTargetPopupLauncher({
14853
- registry: virtualWindows.registry,
14854
- windowId,
14855
- seedQuery,
14856
- stateStore: vwLocalInputTargetState,
14857
- getCurrentDispose: () => vwLocalInputTargetDispose,
14858
- setCurrentDispose: (dispose) => {
14859
- vwLocalInputTargetDispose = dispose;
14860
- },
14861
- termSize,
14862
- pushModalSurface: (surface) => {
14863
- attachSurfaceToWorkspace(surface, currentWorkspaceOwnerId());
14864
- return display.pushModal(surface);
14865
- },
14866
- onTargetPick: (target) => {
14867
- const win = virtualWindows.registry.get(windowId);
14868
- if (win) {
14869
- win.setLocalInputTarget(target);
14870
- win.consumeLocalComposerMentionTargetToken();
14871
- }
14872
- },
14873
- redraw: draw,
14874
- });
14875
- }
14876
-
14877
- /** VW-B1/B2 — shared helper that pops an input modal for renaming
14878
- * either a window or a pane. Disposes any previous rename modal so
14879
- * typing R twice just re-opens with the latest title. */
14880
- let vwRenameDispose: (() => void) | null = null;
14881
- function openVwRenameModal(opts: {
14882
- title: string;
14883
- current: string;
14884
- onSubmit: (next: string) => void;
14885
- onCancel?: () => void;
14886
- }): void {
14887
- openVwRenameModalLauncher({
14888
- title: opts.title,
14889
- current: opts.current,
14890
- onSubmit: (next) => {
14891
- vwRenameDispose = null;
14892
- opts.onSubmit(next);
14893
- },
14894
- onCancel: () => {
14895
- vwRenameDispose = null;
14896
- opts.onCancel?.();
14897
- },
14898
- getCurrentDispose: () => vwRenameDispose,
14899
- setCurrentDispose: (dispose) => {
14900
- vwRenameDispose = dispose;
14901
- },
14902
- termSize,
14903
- pushModalSurface: (surface) => {
14904
- attachSurfaceToWorkspace(surface, currentWorkspaceOwnerId());
14905
- return display.pushModal(surface);
14906
- },
14907
- redraw: draw,
14908
- });
14909
- }
14910
-
14911
12912
  // Track which task ID the cursor is currently LOCKED onto across
14912
12913
  // draws — prevents the cursor from visually sliding when the roster
14913
12914
  // gets re-sorted with a new entry. Empty string = no lock.
@@ -15041,11 +13042,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15041
13042
  return memoWidgetRuntime.readLines(widgetHost.get('wd-memo') as any);
15042
13043
  };
15043
13044
  const publishMemoLines = (lines: string[]): void => {
15044
- const next = scratchStateRuntime.replace('Memo', lines);
15045
- scratchTitle = next.title;
15046
- scratchLines = next.lines;
15047
- scratchOffset = next.offset;
15048
- publishScratchSurface('dashboard:memo', 'preview', scratchTitle, scratchLines);
13045
+ scratchTitle = 'Memo';
13046
+ scratchLines = [...lines];
13047
+ scratchOffset = 0;
13048
+ publishScratchSurface('dashboard:memo', 'preview', scratchTitle, scratchLines);
15049
13049
  };
15050
13050
  const memoCompanionRuntime = createDashboardMemoCompanionRuntime({
15051
13051
  readLines: readMemoWidgetLines,
@@ -15064,26 +13064,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15064
13064
  memoCompanionRuntime.cancel();
15065
13065
  chatScrollOffset = -1;
15066
13066
  };
15067
- const closeVwMemoCompanion = (windowId: number): void => {
15068
- seedMemoWidget(['']);
15069
- const host = companionSurfaceHosts.ensure(vwCompanionOwnerId(windowId), ['clipboard', 'memo', 'detail']);
15070
- host.close('memo');
15071
- };
15072
- const commitVwMemoCompanion = (windowId: number): void => {
15073
- const host = companionSurfaceHosts.ensure(vwCompanionOwnerId(windowId), ['clipboard', 'memo', 'detail']);
15074
- const runtime = createDashboardMemoCompanionRuntime({
15075
- readLines: readMemoWidgetLines,
15076
- resetEditor: () => { seedMemoWidget(['']); },
15077
- close: () => { host.close('memo'); },
15078
- publishSaved: publishMemoLines,
15079
- pushSavedLine: (lineCount) => { chatLines.push(companionFeedbackRuntime.memoSavedLine(lineCount)); },
15080
- pushDiscardedLine: () => { chatLines.push(companionFeedbackRuntime.memoDiscardedLine()); },
15081
- pushCancelledLine: () => { chatLines.push(companionFeedbackRuntime.memoCancelledLine()); },
15082
- });
15083
- runtime.commit();
15084
- chatScrollOffset = -1;
15085
- };
15086
-
15087
13067
  // Repaint hook the active textInput populates so external draws
15088
13068
  // (scratchpad refresh, async chafa render finishing) can ask the
15089
13069
  // prompt to re-paint itself afterwards. While input is idle this
@@ -15135,8 +13115,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15135
13115
  requestRender: () => { requestDashboardRender('detail'); },
15136
13116
  });
15137
13117
  const companionWidgetRuntime = createDashboardCompanionWidgetRuntime();
15138
- const scratchWidgetRuntime = createDashboardScratchWidgetRuntime();
15139
- const scratchStateRuntime = createDashboardScratchStateRuntime();
15140
13118
  const clipboardHistoryRuntime = createDashboardClipboardHistoryRuntime();
15141
13119
  const memoWidgetRuntime = createDashboardMemoWidgetRuntime();
15142
13120
  const logWidgetRuntime = createDashboardLogWidgetRuntime();
@@ -15479,26 +13457,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15479
13457
  * display coordinator. The coordinator coalesces this with any
15480
13458
  * plugin/widget updates and repaints the prompt after the frame. */
15481
13459
  const setScratch = (title: string, lines: string[]): void => {
15482
- const next = scratchStateRuntime.replace(title, lines);
15483
- scratchTitle = next.title;
15484
- scratchLines = next.lines;
15485
- scratchOffset = next.offset;
13460
+ scratchTitle = title;
13461
+ scratchLines = [...lines];
13462
+ scratchOffset = 0;
15486
13463
  publishScratchSurface('dashboard:scratch', 'preview', title, scratchLines);
15487
13464
  };
15488
13465
 
15489
- const currentScratchForCommand = (): { title: string; lines: string[] } => {
15490
- return scratchStateRuntime.resolveCommandSnapshot(display.scratchState(), {
15491
- title: scratchTitle,
15492
- lines: scratchLines,
15493
- });
15494
- };
15495
-
15496
- const { setScratchImage, setScratchFile } = createRichScratchViewers({
15497
- setDetailViewer,
15498
- termSize,
15499
- fmtBytes,
15500
- });
15501
-
15502
13466
  // ── Execute sync inline (output to log pane) ──
15503
13467
  const runSyncInline = async (
15504
13468
  sk: string[] = [...sync.selected[0]!],
@@ -17314,8 +15278,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17314
15278
  }
17315
15279
 
17316
15280
  // Ctrl+W (no chord armed — chord would have consumed the
17317
- // key earlier) closes the scratch pane. Reopen via
17318
- // /scratch open or the ^B ^S chord target.
15281
+ // key earlier) closes the scratch pane. Reopen via the ^B ^S chord.
17319
15282
  if (key.ctrl && (key.name === 'w' || key.name === 'ㅈ')) {
17320
15283
  scratchClosed = true;
17321
15284
  userClosedPanes.add('scratch');
@@ -17415,8 +15378,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17415
15378
  ).persistToolOutputPreview;
17416
15379
  acpPtyAvailable = (await import('../pty-shell/registry.js')).ptyAvailable;
17417
15380
  const dashboardAcpBootResult = await bootDashboardAcpSession({
17418
- ...(opts.remote ? { remote: opts.remote } : {}),
17419
- ...(opts.localDaemon ? { localDaemon: opts.localDaemon } : {}),
15381
+ ...(opts.remote ? {
15382
+ remote: opts.remote,
15383
+ onRemoteDisconnect: () => {
15384
+ pushChatLine(C.warning(' ⚠ 데몬 연결이 끊겼다 · 입력 중인 글은 그대로다 · 다시 붙으려면 TUI 를 다시 띄워라'));
15385
+ draw();
15386
+ },
15387
+ } : {}),
17420
15388
  ...(opts.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {}),
17421
15389
  getCwd: () => getSessionCwd(),
17422
15390
  getChatHistory: () => chat.history,
@@ -17437,7 +15405,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17437
15405
  pluginTools: pluginHost.activePluginLLMTools(),
17438
15406
  optionalTools: acpTurnRef.optionalSpecs,
17439
15407
  surface: acpTurnRef.turnProfile?.surface,
17440
- rich: dashboardUiMode === 'rich',
15408
+ rich: false,
17441
15409
  })
17442
15410
  : [];
17443
15411
  return buildDashboardTurnPreamble({
@@ -17451,7 +15419,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17451
15419
  // codex-only behavioral addendum (fix L-1) inside the
17452
15420
  // universal preamble.
17453
15421
  modelFamily: getModelFamily(inspectActiveProvider().model),
17454
- rich: dashboardUiMode === 'rich',
15422
+ rich: false,
17455
15423
  enabledTools: tools.map(t => t.name),
17456
15424
  });
17457
15425
  },
@@ -17465,7 +15433,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17465
15433
  pluginTools: pluginHost.activePluginLLMTools(),
17466
15434
  optionalTools: acpTurnRef.optionalSpecs,
17467
15435
  surface: acpTurnRef.turnProfile?.surface,
17468
- rich: dashboardUiMode === 'rich',
15436
+ rich: false,
17469
15437
  });
17470
15438
  // ⭐ Remember this turn's catalog so a spawned sub-agent can inherit it.
17471
15439
  //
@@ -17630,18 +15598,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17630
15598
  try { acpSessionIdRef.current = dashboardAcpSession.currentSessionId ?? null; } catch { /* 없으면 넣지 않는다 */ }
17631
15599
  pushDebugLine(C.muted('[acp-boot] dashboard ACP session ready'));
17632
15600
  // User-visible mode indicator. Without this, a user who set
17633
- // ELANOUS_REMOTE / ELANOUS_USE_DAEMON has no way to tell whether the
17634
- // Attach actually succeeded — attached daemon modes are useful
15601
+ // ELANOUS_REMOTE has no way to tell whether the
15602
+ // attach actually succeeded — the attached daemon mode is useful
17635
15603
  // chat transcript context, but the local in-process fallback is
17636
15604
  // runtime plumbing and belongs in the debug log instead.
17637
15605
  if (opts.remote) {
17638
15606
  const authNote = opts.remote.token ? 'token' : 'no-auth';
17639
15607
  const labelNote = opts.remote.label ? ` · ${opts.remote.label}` : '';
17640
15608
  chatLines.push(C.success(` ✓ attached to remote daemon: ${opts.remote.url} (${authNote})${labelNote}`));
17641
- } else if (opts.localDaemon) {
17642
- chatLines.push(C.success(` ✓ attached to local daemon: ${opts.localDaemon.socketPath}`));
17643
15609
  } else {
17644
- pushDebugLine(C.muted(' in-process ACP (no daemon attach · set ELANOUS_REMOTE or ELANOUS_USE_DAEMON to attach)'));
15610
+ pushDebugLine(C.muted(' in-process ACP (no daemon attach · set ELANOUS_REMOTE to attach)'));
17645
15611
  }
17646
15612
  // Tier 1 daemon-resume status. Three branches:
17647
15613
  // - resumed: attachExisting succeeded · the daemon's prior
@@ -17655,9 +15621,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17655
15621
  if (dashboardAcpBootResult.mode === 'resumed' && dashboardAcpBootResult.sessionId) {
17656
15622
  const sid = dashboardAcpBootResult.sessionId;
17657
15623
  chatLines.push(C.success(` ✓ resumed daemon session: ${sid}`));
17658
- // Best-effort REST history replay — only the remote (HTTP/WS)
17659
- // path has a derivable HTTP base. Local-daemon (unix socket)
17660
- // can't fetch over REST today; that's a follow-up.
15624
+ // Best-effort REST history replay over the remote HTTP/WS endpoint.
17661
15625
  if (opts.remote) {
17662
15626
  const httpBase = deriveDaemonHttpBase(opts.remote.url);
17663
15627
  if (httpBase) {
@@ -17693,8 +15657,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17693
15657
  chatLines.push(C.muted(` no prior turns found for ${sid}.`));
17694
15658
  }
17695
15659
  }
17696
- } else {
17697
- chatLines.push(C.muted(' (history replay over unix socket is a follow-up — REST endpoint required)'));
17698
15660
  }
17699
15661
  } else if (dashboardAcpBootResult.mode === 'new' && dashboardAcpBootResult.resumeFallbackReason) {
17700
15662
  // The user asked to resume but the daemon rejected the id —
@@ -17704,7 +15666,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17704
15666
  const reason = dashboardAcpBootResult.resumeFallbackReason;
17705
15667
  const newId = dashboardAcpBootResult.sessionId ?? '(unknown)';
17706
15668
  chatLines.push(C.warning(` ⚠ resume failed (${reason}); started new session: ${newId}`));
17707
- } else if (opts.resumeSessionId && !opts.remote && !opts.localDaemon) {
15669
+ } else if (opts.resumeSessionId && !opts.remote) {
17708
15670
  // Match `/session load <prefix>` for in-process boot: resolve the
17709
15671
  // local prefix, preserve the stock system prompt, and attach the
17710
15672
  // restored session for subsequent persistence.
@@ -17746,6 +15708,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17746
15708
  const key: import('../tui.js').Key = wdAutoInput
17747
15709
  ? { name: CHAT_MAIN_AUTO_ENTRY_KEY_NAME, ctrl: false, shift: false }
17748
15710
  : await readKey();
15711
+ const helpModal = currentTransientTerminalModal('dashboard-help');
15712
+ if (key.name === 'escape' && helpModal) {
15713
+ helpModal.dispose();
15714
+ draw();
15715
+ continue;
15716
+ }
17749
15717
 
17750
15718
  // U-0 · recompute ViewMode at the top of every key dispatch
17751
15719
  // loop iteration. This is the one natural "mode re-evaluation"
@@ -17823,13 +15791,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17823
15791
  && voiceHostFlow.getState().kind === 'idle'
17824
15792
  && workingDir.focus !== 'input'
17825
15793
  ) {
17826
- const focusedTerminalPane = (() => {
17827
- try {
17828
- const vw = virtualWindows?.registry.current();
17829
- const pane = vw?.getFocusedPane();
17830
- return pane?.kind === 'terminal' || pane?.kind === 'terminal-slot';
17831
- } catch { return false; }
17832
- })();
15794
+ const focusedTerminalPane = false;
17833
15795
 
17834
15796
  // Branch 1 — Ctrl+Shift+D trial chord. TEMP DEBUG (2026-04-29):
17835
15797
  // toggle pattern instead of long-press detector. First press
@@ -18069,23 +16031,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18069
16031
  if (result === 'closed') draw();
18070
16032
  return true;
18071
16033
  },
18072
- routeVwTerminalKey: (routeKey) => {
18073
- // Claim when the foreground VW's focused pane is a live
18074
- // terminal — forward the key directly to the pane's PTY
18075
- // and skip every later step (input-core, chord, global,
18076
- // layout). The pane's onKey returns an Action; we only
18077
- // care about triggering a redraw on 'refresh'.
18078
- const vw = virtualWindows.registry.current();
18079
- if (!vw) return false;
18080
- const focusedPane = vw.getFocusedPane();
18081
- if (!focusedPane) return false;
18082
- const isTerminalKind = focusedPane.kind === 'terminal' || focusedPane.kind === 'terminal-slot';
18083
- if (!isTerminalKind) return false;
18084
- const ev = toDashboardKeyEvent(routeKey);
18085
- const action = focusedPane.onKey(ev);
18086
- if (action.type === 'refresh') draw();
18087
- return true;
18088
- },
18089
16034
  routeArmedChordKey: async (routeKey) => {
18090
16035
  if (!isChordArmed(chord)) return false;
18091
16036
  disarmChordHud();
@@ -18134,44 +16079,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18134
16079
  if (routeKey.name === 'ㅃ') return true;
18135
16080
  return false;
18136
16081
  },
18137
- // Step 0a — Alt+W close. Replaces the removed Ctrl+Shift+T close
18138
- // chord (which collided with shell-level Ctrl+T finders). Fires
18139
- // from popup terminal modal AND VW with terminal pane focused.
18140
- // Korean ㅈ is the W-position jamo on 2-bul, included for IME
18141
- // parity. Does nothing when neither a popup nor a VW-terminal
18142
- // is active — the chord is targeted at popup-style surfaces.
18143
- routePopupCloseChord: (routeKey) => {
18144
- if (!routeKey.alt) return false;
18145
- const isW = routeKey.name === 'w' || routeKey.name === 'W' || routeKey.name === 'ㅈ';
18146
- if (!isW) return false;
18147
- // Popup terminal modal close
18148
- if (terminalModalRouter.current() !== null) {
18149
- if (debug.enabled) {
18150
- debug.log('window.terminalModalRouter.handleKey.close', 'alt-w', {
18151
- id: terminalModalRouter.current()?.id,
18152
- });
18153
- }
18154
- terminalModalRouter.close();
18155
- draw();
18156
- return true;
18157
- }
18158
- // VW with terminal pane focused — close the VW
18159
- const vwForClose = virtualWindows.registry.current();
18160
- if (vwForClose) {
18161
- const focusedPane = vwForClose.getFocusedPane();
18162
- if (focusedPane && (focusedPane.kind === 'terminal' || focusedPane.kind === 'terminal-slot')) {
18163
- if (debug.enabled) {
18164
- debug.log('window.vw.close', 'alt-w', {
18165
- windowId: vwForClose.id, paneKind: focusedPane.kind,
18166
- });
18167
- }
18168
- virtualWindows.registry.close(vwForClose.id);
18169
- draw();
18170
- return true;
18171
- }
18172
- }
18173
- return false;
18174
- },
18175
16082
  // PR-S1V.4-wiring · Step 0x — voice mode entry chord.
18176
16083
  // Fires only when voice mode is currently `idle`; once active
18177
16084
  // the next step (routeVoiceModeKey) takes over. Skipping when
@@ -18206,7 +16113,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18206
16113
  // experiment/voice-chat-realtime-rebind · Step 0s — modal-A
18207
16114
  // swallow while voice-chat is active. Returns true when the
18208
16115
  // controller is non-idle so any key not already claimed above
18209
- // (force-quit · Alt+W · Alt+digit · chord toggle) is consumed
16116
+ // (force-quit · Alt+W · chord toggle) is consumed
18210
16117
  // — typing doesn't leak into chat input, Space doesn't toggle
18211
16118
  // pickers, etc. ESC explicitly exits. Idle controller returns
18212
16119
  // false so legacy dispatch is reachable.
@@ -18322,36 +16229,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18322
16229
  })();
18323
16230
  return true;
18324
16231
  },
18325
- // Step 0b — Alt+digit VW switch. Single-keystroke window
18326
- // navigation that survives the popup-terminal / VW-terminal
18327
- // priority forwarding. Alt+1 → 1st VW (sorted ascending by
18328
- // numeric id), Alt+2 → 2nd, …, Alt+9 → 9th. Alt+0 is
18329
- // intentionally omitted (digit-argument convention conflict
18330
- // is rare; reserve for future use).
18331
- routeVwSwitchChord: (routeKey) => {
18332
- if (!routeKey.alt) return false;
18333
- if (routeKey.name.length !== 1) return false;
18334
- const digit = routeKey.name.charCodeAt(0);
18335
- if (digit < 0x31 || digit > 0x39) return false; // '1' .. '9'
18336
- const ordinal = digit - 0x30; // 1..9
18337
- const windows = virtualWindows.registry.list().sort((a, b) => a.id - b.id);
18338
- const target = windows[ordinal - 1];
18339
- if (!target) {
18340
- if (debug.enabled) {
18341
- debug.log('window.vw.switch.miss', `alt-${ordinal}`, {
18342
- ordinal, windowCount: windows.length,
18343
- });
18344
- }
18345
- return true; // claim anyway so it doesn't leak to the child
18346
- }
18347
- if (debug.enabled) {
18348
- debug.log('window.vw.switch', `alt-${ordinal}`, {
18349
- ordinal, targetId: target.id,
18350
- });
18351
- }
18352
- virtualWindows.registry.switchTo(target.id);
18353
- return true;
18354
- },
18355
16232
  // Step 6 hard-quit — only reached when no popup terminal is
18356
16233
  // active. Plain Ctrl+Q stays as the dashboard-mode shortcut
18357
16234
  // so muscle memory survives outside the popup. Inside the
@@ -18359,12 +16236,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18359
16236
  // to force-quit instead).
18360
16237
  isHardQuitKey: (routeKey) => routeKey.ctrl && !routeKey.shift && (routeKey.name === 'q' || routeKey.name === 'ㅂ'),
18361
16238
  matchGlobalAction: (routeKey) => matchTextInputGlobalAction(routeKey, {
18362
- allowSpawnTerminalModal: !(workingDir.focus === 'preview'
18363
- && previewTerminal
18364
- && (previewTerminal as PreviewTerminal).isAlive),
18365
- allowToggleLogZoom: !(workingDir.focus === 'preview'
18366
- && previewTerminal
18367
- && (previewTerminal as PreviewTerminal).isAlive),
16239
+ allowSpawnTerminalModal: false,
16240
+ allowToggleLogZoom: false,
18368
16241
  }),
18369
16242
  runGlobalAction: async (action) => {
18370
16243
  await runChatMainGlobalAction(action);
@@ -18828,8 +16701,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18828
16701
  initialText: inputEntry?.initialText ?? null,
18829
16702
  visibility: chatMainInputVisibilityState(),
18830
16703
  topBlockingForegroundModal: topBlockingForegroundModal()?.id ?? null,
18831
- currentVwId: virtualWindows.registry.current()?.id ?? null,
18832
- currentVwHostChromeProfile: virtualWindows.registry.current()?.getHostChromeProfile?.() ?? null,
16704
+ currentVwId: null,
16705
+ currentVwHostChromeProfile: null,
18833
16706
  });
18834
16707
  }
18835
16708
  if (inputEntry) {
@@ -18910,10 +16783,20 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18910
16783
  : '/quit /clear (run `elanous setup` — no LLM provider available)',
18911
16784
  debugLog: debug.enabled ? (event, label, payload) => debug.log(event, label, payload) : undefined,
18912
16785
  textInputOpts: {
16786
+ onScrollKey: (name) => {
16787
+ const { rows } = termSize();
16788
+ const viewport = computePromptFrameLogViewportBounds(
16789
+ rows, computePaneH(rows), getLayoutPromptFrame(rows),
16790
+ );
16791
+ chatScrollOffset = nextChatScrollOffset(
16792
+ chatScrollOffset, name, Math.max(1, viewport.height),
16793
+ Math.max(0, chatLines.length - viewport.height),
16794
+ );
16795
+ draw();
16796
+ },
18913
16797
  // TUI 부활 후속 — Esc·Esc = /rewind 픽커 (codex backtrack
18914
16798
  // 제스처 · resolveEscEscRewind 순수 판정). 빈 버퍼에서만
18915
- // prime · 1.5s 창 · rich 모드는 Esc 가 입력 루프를 이탈
18916
- // 하므로 사실상 essential 전용(의도). 힌트는 세션 1회.
16799
+ // prime · 1.5s 창. 힌트는 세션 1회.
18917
16800
  onEscape: (bufferText) => {
18918
16801
  const decision = resolveEscEscRewind({
18919
16802
  bufferText: bufferText ?? '',
@@ -19216,6 +17099,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19216
17099
  hasActiveStatusPopup: () => mouseWiring.hasActivePopup(),
19217
17100
  hasTerminalModal: () => terminalModalRouter.current() !== null,
19218
17101
  shouldRouteForegroundModalKey: (key) => {
17102
+ if (key.name === 'escape' && currentTransientTerminalModal('dashboard-help')) return true;
19219
17103
  const topModalId = display.modalStack().at(-1) ?? null;
19220
17104
  const topModal = topModalId ? display.surface(topModalId) : null;
19221
17105
  const topModalSurface =
@@ -19242,7 +17126,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19242
17126
  toKeyEvent: toDashboardKeyEvent,
19243
17127
  routeStatusPopupKey: key => mouseWiring.routeKey(key as never),
19244
17128
  handleTerminalModalKey: keyEvent => terminalModalRouter.handleKey(keyEvent),
19245
- tryRouteForegroundModalKey: keyEvent => display.tryRouteKeyToTopModalAsync(keyEvent),
17129
+ tryRouteForegroundModalKey: async keyEvent => {
17130
+ if (keyEvent.name === 'escape') {
17131
+ const helpModal = currentTransientTerminalModal('dashboard-help');
17132
+ if (helpModal) {
17133
+ helpModal.dispose();
17134
+ draw();
17135
+ return 'consumed';
17136
+ }
17137
+ }
17138
+ return display.tryRouteKeyToTopModalAsync(keyEvent);
17139
+ },
19246
17140
  redraw: draw,
19247
17141
  dispatchMouse: runTextInputOnMouseUnifiedDispatch,
19248
17142
  }),
@@ -19604,6 +17498,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19604
17498
  closeTui,
19605
17499
  exitTui: exitDashboardTui,
19606
17500
  showHelp,
17501
+ showHelpModal: ({ title, lines }) => {
17502
+ const { cols, rows } = termSize();
17503
+ showTransientTerminalModal({ title, lines: [...lines], coordinator: display, termCols: cols, termRows: rows, ttlMs: 0, group: 'dashboard-help' });
17504
+ },
19607
17505
  clearLogSearch,
19608
17506
  clearLogFilter,
19609
17507
  forkAttachedSessionFromChatHistory,
@@ -19628,6 +17526,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19628
17526
  controlSignalSlashRuntime,
19629
17527
  browserCdpSlashRuntime,
19630
17528
  widgetHost,
17529
+ backgroundTasksTextOnly: true,
19631
17530
  widgetModalPopup: {
19632
17531
  open: (s) => {
19633
17532
  // Wave P4b-1 — bridge slash → widget-modal-popup.
@@ -19684,10 +17583,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19684
17583
  attachClipboardImage,
19685
17584
  setNextInitial: (token) => { nextInitial = token; },
19686
17585
  },
19687
- uiModeSlash: {
19688
- getMode: () => dashboardUiMode,
19689
- setMode: (mode) => { applyDashboardUiMode(mode, { persist: true, announce: true }); },
19690
- },
19691
17586
  sessionResume: {
19692
17587
  openPicker: () => { openSessionResumePicker(); },
19693
17588
  },
@@ -19709,23 +17604,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19709
17604
  refresh: refreshInputHistory,
19710
17605
  openDetailViewer: (title, lines) => { setDetailViewer(title, lines); },
19711
17606
  },
19712
- terminal: {
19713
- fullscreenMissingLine: () => termSlashRuntime.fullscreenMissingLine(),
19714
- showFullscreenToast: (opts) => {
19715
- const { cols: tc, rows: tr } = termSize();
19716
- showToast({ title: opts.title, lines: opts.lines, coordinator: display, termCols: tc, termRows: tr });
19717
- },
19718
- },
19719
17607
  undo: {
19720
17608
  refreshGitDirty: () => { refreshGitDirty(getSessionCwd(), { force: true }); },
19721
17609
  bold: C.bold,
19722
17610
  },
19723
- ptyPane: {
19724
- spawnPtyTailWindow: (opts) => virtualWindows.registry.spawn({
19725
- title: opts.title,
19726
- initialContent: { kind: 'pty-tail', ptyId: opts.ptyId },
19727
- }),
19728
- },
19729
17611
  delta: {
19730
17612
  openBrowserPopup: (scope, limit, browserMode) =>
19731
17613
  openSourceDeltaBrowserPopup(scope, limit, browserMode),
@@ -19800,24 +17682,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19800
17682
  },
19801
17683
  compact: {
19802
17684
  getHistory: () => chat.history as unknown as { role: string; [k: string]: unknown }[],
19803
- compactBoundaryEnabled: () => getUserConfig().chat.rendering.compactBoundary.enabled,
19804
17685
  },
19805
17686
  skill: {
19806
17687
  triggersSlashRuntime: skillTriggersSlashRuntime,
19807
17688
  runSlashRuntime: runSkillSlashRuntime,
19808
17689
  runByName: (name, skillArgs) => runSkillByName(name, skillArgs),
19809
17690
  },
19810
- bench: {
19811
- slashRuntime: benchSlashRuntime,
19812
- spawn: (spec) => spawnLLMBenchmark(
19813
- { prompt: spec.prompt, providers: [...spec.providers] },
19814
- {
19815
- registry: virtualWindows.registry,
19816
- eventBus: virtualWindows.bus,
19817
- addressBook: virtualWindows.book,
19818
- },
19819
- ),
19820
- },
19821
17691
  setup: {
19822
17692
  launchPopup: (cmd) => {
19823
17693
  const session = TerminalPopup.shell()
@@ -19902,40 +17772,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19902
17772
  focusPopup: (direction) => focusConversationPopup(direction),
19903
17773
  openModal: (sessionId) => openConversationModal(sessionId),
19904
17774
  },
19905
- scratch: {
19906
- slashRuntime: scratchSlashRuntime,
19907
- isClosed: () => scratchClosed,
19908
- open: () => {
19909
- scratchClosed = false;
19910
- userClosedPanes.delete('scratch');
19911
- setWorkingFocus('scratch', 'slash-scratch-open');
19912
- },
19913
- close: () => {
19914
- scratchClosed = true;
19915
- userClosedPanes.add('scratch');
19916
- if (workingDir.focus === 'scratch') {
19917
- applyFocusToPaneTransition(resolveFocusToPaneTransition({
19918
- targetPane: 'log',
19919
- reason: 'slash-scratch-close',
19920
- }));
19921
- }
19922
- },
19923
- clear: () => { setScratch('', []); },
19924
- currentForCommand: () => currentScratchForCommand(),
19925
- snapshotForAppend: () => ({ title: scratchTitle, lines: scratchLines }),
19926
- setText: (title, lines) => { setScratch(title, [...lines]); },
19927
- buildDumpLines: (lines) => buildDashboardScratchDumpLines([...lines]),
19928
- popup: {
19929
- open: () => { setCompanionPopupOpen('scratch', true); },
19930
- close: () => { setCompanionPopupOpen('scratch', false); },
19931
- toggle: () => toggleCompanionPopup('scratch'),
19932
- promote: () => { promoteCompanionPopup('scratch'); },
19933
- },
19934
- openMemo: () => { openMemoCompanion(); },
19935
- },
19936
17775
  sim: {
19937
17776
  slashRuntime: simSlashRuntime,
19938
- spawnVirtualWindow: () => spawnSimVirtualWindow(),
19939
17777
  openWebCockpit: () => openDashboardSimulationWebCockpit(),
19940
17778
  listScenarios: () => listDashboardSimulationScenarios(),
19941
17779
  runById: (scenarioId) => runDashboardSimulationById(scenarioId as Parameters<typeof runDashboardSimulationById>[0]),
@@ -19959,39 +17797,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19959
17797
  return aliases.get(target) ?? null;
19960
17798
  },
19961
17799
  },
19962
- view: {
19963
- slashRuntime: viewSlashRuntime,
19964
- buildListItems: () => viewRegistry.allViews.map((v) => ({
19965
- active: activeViewId === v.id,
19966
- id: v.id,
19967
- label: v.label,
19968
- enabled: v.enabled,
19969
- shortcut: v.shortcut,
19970
- // List-entry contract types baseView as a display string;
19971
- // the registry holds it as a WorkingDirView number.
19972
- baseView: String(v.baseView),
19973
- })),
19974
- closedPaneLabels: () => closedStarterPanes().map((pane) => paneLabel(pane)),
19975
- next: () => { setActiveDashboardView(nextDashboardView(viewRegistry, activeViewId, 1)); },
19976
- prev: () => { setActiveDashboardView(nextDashboardView(viewRegistry, activeViewId, -1)); },
19977
- reload: () => {
19978
- reloadUserConfig();
19979
- reloadDashboardViews();
19980
- },
19981
- save: () => { saveDashboardViewsConfig(); },
19982
- restoreAllClosed: () => { restoreAllClosedDashboardPanes(); },
19983
- reset: () => { resetDashboardViewsConfig(); },
19984
- exportConfigJson: () => JSON.stringify(serializeDashboardViewsConfig(viewRegistry), null, 2),
19985
- openByQuery: (query) => {
19986
- const def = findDashboardView(viewRegistry, query);
19987
- if (def) {
19988
- setActiveDashboardView(def);
19989
- return true;
19990
- }
19991
- return false;
19992
- },
19993
- openDetailViewer: (title, lines) => { setDetailViewer(title, lines); },
19994
- },
19995
17800
  intake: {
19996
17801
  runSlash: async (intakeArgs) => {
19997
17802
  try {
@@ -20019,7 +17824,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20019
17824
  },
20020
17825
  };
20021
17826
  const r = await resolveIntakeSlash([...intakeArgs], {
20022
- getScratchSnapshot: currentScratchForCommand,
17827
+ getScratchSnapshot: () => {
17828
+ const snapshot = display.scratchState();
17829
+ return snapshot && snapshot.mode !== 'agents'
17830
+ ? { title: snapshot.title, lines: [...snapshot.lines] }
17831
+ : { title: scratchTitle, lines: [...scratchLines] };
17832
+ },
20023
17833
  });
20024
17834
  if (shouldOpenDashboardIntakeSessionPicker([...intakeArgs], r)) {
20025
17835
  openDashboardIntakeSessionPicker(r.sessions, intakeUiDeps);
@@ -20263,44 +18073,22 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20263
18073
  remoteDaemon: () => opts.remote
20264
18074
  ? { url: opts.remote.url, token: opts.remote.token }
20265
18075
  : null,
20266
- localDaemon: () => opts.localDaemon
20267
- ? { socketPath: opts.localDaemon.socketPath }
20268
- : null,
20269
18076
  acpSwapTo: (sessionId, cwd) => dashboardAcpSession.swapTo(sessionId, cwd),
20270
18077
  },
20271
18078
  shellSlash: {
20272
18079
  slashRuntime: shellSlashRuntime,
20273
18080
  openRollupPopup: () => openShellRollupPopup(),
20274
- virtualWindowsSwitchTo: (windowId) => { virtualWindows.registry.switchTo(windowId); },
20275
- resolveVwIdByLabel: (label) => {
20276
- const all = virtualWindows.registry.list();
20277
- const hit = all.find(w => virtualWindows.registry.spawnTitleOf(w.id) === label);
20278
- return hit ? hit.id : null;
20279
- },
20280
- },
20281
- workspaceSlash: {
20282
- slashRuntime: windowSlashRuntime,
20283
- registry: virtualWindows.registry as unknown as DashboardSlashContext['workspaceSlash']['registry'],
20284
- spawnScratchVirtualWindow,
20285
- spawnBrowserVirtualWindow,
20286
- spawnPreviewVirtualWindow,
20287
- spawnBrowserPreviewVirtualWindow,
20288
- spawnIulVirtualWindow,
20289
- spawnAcpVirtualWindow,
20290
- spawnSimVirtualWindow,
20291
- openWindowPicker: () => openWindowPicker(),
20292
- toggleVwCompanion: (windowId, key) => toggleVwCompanion(windowId, key as Parameters<typeof toggleVwCompanion>[1]),
20293
- setVwCompanionOpen: (windowId, key, open) => setVwCompanionOpen(windowId, key as Parameters<typeof setVwCompanionOpen>[1], open),
20294
- },
20295
- acpVwSlash: {
20296
- cwd: () => workingDir.cwd,
20297
- vwRegistry: virtualWindows.registry,
18081
+ virtualWindowsSwitchTo: () => { throw new Error('Virtual windows are unavailable in the essential dashboard'); },
18082
+ resolveVwIdByLabel: () => null,
20298
18083
  },
20299
18084
  termSlash: {
20300
18085
  slashRuntime: termSlashRuntime,
20301
18086
  sessionRegistry: sessionRegistry as unknown as DashboardSlashContext['termSlash']['sessionRegistry'],
20302
18087
  terminalMatrix: terminalMatrix as unknown as DashboardSlashContext['termSlash']['terminalMatrix'],
20303
- vwRegistry: virtualWindows.registry as unknown as DashboardSlashContext['termSlash']['vwRegistry'],
18088
+ vwRegistry: {
18089
+ current: () => null,
18090
+ get: () => null,
18091
+ },
20304
18092
  broadcastBus: broadcastBus as unknown as DashboardSlashContext['termSlash']['broadcastBus'],
20305
18093
  channelBus: channelBus as unknown as DashboardSlashContext['termSlash']['channelBus'],
20306
18094
  openSessionPicker: () => {
@@ -20563,15 +18351,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20563
18351
  }
20564
18352
  continue;
20565
18353
  }
20566
- // T7-N2 — spawn coding-agent CLI in a new VW pane.
20567
- // /acp-vw [claude|codex] — legacy, brand via sub-arg
20568
- // /claude-vw — direct alias (default brand claude-code)
20569
- // /codex-vw — direct alias (brand codex)
20570
- //
20571
- // Sprint 5B (2026-04-28) removed the `/acp-vw cxn` path
20572
- // (codex-native via Embodied Agent Bus) — codex-native
20573
- // source is gone, codex-app-server is canonical.
20574
- // B-3.d migrated: /codex-vw /acp-vw /claude-vw → registry handlers.
20575
18354
  // B-2.w migrated: /conv → registry handler.
20576
18355
  // H5 P3 · /handoff <fromId> <toBrand> [--channels reasoning,plan]
20577
18356
  // Cross-agent snapshot-based context transfer.
@@ -20585,7 +18364,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20585
18364
  // B-2.l migrated: /agents /agent /ag → registry handler.
20586
18365
  // B-1.f migrated: /clipboard /clip /cb · /memo /note /me ·
20587
18366
  // /detail /report /dv → registry handlers (companion ctx field).
20588
- // B-2.v migrated: /scratch /sc → registry handler.
20589
18367
  // B-2.o migrated: /intake → registry handler.
20590
18368
  // B-1.g migrated: /signals /signal → registry handler (controlSignalSlashRuntime ctx field).
20591
18369
  case 'media':
@@ -20727,17 +18505,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20727
18505
  const handled = await pluginHost.dispatchSlash(cmdLower, args);
20728
18506
  if (handled) { chatScrollOffset = -1; continue; }
20729
18507
  }
20730
- const retiredHarnessCommand = ({
20731
- ask: '/harness ask <무엇을 왜 고칠지 한 문장>',
20732
- say: '/harness ask <무엇을 왜 고칠지 한 문장>',
20733
- implement: '/harness implement <feature>',
20734
- dev: '/harness dev <무엇을 왜 고칠지 한 문장>',
20735
- } as Record<string, string>)[cmdLower];
20736
- if (retiredHarnessCommand) {
20737
- chatLines.push(C.warning(`/${cmdLower} has moved; use ${retiredHarnessCommand}`));
20738
- } else {
20739
- chatLines.push(C.warning(`Unknown command: /${cmdLower}`));
20740
- chatLines.push(C.muted('Available: /view /preview /scratch /intake /run-skill /skill-triggers /skill-reload /provider /context /paste /sync /widget /plugin /chat /clear /help /quit /research'));
18508
+ const replyLines = unknownSlashReply(cmdLower, dashboardSlashRegistry.names());
18509
+ for (const [index, line] of replyLines.entries()) {
18510
+ chatLines.push(index === 0 ? C.warning(line) : C.muted(line));
20741
18511
  }
20742
18512
  chatScrollOffset = -1;
20743
18513
  continue; // stay in input mode
@@ -20749,7 +18519,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20749
18519
 
20750
18520
  // ── Q&A chat — requires at least one LLM provider ──
20751
18521
  if (!anyProviderAvailable()) {
20752
- chatLines.push(C.warning('No LLM provider available. Run `elanous setup` or `elanous codex setup`.'));
18522
+ const reply = noProviderReply(input.text);
18523
+ for (const line of reply.lines) {
18524
+ chatLines.push(line.tone === 'warning' ? C.warning(line.text) : C.muted(line.text));
18525
+ }
18526
+ inputPrefixState.set(reply.prefill);
18527
+ nextInitial = inputPrefixState.consumeInitialText('plain');
20753
18528
  continue; // stay in input mode
20754
18529
  }
20755
18530
 
@@ -21052,8 +18827,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21052
18827
  contextRegistry,
21053
18828
  loadAttachments: (reg) => loadAllAttachments(reg as Parameters<typeof loadAllAttachments>[0]),
21054
18829
  sessionRegistry,
21055
- virtualWindowBook: virtualWindows.book,
21056
- virtualWindowRegistry: virtualWindows.registry,
21057
18830
  sync,
21058
18831
  chatFooterLine,
21059
18832
  routeFooter: () => {
@@ -21091,8 +18864,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21091
18864
  contextText: args.contextText,
21092
18865
  contextRegistry: args.contextRegistry as typeof contextRegistry,
21093
18866
  terminalRegistry: args.terminalRegistry as typeof sessionRegistry,
21094
- addressBook: args.addressBook as typeof virtualWindows.book,
21095
- windowRegistry: args.windowRegistry as typeof virtualWindows.registry,
21096
18867
  blockAttach: args.blockAttach,
21097
18868
  pushChatLine: args.pushChatLine,
21098
18869
  }),
@@ -21119,7 +18890,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21119
18890
  pluginTools: pluginHost.activePluginLLMTools(),
21120
18891
  optionalTools: acpTurnRef.optionalSpecs,
21121
18892
  surface: acpTurnRef.turnProfile?.surface,
21122
- rich: dashboardUiMode === 'rich',
18893
+ rich: false,
21123
18894
  })
21124
18895
  : [];
21125
18896
  return buildDashboardTurnPreamble({
@@ -21127,7 +18898,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21127
18898
  cwd: args.cwd,
21128
18899
  turnProfile: args.turnProfile as SessionTurnProfile,
21129
18900
  userConfig: args.userConfig,
21130
- rich: dashboardUiMode === 'rich',
18901
+ rich: false,
21131
18902
  enabledTools: tools.map(t => t.name),
21132
18903
  });
21133
18904
  },
@@ -21151,7 +18922,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21151
18922
  userMsg: userMsg as ChatMessage,
21152
18923
  model: inspectActiveProvider().model,
21153
18924
  autoCompactConfig: userConfig.chat.autoCompact,
21154
- compactBoundaryEnabled: userConfig.chat.rendering.compactBoundary.enabled,
21155
18925
  shouldAutoCompact,
21156
18926
  compactConversation: async (history) => compactConversation(history),
21157
18927
  compactConversationPartial: async (history, opts) => compactConversationPartial(history, opts),
@@ -21190,7 +18960,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21190
18960
  createDashboardTurnStreamRuntimeEscBoundary(escAbortToolState);
21191
18961
  const toolRendering = {
21192
18962
  ...getUserConfig().chat.rendering.tool,
21193
- expandHint: dashboardUiMode === 'rich',
18963
+ expandHint: false,
21194
18964
  };
21195
18965
  const renderedToolRuntime = createDashboardRenderedToolRuntime({
21196
18966
  chatLines,
@@ -21314,7 +19084,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21314
19084
  },
21315
19085
  runCodeEditPostTurn: async () => {
21316
19086
  await runDashboardCodeEditPostTurn({
21317
- turnSummaryEnabled: getUserConfig().chat.rendering.diff.turnSummary,
21318
19087
  pushChatLine: (line) => { chatLines.push(line); },
21319
19088
  setChatScrollBottom: () => { chatScrollOffset = -1; },
21320
19089
  importCodeEdit: () => import('../code-edit/index.js'),