elanous 0.2.0 → 0.2.4

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 (1174) hide show
  1. package/.env.example +6 -0
  2. package/README.md +16 -14
  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/1105-c473ed46272fba79.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
  17. package/apps/pwa/out/_next/static/chunks/{2a79f15a-2013e4d421d6df88.js → 2ea96958-e8c7e112de50b067.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
  20. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6804.8ed6ab492bc5fc0a.js → 4323.c5dfb6f503304775.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{320-d7013ec072863e5e.js → 5080-1613752891fcadb4.js} +4 -4
  26. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/6062-73e68343a9814f6a.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8046cadf-211ca3c0e00f2b72.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8142-011e1f474efb8b33.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +1 -0
  92. package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +1 -0
  93. package/apps/pwa/out/_next/static/zoQX5iTvXOsIvCjxCe1Ji/_buildManifest.js +1 -0
  94. package/apps/pwa/out/approvals/index.html +1 -0
  95. package/apps/pwa/out/approvals/index.txt +30 -0
  96. package/apps/pwa/out/autopilot/index.html +1 -1
  97. package/apps/pwa/out/autopilot/index.txt +27 -26
  98. package/apps/pwa/out/board/index.html +1 -0
  99. package/apps/pwa/out/board/index.txt +30 -0
  100. package/apps/pwa/out/botlab/index.html +1 -1
  101. package/apps/pwa/out/botlab/index.txt +24 -23
  102. package/apps/pwa/out/bots/index.html +1 -1
  103. package/apps/pwa/out/bots/index.txt +24 -23
  104. package/apps/pwa/out/chat/index.html +1 -2
  105. package/apps/pwa/out/chat/index.txt +24 -23
  106. package/apps/pwa/out/control/index.html +1 -1
  107. package/apps/pwa/out/control/index.txt +27 -26
  108. package/apps/pwa/out/design-check/index.html +1 -1
  109. package/apps/pwa/out/design-check/index.txt +27 -26
  110. package/apps/pwa/out/editor/index.html +1 -0
  111. package/apps/pwa/out/editor/index.txt +31 -0
  112. package/apps/pwa/out/healthz +1 -1
  113. package/apps/pwa/out/index.html +1 -1
  114. package/apps/pwa/out/index.txt +24 -23
  115. package/apps/pwa/out/intake/index.html +1 -1
  116. package/apps/pwa/out/intake/index.txt +27 -26
  117. package/apps/pwa/out/live/index.html +1 -0
  118. package/apps/pwa/out/live/index.txt +30 -0
  119. package/apps/pwa/out/market/index.html +1 -0
  120. package/apps/pwa/out/market/index.txt +27 -0
  121. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  122. package/apps/pwa/out/missions/placeholder/index.txt +24 -23
  123. package/apps/pwa/out/morning/index.html +1 -1
  124. package/apps/pwa/out/morning/index.txt +27 -26
  125. package/apps/pwa/out/observatory/index.html +1 -1
  126. package/apps/pwa/out/observatory/index.txt +27 -26
  127. package/apps/pwa/out/reflection/index.html +1 -1
  128. package/apps/pwa/out/reflection/index.txt +25 -24
  129. package/apps/pwa/out/scheduler/index.html +1 -1
  130. package/apps/pwa/out/scheduler/index.txt +27 -26
  131. package/apps/pwa/out/sessions/index.html +1 -1
  132. package/apps/pwa/out/sessions/index.txt +24 -23
  133. package/apps/pwa/out/settings/devices/index.html +1 -1
  134. package/apps/pwa/out/settings/devices/index.txt +27 -26
  135. package/apps/pwa/out/settings/index.html +1 -1
  136. package/apps/pwa/out/settings/index.txt +27 -26
  137. package/apps/pwa/out/setup/done/index.html +1 -1
  138. package/apps/pwa/out/setup/done/index.txt +27 -26
  139. package/apps/pwa/out/setup/index.html +1 -1
  140. package/apps/pwa/out/setup/index.txt +27 -26
  141. package/apps/pwa/out/share/index.html +1 -1
  142. package/apps/pwa/out/share/index.txt +27 -26
  143. package/apps/pwa/out/showroom/index.html +1 -1
  144. package/apps/pwa/out/showroom/index.txt +24 -23
  145. package/apps/pwa/out/tasks/index.html +1 -1
  146. package/apps/pwa/out/tasks/index.txt +27 -26
  147. package/apps/pwa/out/term/index.html +1 -1
  148. package/apps/pwa/out/term/index.txt +27 -26
  149. package/apps/pwa/out/trace/index.html +1 -0
  150. package/apps/pwa/out/trace/index.txt +30 -0
  151. package/apps/pwa/out/vault/index.html +1 -1
  152. package/apps/pwa/out/vault/index.txt +27 -26
  153. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  154. package/apps/pwa/out/workflows/chat-ui/index.txt +27 -26
  155. package/apps/pwa/out/workflows/index.html +1 -1
  156. package/apps/pwa/out/workflows/index.txt +27 -27
  157. package/apps/pwa/out/workspace/index.html +1 -1
  158. package/apps/pwa/out/workspace/index.txt +27 -26
  159. package/apps/pwa/out/worktrees/index.html +1 -1
  160. package/apps/pwa/out/worktrees/index.txt +27 -26
  161. package/catalog/resources.yaml +8 -11
  162. package/docs/design/systems/LICENSE +201 -0
  163. package/docs/design/systems/NOTICE.md +29 -0
  164. package/docs/design/systems/SOURCE.json +291 -0
  165. package/docs/design/systems/application/DESIGN.md +71 -0
  166. package/docs/design/systems/application/manifest.json +54 -0
  167. package/docs/design/systems/application/tokens.css +63 -0
  168. package/docs/design/systems/artistic/DESIGN.md +71 -0
  169. package/docs/design/systems/artistic/manifest.json +54 -0
  170. package/docs/design/systems/artistic/tokens.css +63 -0
  171. package/docs/design/systems/bento/DESIGN.md +71 -0
  172. package/docs/design/systems/bento/manifest.json +54 -0
  173. package/docs/design/systems/bento/tokens.css +63 -0
  174. package/docs/design/systems/bold/DESIGN.md +71 -0
  175. package/docs/design/systems/bold/manifest.json +54 -0
  176. package/docs/design/systems/bold/tokens.css +63 -0
  177. package/docs/design/systems/brutalism/DESIGN.md +71 -0
  178. package/docs/design/systems/brutalism/manifest.json +54 -0
  179. package/docs/design/systems/brutalism/tokens.css +63 -0
  180. package/docs/design/systems/cafe/DESIGN.md +71 -0
  181. package/docs/design/systems/cafe/manifest.json +54 -0
  182. package/docs/design/systems/cafe/tokens.css +63 -0
  183. package/docs/design/systems/claymorphism/DESIGN.md +71 -0
  184. package/docs/design/systems/claymorphism/manifest.json +54 -0
  185. package/docs/design/systems/claymorphism/tokens.css +63 -0
  186. package/docs/design/systems/clean/DESIGN.md +71 -0
  187. package/docs/design/systems/clean/manifest.json +54 -0
  188. package/docs/design/systems/clean/tokens.css +63 -0
  189. package/docs/design/systems/colorful/DESIGN.md +71 -0
  190. package/docs/design/systems/colorful/manifest.json +54 -0
  191. package/docs/design/systems/colorful/tokens.css +63 -0
  192. package/docs/design/systems/contemporary/DESIGN.md +71 -0
  193. package/docs/design/systems/contemporary/manifest.json +54 -0
  194. package/docs/design/systems/contemporary/tokens.css +63 -0
  195. package/docs/design/systems/corporate/DESIGN.md +71 -0
  196. package/docs/design/systems/corporate/manifest.json +54 -0
  197. package/docs/design/systems/corporate/tokens.css +63 -0
  198. package/docs/design/systems/cosmic/DESIGN.md +71 -0
  199. package/docs/design/systems/cosmic/manifest.json +54 -0
  200. package/docs/design/systems/cosmic/tokens.css +63 -0
  201. package/docs/design/systems/creative/DESIGN.md +71 -0
  202. package/docs/design/systems/creative/manifest.json +54 -0
  203. package/docs/design/systems/creative/tokens.css +63 -0
  204. package/docs/design/systems/dashboard/DESIGN.md +71 -0
  205. package/docs/design/systems/dashboard/manifest.json +54 -0
  206. package/docs/design/systems/dashboard/tokens.css +63 -0
  207. package/docs/design/systems/default/DESIGN.md +62 -0
  208. package/docs/design/systems/default/manifest.json +69 -0
  209. package/docs/design/systems/default/tokens.css +200 -0
  210. package/docs/design/systems/dithered/DESIGN.md +71 -0
  211. package/docs/design/systems/dithered/manifest.json +54 -0
  212. package/docs/design/systems/dithered/tokens.css +63 -0
  213. package/docs/design/systems/doodle/DESIGN.md +71 -0
  214. package/docs/design/systems/doodle/manifest.json +54 -0
  215. package/docs/design/systems/doodle/tokens.css +63 -0
  216. package/docs/design/systems/dramatic/DESIGN.md +71 -0
  217. package/docs/design/systems/dramatic/manifest.json +54 -0
  218. package/docs/design/systems/dramatic/tokens.css +63 -0
  219. package/docs/design/systems/editorial/DESIGN.md +71 -0
  220. package/docs/design/systems/editorial/manifest.json +54 -0
  221. package/docs/design/systems/editorial/tokens.css +63 -0
  222. package/docs/design/systems/elegant/DESIGN.md +71 -0
  223. package/docs/design/systems/elegant/manifest.json +54 -0
  224. package/docs/design/systems/elegant/tokens.css +63 -0
  225. package/docs/design/systems/energetic/DESIGN.md +72 -0
  226. package/docs/design/systems/energetic/manifest.json +54 -0
  227. package/docs/design/systems/energetic/tokens.css +63 -0
  228. package/docs/design/systems/enterprise/DESIGN.md +71 -0
  229. package/docs/design/systems/enterprise/manifest.json +54 -0
  230. package/docs/design/systems/enterprise/tokens.css +63 -0
  231. package/docs/design/systems/expressive/DESIGN.md +71 -0
  232. package/docs/design/systems/expressive/manifest.json +54 -0
  233. package/docs/design/systems/expressive/tokens.css +63 -0
  234. package/docs/design/systems/fantasy/DESIGN.md +71 -0
  235. package/docs/design/systems/fantasy/manifest.json +54 -0
  236. package/docs/design/systems/fantasy/tokens.css +63 -0
  237. package/docs/design/systems/flat/DESIGN.md +71 -0
  238. package/docs/design/systems/flat/manifest.json +54 -0
  239. package/docs/design/systems/flat/tokens.css +63 -0
  240. package/docs/design/systems/friendly/DESIGN.md +71 -0
  241. package/docs/design/systems/friendly/manifest.json +54 -0
  242. package/docs/design/systems/friendly/tokens.css +63 -0
  243. package/docs/design/systems/glassmorphism/DESIGN.md +71 -0
  244. package/docs/design/systems/glassmorphism/manifest.json +54 -0
  245. package/docs/design/systems/glassmorphism/tokens.css +63 -0
  246. package/docs/design/systems/gradient/DESIGN.md +71 -0
  247. package/docs/design/systems/gradient/manifest.json +54 -0
  248. package/docs/design/systems/gradient/tokens.css +63 -0
  249. package/docs/design/systems/luxury/DESIGN.md +71 -0
  250. package/docs/design/systems/luxury/manifest.json +54 -0
  251. package/docs/design/systems/luxury/tokens.css +63 -0
  252. package/docs/design/systems/minimal/DESIGN.md +71 -0
  253. package/docs/design/systems/minimal/manifest.json +54 -0
  254. package/docs/design/systems/minimal/tokens.css +63 -0
  255. package/docs/design/systems/modern/DESIGN.md +71 -0
  256. package/docs/design/systems/modern/manifest.json +54 -0
  257. package/docs/design/systems/modern/tokens.css +63 -0
  258. package/docs/design/systems/mono/DESIGN.md +71 -0
  259. package/docs/design/systems/mono/manifest.json +54 -0
  260. package/docs/design/systems/mono/tokens.css +63 -0
  261. package/docs/design/systems/neobrutalism/DESIGN.md +71 -0
  262. package/docs/design/systems/neobrutalism/manifest.json +54 -0
  263. package/docs/design/systems/neobrutalism/tokens.css +63 -0
  264. package/docs/design/systems/neon/DESIGN.md +71 -0
  265. package/docs/design/systems/neon/manifest.json +54 -0
  266. package/docs/design/systems/neon/tokens.css +63 -0
  267. package/docs/design/systems/neumorphism/DESIGN.md +71 -0
  268. package/docs/design/systems/neumorphism/manifest.json +54 -0
  269. package/docs/design/systems/neumorphism/tokens.css +63 -0
  270. package/docs/design/systems/paper/DESIGN.md +71 -0
  271. package/docs/design/systems/paper/manifest.json +54 -0
  272. package/docs/design/systems/paper/tokens.css +63 -0
  273. package/docs/design/systems/perspective/DESIGN.md +71 -0
  274. package/docs/design/systems/perspective/manifest.json +54 -0
  275. package/docs/design/systems/perspective/tokens.css +63 -0
  276. package/docs/design/systems/premium/DESIGN.md +71 -0
  277. package/docs/design/systems/premium/manifest.json +54 -0
  278. package/docs/design/systems/premium/tokens.css +63 -0
  279. package/docs/design/systems/professional/DESIGN.md +71 -0
  280. package/docs/design/systems/professional/manifest.json +54 -0
  281. package/docs/design/systems/professional/tokens.css +63 -0
  282. package/docs/design/systems/publication/DESIGN.md +71 -0
  283. package/docs/design/systems/publication/manifest.json +54 -0
  284. package/docs/design/systems/publication/tokens.css +63 -0
  285. package/docs/design/systems/refined/DESIGN.md +71 -0
  286. package/docs/design/systems/refined/manifest.json +54 -0
  287. package/docs/design/systems/refined/tokens.css +63 -0
  288. package/docs/design/systems/retro/DESIGN.md +71 -0
  289. package/docs/design/systems/retro/manifest.json +54 -0
  290. package/docs/design/systems/retro/tokens.css +63 -0
  291. package/docs/design/systems/shadcn/DESIGN.md +71 -0
  292. package/docs/design/systems/shadcn/manifest.json +54 -0
  293. package/docs/design/systems/shadcn/tokens.css +255 -0
  294. package/docs/design/systems/simple/DESIGN.md +71 -0
  295. package/docs/design/systems/simple/manifest.json +54 -0
  296. package/docs/design/systems/simple/tokens.css +63 -0
  297. package/docs/design/systems/skeumorphism/DESIGN.md +71 -0
  298. package/docs/design/systems/skeumorphism/manifest.json +54 -0
  299. package/docs/design/systems/skeumorphism/tokens.css +63 -0
  300. package/docs/design/systems/sleek/DESIGN.md +71 -0
  301. package/docs/design/systems/sleek/manifest.json +54 -0
  302. package/docs/design/systems/sleek/tokens.css +63 -0
  303. package/docs/design/systems/spacious/DESIGN.md +71 -0
  304. package/docs/design/systems/spacious/manifest.json +54 -0
  305. package/docs/design/systems/spacious/tokens.css +63 -0
  306. package/docs/design/systems/storytelling/DESIGN.md +71 -0
  307. package/docs/design/systems/storytelling/manifest.json +54 -0
  308. package/docs/design/systems/storytelling/tokens.css +63 -0
  309. package/docs/design/systems/tom-modern/DESIGN.md +361 -0
  310. package/docs/design/systems/tom-modern/manifest.json +16 -0
  311. package/docs/design/systems/tom-modern/tokens.css +99 -0
  312. package/docs/design/systems/vibrant/DESIGN.md +71 -0
  313. package/docs/design/systems/vibrant/manifest.json +54 -0
  314. package/docs/design/systems/vibrant/tokens.css +63 -0
  315. package/docs/design/systems/vintage/DESIGN.md +71 -0
  316. package/docs/design/systems/vintage/manifest.json +54 -0
  317. package/docs/design/systems/vintage/tokens.css +63 -0
  318. package/docs/design/systems/warm-editorial/DESIGN.md +65 -0
  319. package/docs/design/systems/warm-editorial/manifest.json +54 -0
  320. package/docs/design/systems/warm-editorial/tokens.css +63 -0
  321. package/docs/goal-context/01-goal-authoring-checks.md +18 -0
  322. package/docs/goal-context/steward-directives-2026-09-28.md +44 -0
  323. package/graphs/catalog/catalog.yaml +615 -0
  324. package/graphs/default-loop.yaml +1 -1
  325. package/graphs/document-loop.yaml +1 -1
  326. package/graphs/examples/stale-draft-digest/digest.ts +26 -0
  327. package/graphs/examples/stale-draft-digest/recipes.yaml +8 -0
  328. package/graphs/examples/stale-draft-digest/stale-draft-digest.yaml +22 -0
  329. package/graphs/heal/heal-loop.yaml +155 -0
  330. package/graphs/heal/recipes.yaml +19 -0
  331. package/graphs/implement-loop.yaml +2 -2
  332. package/graphs/intake/intake-daily.yaml +55 -0
  333. package/graphs/intake/recipes.yaml +16 -0
  334. package/graphs/landing/landing-heal.yaml +22 -0
  335. package/graphs/landing/recipes.yaml +8 -0
  336. package/graphs/launch/launch-head.yaml +66 -0
  337. package/graphs/launch/recipes.yaml +8 -0
  338. package/graphs/operate-loop.yaml +1 -1
  339. package/graphs/ops/docs-publish.yaml +31 -0
  340. package/graphs/ops/recipes.yaml +8 -0
  341. package/graphs/release/recipes.yaml +38 -0
  342. package/graphs/release/release-loop.yaml +40 -0
  343. package/graphs/research-loop.yaml +1 -1
  344. package/graphs/steward/recipes.yaml +12 -0
  345. package/graphs/steward/steward.yaml +45 -0
  346. package/graphs/video/broll-line.yaml +54 -0
  347. package/package.json +7 -5
  348. package/plugins/job-coach/.agents/plugins/marketplace.json +12 -0
  349. package/plugins/job-coach/.codex-plugin/plugin.json +6 -0
  350. package/plugins/job-coach/.mcp.json +9 -0
  351. package/plugins/job-coach/README.md +20 -0
  352. package/plugins/job-coach/connectors/ncs/server.ts +89 -0
  353. package/plugins/job-coach/examples/interview-sample.md +11 -0
  354. package/plugins/job-coach/graphs/course-links.ts +46 -0
  355. package/plugins/job-coach/graphs/recipes.yaml +18 -0
  356. package/plugins/job-coach/graphs/report.yaml +20 -0
  357. package/plugins/job-coach/graphs/run-step.ts +108 -0
  358. package/plugins/job-coach/plugin.json +13 -0
  359. package/plugins/job-coach/plugin.ts +6 -0
  360. package/plugins/job-coach/skills/career-report/SKILL.md +6 -0
  361. package/plugins/job-coach/skills/interview-to-profile/SKILL.md +6 -0
  362. package/plugins/job-coach/skills/run-report/SKILL.md +15 -0
  363. package/scripts/backup/elanous-backup.sh +139 -13
  364. package/scripts/backup/elanous-restore.sh +122 -0
  365. package/scripts/ci-daemon-port-gate.ts +164 -0
  366. package/scripts/ci-typecheck-changed.ts +15 -1
  367. package/scripts/cli-json-exit-scan.ts +126 -0
  368. package/scripts/codex-quota-alert-format.ts +52 -0
  369. package/scripts/codex-quota-alert.ts +63 -13
  370. package/scripts/continuation-dogfood.ts +2 -1
  371. package/scripts/coord-channel-watch.sh +5 -5
  372. package/scripts/coord-tracks.json +5 -4
  373. package/scripts/cron-run.ts +37 -16
  374. package/scripts/daemon-port-baseline.txt +9 -0
  375. package/scripts/design/extract-corpus-bench.ts +469 -0
  376. package/scripts/design/extract-corpus-score.ts +510 -0
  377. package/scripts/design/vendor-open-design-systems.ts +100 -0
  378. package/scripts/docs-lint.ts +1 -1
  379. package/scripts/fleet/node-setup.sh +38 -4
  380. package/scripts/heal/run-role.ts +328 -0
  381. package/scripts/install-role-watch.sh +95 -0
  382. package/scripts/install.ps1 +11 -1
  383. package/scripts/install.sh +81 -9
  384. package/scripts/isolation-hardcode-baseline.txt +1 -1
  385. package/scripts/landing-heal/must-fix-match.ts +37 -0
  386. package/scripts/landing-heal/run-mission.ts +101 -0
  387. package/scripts/lib/pwa-scenarios/acp-replay.ts +79 -0
  388. package/scripts/lib/pwa-scenarios/cdp-driver.ts +67 -0
  389. package/scripts/lib/pwa-scenarios/runner.ts +200 -0
  390. package/scripts/lib/pwa-scenarios/scenarios.ts +202 -0
  391. package/scripts/market/db-check.ts +212 -0
  392. package/scripts/market-posture-cycle.ts +63 -4
  393. package/scripts/nightly-docops.ts +1 -1
  394. package/scripts/ops/channel-bot-token-migrate.ts +146 -0
  395. package/scripts/ops-health-check.ts +15 -3
  396. package/scripts/pty-scenarios/run.ts +272 -0
  397. package/scripts/public-leak-baseline.txt +0 -7
  398. package/scripts/publish-internal-dist.ts +119 -0
  399. package/scripts/pwa-scenarios.ts +106 -0
  400. package/scripts/rebrand-elanous.ts +1 -1
  401. package/scripts/release-loop/cutoff.ts +139 -0
  402. package/scripts/release-loop/docs-land-node.ts +65 -0
  403. package/scripts/release-loop/docs-node.ts +172 -0
  404. package/scripts/release-loop/gate-diff.ts +29 -0
  405. package/scripts/release-loop/gate-node.ts +353 -0
  406. package/scripts/release-loop/node-verdict.ts +85 -0
  407. package/scripts/release-loop/prepare-node.ts +31 -0
  408. package/scripts/release-loop/publish-node.ts +57 -0
  409. package/scripts/release-loop/pwa-node-verdict.ts +45 -0
  410. package/scripts/release-loop/pwa-scenarios-node.ts +140 -0
  411. package/scripts/release-loop/release-docs.ts +102 -0
  412. package/scripts/release-loop/tui-node-checks.ts +29 -0
  413. package/scripts/release-loop/tui-sim-node.ts +113 -0
  414. package/scripts/release-loop/upgrade-node.ts +25 -0
  415. package/scripts/release-loop/verify-node.ts +21 -0
  416. package/scripts/release-loop/version-node.ts +147 -0
  417. package/scripts/release-upgrade-check.sh +53 -0
  418. package/scripts/se-mission-prepare.ts +9 -15
  419. package/scripts/test-deterministic.ts +2 -1
  420. package/scripts/uninstall.sh +10 -3
  421. package/scripts/video-broll-line.ts +89 -0
  422. package/scripts/waitlist-daily-digest.ts +108 -0
  423. package/scripts/window-landings.sh +1 -1
  424. package/skills/motion-broll/LICENSE +25 -0
  425. package/skills/motion-broll/SKILL.md +150 -0
  426. package/skills/motion-broll/SOURCE.md +6 -0
  427. package/skills/motion-broll/engine/base.css +19 -0
  428. package/skills/motion-broll/engine/beats.js +11 -0
  429. package/skills/motion-broll/engine/build.py +21 -0
  430. package/skills/motion-broll/engine/fonts/Geist-Variable.woff2 +0 -0
  431. package/skills/motion-broll/engine/fonts/GeistMono-Medium.woff2 +0 -0
  432. package/skills/motion-broll/engine/fonts/OFL-Geist.txt +92 -0
  433. package/skills/motion-broll/engine/motion.js +146 -0
  434. package/skills/motion-broll/engine/render.js +22 -0
  435. package/skills/motion-broll/examples/opus-aoe2/01-opus-drop.html +41 -0
  436. package/skills/motion-broll/examples/opus-aoe2/02-pip-builds.html +110 -0
  437. package/skills/motion-broll/examples/opus-aoe2/03-effort-slider.html +38 -0
  438. package/skills/motion-broll/examples/opus-aoe2/04-master-prompt.html +56 -0
  439. package/skills/motion-broll/examples/opus-aoe2/05-low-vs-max.html +49 -0
  440. package/skills/motion-broll/examples/opus-aoe2/06-chapter.html +18 -0
  441. package/skills/motion-broll/examples/opus-aoe2/plan.json +14 -0
  442. package/skills/motion-broll/reference/engine-api.md +106 -0
  443. package/skills/motion-broll/scripts/composite.py +14 -0
  444. package/skills/motion-broll/scripts/inspect_video.py +34 -0
  445. package/skills/motion-broll/scripts/make_pages.py +34 -0
  446. package/skills/motion-broll/scripts/setup.sh +16 -0
  447. package/skills/motion-broll/scripts/words.py +13 -0
  448. package/skills/motion-broll/templates/compare.html +135 -0
  449. package/skills/motion-broll/templates/viewer.html +97 -0
  450. package/skills/omni-digest/SKILL.md +260 -0
  451. package/skills/omni-digest/digest.md +197 -0
  452. package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +32 -0
  453. package/skills/omni-digest/package-lock.json +558 -0
  454. package/skills/omni-digest/package.json +13 -0
  455. package/skills/omni-digest/scripts/main.ts +404 -0
  456. package/skills/omni-digest/src/delegate.ts +34 -0
  457. package/skills/omni-digest/src/detect.ts +46 -0
  458. package/skills/omni-digest/src/diagram.ts +72 -0
  459. package/skills/omni-digest/src/domain-staging.ts +50 -0
  460. package/skills/omni-digest/src/env.ts +38 -0
  461. package/skills/omni-digest/src/fetch.ts +237 -0
  462. package/skills/omni-digest/src/media.ts +180 -0
  463. package/skills/omni-digest/src/obsidian.ts +69 -0
  464. package/skills/omni-digest/src/ocr.ts +149 -0
  465. package/skills/omni-digest/src/router.ts +60 -0
  466. package/skills/omni-digest/src/summarize.ts +87 -0
  467. package/skills/omni-digest/src/types.ts +124 -0
  468. package/skills/omni-digest/src/util.ts +34 -0
  469. package/skills/omni-digest/src/visual.ts +129 -0
  470. package/skills/omni-digest/src/x-api.ts +151 -0
  471. package/skills/omni-digest/tsconfig.json +13 -0
  472. package/src/a2a/agent-card.ts +29 -0
  473. package/src/a2a/server.ts +173 -0
  474. package/src/acp/auto-persist.ts +3 -6
  475. package/src/acp/backend-registry.ts +6 -10
  476. package/src/acp/channel-browser-shell.ts +1 -535
  477. package/src/acp/client.ts +106 -36
  478. package/src/acp/fs-roots.ts +20 -4
  479. package/src/acp/obsidian-tags.ts +6 -3
  480. package/src/acp/server.ts +189 -32
  481. package/src/agent/pty-binding.ts +1 -1
  482. package/src/agent/spawn-embodied-agent-in-vw.ts +5 -336
  483. package/src/agent/subagent-callable.ts +39 -1
  484. package/src/agent-mission/browser-login.ts +241 -0
  485. package/src/agent-mission/capabilities-cli.ts +33 -0
  486. package/src/agent-mission/capability-matrix.ts +31 -0
  487. package/src/agent-mission/capability-readers.ts +79 -0
  488. package/src/agent-mission/capability-types.ts +15 -0
  489. package/src/agent-mission/claude-headless.ts +131 -0
  490. package/src/agent-mission/claude-subscription.ts +57 -0
  491. package/src/agent-mission/doc-guided-install.ts +185 -0
  492. package/src/agent-mission/driver.ts +821 -69
  493. package/src/agent-mission/env-profile.ts +68 -0
  494. package/src/agent-mission/handoff.ts +128 -0
  495. package/src/agent-mission/in-agent-plugin.ts +311 -0
  496. package/src/agent-mission/mission-cli.ts +19 -1
  497. package/src/agent-mission/parse.ts +9 -4
  498. package/src/agent-mission/pr-review-watch.ts +62 -13
  499. package/src/agent-mission/resource-ladder.ts +187 -0
  500. package/src/agent-mission/tool-install.ts +249 -0
  501. package/src/agent-mission/tool-ladder.ts +37 -0
  502. package/src/agent-room/room-builder.ts +6 -600
  503. package/src/agent-substrate/review-intent.ts +43 -1
  504. package/src/auth/scope.ts +8 -4
  505. package/src/auth/temp-tokens.ts +131 -0
  506. package/src/auto-research/obsidian-bridge.ts +2 -0
  507. package/src/autopilot/build/integrity-gate.ts +27 -4
  508. package/src/autopilot/build/isolated-instance.ts +2 -1
  509. package/src/autopilot/build/llm-conflict-merge.ts +27 -0
  510. package/src/autopilot/llm-control-brain.ts +54 -12
  511. package/src/autopilot/mission-engine.ts +15 -1
  512. package/src/autopilot/mission-notify.ts +4 -2
  513. package/src/autopilot/mission-origin.ts +5 -5
  514. package/src/autopilot/mission-phase-track.ts +52 -0
  515. package/src/autopilot/mission-tool.ts +1 -1
  516. package/src/autopilot/pty-control-loop.ts +94 -2
  517. package/src/autopilot/track-agent.ts +138 -0
  518. package/src/autopilot/track-courier.ts +153 -0
  519. package/src/boot/acp-server.ts +16 -2
  520. package/src/boot/check-same-origin.ts +19 -5
  521. package/src/boot/daemon-multi-llm-runtime.ts +14 -8
  522. package/src/boot/daemon-prompt-turn.ts +2 -0
  523. package/src/boot/daemon-runtime.ts +36 -14
  524. package/src/boot/daemon-tools/index.ts +16 -0
  525. package/src/boot/daemon-tools/self-implement.ts +4 -0
  526. package/src/boot/daemon-tools/types.ts +3 -0
  527. package/src/browser-cdp/chrome-flags.ts +7 -0
  528. package/src/browser-cdp/client.ts +2 -0
  529. package/src/browser-pane/mount.ts +1 -246
  530. package/src/budget/codex-reset-credit-state.ts +15 -0
  531. package/src/budget/fetchers/codex.ts +2 -1
  532. package/src/budget/run-usage-rollup.ts +11 -4
  533. package/src/budget/unified-usage.ts +2 -1
  534. package/src/capture/tui-self-report.ts +10 -0
  535. package/src/channel-bot-token.ts +126 -0
  536. package/src/chat/index.ts +21 -45
  537. package/src/chat/input-text-key.ts +2 -2
  538. package/src/chat/mode/manual.ts +2 -4
  539. package/src/chat/pickers/state.ts +10 -27
  540. package/src/chat/search/modal.ts +14 -2
  541. package/src/chat/tool-render/types.ts +1 -1
  542. package/src/chat/turn-typeahead.ts +2 -1
  543. package/src/chat/wrap.ts +6 -8
  544. package/src/cli/__fixtures__/agent-help-before/agent.txt +18 -0
  545. package/src/cli/__fixtures__/agent-help-before/registry.txt +13 -0
  546. package/src/cli/__fixtures__/agent-help-before/root.txt +100 -0
  547. package/src/cli/__fixtures__/agent-help-before/tier.txt +15 -0
  548. package/src/cli/a2a-cli.ts +52 -0
  549. package/src/cli/agent-cli.ts +721 -0
  550. package/src/cli/autopilot-cli.ts +739 -0
  551. package/src/cli/bg-launch.ts +20 -4
  552. package/src/cli/claude-plugin-setup.ts +199 -0
  553. package/src/cli/config-test-sync.ts +94 -10
  554. package/src/cli/connector-cli.ts +204 -0
  555. package/src/cli/control-cli.ts +220 -0
  556. package/src/cli/control-join-cli.ts +142 -0
  557. package/src/cli/docs-cli.ts +38 -0
  558. package/src/cli/doctor-cli.ts +225 -26
  559. package/src/cli/doctor-distro.ts +17 -1
  560. package/src/cli/doctor-fix.ts +145 -17
  561. package/src/cli/doctor-llm-assist.ts +71 -0
  562. package/src/cli/doctor-private-files.ts +66 -0
  563. package/src/cli/doctor-readiness.ts +8 -5
  564. package/src/cli/fleet-cli.ts +63 -0
  565. package/src/cli/g6a-registration-probe.ts +16 -0
  566. package/src/cli/git-install-plan.ts +51 -0
  567. package/src/cli/hooks-cli.ts +44 -0
  568. package/src/cli/ingest-token-cli.ts +21 -0
  569. package/src/cli/intake-cli.ts +416 -0
  570. package/src/cli/labels-cli.ts +74 -0
  571. package/src/cli/leader-cli.ts +10 -159
  572. package/src/cli/live-cli.ts +48 -0
  573. package/src/cli/llm-detect-cli.ts +130 -0
  574. package/src/cli/logs-cli.ts +231 -7
  575. package/src/cli/machine-cli.ts +38 -0
  576. package/src/cli/mcp-call.ts +43 -13
  577. package/src/cli/mcp-gateway-cli.ts +90 -0
  578. package/src/cli/mcp-login.ts +116 -8
  579. package/src/cli/mcp-reload.ts +20 -3
  580. package/src/cli/nexus-dist.ts +18 -13
  581. package/src/cli/nexus-ios-bind.ts +20 -7
  582. package/src/cli/ops-cli.ts +225 -0
  583. package/src/cli/plugin-cli.ts +103 -0
  584. package/src/cli/pod-cli.ts +83 -0
  585. package/src/cli/port-lease-local.ts +124 -0
  586. package/src/cli/pr-cli.ts +12 -1
  587. package/src/cli/provider-cli.ts +720 -0
  588. package/src/cli/pty-record-cli.ts +63 -0
  589. package/src/cli/pty-takeover-cli.ts +57 -40
  590. package/src/cli/pty-web-address.ts +52 -0
  591. package/src/cli/publish-cli.ts +44 -0
  592. package/src/cli/pwa-build.ts +18 -12
  593. package/src/cli/pwa-dev.ts +13 -8
  594. package/src/cli/pwa-registry.ts +6 -0
  595. package/src/cli/pwa-share.ts +65 -22
  596. package/src/cli/pwa-show.ts +20 -4
  597. package/src/cli/pwa-start.ts +99 -27
  598. package/src/cli/pwa-test.ts +201 -96
  599. package/src/cli/release-cli.ts +90 -7
  600. package/src/cli/remotes-cli.ts +20 -1
  601. package/src/cli/remotes.ts +2 -1
  602. package/src/cli/repo-cli.ts +315 -36
  603. package/src/cli/research-cli.ts +66 -0
  604. package/src/cli/role-cli.ts +320 -0
  605. package/src/cli/schedule-cli.ts +246 -0
  606. package/src/cli/self-update.ts +98 -10
  607. package/src/cli/setup-cli.ts +64 -2
  608. package/src/cli/shadow-cli.ts +188 -0
  609. package/src/cli/share-auto-mount.ts +12 -8
  610. package/src/cli/start-cli.ts +221 -0
  611. package/src/cli/stdout-flush.ts +8 -0
  612. package/src/cli/storage-cli.ts +334 -0
  613. package/src/cli/tailscale-serve.ts +34 -19
  614. package/src/cli/tasks-cli.ts +176 -0
  615. package/src/cli/test-state-dir-flag.ts +1 -1
  616. package/src/cli/usage-cli.ts +15 -4
  617. package/src/cli/where-cli.ts +22 -55
  618. package/src/code-edit/diff-render.ts +6 -32
  619. package/src/code-edit/source-delta-browser.ts +0 -1
  620. package/src/codex/models.ts +1 -1
  621. package/src/config.ts +8 -13
  622. package/src/connectors/event-ledger.ts +41 -0
  623. package/src/connectors/linear-projector.ts +88 -0
  624. package/src/connectors/linear.ts +143 -0
  625. package/src/connectors/types.ts +19 -0
  626. package/src/control-plane/lease-holder.ts +41 -0
  627. package/src/control-plane/ledger.ts +146 -0
  628. package/src/control-plane/light-member.ts +164 -0
  629. package/src/control-plane/member-tokens.ts +88 -0
  630. package/src/control-plane/member.ts +159 -0
  631. package/src/control-plane/port-lease-heartbeat.ts +101 -0
  632. package/src/control-plane/ports.ts +40 -0
  633. package/src/control-plane/primary.ts +169 -0
  634. package/src/control-plane/server.ts +216 -0
  635. package/src/dashboard/acp-boot.ts +11 -38
  636. package/src/dashboard/assistant-render-state-runtime.ts +2 -2
  637. package/src/dashboard/assistant-sample-runtime.ts +2 -2
  638. package/src/dashboard/auto-compact-runtime.ts +11 -16
  639. package/src/dashboard/browser-help-runtime.ts +2 -3
  640. package/src/dashboard/chat/acp-chat.ts +2 -5
  641. package/src/dashboard/code-edit-post-turn-runtime.ts +1 -2
  642. package/src/dashboard/compact-surface-assembly.ts +0 -14
  643. package/src/dashboard/compact-surface-dispatch.ts +1 -38
  644. package/src/dashboard/compact-surface-inventory.ts +0 -8
  645. package/src/dashboard/compact-surface-runtime.ts +0 -14
  646. package/src/dashboard/context-menu-provider-boot.ts +0 -9
  647. package/src/dashboard/context-window-runtime-boot.ts +0 -37
  648. package/src/dashboard/index.ts +278 -2558
  649. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +0 -6
  650. package/src/dashboard/input/chat-main-slash-command.ts +2 -2
  651. package/src/dashboard/input/dashboard-priority-key-route.ts +1 -43
  652. package/src/dashboard/input/mouse-modal-surface-runtime.ts +1 -5
  653. package/src/dashboard/input/mouse-wiring.ts +2 -3
  654. package/src/dashboard/layout-display-runtime-registration.ts +0 -10
  655. package/src/dashboard/modals/pane-multi.ts +1 -1
  656. package/src/dashboard/replay-preview.ts +1 -1
  657. package/src/dashboard/runtime/approvers.ts +0 -1
  658. package/src/dashboard/runtime/state-snapshot.ts +0 -90
  659. package/src/dashboard/scenario-runtime-boot.ts +16 -16
  660. package/src/dashboard/slash-runtime/dashboard-handlers.ts +134 -1456
  661. package/src/dashboard/slash-runtime/design-pick.ts +96 -0
  662. package/src/dashboard/slash-runtime/help-from-registry.ts +77 -0
  663. package/src/dashboard/surface-registry-wiring.ts +2 -15
  664. package/src/dashboard/turn-message-runtime.ts +0 -4
  665. package/src/dashboard/turn-preamble.ts +1 -1
  666. package/src/dashboard/turn-stream-formatter.ts +2 -2
  667. package/src/dashboard/turn-stream-runtime.ts +2 -2
  668. package/src/dashboard-pane-context-menu.ts +0 -14
  669. package/src/debug/log.ts +7 -0
  670. package/src/decide/boundary-shadow.ts +131 -0
  671. package/src/design/apply-direction.ts +209 -0
  672. package/src/design/css-root-tokens.ts +89 -0
  673. package/src/design/custom-system-create.ts +230 -0
  674. package/src/design/design-directions.ts +61 -41
  675. package/src/design/design-gate.ts +241 -0
  676. package/src/design/design-library.ts +57 -0
  677. package/src/design/design-previews.ts +138 -0
  678. package/src/design/design-systems.ts +61 -0
  679. package/src/design/lint-artifact-run.ts +46 -6
  680. package/src/design/lint-artifact.ts +5 -1
  681. package/src/design/open-design-client.ts +247 -0
  682. package/src/design/system-from-extract.ts +767 -0
  683. package/src/design/system-schema.ts +123 -0
  684. package/src/discord-slash-wire.ts +2 -1
  685. package/src/discord-test-runner.ts +2 -1
  686. package/src/discord-voice-wire.ts +2 -1
  687. package/src/discord.ts +1 -1
  688. package/src/dispatch/opportunistic-launcher.ts +24 -1
  689. package/src/domains/ops-status.ts +35 -2
  690. package/src/domains/outbound-alert.ts +16 -4
  691. package/src/domains/repeated-failure.ts +89 -0
  692. package/src/domains/schedule-manage-tool.ts +28 -8
  693. package/src/domains/schedule-migrate.ts +22 -4
  694. package/src/domains/schedule-registry.ts +88 -33
  695. package/src/domains/schedule-runs.ts +132 -0
  696. package/src/domains/schedule-state.ts +152 -0
  697. package/src/element-registry/address.ts +1 -3
  698. package/src/git-fs/gh-cli.ts +47 -17
  699. package/src/git-fs/retry.ts +1 -0
  700. package/src/git-fs/runner.ts +1 -0
  701. package/src/git-fs/worktree.ts +6 -5
  702. package/src/github/pr-labels.ts +68 -0
  703. package/src/graph-kinds/plugin-nodes.ts +60 -0
  704. package/src/graph-kinds/registry.ts +118 -0
  705. package/src/graph-runner/graph-cli.ts +103 -0
  706. package/src/graph-runner/graph-notify.ts +220 -0
  707. package/src/graph-runner/graph-tick.ts +119 -0
  708. package/src/graph-runner/release-manifest.ts +93 -0
  709. package/src/graph-runner/runner.ts +457 -0
  710. package/src/grok/credential.ts +158 -5
  711. package/src/grounding/ensure-grounding.ts +131 -0
  712. package/src/grounding/mirror-export.ts +89 -0
  713. package/src/grounding/mirror-manifest.ts +252 -0
  714. package/src/grounding/sources-cli.ts +209 -0
  715. package/src/grounding/sources.ts +333 -0
  716. package/src/grounding/token.ts +115 -0
  717. package/src/harness/harness-cli-command.ts +352 -19
  718. package/src/harness/harness-pod-dispatch.ts +110 -0
  719. package/src/harness/harness-stop.ts +131 -0
  720. package/src/harness/harness-substrate-default.ts +40 -0
  721. package/src/harness/harness-worktrees.ts +1 -1
  722. package/src/harness/harness-write-boundary.ts +2 -0
  723. package/src/harness/llm-decompose.ts +2 -1
  724. package/src/harness/self-send-decide.ts +224 -0
  725. package/src/harness/shell-relay.ts +5 -0
  726. package/src/hooks/providers.ts +77 -0
  727. package/src/hooks/queue.ts +73 -0
  728. package/src/hooks/receiver.ts +133 -0
  729. package/src/index.ts +666 -3406
  730. package/src/input-core/dispatcher.ts +1 -1
  731. package/src/input-core/drag-esc-interceptor.ts +1 -1
  732. package/src/instance/current.ts +5 -5
  733. package/src/instance/leader.ts +10 -326
  734. package/src/instance/nexus-run-refusal.ts +27 -92
  735. package/src/instance/project-local-exclude.ts +27 -0
  736. package/src/instance/resolve.ts +40 -175
  737. package/src/intake-plane/capture.ts +14 -1
  738. package/src/intake-plane/check.ts +34 -14
  739. package/src/intake-plane/collect-github.ts +258 -0
  740. package/src/intake-plane/collect-pod.ts +100 -0
  741. package/src/intake-plane/collect-telegram-saved.ts +86 -0
  742. package/src/intake-plane/digest.ts +102 -0
  743. package/src/intake-plane/enrich-plugins.ts +1 -1
  744. package/src/intake-plane/front-classifier.ts +56 -0
  745. package/src/intake-plane/front-route-classifier.ts +80 -0
  746. package/src/intake-plane/front-route-rules.ts +152 -0
  747. package/src/intake-plane/grounding-docs.ts +185 -0
  748. package/src/intake-plane/harness-target.ts +20 -0
  749. package/src/intake-plane/intake-to-tasks.ts +303 -0
  750. package/src/intake-plane/items.ts +247 -0
  751. package/src/intake-plane/link-ledger.ts +27 -0
  752. package/src/intake-plane/route.ts +104 -0
  753. package/src/intake-plane/runtime-callables.ts +3 -1
  754. package/src/intake-plane/sketch-ledger.ts +34 -0
  755. package/src/intake-plane/slash.ts +30 -2
  756. package/src/intake-plane/submit-intake-work.ts +160 -0
  757. package/src/intake-plane/telegram-work.ts +56 -0
  758. package/src/iul/sidebar-shell.ts +0 -165
  759. package/src/launch-head/capability-check.ts +162 -0
  760. package/src/launch-head/launch-head-cli.ts +242 -0
  761. package/src/launch-head/load-balance.ts +81 -0
  762. package/src/live/detail-switch.ts +175 -0
  763. package/src/llm/provider-detect.ts +247 -0
  764. package/src/llm/provider-probe.ts +57 -0
  765. package/src/llm.ts +55 -23
  766. package/src/log-entry.ts +1 -1
  767. package/src/log-pane/attachment-popup.ts +1 -2
  768. package/src/loops/loop-cli.ts +37 -0
  769. package/src/loops/registry.ts +174 -0
  770. package/src/market/official-keys.ts +5 -0
  771. package/src/market/publish.ts +273 -0
  772. package/src/market/signed-index.ts +156 -0
  773. package/src/market/tgz.ts +121 -0
  774. package/src/mcp/client.ts +1 -1
  775. package/src/mcp/mcp-oauth.ts +40 -5
  776. package/src/mcp/server.ts +75 -7
  777. package/src/mcp-gateway/gateway.ts +128 -0
  778. package/src/mcp-gateway/pat-store.ts +124 -0
  779. package/src/mss/logging/log-store.ts +91 -2
  780. package/src/mss/logging/scoped-level.ts +5 -9
  781. package/src/native-tool-catalog.ts +64 -343
  782. package/src/nexus/api/connect-info.ts +23 -25
  783. package/src/nexus/api/context-url.ts +1 -1
  784. package/src/nexus/api/design-check.ts +90 -59
  785. package/src/nexus/api/design-previews-route.ts +182 -0
  786. package/src/nexus/api/design-system-create-route.ts +123 -0
  787. package/src/nexus/api/graph-kinds.ts +69 -0
  788. package/src/nexus/api/graphs-api.ts +221 -0
  789. package/src/nexus/api/harness-api.ts +31 -2
  790. package/src/nexus/api/health.ts +15 -0
  791. package/src/nexus/api/http-server.ts +274 -43
  792. package/src/nexus/api/image-generate.ts +3 -0
  793. package/src/nexus/api/ingest-token.ts +175 -0
  794. package/src/nexus/api/intake-ledger-api.ts +54 -0
  795. package/src/nexus/api/intake-route.ts +111 -0
  796. package/src/nexus/api/json-response.ts +33 -0
  797. package/src/nexus/api/live-detail.ts +47 -0
  798. package/src/nexus/api/live-shipped.ts +78 -0
  799. package/src/nexus/api/log-fabric.ts +33 -2
  800. package/src/nexus/api/markdown-publish.ts +3 -2
  801. package/src/nexus/api/mcp-http.ts +14 -9
  802. package/src/nexus/api/mcp-oauth-hint.ts +30 -0
  803. package/src/nexus/api/mcp-public-allowlist.ts +33 -0
  804. package/src/nexus/api/merge-approvals-gate.ts +79 -0
  805. package/src/nexus/api/merge-approvals.ts +306 -0
  806. package/src/nexus/api/meta-api.ts +4 -0
  807. package/src/nexus/api/next-fluent-wiring.ts +7 -9
  808. package/src/nexus/api/plugins-market.ts +125 -0
  809. package/src/nexus/api/pod-credential-api.ts +133 -0
  810. package/src/nexus/api/public-routes.ts +80 -0
  811. package/src/nexus/api/rest-route-paths.ts +15 -0
  812. package/src/nexus/api/schedules-action.ts +12 -3
  813. package/src/nexus/api/schedules-read.ts +228 -0
  814. package/src/nexus/api/sessions-store.ts +8 -2
  815. package/src/nexus/api/setup-answer-priority.ts +130 -0
  816. package/src/nexus/api/setup-channel-bot.ts +56 -0
  817. package/src/nexus/api/setup-child-llm.ts +228 -0
  818. package/src/nexus/api/setup-obsidian-skills.ts +93 -0
  819. package/src/nexus/api/task-cards-api.ts +34 -0
  820. package/src/nexus/api/tasks-create.ts +136 -0
  821. package/src/nexus/api/telegram-trigger-bot.ts +3 -3
  822. package/src/nexus/api/terminals.ts +204 -18
  823. package/src/nexus/api/trace.ts +354 -0
  824. package/src/nexus/api/vault-api.ts +140 -7
  825. package/src/nexus/api/worktrees.ts +18 -2
  826. package/src/nexus/boot/register-mcp-clients.ts +38 -4
  827. package/src/nexus/daemon-endpoint.ts +85 -0
  828. package/src/nexus/default-port.ts +1 -0
  829. package/src/nexus/index.ts +329 -111
  830. package/src/nexus/onboarding/pwa-share-prompt.ts +31 -8
  831. package/src/nexus/onboarding/tailscale-probe.ts +9 -1
  832. package/src/nexus/setup-status.ts +58 -0
  833. package/src/nexus/supervisor/lock.ts +8 -0
  834. package/src/nexus/webterm/pty.ts +15 -0
  835. package/src/oauth/codex-account-rotation.ts +122 -16
  836. package/src/oauth/codex-account-store.ts +88 -19
  837. package/src/oauth/codex-quota-policy.ts +55 -0
  838. package/src/oauth/store.ts +28 -25
  839. package/src/obsidian/default-vault.ts +17 -0
  840. package/src/obsidian/vault-write-guard.ts +46 -0
  841. package/src/onboarding/help/llm.md +2 -2
  842. package/src/onboarding.ts +2 -2
  843. package/src/panes/alt-skip-predicate.ts +4 -3
  844. package/src/panes/external-terminal-pane.ts +3 -4
  845. package/src/panes/factory.ts +1 -18
  846. package/src/panes/index.ts +1 -5
  847. package/src/panes/pane-cycle.ts +1 -1
  848. package/src/panes/types.ts +1 -1
  849. package/src/platform/env-literal.ts +4 -0
  850. package/src/plugin-hooks/dispatcher.ts +10 -4
  851. package/src/plugins/core/host.ts +94 -4
  852. package/src/plugins/core/manifest.ts +104 -7
  853. package/src/plugins/install/market-fetch.ts +249 -0
  854. package/src/plugins/install/plugin-install.ts +387 -0
  855. package/src/policy/claude-subscription-guard.ts +25 -0
  856. package/src/preview/terminal.ts +24 -1
  857. package/src/preview-pane/mount.ts +1 -142
  858. package/src/prompt/references.ts +4 -51
  859. package/src/pty-shell/pty-control-ipc.ts +57 -3
  860. package/src/pty-shell/pty-mouse.ts +75 -0
  861. package/src/pty-shell/pty-recording.ts +108 -0
  862. package/src/pty-shell/registry.ts +53 -3
  863. package/src/python/resolve-python.ts +1 -1
  864. package/src/release-loop/manifest.ts +65 -0
  865. package/src/release-loop/release-note.ts +174 -0
  866. package/src/roles/lease-cas.ts +52 -0
  867. package/src/roles/machine-name.ts +48 -0
  868. package/src/roles/machine-profile.ts +65 -0
  869. package/src/roles/role-lease-read.ts +81 -0
  870. package/src/roles/role-lease.ts +63 -0
  871. package/src/roles/role-watch.ts +69 -0
  872. package/src/self-dev/ask-launch-flow.ts +39 -22
  873. package/src/self-dev/ask-launch-io.ts +1 -1
  874. package/src/self-dev/dev-cli.ts +250 -139
  875. package/src/self-dev/dev-pipeline.ts +80 -8
  876. package/src/self-dev/draft-sweep.ts +231 -0
  877. package/src/self-dev/draft-triage-rules.ts +42 -0
  878. package/src/self-dev/entrance-baseline.ts +156 -4
  879. package/src/self-dev/fabric-decompose-adapter.ts +10 -0
  880. package/src/self-dev/launch-preflight.ts +1 -1
  881. package/src/self-dev/orchestrate.ts +297 -20
  882. package/src/self-dev/repair-signals.ts +6 -0
  883. package/src/self-dev/run-store.ts +20 -5
  884. package/src/self-dev/run-supervisor.ts +43 -3
  885. package/src/self-implement/abandoned-classification.ts +15 -1
  886. package/src/self-implement/blocked-draft-policy.ts +6 -0
  887. package/src/self-implement/budget-gate.ts +236 -0
  888. package/src/self-implement/child-llm-preference.ts +84 -0
  889. package/src/self-implement/decision-events.ts +57 -0
  890. package/src/self-implement/decision-signal-press.ts +5 -2
  891. package/src/self-implement/dependency-change-gate.ts +90 -0
  892. package/src/self-implement/gate-baseline.ts +32 -1
  893. package/src/self-implement/gate-cli.ts +29 -7
  894. package/src/self-implement/gate-scope.ts +7 -1
  895. package/src/self-implement/goal-author-decompose.ts +22 -4
  896. package/src/self-implement/goal-author.ts +23 -2
  897. package/src/self-implement/goal-boundary.ts +65 -0
  898. package/src/self-implement/goal-clarification-escalation.ts +1 -0
  899. package/src/self-implement/goal-documents-dir.ts +6 -1
  900. package/src/self-implement/goal-execution-records.ts +58 -0
  901. package/src/self-implement/goal-run-store.ts +42 -24
  902. package/src/self-implement/graph-authority.ts +13 -28
  903. package/src/self-implement/graph-catalog.ts +48 -0
  904. package/src/self-implement/graph-run-contract.ts +127 -0
  905. package/src/self-implement/graph-templates.ts +14 -7
  906. package/src/self-implement/graph-yaml.ts +35 -6
  907. package/src/self-implement/harness-policy-gates.ts +42 -0
  908. package/src/self-implement/headless-elanous-driver.ts +30 -6
  909. package/src/self-implement/heal-triage.ts +229 -0
  910. package/src/self-implement/host-regate.ts +240 -0
  911. package/src/self-implement/non-git-consent.ts +23 -0
  912. package/src/self-implement/orchestrator.ts +481 -132
  913. package/src/self-implement/pipeline-shape.ts +1 -1
  914. package/src/self-implement/pr-evidence-artifact.ts +6 -19
  915. package/src/self-implement/prior-draft-findings.ts +128 -0
  916. package/src/self-implement/pwa-import-graph.ts +65 -0
  917. package/src/self-implement/repo-provision.ts +18 -21
  918. package/src/self-implement/report-origin.ts +39 -0
  919. package/src/self-implement/rework-policy.ts +21 -0
  920. package/src/self-implement/run-ledger-compact.ts +438 -0
  921. package/src/self-implement/run-ledger.ts +212 -29
  922. package/src/self-implement/run-report.ts +24 -0
  923. package/src/self-implement/run-status-mapping.ts +2 -0
  924. package/src/self-implement/running-runs.ts +112 -14
  925. package/src/self-implement/scoped-typecheck-config.ts +49 -0
  926. package/src/self-implement/seams.ts +203 -38
  927. package/src/self-implement/self-implement-cli.ts +17 -2
  928. package/src/self-implement/shadow-repo.ts +350 -0
  929. package/src/self-implement/supervision-vocabulary.ts +7 -0
  930. package/src/shell-runner/dispatch.ts +3 -0
  931. package/src/shell-runner/index.ts +0 -7
  932. package/src/skills/runner.ts +0 -69
  933. package/src/skills/tools/agent-room-slash.ts +2 -1
  934. package/src/skills/tools/agent-room.ts +8 -140
  935. package/src/skills/tools/agent.ts +5 -3
  936. package/src/skills/tools/context.ts +5 -140
  937. package/src/skills/tools/control.ts +4 -188
  938. package/src/skills/tools/dashboard-slash.ts +3 -11
  939. package/src/skills/tools/dashboard-state.ts +3 -5
  940. package/src/skills/tools/dev-harness.ts +3 -2
  941. package/src/skills/tools/omni-search.ts +29 -1
  942. package/src/skills/tools/shell.ts +11 -29
  943. package/src/skills/tools/spawn-coding-agent-headless.ts +6 -9
  944. package/src/ssh/ssh-picker-modal.ts +1 -1
  945. package/src/steward/directive.ts +99 -0
  946. package/src/steward/triage.ts +203 -0
  947. package/src/storage/cli-bin.ts +67 -0
  948. package/src/storage/object-store-visibility.ts +169 -0
  949. package/src/storage/object-store.ts +452 -0
  950. package/src/storage/s3.ts +53 -10
  951. package/src/storage/storage-detect.ts +337 -0
  952. package/src/surface/address.ts +3 -5
  953. package/src/surface/index.ts +0 -6
  954. package/src/surface/llm-tools.ts +1 -2
  955. package/src/surface/shell-runner-boot.ts +4 -89
  956. package/src/task-cards/card-cli.ts +64 -0
  957. package/src/task-cards/card-store.ts +241 -0
  958. package/src/task-orchestrator/boot.ts +27 -1
  959. package/src/task-orchestrator/dispatcher.ts +49 -7
  960. package/src/task-orchestrator/external-fingerprint.ts +10 -0
  961. package/src/task-orchestrator/external-policy.ts +86 -0
  962. package/src/task-orchestrator/feedback-loop.ts +4 -2
  963. package/src/task-orchestrator/graph.ts +18 -1
  964. package/src/task-orchestrator/runtimes/create.ts +128 -0
  965. package/src/task-orchestrator/store.ts +78 -18
  966. package/src/task-orchestrator/surface-registry.ts +5 -2
  967. package/src/task-orchestrator/surfaces/external-exec.ts +180 -0
  968. package/src/task-orchestrator/surfaces/idea-approval-pr.ts +118 -0
  969. package/src/task-orchestrator/surfaces/index.ts +4 -3
  970. package/src/task-orchestrator/surfaces/pod-account-broker.ts +60 -5
  971. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +136 -0
  972. package/src/task-orchestrator/surfaces/pod-command-job.ts +348 -0
  973. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +177 -0
  974. package/src/task-orchestrator/surfaces/pod-pool.ts +128 -12
  975. package/src/task-orchestrator/surfaces/pod-skills.ts +11 -3
  976. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +132 -0
  977. package/src/task-orchestrator/surfaces/pod-source-receive.ts +91 -0
  978. package/src/task-orchestrator/surfaces/pod-source-spec.ts +77 -0
  979. package/src/task-orchestrator/surfaces/self-implement-pod.ts +782 -41
  980. package/src/task-orchestrator/surfaces/self-implement.ts +8 -1
  981. package/src/task-orchestrator/tox-loop.ts +204 -0
  982. package/src/task-orchestrator/types.ts +21 -1
  983. package/src/telegram-commands.ts +8 -0
  984. package/src/telegram-report.ts +2 -1
  985. package/src/telegram-run.ts +18 -5
  986. package/src/telegram.ts +92 -22
  987. package/src/tool-runtime/acp-session-runtime.ts +28 -7
  988. package/src/tool-runtime/agent-runtime.ts +1 -0
  989. package/src/tool-runtime/context-runtime.ts +18 -35
  990. package/src/tool-runtime/control-runtime.ts +8 -21
  991. package/src/tool-runtime/dashboard-state-runtime.ts +1 -5
  992. package/src/tool-runtime/elanous-asana-runtimes.ts +205 -0
  993. package/src/tool-runtime/elanous-control-runtimes.ts +16 -2
  994. package/src/tool-runtime/elanous-task-submit-runtime.ts +124 -0
  995. package/src/tool-runtime/index.ts +4 -1
  996. package/src/tool-runtime/scenario-runtimes.ts +22 -21
  997. package/src/tool-runtime/scenario-target-mount.ts +6 -32
  998. package/src/tool-runtime/types.ts +8 -0
  999. package/src/tui/chat/attachment-summary.ts +4 -4
  1000. package/src/tui-client/headless-core-guard.ts +0 -1
  1001. package/src/tui.ts +8 -5
  1002. package/src/typecheck-ratchet.ts +15 -0
  1003. package/src/ui/hover-presenter.ts +3 -7
  1004. package/src/user-config.ts +427 -190
  1005. package/src/version/code-revision.ts +33 -8
  1006. package/src/version/packed-revision.json +1 -1
  1007. package/src/video-pipeline/broll-agent.ts +102 -0
  1008. package/src/video-pipeline/recipes/broll.ts +263 -0
  1009. package/src/video-pipeline/walk-line.ts +2 -1
  1010. package/src/views/scratch-image-size.ts +10 -0
  1011. package/src/voice/channel-adapters/pwa-voice-adapter.ts +2 -18
  1012. package/src/voice/channel-adapters/telegram-voice-adapter.ts +1 -6
  1013. package/src/voice/voice-tts-singleton.ts +46 -8
  1014. package/src/web-push/vapid-keys.ts +1 -1
  1015. package/src/web-search/index.ts +5 -5
  1016. package/src/web-terminal/preview-tap-registry.ts +152 -43
  1017. package/src/workflow-runtime/executor.ts +18 -22
  1018. package/src/workflow-runtime/node-catalog.ts +20 -1
  1019. package/src/workflow-runtime/nodes/bash.ts +1 -0
  1020. package/src/workflow-runtime/nodes/task.ts +74 -0
  1021. package/src/workflow-runtime/nodes/triggers.ts +24 -1
  1022. package/src/workflow-runtime/plugin-kind-node.ts +138 -0
  1023. package/src/workflow-runtime/schema.ts +98 -2
  1024. package/src/workflow-runtime/triggers/webhook-router.ts +43 -3
  1025. package/src/workflow-runtime/triggers/webhook-source.ts +19 -1
  1026. package/src/workflow-runtime/types.ts +74 -4
  1027. package/apps/pwa/out/_next/static/Ksn99fVf2HUWhSyV1-FC8/_buildManifest.js +0 -1
  1028. package/apps/pwa/out/_next/static/chunks/1176-4943bd83b00e53a2.js +0 -25
  1029. package/apps/pwa/out/_next/static/chunks/1380-9c652e1002032c86.js +0 -1
  1030. package/apps/pwa/out/_next/static/chunks/1863-2ea170dfec26b88b.js +0 -1
  1031. package/apps/pwa/out/_next/static/chunks/2091-2d6b7f4f16300f83.js +0 -1
  1032. package/apps/pwa/out/_next/static/chunks/2347-366617ec73babe3b.js +0 -1
  1033. package/apps/pwa/out/_next/static/chunks/2598.814f64e3268634e7.js +0 -1
  1034. package/apps/pwa/out/_next/static/chunks/3089-e60cc8f5d35ee239.js +0 -1
  1035. package/apps/pwa/out/_next/static/chunks/3158-c77ed9ea34f9cc9f.js +0 -1
  1036. package/apps/pwa/out/_next/static/chunks/3607-f0b5c5ee18fcf56a.js +0 -1
  1037. package/apps/pwa/out/_next/static/chunks/3659-05b2d5d971b42dbc.js +0 -1
  1038. package/apps/pwa/out/_next/static/chunks/3736-b6a7bfdcb8f56ec6.js +0 -1
  1039. package/apps/pwa/out/_next/static/chunks/3839-eebf298e89a7bb77.js +0 -1
  1040. package/apps/pwa/out/_next/static/chunks/4012.832a2824b978e7ee.js +0 -5
  1041. package/apps/pwa/out/_next/static/chunks/4706-d85d7f3345f3ff91.js +0 -1
  1042. package/apps/pwa/out/_next/static/chunks/526-17a6a2e0e6dc4498.js +0 -1
  1043. package/apps/pwa/out/_next/static/chunks/5508-676ac75f2757ad40.js +0 -1
  1044. package/apps/pwa/out/_next/static/chunks/5583-8a0317f4b8e68db9.js +0 -1
  1045. package/apps/pwa/out/_next/static/chunks/5927.8378481a12cc8044.js +0 -1
  1046. package/apps/pwa/out/_next/static/chunks/6258-b58f722b10e8d31d.js +0 -1
  1047. package/apps/pwa/out/_next/static/chunks/6820-32fd097e6186fdc9.js +0 -1
  1048. package/apps/pwa/out/_next/static/chunks/6852-983db7ca958634b7.js +0 -1
  1049. package/apps/pwa/out/_next/static/chunks/7235-e583e4a015e159b9.js +0 -1
  1050. package/apps/pwa/out/_next/static/chunks/7482-7b417330b24a996a.js +0 -1
  1051. package/apps/pwa/out/_next/static/chunks/7701-99b7d4dedb955bf8.js +0 -1
  1052. package/apps/pwa/out/_next/static/chunks/77f1928d-bd3777589042d3ea.js +0 -1
  1053. package/apps/pwa/out/_next/static/chunks/8366-36956cafc5e33614.js +0 -1
  1054. package/apps/pwa/out/_next/static/chunks/8370-23d0de995be666f8.js +0 -1
  1055. package/apps/pwa/out/_next/static/chunks/8617-307421f3b0e8b2a2.js +0 -1
  1056. package/apps/pwa/out/_next/static/chunks/8837-e45de46a9fedf1f5.js +0 -1
  1057. package/apps/pwa/out/_next/static/chunks/9058-0e8055a5ea3ebdd6.js +0 -1
  1058. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-84b84eca12acada4.js +0 -1
  1059. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1f23568c29e88f7a.js +0 -1
  1060. package/apps/pwa/out/_next/static/chunks/app/botlab/page-bf6fc9aafd6135a7.js +0 -1
  1061. package/apps/pwa/out/_next/static/chunks/app/bots/page-3f8539ae4b719d2e.js +0 -1
  1062. package/apps/pwa/out/_next/static/chunks/app/chat/page-d2900febb74ecf32.js +0 -1
  1063. package/apps/pwa/out/_next/static/chunks/app/control/page-e82dec647dd79c36.js +0 -1
  1064. package/apps/pwa/out/_next/static/chunks/app/dashboard/page-096ec743a8ee955a.js +0 -1
  1065. package/apps/pwa/out/_next/static/chunks/app/design-check/page-255c2ceded4b3493.js +0 -1
  1066. package/apps/pwa/out/_next/static/chunks/app/healthz/route-84b84eca12acada4.js +0 -1
  1067. package/apps/pwa/out/_next/static/chunks/app/intake/page-2db61b2f9efba61f.js +0 -1
  1068. package/apps/pwa/out/_next/static/chunks/app/layout-e11ee17f221aaa9b.js +0 -1
  1069. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e075974e029ce4c4.js +0 -1
  1070. package/apps/pwa/out/_next/static/chunks/app/morning/page-ec5b68a9af3366a0.js +0 -1
  1071. package/apps/pwa/out/_next/static/chunks/app/not-found-986c177afb4adae6.js +0 -1
  1072. package/apps/pwa/out/_next/static/chunks/app/observatory/page-a72781461af2f2a3.js +0 -1
  1073. package/apps/pwa/out/_next/static/chunks/app/page-92781cbaafc487ad.js +0 -1
  1074. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2c14d2a1f2930f33.js +0 -1
  1075. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-22ee4bf8070b2b4d.js +0 -1
  1076. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9c06e4a8297942a8.js +0 -1
  1077. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d0cd32f6d373798f.js +0 -1
  1078. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c1642018d87ebb8.js +0 -1
  1079. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-93e5e97d614df487.js +0 -1
  1080. package/apps/pwa/out/_next/static/chunks/app/setup/layout-84b84eca12acada4.js +0 -1
  1081. package/apps/pwa/out/_next/static/chunks/app/setup/page-b1ce871940d7393e.js +0 -1
  1082. package/apps/pwa/out/_next/static/chunks/app/share/page-ae154addf345febe.js +0 -1
  1083. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90af3aa813d2053c.js +0 -1
  1084. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f3f962fda428a738.js +0 -1
  1085. package/apps/pwa/out/_next/static/chunks/app/term/page-1c1f7cc1d5def06d.js +0 -1
  1086. package/apps/pwa/out/_next/static/chunks/app/vault/page-09d4cbdaaacf06c1.js +0 -1
  1087. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e877b8e8ca90b27f.js +0 -1
  1088. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71d902f463dea526.js +0 -1
  1089. package/apps/pwa/out/_next/static/chunks/app/workspace/page-40ea576cefa25fe6.js +0 -1
  1090. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a56a3f178c3831f6.js +0 -1
  1091. package/apps/pwa/out/_next/static/chunks/bcc499d1-804a270d878e0842.js +0 -1
  1092. package/apps/pwa/out/_next/static/chunks/framework-b25b7c5501c59b6d.js +0 -1
  1093. package/apps/pwa/out/_next/static/chunks/main-a9ec9da43387afff.js +0 -1
  1094. package/apps/pwa/out/_next/static/chunks/main-app-aa2c05954aaa6754.js +0 -1
  1095. package/apps/pwa/out/_next/static/chunks/pages/_app-bb3768fb4b84aebb.js +0 -1
  1096. package/apps/pwa/out/_next/static/chunks/pages/_error-2820ba75b87b2f5e.js +0 -1
  1097. package/apps/pwa/out/_next/static/chunks/webpack-58c73b145f189b1c.js +0 -1
  1098. package/apps/pwa/out/_next/static/css/effec8adf767aa75.css +0 -1
  1099. package/apps/pwa/out/dashboard/index.html +0 -1
  1100. package/apps/pwa/out/dashboard/index.txt +0 -26
  1101. package/src/acp/resident-shell.ts +0 -244
  1102. package/src/agent/spawn-local-llm-in-vw.ts +0 -240
  1103. package/src/dashboard/agent-spawn-tools-boot.ts +0 -14
  1104. package/src/dashboard/dock-picker-inventory.ts +0 -41
  1105. package/src/dashboard/goal-plan-hud-bridge.ts +0 -112
  1106. package/src/dashboard/hud-mirror.ts +0 -207
  1107. package/src/dashboard/input/mouse-dock-runtime.ts +0 -156
  1108. package/src/dashboard/input/mouse-hover-runtime.ts +0 -46
  1109. package/src/dashboard/input/mouse-pill-context-runtime.ts +0 -74
  1110. package/src/dashboard/input/mouse-workspace-restore-runtime.ts +0 -64
  1111. package/src/dashboard/input/scratch-actions.ts +0 -36
  1112. package/src/dashboard/input/virtual-window-key-router.ts +0 -24
  1113. package/src/dashboard/rich-scratch-viewers.ts +0 -77
  1114. package/src/dashboard/scratch-slash-runtime.ts +0 -36
  1115. package/src/dashboard/scratch-state-runtime.ts +0 -42
  1116. package/src/dashboard/scratch-widget-runtime.ts +0 -37
  1117. package/src/dashboard/sim-shell.ts +0 -313
  1118. package/src/dashboard/virtual-window-control-runtime.ts +0 -57
  1119. package/src/dashboard/virtual-window-help-runtime.ts +0 -113
  1120. package/src/dashboard/virtual-window-input-runtime.ts +0 -99
  1121. package/src/dashboard/virtual-window-mutation-runtime.ts +0 -102
  1122. package/src/dashboard/virtual-window-split-runtime.ts +0 -70
  1123. package/src/dashboard/vw-popup-openers.ts +0 -126
  1124. package/src/dashboard/window-picker-popup.ts +0 -74
  1125. package/src/dashboard/window-slash-runtime.ts +0 -108
  1126. package/src/dashboard/windowing/companion-slash.ts +0 -78
  1127. package/src/dashboard/windowing/fast-switch.ts +0 -138
  1128. package/src/dashboard/windowing/lifecycle.ts +0 -185
  1129. package/src/dashboard/windowing/virtual-windows.ts +0 -259
  1130. package/src/dashboard/windowing/visibility-chord.ts +0 -77
  1131. package/src/iul/resident-vw.ts +0 -55
  1132. package/src/panes/content-adapter.ts +0 -162
  1133. package/src/prompt/vw-reference.ts +0 -153
  1134. package/src/shell-runner/external-terminal-pane.ts +0 -157
  1135. package/src/sim/resident-vw.ts +0 -48
  1136. package/src/skills/tools/spawn-coding-agent-vw.ts +0 -181
  1137. package/src/skills/tools/virtual-windows.ts +0 -632
  1138. package/src/surface/adapters/window-surface-adapter.ts +0 -141
  1139. package/src/tool-runtime/layout-runtimes.ts +0 -81
  1140. package/src/tool-runtime/scenario-window-mount.ts +0 -207
  1141. package/src/views/ui-mode.ts +0 -100
  1142. package/src/virtual-window-context-menu.ts +0 -97
  1143. package/src/virtual-windows/addressing.ts +0 -195
  1144. package/src/virtual-windows/benchmark-preset.ts +0 -119
  1145. package/src/virtual-windows/event-bus.ts +0 -202
  1146. package/src/virtual-windows/layout/apply-planner.ts +0 -189
  1147. package/src/virtual-windows/layout/index.ts +0 -68
  1148. package/src/virtual-windows/layout/layout-commands.ts +0 -186
  1149. package/src/virtual-windows/layout/layout-tools.ts +0 -262
  1150. package/src/virtual-windows/layout/persistence.ts +0 -219
  1151. package/src/virtual-windows/layout/presets.ts +0 -114
  1152. package/src/virtual-windows/layout/restore-planner.ts +0 -223
  1153. package/src/virtual-windows/layout/serializer.ts +0 -197
  1154. package/src/virtual-windows/layout/tree.ts +0 -147
  1155. package/src/virtual-windows/layout/types.ts +0 -63
  1156. package/src/virtual-windows/layout-tree.ts +0 -389
  1157. package/src/virtual-windows/navigation.ts +0 -251
  1158. package/src/virtual-windows/pane-capture.ts +0 -142
  1159. package/src/virtual-windows/pane-content.ts +0 -611
  1160. package/src/virtual-windows/pane-spawner.ts +0 -278
  1161. package/src/virtual-windows/pty-tail-content.ts +0 -291
  1162. package/src/virtual-windows/resident-window.ts +0 -59
  1163. package/src/virtual-windows/secondary-action.ts +0 -25
  1164. package/src/virtual-windows/terminal-slot-content.ts +0 -165
  1165. package/src/virtual-windows/virtual-window.ts +0 -1301
  1166. package/src/virtual-windows/vw-local-input-target-popup.ts +0 -110
  1167. package/src/virtual-windows/vw-rename-modal.ts +0 -77
  1168. package/src/virtual-windows/vw-selector-popup.ts +0 -177
  1169. package/src/virtual-windows/window-registry.ts +0 -519
  1170. package/src/voice/channel-adapters/discord-voice-adapter.ts +0 -202
  1171. package/src/voice/voice-pwa-dispatch.ts +0 -245
  1172. package/src/window-picker-modal.ts +0 -135
  1173. /package/apps/pwa/out/_next/static/{Ksn99fVf2HUWhSyV1-FC8 → zoQX5iTvXOsIvCjxCe1Ji}/_ssgManifest.js +0 -0
  1174. /package/src/dashboard/{companion-slash-runtime.ts → companion-messages-runtime.ts} +0 -0
@@ -295,24 +295,10 @@ export interface RegisterDashboardGlobalKeysDeps {
295
295
  register: (binding: DashboardGlobalKeyRegistration) => void;
296
296
  openSurfaceCatalog: () => void;
297
297
  surfaceCatalogWhen?: () => boolean;
298
- dispatchVirtualWindowChord: (event: DisplayKeyEvent) => void;
299
298
  }
300
299
 
301
- /** Registers optional dashboard controls and the core VW controls that must always remain available. */
300
+ /** Registers optional dashboard controls. */
302
301
  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
302
  if (deps.enableSupplementalGlobalKeys) {
317
303
  deps.register({
318
304
  id: 'dashboard:open-surface-catalog',
@@ -321,14 +307,7 @@ export function registerDashboardGlobalKeys(deps: RegisterDashboardGlobalKeysDep
321
307
  handler: deps.openSurfaceCatalog,
322
308
  when: deps.surfaceCatalogWhen,
323
309
  });
324
- registerVwChord('picker', '0', vwKey('0'));
325
- registerVwChord('close-window', 'S-x', vwKey('x', { shift: true }));
326
- registerVwChord('last-pane', 'tab', vwKey('tab'));
327
310
  }
328
-
329
- registerVwChord('new-window', 'c', vwKey('c'));
330
- registerVwChord('close-pane', 'x', vwKey('x'));
331
- registerVwChord('zoom-toggle', 'z', vwKey('z'));
332
311
  }
333
312
 
334
313
  export type ForkableSessionTurn = {
@@ -509,13 +488,10 @@ import {
509
488
  } from '../primitives/damage-region/index.js';
510
489
  import {
511
490
  createArtifactStore,
512
- createLegacyLayoutProvider,
513
491
  createLegacyTimelineProvider,
514
492
  } from '../artifact/index.js';
515
493
  import { defaultTimelineBaseDir } from '../tool-runtime/recording-runtimes-paths.js';
516
- import { layoutsDir } from '../virtual-windows/layout/persistence.js';
517
494
  import { createVisualStateStore } from '../panes/visual-state.js';
518
- import { skipWindowWhenStorePredicate } from '../panes/alt-skip-predicate.js';
519
495
  import { composeIdentityHooks } from '../display/modal-identity-wiring.js';
520
496
  import { composeWorkspaceHostHooks } from '../display/workspace-host-wiring.js';
521
497
  import {
@@ -650,7 +626,6 @@ import {
650
626
  matchDashboardChordAction,
651
627
  } from './input/dashboard-chord-actions.js';
652
628
  import { createDashboardChordRuntime } from './input/dashboard-chord-runtime.js';
653
- import { createMouseDockRuntime } from './input/mouse-dock-runtime.js';
654
629
  import { handleDashboardDisplayKeyRoute } from './input/dashboard-display-key-route.js';
655
630
  import { routeDashboardPriorityKey } from './input/dashboard-priority-key-route.js';
656
631
  import { shouldPassthroughDashboardHostOwnedInputCoreAction } from './input/input-core-host-action.js';
@@ -915,15 +890,12 @@ import {
915
890
  import { globalVoiceCostTracker } from '../voice/cost-tracker.js';
916
891
  import { createDashboardMouseWiring } from './input/mouse-wiring.js';
917
892
  import { createMouseConversationPopupRuntime } from './input/mouse-conversation-popup-runtime.js';
918
- import { createMouseHoverRuntime } from './input/mouse-hover-runtime.js';
919
893
  import { createMouseModalHitRuntime } from './input/mouse-modal-hit-runtime.js';
920
894
  import { createMouseModalSurfaceRuntime } from './input/mouse-modal-surface-runtime.js';
921
895
  import { createMouseModeSwitchRuntime } from './input/mouse-mode-switch-runtime.js';
922
896
  import { createMousePaneHitRuntime } from './input/mouse-pane-hit-runtime.js';
923
897
  import { createMousePickerRuntime } from './input/mouse-picker-runtime.js';
924
- import { createMousePillContextRuntime } from './input/mouse-pill-context-runtime.js';
925
898
  import { createMouseShellRollupRuntime } from './input/mouse-shell-rollup-runtime.js';
926
- import { createMouseWorkspaceRestoreRuntime } from './input/mouse-workspace-restore-runtime.js';
927
899
  import { createDashboardPluginBaseRuntime } from './plugin-base-runtime.js';
928
900
  import { createDashboardPluginExecutionRuntime } from './plugin-execution-runtime.js';
929
901
  import { createDashboardPluginPaneRuntime } from './plugin-pane-runtime.js';
@@ -987,7 +959,6 @@ import { runDashboardSkillByName } from './skill-runtime.js';
987
959
  import { createDashboardClipboardActions } from './clipboard-actions.js';
988
960
  import {
989
961
  buildDashboardEditorOpenPayload,
990
- buildDashboardScratchDumpLines,
991
962
  } from './clipboard-message-runtime.js';
992
963
  import { createDashboardCopyRuntime } from './copy-runtime.js';
993
964
  import { createDashboardMediaRuntime } from './media-runtime.js';
@@ -998,18 +969,14 @@ import { createDashboardContextMenuFeedbackRuntime } from './context-menu-feedba
998
969
  import { createDashboardStatusFeedbackRuntime } from './status-feedback-runtime.js';
999
970
  import { createDashboardTransferFeedbackRuntime } from './transfer-feedback-runtime.js';
1000
971
  import { createDashboardFinderFeedbackRuntime } from './finder-feedback-runtime.js';
1001
- import { createDashboardWindowSlashRuntime } from './window-slash-runtime.js';
1002
- import { createDashboardBenchSlashRuntime } from './bench-slash-runtime.js';
1003
972
  import { createDashboardTermSlashRuntime } from './term-slash-runtime.js';
1004
973
  import { createDashboardShellSlashRuntime } from './shell-slash-runtime.js';
1005
974
  import { createDashboardRunSkillSlashRuntime } from './run-skill-slash-runtime.js';
1006
975
  import { createDashboardSkillTriggersSlashRuntime } from './skill-triggers-slash-runtime.js';
1007
976
  import { createDashboardProviderSlashRuntime } from './provider-slash-runtime.js';
1008
977
  import { createDashboardPreviewSlashRuntime } from './preview-slash-runtime.js';
1009
- import { createDashboardScratchSlashRuntime } from './scratch-slash-runtime.js';
1010
- import { createDashboardCompanionSlashRuntime } from './companion-slash-runtime.js';
978
+ import { createDashboardCompanionSlashRuntime } from './companion-messages-runtime.js';
1011
979
  import { createDashboardAgentsSlashRuntime } from './agents-slash-runtime.js';
1012
- import { createDashboardViewSlashRuntime } from './view-slash-runtime.js';
1013
980
  import { createDashboardControlSignalSlashRuntime } from './control-signal-slash-runtime.js';
1014
981
  import { createDashboardBrowserCdpSlashRuntime } from './browser-cdp-slash-runtime.js';
1015
982
  import { collectGroundingFactsViaCdp } from '../product-grounding/cdp-collect.js';
@@ -1025,9 +992,6 @@ import { resolveDashboardMediaSlash } from './media-slash-runtime.js';
1025
992
  import { createDashboardMemoCompanionRuntime } from './memo-companion-runtime.js';
1026
993
  import { createDashboardDetailViewerRuntime } from './detail-viewer-runtime.js';
1027
994
  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
995
  import { createDashboardClipboardHistoryRuntime } from './clipboard-history-runtime.js';
1032
996
  import { createDashboardClipboardCompanionRuntime } from './clipboard-companion-runtime.js';
1033
997
  import { createDashboardMemoWidgetRuntime } from './memo-widget-runtime.js';
@@ -1082,7 +1046,6 @@ import {
1082
1046
  import { createDashboardTerminalPopupRuntime } from './terminal-popup-runtime.js';
1083
1047
  import { toggleDashboardAssistantRenderState } from './assistant-render-state-runtime.js';
1084
1048
  import { bootDashboardAcpBgSweep } from './acp-bg-sweep-boot.js';
1085
- import { bootDashboardAgentSpawnTools } from './agent-spawn-tools-boot.js';
1086
1049
  import { bootDashboardConfigTools } from './config-tools-boot.js';
1087
1050
  import { bootDashboardConversationPopupRuntime } from './conversation-popup-boot.js';
1088
1051
  import { bootDashboardTerminalRuntime } from './terminal-runtime-boot.js';
@@ -1108,15 +1071,10 @@ import type { SurfaceUx } from '../agent/surface-ux/types.js';
1108
1071
  import { getDefaultQuestionChannels, type QuestionChannel } from '../hitl/question.js';
1109
1072
  import { getDefaultCompactProvider, scheduleArchiveRetentionOnce } from '../compact/index.js';
1110
1073
  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
1074
  import { createBrowserHelpRuntime } from './browser-help-runtime.js';
1114
1075
  import { createBrowserChordRuntime } from './browser-chord-runtime.js';
1115
1076
  import { summarizeFileWithLLM } from './browser-llm-summary.js';
1116
- import { createDashboardVirtualWindowInputRuntime } from './virtual-window-input-runtime.js';
1117
1077
  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
1078
  import {
1121
1079
  bootDashboardPlanModeRuntime,
1122
1080
  cleanupDashboardWorktreeSessions,
@@ -1152,11 +1110,7 @@ import {
1152
1110
  import {
1153
1111
  type DashboardPaneTitleMenuPayload,
1154
1112
  } from '../dashboard-pane-context-menu.js';
1155
- import {
1156
- type VirtualWindowTitleMenuPayload,
1157
- } from '../virtual-window-context-menu.js';
1158
1113
  // writeClipboard imported below with other clipboard helpers (line ~185).
1159
- import { registerVwFastSwitchBindings } from './windowing/fast-switch.js';
1160
1114
  import {
1161
1115
  renderPaneNav, paneAtColumn, paneNavLabel,
1162
1116
  type PaneNavHitArea,
@@ -1204,15 +1158,9 @@ import { createInteractiveTerminalModal } from '../interactive-terminal-modal.js
1204
1158
  import { showToast } from './render/toast.js';
1205
1159
  import {
1206
1160
  browserWidgetInstanceIdForView,
1207
- renderPaneModalHint,
1208
1161
  shortcutFor,
1209
- SHORTCUT_TABLE as PANE_SHORTCUT_TABLE,
1210
- PANE_MODAL_CHORD_TIMEOUT_MS,
1211
1162
  } 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';
1163
+ import { showTransientTerminalModal, currentTransientTerminalModal } from './modals/transient.js';
1216
1164
  import { showPaneMultiModal, showLivePaneMultiModal, currentPaneMultiModal, type PaneMultiModalHandle } from './modals/pane-multi.js';
1217
1165
  import {
1218
1166
  resolveCompanionPopupChrome,
@@ -1234,26 +1182,10 @@ import {
1234
1182
  TerminalRegistry,
1235
1183
  BroadcastBus,
1236
1184
  PreviewSlotAdapter,
1237
- VwPlacementAdapter,
1238
1185
  getChannelBus,
1239
1186
  type TerminalPlacement,
1240
1187
  } 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';
1188
+ import { resolveTerminalMoveDestination } from '../terminal-matrix/mobility.js';
1257
1189
  import { initElementObservability } from '../element-registry/index.js';
1258
1190
  import {
1259
1191
  armSessionQuickControl,
@@ -1278,8 +1210,6 @@ import {
1278
1210
  import {
1279
1211
  initDashboardApprovers,
1280
1212
  createInjectApprover,
1281
- createPaneInjectApprover,
1282
- createBroadcastApprover,
1283
1213
  createConfigSetApprover,
1284
1214
  createAcpPermissionApprover,
1285
1215
  openGenericApproval,
@@ -1303,7 +1233,6 @@ import { createDashboardCompactSurfaceAssembly } from './compact-surface-assembl
1303
1233
  import { openCompactSurfaceCatalogPopup } from './compact-surface-popup.js';
1304
1234
  import { approvalModalRouter, type ApprovalModalRouter } from '../approval-modal.js';
1305
1235
  import { routeApprovalModalKey } from './input/approval-key-router.js';
1306
- import { createWindowPickerModal } from '../window-picker-modal.js';
1307
1236
 
1308
1237
  export function routeStreamingApprovalModalKey(
1309
1238
  key: Parameters<typeof routeApprovalModalKey>[0],
@@ -1320,21 +1249,9 @@ export function routeStreamingApprovalModalKey(
1320
1249
  import {
1321
1250
  attachSurfaceToWorkspace,
1322
1251
  DASHBOARD_MAIN_WORKSPACE_ID,
1323
- workspaceOwnerIdForVirtualWindow,
1324
1252
  } 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
1253
  import { initDashboardHitl, stopDashboardHitl } from './runtime/hitl.js';
1331
- import { openDashboardWindowPickerPopup } from './window-picker-popup.js';
1332
- import { createIulSidebarShellPaneContent } from '../iul/sidebar-shell.js';
1333
1254
  import { openDashboardShellRollupPopup } from './shell-rollup-popup.js';
1334
- import {
1335
- openVwLocalInputTargetPopupLauncher,
1336
- openVwRenameModalLauncher,
1337
- } from './vw-popup-openers.js';
1338
1255
  import { expandPromptReferences } from '../prompt/references.js';
1339
1256
  import { spawnCodingAgent, CodingAgentBinaryMissing } from '../terminal/coding-agent.js';
1340
1257
  import type { TerminalSession } from '../terminal/session-registry.js';
@@ -1367,7 +1284,7 @@ import { enqueuePendingUserInput } from '../session/pending-input.js';
1367
1284
  import { createAttachmentRowMap } from '../log-pane/attachment-row-map.js';
1368
1285
  import { createAttachmentPopup, type AttachmentPopupAction } from '../log-pane/attachment-popup.js';
1369
1286
  import { findLogMatches, type LogSearchResult } from '../log-pane/search.js';
1370
- import { createLogSearchModal } from '../log-pane/search-modal.js';
1287
+ import { createLogSearchModal, type LogSearchModalOpts } from '../log-pane/search-modal.js';
1371
1288
  // U-4.3 · direct imports of tryAttachmentHitAtBodyRow /
1372
1289
  // tryHandleLogAreaClick are gone · wd-log.onMouse owns the hit-test.
1373
1290
  // LogClickDispatchDeps type still needed for buildLogClickDeps +
@@ -1379,7 +1296,7 @@ import {
1379
1296
  import { fileColor, fileIcon, sizeStr, dirColor } from '../panes/file-icons.js';
1380
1297
  import { canPreview } from '../panes/preview-pane.js';
1381
1298
  import { colorLine } from '../panes/syntax-color.js';
1382
- import { createHud, setSegment, clearSegment, renderHud } from '../panes/hud.js';
1299
+ import { createHud, setSegment, clearSegment } from '../panes/hud.js';
1383
1300
  import {
1384
1301
  paneHeightBeforeTargetRow,
1385
1302
  paneHeightForTargetRows,
@@ -1429,11 +1346,9 @@ import { initWidgetInspectorTools } from '../skills/tools/widget-inspector.js';
1429
1346
  import {
1430
1347
  initDashboardViewTools,
1431
1348
  } from '../skills/tools/dashboard-view.js';
1432
- import { initSpawnCodingAgentInVW } from '../skills/tools/spawn-coding-agent-vw.js';
1433
1349
  import {
1434
1350
  findLiveSessionById,
1435
1351
  findLiveSessionByPaneId,
1436
- initSpawnEmbodiedAgentInVW,
1437
1352
  listLiveEmbodiedSessions,
1438
1353
  } from '../agent/spawn-embodied-agent-in-vw.js';
1439
1354
  import { findSessionObserver } from '../agent/observer-registry.js';
@@ -1496,7 +1411,7 @@ import { buildAstGrepHostTool } from '../skills/tools/ast-grep.js';
1496
1411
  import { buildLspHostTool } from '../skills/tools/lsp/index.js';
1497
1412
  import { PluginHost } from '../plugins/core/host.js';
1498
1413
  import { registerDashboardPaneHostTools } from './panes/host-tools.js';
1499
- import { DEFAULT_THEME_TOKENS, mergeThemeTokens, resolveThemeTokens, type ThemeTokenInput } from '../theme/tokens.js';
1414
+ import { DEFAULT_THEME_TOKENS, mergeThemeTokens, resolveThemeTokens, type ThemeTokenInput, type ThemeTokens } from '../theme/tokens.js';
1500
1415
  import { resolveActiveTheme, setActivePresetInConfig } from './render/theme-resolver.js';
1501
1416
  import { getTheme, listThemes } from '../themes/index.js';
1502
1417
  import type { PaneFocus, PreviewSource, WorkingDirView } from '../workspace-types.js';
@@ -1525,9 +1440,8 @@ import {
1525
1440
  resolveBrowserActionContextFromHit,
1526
1441
  } from '../browser-pane/actions.js';
1527
1442
  import { resolveBrowserTransferCapability } from '../browser-pane/capabilities.js';
1528
- import { createBrowserPaneContent, openBrowserPaneModal } from '../browser-pane/mount.js';
1443
+ import { openBrowserPaneModal } from '../browser-pane/mount.js';
1529
1444
  import { openPreviewPaneModal } from '../preview-pane/mount.js';
1530
- import { createPreviewPaneContent } from '../preview-pane/mount.js';
1531
1445
  import {
1532
1446
  cyclePreviewSourceForView,
1533
1447
  formatPreviewSourceLabel,
@@ -1558,7 +1472,6 @@ import {
1558
1472
  pruneSurfaceCatalogForCompactMode,
1559
1473
  productCompactModeForViewport,
1560
1474
  } from '../views/product-compact-mode.js';
1561
- import { isDashboardHeavyFeatureEnabled, resolveDashboardUiMode, type DashboardUiMode } from '../views/ui-mode.js';
1562
1475
  import { readScopedRenderLogs, resolveRenderSuppressed, readScopedDebugLevel, resolveStartupDebugLevel, hotPathGateOpen } from '../mss/logging/scoped-level.js';
1563
1476
  import { resolveCurrentInstance } from '../instance/current.js';
1564
1477
  import { createPushLog } from '../push-log.js';
@@ -1658,34 +1571,16 @@ import {
1658
1571
  TERMINAL_BACKGROUND_STATES,
1659
1572
  withBgApprovalSignals,
1660
1573
  } 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';
1574
+ import { dispatchAcpSessionJoin } from '../skills/tools/acp-session.js';
1669
1575
  import {
1670
1576
  listDashboardSimulationScenarios,
1671
1577
  runDashboardSimulationScenario,
1672
1578
  } 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
1579
  import { preservesHostChromeInput } from '../display/host-chrome-profile.js';
1685
1580
  // H3 #6 follow-up #1 — auto-persist on turn-end. Subscribes DRM +
1686
1581
  // BG managers to the H2 #5 persistence primitive so
1687
1582
  // `AcpSessionList` returns real records instead of empty arrays.
1688
- // Mode controlled by ELANOUS_ACP_PERSIST_MODE env (default 'auto').
1583
+ // Mode is always 'auto' (the ELANOUS_ACP_PERSIST_MODE off-switch was graduated 2026-09-26).
1689
1584
  import { wireAutoPersist } from '../acp/auto-persist.js';
1690
1585
  import { globalAcpSessionPersistence } from '../acp/session-persistence.js';
1691
1586
  import { AgentStatusStore } from '../agent-status/store.js';
@@ -1752,10 +1647,6 @@ export interface ShowDashboardOptions {
1752
1647
  * input). Useful on tablets where the 3-pane grid is too narrow
1753
1648
  * to read. Toggle at runtime with /chat or the chord in the HUD. */
1754
1649
  chatOnly?: boolean;
1755
- /** TUI 부활 T0 — CLI `--rich`. 이번 실행만 uiMode 를 rich 로 강제
1756
- * (config 무변). essential 이 기본이 된 뒤 기존 full dashboard 로
1757
- * 들어가는 탈출구. 해석은 views/ui-mode.ts resolveDashboardUiMode. */
1758
- rich?: boolean;
1759
1650
  /** Flip code-edit policy to unsupervised at boot — every Edit/Write
1760
1651
  * from the LLM applies without an approval modal. Equivalent to
1761
1652
  * typing `/code-edit policy unsupervised` right after launch.
@@ -1771,15 +1662,8 @@ export interface ShowDashboardOptions {
1771
1662
  * (resolved in `main()`). Tool dispatch + history live on the
1772
1663
  * daemon side; the local TUI is a thin client. */
1773
1664
  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
1665
+ /** Tier 1 daemon-resume — when set together with `remote`,
1666
+ * the dashboard attaches to this existing
1783
1667
  * daemon-side sessionId instead of minting a new one. The boot
1784
1668
  * also fetches the session's prior turns via REST (when an HTTP
1785
1669
  * endpoint is reachable, derived from `remote.url`) and replays
@@ -1901,18 +1785,16 @@ export function renderDashboardCoordinatorModalOverlay(display: Pick<DisplayCoor
1901
1785
  * ⭐⭐⭐ **essential 은 커서 값을 Z축에 묻지 않는다**(2026-08-02 · RFC essential-z-axis-off).
1902
1786
  * 입력 루프가 끝나면 `setCursor(null)` 로 소유가 비므로 물어보면 빈손이고, 그래서 턴이 도는
1903
1787
  * 동안 커서가 사라졌다(라이브 14 표본 전부 `visible false`). ⇒ 모달이 **실제로 claim** 했으면
1904
- * 그 값을 쓰고, 아니면 **프롬프트 caret** 을 쓴다. 프롬프트가 감춰졌으면 숨긴다.
1905
- * ⚠️ rich 는 Z축을 실제로 쓰므로 종전 동작(조율자가 정한 값)을 그대로 둔다. */
1788
+ * 그 값을 쓰고, 아니면 **프롬프트 caret** 을 쓴다. 프롬프트가 감춰졌으면 숨긴다. */
1906
1789
  export interface DashboardFrameInput {
1907
1790
  overlay: string;
1908
1791
  force: boolean;
1909
- essential: boolean;
1910
1792
  /** ⭐ Z축이 **실제로 누구에게** 커서를 준 결정. essential 은 `modal`·`terminal` 일 때만
1911
1793
  * 그 값에 양보한다 — `coordinator` fallback 은 소유자가 없다는 뜻이지 claim 이 아니다. */
1912
1794
  cursorOwner: CursorDecision['owner'];
1913
1795
  claimedCursor: CursorState | null;
1914
- /** rich 전용 — `#6528` 까지의 동작을 그대로 보존한다(조율자가 emit 하던 값). */
1915
- coordinatorCursor: CursorState | null;
1796
+ /** essential 결정이 커서를 안 낼 때(null)의 폴백 — 조율자가 emit 하던 값(종전 동작 보존). */
1797
+ coordinatorCursor?: CursorState | null;
1916
1798
  promptCaret: CursorState | null;
1917
1799
  suppressPromptArea: boolean;
1918
1800
  onEssentialFrameCursorDecision?: (observation: EssentialFrameCursorObservation) => void;
@@ -1948,31 +1830,27 @@ export function createDashboardEssentialCursorObserver(deps: DashboardEssentialC
1948
1830
 
1949
1831
  export function renderDashboardFrame(
1950
1832
  lines: string[],
1951
- input: DashboardFrameInput & { onEssentialFrameCursorDecision?: (observation: EssentialFrameCursorObservation) => void },
1833
+ input: DashboardFrameInput,
1952
1834
  renderFrame: DashboardFrameRenderer = render,
1953
1835
  ): void {
1954
1836
  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;
1837
+ const essentialDecision = resolveEssentialFrameCursorDecision({
1838
+ claimedCursor: cursorDecisionExists ? input.claimedCursor : null,
1839
+ promptCaret: input.promptCaret,
1840
+ suppressPromptArea: input.suppressPromptArea,
1841
+ });
1962
1842
  renderFrame(lines, {
1963
1843
  overlay: input.overlay,
1964
1844
  force: input.force,
1965
- cursor: paintCursor(essentialDecision?.cursor ?? input.coordinatorCursor),
1845
+ cursor: paintCursor(essentialDecision.cursor ?? input.coordinatorCursor ?? null),
1846
+ });
1847
+ input.onEssentialFrameCursorDecision?.({
1848
+ caret: essentialDecision.cursor,
1849
+ cursorOwner: input.cursorOwner,
1850
+ reason: essentialDecision.reason,
1851
+ source: essentialDecision.source,
1852
+ suppressPromptArea: input.suppressPromptArea,
1966
1853
  });
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
1854
  }
1977
1855
 
1978
1856
  /** Production frame input: append coordinator modals after every dashboard-owned overlay. */
@@ -2175,6 +2053,47 @@ export async function dispatchDashboardAdSlash(
2175
2053
  afterRun();
2176
2054
  }
2177
2055
 
2056
+ export function createDashboardLogSearchOpener(deps: {
2057
+ display: () => DisplayCoordinator;
2058
+ linesGetter: LogSearchModalOpts['linesGetter'];
2059
+ initialQuery: () => string;
2060
+ termSize: () => { cols: number; rows: number };
2061
+ computePaneH: (rows: number) => number;
2062
+ currentThemeTokens: () => ThemeTokens;
2063
+ currentWorkspaceOwnerId: () => string;
2064
+ draw: () => void;
2065
+ onJump: LogSearchModalOpts['onJump'];
2066
+ onCancel: NonNullable<LogSearchModalOpts['onCancel']>;
2067
+ }): () => void {
2068
+ return () => {
2069
+ let modalHandle: { dispose(): void } | null = null;
2070
+ const closeLogSearchModal = (): void => { modalHandle?.dispose(); modalHandle = null; };
2071
+ const { cols: tc, rows: tr } = deps.termSize();
2072
+ const anchorRow = Math.max(1, deps.computePaneH(tr) + 2);
2073
+ const anchorCol = Math.max(2, Math.floor(tc / 4));
2074
+ const popup = createLogSearchModal({
2075
+ linesGetter: deps.linesGetter,
2076
+ initialQuery: deps.initialQuery(),
2077
+ termCols: tc,
2078
+ termRows: tr,
2079
+ anchorRow,
2080
+ anchorCol,
2081
+ theme: deps.currentThemeTokens(),
2082
+ onJump: (result, query) => {
2083
+ closeLogSearchModal();
2084
+ deps.onJump(result, query);
2085
+ },
2086
+ onCancel: query => {
2087
+ closeLogSearchModal();
2088
+ deps.onCancel(query);
2089
+ },
2090
+ });
2091
+ attachSurfaceToWorkspace(popup.surface, deps.currentWorkspaceOwnerId());
2092
+ modalHandle = deps.display().pushModal(popup.surface);
2093
+ deps.draw();
2094
+ };
2095
+ }
2096
+
2178
2097
  export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<DashboardAction> {
2179
2098
  const dashboardQuitConfirmationState = createQuitConfirmationState();
2180
2099
  const signalQuitConfirmationState = createQuitConfirmationState();
@@ -2196,7 +2115,6 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2196
2115
  forceQuit: true,
2197
2116
  });
2198
2117
  });
2199
- let virtualWindows!: DashboardVirtualWindows;
2200
2118
  // E3 (§7.4 TS baseline reduction · 2026-05-17) — forward declarations
2201
2119
  // for handler functions that get assigned later via destructure from
2202
2120
  // createDashboardCopyRuntime / createDashboardMediaRuntime (lines
@@ -2404,10 +2322,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2404
2322
  * same file isn't re-loading twice. Repeats inside the same call
2405
2323
  * (same id appearing twice in `added`) are also collapsed.
2406
2324
  *
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. */
2325
+ * Image attachments stay in the chat log without a scratch preview. */
2411
2326
  const renderAttachmentSummary = (added: { attachment: Attachment; isNew: boolean }[]): void => {
2412
2327
  renderDashboardAttachmentSummary(added, {
2413
2328
  formatNewLine: (attachment) =>
@@ -2419,16 +2334,6 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2419
2334
  return chatLines.length - 1;
2420
2335
  },
2421
2336
  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
2337
  });
2433
2338
  };
2434
2339
 
@@ -2572,7 +2477,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2572
2477
  });
2573
2478
  },
2574
2479
  foldMode: logFoldMode,
2575
- expandHint: dashboardUiMode === 'rich',
2480
+ expandHint: false,
2576
2481
  });
2577
2482
 
2578
2483
  // Session-scoped skill decline memory (session 21). When the user
@@ -2633,32 +2538,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2633
2538
  pushDebugLine(C.muted(` \u251c\u2500 `) + C.text(att.token) + ' ' + C.subtext('(already attached)'));
2634
2539
  }
2635
2540
  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
- }
2541
+ // The attachment summary confirms the paste without a scratch preview or modal.
2662
2542
  return `${att.token} `;
2663
2543
  } catch (err: any) {
2664
2544
  // grabClipboardImage now throws with the osascript diagnostic
@@ -2675,19 +2555,21 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2675
2555
  }
2676
2556
  };
2677
2557
 
2678
- const renderContextList = (): void => {
2558
+ const renderContextList = (): string[] => {
2679
2559
  const items = ctxList(contextRegistry);
2560
+ const lines: string[] = [];
2561
+ const output = (line: string): void => { pushDebugLine(line); lines.push(line); };
2680
2562
  pushDebugBlank();
2681
- pushDebugLine(C.accent('\u276f /context'));
2563
+ output(C.accent('\u276f /context'));
2682
2564
  if (!items.length) {
2683
- pushDebugLine(C.muted(' (empty)'));
2684
- return;
2565
+ output(C.muted(' (empty)'));
2566
+ return lines;
2685
2567
  }
2686
2568
  const totalBytes = ctxTotalBytes(contextRegistry);
2687
2569
  const total = fmtBytes(totalBytes);
2688
- pushDebugLine(C.muted(` ${items.length} items, ${total} total`));
2570
+ output(C.muted(` ${items.length} items, ${total} total`));
2689
2571
  if (totalBytes > CTX_WARN_BYTES) {
2690
- pushDebugLine(C.warning(` ⚠ ${total} exceeds 10MB — consider /context clear big`));
2572
+ output(C.warning(` ⚠ ${total} exceeds 10MB — consider /context clear big`));
2691
2573
  }
2692
2574
 
2693
2575
  // Table columns: ✓ id kind filename(trunc) size [source-tail]
@@ -2695,7 +2577,7 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2695
2577
  // left indent; filename gets the slack column so long paths just clip.
2696
2578
  const { cols } = termSize();
2697
2579
  const filenameMax = Math.max(18, cols - 36);
2698
- pushDebugLine(C.muted(
2580
+ output(C.muted(
2699
2581
  ` ${' '}${'id'.padEnd(4)}${'kind'.padEnd(7)}${'file'.padEnd(filenameMax)}${'size'.padStart(8)}`
2700
2582
  ));
2701
2583
  for (const a of items) {
@@ -2704,8 +2586,9 @@ export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<Da
2704
2586
  const kindCol = (KIND_DISPLAY[a.kind] ?? a.kind).padEnd(7);
2705
2587
  const file = ellipsize(a.filename, filenameMax).padEnd(filenameMax);
2706
2588
  const size = fmtBytes(a.sizeBytes).padStart(8);
2707
- pushDebugLine(` ${mark} ${C.muted(idCol)}${C.muted(kindCol)}${C.text(file)}${C.subtext(size)}`);
2589
+ output(` ${mark} ${C.muted(idCol)}${C.muted(kindCol)}${C.text(file)}${C.subtext(size)}`);
2708
2590
  }
2591
+ return lines;
2709
2592
  };
2710
2593
 
2711
2594
  const chat = createChatState(
@@ -2863,40 +2746,30 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2863
2746
  const target = logSearchResults[logSearchCursor]!;
2864
2747
  scrollToSearchResult(target);
2865
2748
  };
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
- };
2749
+ const openLogSearchModal = createDashboardLogSearchOpener({
2750
+ display: () => display,
2751
+ linesGetter: () => chatLines,
2752
+ initialQuery: () => logSearchQuery,
2753
+ termSize,
2754
+ computePaneH: rows => computePaneH(rows),
2755
+ currentThemeTokens: () => currentThemeTokens(),
2756
+ currentWorkspaceOwnerId: () => currentWorkspaceOwnerId(),
2757
+ draw: () => draw(),
2758
+ onJump: (result, query) => {
2759
+ applyLogSearch(query);
2760
+ // Position cursor on the picked result. applyLogSearch reset
2761
+ // it to 0; advance it to the result the user actually picked.
2762
+ const idx = logSearchResults.findIndex(r => r.lineIdx === result.lineIdx);
2763
+ logSearchCursor = idx >= 0 ? idx : 0;
2764
+ scrollToSearchResult(result);
2765
+ draw();
2766
+ },
2767
+ onCancel: query => {
2768
+ // Keep the query so n/N still works after cancel — tmux convention.
2769
+ if (query.trim()) applyLogSearch(query);
2770
+ draw();
2771
+ },
2772
+ });
2900
2773
 
2901
2774
  // TUI 부활 S-a — /resume 세션 픽커 (codex 패리티). chat 세션 스토어를
2902
2775
  // 필터형 모달로 띄우고, 선택 시 기존 `/session load <id>` slash 를
@@ -2920,7 +2793,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2920
2793
  draw();
2921
2794
  return;
2922
2795
  }
2923
- // C-d-1 — essential 은 composer 자리(하단 슬롯) · rich 는 중앙 플로팅.
2796
+ // Pickers share the composer bottom slot.
2924
2797
  const placement = resolveDecisionPickerPlacement({ height: 16 });
2925
2798
  const modalWidth = placement.width;
2926
2799
  const fmtRow = (m: (typeof rows)[number]): string => {
@@ -2979,12 +2852,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
2979
2852
  browserMode?: 'all' | 'files' | 'turns',
2980
2853
  ): Promise<boolean> => {
2981
2854
  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
2855
  const { getSourceDeltaManager, createSourceDeltaBrowserPopup } = await import('../code-edit/index.js');
2989
2856
  const turns = mode === 'latest'
2990
2857
  ? getSourceDeltaManager().recentTurns(1)
@@ -3284,7 +3151,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3284
3151
  })
3285
3152
  .reverse(); // 최신 턴 먼저
3286
3153
  const items: SearchItem[] = turnItems;
3287
- // C-d-1 — essential 은 composer 자리(하단 슬롯) · rich 는 중앙 플로팅.
3154
+ // Pickers share the composer bottom slot.
3288
3155
  const placement = resolveDecisionPickerPlacement({ height: 16 });
3289
3156
  const modalWidth = placement.width;
3290
3157
  const handle = createSearchModal({
@@ -3350,7 +3217,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3350
3217
  ...HITL_REVISE_PRESETS.map((p): SearchItem => ({ label: `${p.label} — 재분해`, payload: `preset:${p.action}` })),
3351
3218
  { label: '✏️ 직접입력 — 정정 코멘트 작성', payload: 'custom' },
3352
3219
  ];
3353
- // C-d-1 — essential 은 composer 자리(하단 슬롯) · rich 는 중앙 플로팅.
3220
+ // Pickers share the composer bottom slot.
3354
3221
  const placement = resolveDecisionPickerPlacement({ height: 14 });
3355
3222
  const modalWidth = placement.width;
3356
3223
  const handle = createSearchModal({
@@ -3681,33 +3548,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3681
3548
  let lastAssistantRaw: string | null = null;
3682
3549
  let lastAssistantRange: { start: number; end: number } | null = null;
3683
3550
  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.
3551
+ // Log-pane resize remains available when chatOnlyMode is toggled off.
3687
3552
  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
3553
  // Chat-only mode: 3-pane grid collapses, log pane owns all space above
3700
3554
  // the input. Toggled by /chat or /dashboard. Input loop stays alive
3701
3555
  // 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';
3556
+ // Essential layout starts chat-only; /chat and /dashboard still toggle it.
3557
+ let chatOnlyMode = true;
3711
3558
 
3712
3559
  // Debug log — auto-start forensic file capture so every run has a
3713
3560
  // tail-able JSONL trail without the user thinking about it. Default
@@ -3739,12 +3586,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3739
3586
  debug.setFileEnabled(dbgCfg.file);
3740
3587
  // OH9 — 렌더 로그 무음 시드(레벨과 직교). 우선순위:
3741
3588
  // level.json.render(명시) > config.debug.renderLogs(true=override) >
3742
- // uiMode essential(억제). essential 부팅이면 렌더 노이즈를 자동으로
3589
+ // essential 기본 억제. 부팅 시 렌더 노이즈를 자동으로
3743
3590
  // 끄고, 대표가 diag 로 올려도 진단은 그대로 흐른다.
3744
3591
  debug.setRenderSuppressed(resolveRenderSuppressed({
3745
3592
  scopedRender: readScopedRenderLogs(),
3746
3593
  configRenderLogs: dbgCfg.renderLogs,
3747
- uiModeEssential: dashboardUiMode === 'essential',
3594
+ defaultSuppressed: true,
3748
3595
  }));
3749
3596
  // Announce the path so the user knows where to tail. Prefixed
3750
3597
  // with (muted) so it doesn't compete with real chat output.
@@ -3771,9 +3618,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3771
3618
  debugScrollOffset = -1;
3772
3619
  mirrorDrawThrottle.request();
3773
3620
  });
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.
3621
+ // --debug launch flag: force both sinks ON and announce it in the log pane.
3777
3622
  if (opts.debug) {
3778
3623
  debug.setFileEnabled(true);
3779
3624
  debug.enable();
@@ -3837,14 +3682,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3837
3682
  // ── Plugin host (Phase 2) ──
3838
3683
  const hud = createHud();
3839
3684
 
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
3685
  // Reasoning HUD segment — provider-agnostic emoji + label that
3849
3686
  // sits in the top transient HUD row (next to voice / streaming
3850
3687
  // indicators), NOT the bottom status bar. Capability-driven: pill
@@ -3929,42 +3766,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3929
3766
  const toggleChatOnlyLayout = (opts: { announce?: boolean } = {}): void => {
3930
3767
  setChatOnlyLayout(!chatOnlyMode, opts);
3931
3768
  };
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
3769
  // Mirror chatOnly launch flag into the HUD pill (same style /chat
3969
3770
  // toggle uses) so the user sees "mode: chat" from the first frame.
3970
3771
  if (chatOnlyMode) setSegment(hud, 'mode', C.info('mode: chat'), 10);
@@ -4004,25 +3805,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4004
3805
  }
4005
3806
  : {};
4006
3807
  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
3808
  pruneConversationPopupHost();
4027
3809
  const conversationSnapshot = conversationPopupHost.snapshot();
4028
3810
  const metrics = getSessionMetrics();
@@ -4039,7 +3821,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4039
3821
  controller: process.env.ELANOUS_CONTROLLER?.trim() || undefined,
4040
3822
  shellCount: liveShells,
4041
3823
  shellRollup: latestShellRollup ?? undefined,
4042
- vw: vwSummary,
3824
+ vw: null,
4043
3825
  conversationPopups: {
4044
3826
  liveCount: conversationSnapshot.live.length,
4045
3827
  minimizedCount: conversationSnapshot.minimized.length,
@@ -4368,9 +4150,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4368
4150
  cols: termSize().cols,
4369
4151
  syntax: true,
4370
4152
  colorTier: diffRendering.colorTier,
4371
- adaptiveBg: diffRendering.adaptiveBg,
4372
- syntaxPerHunk: diffRendering.syntaxPerHunk,
4373
- cache: diffRendering.cache,
4374
4153
  headerStyle: diffRendering.headerStyle,
4375
4154
  })
4376
4155
  .then((rows) => {
@@ -4471,13 +4250,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4471
4250
  // tools) has one place to fan keys + clipboard across groups.
4472
4251
  // Phase T5 — terminal IPC channel bus. Fetched via getChannelBus()
4473
4252
  // 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
4253
  // P16 — mirror session metadata to
4482
4254
  // ~/.config/monad-agent/terminal-sessions.json on each lifecycle
4483
4255
  // change (debounced 250ms). /term resume later re-spawns from this.
@@ -4508,22 +4280,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4508
4280
  // dispatch so browser Ctrl+W / /wd retargets Bash immediately.
4509
4281
  setBashRuntimeDeps({});
4510
4282
 
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.
4283
+ // ShellRunner registry tracks handles; file capture runs inline/bg commands.
4519
4284
  {
4520
4285
  const { initShellRegistry } = await import('../shell-runner/registry.js');
4521
4286
  const { createFileCaptureEngine } = await import('../shell-runner/file-engine.js');
4522
4287
  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
4288
  const { createBackgroundSurface } = await import('../shell-runner/background-surface.js');
4526
- const { registerPaneContentKind } = await import('../virtual-windows/pane-content.js');
4527
4289
  // The boot seam types the registry as the opaque `ShellRegistryLike`
4528
4290
  // stub (decoupling), while the real `wireShellRunnerSurface` takes a
4529
4291
  // concrete `ShellRegistry`. At runtime `initShellRegistry` returns the
@@ -4538,161 +4300,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4538
4300
  deps: { shellRegistry: unknown },
4539
4301
  ) => void;
4540
4302
  } 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
4303
  bootDashboardShellRunner({
4638
4304
  createBackgroundSurface,
4639
4305
  initShellRegistry,
4640
4306
  wireShellRunnerSurface,
4641
- registerPaneContentKind,
4642
- createExternalTerminalPaneContent,
4643
- createRunnerHostFactory,
4644
4307
  createFileCaptureEngine,
4645
4308
  setShellRunnerDeps,
4646
- getSessionCwd: () => getSessionCwd(),
4647
- termSize: () => termSize(),
4648
4309
  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
4310
  });
4697
4311
  }
4698
4312
  const { setShellApprover } = await import('../shell-primitive/index.js');
@@ -4819,9 +4433,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4819
4433
  } catch { return []; }
4820
4434
  });
4821
4435
  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).
4436
+ // Phase C — wire context.* tools without virtual-window state.
4825
4437
  const { setContextRuntimeDeps } = await import('../tool-runtime/index.js');
4826
4438
  // Surface-unification v2.2 V2.2-5 (2026-05-11) — scheduler store
4827
4439
  // wire retired together with `context.jobs.list` LLM tool.
@@ -4924,103 +4536,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
4924
4536
  debug.log(scope, area, details);
4925
4537
  },
4926
4538
  });
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.
4539
+ // PaneVisualStateStore serves dashboard pane tools.
4934
4540
  const paneVisualStateStore = createVisualStateStore();
4935
4541
  let browserPaneRegistry!: BrowserPaneRegistry;
4936
4542
  let previewPaneRegistry!: PreviewPaneRegistry;
4937
4543
 
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.
4544
+ // Browser-pane help (`~`) uses the transient modal primitive.
5024
4545
  const browserHelpRuntime = createBrowserHelpRuntime({
5025
4546
  termSize,
5026
4547
  keyLabel: (key, desc) => ` ${C.key(pad(key, 14))} ${C.text(desc)}`,
@@ -5038,243 +4559,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5038
4559
  });
5039
4560
  },
5040
4561
  });
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.
4562
+ // Refresh context runtime deps after the dashboard session registry is ready.
5269
4563
  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.
4564
+ const { setContextRuntimeDeps } = await import('../tool-runtime/index.js');
5274
4565
  bootDashboardContextWindowRuntime({
5275
- setControlRuntimeDeps,
5276
4566
  setContextRuntimeDeps,
5277
- registry: virtualWindows.registry,
5278
4567
  // WD7 — context runtime cwd follows SWD.
5279
4568
  cwd: getSessionCwd(),
5280
4569
  getTerminalSessions: () => sessionRegistry.list().map((s) => ({
@@ -5463,7 +4752,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5463
4752
  wireDashboardSurfaceRegistry({
5464
4753
  surfaceRegistry,
5465
4754
  widgetHost,
5466
- windowRegistry: virtualWindows.registry,
5467
4755
  });
5468
4756
  } catch { /* never break boot */ }
5469
4757
 
@@ -5495,8 +4783,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5495
4783
  try {
5496
4784
  registerDashboardScenarioRuntimes({
5497
4785
  scenarioCatalog,
5498
- registry: virtualWindows.registry,
5499
- createPaneContent,
5500
4786
  spawnWidget: (spec) => widgetHost.spawn(spec),
5501
4787
  disposeWidget: (id) => { widgetHost.dispose(id); },
5502
4788
  getDashboardModals: () => dashboardModals,
@@ -5523,7 +4809,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5523
4809
  const artifactStore = createArtifactStore({
5524
4810
  legacyProviders: [
5525
4811
  createLegacyTimelineProvider({ dir: defaultTimelineBaseDir() }),
5526
- createLegacyLayoutProvider({ dir: layoutsDir() }),
5527
4812
  ],
5528
4813
  });
5529
4814
  try {
@@ -5960,19 +5245,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
5960
5245
  afterThemeActiveSaved: draw,
5961
5246
  });
5962
5247
 
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
5248
  // H5 P2 bootstrap — register the default channel router patterns
5977
5249
  // + codex-pty adapter hook, then bind the TTY snapshot LLM tools
5978
5250
  // to the live embodied-session registry. Without this wiring:
@@ -6127,15 +5399,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6127
5399
  findLiveSessionById,
6128
5400
  findLiveSessionByPaneId,
6129
5401
  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
- },
5402
+ getFocusedVirtualWindowPane: () => ({ vwId: null, paneId: null, paneKind: null }),
6139
5403
  noteUserSubmit: (sessionId, text) => globalAcpEventRouter().noteUserSubmit(sessionId, text),
6140
5404
  clientSessionSend: (opts) => globalDualRoleManager().clientSessionSend(opts),
6141
5405
  getWorkingFocus: () => workingDir.focus,
@@ -6200,25 +5464,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6200
5464
  '../capture/capture-tools.js'
6201
5465
  );
6202
5466
  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
- },
5467
+ resolveScreenshotTarget: () => null,
6222
5468
  dispatchScreenshot: async (args) => {
6223
5469
  const out = await pfcDispatchScreenshot(args);
6224
5470
  return {
@@ -6620,25 +5866,19 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6620
5866
  // 페인트를 멈춰 하단 영역의 단일 소유자를 보장(codex mod.rs:1674 · CC
6621
5867
  // REPL.tsx:4894 동형). 키는 기존 blocking-modal 라우팅 그대로 뷰가 소비.
6622
5868
  const bottomSlotModalActive = (): boolean => topBlockingForegroundModal()?.slot === 'bottom';
6623
- // 픽커류의 배치 결정 — essential 은 composer 자리(하단 슬롯), rich 는 기존
6624
- // 중앙 플로팅 유지(/ui 왕복 즉시 반영 — 호출 시점 게이트 패턴).
5869
+ // Pickers occupy the composer bottom slot.
6625
5870
  const resolveDecisionPickerPlacement = (desired: { height: number }): {
6626
5871
  bounds: { row: number; col: number; width: number; height: number };
6627
5872
  slot?: 'bottom';
6628
5873
  width: number;
6629
5874
  } => {
6630
5875
  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 };
5876
+ const bounds = resolveBottomSlotBounds({
5877
+ termCols: tc, termRows: tr,
5878
+ promptFrame: currentPromptFrame(tr),
5879
+ height: desired.height,
5880
+ });
5881
+ return { bounds, slot: 'bottom', width: bounds.width };
6642
5882
  };
6643
5883
  const topBottomAreaFreezeModal = (
6644
5884
  termRows = termSize().rows,
@@ -6688,7 +5928,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6688
5928
  }
6689
5929
  return policy.suppressPromptArea || policy.suppressStatusArea;
6690
5930
  };
6691
- const vwCompanionOwnerId = (windowId: string | number): string => `virtual-window:${windowId}`;
6692
5931
  const companionPopupSpec = (
6693
5932
  key: CompanionPopupKey,
6694
5933
  ): {
@@ -6931,189 +6170,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
6931
6170
  syncCompanionPopups();
6932
6171
  return next;
6933
6172
  };
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
6173
  const {
7118
6174
  syncConversationPopupModals,
7119
6175
  setConversationPopupLayoutMode,
@@ -7144,7 +6200,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7144
6200
  createAgentSessionProvider,
7145
6201
  createBrowserCdpProvider,
7146
6202
  registerProvider: (registry, provider) => { registry.registerProvider(provider); },
7147
- getWindows: () => virtualWindows.registry.list(),
6203
+ getWindows: () => [],
7148
6204
  listSessions: () => listLiveEmbodiedSessions(),
7149
6205
  // H6 P5 observer-registry landed · threading through enables
7150
6206
  // the channel-aware snapshot path (falls back to raw snapshot
@@ -7167,7 +6223,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7167
6223
 
7168
6224
  // ACP follow-up #1 — auto-persist on turn-end. DRM end_turn
7169
6225
  // transitions + BG terminal states flow into the H2 #5 persistence
7170
- // primitive automatically. Opt out with ELANOUS_ACP_PERSIST_MODE=off.
6226
+ // primitive automatically (always on — off-switch graduated 2026-09-26).
7171
6227
  wireAutoPersist({
7172
6228
  drm: globalDualRoleManager(),
7173
6229
  bg: globalBackgroundManager(),
@@ -7178,17 +6234,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7178
6234
  // the in-memory BackgroundManager bounded for long-running elanous
7179
6235
  // processes. Auto-persist (#1) already flushed the terminal record
7180
6236
  // 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 ·
6237
+ // Always on (ELANOUS_ACP_BG_SWEEP_MODE off-switch graduated 2026-09-26). Defaults: 24h TTL ·
7182
6238
  // 1h tick (matches Warp cloud-agent "stale after a day" heuristic).
7183
6239
  bootDashboardAcpBgSweep({
7184
- enabled: process.env['ELANOUS_ACP_BG_SWEEP_MODE'] !== 'off',
6240
+ enabled: true,
7185
6241
  backgroundManager: globalBackgroundManager(),
7186
6242
  });
7187
6243
 
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
6244
  // T6-K5 — wire widget + pane control tools. Widgets are reported
7193
6245
  // from widgetHost; pane focus piggybacks on workingDir.focus. The
7194
6246
  // pane name list mirrors the PaneFocus type so the LLM sees valid
@@ -7268,32 +6320,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
7268
6320
  openSurfaceCatalogPopup();
7269
6321
  return true;
7270
6322
  },
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
- },
6323
+ openSurfaceInVw: () => false,
7290
6324
  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
- }
6325
+ if (target === 'vw') return false;
7297
6326
  setCompanionPopupOpen(surface, true);
7298
6327
  return true;
7299
6328
  },
@@ -8096,131 +7125,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
8096
7125
  const activeBrowserState = (): BrowserPaneModel => (
8097
7126
  resolveBrowserStateById(activeBrowserWidgetId()) ?? (workingDir as BrowserPaneModel)
8098
7127
  );
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
7128
  // T7-N1 — ACP chat bridge. Speaks JSON-RPC to claude-code-acp /
8225
7129
  // codex-acp / gemini via the real client in src/acp/*. Subprocess
8226
7130
  // spawn is lazy — no cost until the user runs /acp.
@@ -8255,7 +7159,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
8255
7159
  // Scratch visibility (Phase S5). When closed, the workingDir
8256
7160
  // layouts drop the scratch cell so the remaining panes expand
8257
7161
  // to fill the reclaimed columns — useful on narrow terminals.
8258
- // Reopen via the /scratch open slash or the ^B ^S chord.
7162
+ // Reopen via the ^B ^S chord.
8259
7163
  let scratchClosed = false;
8260
7164
  const dashboardViewRegistryOptions = () => ({
8261
7165
  extraPanes: pluginHost.activeDashboardPanes().map(p => p.paneId),
@@ -8322,7 +7226,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
8322
7226
  // chrome rows (input/status/dock), but that does not mean the
8323
7227
  // main workspace's chat composer remains an active input owner.
8324
7228
  const chatMainAvailable = foregroundWorkspace === null;
8325
- const vwLocalComposerActive = !!virtualWindows.registry.current()?.isLocalComposerActive();
7229
+ const vwLocalComposerActive = false;
8326
7230
  const overlayInputActive = hasOverlayInputOwner({
8327
7231
  focusStack: display.modalStack(),
8328
7232
  surfaceAt: (id) => display.surface(id),
@@ -9579,13 +8483,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
9579
8483
  agentStatusStore.clear(ev.instance.id);
9580
8484
  claudeStatusParser.reset(ev.instance.id);
9581
8485
  codexStatusParser.reset(ev.instance.id);
9582
- closeVwPaneForExit(ev.instance.placement, 'exited');
9583
8486
  } else if (ev.type === 'killed') {
9584
8487
  notificationStore.push(exitToNotification(ev.instance.id, null));
9585
8488
  agentStatusStore.clear(ev.instance.id);
9586
8489
  claudeStatusParser.reset(ev.instance.id);
9587
8490
  codexStatusParser.reset(ev.instance.id);
9588
- closeVwPaneForExit(ev.instance.placement, 'killed');
9589
8491
  } else if (ev.type === 'attention') {
9590
8492
  notificationStore.push(attentionToNotification(ev.instance.id, ev.level));
9591
8493
  }
@@ -9620,27 +8522,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
9620
8522
  try { debug.log('agent-status.mirror.activate-failed', String(err), { level: 'error' }); } catch { /* swallow */ }
9621
8523
  }
9622
8524
  })();
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
8525
  const disposeConversationWidgetLiveBridge = createConversationWidgetLiveBridge({
9645
8526
  widgetHost,
9646
8527
  listSessions: () => listLiveEmbodiedSessions().map((entry) => ({
@@ -10888,24 +9769,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
10888
9769
  });
10889
9770
  }
10890
9771
  }
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
9772
  const clipboardWidget = widgetHost.get('wd-clipboard') as any;
10910
9773
  companionWidgetRuntime.projectClipboardWidget(
10911
9774
  clipboardWidget,
@@ -11726,18 +10589,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11726
10589
  getScratchTotalBytes: () => scratchLines.reduce((n, l) => n + l.length, 0),
11727
10590
  isCompanionOpen: (key) => companionPopupHost.isActive(key as CompanionPopupKey),
11728
10591
  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
10592
  getDebugPath: () => debug.path(),
11742
10593
  getDebugLevel: () => debug.level(),
11743
10594
  });
@@ -11915,103 +10766,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
11915
10766
  });
11916
10767
  return;
11917
10768
  }
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
10769
  case 'dashboard-pane.open-popup': {
11994
10770
  const payload = result.payload as DashboardPaneTitleMenuPayload | undefined;
11995
10771
  if (!payload) return;
11996
10772
  openDashboardPaneModal(payload.pane);
11997
10773
  return;
11998
10774
  }
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
10775
  case 'dashboard-pane.clipboard-companion': {
12016
10776
  const opened = toggleCompanionPopup('clipboard');
12017
10777
  contextMenuFeedbackRuntime.onDashboardCompanionToggled('clipboard', opened);
@@ -12071,13 +10831,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12071
10831
  openBrowserPreviewModal: () => openBrowserPreviewModal(),
12072
10832
  openDashboardPaneModal: (pane) => openDashboardPaneModal(pane),
12073
10833
  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
10834
  onWarning: (message) => {
12082
10835
  pushDebugLine(C.warning(` ${message}`));
12083
10836
  chatScrollOffset = -1;
@@ -12131,20 +10884,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12131
10884
  pushChatLine: (line) => { chatLines.push(line); },
12132
10885
  setChatScrollBottom: () => { chatScrollOffset = -1; },
12133
10886
  });
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
10887
  const termSlashRuntime = createDashboardTermSlashRuntime({
12149
10888
  accent: C.accent,
12150
10889
  muted: C.muted,
@@ -12182,36 +10921,27 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12182
10921
  muted: C.muted,
12183
10922
  warning: C.warning,
12184
10923
  });
12185
- const scratchSlashRuntime = createDashboardScratchSlashRuntime({
10924
+ const companionSlashRuntime = createDashboardCompanionSlashRuntime({
12186
10925
  muted: C.muted,
12187
10926
  warning: C.warning,
12188
10927
  });
12189
- const companionSlashRuntime = createDashboardCompanionSlashRuntime({
10928
+ const agentsSlashRuntime = createDashboardAgentsSlashRuntime({
12190
10929
  muted: C.muted,
12191
10930
  warning: C.warning,
12192
10931
  });
12193
- const agentsSlashRuntime = createDashboardAgentsSlashRuntime({
10932
+ const controlSignalSlashRuntime = createDashboardControlSignalSlashRuntime({
10933
+ accent: C.accent,
12194
10934
  muted: C.muted,
12195
10935
  warning: C.warning,
10936
+ observer: defaultControlSignalObserver(),
10937
+ signalBus: dashboardControlSignals,
12196
10938
  });
12197
- const viewSlashRuntime = createDashboardViewSlashRuntime({
10939
+ const browserCdpSlashRuntime = createDashboardBrowserCdpSlashRuntime({
10940
+ accent: C.accent,
12198
10941
  muted: C.muted,
12199
- success: C.success,
12200
10942
  warning: C.warning,
10943
+ signalBus: dashboardControlSignals,
12201
10944
  });
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
10945
  const simSlashRuntime = createDashboardSimSlashRuntime({
12216
10946
  accent: C.accent,
12217
10947
  muted: C.muted,
@@ -12252,27 +10982,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12252
10982
  muted: C.muted,
12253
10983
  warning: C.warning,
12254
10984
  });
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
10985
  const mousePickerRuntime = createMousePickerRuntime({
12277
10986
  getRotation: () => getUserConfig().llm.rotation ?? [],
12278
10987
  getCurrentModelEntry: () => {
@@ -12283,343 +10992,58 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12283
10992
  ...(llm.model !== undefined ? { model: llm.model } : {}),
12284
10993
  };
12285
10994
  },
12286
- applyActiveModel: (entry) => {
12287
- const nextCfg = applyRotationEntry(getUserConfig(), entry);
12288
- saveUserConfig(nextCfg);
12289
- reloadUserConfig();
12290
- },
12291
- getRecentWds: () => [...wdHistory],
12292
- 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
- }
12622
- },
10995
+ applyActiveModel: (entry) => {
10996
+ const nextCfg = applyRotationEntry(getUserConfig(), entry);
10997
+ saveUserConfig(nextCfg);
10998
+ reloadUserConfig();
10999
+ },
11000
+ getRecentWds: () => [...wdHistory],
11001
+ applySessionWd: (path) => {
11002
+ setSessionCwd(path, 'user');
11003
+ rememberWd(path);
11004
+ },
11005
+ reportModelSwitchError: (message) => {
11006
+ chatLines.push(C.error(` model switch failed: ${message}`));
11007
+ chatScrollOffset = -1;
11008
+ },
11009
+ reportWdSwitchError: (message) => {
11010
+ chatLines.push(C.error(` cwd switch failed: ${message}`));
11011
+ chatScrollOffset = -1;
11012
+ },
11013
+ });
11014
+ const mouseShellRollupRuntime = createMouseShellRollupRuntime({
11015
+ resolveVwIdByLabel: () => null,
11016
+ switchToVirtualWindow: () => { throw new Error('Virtual windows are unavailable in the essential dashboard'); },
11017
+ onWarning: (message) => {
11018
+ chatLines.push(C.warning(` ${message}`));
11019
+ chatScrollOffset = -1;
11020
+ },
11021
+ });
11022
+ const mouseConversationPopupRuntime = createMouseConversationPopupRuntime({
11023
+ conversationPopupHost,
11024
+ ensureConversationWidgetMounted,
11025
+ syncConversationPopupModals,
11026
+ redraw: () => { draw(); },
11027
+ });
11028
+ const mouseModeSwitchRuntime = createMouseModeSwitchRuntime({
11029
+ getActiveMode: () => inputCoreActiveMode(),
11030
+ getModeAction: (actionId) => getInputCoreAction(actionId),
11031
+ setMode: (next) => inputCoreSetMode(next),
11032
+ });
11033
+ const mouseModalHitRuntime = createMouseModalHitRuntime({
11034
+ getTopSurface: () => {
11035
+ const stack = display.modalStack();
11036
+ const topId = stack[stack.length - 1];
11037
+ if (!topId) return null;
11038
+ return display.surface(topId) as import('../display/modal-stack.js').ModalSurface | null;
11039
+ },
11040
+ });
11041
+ const mouseModalSurfaceRuntime = createMouseModalSurfaceRuntime({
11042
+ getFocusStack: () => display.modalStack(),
11043
+ surfaceAt: (id) =>
11044
+ display.surface(id) as import('../display/modal-stack.js').ModalSurface | null | undefined,
11045
+ getTopBlockingModalSurface: topBlockingForegroundModal,
11046
+ routeModalMouse: (surface, ev) => display.routeMouseToSurface(surface, ev),
12623
11047
  });
12624
11048
  const mousePaneHitRuntime = createMousePaneHitRuntime({
12625
11049
  getPaneNavRow: () => paneNavRow,
@@ -12649,29 +11073,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12649
11073
 
12650
11074
  const mouseWiring = createDashboardMouseWiring({
12651
11075
  termSize: () => termSize(),
12652
- // TUI 부활 T4 — hover 팝업(툴팁 자동 표시)은 rich UI 전용.
12653
- // /ui 런타임 전환을 따르도록 predicate 로 배선.
12654
- hoverPopupsEnabled: () => isDashboardHeavyFeatureEnabled(dashboardUiMode, 'hover-popups'),
11076
+ hoverPopupsEnabled: () => false,
12655
11077
  getRotation: mousePickerRuntime.getRotation,
12656
11078
  getCurrentModelEntry: mousePickerRuntime.getCurrentModelEntry,
12657
11079
  setActiveModel: mousePickerRuntime.setActiveModel,
12658
11080
  getRecentWds: mousePickerRuntime.getRecentWds,
12659
11081
  setSessionWd: mousePickerRuntime.setSessionWd,
12660
- getDockMenuWindowTargets: mouseDockRuntime.getDockMenuWindowTargets,
12661
- onOpenDockWindow: mouseDockRuntime.onOpenDockWindow,
12662
- getDockMenuSurfaceTargets: mouseDockRuntime.getDockMenuSurfaceTargets,
12663
- onOpenDockSurface: mouseDockRuntime.onOpenDockSurface,
12664
- onToggleChatOnly: mouseDockRuntime.onToggleChatOnly,
12665
11082
  onExitProgram: async () => {
12666
11083
  exitDashboardTui();
12667
11084
  setTimeout(() => process.exit(0), 0);
12668
11085
  },
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
11086
  pushModalSurface: surface => display.pushModal(surface),
12676
11087
  // DS-2b (2026-04-21) — drag-session routing hook. Adapter lives
12677
11088
  // in src/display/drag-dispatch.ts (PR #319); this single line is
@@ -12680,10 +11091,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12680
11091
  // the existing chain (0 overhead path). See PLAN-drag-session-
12681
11092
  // primitive.md §3.3 and PR #319 for the contract.
12682
11093
  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
11094
+ // CMX-2 (2026-04-22) — non-pill right-click dispatch. This hook
11095
+ // serves pane-body / pane-title / input / VW right-clicks via
11096
+ // MenuProviderRegistry. Unconsumed
12687
11097
  // (no provider matches) falls through to modal forwarding.
12688
11098
  contextMenuDispatch: (ev) => ctxMenuWire.onMouse(ev),
12689
11099
  // DS-3a (2026-04-21) — chat input hit classifier. Routes clicks
@@ -12703,18 +11113,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12703
11113
  updateModalBounds: (id, bounds) => display.updateModalBounds(id, bounds),
12704
11114
  redraw: () => { draw(); },
12705
11115
  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
11116
  conversationPopupHost,
12716
11117
  onConversationPopupPick: mouseConversationPopupRuntime.onConversationPopupPick,
12717
- onWindowPillClick: mouseModalSurfaceRuntime.onWindowPillClick,
12718
11118
  // SRF-4 — shell rollup pill click → popup listing live handles.
12719
11119
  // Handles are fetched at click time so the popup always reflects
12720
11120
  // the current ShellRegistry state (not a frozen snapshot).
@@ -12742,13 +11142,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12742
11142
  getTheme: () => currentThemeTokens(),
12743
11143
  suppressHoverHints: true,
12744
11144
  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
11145
  // IDX-5 Phase 3 B-4 — classify clicks outside the status-bar pill
12753
11146
  // row into broader pane regions so lastClickHitKind can carry
12754
11147
  // 'pane-nav' / 'pane-title' / 'pane-body' values (previously only
@@ -12781,14 +11174,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12781
11174
  getTheme: () => currentThemeTokens(),
12782
11175
  });
12783
11176
 
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.
11177
+ // F6 / S1.D — display-control runtime registration needs late-boot mouse/context-menu state.
12788
11178
  try {
12789
11179
  registerDashboardLayoutDisplayRuntimes({
12790
- windowRegistry: virtualWindows.registry,
12791
- artifactStore,
12792
11180
  coordinator: display,
12793
11181
  mouseDispatch: (ev) => mouseWiring.handleMouse(ev),
12794
11182
  menuProviderRegistry: ctxMenuProviders,
@@ -12815,16 +11203,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12815
11203
  // values are cheap no-ops (one field comparison per key).
12816
11204
  publishFocusContextKeysFromService(workingDir.focus);
12817
11205
 
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
11206
 
12829
11207
  // T3-B1: agent-activity HUD segment. Pulsing glyph + running
12830
11208
  // count drives awareness that sub-agent work is in flight —
@@ -12962,9 +11340,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
12962
11340
  }
12963
11341
  const suppressPromptArea = hostChromePolicy.suppressPromptArea;
12964
11342
  const suppressStatusArea = hostChromePolicy.suppressStatusArea;
12965
- const suppressHudArea = hostChromePolicy.suppressHudArea;
12966
11343
  const suppressDashboardBackground = hostChromePolicy.suppressDashboardBackground;
12967
- const suppressDockArea = hostChromePolicy.suppressDockArea;
12968
11344
  if (suppressPromptArea) display.setCursor(null);
12969
11345
  // ── Zone list (top-to-bottom) ──
12970
11346
  // Replaces the old inline `termRows - N` arithmetic and hand-rolled
@@ -13034,18 +11410,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13034
11410
  });
13035
11411
  }
13036
11412
 
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
- }
11413
+ // Essential chrome uses only the status line, not a HUD row.
13049
11414
 
13050
11415
  // Input block.
13051
11416
  let promptCaret: CursorState | null = null;
@@ -13075,24 +11440,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13075
11440
  render: (_h, c) => [suppressPromptArea ? ' '.repeat(c) : hLine(c)],
13076
11441
  });
13077
11442
 
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) 경로로 자연 비활성.
11443
+ // Essential bottom area: status line only; no dock row.
13082
11444
  zones.push({ id: 'status-gap-top', height: 1, render: () => [''] });
13083
11445
  zones.push({
13084
11446
  id: 'status',
13085
11447
  height: 1,
13086
11448
  render: () => [suppressStatusArea ? '' : buildStatusLine()],
13087
11449
  });
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
11450
 
13097
11451
  // Compose.
13098
11452
  const composed = composeVertical(zones, { rows: termRows, cols: totalW });
@@ -13151,11 +11505,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13151
11505
  // is one of the easiest ways to see a flash under tmux.
13152
11506
  let modalFrame = '';
13153
11507
  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
11508
  if (effectiveLayout && effectiveLayout.modals.length > 0) {
13160
11509
  const m = effectiveLayout.modals[0]!;
13161
11510
  modalKey = `${m.id}:${m.widgetInstanceId}:${JSON.stringify(m.position)}:${m.size?.width ?? ''}x${m.size?.height ?? ''}`;
@@ -13163,15 +11512,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13163
11512
  termRows, termCols: cols, theme: currentThemeTokens(),
13164
11513
  });
13165
11514
  }
13166
- if (foregroundWorkspaceFrame.length > 0) {
13167
- modalFrame += foregroundWorkspaceFrame;
13168
- }
13169
11515
  if (debug.isKeyTraceEnabled() && blockingForegroundModal) {
13170
11516
  debug.log('dashboard.frame-compose', 'blocking-modal', {
13171
11517
  modalId: blockingForegroundModal.id,
13172
11518
  baseLineCount: baseLines.length,
13173
11519
  overlayLen: modalFrame.length,
13174
- hasForegroundWorkspaceFrame: foregroundWorkspaceFrame.length > 0,
11520
+ hasForegroundWorkspaceFrame: false,
13175
11521
  });
13176
11522
  }
13177
11523
  try {
@@ -13183,15 +11529,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
13183
11529
  // propagated request.force (e.g. popModal / closeSurface on a
13184
11530
  // coordinator-owned modal). Before V3 only the layout-modal signal
13185
11531
  // was honored, so coordinator modal closes left residual pixels.
13186
- // ⭐ 단일 flush 지점 — essential 은 프롬프트 caret 을 스스로 정하고, rich 는 종전 그대로다.
11532
+ // ⭐ Single flush: resolve the prompt caret after overlays.
13187
11533
  const frameCursorDecision = display.cursorDecision();
13188
11534
  renderDashboardFrame(baseLines, composeDashboardFrameWithCoordinatorModal(display, {
13189
11535
  overlay: modalFrame,
13190
11536
  force: forceFromCoordinator || modalKey !== lastModalKey,
13191
- essential: dashboardUiMode === 'essential',
13192
11537
  cursorOwner: frameCursorDecision.owner,
13193
11538
  claimedCursor: frameCursorDecision.cursor,
13194
- // ⛔ rich 는 #6528 까지의 값을 그대로 쓴다 — 이 PR 은 essential 축만 바꾼다.
13195
11539
  coordinatorCursor: display.getCursor(),
13196
11540
  promptCaret,
13197
11541
  suppressPromptArea,
@@ -14109,42 +12453,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14109
12453
  when: () => agentSearchModal === null,
14110
12454
  });
14111
12455
 
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
12456
  let debugWorkbenchHandle: PaneMultiModalHandle | null = null;
14149
12457
  let debugWindowHandle: PaneMultiModalHandle | null = null;
14150
12458
  let debugWindowHoverActive = false;
@@ -14469,14 +12777,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14469
12777
  onContextMenuMouse: (ev) => ctxMenuWire.onMouse(ev),
14470
12778
  });
14471
12779
  };
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
12780
  const openBrowserOnlyModal = (): void => {
14481
12781
  const { cols: tc, rows: tr } = termSize();
14482
12782
  const liveMode = resolveBrowserPreviewModalLiveMode(process.env.ELANOUS_PANE_MODAL_LIVE);
@@ -14497,14 +12797,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14497
12797
  onCancel: () => { draw(); },
14498
12798
  });
14499
12799
  };
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
12800
  const openPreviewOnlyModal = (): void => {
14509
12801
  const { cols: tc, rows: tr } = termSize();
14510
12802
  const liveMode = (process.env.ELANOUS_PANE_MODAL_LIVE ?? 'on').toLowerCase() !== 'off';
@@ -14522,237 +12814,22 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14522
12814
  onCancel: () => { draw(); },
14523
12815
  });
14524
12816
  };
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
12817
  registerDashboardGlobalKeys({
14565
12818
  enableSupplementalGlobalKeys: getUserConfig().dashboard.enableSupplementalGlobalKeys,
14566
12819
  register: (binding) => { display.registerKeyBinding(binding); },
14567
12820
  openSurfaceCatalog: () => openSurfaceCatalogPopup(),
14568
12821
  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
12822
  });
14705
12823
 
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
12824
  function currentWorkspaceOwnerId(): string {
14742
- const currentWindow = virtualWindows.registry.current();
14743
- return currentWindow
14744
- ? workspaceOwnerIdForVirtualWindow(currentWindow.id)
14745
- : DASHBOARD_MAIN_WORKSPACE_ID;
12825
+ return DASHBOARD_MAIN_WORKSPACE_ID;
14746
12826
  }
14747
12827
 
14748
12828
  function openSurfaceCatalogPopup(): void {
14749
- const currentWindow = virtualWindows.registry.current();
14750
12829
  void openCompactSurfaceCatalogPopup({
14751
12830
  getTargets: () => compactSurfaceRuntime.buildTargets(),
14752
12831
  openTarget: (surfaceId) => compactSurfaceRuntime.openTarget(surfaceId),
14753
- ownerWorkspaceId: currentWindow
14754
- ? workspaceOwnerIdForVirtualWindow(currentWindow.id)
14755
- : DASHBOARD_MAIN_WORKSPACE_ID,
12832
+ ownerWorkspaceId: DASHBOARD_MAIN_WORKSPACE_ID,
14756
12833
  termSize,
14757
12834
  getTheme: () => currentThemeTokens(),
14758
12835
  pushModalSurface: (surface) => display.pushModal(surface),
@@ -14773,24 +12850,15 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14773
12850
  let shellRollupDispose: (() => void) | null = null;
14774
12851
  async function openShellRollupPopup(): Promise<void> {
14775
12852
  const { getShellRegistry } = await import('../shell-runner/registry.js');
14776
- const currentWindow = virtualWindows.registry.current();
14777
12853
  await openDashboardShellRollupPopup({
14778
12854
  getShellRegistry,
14779
12855
  getCurrentDispose: () => shellRollupDispose,
14780
12856
  setCurrentDispose: (dispose) => {
14781
12857
  shellRollupDispose = dispose;
14782
12858
  },
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,
12859
+ resolveVwIdByLabel: () => null,
12860
+ switchVirtualWindow: () => { throw new Error('Virtual windows are unavailable in the essential dashboard'); },
12861
+ ownerWorkspaceId: DASHBOARD_MAIN_WORKSPACE_ID,
14794
12862
  termSize,
14795
12863
  getTheme: () => currentThemeTokens(),
14796
12864
  pushModalSurface: (surface) => display.pushModal(surface),
@@ -14806,108 +12874,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
14806
12874
  });
14807
12875
  }
14808
12876
 
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
12877
  // Track which task ID the cursor is currently LOCKED onto across
14912
12878
  // draws — prevents the cursor from visually sliding when the roster
14913
12879
  // gets re-sorted with a new entry. Empty string = no lock.
@@ -15041,11 +13007,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15041
13007
  return memoWidgetRuntime.readLines(widgetHost.get('wd-memo') as any);
15042
13008
  };
15043
13009
  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);
13010
+ scratchTitle = 'Memo';
13011
+ scratchLines = [...lines];
13012
+ scratchOffset = 0;
13013
+ publishScratchSurface('dashboard:memo', 'preview', scratchTitle, scratchLines);
15049
13014
  };
15050
13015
  const memoCompanionRuntime = createDashboardMemoCompanionRuntime({
15051
13016
  readLines: readMemoWidgetLines,
@@ -15064,26 +13029,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15064
13029
  memoCompanionRuntime.cancel();
15065
13030
  chatScrollOffset = -1;
15066
13031
  };
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
13032
  // Repaint hook the active textInput populates so external draws
15088
13033
  // (scratchpad refresh, async chafa render finishing) can ask the
15089
13034
  // prompt to re-paint itself afterwards. While input is idle this
@@ -15135,8 +13080,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15135
13080
  requestRender: () => { requestDashboardRender('detail'); },
15136
13081
  });
15137
13082
  const companionWidgetRuntime = createDashboardCompanionWidgetRuntime();
15138
- const scratchWidgetRuntime = createDashboardScratchWidgetRuntime();
15139
- const scratchStateRuntime = createDashboardScratchStateRuntime();
15140
13083
  const clipboardHistoryRuntime = createDashboardClipboardHistoryRuntime();
15141
13084
  const memoWidgetRuntime = createDashboardMemoWidgetRuntime();
15142
13085
  const logWidgetRuntime = createDashboardLogWidgetRuntime();
@@ -15479,26 +13422,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15479
13422
  * display coordinator. The coordinator coalesces this with any
15480
13423
  * plugin/widget updates and repaints the prompt after the frame. */
15481
13424
  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;
13425
+ scratchTitle = title;
13426
+ scratchLines = [...lines];
13427
+ scratchOffset = 0;
15486
13428
  publishScratchSurface('dashboard:scratch', 'preview', title, scratchLines);
15487
13429
  };
15488
13430
 
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
13431
  // ── Execute sync inline (output to log pane) ──
15503
13432
  const runSyncInline = async (
15504
13433
  sk: string[] = [...sync.selected[0]!],
@@ -17314,8 +15243,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17314
15243
  }
17315
15244
 
17316
15245
  // 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.
15246
+ // key earlier) closes the scratch pane. Reopen via the ^B ^S chord.
17319
15247
  if (key.ctrl && (key.name === 'w' || key.name === 'ㅈ')) {
17320
15248
  scratchClosed = true;
17321
15249
  userClosedPanes.add('scratch');
@@ -17416,7 +15344,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17416
15344
  acpPtyAvailable = (await import('../pty-shell/registry.js')).ptyAvailable;
17417
15345
  const dashboardAcpBootResult = await bootDashboardAcpSession({
17418
15346
  ...(opts.remote ? { remote: opts.remote } : {}),
17419
- ...(opts.localDaemon ? { localDaemon: opts.localDaemon } : {}),
17420
15347
  ...(opts.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {}),
17421
15348
  getCwd: () => getSessionCwd(),
17422
15349
  getChatHistory: () => chat.history,
@@ -17437,7 +15364,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17437
15364
  pluginTools: pluginHost.activePluginLLMTools(),
17438
15365
  optionalTools: acpTurnRef.optionalSpecs,
17439
15366
  surface: acpTurnRef.turnProfile?.surface,
17440
- rich: dashboardUiMode === 'rich',
15367
+ rich: false,
17441
15368
  })
17442
15369
  : [];
17443
15370
  return buildDashboardTurnPreamble({
@@ -17451,7 +15378,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17451
15378
  // codex-only behavioral addendum (fix L-1) inside the
17452
15379
  // universal preamble.
17453
15380
  modelFamily: getModelFamily(inspectActiveProvider().model),
17454
- rich: dashboardUiMode === 'rich',
15381
+ rich: false,
17455
15382
  enabledTools: tools.map(t => t.name),
17456
15383
  });
17457
15384
  },
@@ -17465,7 +15392,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17465
15392
  pluginTools: pluginHost.activePluginLLMTools(),
17466
15393
  optionalTools: acpTurnRef.optionalSpecs,
17467
15394
  surface: acpTurnRef.turnProfile?.surface,
17468
- rich: dashboardUiMode === 'rich',
15395
+ rich: false,
17469
15396
  });
17470
15397
  // ⭐ Remember this turn's catalog so a spawned sub-agent can inherit it.
17471
15398
  //
@@ -17630,18 +15557,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17630
15557
  try { acpSessionIdRef.current = dashboardAcpSession.currentSessionId ?? null; } catch { /* 없으면 넣지 않는다 */ }
17631
15558
  pushDebugLine(C.muted('[acp-boot] dashboard ACP session ready'));
17632
15559
  // 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
15560
+ // ELANOUS_REMOTE has no way to tell whether the
15561
+ // attach actually succeeded — the attached daemon mode is useful
17635
15562
  // chat transcript context, but the local in-process fallback is
17636
15563
  // runtime plumbing and belongs in the debug log instead.
17637
15564
  if (opts.remote) {
17638
15565
  const authNote = opts.remote.token ? 'token' : 'no-auth';
17639
15566
  const labelNote = opts.remote.label ? ` · ${opts.remote.label}` : '';
17640
15567
  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
15568
  } else {
17644
- pushDebugLine(C.muted(' in-process ACP (no daemon attach · set ELANOUS_REMOTE or ELANOUS_USE_DAEMON to attach)'));
15569
+ pushDebugLine(C.muted(' in-process ACP (no daemon attach · set ELANOUS_REMOTE to attach)'));
17645
15570
  }
17646
15571
  // Tier 1 daemon-resume status. Three branches:
17647
15572
  // - resumed: attachExisting succeeded · the daemon's prior
@@ -17655,9 +15580,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17655
15580
  if (dashboardAcpBootResult.mode === 'resumed' && dashboardAcpBootResult.sessionId) {
17656
15581
  const sid = dashboardAcpBootResult.sessionId;
17657
15582
  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.
15583
+ // Best-effort REST history replay over the remote HTTP/WS endpoint.
17661
15584
  if (opts.remote) {
17662
15585
  const httpBase = deriveDaemonHttpBase(opts.remote.url);
17663
15586
  if (httpBase) {
@@ -17693,8 +15616,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17693
15616
  chatLines.push(C.muted(` no prior turns found for ${sid}.`));
17694
15617
  }
17695
15618
  }
17696
- } else {
17697
- chatLines.push(C.muted(' (history replay over unix socket is a follow-up — REST endpoint required)'));
17698
15619
  }
17699
15620
  } else if (dashboardAcpBootResult.mode === 'new' && dashboardAcpBootResult.resumeFallbackReason) {
17700
15621
  // The user asked to resume but the daemon rejected the id —
@@ -17704,7 +15625,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17704
15625
  const reason = dashboardAcpBootResult.resumeFallbackReason;
17705
15626
  const newId = dashboardAcpBootResult.sessionId ?? '(unknown)';
17706
15627
  chatLines.push(C.warning(` ⚠ resume failed (${reason}); started new session: ${newId}`));
17707
- } else if (opts.resumeSessionId && !opts.remote && !opts.localDaemon) {
15628
+ } else if (opts.resumeSessionId && !opts.remote) {
17708
15629
  // Match `/session load <prefix>` for in-process boot: resolve the
17709
15630
  // local prefix, preserve the stock system prompt, and attach the
17710
15631
  // restored session for subsequent persistence.
@@ -17746,6 +15667,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17746
15667
  const key: import('../tui.js').Key = wdAutoInput
17747
15668
  ? { name: CHAT_MAIN_AUTO_ENTRY_KEY_NAME, ctrl: false, shift: false }
17748
15669
  : await readKey();
15670
+ const helpModal = currentTransientTerminalModal('dashboard-help');
15671
+ if (key.name === 'escape' && helpModal) {
15672
+ helpModal.dispose();
15673
+ draw();
15674
+ continue;
15675
+ }
17749
15676
 
17750
15677
  // U-0 · recompute ViewMode at the top of every key dispatch
17751
15678
  // loop iteration. This is the one natural "mode re-evaluation"
@@ -17823,13 +15750,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17823
15750
  && voiceHostFlow.getState().kind === 'idle'
17824
15751
  && workingDir.focus !== 'input'
17825
15752
  ) {
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
- })();
15753
+ const focusedTerminalPane = false;
17833
15754
 
17834
15755
  // Branch 1 — Ctrl+Shift+D trial chord. TEMP DEBUG (2026-04-29):
17835
15756
  // toggle pattern instead of long-press detector. First press
@@ -18069,23 +15990,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18069
15990
  if (result === 'closed') draw();
18070
15991
  return true;
18071
15992
  },
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
15993
  routeArmedChordKey: async (routeKey) => {
18090
15994
  if (!isChordArmed(chord)) return false;
18091
15995
  disarmChordHud();
@@ -18134,44 +16038,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18134
16038
  if (routeKey.name === 'ㅃ') return true;
18135
16039
  return false;
18136
16040
  },
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
16041
  // PR-S1V.4-wiring · Step 0x — voice mode entry chord.
18176
16042
  // Fires only when voice mode is currently `idle`; once active
18177
16043
  // the next step (routeVoiceModeKey) takes over. Skipping when
@@ -18206,7 +16072,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18206
16072
  // experiment/voice-chat-realtime-rebind · Step 0s — modal-A
18207
16073
  // swallow while voice-chat is active. Returns true when the
18208
16074
  // controller is non-idle so any key not already claimed above
18209
- // (force-quit · Alt+W · Alt+digit · chord toggle) is consumed
16075
+ // (force-quit · Alt+W · chord toggle) is consumed
18210
16076
  // — typing doesn't leak into chat input, Space doesn't toggle
18211
16077
  // pickers, etc. ESC explicitly exits. Idle controller returns
18212
16078
  // false so legacy dispatch is reachable.
@@ -18322,36 +16188,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18322
16188
  })();
18323
16189
  return true;
18324
16190
  },
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
16191
  // Step 6 hard-quit — only reached when no popup terminal is
18356
16192
  // active. Plain Ctrl+Q stays as the dashboard-mode shortcut
18357
16193
  // so muscle memory survives outside the popup. Inside the
@@ -18359,12 +16195,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18359
16195
  // to force-quit instead).
18360
16196
  isHardQuitKey: (routeKey) => routeKey.ctrl && !routeKey.shift && (routeKey.name === 'q' || routeKey.name === 'ㅂ'),
18361
16197
  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),
16198
+ allowSpawnTerminalModal: false,
16199
+ allowToggleLogZoom: false,
18368
16200
  }),
18369
16201
  runGlobalAction: async (action) => {
18370
16202
  await runChatMainGlobalAction(action);
@@ -18828,8 +16660,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18828
16660
  initialText: inputEntry?.initialText ?? null,
18829
16661
  visibility: chatMainInputVisibilityState(),
18830
16662
  topBlockingForegroundModal: topBlockingForegroundModal()?.id ?? null,
18831
- currentVwId: virtualWindows.registry.current()?.id ?? null,
18832
- currentVwHostChromeProfile: virtualWindows.registry.current()?.getHostChromeProfile?.() ?? null,
16663
+ currentVwId: null,
16664
+ currentVwHostChromeProfile: null,
18833
16665
  });
18834
16666
  }
18835
16667
  if (inputEntry) {
@@ -18912,8 +16744,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18912
16744
  textInputOpts: {
18913
16745
  // TUI 부활 후속 — Esc·Esc = /rewind 픽커 (codex backtrack
18914
16746
  // 제스처 · resolveEscEscRewind 순수 판정). 빈 버퍼에서만
18915
- // prime · 1.5s 창 · rich 모드는 Esc 가 입력 루프를 이탈
18916
- // 하므로 사실상 essential 전용(의도). 힌트는 세션 1회.
16747
+ // prime · 1.5s 창. 힌트는 세션 1회.
18917
16748
  onEscape: (bufferText) => {
18918
16749
  const decision = resolveEscEscRewind({
18919
16750
  bufferText: bufferText ?? '',
@@ -19216,6 +17047,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19216
17047
  hasActiveStatusPopup: () => mouseWiring.hasActivePopup(),
19217
17048
  hasTerminalModal: () => terminalModalRouter.current() !== null,
19218
17049
  shouldRouteForegroundModalKey: (key) => {
17050
+ if (key.name === 'escape' && currentTransientTerminalModal('dashboard-help')) return true;
19219
17051
  const topModalId = display.modalStack().at(-1) ?? null;
19220
17052
  const topModal = topModalId ? display.surface(topModalId) : null;
19221
17053
  const topModalSurface =
@@ -19242,7 +17074,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19242
17074
  toKeyEvent: toDashboardKeyEvent,
19243
17075
  routeStatusPopupKey: key => mouseWiring.routeKey(key as never),
19244
17076
  handleTerminalModalKey: keyEvent => terminalModalRouter.handleKey(keyEvent),
19245
- tryRouteForegroundModalKey: keyEvent => display.tryRouteKeyToTopModalAsync(keyEvent),
17077
+ tryRouteForegroundModalKey: async keyEvent => {
17078
+ if (keyEvent.name === 'escape') {
17079
+ const helpModal = currentTransientTerminalModal('dashboard-help');
17080
+ if (helpModal) {
17081
+ helpModal.dispose();
17082
+ draw();
17083
+ return 'consumed';
17084
+ }
17085
+ }
17086
+ return display.tryRouteKeyToTopModalAsync(keyEvent);
17087
+ },
19246
17088
  redraw: draw,
19247
17089
  dispatchMouse: runTextInputOnMouseUnifiedDispatch,
19248
17090
  }),
@@ -19604,6 +17446,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19604
17446
  closeTui,
19605
17447
  exitTui: exitDashboardTui,
19606
17448
  showHelp,
17449
+ showHelpModal: ({ title, lines }) => {
17450
+ const { cols, rows } = termSize();
17451
+ showTransientTerminalModal({ title, lines: [...lines], coordinator: display, termCols: cols, termRows: rows, ttlMs: 0, group: 'dashboard-help' });
17452
+ },
19607
17453
  clearLogSearch,
19608
17454
  clearLogFilter,
19609
17455
  forkAttachedSessionFromChatHistory,
@@ -19628,6 +17474,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19628
17474
  controlSignalSlashRuntime,
19629
17475
  browserCdpSlashRuntime,
19630
17476
  widgetHost,
17477
+ backgroundTasksTextOnly: true,
19631
17478
  widgetModalPopup: {
19632
17479
  open: (s) => {
19633
17480
  // Wave P4b-1 — bridge slash → widget-modal-popup.
@@ -19684,10 +17531,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19684
17531
  attachClipboardImage,
19685
17532
  setNextInitial: (token) => { nextInitial = token; },
19686
17533
  },
19687
- uiModeSlash: {
19688
- getMode: () => dashboardUiMode,
19689
- setMode: (mode) => { applyDashboardUiMode(mode, { persist: true, announce: true }); },
19690
- },
19691
17534
  sessionResume: {
19692
17535
  openPicker: () => { openSessionResumePicker(); },
19693
17536
  },
@@ -19709,23 +17552,10 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19709
17552
  refresh: refreshInputHistory,
19710
17553
  openDetailViewer: (title, lines) => { setDetailViewer(title, lines); },
19711
17554
  },
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
17555
  undo: {
19720
17556
  refreshGitDirty: () => { refreshGitDirty(getSessionCwd(), { force: true }); },
19721
17557
  bold: C.bold,
19722
17558
  },
19723
- ptyPane: {
19724
- spawnPtyTailWindow: (opts) => virtualWindows.registry.spawn({
19725
- title: opts.title,
19726
- initialContent: { kind: 'pty-tail', ptyId: opts.ptyId },
19727
- }),
19728
- },
19729
17559
  delta: {
19730
17560
  openBrowserPopup: (scope, limit, browserMode) =>
19731
17561
  openSourceDeltaBrowserPopup(scope, limit, browserMode),
@@ -19800,24 +17630,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19800
17630
  },
19801
17631
  compact: {
19802
17632
  getHistory: () => chat.history as unknown as { role: string; [k: string]: unknown }[],
19803
- compactBoundaryEnabled: () => getUserConfig().chat.rendering.compactBoundary.enabled,
19804
17633
  },
19805
17634
  skill: {
19806
17635
  triggersSlashRuntime: skillTriggersSlashRuntime,
19807
17636
  runSlashRuntime: runSkillSlashRuntime,
19808
17637
  runByName: (name, skillArgs) => runSkillByName(name, skillArgs),
19809
17638
  },
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
17639
  setup: {
19822
17640
  launchPopup: (cmd) => {
19823
17641
  const session = TerminalPopup.shell()
@@ -19902,40 +17720,8 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19902
17720
  focusPopup: (direction) => focusConversationPopup(direction),
19903
17721
  openModal: (sessionId) => openConversationModal(sessionId),
19904
17722
  },
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
17723
  sim: {
19937
17724
  slashRuntime: simSlashRuntime,
19938
- spawnVirtualWindow: () => spawnSimVirtualWindow(),
19939
17725
  openWebCockpit: () => openDashboardSimulationWebCockpit(),
19940
17726
  listScenarios: () => listDashboardSimulationScenarios(),
19941
17727
  runById: (scenarioId) => runDashboardSimulationById(scenarioId as Parameters<typeof runDashboardSimulationById>[0]),
@@ -19959,39 +17745,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19959
17745
  return aliases.get(target) ?? null;
19960
17746
  },
19961
17747
  },
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
17748
  intake: {
19996
17749
  runSlash: async (intakeArgs) => {
19997
17750
  try {
@@ -20019,7 +17772,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20019
17772
  },
20020
17773
  };
20021
17774
  const r = await resolveIntakeSlash([...intakeArgs], {
20022
- getScratchSnapshot: currentScratchForCommand,
17775
+ getScratchSnapshot: () => {
17776
+ const snapshot = display.scratchState();
17777
+ return snapshot && snapshot.mode !== 'agents'
17778
+ ? { title: snapshot.title, lines: [...snapshot.lines] }
17779
+ : { title: scratchTitle, lines: [...scratchLines] };
17780
+ },
20023
17781
  });
20024
17782
  if (shouldOpenDashboardIntakeSessionPicker([...intakeArgs], r)) {
20025
17783
  openDashboardIntakeSessionPicker(r.sessions, intakeUiDeps);
@@ -20263,44 +18021,22 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20263
18021
  remoteDaemon: () => opts.remote
20264
18022
  ? { url: opts.remote.url, token: opts.remote.token }
20265
18023
  : null,
20266
- localDaemon: () => opts.localDaemon
20267
- ? { socketPath: opts.localDaemon.socketPath }
20268
- : null,
20269
18024
  acpSwapTo: (sessionId, cwd) => dashboardAcpSession.swapTo(sessionId, cwd),
20270
18025
  },
20271
18026
  shellSlash: {
20272
18027
  slashRuntime: shellSlashRuntime,
20273
18028
  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,
18029
+ virtualWindowsSwitchTo: () => { throw new Error('Virtual windows are unavailable in the essential dashboard'); },
18030
+ resolveVwIdByLabel: () => null,
20298
18031
  },
20299
18032
  termSlash: {
20300
18033
  slashRuntime: termSlashRuntime,
20301
18034
  sessionRegistry: sessionRegistry as unknown as DashboardSlashContext['termSlash']['sessionRegistry'],
20302
18035
  terminalMatrix: terminalMatrix as unknown as DashboardSlashContext['termSlash']['terminalMatrix'],
20303
- vwRegistry: virtualWindows.registry as unknown as DashboardSlashContext['termSlash']['vwRegistry'],
18036
+ vwRegistry: {
18037
+ current: () => null,
18038
+ get: () => null,
18039
+ },
20304
18040
  broadcastBus: broadcastBus as unknown as DashboardSlashContext['termSlash']['broadcastBus'],
20305
18041
  channelBus: channelBus as unknown as DashboardSlashContext['termSlash']['channelBus'],
20306
18042
  openSessionPicker: () => {
@@ -20563,15 +18299,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20563
18299
  }
20564
18300
  continue;
20565
18301
  }
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
18302
  // B-2.w migrated: /conv → registry handler.
20576
18303
  // H5 P3 · /handoff <fromId> <toBrand> [--channels reasoning,plan]
20577
18304
  // Cross-agent snapshot-based context transfer.
@@ -20585,7 +18312,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20585
18312
  // B-2.l migrated: /agents /agent /ag → registry handler.
20586
18313
  // B-1.f migrated: /clipboard /clip /cb · /memo /note /me ·
20587
18314
  // /detail /report /dv → registry handlers (companion ctx field).
20588
- // B-2.v migrated: /scratch /sc → registry handler.
20589
18315
  // B-2.o migrated: /intake → registry handler.
20590
18316
  // B-1.g migrated: /signals /signal → registry handler (controlSignalSlashRuntime ctx field).
20591
18317
  case 'media':
@@ -20737,7 +18463,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
20737
18463
  chatLines.push(C.warning(`/${cmdLower} has moved; use ${retiredHarnessCommand}`));
20738
18464
  } else {
20739
18465
  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'));
18466
+ chatLines.push(C.muted('Type /help for the list of commands.'));
20741
18467
  }
20742
18468
  chatScrollOffset = -1;
20743
18469
  continue; // stay in input mode
@@ -21052,8 +18778,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21052
18778
  contextRegistry,
21053
18779
  loadAttachments: (reg) => loadAllAttachments(reg as Parameters<typeof loadAllAttachments>[0]),
21054
18780
  sessionRegistry,
21055
- virtualWindowBook: virtualWindows.book,
21056
- virtualWindowRegistry: virtualWindows.registry,
21057
18781
  sync,
21058
18782
  chatFooterLine,
21059
18783
  routeFooter: () => {
@@ -21091,8 +18815,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21091
18815
  contextText: args.contextText,
21092
18816
  contextRegistry: args.contextRegistry as typeof contextRegistry,
21093
18817
  terminalRegistry: args.terminalRegistry as typeof sessionRegistry,
21094
- addressBook: args.addressBook as typeof virtualWindows.book,
21095
- windowRegistry: args.windowRegistry as typeof virtualWindows.registry,
21096
18818
  blockAttach: args.blockAttach,
21097
18819
  pushChatLine: args.pushChatLine,
21098
18820
  }),
@@ -21119,7 +18841,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21119
18841
  pluginTools: pluginHost.activePluginLLMTools(),
21120
18842
  optionalTools: acpTurnRef.optionalSpecs,
21121
18843
  surface: acpTurnRef.turnProfile?.surface,
21122
- rich: dashboardUiMode === 'rich',
18844
+ rich: false,
21123
18845
  })
21124
18846
  : [];
21125
18847
  return buildDashboardTurnPreamble({
@@ -21127,7 +18849,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21127
18849
  cwd: args.cwd,
21128
18850
  turnProfile: args.turnProfile as SessionTurnProfile,
21129
18851
  userConfig: args.userConfig,
21130
- rich: dashboardUiMode === 'rich',
18852
+ rich: false,
21131
18853
  enabledTools: tools.map(t => t.name),
21132
18854
  });
21133
18855
  },
@@ -21151,7 +18873,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21151
18873
  userMsg: userMsg as ChatMessage,
21152
18874
  model: inspectActiveProvider().model,
21153
18875
  autoCompactConfig: userConfig.chat.autoCompact,
21154
- compactBoundaryEnabled: userConfig.chat.rendering.compactBoundary.enabled,
21155
18876
  shouldAutoCompact,
21156
18877
  compactConversation: async (history) => compactConversation(history),
21157
18878
  compactConversationPartial: async (history, opts) => compactConversationPartial(history, opts),
@@ -21190,7 +18911,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21190
18911
  createDashboardTurnStreamRuntimeEscBoundary(escAbortToolState);
21191
18912
  const toolRendering = {
21192
18913
  ...getUserConfig().chat.rendering.tool,
21193
- expandHint: dashboardUiMode === 'rich',
18914
+ expandHint: false,
21194
18915
  };
21195
18916
  const renderedToolRuntime = createDashboardRenderedToolRuntime({
21196
18917
  chatLines,
@@ -21314,7 +19035,6 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
21314
19035
  },
21315
19036
  runCodeEditPostTurn: async () => {
21316
19037
  await runDashboardCodeEditPostTurn({
21317
- turnSummaryEnabled: getUserConfig().chat.rendering.diff.turnSummary,
21318
19038
  pushChatLine: (line) => { chatLines.push(line); },
21319
19039
  setChatScrollBottom: () => { chatScrollOffset = -1; },
21320
19040
  importCodeEdit: () => import('../code-edit/index.js'),