@ashlr/hub 3.13.0 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (625) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/authority-surface.json +204 -124
  3. package/dist/build-identity.json +2 -2
  4. package/dist/cli/authority.js +112 -18
  5. package/dist/cli/authority.js.map +1 -1
  6. package/dist/cli/comms.d.ts +33 -19
  7. package/dist/cli/comms.js +128 -151
  8. package/dist/cli/comms.js.map +1 -1
  9. package/dist/cli/completions.js +1 -1
  10. package/dist/cli/completions.js.map +1 -1
  11. package/dist/cli/daemon.js +29 -0
  12. package/dist/cli/daemon.js.map +1 -1
  13. package/dist/cli/director.d.ts +8 -12
  14. package/dist/cli/director.js +17 -52
  15. package/dist/cli/director.js.map +1 -1
  16. package/dist/cli/fleet.js +4 -1
  17. package/dist/cli/fleet.js.map +1 -1
  18. package/dist/cli/help.js +1 -0
  19. package/dist/cli/help.js.map +1 -1
  20. package/dist/cli/leader.d.ts +27 -3
  21. package/dist/cli/leader.js +253 -4
  22. package/dist/cli/leader.js.map +1 -1
  23. package/dist/core/authority/server-enforcement.d.ts +110 -0
  24. package/dist/core/authority/server-enforcement.js +260 -0
  25. package/dist/core/authority/server-enforcement.js.map +1 -0
  26. package/dist/core/authority/standing-grant.d.ts +24 -4
  27. package/dist/core/authority/standing-grant.js +19 -3
  28. package/dist/core/authority/standing-grant.js.map +1 -1
  29. package/dist/core/cloud/timeline.js +13 -7
  30. package/dist/core/cloud/timeline.js.map +1 -1
  31. package/dist/core/comms/change-digest.d.ts +130 -0
  32. package/dist/core/comms/change-digest.js +451 -0
  33. package/dist/core/comms/change-digest.js.map +1 -0
  34. package/dist/core/comms/director-prompt.d.ts +4 -5
  35. package/dist/core/comms/director-prompt.js +4 -71
  36. package/dist/core/comms/director-prompt.js.map +1 -1
  37. package/dist/core/comms/director.d.ts +24 -61
  38. package/dist/core/comms/director.js +29 -382
  39. package/dist/core/comms/director.js.map +1 -1
  40. package/dist/core/comms/dispatch.d.ts +37 -12
  41. package/dist/core/comms/dispatch.js +244 -177
  42. package/dist/core/comms/dispatch.js.map +1 -1
  43. package/dist/core/comms/elon-dialogue.d.ts +23 -15
  44. package/dist/core/comms/elon-dialogue.js +36 -307
  45. package/dist/core/comms/elon-dialogue.js.map +1 -1
  46. package/dist/core/comms/fleet-pulse.js +4 -7
  47. package/dist/core/comms/fleet-pulse.js.map +1 -1
  48. package/dist/core/comms/handlers.d.ts +10 -6
  49. package/dist/core/comms/handlers.js +14 -48
  50. package/dist/core/comms/handlers.js.map +1 -1
  51. package/dist/core/comms/migrations.d.ts +47 -0
  52. package/dist/core/comms/migrations.js +125 -0
  53. package/dist/core/comms/migrations.js.map +1 -0
  54. package/dist/core/comms/requests.d.ts +32 -5
  55. package/dist/core/comms/requests.js +101 -5
  56. package/dist/core/comms/requests.js.map +1 -1
  57. package/dist/core/comms/telegram-channel.d.ts +104 -0
  58. package/dist/core/comms/telegram-channel.js +622 -0
  59. package/dist/core/comms/telegram-channel.js.map +1 -0
  60. package/dist/core/comms/telegram-thread-map.d.ts +56 -0
  61. package/dist/core/comms/telegram-thread-map.js +144 -0
  62. package/dist/core/comms/telegram-thread-map.js.map +1 -0
  63. package/dist/core/daemon/capacity-publisher.d.ts +8 -1
  64. package/dist/core/daemon/capacity-publisher.js +40 -2
  65. package/dist/core/daemon/capacity-publisher.js.map +1 -1
  66. package/dist/core/daemon/loop.js +18 -4
  67. package/dist/core/daemon/loop.js.map +1 -1
  68. package/dist/core/daemon/tick-deadline.d.ts +44 -0
  69. package/dist/core/daemon/tick-deadline.js +83 -0
  70. package/dist/core/daemon/tick-deadline.js.map +1 -0
  71. package/dist/core/daemon/tick-hooks.d.ts +7 -0
  72. package/dist/core/daemon/tick-hooks.js.map +1 -1
  73. package/dist/core/daemon/tick-progress.d.ts +39 -0
  74. package/dist/core/daemon/tick-progress.js +194 -0
  75. package/dist/core/daemon/tick-progress.js.map +1 -0
  76. package/dist/core/dashboard.js +3 -1
  77. package/dist/core/dashboard.js.map +1 -1
  78. package/dist/core/fleet/fleet-merge-state.d.ts +6 -0
  79. package/dist/core/fleet/fleet-merge-state.js.map +1 -1
  80. package/dist/core/fleet/host-merge.d.ts +9 -0
  81. package/dist/core/fleet/host-merge.js +13 -2
  82. package/dist/core/fleet/host-merge.js.map +1 -1
  83. package/dist/core/fleet/merge-gates.d.ts +13 -0
  84. package/dist/core/fleet/merge-gates.js +14 -0
  85. package/dist/core/fleet/merge-gates.js.map +1 -1
  86. package/dist/core/fleet/mirrors.d.ts +162 -6
  87. package/dist/core/fleet/mirrors.js +1051 -49
  88. package/dist/core/fleet/mirrors.js.map +1 -1
  89. package/dist/core/fleet/standing-merge-pass.d.ts +8 -0
  90. package/dist/core/fleet/standing-merge-pass.js +72 -1
  91. package/dist/core/fleet/standing-merge-pass.js.map +1 -1
  92. package/dist/core/fleet/status.d.ts +17 -0
  93. package/dist/core/fleet/status.js +22 -0
  94. package/dist/core/fleet/status.js.map +1 -1
  95. package/dist/core/fleet/tick-hooks-live.d.ts +1 -1
  96. package/dist/core/fleet/tick-hooks-live.js +13 -3
  97. package/dist/core/fleet/tick-hooks-live.js.map +1 -1
  98. package/dist/core/git.d.ts +17 -0
  99. package/dist/core/git.js +124 -25
  100. package/dist/core/git.js.map +1 -1
  101. package/dist/core/goals/open-goals.d.ts +47 -0
  102. package/dist/core/goals/open-goals.js +33 -0
  103. package/dist/core/goals/open-goals.js.map +1 -0
  104. package/dist/core/inbox/merge.d.ts +5 -0
  105. package/dist/core/inbox/merge.js +58 -2
  106. package/dist/core/inbox/merge.js.map +1 -1
  107. package/dist/core/integrations/locus.d.ts +28 -0
  108. package/dist/core/integrations/locus.js +155 -30
  109. package/dist/core/integrations/locus.js.map +1 -1
  110. package/dist/core/integrations/telegram-format.d.ts +40 -0
  111. package/dist/core/integrations/telegram-format.js +123 -0
  112. package/dist/core/integrations/telegram-format.js.map +1 -0
  113. package/dist/core/integrations/telegram.d.ts +60 -4
  114. package/dist/core/integrations/telegram.js +161 -28
  115. package/dist/core/integrations/telegram.js.map +1 -1
  116. package/dist/core/mcp-registry.d.ts +12 -0
  117. package/dist/core/mcp-registry.js +70 -0
  118. package/dist/core/mcp-registry.js.map +1 -1
  119. package/dist/core/observability/rollup.d.ts +12 -10
  120. package/dist/core/observability/rollup.js +44 -17
  121. package/dist/core/observability/rollup.js.map +1 -1
  122. package/dist/core/resources/connection-monitor.d.ts +9 -0
  123. package/dist/core/resources/connection-monitor.js +19 -9
  124. package/dist/core/resources/connection-monitor.js.map +1 -1
  125. package/dist/core/resources/quota-refresh-lease.d.ts +17 -0
  126. package/dist/core/resources/quota-refresh-lease.js +34 -1
  127. package/dist/core/resources/quota-refresh-lease.js.map +1 -1
  128. package/dist/core/routing/budget-api.d.ts +7 -1
  129. package/dist/core/routing/budget-api.js +56 -2
  130. package/dist/core/routing/budget-api.js.map +1 -1
  131. package/dist/core/routing/readiness-types.d.ts +82 -0
  132. package/dist/core/routing/readiness-types.js +23 -0
  133. package/dist/core/routing/readiness-types.js.map +1 -0
  134. package/dist/core/routing/readiness.d.ts +103 -0
  135. package/dist/core/routing/readiness.js +480 -0
  136. package/dist/core/routing/readiness.js.map +1 -0
  137. package/dist/core/sandbox/confine.js +22 -1
  138. package/dist/core/sandbox/confine.js.map +1 -1
  139. package/dist/core/sandbox/policy.d.ts +23 -0
  140. package/dist/core/sandbox/policy.js +100 -0
  141. package/dist/core/sandbox/policy.js.map +1 -1
  142. package/dist/core/types.d.ts +13 -0
  143. package/dist/core/types.js.map +1 -1
  144. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  145. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +1204 -939
  146. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +1319 -1054
  147. package/dist/core/usage/frontier-usage.d.ts +20 -2
  148. package/dist/core/usage/frontier-usage.js +75 -17
  149. package/dist/core/usage/frontier-usage.js.map +1 -1
  150. package/dist/core/verse/accounts.d.ts +45 -0
  151. package/dist/core/verse/accounts.js +246 -17
  152. package/dist/core/verse/accounts.js.map +1 -1
  153. package/dist/core/verse/apps-api.js +23 -11
  154. package/dist/core/verse/apps-api.js.map +1 -1
  155. package/dist/core/verse/apps.d.ts +11 -9
  156. package/dist/core/verse/apps.js +2 -2
  157. package/dist/core/verse/apps.js.map +1 -1
  158. package/dist/core/verse/authority-api.d.ts +8 -2
  159. package/dist/core/verse/authority-api.js +40 -5
  160. package/dist/core/verse/authority-api.js.map +1 -1
  161. package/dist/core/verse/autonomy-ladder.d.ts +172 -0
  162. package/dist/core/verse/autonomy-ladder.js +251 -0
  163. package/dist/core/verse/autonomy-ladder.js.map +1 -0
  164. package/dist/core/verse/context-fit.js +7 -5
  165. package/dist/core/verse/context-fit.js.map +1 -1
  166. package/dist/core/verse/control-api.d.ts +23 -1
  167. package/dist/core/verse/control-api.js +117 -10
  168. package/dist/core/verse/control-api.js.map +1 -1
  169. package/dist/core/verse/fleet-live-api.js +13 -5
  170. package/dist/core/verse/fleet-live-api.js.map +1 -1
  171. package/dist/core/verse/folder-io.d.ts +50 -0
  172. package/dist/core/verse/folder-io.js +142 -0
  173. package/dist/core/verse/folder-io.js.map +1 -0
  174. package/dist/core/verse/git-api.js +5 -4
  175. package/dist/core/verse/git-api.js.map +1 -1
  176. package/dist/core/verse/git-ops.d.ts +10 -1
  177. package/dist/core/verse/git-ops.js +18 -2
  178. package/dist/core/verse/git-ops.js.map +1 -1
  179. package/dist/core/verse/github-api.d.ts +1 -1
  180. package/dist/core/verse/github-api.js +5 -4
  181. package/dist/core/verse/github-api.js.map +1 -1
  182. package/dist/core/verse/github-repo.js +118 -14
  183. package/dist/core/verse/github-repo.js.map +1 -1
  184. package/dist/core/verse/leader-api.d.ts +5 -4
  185. package/dist/core/verse/leader-api.js +237 -10
  186. package/dist/core/verse/leader-api.js.map +1 -1
  187. package/dist/core/verse/local-models.d.ts +10 -0
  188. package/dist/core/verse/local-models.js +10 -1
  189. package/dist/core/verse/local-models.js.map +1 -1
  190. package/dist/core/verse/login-path.d.ts +73 -0
  191. package/dist/core/verse/login-path.js +123 -14
  192. package/dist/core/verse/login-path.js.map +1 -1
  193. package/dist/core/verse/mcp-control-api.js +4 -4
  194. package/dist/core/verse/mcp-control-api.js.map +1 -1
  195. package/dist/core/verse/mcp-scope.d.ts +22 -0
  196. package/dist/core/verse/mcp-scope.js +41 -1
  197. package/dist/core/verse/mcp-scope.js.map +1 -1
  198. package/dist/core/verse/mcp-seat-view.d.ts +10 -0
  199. package/dist/core/verse/mcp-seat-view.js +30 -4
  200. package/dist/core/verse/mcp-seat-view.js.map +1 -1
  201. package/dist/core/verse/overnight-api.js +20 -2
  202. package/dist/core/verse/overnight-api.js.map +1 -1
  203. package/dist/core/verse/path-guard.d.ts +24 -0
  204. package/dist/core/verse/path-guard.js +91 -22
  205. package/dist/core/verse/path-guard.js.map +1 -1
  206. package/dist/core/verse/preview-api.js +22 -0
  207. package/dist/core/verse/preview-api.js.map +1 -1
  208. package/dist/core/verse/projects.d.ts +21 -0
  209. package/dist/core/verse/projects.js +82 -0
  210. package/dist/core/verse/projects.js.map +1 -1
  211. package/dist/core/verse/session-controls-api.js +5 -3
  212. package/dist/core/verse/session-controls-api.js.map +1 -1
  213. package/dist/core/verse/session-handoff.d.ts +26 -0
  214. package/dist/core/verse/session-handoff.js +90 -12
  215. package/dist/core/verse/session-handoff.js.map +1 -1
  216. package/dist/core/verse/terminal-api.js +7 -4
  217. package/dist/core/verse/terminal-api.js.map +1 -1
  218. package/dist/core/verse/terminal.js +9 -5
  219. package/dist/core/verse/terminal.js.map +1 -1
  220. package/dist/core/verse/verse-api.js +81 -27
  221. package/dist/core/verse/verse-api.js.map +1 -1
  222. package/dist/core/verse/workspaces.d.ts +51 -0
  223. package/dist/core/verse/workspaces.js +177 -47
  224. package/dist/core/verse/workspaces.js.map +1 -1
  225. package/dist/core/vision/leader-apply.d.ts +40 -0
  226. package/dist/core/vision/leader-apply.js +101 -30
  227. package/dist/core/vision/leader-apply.js.map +1 -1
  228. package/dist/core/vision/leader-cadence.d.ts +114 -0
  229. package/dist/core/vision/leader-cadence.js +127 -0
  230. package/dist/core/vision/leader-cadence.js.map +1 -0
  231. package/dist/core/vision/leader-goal-evidence.d.ts +25 -0
  232. package/dist/core/vision/leader-goal-evidence.js +36 -0
  233. package/dist/core/vision/leader-goal-evidence.js.map +1 -0
  234. package/dist/core/vision/leader-health.d.ts +53 -0
  235. package/dist/core/vision/leader-health.js +92 -0
  236. package/dist/core/vision/leader-health.js.map +1 -0
  237. package/dist/core/vision/leader-operator.d.ts +176 -0
  238. package/dist/core/vision/leader-operator.js +355 -0
  239. package/dist/core/vision/leader-operator.js.map +1 -0
  240. package/dist/core/vision/leader-run-chain.d.ts +41 -0
  241. package/dist/core/vision/leader-run-chain.js +54 -0
  242. package/dist/core/vision/leader-run-chain.js.map +1 -0
  243. package/dist/core/vision/leader-seat-plan.d.ts +68 -0
  244. package/dist/core/vision/leader-seat-plan.js +93 -0
  245. package/dist/core/vision/leader-seat-plan.js.map +1 -0
  246. package/dist/core/vision/leader-seat.d.ts +71 -5
  247. package/dist/core/vision/leader-seat.js +235 -33
  248. package/dist/core/vision/leader-seat.js.map +1 -1
  249. package/dist/core/vision/leader-thread-types.d.ts +104 -0
  250. package/dist/core/vision/leader-thread-types.js +9 -0
  251. package/dist/core/vision/leader-thread-types.js.map +1 -0
  252. package/dist/core/vision/leader-thread.d.ts +138 -0
  253. package/dist/core/vision/leader-thread.js +926 -0
  254. package/dist/core/vision/leader-thread.js.map +1 -0
  255. package/dist/core/vision/leader-types.d.ts +75 -1
  256. package/dist/core/vision/leader.d.ts +88 -18
  257. package/dist/core/vision/leader.js +269 -69
  258. package/dist/core/vision/leader.js.map +1 -1
  259. package/dist/core/web/public/next/assets/{ActionMenu-Dwjk6rUQ.js → ActionMenu-CIiX2X8w.js} +2 -2
  260. package/dist/core/web/public/next/assets/{ActionMenu-Dwjk6rUQ.js.map → ActionMenu-CIiX2X8w.js.map} +1 -1
  261. package/dist/core/web/public/next/assets/Advanced-DHsysoIL.js +2 -0
  262. package/dist/core/web/public/next/assets/{Advanced-cVYTAsfB.js.map → Advanced-DHsysoIL.js.map} +1 -1
  263. package/dist/core/web/public/next/assets/{App-IsaWDsdF.js → App-CMeAocmW.js} +5 -5
  264. package/dist/core/web/public/next/assets/{App-IsaWDsdF.js.map → App-CMeAocmW.js.map} +1 -1
  265. package/dist/core/web/public/next/assets/AppsSection-DISYnMnc.js +2 -0
  266. package/dist/core/web/public/next/assets/{AppsSection-DDqeSrty.js.map → AppsSection-DISYnMnc.js.map} +1 -1
  267. package/dist/core/web/public/next/assets/AreaTrend-DK2Y2TbZ.js +2 -0
  268. package/dist/core/web/public/next/assets/AreaTrend-DK2Y2TbZ.js.map +1 -0
  269. package/dist/core/web/public/next/assets/{AutonomyOffState-CpFYS3tQ.js → AutonomyOffState-CEPSZToP.js} +3 -3
  270. package/dist/core/web/public/next/assets/{AutonomyOffState-CpFYS3tQ.js.map → AutonomyOffState-CEPSZToP.js.map} +1 -1
  271. package/dist/core/web/public/next/assets/AutonomySection-DWc3ciAD.js +2 -0
  272. package/dist/core/web/public/next/assets/{AutonomySection-Dwn8UtOy.js.map → AutonomySection-DWc3ciAD.js.map} +1 -1
  273. package/dist/core/web/public/next/assets/AutonomyStatus-CYaHjJ9U.js +2 -0
  274. package/dist/core/web/public/next/assets/AutonomyStatus-CYaHjJ9U.js.map +1 -0
  275. package/dist/core/web/public/next/assets/{BranchBar-ISZ7xUCj.js → BranchBar-BxhdiIpx.js} +2 -2
  276. package/dist/core/web/public/next/assets/{BranchBar-ISZ7xUCj.js.map → BranchBar-BxhdiIpx.js.map} +1 -1
  277. package/dist/core/web/public/next/assets/{BudgetControl-BARVp-En.js → BudgetControl-C-0KhjRS.js} +2 -2
  278. package/dist/core/web/public/next/assets/{BudgetControl-BARVp-En.js.map → BudgetControl-C-0KhjRS.js.map} +1 -1
  279. package/dist/core/web/public/next/assets/{Button-DtE0rlWD.js → Button-D90dzI_o.js} +2 -2
  280. package/dist/core/web/public/next/assets/{Button-DtE0rlWD.js.map → Button-D90dzI_o.js.map} +1 -1
  281. package/dist/core/web/public/next/assets/{CapacityStrip-DKbKA_Aa.js → CapacityStrip-qKDBC65C.js} +2 -2
  282. package/dist/core/web/public/next/assets/{CapacityStrip-DKbKA_Aa.js.map → CapacityStrip-qKDBC65C.js.map} +1 -1
  283. package/dist/core/web/public/next/assets/ChartFrame-DY-GVUb3.js +2 -0
  284. package/dist/core/web/public/next/assets/ChartFrame-DY-GVUb3.js.map +1 -0
  285. package/dist/core/web/public/next/assets/ChartFrame-DyjmyV8N.css +1 -0
  286. package/dist/core/web/public/next/assets/ChatSection-DlT4QU6a.js +3 -0
  287. package/dist/core/web/public/next/assets/{ChatSection-C9myY4ei.js.map → ChatSection-DlT4QU6a.js.map} +1 -1
  288. package/dist/core/web/public/next/assets/{CloudBudgetForm-CvjK4Oh6.js → CloudBudgetForm-BrNun_Yu.js} +2 -2
  289. package/dist/core/web/public/next/assets/{CloudBudgetForm-CvjK4Oh6.js.map → CloudBudgetForm-BrNun_Yu.js.map} +1 -1
  290. package/dist/core/web/public/next/assets/CommandPalette-C5lDDKN0.js +2 -0
  291. package/dist/core/web/public/next/assets/CommandPalette-C5lDDKN0.js.map +1 -0
  292. package/dist/core/web/public/next/assets/CommandSection-B0xuU-yZ.css +1 -0
  293. package/dist/core/web/public/next/assets/CommandSection-DXFvPgKp.js +3 -0
  294. package/dist/core/web/public/next/assets/CommandSection-DXFvPgKp.js.map +1 -0
  295. package/dist/core/web/public/next/assets/{ConfirmDialog-CLUhzaBj.js → ConfirmDialog-BR4nZQ2D.js} +2 -2
  296. package/dist/core/web/public/next/assets/{ConfirmDialog-CLUhzaBj.js.map → ConfirmDialog-BR4nZQ2D.js.map} +1 -1
  297. package/dist/core/web/public/next/assets/{DeleteChatDialog-C_eDhV8b.js → DeleteChatDialog-BCfP12GH.js} +2 -2
  298. package/dist/core/web/public/next/assets/{DeleteChatDialog-C_eDhV8b.js.map → DeleteChatDialog-BCfP12GH.js.map} +1 -1
  299. package/dist/core/web/public/next/assets/{Dialog-BRd_B9OE.js → Dialog-D3msQlCj.js} +2 -2
  300. package/dist/core/web/public/next/assets/{Dialog-BRd_B9OE.js.map → Dialog-D3msQlCj.js.map} +1 -1
  301. package/dist/core/web/public/next/assets/{DiffPane-Cpr9soB2.js → DiffPane-Ix6hvXlY.js} +2 -2
  302. package/dist/core/web/public/next/assets/{DiffPane-Cpr9soB2.js.map → DiffPane-Ix6hvXlY.js.map} +1 -1
  303. package/dist/core/web/public/next/assets/{DiffViewer-CNwd0W3Q.js → DiffViewer-C4WKsCIe.js} +2 -2
  304. package/dist/core/web/public/next/assets/{DiffViewer-CNwd0W3Q.js.map → DiffViewer-C4WKsCIe.js.map} +1 -1
  305. package/dist/core/web/public/next/assets/DockHost-aWP7liri.js +2 -0
  306. package/dist/core/web/public/next/assets/{DockHost-BcEq2qaS.js.map → DockHost-aWP7liri.js.map} +1 -1
  307. package/dist/core/web/public/next/assets/{EmptyState-DsN1H6MC.js → EmptyState-D2IRy3Cg.js} +2 -2
  308. package/dist/core/web/public/next/assets/{EmptyState-DsN1H6MC.js.map → EmptyState-D2IRy3Cg.js.map} +1 -1
  309. package/dist/core/web/public/next/assets/{Epistemic-fycQTBRO.js → Epistemic-DMr18muX.js} +2 -2
  310. package/dist/core/web/public/next/assets/{Epistemic-fycQTBRO.js.map → Epistemic-DMr18muX.js.map} +1 -1
  311. package/dist/core/web/public/next/assets/{EvidenceTimeline-sCCAdnBK.js → EvidenceTimeline-DXmmz6Fi.js} +2 -2
  312. package/dist/core/web/public/next/assets/{EvidenceTimeline-sCCAdnBK.js.map → EvidenceTimeline-DXmmz6Fi.js.map} +1 -1
  313. package/dist/core/web/public/next/assets/FleetSection-BfplcwFU.js +3 -0
  314. package/dist/core/web/public/next/assets/FleetSection-BfplcwFU.js.map +1 -0
  315. package/dist/core/web/public/next/assets/Funnel-Dh2yVHws.js +2 -0
  316. package/dist/core/web/public/next/assets/Funnel-Dh2yVHws.js.map +1 -0
  317. package/dist/core/web/public/next/assets/{GearTray-CpDt0xwj.js → GearTray-Dt-ee32-.js} +2 -2
  318. package/dist/core/web/public/next/assets/{GearTray-CpDt0xwj.js.map → GearTray-Dt-ee32-.js.map} +1 -1
  319. package/dist/core/web/public/next/assets/GrowthSection-P9EUfGeA.js +2 -0
  320. package/dist/core/web/public/next/assets/{GrowthSection-BvZezOsX.js.map → GrowthSection-P9EUfGeA.js.map} +1 -1
  321. package/dist/core/web/public/next/assets/{HandoffDialog-B1G6gQyk.js → HandoffDialog-CLJQLe7M.js} +2 -2
  322. package/dist/core/web/public/next/assets/{HandoffDialog-B1G6gQyk.js.map → HandoffDialog-CLJQLe7M.js.map} +1 -1
  323. package/dist/core/web/public/next/assets/{Input-CI0yBHmn.js → Input-D_KRJA9V.js} +2 -2
  324. package/dist/core/web/public/next/assets/{Input-CI0yBHmn.js.map → Input-D_KRJA9V.js.map} +1 -1
  325. package/dist/core/web/public/next/assets/LeaderCard-D-7DlJuR.js +2 -0
  326. package/dist/core/web/public/next/assets/LeaderCard-D-7DlJuR.js.map +1 -0
  327. package/dist/core/web/public/next/assets/LeaderCard-DNsbLwY3.css +1 -0
  328. package/dist/core/web/public/next/assets/LeaderConversation-DGZjWI-E.js +2 -0
  329. package/dist/core/web/public/next/assets/LeaderConversation-DGZjWI-E.js.map +1 -0
  330. package/dist/core/web/public/next/assets/LeaderConversation-DPHUTq6W.css +1 -0
  331. package/dist/core/web/public/next/assets/MatrixHeatmap-C7BCG2d7.js +2 -0
  332. package/dist/core/web/public/next/assets/MatrixHeatmap-C7BCG2d7.js.map +1 -0
  333. package/dist/core/web/public/next/assets/McpSection-DGbRcyv8.js +2 -0
  334. package/dist/core/web/public/next/assets/{McpSection-nuXer4K-.js.map → McpSection-DGbRcyv8.js.map} +1 -1
  335. package/dist/core/web/public/next/assets/{MessageMarkdown-BCe4TsXk.js → MessageMarkdown-DFvk06qG.js} +3 -3
  336. package/dist/core/web/public/next/assets/{MessageMarkdown-BCe4TsXk.js.map → MessageMarkdown-DFvk06qG.js.map} +1 -1
  337. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DL3LU5kL.js → MessageMarkdownRenderer-BAv4oEUf.js} +2 -2
  338. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DL3LU5kL.js.map → MessageMarkdownRenderer-BAv4oEUf.js.map} +1 -1
  339. package/dist/core/web/public/next/assets/{Meter-b69LM0mU.js → Meter-CMj-VafN.js} +2 -2
  340. package/dist/core/web/public/next/assets/{Meter-b69LM0mU.js.map → Meter-CMj-VafN.js.map} +1 -1
  341. package/dist/core/web/public/next/assets/MindSection-C6kLPIjt.js +3 -0
  342. package/dist/core/web/public/next/assets/MindSection-C6kLPIjt.js.map +1 -0
  343. package/dist/core/web/public/next/assets/{MutationTokenDialog-Bm34BNVV.js → MutationTokenDialog-DbnO8aYi.js} +2 -2
  344. package/dist/core/web/public/next/assets/{MutationTokenDialog-Bm34BNVV.js.map → MutationTokenDialog-DbnO8aYi.js.map} +1 -1
  345. package/dist/core/web/public/next/assets/NeedsYouDrawer-crsiX-Nc.js +3 -0
  346. package/dist/core/web/public/next/assets/NeedsYouDrawer-crsiX-Nc.js.map +1 -0
  347. package/dist/core/web/public/next/assets/{NeedsYouList-D5Kk549g.js → NeedsYouList-CCsijdWt.js} +2 -2
  348. package/dist/core/web/public/next/assets/{NeedsYouList-D5Kk549g.js.map → NeedsYouList-CCsijdWt.js.map} +1 -1
  349. package/dist/core/web/public/next/assets/NewChatDialog-BlCHtOIr.js +3 -0
  350. package/dist/core/web/public/next/assets/{NewChatDialog-BqFTWgHU.js.map → NewChatDialog-BlCHtOIr.js.map} +1 -1
  351. package/dist/core/web/public/next/assets/{OnboardingFlow-DcDsguAM.js → OnboardingFlow-Cy9BA9eR.js} +3 -3
  352. package/dist/core/web/public/next/assets/{OnboardingFlow-DcDsguAM.js.map → OnboardingFlow-Cy9BA9eR.js.map} +1 -1
  353. package/dist/core/web/public/next/assets/OnboardingGate-BXpNFoLt.js +3 -0
  354. package/dist/core/web/public/next/assets/{OnboardingGate-BZafM1Vf.js.map → OnboardingGate-BXpNFoLt.js.map} +1 -1
  355. package/dist/core/web/public/next/assets/PreviewPane-COid5A0f.js +2 -0
  356. package/dist/core/web/public/next/assets/{PreviewPane-zT3gK9TD.js.map → PreviewPane-COid5A0f.js.map} +1 -1
  357. package/dist/core/web/public/next/assets/{ProviderLogo-AwHn2sKm.js → ProviderLogo-Cq-CYIwT.js} +2 -2
  358. package/dist/core/web/public/next/assets/{ProviderLogo-AwHn2sKm.js.map → ProviderLogo-Cq-CYIwT.js.map} +1 -1
  359. package/dist/core/web/public/next/assets/{RailStatus-xHrovWvI.js → RailStatus-Co5UB6We.js} +2 -2
  360. package/dist/core/web/public/next/assets/{RailStatus-xHrovWvI.js.map → RailStatus-Co5UB6We.js.map} +1 -1
  361. package/dist/core/web/public/next/assets/{RefreshIndicator-SEFZlMB2.js → RefreshIndicator-DO9ibnSq.js} +2 -2
  362. package/dist/core/web/public/next/assets/{RefreshIndicator-SEFZlMB2.js.map → RefreshIndicator-DO9ibnSq.js.map} +1 -1
  363. package/dist/core/web/public/next/assets/ResourcePoolConsoleApp-Dr2WRgkq.js +7 -0
  364. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-CAxPQ4sZ.js.map → ResourcePoolConsoleApp-Dr2WRgkq.js.map} +1 -1
  365. package/dist/core/web/public/next/assets/ResourcesChrome-CwMnbqI1.js +3 -0
  366. package/dist/core/web/public/next/assets/ResourcesChrome-CwMnbqI1.js.map +1 -0
  367. package/dist/core/web/public/next/assets/ResourcesDrawer-B_Xp_JNc.js +2 -0
  368. package/dist/core/web/public/next/assets/ResourcesDrawer-B_Xp_JNc.js.map +1 -0
  369. package/dist/core/web/public/next/assets/ResourcesDrawer-DrGoO8KG.css +1 -0
  370. package/dist/core/web/public/next/assets/{RouteErrorBoundary-C8WGiixc.js → RouteErrorBoundary-SkNhhUbt.js} +2 -2
  371. package/dist/core/web/public/next/assets/{RouteErrorBoundary-C8WGiixc.js.map → RouteErrorBoundary-SkNhhUbt.js.map} +1 -1
  372. package/dist/core/web/public/next/assets/{RunInCloudAction-1-IPLYhf.js → RunInCloudAction-BXPlZwJQ.js} +2 -2
  373. package/dist/core/web/public/next/assets/{RunInCloudAction-1-IPLYhf.js.map → RunInCloudAction-BXPlZwJQ.js.map} +1 -1
  374. package/dist/core/web/public/next/assets/{SeatSelector-DpwNiX8F.js → SeatSelector-J5R3zDkB.js} +3 -3
  375. package/dist/core/web/public/next/assets/{SeatSelector-DpwNiX8F.js.map → SeatSelector-J5R3zDkB.js.map} +1 -1
  376. package/dist/core/web/public/next/assets/{Segmented-Wt1ATod9.js → Segmented-CGh_-tBb.js} +2 -2
  377. package/dist/core/web/public/next/assets/{Segmented-Wt1ATod9.js.map → Segmented-CGh_-tBb.js.map} +1 -1
  378. package/dist/core/web/public/next/assets/{Select-By-V9lMc.js → Select-CWnmUzs3.js} +2 -2
  379. package/dist/core/web/public/next/assets/{Select-By-V9lMc.js.map → Select-CWnmUzs3.js.map} +1 -1
  380. package/dist/core/web/public/next/assets/{SessionGate-WROS02tK.js → SessionGate-sp4KsnUo.js} +2 -2
  381. package/dist/core/web/public/next/assets/{SessionGate-WROS02tK.js.map → SessionGate-sp4KsnUo.js.map} +1 -1
  382. package/dist/core/web/public/next/assets/{SessionInsightChip-DNUcruwp.js → SessionInsightChip-DLFAzdbi.js} +2 -2
  383. package/dist/core/web/public/next/assets/{SessionInsightChip-DNUcruwp.js.map → SessionInsightChip-DLFAzdbi.js.map} +1 -1
  384. package/dist/core/web/public/next/assets/{SessionSearch-UHtt7a8f.js → SessionSearch-DbFjwR1h.js} +2 -2
  385. package/dist/core/web/public/next/assets/{SessionSearch-UHtt7a8f.js.map → SessionSearch-DbFjwR1h.js.map} +1 -1
  386. package/dist/core/web/public/next/assets/{SettingsSection-Bf74l0Gj.js → SettingsSection-Ch23msJ5.js} +2 -2
  387. package/dist/core/web/public/next/assets/SettingsSection-Ch23msJ5.js.map +1 -0
  388. package/dist/core/web/public/next/assets/{SetupChecklist-CzpEcfOl.js → SetupChecklist-Bd76MBAR.js} +2 -2
  389. package/dist/core/web/public/next/assets/{SetupChecklist-CzpEcfOl.js.map → SetupChecklist-Bd76MBAR.js.map} +1 -1
  390. package/dist/core/web/public/next/assets/ShadowDecisions-C5RAY91f.js +3 -0
  391. package/dist/core/web/public/next/assets/ShadowDecisions-C5RAY91f.js.map +1 -0
  392. package/dist/core/web/public/next/assets/{Sheet-HkBOBMTT.js → Sheet-BZ2AG4Eo.js} +2 -2
  393. package/dist/core/web/public/next/assets/{Sheet-HkBOBMTT.js.map → Sheet-BZ2AG4Eo.js.map} +1 -1
  394. package/dist/core/web/public/next/assets/{ShortcutsOverlay-DiigiT8H.js → ShortcutsOverlay-C8n8y3zf.js} +2 -2
  395. package/dist/core/web/public/next/assets/{ShortcutsOverlay-DiigiT8H.js.map → ShortcutsOverlay-C8n8y3zf.js.map} +1 -1
  396. package/dist/core/web/public/next/assets/Sidebar-DN--cPYN.js +3 -0
  397. package/dist/core/web/public/next/assets/{Sidebar-C9YtBYVZ.js.map → Sidebar-DN--cPYN.js.map} +1 -1
  398. package/dist/core/web/public/next/assets/{Slider-DaEDws-b.js → Slider-KVoJLDD6.js} +2 -2
  399. package/dist/core/web/public/next/assets/{Slider-DaEDws-b.js.map → Slider-KVoJLDD6.js.map} +1 -1
  400. package/dist/core/web/public/next/assets/StatTile-C4KgjltA.js +2 -0
  401. package/dist/core/web/public/next/assets/StatTile-C4KgjltA.js.map +1 -0
  402. package/dist/core/web/public/next/assets/StatTile-CNCqDoPO.css +1 -0
  403. package/dist/core/web/public/next/assets/{StatusBadge-CXG3iNL4.js → StatusBadge-CXsYqmdU.js} +2 -2
  404. package/dist/core/web/public/next/assets/{StatusBadge-CXG3iNL4.js.map → StatusBadge-CXsYqmdU.js.map} +1 -1
  405. package/dist/core/web/public/next/assets/StepBand-BMtVOFpK.js +2 -0
  406. package/dist/core/web/public/next/assets/StepBand-BMtVOFpK.js.map +1 -0
  407. package/dist/core/web/public/next/assets/{Surface-5bNA08tK.js → Surface-BfDmQcKe.js} +2 -2
  408. package/dist/core/web/public/next/assets/{Surface-5bNA08tK.js.map → Surface-BfDmQcKe.js.map} +1 -1
  409. package/dist/core/web/public/next/assets/Swimlane-B9Exxekk.js +2 -0
  410. package/dist/core/web/public/next/assets/Swimlane-B9Exxekk.js.map +1 -0
  411. package/dist/core/web/public/next/assets/Swimlane-Bf4Rjoy9.css +1 -0
  412. package/dist/core/web/public/next/assets/{Switch-BmCzfFnu.js → Switch-9NtO_6Lj.js} +2 -2
  413. package/dist/core/web/public/next/assets/{Switch-BmCzfFnu.js.map → Switch-9NtO_6Lj.js.map} +1 -1
  414. package/dist/core/web/public/next/assets/{Tag-WSUIaMVy.js → Tag-DscCRFcN.js} +2 -2
  415. package/dist/core/web/public/next/assets/{Tag-WSUIaMVy.js.map → Tag-DscCRFcN.js.map} +1 -1
  416. package/dist/core/web/public/next/assets/{TerminalPane-BVFgYX6K.js → TerminalPane-B668-DJW.js} +5 -5
  417. package/dist/core/web/public/next/assets/{TerminalPane-BVFgYX6K.js.map → TerminalPane-B668-DJW.js.map} +1 -1
  418. package/dist/core/web/public/next/assets/UniverseConsoleApp-BUV_brY7.js +2 -0
  419. package/dist/core/web/public/next/assets/{UniverseConsoleApp-BpphLqoz.js.map → UniverseConsoleApp-BUV_brY7.js.map} +1 -1
  420. package/dist/core/web/public/next/assets/{UniverseView-DbpbSMwO.js → UniverseView-DG70zH0W.js} +4 -4
  421. package/dist/core/web/public/next/assets/{UniverseView-DbpbSMwO.js.map → UniverseView-DG70zH0W.js.map} +1 -1
  422. package/dist/core/web/public/next/assets/UsageSection-DY7uOTm1.js +3 -0
  423. package/dist/core/web/public/next/assets/UsageSection-DY7uOTm1.js.map +1 -0
  424. package/dist/core/web/public/next/assets/VerseConsoleApp-CWU6R3By.js +3 -0
  425. package/dist/core/web/public/next/assets/VerseConsoleApp-CWU6R3By.js.map +1 -0
  426. package/dist/core/web/public/next/assets/Workspace-D1nP9BhD.js +10 -0
  427. package/dist/core/web/public/next/assets/{Workspace-RXy0pqp6.js.map → Workspace-D1nP9BhD.js.map} +1 -1
  428. package/dist/core/web/public/next/assets/actions-CY-PjpGc.js +2 -0
  429. package/dist/core/web/public/next/assets/{actions-BZTidCXZ.js.map → actions-CY-PjpGc.js.map} +1 -1
  430. package/dist/core/web/public/next/assets/actions-DkWhhWIg.css +1 -0
  431. package/dist/core/web/public/next/assets/{apps-model-CHO133qt.js → apps-model-Du02KhAf.js} +2 -2
  432. package/dist/core/web/public/next/assets/{apps-model-CHO133qt.js.map → apps-model-Du02KhAf.js.map} +1 -1
  433. package/dist/core/web/public/next/assets/{authority-model-8NeAEwAF.js → authority-model-79SeuKlJ.js} +2 -2
  434. package/dist/core/web/public/next/assets/{authority-model-8NeAEwAF.js.map → authority-model-79SeuKlJ.js.map} +1 -1
  435. package/dist/core/web/public/next/assets/autonomy-ladder-BGWmZzP7.css +1 -0
  436. package/dist/core/web/public/next/assets/autonomy-ladder.module-ArDv4bkr.js +2 -0
  437. package/dist/core/web/public/next/assets/autonomy-ladder.module-ArDv4bkr.js.map +1 -0
  438. package/dist/core/web/public/next/assets/budget-queries-D8QV5XtH.js +2 -0
  439. package/dist/core/web/public/next/assets/{budget-queries-DFF-3ICg.js.map → budget-queries-D8QV5XtH.js.map} +1 -1
  440. package/dist/core/web/public/next/assets/{capacity-strip-model-5ViUgUlP.js → capacity-strip-model-BwZ1ZbkU.js} +2 -2
  441. package/dist/core/web/public/next/assets/{capacity-strip-model-5ViUgUlP.js.map → capacity-strip-model-BwZ1ZbkU.js.map} +1 -1
  442. package/dist/core/web/public/next/assets/charts-BZ50mZD_.js +2 -0
  443. package/dist/core/web/public/next/assets/charts-BZ50mZD_.js.map +1 -0
  444. package/dist/core/web/public/next/assets/charts-ClCB18la.css +1 -0
  445. package/dist/core/web/public/next/assets/{cloud-model-CfRydq8N.js → cloud-model-BDivRMqv.js} +2 -2
  446. package/dist/core/web/public/next/assets/{cloud-model-CfRydq8N.js.map → cloud-model-BDivRMqv.js.map} +1 -1
  447. package/dist/core/web/public/next/assets/{cloud-queries-DmptbjS8.js → cloud-queries-BPQQ5_pJ.js} +2 -2
  448. package/dist/core/web/public/next/assets/{cloud-queries-DmptbjS8.js.map → cloud-queries-BPQQ5_pJ.js.map} +1 -1
  449. package/dist/core/web/public/next/assets/{colors-BZ3a9wFf.js → colors-DvK0wMHH.js} +2 -2
  450. package/dist/core/web/public/next/assets/{colors-BZ3a9wFf.js.map → colors-DvK0wMHH.js.map} +1 -1
  451. package/dist/core/web/public/next/assets/{command-catalog-ffCAijVY.js → command-catalog-BIntSa9l.js} +2 -2
  452. package/dist/core/web/public/next/assets/command-catalog-BIntSa9l.js.map +1 -0
  453. package/dist/core/web/public/next/assets/{context-model-CDeyP0cA.js → context-model-Br8t9jgG.js} +2 -2
  454. package/dist/core/web/public/next/assets/{context-model-CDeyP0cA.js.map → context-model-Br8t9jgG.js.map} +1 -1
  455. package/dist/core/web/public/next/assets/{context-model-Bb15chFt.js → context-model-CokrgEUq.js} +3 -3
  456. package/dist/core/web/public/next/assets/{context-model-Bb15chFt.js.map → context-model-CokrgEUq.js.map} +1 -1
  457. package/dist/core/web/public/next/assets/context-queries-eL3caKVG.js +2 -0
  458. package/dist/core/web/public/next/assets/{context-queries-_9bFWb9v.js.map → context-queries-eL3caKVG.js.map} +1 -1
  459. package/dist/core/web/public/next/assets/{control-queries-CXtmZZH6.js → control-queries-B9K-wmcB.js} +2 -2
  460. package/dist/core/web/public/next/assets/{control-queries-CXtmZZH6.js.map → control-queries-B9K-wmcB.js.map} +1 -1
  461. package/dist/core/web/public/next/assets/{copy-setup-BIp0iaiZ.js → copy-setup-XMgCC5f8.js} +2 -2
  462. package/dist/core/web/public/next/assets/{copy-setup-BIp0iaiZ.js.map → copy-setup-XMgCC5f8.js.map} +1 -1
  463. package/dist/core/web/public/next/assets/{dock-icons-Cc43W7r4.js → dock-icons-BJ6ZqB2t.js} +2 -2
  464. package/dist/core/web/public/next/assets/{dock-icons-Cc43W7r4.js.map → dock-icons-BJ6ZqB2t.js.map} +1 -1
  465. package/dist/core/web/public/next/assets/{fleet-model-DOK8ZtEt.js → fleet-model-BvDR3gjp.js} +2 -2
  466. package/dist/core/web/public/next/assets/{fleet-model-DOK8ZtEt.js.map → fleet-model-BvDR3gjp.js.map} +1 -1
  467. package/dist/core/web/public/next/assets/{fleet-queries-BeEAqD6y.js → fleet-queries-W2UFVMZJ.js} +2 -2
  468. package/dist/core/web/public/next/assets/{fleet-queries-BeEAqD6y.js.map → fleet-queries-W2UFVMZJ.js.map} +1 -1
  469. package/dist/core/web/public/next/assets/{focus-trap-ChZ5EFfM.js → focus-trap-CtI1SlMX.js} +2 -2
  470. package/dist/core/web/public/next/assets/{focus-trap-ChZ5EFfM.js.map → focus-trap-CtI1SlMX.js.map} +1 -1
  471. package/dist/core/web/public/next/assets/{format-BmH2yWL6.js → format-CH4g_sl1.js} +2 -2
  472. package/dist/core/web/public/next/assets/{format-BmH2yWL6.js.map → format-CH4g_sl1.js.map} +1 -1
  473. package/dist/core/web/public/next/assets/{format-DKrNjdwD.js → format-D7nBg2bn.js} +2 -2
  474. package/dist/core/web/public/next/assets/{format-DKrNjdwD.js.map → format-D7nBg2bn.js.map} +1 -1
  475. package/dist/core/web/public/next/assets/{git-model-BFwAVbxF.js → git-model-Dnkd1TnK.js} +2 -2
  476. package/dist/core/web/public/next/assets/{git-model-BFwAVbxF.js.map → git-model-Dnkd1TnK.js.map} +1 -1
  477. package/dist/core/web/public/next/assets/global-CMNW_dqn.js +2 -0
  478. package/dist/core/web/public/next/assets/{global-y_exIyAP.js.map → global-CMNW_dqn.js.map} +1 -1
  479. package/dist/core/web/public/next/assets/guarded-runners-YeAc9wLQ.js +2 -0
  480. package/dist/core/web/public/next/assets/{guarded-runners-Cd4POBtI.js.map → guarded-runners-YeAc9wLQ.js.map} +1 -1
  481. package/dist/core/web/public/next/assets/{icons-CygCEFWs.js → icons-D1sYbCTQ.js} +2 -2
  482. package/dist/core/web/public/next/assets/{icons-CygCEFWs.js.map → icons-D1sYbCTQ.js.map} +1 -1
  483. package/dist/core/web/public/next/assets/{index-CyK31YRm.js → index-DZ-UQ7v8.js} +3 -3
  484. package/dist/core/web/public/next/assets/{index-CyK31YRm.js.map → index-DZ-UQ7v8.js.map} +1 -1
  485. package/dist/core/web/public/next/assets/leader-focus-BjeJv7CW.js +2 -0
  486. package/dist/core/web/public/next/assets/leader-focus-BjeJv7CW.js.map +1 -0
  487. package/dist/core/web/public/next/assets/leader-thread-types-CDb-VPPO.js +2 -0
  488. package/dist/core/web/public/next/assets/leader-thread-types-CDb-VPPO.js.map +1 -0
  489. package/dist/core/web/public/next/assets/{live-model-D7hilYFk.js → live-model-D_32RAOR.js} +2 -2
  490. package/dist/core/web/public/next/assets/{live-model-D7hilYFk.js.map → live-model-D_32RAOR.js.map} +1 -1
  491. package/dist/core/web/public/next/assets/{mind.module-B15k0Y9q.js → mind.module-7hA92hRg.js} +2 -2
  492. package/dist/core/web/public/next/assets/{mind.module-B15k0Y9q.js.map → mind.module-7hA92hRg.js.map} +1 -1
  493. package/dist/core/web/public/next/assets/mutations-DQPbFdRJ.js +2 -0
  494. package/dist/core/web/public/next/assets/{mutations-CWlxEMEm.js.map → mutations-DQPbFdRJ.js.map} +1 -1
  495. package/dist/core/web/public/next/assets/{nav-mimleI0R.js → nav-0dOYe2rJ.js} +2 -2
  496. package/dist/core/web/public/next/assets/{nav-mimleI0R.js.map → nav-0dOYe2rJ.js.map} +1 -1
  497. package/dist/core/web/public/next/assets/{overnight-queries-DJ4mZKSj.js → overnight-queries-C4wOnFtE.js} +2 -2
  498. package/dist/core/web/public/next/assets/{overnight-queries-DJ4mZKSj.js.map → overnight-queries-C4wOnFtE.js.map} +1 -1
  499. package/dist/core/web/public/next/assets/primitives-2A5Eq8FQ.js +1 -0
  500. package/dist/core/web/public/next/assets/{queries-Bw1McHTj.js → queries-BTXWdZzU.js} +2 -2
  501. package/dist/core/web/public/next/assets/{queries-Bw1McHTj.js.map → queries-BTXWdZzU.js.map} +1 -1
  502. package/dist/core/web/public/next/assets/{reasoning-pref-KkhI_5-j.js → reasoning-pref-Ca6lN6x_.js} +2 -2
  503. package/dist/core/web/public/next/assets/{reasoning-pref-KkhI_5-j.js.map → reasoning-pref-Ca6lN6x_.js.map} +1 -1
  504. package/dist/core/web/public/next/assets/{reveal-anchor-CCCx_kfT.js → reveal-anchor-DfNLgK_I.js} +2 -2
  505. package/dist/core/web/public/next/assets/{reveal-anchor-CCCx_kfT.js.map → reveal-anchor-DfNLgK_I.js.map} +1 -1
  506. package/dist/core/web/public/next/assets/seat-subscription-C5uPHBsN.js +2 -0
  507. package/dist/core/web/public/next/assets/{seat-subscription-BhUUz7VL.js.map → seat-subscription-C5uPHBsN.js.map} +1 -1
  508. package/dist/core/web/public/next/assets/{sidebar-model-Dcd437cf.js → sidebar-model-CLCdqwkR.js} +2 -2
  509. package/dist/core/web/public/next/assets/{sidebar-model-Dcd437cf.js.map → sidebar-model-CLCdqwkR.js.map} +1 -1
  510. package/dist/core/web/public/next/assets/{slots-BL_J5mmV.js → slots-C-qBeu-8.js} +3 -3
  511. package/dist/core/web/public/next/assets/{slots-BL_J5mmV.js.map → slots-C-qBeu-8.js.map} +1 -1
  512. package/dist/core/web/public/next/assets/{surface-data-C3UAQ4QX.js → surface-data-Dj71tdD-.js} +2 -2
  513. package/dist/core/web/public/next/assets/{surface-data-C3UAQ4QX.js.map → surface-data-Dj71tdD-.js.map} +1 -1
  514. package/dist/core/web/public/next/assets/surface-prefetch-B0WdJOJG.js +2 -0
  515. package/dist/core/web/public/next/assets/surface-prefetch-B0WdJOJG.js.map +1 -0
  516. package/dist/core/web/public/next/assets/{tasks-model-CzHvclVm.js → tasks-model-O8yR5YXp.js} +2 -2
  517. package/dist/core/web/public/next/assets/{tasks-model-CzHvclVm.js.map → tasks-model-O8yR5YXp.js.map} +1 -1
  518. package/dist/core/web/public/next/assets/{terminal-icons-BwgQTQP0.js → terminal-icons-CoRZq9iO.js} +2 -2
  519. package/dist/core/web/public/next/assets/{terminal-icons-BwgQTQP0.js.map → terminal-icons-CoRZq9iO.js.map} +1 -1
  520. package/dist/core/web/public/next/assets/thread-data-DQB_g66d.js +2 -0
  521. package/dist/core/web/public/next/assets/thread-data-DQB_g66d.js.map +1 -0
  522. package/dist/core/web/public/next/assets/{usage-queries-pXxvy54x.js → usage-queries-AdjEyEtn.js} +2 -2
  523. package/dist/core/web/public/next/assets/{usage-queries-pXxvy54x.js.map → usage-queries-AdjEyEtn.js.map} +1 -1
  524. package/dist/core/web/public/next/assets/{use-guarded-action-EgVWjM5c.js → use-guarded-action-CuuMHeDX.js} +2 -2
  525. package/dist/core/web/public/next/assets/{use-guarded-action-EgVWjM5c.js.map → use-guarded-action-CuuMHeDX.js.map} +1 -1
  526. package/dist/core/web/public/next/assets/{use-ticker-BoqFxRFG.js → use-ticker-BBcp02SZ.js} +2 -2
  527. package/dist/core/web/public/next/assets/{use-ticker-BoqFxRFG.js.map → use-ticker-BBcp02SZ.js.map} +1 -1
  528. package/dist/core/web/public/next/assets/{use-token-gate-DSNIIpI6.js → use-token-gate-CbXVYQYI.js} +2 -2
  529. package/dist/core/web/public/next/assets/{use-token-gate-DSNIIpI6.js.map → use-token-gate-CbXVYQYI.js.map} +1 -1
  530. package/dist/core/web/public/next/assets/{useOnboarding-Bk0VnkxQ.js → useOnboarding-C_Fpe_sH.js} +2 -2
  531. package/dist/core/web/public/next/assets/{useOnboarding-Bk0VnkxQ.js.map → useOnboarding-C_Fpe_sH.js.map} +1 -1
  532. package/dist/core/web/public/next/assets/{useSeatsRefresh-q7Id9RcW.js → useSeatsRefresh-Co1CnxLc.js} +2 -2
  533. package/dist/core/web/public/next/assets/{useSeatsRefresh-q7Id9RcW.js.map → useSeatsRefresh-Co1CnxLc.js.map} +1 -1
  534. package/dist/core/web/public/next/assets/{useVerseTranscript-CqH51rt9.js → useVerseTranscript-DgbigUlV.js} +2 -2
  535. package/dist/core/web/public/next/assets/{useVerseTranscript-CqH51rt9.js.map → useVerseTranscript-DgbigUlV.js.map} +1 -1
  536. package/dist/core/web/public/next/assets/{verse-icons-DsG1dOQx.js → verse-icons-S56hIiXS.js} +2 -2
  537. package/dist/core/web/public/next/assets/{verse-icons-DsG1dOQx.js.map → verse-icons-S56hIiXS.js.map} +1 -1
  538. package/dist/core/web/public/next/assets/verse-model-DAVevkNe.js +2 -0
  539. package/dist/core/web/public/next/assets/{verse-model-JTuvRBVK.js.map → verse-model-DAVevkNe.js.map} +1 -1
  540. package/dist/core/web/public/next/assets/{warmup-Bv1RumVq.js → warmup-B_RAtNZ_.js} +3 -3
  541. package/dist/core/web/public/next/assets/{warmup-Bv1RumVq.js.map → warmup-B_RAtNZ_.js.map} +1 -1
  542. package/dist/core/web/public/next/assets/{why-seat-model-Cau8qG6O.js → why-seat-model-M_r0wX3b.js} +2 -2
  543. package/dist/core/web/public/next/assets/{why-seat-model-Cau8qG6O.js.map → why-seat-model-M_r0wX3b.js.map} +1 -1
  544. package/dist/core/web/public/next/assets/{workspace-model-D-SLfads.js → workspace-model-dji5-Al3.js} +2 -2
  545. package/dist/core/web/public/next/assets/{workspace-model-D-SLfads.js.map → workspace-model-dji5-Al3.js.map} +1 -1
  546. package/dist/core/web/public/next/index.html +1 -1
  547. package/dist/core/web/read-projection-worker.js +2 -1
  548. package/dist/core/web/read-projection-worker.js.map +1 -1
  549. package/dist/core/web/read-projections.d.ts +3 -0
  550. package/dist/core/web/read-projections.js +2 -2
  551. package/dist/core/web/read-projections.js.map +1 -1
  552. package/dist/core/web/visibility.js +3 -4
  553. package/dist/core/web/visibility.js.map +1 -1
  554. package/dist/release-dependency-inventory.json +1 -1
  555. package/package.json +3 -2
  556. package/dist/core/web/public/next/assets/Advanced-cVYTAsfB.js +0 -2
  557. package/dist/core/web/public/next/assets/AppsSection-DDqeSrty.js +0 -2
  558. package/dist/core/web/public/next/assets/AreaTrend-C32rUaxm.js +0 -2
  559. package/dist/core/web/public/next/assets/AreaTrend-C32rUaxm.js.map +0 -1
  560. package/dist/core/web/public/next/assets/AutonomySection-Dwn8UtOy.js +0 -2
  561. package/dist/core/web/public/next/assets/ChartFrame-BVuBe6RP.css +0 -1
  562. package/dist/core/web/public/next/assets/ChartFrame-DK_BDXCt.js +0 -2
  563. package/dist/core/web/public/next/assets/ChartFrame-DK_BDXCt.js.map +0 -1
  564. package/dist/core/web/public/next/assets/ChatSection-C9myY4ei.js +0 -3
  565. package/dist/core/web/public/next/assets/CommandPalette-DzPsth0u.js +0 -2
  566. package/dist/core/web/public/next/assets/CommandPalette-DzPsth0u.js.map +0 -1
  567. package/dist/core/web/public/next/assets/CommandSection-Brp73iox.js +0 -3
  568. package/dist/core/web/public/next/assets/CommandSection-Brp73iox.js.map +0 -1
  569. package/dist/core/web/public/next/assets/CommandSection-CvAVd1w4.css +0 -1
  570. package/dist/core/web/public/next/assets/DockHost-BcEq2qaS.js +0 -2
  571. package/dist/core/web/public/next/assets/FleetSection-BG8iGraG.js +0 -3
  572. package/dist/core/web/public/next/assets/FleetSection-BG8iGraG.js.map +0 -1
  573. package/dist/core/web/public/next/assets/Funnel-B88_wpA4.js +0 -2
  574. package/dist/core/web/public/next/assets/Funnel-B88_wpA4.js.map +0 -1
  575. package/dist/core/web/public/next/assets/GrowthSection-BvZezOsX.js +0 -2
  576. package/dist/core/web/public/next/assets/LeaderCard-BxxiEg4e.js +0 -2
  577. package/dist/core/web/public/next/assets/LeaderCard-BxxiEg4e.js.map +0 -1
  578. package/dist/core/web/public/next/assets/MatrixHeatmap-iPFEU2Tv.js +0 -2
  579. package/dist/core/web/public/next/assets/MatrixHeatmap-iPFEU2Tv.js.map +0 -1
  580. package/dist/core/web/public/next/assets/McpSection-nuXer4K-.js +0 -2
  581. package/dist/core/web/public/next/assets/MindSection-Dc5v5rH3.js +0 -2
  582. package/dist/core/web/public/next/assets/MindSection-Dc5v5rH3.js.map +0 -1
  583. package/dist/core/web/public/next/assets/NeedsYouDrawer-N1cWJjpz.js +0 -3
  584. package/dist/core/web/public/next/assets/NeedsYouDrawer-N1cWJjpz.js.map +0 -1
  585. package/dist/core/web/public/next/assets/NewChatDialog-BqFTWgHU.js +0 -3
  586. package/dist/core/web/public/next/assets/OnboardingGate-BZafM1Vf.js +0 -3
  587. package/dist/core/web/public/next/assets/PreviewPane-zT3gK9TD.js +0 -2
  588. package/dist/core/web/public/next/assets/ResourcePoolConsoleApp-CAxPQ4sZ.js +0 -7
  589. package/dist/core/web/public/next/assets/ResourcesChrome-BnhnEdJe.js +0 -3
  590. package/dist/core/web/public/next/assets/ResourcesChrome-BnhnEdJe.js.map +0 -1
  591. package/dist/core/web/public/next/assets/ResourcesDrawer-BAFGodny.js +0 -2
  592. package/dist/core/web/public/next/assets/ResourcesDrawer-BAFGodny.js.map +0 -1
  593. package/dist/core/web/public/next/assets/ResourcesDrawer-BCFJ2Uq8.css +0 -1
  594. package/dist/core/web/public/next/assets/SettingsSection-Bf74l0Gj.js.map +0 -1
  595. package/dist/core/web/public/next/assets/Sidebar-C9YtBYVZ.js +0 -3
  596. package/dist/core/web/public/next/assets/StatTile-BuLMsJMx.css +0 -1
  597. package/dist/core/web/public/next/assets/StatTile-CTX41O6y.js +0 -2
  598. package/dist/core/web/public/next/assets/StatTile-CTX41O6y.js.map +0 -1
  599. package/dist/core/web/public/next/assets/StepBand-jJTqYIdX.js +0 -2
  600. package/dist/core/web/public/next/assets/StepBand-jJTqYIdX.js.map +0 -1
  601. package/dist/core/web/public/next/assets/Swimlane-LrslsGxw.js +0 -2
  602. package/dist/core/web/public/next/assets/Swimlane-LrslsGxw.js.map +0 -1
  603. package/dist/core/web/public/next/assets/Swimlane-o6_Zsrnr.css +0 -1
  604. package/dist/core/web/public/next/assets/UniverseConsoleApp-BpphLqoz.js +0 -2
  605. package/dist/core/web/public/next/assets/UsageSection-CUlaKtws.js +0 -3
  606. package/dist/core/web/public/next/assets/UsageSection-CUlaKtws.js.map +0 -1
  607. package/dist/core/web/public/next/assets/VerseConsoleApp-DXDv1aOi.js +0 -3
  608. package/dist/core/web/public/next/assets/VerseConsoleApp-DXDv1aOi.js.map +0 -1
  609. package/dist/core/web/public/next/assets/Workspace-RXy0pqp6.js +0 -10
  610. package/dist/core/web/public/next/assets/actions-BZTidCXZ.js +0 -2
  611. package/dist/core/web/public/next/assets/actions-BmIf9avl.css +0 -1
  612. package/dist/core/web/public/next/assets/budget-queries-DFF-3ICg.js +0 -2
  613. package/dist/core/web/public/next/assets/charts-CzxWrZn7.js +0 -2
  614. package/dist/core/web/public/next/assets/charts-CzxWrZn7.js.map +0 -1
  615. package/dist/core/web/public/next/assets/charts-DhKTrQLw.css +0 -1
  616. package/dist/core/web/public/next/assets/command-catalog-ffCAijVY.js.map +0 -1
  617. package/dist/core/web/public/next/assets/context-queries-_9bFWb9v.js +0 -2
  618. package/dist/core/web/public/next/assets/global-y_exIyAP.js +0 -2
  619. package/dist/core/web/public/next/assets/guarded-runners-Cd4POBtI.js +0 -2
  620. package/dist/core/web/public/next/assets/mutations-CWlxEMEm.js +0 -2
  621. package/dist/core/web/public/next/assets/primitives-DMijqcu0.js +0 -1
  622. package/dist/core/web/public/next/assets/seat-subscription-BhUUz7VL.js +0 -2
  623. package/dist/core/web/public/next/assets/surface-prefetch-BXKjPsV5.js +0 -2
  624. package/dist/core/web/public/next/assets/surface-prefetch-BXKjPsV5.js.map +0 -1
  625. package/dist/core/web/public/next/assets/verse-model-JTuvRBVK.js +0 -2
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/command-keys.ts","../../../../../../src/web-ui/app/desktop-shell.ts","../../../../../../src/web-ui/components/primitives/Tooltip.tsx","../../../../../../src/core/verse/workbench-types.ts","../../../../../../src/web-ui/routes/verse/resources/resources-store.ts","../../../../../../src/web-ui/routes/verse/shell/preloaded.tsx","../../../../../../src/web-ui/routes/verse/shell/guarded-action.tsx","../../../../../../src/web-ui/routes/verse/shell/anchor-requests.ts","../../../../../../src/web-ui/routes/verse/shell/viewport.ts","../../../../../../src/web-ui/routes/verse/shell/dock-catalog.ts","../../../../../../src/web-ui/routes/verse/dock/dock-store.ts","../../../../../../src/web-ui/routes/verse/shell/nav-history.ts","../../../../../../src/web-ui/routes/verse/shell/section-modules.ts","../../../../../../src/web-ui/routes/verse/verse-ui-store.ts","../../../../../../src/web-ui/routes/verse/composer/composer-keys.ts","../../../../../../src/web-ui/routes/verse/shell/command-bus.ts","../../../../../../src/web-ui/routes/verse/shell/run-command.ts","../../../../../../src/web-ui/routes/verse/shell/section-visibility.tsx","../../../../../../src/web-ui/routes/verse/shell/skeletons.tsx","../../../../../../src/web-ui/routes/verse/shell/useActivity.ts","../../../../../../src/web-ui/routes/verse/useVerseUi.ts","../../../../../../src/web-ui/components/primitives/icon-base.tsx","../../../../../../src/web-ui/routes/verse/rail-icons.tsx","../../../../../../src/web-ui/routes/verse/percent-text.ts","../../../../../../src/web-ui/routes/verse/VerseApp.tsx","../../../../../../src/web-ui/routes/verse/verse-queries.ts","../../../../../../src/web-ui/design/fonts/AshlrSans-latin.woff2?url","../../../../../../src/web-ui/design/fonts/SpaceGrotesk-latin.woff2?url","../../../../../../src/web-ui/app/VerseConsoleApp.tsx"],"sourcesContent":["/**\n * routes/verse/shell/command-keys.ts — every workbench KEY: which command a\n * chord runs, in which scope, and how a native layer delivers it — plus the\n * pure helpers that match and print chords (unit C0; SPEC-310C §1).\n *\n * This is the half of the command catalog a key press needs, and the only\n * half on the chat first-paint path: the shell's and the chat's key\n * handlers, the rail's shortcut hints, the composer's keys and the desktop\n * menu bridge read it. Titles, palette groups, keywords, guards and the\n * keyless palette actions are command-catalog.ts, which loads with the ⌘K\n * palette (warmed after first paint) and binds its keyed rows from the\n * table below by id — so a key is still written exactly once, here. A key\n * that is not here does not exist.\n *\n * Pure data plus pure helpers — no React, no DOM beyond reading a\n * KeyboardEvent-shaped object.\n *\n * Adding a key: an entry in COMMAND_KEYS, and `...bind('<id>')` in the\n * command's catalog row. command-catalog.test.ts checks the two agree.\n */\nimport type { WorkbenchCommand } from './command-catalog.js';\n\n// ===========================================================================\n// Scopes — where a key is live\n// ===========================================================================\n\n/**\n * global anywhere in the workbench (except inside the palette, which\n * owns every key while open, and native text editing keys)\n * chat the Chat surface has focus (transcript, sidebar, dock)\n * composer the composer's text box has focus\n * drawer the Needs-you drawer list has focus (bare letters are safe\n * there: nothing in it takes text)\n * chart a chart card has focus\n */\nexport const COMMAND_SCOPES = ['global', 'chat', 'composer', 'drawer', 'chart'] as const;\nexport type CommandScope = (typeof COMMAND_SCOPES)[number];\n\n/**\n * The scopes live AT THE SAME TIME as each scope (itself included). A key\n * must be unique across every such set — the key test checks exactly this —\n * while two scopes that are never live together (the drawer's bare `Enter`\n * and the composer's) may reuse a key.\n */\nexport const SCOPE_LAYERS: Readonly<Record<CommandScope, readonly CommandScope[]>> = {\n global: ['global'],\n chat: ['chat', 'global'],\n composer: ['composer', 'chat', 'global'],\n drawer: ['drawer', 'global'],\n chart: ['chart', 'global'],\n};\n\n// ===========================================================================\n// Keys\n// ===========================================================================\n\n/**\n * One key chord. `key` is the UNSHIFTED US-layout name: a lowercase letter or\n * digit, a punctuation character, or one of the named keys below. `mod` is ⌘\n * on macOS and Ctrl elsewhere (\"CmdOrCtrl\"); `ctrl` is the literal ⌃ key.\n */\nexport interface KeyChord {\n key: string;\n mod?: true;\n ctrl?: true;\n shift?: true;\n alt?: true;\n}\n\nexport const NAMED_KEYS = ['enter', 'escape', 'tab', 'space', 'arrowup', 'arrowdown', 'arrowleft', 'arrowright', 'backspace'] as const;\n\n/**\n * Keys the page never binds, because macOS or the desktop shell answers them\n * first: the native menu's own accelerators live in app_menu.rs (the key test\n * parses that file), these are the system's.\n */\nexport const SYSTEM_RESERVED_CHORDS: readonly KeyChord[] = [\n { key: 'space', mod: true }, // Spotlight\n { key: 'space', ctrl: true }, // input sources\n { key: 'tab', mod: true }, // app switcher\n { key: 'tab', mod: true, shift: true },\n { key: '`', mod: true }, // cycle this app's windows\n { key: '3', mod: true, shift: true }, // screenshots\n { key: '4', mod: true, shift: true },\n { key: '5', mod: true, shift: true },\n];\n\n/**\n * How a key reaches the page when a native layer is involved.\n * menu the desktop menu owns the accelerator and forwards the\n * command (via `ashlr:desktop-command`); the page's own\n * handler only fires in a plain browser.\n * global-hotkey registered system-wide by the desktop app (C8); the page\n * never binds it — it is listed for the shortcuts overlay.\n */\nexport type NativeBinding =\n | { kind: 'menu'; desktopCommand: DesktopCommandName; accelerator: string }\n | { kind: 'global-hotkey'; accelerator: string };\n\n/** One command's keys: where they are live, the chords, and any native binding. */\nexport interface KeySpec {\n scope: CommandScope;\n /** Non-empty: a command without a key has no entry here. */\n keys: readonly KeyChord[];\n native?: NativeBinding;\n}\n\n/**\n * A chord as the table writes it: its modifiers and its key, `+`-joined —\n * 'mod+k', 'ctrl+shift+`', 'alt+arrowup', 'enter'. Text rather than KeyChord\n * objects because this table is on the chat first-paint path and the text\n * is a third of the bytes; parseChord() turns it into the KeyChord every\n * helper below takes.\n */\ntype ChordText = string;\n\n/**\n * Every key: by scope, then command id. The ids are the catalog's\n * (command-catalog.ts WORKBENCH_COMMANDS); each row there with keys takes\n * them from here with `bind(id)`.\n */\nconst KEYS = {\n global: {\n // Surfaces (⌘1 Command … ⌘5 Chat) and the gear tray\n 'surface.command': 'mod+1',\n 'surface.fleet': 'mod+2',\n 'surface.growth': 'mod+3',\n 'surface.mind': 'mod+4',\n 'surface.chat': 'mod+5',\n 'section.settings': 'mod+,',\n // Navigation\n 'palette.open': 'mod+k',\n 'needs-you.open': 'mod+j',\n 'shortcuts.open': 'mod+/',\n // ⌘. — not ⇧⌘R, which browsers keep for a hard reload.\n 'resources.toggle': 'mod+.',\n 'history.back': 'mod+[',\n 'history.forward': 'mod+]',\n 'chat.recent-next': 'ctrl+tab',\n 'chat.recent-prev': 'ctrl+shift+tab',\n 'rail.toggle-labels': 'mod+shift+\\\\',\n // Chats\n 'chat.new': 'mod+n',\n // App\n 'appearance.toggle-theme': 'mod+shift+l',\n 'app.summon': 'ctrl+alt+space',\n },\n chat: {\n 'chat.sidebar': 'mod+b',\n 'chat.find': 'mod+f',\n 'chat.turn-prev': 'alt+arrowup',\n 'chat.turn-next': 'alt+arrowdown',\n // Dock\n 'dock.toggle': 'mod+\\\\',\n 'dock.terminal': 'ctrl+`',\n 'dock.terminal-new': 'ctrl+shift+`',\n 'dock.preview': 'mod+shift+b',\n 'dock.diff': 'mod+shift+d',\n // Composer menus (open from anywhere in the chat)\n 'composer.permission': 'mod+shift+m',\n 'composer.model': 'mod+shift+i',\n 'composer.effort': 'mod+shift+e',\n 'composer.attach': 'mod+u',\n },\n composer: {\n 'composer.send': 'enter',\n 'composer.stop-and-send': 'mod+shift+enter',\n 'composer.stop': 'escape',\n },\n drawer: {\n 'drawer.next': 'j',\n 'drawer.prev': 'k',\n 'drawer.open': 'enter',\n 'drawer.approve': 'a',\n 'drawer.reject': 'r',\n 'drawer.veto': 'v',\n 'drawer.done': 'e',\n 'drawer.select': 'x',\n 'drawer.split-prev': 'h',\n 'drawer.split-next': 'l',\n },\n chart: {\n 'chart.table': 't',\n },\n} as const satisfies Record<CommandScope, Record<string, ChordText>>;\n\ntype KeyTable = typeof KEYS;\nexport type KeyedCommandId = { [S in keyof KeyTable]: keyof KeyTable[S] }[keyof KeyTable];\n\n/** The keys a native layer delivers or owns (C8). */\nconst NATIVE: Readonly<Partial<Record<KeyedCommandId, NativeBinding>>> = {\n 'section.settings': { kind: 'menu', desktopCommand: 'open-settings', accelerator: 'CmdOrCtrl+,' },\n 'appearance.toggle-theme': { kind: 'menu', desktopCommand: 'toggle-theme', accelerator: 'Shift+CmdOrCtrl+L' },\n 'app.summon': { kind: 'global-hotkey', accelerator: 'Control+Alt+Space' },\n};\n\n/** 'mod+shift+b' → { key: 'b', mod: true, shift: true }. */\nexport function parseChord(text: ChordText): KeyChord {\n const parts = text.split('+');\n const chord: KeyChord = { key: parts.pop()! };\n for (const modifier of parts) chord[modifier as 'mod' | 'ctrl' | 'shift' | 'alt'] = true;\n return chord;\n}\n\n/** A command's keys, with its id. */\nexport interface KeyBinding extends KeySpec {\n id: KeyedCommandId;\n}\n\nconst BINDINGS: readonly KeyBinding[] = (Object.entries(KEYS) as Array<[CommandScope, Record<string, ChordText>]>).flatMap(([scope, byId]) =>\n Object.entries(byId).map(([id, text]) => {\n const native = NATIVE[id as KeyedCommandId];\n return { id: id as KeyedCommandId, scope, keys: [parseChord(text)], ...(native ? { native } : {}) };\n }),\n);\n\n/** Every command's keys, by id (the catalog's bind() reads it). */\nexport const COMMAND_KEYS = Object.fromEntries(BINDINGS.map((b) => [b.id, b])) as unknown as Readonly<Record<KeyedCommandId, KeySpec>>;\n\nconst BINDING_BY_ID: ReadonlyMap<string, KeyBinding> = new Map(BINDINGS.map((b) => [b.id, b]));\n\n/** The keys of command `id`; null for a command with none (or no such command). */\nexport function keyBinding(id: string): KeyBinding | null {\n return BINDING_BY_ID.get(id) ?? null;\n}\n\n/** The chord a hint prints for `id` (its first); undefined when it has no key. */\nexport function commandChord(id: string): KeyChord | undefined {\n return BINDING_BY_ID.get(id)?.keys[0];\n}\n\n// ===========================================================================\n// The window event (units that are not on the command bus)\n// ===========================================================================\n\n/**\n * The window event the palette (C1) and the native menu bridge dispatch to\n * run a catalog command on a surface that listens for it instead of\n * registering on the command bus:\n * window.dispatchEvent(new CustomEvent(WORKBENCH_COMMAND_EVENT, { detail: { id } }))\n * The composer (C3) answers the `composer.*` ids it implements.\n *\n * It lives HERE, beside the ids it carries, so the shell (run-command.ts)\n * does not have to import a composer module to name its own event —\n * composer/composer-keys.ts re-exports it for its existing importers.\n */\nexport const WORKBENCH_COMMAND_EVENT = 'ashlr:command';\n\n// ===========================================================================\n// Desktop commands (C8 → page)\n// ===========================================================================\n\n/**\n * What the desktop shell may send the page (`ashlr:desktop-command`), and the\n * catalog command each one runs. C8 dispatches these from the menu bar, the\n * tray, a clicked notification (focus regained within 60 s) and the global\n * hotkey; C1 runs them. `open-session:<id>` carries a session id.\n */\nexport const DESKTOP_COMMAND_NAMES = ['open-settings', 'toggle-theme', 'open-needs-you', 'new-chat', 'focus-composer'] as const;\n// Its own literal list (not `keyof typeof DESKTOP_COMMANDS`): NativeBinding\n// names it, COMMAND_KEYS uses NativeBinding, and DESKTOP_COMMANDS is typed by\n// KeyedCommandId — deriving it from the map would make the types circular.\nexport type DesktopCommandName = (typeof DESKTOP_COMMAND_NAMES)[number];\n\n/** Each runs a command with a key (the menu shows it), so the ids are keyed ones. */\nexport const DESKTOP_COMMANDS: Readonly<Record<DesktopCommandName, KeyedCommandId>> = {\n 'open-settings': 'section.settings',\n 'toggle-theme': 'appearance.toggle-theme',\n 'open-needs-you': 'needs-you.open',\n 'new-chat': 'chat.new',\n 'focus-composer': 'app.summon',\n};\n\n/** `open-session:<id>` — the notification for a finished or failed chat. */\nexport const OPEN_SESSION_COMMAND_RE = /^open-session:([A-Za-z0-9._-]{1,128})$/;\n\nexport type ParsedDesktopCommand =\n | { kind: 'command'; name: DesktopCommandName; commandId: KeyedCommandId }\n | { kind: 'open-session'; sessionId: string };\n\n/** Parse a raw desktop command string; null for anything unknown (never guessed). */\nexport function parseDesktopCommand(raw: unknown): ParsedDesktopCommand | null {\n if (typeof raw !== 'string') return null;\n if (Object.prototype.hasOwnProperty.call(DESKTOP_COMMANDS, raw)) {\n const name = raw as DesktopCommandName;\n return { kind: 'command', name, commandId: DESKTOP_COMMANDS[name] };\n }\n const session = OPEN_SESSION_COMMAND_RE.exec(raw);\n return session ? { kind: 'open-session', sessionId: session[1]! } : null;\n}\n\n// ===========================================================================\n// The whole catalog, once it has loaded\n// ===========================================================================\n\nlet catalogLookup: ((id: string) => WorkbenchCommand | null) | null = null;\n\n/**\n * command-catalog.ts registers its lookup here when its chunk evaluates.\n * Everything that can name a keyless command — the palette, onboarding's\n * \"Approve grant…\" — imports the catalog, so by the time such an id reaches\n * run-command.ts the lookup is in; the warm-up fetches it after first paint\n * regardless.\n */\nexport function registerCommandCatalog(lookup: (id: string) => WorkbenchCommand | null): void {\n catalogLookup = lookup;\n}\n\n/**\n * The full catalog entry for `id` — null before the catalog has loaded (a\n * key's id then still resolves through keyBinding()) or for an id the\n * catalog does not have.\n */\nexport function loadedCommand(id: string): WorkbenchCommand | null {\n return catalogLookup ? catalogLookup(id) : null;\n}\n\n/** Has the catalog registered its lookup yet? */\nexport function commandCatalogLoaded(): boolean {\n return catalogLookup !== null;\n}\n\n// ===========================================================================\n// Matching and display\n// ===========================================================================\n\nexport type KeyPlatform = 'mac' | 'other';\n\n/** The platform the page runs on, for ⌘ vs Ctrl. */\nexport function detectKeyPlatform(): KeyPlatform {\n if (typeof navigator === 'undefined') return 'mac';\n const nav = navigator as Navigator & { userAgentData?: { platform?: string } };\n const platform = nav.userAgentData?.platform ?? nav.platform ?? '';\n return /mac|iphone|ipad|ipod/i.test(platform) ? 'mac' : 'other';\n}\n\n/** The part of a KeyboardEvent matching needs (so tests can pass plain objects). */\nexport interface KeyEventLike {\n key: string;\n code?: string;\n metaKey: boolean;\n ctrlKey: boolean;\n shiftKey: boolean;\n altKey: boolean;\n}\n\nconst CODE_CHARS: Readonly<Record<string, string>> = {\n Backslash: '\\\\',\n Backquote: '`',\n BracketLeft: '[',\n BracketRight: ']',\n Slash: '/',\n Comma: ',',\n Period: '.',\n Semicolon: ';',\n Quote: \"'\",\n Minus: '-',\n Equal: '=',\n Space: 'space',\n};\n\nconst KEY_ALIASES: Readonly<Record<string, string>> = {\n ' ': 'space',\n spacebar: 'space',\n esc: 'escape',\n return: 'enter',\n up: 'arrowup',\n down: 'arrowdown',\n left: 'arrowleft',\n right: 'arrowright',\n};\n\n/**\n * The event's key as a chord `key`. Letters come from `event.key` (so ⌘K is\n * ⌘K on any keyboard layout); digits and punctuation come from `event.code`\n * when it is known, because Shift and ⌥ change `event.key` (⇧\\ reads \"|\",\n * ⌥J reads \"∆\") while the chord names the unshifted key.\n */\nexport function eventKeyName(event: Pick<KeyEventLike, 'key' | 'code'>): string {\n const key = event.key ?? '';\n if (/^[a-z]$/i.test(key)) return key.toLowerCase();\n const code = event.code ?? '';\n if (/^Digit[0-9]$/.test(code)) return code.slice(5);\n if (CODE_CHARS[code]) return CODE_CHARS[code]!;\n if (/^Key[A-Z]$/.test(code)) return code.slice(3).toLowerCase();\n const lower = key.toLowerCase();\n return KEY_ALIASES[lower] ?? lower;\n}\n\n/**\n * Does `event` press `chord`? Modifiers must match EXACTLY (⌘K is not ⇧⌘K).\n * Off macOS, `mod` and `ctrl` are both the Ctrl key and ⌘/Win never matches.\n */\nexport function chordMatches(event: KeyEventLike, chord: KeyChord, platform: KeyPlatform = detectKeyPlatform()): boolean {\n if (platform === 'mac') {\n if (Boolean(chord.mod) !== event.metaKey) return false;\n if (Boolean(chord.ctrl) !== event.ctrlKey) return false;\n } else {\n if (event.metaKey) return false;\n if (Boolean(chord.mod || chord.ctrl) !== event.ctrlKey) return false;\n }\n if (Boolean(chord.shift) !== event.shiftKey) return false;\n if (Boolean(chord.alt) !== event.altKey) return false;\n return eventKeyName(event) === chord.key;\n}\n\n/**\n * The key binding a key press runs, given the scopes live right now\n * (innermost first, e.g. ['composer', 'chat', 'global']). Null when nothing\n * is bound — the caller must then leave the event alone.\n * (command-catalog.ts matchCommand returns the full catalog entry.)\n */\nexport function matchKey(event: KeyEventLike, liveScopes: readonly CommandScope[], platform: KeyPlatform = detectKeyPlatform()): KeyBinding | null {\n for (const scope of liveScopes) {\n for (const binding of BINDINGS) {\n if (binding.scope !== scope) continue;\n // The page never handles a system-wide hotkey itself.\n if (binding.native?.kind === 'global-hotkey') continue;\n if (binding.keys.some((chord) => chordMatches(event, chord, platform))) return binding;\n }\n }\n return null;\n}\n\n/** Stable identity for a chord (\"mod+shift+b\"), for de-duplication and tests. */\nexport function chordId(chord: KeyChord): string {\n return [chord.ctrl ? 'ctrl' : '', chord.alt ? 'alt' : '', chord.shift ? 'shift' : '', chord.mod ? 'mod' : '', chord.key].filter(Boolean).join('+');\n}\n\nconst MAC_KEY_GLYPHS: Readonly<Record<string, string>> = {\n enter: '↩',\n escape: 'Esc',\n tab: '⇥',\n space: 'Space',\n arrowup: '↑',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n backspace: '⌫',\n};\n\nconst OTHER_KEY_NAMES: Readonly<Record<string, string>> = {\n enter: 'Enter',\n escape: 'Esc',\n tab: 'Tab',\n space: 'Space',\n arrowup: '↑',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n backspace: 'Backspace',\n};\n\n/**\n * How a chord is printed. macOS uses Apple's modifier order (⌃ ⌥ ⇧ ⌘) with\n * no separators (\"⇧⌘B\", \"⌃`\", \"⌥↑\"); elsewhere \"Ctrl+Shift+B\".\n */\nexport function formatChord(chord: KeyChord, platform: KeyPlatform = detectKeyPlatform()): string {\n const key = chord.key.length === 1 ? chord.key.toUpperCase() : (platform === 'mac' ? MAC_KEY_GLYPHS : OTHER_KEY_NAMES)[chord.key] ?? chord.key;\n if (platform === 'mac') {\n return `${chord.ctrl ? '⌃' : ''}${chord.alt ? '⌥' : ''}${chord.shift ? '⇧' : ''}${chord.mod ? '⌘' : ''}${key}`;\n }\n const parts = [chord.mod || chord.ctrl ? 'Ctrl' : '', chord.alt ? 'Alt' : '', chord.shift ? 'Shift' : '', key].filter(Boolean);\n return parts.join('+');\n}\n\n/**\n * Tauri's accelerator string for a chord (\"Shift+CmdOrCtrl+L\") — what C8\n * writes in app_menu.rs, and what the key test compares against.\n */\nexport function chordAccelerator(chord: KeyChord): string {\n const TAURI_KEYS: Readonly<Record<string, string>> = {\n enter: 'Enter',\n escape: 'Escape',\n tab: 'Tab',\n space: 'Space',\n arrowup: 'Up',\n arrowdown: 'Down',\n arrowleft: 'Left',\n arrowright: 'Right',\n backspace: 'Backspace',\n };\n const key = TAURI_KEYS[chord.key] ?? (chord.key.length === 1 ? chord.key.toUpperCase() : chord.key);\n return [chord.shift ? 'Shift' : '', chord.ctrl ? 'Control' : '', chord.alt ? 'Alt' : '', chord.mod ? 'CmdOrCtrl' : '', key].filter(Boolean).join('+');\n}\n","/**\n * app/desktop-shell.ts — the web half of the desktop shell contract.\n *\n * The native side (`desktop/src-tauri/src/shell_contract.{rs,js}`, documented\n * in `desktop/README.md` → \"Desktop shell contract\") injects markers, CSS\n * variables, a drag-region mirror, a command event (menu bar, tray, clicked\n * notifications, the global hotkey), the desktop state behind Settings ▸\n * Desktop, and two page→native calls (theme, preferences). This module is the\n * ONLY place the web UI touches any of it, so the console keeps exactly one\n * seam to the desktop app and never references Tauri.\n *\n * Everything here is inert in a browser: the markers are absent, the global\n * is undefined, and the event is never dispatched. No caller needs a\n * conditional build or a feature flag.\n */\nimport { useSyncExternalStore } from 'react';\nimport type { ThemePreference } from '../data/theme-store.js';\nimport { parseDesktopCommand, type ParsedDesktopCommand } from '../routes/verse/shell/command-keys.js';\n\n/**\n * The two commands the native MENU BAR sends (the 3.9 contract).\n * `subscribeDesktopCommands` keeps delivering exactly these; everything the\n * 3.10 shell sends (tray, notifications, hotkey) arrives through\n * `subscribeShellCommands`, parsed by the command catalog.\n */\nexport type DesktopCommand = 'open-settings' | 'toggle-theme';\n\nconst DESKTOP_COMMAND_EVENT = 'ashlr:desktop-command';\nconst COMMANDS: readonly DesktopCommand[] = ['open-settings', 'toggle-theme'];\n\n/**\n * True only inside the Tauri window, where `shell_contract.js` has stamped\n * `data-app-shell=\"desktop\"` on `<html>`. Layout does NOT branch on this —\n * the CSS reads `--app-titlebar-height` / `--app-traffic-light-inset` with a\n * `0px` fallback instead, so there is one layout, not two.\n */\nexport function isDesktopShell(): boolean {\n try {\n return document.documentElement.getAttribute('data-app-shell') === 'desktop';\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve a three-state preference down to what is actually painted, which is\n * what the native side needs: it stores the value and paints the window\n * background with the matching canvas colour on the NEXT launch, before the\n * webview has painted. 'system' therefore has to become a concrete answer\n * here, not be passed through.\n */\nexport function resolveTheme(preference: ThemePreference): 'light' | 'dark' {\n if (preference === 'light' || preference === 'dark') return preference;\n try {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n } catch {\n // jsdom and old webviews without matchMedia: light is the token default.\n return 'light';\n }\n}\n\ninterface DesktopBridge {\n reportTheme?: (theme: 'light' | 'dark') => void;\n getState?: () => unknown;\n setPreference?: (name: DesktopPreference, value: boolean) => boolean;\n}\n\nfunction bridge(): DesktopBridge | undefined {\n return (window as unknown as { __ASHLR_DESKTOP__?: DesktopBridge }).__ASHLR_DESKTOP__;\n}\n\n/**\n * Tell the native window which theme is painted, so the next cold launch does\n * not flash white. Optional by contract, best-effort by implementation: a\n * browser has no bridge, and a throwing bridge must never break a render.\n * Repeat calls with the same value are dropped on the native side.\n */\nexport function reportThemeToShell(preference: ThemePreference): void {\n try {\n bridge()?.reportTheme?.(resolveTheme(preference));\n } catch {\n /* the shell is an enhancement; never let it fail the page */\n }\n}\n\nfunction isDesktopCommand(value: unknown): value is DesktopCommand {\n return typeof value === 'string' && (COMMANDS as readonly string[]).includes(value);\n}\n\n/**\n * Listen for menu commands. Returns an unsubscribe function, so a component\n * can register it from an effect.\n *\n * Unknown commands are ignored rather than forwarded: the native menu can\n * grow ahead of the web UI, and an unhandled command is specified to be inert\n * rather than an error.\n */\nexport function subscribeDesktopCommands(handler: (command: DesktopCommand) => void): () => void {\n function onCommand(event: Event): void {\n const detail = (event as CustomEvent<{ command?: unknown }>).detail;\n const command = detail?.command;\n if (isDesktopCommand(command)) handler(command);\n }\n window.addEventListener(DESKTOP_COMMAND_EVENT, onCommand);\n return () => window.removeEventListener(DESKTOP_COMMAND_EVENT, onCommand);\n}\n\n/**\n * Every desktop command — menu bar, tray, a clicked notification, the global\n * hotkey — parsed by the command catalog (`parseDesktopCommand`): a catalog\n * command (`open-needs-you`, `new-chat`, `focus-composer`, …) or\n * `open-session:<id>`. Unknown strings are dropped, never guessed. This is\n * what the workbench shell (C1) listens to; `subscribeDesktopCommands` stays\n * for the 3.9 menu-only contract.\n */\nexport function subscribeShellCommands(handler: (command: ParsedDesktopCommand) => void): () => void {\n function onCommand(event: Event): void {\n const parsed = parseDesktopCommand((event as CustomEvent<{ command?: unknown }>).detail?.command);\n if (parsed) handler(parsed);\n }\n window.addEventListener(DESKTOP_COMMAND_EVENT, onCommand);\n return () => window.removeEventListener(DESKTOP_COMMAND_EVENT, onCommand);\n}\n\n// ===========================================================================\n// Desktop state — Settings ▸ Desktop (V3.10, C8)\n// ===========================================================================\n\n/** Preferences Settings ▸ Desktop may change (desktop_prefs.rs `PrefsPatch`). */\nexport type DesktopPreference = 'globalHotkey' | 'notifications';\n\n/**\n * What the desktop app reports (desktop_prefs.rs `DesktopStateView`). Every\n * string is the shell's own copy — nothing from the server.\n */\nexport interface DesktopState {\n hotkey: {\n /** The operator's choice. */\n enabled: boolean;\n /** Whether macOS actually gave Ashlr the chord. */\n registered: boolean;\n /** Display form, e.g. \"⌃⌥Space\". */\n accelerator: string;\n /** Why `registered` is false while `enabled` is true; null otherwise. */\n error: string | null;\n };\n notifications: {\n enabled: boolean;\n /**\n * `native` — a signed build, banners come from Ashlr.\n * `script` — an unsigned build, banners come through osascript and show\n * as Script Editor (say so beside the toggle).\n */\n delivery: 'native' | 'script';\n };\n}\n\nconst DESKTOP_STATE_EVENT = 'ashlr:desktop-state';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Boundary check: the value crossed from another runtime. */\nexport function isDesktopState(value: unknown): value is DesktopState {\n if (!isRecord(value) || !isRecord(value['hotkey']) || !isRecord(value['notifications'])) return false;\n const h = value['hotkey'];\n const n = value['notifications'];\n return (\n typeof h['enabled'] === 'boolean' &&\n typeof h['registered'] === 'boolean' &&\n typeof h['accelerator'] === 'string' &&\n h['accelerator'].length > 0 &&\n h['accelerator'].length <= 32 &&\n (h['error'] === null || (typeof h['error'] === 'string' && h['error'].length <= 300)) &&\n typeof n['enabled'] === 'boolean' &&\n (n['delivery'] === 'native' || n['delivery'] === 'script')\n );\n}\n\n// One module-level listener feeds a cached snapshot, so readers (and\n// useSyncExternalStore, which needs a STABLE snapshot) never see a stale or\n// freshly-copied object.\nlet cachedState: DesktopState | null | undefined;\nlet listening = false;\nconst stateListeners = new Set<(state: DesktopState) => void>();\n\nfunction readBridgeState(): DesktopState | null {\n try {\n const raw = bridge()?.getState?.();\n return isDesktopState(raw) ? raw : null;\n } catch {\n return null;\n }\n}\n\nfunction onDesktopState(event: Event): void {\n const detail = (event as CustomEvent<unknown>).detail;\n if (!isDesktopState(detail)) return;\n cachedState = detail;\n for (const listener of [...stateListeners]) listener(detail);\n}\n\nfunction ensureListening(): void {\n if (listening) return;\n listening = true;\n window.addEventListener(DESKTOP_STATE_EVENT, onDesktopState);\n}\n\n/** The latest desktop state, or null in a browser (and before native answered). */\nexport function getDesktopState(): DesktopState | null {\n ensureListening();\n if (cachedState === undefined || cachedState === null) cachedState = readBridgeState();\n return cachedState;\n}\n\n/** Called with every new state native sends. Returns an unsubscribe function. */\nexport function subscribeDesktopState(handler: (state: DesktopState) => void): () => void {\n ensureListening();\n stateListeners.add(handler);\n return () => {\n stateListeners.delete(handler);\n };\n}\n\n/**\n * Ask the desktop app to change a preference. True when the request was sent\n * (a desktop bridge exists), false in a browser. The answer arrives as a new\n * state — e.g. a hotkey another app holds comes back\n * `{ enabled: true, registered: false, error }` — so render from state, never\n * from the value you asked for.\n */\nexport function setDesktopPreference(name: DesktopPreference, value: boolean): boolean {\n if (name !== 'globalHotkey' && name !== 'notifications') return false;\n if (typeof value !== 'boolean') return false;\n try {\n return bridge()?.setPreference?.(name, value) === true;\n } catch {\n return false;\n }\n}\n\nfunction subscribeStore(onChange: () => void): () => void {\n return subscribeDesktopState(() => onChange());\n}\n\n/**\n * React: the live desktop state, or null in a browser. Settings ▸ Desktop\n * renders its hotkey and notification rows from this and hides them (or says\n * \"available in the desktop app\") when it is null.\n */\nexport function useDesktopState(): DesktopState | null {\n return useSyncExternalStore(subscribeStore, getDesktopState, () => null);\n}\n\n/** Tests only: forget the cached state and the module listener. */\nexport function resetDesktopStateForTests(): void {\n if (listening) window.removeEventListener(DESKTOP_STATE_EVENT, onDesktopState);\n listening = false;\n cachedState = undefined;\n stateListeners.clear();\n}\n","/**\n * components/primitives/Tooltip.tsx — the label for an icon-only control and\n * the numbers behind a 2px meter line.\n *\n * Rules it enforces so a tooltip never becomes the only way to read the UI:\n * - it shows on HOVER *and* on keyboard FOCUS (a mouse-only tooltip is\n * invisible to keyboard operators);\n * - Escape dismisses it while the trigger keeps focus (WCAG 1.4.13);\n * - the trigger is wired with aria-describedby, so the content is\n * announced rather than merely drawn;\n * - it carries description, never the accessible NAME — icon buttons still\n * need their own aria-label.\n *\n * WHY IT RENDERS THROUGH A PORTAL. This used to be an absolutely-positioned\n * span inside a `position: relative` wrapper, which is correct right up until\n * the trigger lives inside something that clips — and the first real consumer,\n * the Verse rail, is exactly that: a 56px flex column inside a grid shell with\n * its own stacking context. A bubble anchored there is cut off at the rail's\n * edge, which is the failure the native `title=` tooltip already had. Rendering\n * into `document.body` at `position: fixed`, positioned from the trigger's\n * bounding rect, means NO ancestor can clip it: not `overflow: hidden`, not a\n * `transform`, not a lower `z-index` on a sibling pane.\n *\n * The Popover API (`popover=\"manual\"` + the top layer) would do the same job\n * without the portal, but it is not yet safe to depend on across the WebKit\n * version bundled with the Tauri shell, and it still needs manual positioning\n * without `anchor-name`. The portal is the boring, verifiable option.\n *\n * TWO PROP SPELLINGS, on purpose. `label` + `shortcut` is the shape icon\n * controls want, and is what new call sites should use. `content` predates it\n * and takes arbitrary nodes (the Meter's numbers, for one), so it stays as the\n * other half of a union rather than being renamed out from under its callers.\n * Exactly one of the two is required.\n */\nimport {\n cloneElement,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type FocusEvent as ReactFocusEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport styles from './Tooltip.module.css';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Hovering across a rail of six icons must not strobe six bubbles, so the\n * POINTER waits. Focus does not: a keyboard operator has already committed to\n * the control, and a delay there just reads as lag.\n */\nconst OPEN_DELAY_MS = 160;\n/** Distance between the trigger's edge and the bubble. */\nconst GAP = 8;\n/** Smallest distance the bubble may sit from the viewport edge. */\nconst MARGIN = 8;\n\n/** The props Tooltip clones onto its trigger. */\ninterface TooltipTriggerProps {\n 'aria-describedby'?: string;\n /** Read, never set: a disabled trigger fires no pointer events, so the wrapper must take over. */\n disabled?: boolean;\n onMouseEnter?: (event: ReactMouseEvent<HTMLElement>) => void;\n onMouseLeave?: (event: ReactMouseEvent<HTMLElement>) => void;\n onFocus?: (event: ReactFocusEvent<HTMLElement>) => void;\n onBlur?: (event: ReactFocusEvent<HTMLElement>) => void;\n}\n\ninterface TooltipCommonProps {\n /** Rendered as a distinct keycap beside the label, e.g. `⌘2`. */\n shortcut?: string;\n placement?: TooltipPlacement;\n /** Skip the tooltip entirely without changing the tree (e.g. the label is already visible). */\n disabled?: boolean;\n className?: string;\n /** The trigger. Tooltip adds no layout box of its own around it. */\n children: ReactElement<TooltipTriggerProps>;\n}\n\nexport type TooltipProps = TooltipCommonProps &\n (\n | { label: string; content?: undefined }\n | { label?: undefined; content: ReactNode }\n );\n\ninterface Position {\n left: number;\n top: number;\n placement: TooltipPlacement;\n}\n\n/** Placement order: the caller's choice first, then its opposite, then the rest. */\nfunction candidates(preferred: TooltipPlacement): TooltipPlacement[] {\n const opposite: Record<TooltipPlacement, TooltipPlacement> = {\n top: 'bottom', bottom: 'top', left: 'right', right: 'left',\n };\n const rest = (['top', 'bottom', 'right', 'left'] as const).filter(\n (p) => p !== preferred && p !== opposite[preferred],\n );\n return [preferred, opposite[preferred], ...rest];\n}\n\nfunction place(placement: TooltipPlacement, trigger: DOMRect, w: number, h: number): { left: number; top: number } {\n switch (placement) {\n case 'top': return { left: trigger.left + trigger.width / 2 - w / 2, top: trigger.top - h - GAP };\n case 'bottom': return { left: trigger.left + trigger.width / 2 - w / 2, top: trigger.bottom + GAP };\n case 'left': return { left: trigger.left - w - GAP, top: trigger.top + trigger.height / 2 - h / 2 };\n case 'right': return { left: trigger.right + GAP, top: trigger.top + trigger.height / 2 - h / 2 };\n }\n}\n\n/**\n * First placement whose bubble fits the viewport wins; if none fits we keep\n * the preferred one and clamp, which is still readable — a bubble nudged\n * inward beats a bubble half off-screen.\n */\nfunction resolve(trigger: DOMRect, w: number, h: number, preferred: TooltipPlacement): Position {\n const vw = window.innerWidth || 0;\n const vh = window.innerHeight || 0;\n let fallback: Position | null = null;\n for (const placement of candidates(preferred)) {\n const { left, top } = place(placement, trigger, w, h);\n if (!fallback) fallback = { left, top, placement };\n const fits = left >= MARGIN && top >= MARGIN && left + w <= vw - MARGIN && top + h <= vh - MARGIN;\n if (fits) return { left, top, placement };\n }\n const chosen = fallback!;\n return {\n placement: chosen.placement,\n left: Math.max(MARGIN, Math.min(chosen.left, vw - w - MARGIN)),\n top: Math.max(MARGIN, Math.min(chosen.top, vh - h - MARGIN)),\n };\n}\n\nexport function Tooltip(props: TooltipProps) {\n const { label, content, shortcut, placement = 'top', disabled = false, className, children } = props;\n const [open, setOpen] = useState(false);\n const [position, setPosition] = useState<Position | null>(null);\n const id = useId();\n const triggerRef = useRef<HTMLElement | null>(null);\n const bubbleRef = useRef<HTMLDivElement | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancelTimer = useCallback(() => {\n if (timer.current !== null) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n }, []);\n\n const close = useCallback(() => {\n cancelTimer();\n setOpen(false);\n setPosition(null);\n }, [cancelTimer]);\n\n // Unmounting mid-delay must not fire setOpen on a dead component.\n useEffect(() => cancelTimer, [cancelTimer]);\n\n const reposition = useCallback(() => {\n const trigger = triggerRef.current;\n const bubble = bubbleRef.current;\n if (!trigger || !bubble) return;\n const rect = trigger.getBoundingClientRect();\n setPosition(resolve(rect, bubble.offsetWidth, bubble.offsetHeight, placement));\n }, [placement]);\n\n // Measure AFTER the bubble exists but BEFORE paint, so it is never seen at\n // its unpositioned origin first.\n useLayoutEffect(() => {\n if (!open) return;\n reposition();\n }, [open, reposition]);\n\n // Escape closes while the trigger keeps focus (WCAG 1.4.13). Held at the\n // document so it works wherever the keystroke lands, and a scroll or resize\n // under an open bubble re-anchors it rather than leaving it stranded.\n useEffect(() => {\n if (!open) return;\n function onKeyDown(event: globalThis.KeyboardEvent) {\n if (event.key === 'Escape') close();\n }\n document.addEventListener('keydown', onKeyDown);\n window.addEventListener('resize', reposition);\n // Capture: a scroll inside any ancestor moves the trigger too, and scroll\n // events from those do not bubble to window.\n window.addEventListener('scroll', reposition, true);\n return () => {\n document.removeEventListener('keydown', onKeyDown);\n window.removeEventListener('resize', reposition);\n window.removeEventListener('scroll', reposition, true);\n };\n }, [open, close, reposition]);\n\n const body = label ?? content;\n const empty = body === null || body === undefined || body === '';\n if (disabled || empty) return children;\n\n function openAfterDelay(element: HTMLElement) {\n triggerRef.current = element;\n cancelTimer();\n timer.current = setTimeout(() => {\n timer.current = null;\n setOpen(true);\n }, OPEN_DELAY_MS);\n }\n\n function openNow(element: HTMLElement) {\n triggerRef.current = element;\n cancelTimer();\n setOpen(true);\n }\n\n const triggerDisabled = children.props.disabled === true;\n const describedBy = open ? id : undefined;\n\n const trigger = cloneElement(children, {\n 'aria-describedby': describedBy,\n onMouseEnter: (event: ReactMouseEvent<HTMLElement>) => {\n children.props.onMouseEnter?.(event);\n openAfterDelay(event.currentTarget);\n },\n onMouseLeave: (event: ReactMouseEvent<HTMLElement>) => {\n children.props.onMouseLeave?.(event);\n close();\n },\n onFocus: (event: ReactFocusEvent<HTMLElement>) => {\n children.props.onFocus?.(event);\n openNow(event.currentTarget);\n },\n onBlur: (event: ReactFocusEvent<HTMLElement>) => {\n children.props.onBlur?.(event);\n close();\n },\n });\n\n return (\n <>\n {/*\n `display: contents` — the wrapper exists in the DOM but generates no\n box, so it cannot disturb the trigger's own flex/grid placement. It is\n here only to carry the describedby relationship at the trigger's parent\n as well as on the trigger itself; the behaviour all lives on the cloned\n child, which is what actually has a hover target.\n */}\n <span\n className={`${styles.wrapper} ${className ?? ''}`}\n aria-describedby={describedBy}\n /*\n A DISABLED trigger emits no pointer events at all, so handlers on the\n cloned child can never fire and its tooltip could never open — which\n is precisely the tooltip that matters, since it is the one explaining\n WHY the control is disabled. When the child is disabled the wrapper\n stops being `display: contents`, takes a box of its own, and carries\n the hover itself. Focus is not mirrored here: a disabled control is\n not focusable, so there is no focus to mirror.\n */\n data-disabled-trigger={triggerDisabled ? 'true' : undefined}\n onMouseOver={triggerDisabled ? (event) => openNow(event.currentTarget) : undefined}\n onMouseOut={triggerDisabled ? close : undefined}\n >\n {trigger}\n </span>\n {open\n ? createPortal(\n <div\n ref={bubbleRef}\n role=\"tooltip\"\n id={id}\n className={styles.bubble}\n data-placement={position?.placement ?? placement}\n style={{\n // Inline, though the stylesheet says it too: `left`/`top`\n // below are VIEWPORT coordinates, and they only mean that\n // under `fixed`. The two travel together or the bubble lands\n // somewhere arbitrary.\n position: 'fixed',\n left: position ? `${position.left}px` : '0px',\n top: position ? `${position.top}px` : '0px',\n // Hidden until measured, but still laid out — the measurement\n // needs a real offsetWidth, and `visibility` gives one where\n // `display: none` would not.\n visibility: position ? 'visible' : 'hidden',\n }}\n >\n <span className={styles.label}>{body}</span>\n {shortcut ? <kbd className={styles.shortcut}>{shortcut}</kbd> : null}\n </div>,\n document.body,\n )\n : null}\n </>\n );\n}\n","/**\n * Verse 3.10 workbench — cross-unit contracts (unit C0, frozen once written).\n *\n * SPEC-310C builds the workbench in parallel: C1 shell/palette/activity, C2\n * chat + dock, C3 composer, C4 terminal + preview, C5 git, C6 apps, C7\n * surfaces, C8 native — and Track B's autonomy units feed it (R1). Every shape\n * two of those units exchange is declared HERE, once, so each can build\n * against fixtures on day 0 and the pieces fit when they land:\n *\n * §1 information architecture (surfaces, tray, the v2 → v3 migration)\n * §2 Needs you (R1: the item B-U1 / B-U5 / B-U8 export, and the drawer acts on)\n * §3 activity + session meta (C1; the engine signatures C3 implements)\n * §4 session controls, attachments, queue, files (C3)\n * §5 terminal (C4) §6 preview (C4) §7 git / review / PR (C5)\n * §8 apps & accounts (C6)\n * §9 the route families C0 mounts in verse-api.ts\n *\n * Honesty rule (docs/VERSE-TELEMETRY-V2.md): `null` means UNKNOWN, never\n * \"none\" or \"zero\". Copy is operator language with units; no file paths,\n * stack traces or secrets in any string field.\n *\n * BROWSER-SAFE: the web bundle imports this (type-only via data/api-types.ts,\n * plus the plain constants). Type-only imports and plain data — no node:\n * modules, ever.\n */\nimport type {\n VerseEffort,\n VerseEngine,\n VersePermissionMode,\n VerseProgressPhase,\n VerseSessionControls,\n} from './types.js';\n\nexport type { VerseEffort, VersePermissionMode, VerseSessionControls };\n\n// ===========================================================================\n// §1 Information architecture (SPEC-310C §0.1, SPEC-310B §6)\n// ===========================================================================\n\n/** The rail, in ⌘1–⌘5 order. Chat moved from ⌘1 to ⌘5 in 3.10. */\nexport const WORKBENCH_SURFACES = ['command', 'fleet', 'growth', 'mind', 'chat'] as const;\nexport type WorkbenchSurfaceId = (typeof WORKBENCH_SURFACES)[number];\n\n/** Sections reached from the gear tray, not the rail (Shortcuts is an overlay, not a section). */\nexport const WORKBENCH_TRAY_SECTIONS = ['settings', 'apps', 'usage'] as const;\nexport type WorkbenchTraySectionId = (typeof WORKBENCH_TRAY_SECTIONS)[number];\n\nexport type WorkbenchSectionId = WorkbenchSurfaceId | WorkbenchTraySectionId;\n\nexport const WORKBENCH_SECTION_IDS: readonly WorkbenchSectionId[] = [...WORKBENCH_SURFACES, ...WORKBENCH_TRAY_SECTIONS];\n\nexport function isWorkbenchSectionId(value: unknown): value is WorkbenchSectionId {\n return typeof value === 'string' && (WORKBENCH_SECTION_IDS as readonly string[]).includes(value);\n}\n\n/** The `section` values `ashlr.verse.ui.v2` could hold. */\nexport type LegacyVerseSectionId = 'chat' | 'autonomy' | 'approvals' | 'usage' | 'settings' | 'mcp';\n\nexport interface SectionMigration {\n section: WorkbenchSectionId;\n /** Approvals became the Needs-you drawer: land on Command WITH the drawer open. */\n openNeedsYou: boolean;\n}\n\n/**\n * v2 → v3 (SPEC-310C §0.1): autonomy → fleet, approvals → command + drawer,\n * mcp → apps. Chat, Usage and Settings keep their ids.\n */\nexport const LEGACY_SECTION_MIGRATION: Readonly<Record<LegacyVerseSectionId, SectionMigration>> = {\n chat: { section: 'chat', openNeedsYou: false },\n autonomy: { section: 'fleet', openNeedsYou: false },\n approvals: { section: 'command', openNeedsYou: true },\n usage: { section: 'usage', openNeedsYou: false },\n settings: { section: 'settings', openNeedsYou: false },\n mcp: { section: 'apps', openNeedsYou: false },\n};\n\n/**\n * A stored `section` value → its v3 home. A v3 id passes through; a v2 id is\n * migrated; anything else (a hand-edited or future value) is null, and the\n * caller falls back to its own default rather than to a guess.\n */\nexport function migrateSectionId(value: unknown): SectionMigration | null {\n if (typeof value !== 'string') return null;\n if (Object.prototype.hasOwnProperty.call(LEGACY_SECTION_MIGRATION, value)) {\n return LEGACY_SECTION_MIGRATION[value as LegacyVerseSectionId];\n }\n return isWorkbenchSectionId(value) ? { section: value, openNeedsYou: false } : null;\n}\n\n/**\n * Engine identity is a 2px tick plus a one-letter monogram — never a vendor\n * logo (SPEC-310C §6). One table so the rail, sidebar, Apps and the native\n * notifications all print the same letter.\n */\nexport const ENGINE_MONOGRAM: Readonly<Record<VerseEngine, 'C' | 'X' | 'G' | 'L'>> = {\n claude: 'C',\n codex: 'X',\n grok: 'G',\n local: 'L',\n};\n\n// ===========================================================================\n// §2 Needs you (cross-track request R1)\n// ===========================================================================\n//\n// ONE inbox for everything waiting on Mason: approvals, fleet holds, Leader\n// questions and vetoes, grant renewal, failed chats, seats to reconnect.\n// Producers:\n// - C1 derives `approvals` (inbox), `chats` and `accounts` (A2 health);\n// - B-U1, B-U5 and B-U8 each EXPORT `needsYouItems(): NeedsYouItem[]` —\n// see NEEDS_YOU_PROVIDERS for where C1's activity route looks for them.\n// An item carries its own actions (route + confirmation), so the drawer is\n// generic: it never needs to know what a grant or an owner-lane PR is.\n\nexport const NEEDS_YOU_SOURCES = ['approvals', 'authority', 'fleet', 'leader', 'chats', 'accounts'] as const;\nexport type NeedsYouSource = (typeof NEEDS_YOU_SOURCES)[number];\n\n/**\n * What the item is. A new kind is a C0 contract change (send the request):\n * the drawer's split and the native notification copy both key off it.\n */\nexport const NEEDS_YOU_KINDS = [\n // approvals split\n 'approval', // an inbox proposal waiting for approve / reject\n 'owner-lane-pr', // G1: a fleet PR on the owner lane — never auto-merged\n 'class-c', // a Leader action outside the grant: Mason only, argument attached\n // fleet split\n 'veto-window', // a class-B Leader action that applies when its window closes\n 'leader-question', // the memo's questionsForMason\n 'owner-hold', // a repo on owner-hold (one-click resume)\n 'quarantine', // a repo quarantined after a post-merge red\n 'revert', // a fleet merge was reverted\n 'grant', // grant renewal / expiring / paused (\"authority code changed\")\n 'kill', // the fleet is stopped (KILL or a soft kill)\n // chats split\n 'chat-failed', // a turn failed and has not been looked at\n 'queue-held', // a queued follow-up is held after a failure or Stop\n // accounts split\n 'reconnect', // a seat is signed out or its credential is expiring\n 'repin', // a seat is pinned to an older CLI than the newest installed\n] as const;\nexport type NeedsYouKind = (typeof NEEDS_YOU_KINDS)[number];\n\n/** The drawer's splits besides All (SPEC-310C §1: H / L switch between them). */\nexport const NEEDS_YOU_CATEGORIES = ['approvals', 'fleet', 'chats', 'accounts'] as const;\nexport type NeedsYouCategory = (typeof NEEDS_YOU_CATEGORIES)[number];\n\n/** Split is a pure function of kind, so a producer can never file an item in the wrong one. */\nexport const NEEDS_YOU_KIND_CATEGORY: Readonly<Record<NeedsYouKind, NeedsYouCategory>> = {\n approval: 'approvals',\n 'owner-lane-pr': 'approvals',\n 'class-c': 'approvals',\n 'veto-window': 'fleet',\n 'leader-question': 'fleet',\n 'owner-hold': 'fleet',\n quarantine: 'fleet',\n revert: 'fleet',\n grant: 'fleet',\n kill: 'fleet',\n 'chat-failed': 'chats',\n 'queue-held': 'chats',\n reconnect: 'accounts',\n repin: 'accounts',\n};\n\nexport function needsYouCategory(item: Pick<NeedsYouItem, 'kind'>): NeedsYouCategory {\n return NEEDS_YOU_KIND_CATEGORY[item.kind];\n}\n\n/** Same vocabulary as ReasoningInsight severity. */\nexport type NeedsYouSeverity = 'info' | 'warn' | 'high';\n\n/**\n * Drawer keys (SPEC-310C §1): A approves, R rejects, V vetoes, E marks done.\n * `resume`, `renew` and `fix` are buttons only.\n */\nexport const NEEDS_YOU_ACTION_KINDS = ['approve', 'reject', 'veto', 'done', 'resume', 'renew', 'fix'] as const;\nexport type NeedsYouActionKind = (typeof NEEDS_YOU_ACTION_KINDS)[number];\n\nexport const NEEDS_YOU_ACTION_KEYS: Readonly<Partial<Record<NeedsYouActionKind, 'A' | 'R' | 'V' | 'E'>>> = {\n approve: 'A',\n reject: 'R',\n veto: 'V',\n done: 'E',\n};\n\nexport interface NeedsYouConfirm {\n title: string;\n body: string;\n confirmLabel: string;\n}\n\nexport interface NeedsYouAction {\n kind: NeedsYouActionKind;\n /** Button text, operator language: \"Approve\", \"Veto\", \"Resume repo\". */\n label: string;\n /**\n * The same-origin route the drawer POSTs (JSON body, mutation token) — always\n * under `/api/` (isNeedsYouItem enforces it). Null when the action cannot be\n * taken from the page (it needs Touch ID or a terminal): the drawer opens the\n * item's `target` instead, where that flow lives.\n */\n request: { method: 'POST'; path: string; body: Record<string, unknown> } | null;\n /**\n * Dialog text. The drawer confirms approve / reject / veto ALWAYS\n * (SPEC-310C: keyboard triage goes through confirmation, then the token) —\n * a null here gets its generic copy; other kinds confirm only when set.\n */\n confirm: NeedsYouConfirm | null;\n /** Painted as a destructive action (reject, veto, close). */\n destructive: boolean;\n}\n\n/** Where Enter goes. */\nexport type NeedsYouTarget =\n | { kind: 'session'; sessionId: string }\n | { kind: 'approval'; proposalId: string }\n | { kind: 'section'; section: WorkbenchSectionId; anchor: string | null }\n | { kind: 'seat'; seatId: string }\n /** Opened outside the app (a GitHub PR). https only. */\n | { kind: 'url'; url: string };\n\n/** What the row is about, for its meta line. Every field null when not applicable. */\nexport interface NeedsYouSubject {\n repo: string | null;\n pr: number | null;\n seatId: string | null;\n sessionId: string | null;\n engine: VerseEngine | null;\n}\n\nexport interface NeedsYouItem {\n /**\n * Stable across polls AND restarts — `<source>:<kind>:<ref>` (e.g.\n * `fleet:owner-hold:ashlrai/binshield`). The drawer's selection, \"seen\" and\n * native \"N new\" counting all key on it.\n */\n id: string;\n source: NeedsYouSource;\n kind: NeedsYouKind;\n severity: NeedsYouSeverity;\n /** One line, ≤ 120 chars, operator language with units. Never a path or a trace. */\n title: string;\n /** Optional second line — the Leader's argument, the failing check. */\n detail: string | null;\n /** ISO time it became actionable. */\n since: string;\n /** ISO time it stops being actionable on its own (veto window close, grant expiry); null = never. */\n expiresAt: string | null;\n subject: NeedsYouSubject;\n target: NeedsYouTarget;\n actions: NeedsYouAction[];\n}\n\n/**\n * R1: `export function needsYouItems(): NeedsYouItem[]`. PURE and served from\n * the producer's own cache: activity calls it on every poll (Rust every 5 s),\n * so it must return in well under a millisecond and never do I/O on the\n * caller's stack. A producer that cannot answer throws — activity then reports\n * `sources.<source>: 'error'` rather than an all-clear it cannot vouch for.\n */\nexport type NeedsYouItemsProvider = () => NeedsYouItem[];\n\n/**\n * Where C1's activity route lazy-imports each Track B producer from: the\n * unit's own mounted API module (§9), which activity can import without a\n * load-time edge into core/{authority,fleet,vision}. A module that has not\n * landed makes activity report that source `unavailable` — never a false\n * all-clear. Moving a producer is a C0 contract change.\n */\nexport const NEEDS_YOU_PROVIDERS = [\n { source: 'authority', owner: 'B-U1', module: 'src/core/verse/authority-api.ts', exportName: 'needsYouItems' },\n { source: 'fleet', owner: 'B-U5', module: 'src/core/verse/fleet-live-api.ts', exportName: 'needsYouItems' },\n { source: 'leader', owner: 'B-U8', module: 'src/core/verse/leader-api.ts', exportName: 'needsYouItems' },\n] as const satisfies readonly { source: NeedsYouSource; owner: string; module: string; exportName: 'needsYouItems' }[];\n\nexport const NEEDS_YOU_TITLE_MAX = 120;\nexport const NEEDS_YOU_DETAIL_MAX = 400;\nconst NEEDS_YOU_ID_MAX = 200;\n\nconst ISO_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?(?:Z|[+-]\\d{2}:\\d{2})$/;\n\nfunction isIso(value: unknown): value is string {\n return typeof value === 'string' && ISO_RE.test(value) && !Number.isNaN(Date.parse(value));\n}\n\nfunction isNullableString(value: unknown, max: number): boolean {\n return value === null || (typeof value === 'string' && value.length <= max);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * A same-origin API route: `/api/…`, no scheme, no host, no `..`, no\n * backslash, no whitespace. The drawer POSTs these WITH the mutation token, so\n * an item must never be able to aim that token anywhere else.\n */\nexport function isSafeApiRoute(path: unknown): path is string {\n return typeof path === 'string'\n && path.length <= 512\n && path.startsWith('/api/')\n && !path.startsWith('//')\n && !path.includes('..')\n && !/[\\s\\\\]/.test(path);\n}\n\nfunction isTarget(value: unknown): value is NeedsYouTarget {\n if (!isRecord(value)) return false;\n switch (value['kind']) {\n case 'session':\n return typeof value['sessionId'] === 'string' && value['sessionId'].length > 0;\n case 'approval':\n return typeof value['proposalId'] === 'string' && value['proposalId'].length > 0;\n case 'section':\n return isWorkbenchSectionId(value['section']) && isNullableString(value['anchor'], 200);\n case 'seat':\n return typeof value['seatId'] === 'string' && value['seatId'].length > 0;\n case 'url':\n return typeof value['url'] === 'string' && /^https:\\/\\/[^\\s]+$/.test(value['url']) && value['url'].length <= 2048;\n default:\n return false;\n }\n}\n\nfunction isAction(value: unknown): value is NeedsYouAction {\n if (!isRecord(value)) return false;\n if (!(NEEDS_YOU_ACTION_KINDS as readonly unknown[]).includes(value['kind'])) return false;\n if (typeof value['label'] !== 'string' || value['label'].length === 0 || value['label'].length > 60) return false;\n if (typeof value['destructive'] !== 'boolean') return false;\n const request = value['request'];\n if (request !== null) {\n if (!isRecord(request) || request['method'] !== 'POST' || !isSafeApiRoute(request['path']) || !isRecord(request['body'])) {\n return false;\n }\n }\n const confirm = value['confirm'];\n if (confirm !== null) {\n if (!isRecord(confirm)) return false;\n for (const key of ['title', 'body', 'confirmLabel'] as const) {\n if (typeof confirm[key] !== 'string' || (confirm[key] as string).length === 0) return false;\n }\n }\n return true;\n}\n\n/**\n * Boundary check for items that come from ANOTHER unit's producer. Activity\n * drops (and counts) anything that fails, so one malformed producer degrades\n * to \"that source errored\" instead of breaking the drawer or smuggling a\n * foreign route into a token-bearing POST.\n */\nexport function isNeedsYouItem(value: unknown): value is NeedsYouItem {\n if (!isRecord(value)) return false;\n const id = value['id'];\n if (typeof id !== 'string' || id.length === 0 || id.length > NEEDS_YOU_ID_MAX) return false;\n if (!(NEEDS_YOU_SOURCES as readonly unknown[]).includes(value['source'])) return false;\n if (!(NEEDS_YOU_KINDS as readonly unknown[]).includes(value['kind'])) return false;\n if (!['info', 'warn', 'high'].includes(value['severity'] as string)) return false;\n const title = value['title'];\n if (typeof title !== 'string' || title.trim().length === 0 || title.length > NEEDS_YOU_TITLE_MAX) return false;\n if (!isNullableString(value['detail'], NEEDS_YOU_DETAIL_MAX)) return false;\n if (!isIso(value['since'])) return false;\n if (value['expiresAt'] !== null && !isIso(value['expiresAt'])) return false;\n const subject = value['subject'];\n if (!isRecord(subject)) return false;\n for (const key of ['repo', 'seatId', 'sessionId'] as const) {\n if (!isNullableString(subject[key], 300)) return false;\n }\n if (subject['pr'] !== null && !(Number.isInteger(subject['pr']) && (subject['pr'] as number) > 0)) return false;\n if (subject['engine'] !== null && !['claude', 'codex', 'grok', 'local'].includes(subject['engine'] as string)) return false;\n if (!isTarget(value['target'])) return false;\n const actions = value['actions'];\n return Array.isArray(actions) && actions.length <= 6 && actions.every(isAction);\n}\n\n// ===========================================================================\n// §3 Activity + session meta (C1) and the engine signatures behind it (C3)\n// ===========================================================================\n\nexport const VERSE_ACTIVITY_PATH = '/api/verse/activity';\nexport const VERSE_ACTIVITY_SEEN_PATH = '/api/verse/activity/seen';\nexport const VERSE_SESSION_META_PATH = '/api/verse/session-meta';\n\n/** Activity must answer in < 5 ms (SPEC-310C budgets): everything it reads is already in memory. */\nexport const VERSE_ACTIVITY_BUDGET_MS = 5;\n\n/**\n * ENGINE SIGNATURE (C3 implements on the engine handle; C1's activity reads\n * it): what a running turn is doing right now, folded from the transient\n * `progress` / `thinking-delta` events the engine already fans out. Returns\n * null when the session has no running turn. O(1), no I/O.\n */\nexport interface VerseLiveStatus {\n sessionId: string;\n turnId: string;\n /** ISO start of the running turn. */\n startedAt: string;\n /** Last `progress.phase`; null before the first progress event. */\n phase: VerseProgressPhase | null;\n /** The running tool when `phase` is `tool` (\"npm test\"); null otherwise. */\n tool: string | null;\n /** Wall time since `startedAt`, computed at peek time. */\n elapsedMs: number;\n /** Last ≤ VERSE_LIVE_THINKING_TAIL_CHARS of streamed reasoning, scrubbed; null when none streamed. */\n thinkingTail: string | null;\n outTokens: number | null;\n tokPerSec: number | null;\n}\nexport type PeekLiveStatus = (sessionId: string) => VerseLiveStatus | null;\nexport const VERSE_LIVE_THINKING_TAIL_CHARS = 160;\n\nexport type VerseTurnOutcome = 'ok' | 'failed' | 'cancelled';\n\n/**\n * ENGINE SIGNATURE (C3): one finished turn. `seq` is an engine-wide counter,\n * PROCESS-LOCAL — it restarts at 0 with the server, which is why activity's\n * wire cursor (VerseActivityResponse.cursor) is opaque and carries a boot id.\n */\nexport interface VerseTurnEnd {\n seq: number;\n sessionId: string;\n turnId: string;\n outcome: VerseTurnOutcome;\n /** ISO end time. */\n at: string;\n durationMs: number | null;\n /** The session's turnCount after this turn — unread = turnCount > seenTurnCount. */\n turnCount: number;\n}\n\n/**\n * ENGINE SIGNATURE (C3): turn ends with `seq > cursor`, oldest first. The\n * engine keeps the last VERSE_TURN_END_BUFFER in memory; a cursor older than\n * that gets what is left (activity never replays a log from disk).\n */\nexport type TurnEndsSince = (cursor: number) => { cursor: number; ends: VerseTurnEnd[] };\nexport const VERSE_TURN_END_BUFFER = 200;\n\nexport interface VerseActivityLive {\n phase: VerseProgressPhase | null;\n tool: string | null;\n elapsedMs: number;\n thinkingTail: string | null;\n}\n\nexport interface VerseActivityRunning {\n sessionId: string;\n title: string;\n engine: VerseEngine;\n seatId: string;\n startedAt: string;\n /** Null when the engine could not be peeked (the row still shows \"running\"). */\n live: VerseActivityLive | null;\n}\n\nexport interface VerseActivityCompletion {\n sessionId: string;\n title: string;\n outcome: VerseTurnOutcome;\n at: string;\n durationMs: number | null;\n}\n\nexport interface VerseActivityCounts {\n running: number;\n needsYou: number;\n unread: number;\n}\n\n/**\n * Per producer: `ok` answered; `unavailable` its module has not landed or is\n * not mounted in this build; `error` it threw or returned items that failed\n * isNeedsYouItem. Anything but `ok` means the drawer must NOT say \"All clear\"\n * for that split.\n */\nexport type VerseActivitySourceState = 'ok' | 'unavailable' | 'error';\nexport type VerseActivitySources = Record<NeedsYouSource, VerseActivitySourceState>;\n\n/** The rail's Fleet badge (SPEC-310C §1). Null in the response = unknown. */\nexport interface VerseAutonomyBadge {\n /** The clamp switch (B-U1): Off | Propose | Autonomous. */\n mode: 'off' | 'propose' | 'autonomous';\n /** Grant paused (\"authority code changed — re-approve\") or expired. */\n paused: boolean;\n /** KILL / soft kill in force. */\n stopped: boolean;\n /** Operator-language one-liner for the tooltip: \"Autonomous · 5 building\". */\n label: string;\n}\n\n/** The rail foot's capacity ring: the scarcest seat. */\nexport interface VerseCapacityBadge {\n seatId: string;\n engine: VerseEngine;\n label: string;\n /** 0–100 of the binding window. */\n usedPercent: number;\n /** Window label as the seat names it (\"5h\", \"weekly\"). */\n window: string;\n resetsAt: string | null;\n}\n\n/** The rail's Mind dot. */\nexport interface VerseMindBadge {\n latestMemoAt: string | null;\n /** A memo newer than the operator last opened Mind. */\n unseen: boolean;\n}\n\n/** GET /api/verse/activity?since=<cursor> */\nexport interface VerseActivityResponse {\n /** Opaque; send back as `?since=`. A different boot id resets completions (never an error). */\n cursor: string;\n generatedAt: string;\n running: VerseActivityRunning[];\n needsYou: NeedsYouItem[];\n /** Turn ends after `since`, oldest first (empty on the first poll: history is not \"new\"). */\n completions: VerseActivityCompletion[];\n counts: VerseActivityCounts;\n sources: VerseActivitySources;\n autonomy: VerseAutonomyBadge | null;\n capacity: VerseCapacityBadge | null;\n mind: VerseMindBadge | null;\n}\n\n/** POST /api/verse/activity/seen — the operator has looked at this chat up to `turnCount`. */\nexport interface VerseActivitySeenRequest {\n sessionId: string;\n turnCount: number;\n}\n\n/** Per-chat organisation + read state (NOT in the session record: the engine owns that). */\nexport interface VerseSessionMeta {\n sessionId: string;\n pinned: boolean;\n archived: boolean;\n /** Highest turnCount the operator has seen; 0 = never opened. */\n seenTurnCount: number;\n}\n\n/** GET /api/verse/session-meta → every chat with non-default meta. GET …/:id → VerseSessionMeta. */\nexport interface VerseSessionMetaResponse {\n sessions: Record<string, VerseSessionMeta>;\n}\n\n/** POST /api/verse/session-meta/:id — unknown keys are a 400. */\nexport interface VerseSessionMetaUpdate {\n pinned?: boolean;\n archived?: boolean;\n}\n\n// ===========================================================================\n// §4 Session controls, attachments, queue, files (C3)\n// ===========================================================================\n\nexport const VERSE_SESSION_CONTROLS_PATH = '/api/verse/session-controls';\nexport const VERSE_SESSION_CONTROL_DEFAULTS_PATH = '/api/verse/session-controls/defaults';\nexport const VERSE_ATTACHMENTS_PATH = '/api/verse/attachments';\nexport const VERSE_QUEUE_PATH = '/api/verse/queue';\nexport const VERSE_FILES_PATH = '/api/verse/files';\n\n/** One picker option. Unavailable options are shown DISABLED with `reason`, never hidden. */\nexport interface VerseControlOption<T extends string = string> {\n id: T;\n label: string;\n available: boolean;\n reason?: string;\n /** Painted red (bypass). */\n danger?: boolean;\n}\n\n/** GET|POST /api/verse/session-controls/:id */\nexport interface VerseSessionControlsResponse {\n sessionId: string;\n /** Effective values: `model` is the session's model; `effort` null = the CLI's own default. */\n controls: { model: string; effort: VerseEffort | null; permissionMode: VersePermissionMode };\n /** True while a turn runs: the change is recorded now and applies from the next turn. */\n appliesNextTurn: boolean;\n options: {\n models: VerseControlOption[];\n efforts: VerseControlOption<VerseEffort>[];\n permissionModes: VerseControlOption<VersePermissionMode>[];\n };\n}\n\n/**\n * POST /api/verse/session-controls/:id — unknown keys are a 400. `effort: null`\n * resets to the CLI default. Setting `permissionMode: 'bypass'` REQUIRES\n * `confirmBypass: true` (400 otherwise): the palette and the picker both\n * reach this route, and neither may turn every check off with one keystroke.\n */\nexport interface VerseSessionControlsUpdate {\n model?: string;\n effort?: VerseEffort | null;\n permissionMode?: VersePermissionMode;\n confirmBypass?: true;\n}\n\n/**\n * GET|POST /api/verse/session-controls/defaults — what a NEW chat starts with.\n * `bypass` is refused here: it is confirmed per chat, never inherited.\n */\nexport interface VerseSessionControlDefaults {\n global: VerseSessionControls;\n seats: Record<string, VerseSessionControls>;\n}\nexport interface VerseSessionControlDefaultsUpdate {\n /** Absent = the global default. */\n seatId?: string;\n effort?: VerseEffort | null;\n permissionMode?: Exclude<VersePermissionMode, 'bypass'>;\n}\n\n/** POST /api/verse/attachments/:sessionId (body cap raised for this route only). */\nexport interface VerseAttachmentUpload {\n name: string;\n mime: string;\n dataBase64: string;\n}\nexport interface VerseAttachment {\n id: string;\n sessionId: string;\n /** Sanitised display name. */\n name: string;\n mime: string;\n bytes: number;\n /** The `@path` token the composer inserts (`~`-relative, as every response is sanitised). */\n ref: string;\n createdAt: string;\n}\n/** Per file. Stored 0600 under ~/.ashlr/verse/attachments/<sid>/ and granted with exactly that `--add-dir`. */\nexport const VERSE_ATTACHMENT_MAX_BYTES = 8 * 1024 * 1024;\n\n/** GET /api/verse/queue/:sessionId; POST …/:sessionId {text}; POST …/:sessionId/:qid/delete */\nexport interface VerseQueuedTurn {\n id: string;\n sessionId: string;\n text: string;\n createdAt: string;\n}\nexport interface VerseQueueResponse {\n sessionId: string;\n items: VerseQueuedTurn[];\n /** After a failed or stopped turn the queue HOLDS and asks, instead of draining. */\n held: boolean;\n heldReason: string | null;\n}\n/**\n * POST /api/verse/queue/:sessionId. `sendNow` (⇧⌘↩ / \"Send now\") stops the\n * running turn and makes this text the next turn instead of queueing it last.\n */\nexport interface VerseQueueRequest {\n text: string;\n sendNow?: boolean;\n}\n/**\n * POST /api/verse/queue/:sessionId (and POST …/:sessionId/:qid/send) answer\n * with the queue PLUS the turn they started, if any — null when the text was\n * only queued behind a running turn.\n */\nexport interface VerseQueueSendResponse extends VerseQueueResponse {\n sentTurnId: string | null;\n}\n/** Additional queue route (C3): send one queued item now. */\nexport const VERSE_QUEUE_SEND_SUFFIX = '/send';\n/** Enter during a running turn queues server-side, at most this many. */\nexport const VERSE_QUEUE_MAX = 3;\n\n/** GET /api/verse/files?sessionId=&q= — the `@` fuzzy finder over the chat's roots. */\nexport interface VerseFileMatch {\n /** Path relative to `root`. */\n path: string;\n root: string;\n}\nexport interface VerseFilesResponse {\n sessionId: string;\n query: string;\n files: VerseFileMatch[];\n truncated: boolean;\n /**\n * The chat's primary root (C3 addition): matches in it are inserted as bare\n * relative `@path`s, matches in other roots keep their root. Optional — an\n * older server omits it and every match is shown with its root.\n */\n primaryRoot?: string | null;\n}\n/*\n * Attachment routes beyond the upload (C3 additions; same family):\n * GET /api/verse/attachments/:sessionId → { attachments: VerseAttachment[] }\n * POST /api/verse/attachments/:sessionId/:aid/delete → { ok: true }\n */\nexport interface VerseAttachmentListResponse {\n attachments: VerseAttachment[];\n}\n\n// ===========================================================================\n// §5 Terminal (C4)\n// ===========================================================================\n\nexport const VERSE_TERMINAL_PATH = '/api/verse/terminal';\nexport const VERSE_TERMINAL_MAX_TABS = 8;\n/** Per request, before base64. */\nexport const VERSE_TERMINAL_INPUT_MAX_BYTES = 16 * 1024;\n/** Per tab, in memory only — never written to disk. */\nexport const VERSE_TERMINAL_SCROLLBACK_BYTES = 256 * 1024;\nexport const VERSE_TERMINAL_IDLE_KILL_MS = 12 * 60 * 60 * 1000;\n/** SSE frames are coalesced to at most this rate. */\nexport const VERSE_TERMINAL_MAX_FRAME_HZ = 60;\n\nexport interface VerseTerminalTab {\n id: string;\n sessionId: string | null;\n /** The session root (or discovered project) the shell started in. */\n root: string;\n /** From the shell's OSC title; falls back to the root's name. */\n title: string;\n cols: number;\n rows: number;\n createdAt: string;\n lastActivityAt: string;\n /** Set when the shell exited; the tab stays until closed so its output can be read. */\n exited: { code: number | null; signal: string | null; at: string } | null;\n /** Opened from Apps' [Launch ▸] or Preview's dev-server Start, when it was. */\n appId: string | null;\n devServerId: string | null;\n}\n\n/** GET /api/verse/terminal. `available: false` under Node (no Bun PTY) — `reason` says \"needs the desktop app\". */\nexport interface VerseTerminalListResponse {\n available: boolean;\n reason: string | null;\n tabs: VerseTerminalTab[];\n}\n\n/** How an Apps [Launch ▸] runs an agent: its own command, or `ollama launch <id>` (C6). */\nexport type VerseTerminalLaunchVia = 'native' | 'ollama';\n\n/**\n * POST /api/verse/terminal. With `appId`, the server resolves the command from\n * the apps catalog and the installed binaries (`resolveAppLaunch`) — the page\n * never supplies a command. `via` / `model` choose HOW that app launches:\n * `model` is an installed Ollama tag and implies `via: 'ollama'` (a local\n * model only launches through Ollama). Both are refused without `appId`.\n */\nexport interface VerseTerminalCreateRequest {\n sessionId: string;\n root?: string;\n appId?: string;\n via?: VerseTerminalLaunchVia;\n model?: string;\n devServerId?: string;\n cols: number;\n rows: number;\n}\n\n/**\n * POST /api/verse/terminal/open-external { sessionId, root? } → 202 — opens\n * Terminal.app at the root (C4 addition: the pane header's \"Open in\n * Terminal.app\", and the pane's fallback where no Bun PTY exists).\n */\nexport const VERSE_TERMINAL_OPEN_EXTERNAL_PATH = '/api/verse/terminal/open-external';\nexport interface VerseTerminalOpenExternalRequest {\n sessionId: string;\n root?: string;\n}\n\n/** POST /api/verse/terminal/:id/input */\nexport interface VerseTerminalInputRequest {\n dataBase64: string;\n}\n\n/** POST /api/verse/terminal/:id/resize */\nexport interface VerseTerminalResizeRequest {\n cols: number;\n rows: number;\n}\n\n/**\n * GET /api/verse/terminal/:id/stream?after=<seq> — SSE. Scrollback is sent\n * first (as `output` frames), so a reload re-attaches. `seq` counts output\n * frames per tab; `after` resumes past what the client already has.\n */\nexport type VerseTerminalFrame =\n | { type: 'output'; seq: number; dataBase64: string }\n | { type: 'title'; title: string }\n | { type: 'exit'; code: number | null; signal: string | null };\n\n// ===========================================================================\n// §6 Preview (C4)\n// ===========================================================================\n\nexport const VERSE_PREVIEW_TARGETS_PATH = '/api/verse/preview/targets';\nexport const VERSE_PREVIEW_RAW_PATH = '/api/verse/preview/raw';\n/** preview/raw serves at most this much (nosniff, `CSP: sandbox allow-scripts`). */\nexport const VERSE_PREVIEW_RAW_MAX_BYTES = 5 * 1024 * 1024;\n\nexport type VersePreviewDevServerSource = 'launch-json' | 'package-json' | 'listening';\n\nexport interface VersePreviewDevServer {\n id: string;\n label: string;\n /** Loopback http only (127.0.0.1 / localhost). */\n url: string;\n port: number;\n source: VersePreviewDevServerSource;\n running: boolean;\n root: string;\n /**\n * Exactly what Start types into a terminal tab (`npm run dev`, a launch.json\n * `runtimeExecutable` + args), so the row can show it BEFORE the click\n * (C4 addition). Null for a server only observed listening — there is\n * nothing to start. Optional: an older server omits it.\n */\n command?: string | null;\n}\n\nexport type VersePreviewArtifactKind = 'html' | 'md' | 'svg' | 'image' | 'pdf';\n\nexport interface VersePreviewArtifact {\n /** Relative to the session root it lives in; served only through preview/raw. */\n path: string;\n kind: VersePreviewArtifactKind;\n bytes: number;\n}\n\n/** GET /api/verse/preview/targets?sessionId= */\nexport interface VersePreviewTargetsResponse {\n devServers: VersePreviewDevServer[];\n artifacts: VersePreviewArtifact[];\n}\n\n/*\n * Preview frame tickets (C4 addition). An `<iframe src>` cannot send the\n * read-client header, and a srcdoc/blob frame would inherit the page's\n * script-src, so an artifact frame is loaded through a one-file ticket:\n * GET /api/verse/preview/ticket?sessionId=&path= (read session) → { url, expiresAt, kind }\n * GET /api/verse/preview/frame/<43-char ticket> (NO read-session check; the\n * ticket expires in 5 min and redeems only with the minting session's cookie)\n * That frame path is the ONE route in the workbench exempt from the read-session\n * gate (core/web/server.ts), and it serves under `CSP: sandbox allow-scripts`.\n */\nexport const VERSE_PREVIEW_TICKET_PATH = '/api/verse/preview/ticket';\nexport const VERSE_PREVIEW_FRAME_PATH = '/api/verse/preview/frame';\n/** The frame path's exact shape — the one server.ts lets past the read boundary. */\nexport const VERSE_PREVIEW_FRAME_PATH_RE = /^\\/api\\/verse\\/preview\\/frame\\/([A-Za-z0-9_-]{43})$/;\nexport interface VersePreviewTicketResponse {\n /** The frame URL carrying the ticket (`/api/verse/preview/frame/<ticket>`). */\n url: string;\n expiresAt: string;\n kind: VersePreviewArtifactKind;\n}\n\n/**\n * Loopback http only — the one rule the Preview URL bar, the iframe and the\n * server's `frame-src` all share. Anything else gets \"Open in browser ↗\".\n */\nexport function isLoopbackPreviewUrl(value: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n return url.protocol === 'http:'\n && (url.hostname === '127.0.0.1' || url.hostname === 'localhost')\n && url.username === ''\n && url.password === '';\n}\n\n// ===========================================================================\n// §7 Git, review, PR (C5)\n// ===========================================================================\n\nexport const VERSE_GIT_PATH = '/api/verse/git';\nexport const VERSE_GIT_STATUS_PATH = '/api/verse/git/status';\nexport const VERSE_GIT_DIFF_PATH = '/api/verse/git/diff';\n/** Patches load lazily per file, each capped here. */\nexport const VERSE_GIT_PATCH_MAX_BYTES = 256 * 1024;\n/** git/status answers < 300 ms and is cached this long. */\nexport const VERSE_GIT_STATUS_CACHE_MS = 5_000;\n/** Every git/gh subprocess: async execFile, this timeout. */\nexport const VERSE_GIT_TIMEOUT_MS = 60_000;\n/** \"Isolate in worktree\": ~/.ashlr-worktrees/<repo>/<name> on branch verse/<name>. */\nexport const VERSE_WORKTREE_DIR = '~/.ashlr-worktrees';\nexport const VERSE_WORKTREE_BRANCH_PREFIX = 'verse/';\n\n/** The branch bar's primary button (SPEC-310C §2). `merge` only with green checks and a mergeable branch. */\nexport type VerseGitSuggestedAction = 'commit' | 'push' | 'create-pr' | 'merge' | 'view-pr' | 'none';\nexport type VersePrState = 'open' | 'draft' | 'merged' | 'closed';\nexport type VersePrChecks = 'passing' | 'failing' | 'pending' | 'none' | 'unknown';\n\nexport interface VerseGitPr {\n number: number;\n title: string;\n url: string;\n state: VersePrState;\n checks: VersePrChecks;\n /** GitHub's mergeability; null while GitHub is still computing it. */\n mergeable: boolean | null;\n headSha: string | null;\n baseRef: string;\n headRef: string;\n}\n\nexport interface VerseGitDiffstat {\n files: number;\n additions: number;\n deletions: number;\n}\n\n/** GET /api/verse/git/status?root= */\nexport interface VerseGitStatus {\n root: string;\n name: string;\n /** Null on a detached HEAD. */\n branch: string | null;\n upstream: string | null;\n /** The branch the PR would target; null when unknown. */\n base: string | null;\n ahead: number;\n behind: number;\n /** Changed paths in the working tree (staged + unstaged + untracked). */\n dirty: number;\n /** Everything this branch would ship against `base`, uncommitted work included (the bar's ± counts). */\n diffstat: VerseGitDiffstat;\n pr: VerseGitPr | null;\n suggested: VerseGitSuggestedAction;\n checkedAt: string;\n}\n\n/**\n * `pr` is only as good as GitHub's answer (C5 addition):\n * ok GitHub answered — `pr: null` really means \"no PR\";\n * pending the answer is being fetched in the background (the bar says\n * \"Checking GitHub…\" and re-reads shortly);\n * unavailable gh missing, signed out, offline, or not a GitHub remote —\n * `pr: null` means \"could not look\", never \"no PR\".\n */\nexport type VerseGitPrLookup = 'ok' | 'pending' | 'unavailable';\n\n/** The counts behind `pr.checks`, for \"3 of 9 checks failing\". */\nexport interface VerseGitCheckCounts {\n total: number;\n passed: number;\n failed: number;\n pending: number;\n}\n\n/**\n * What GET /api/verse/git/status actually serves: the frozen VerseGitStatus\n * plus C5's additive fields. Kept a separate interface (not new required\n * fields on VerseGitStatus) so a fixture written against §7 still compiles.\n */\nexport interface VerseGitStatusDetail extends VerseGitStatus {\n /** The repository's top level. Several chat roots inside one repo share one bar row. */\n gitRoot: string;\n prLookup: VerseGitPrLookup;\n prCheckCounts: VerseGitCheckCounts | null;\n /** Conflicted paths (merge/rebase in progress). Commit is refused while > 0. */\n conflicts: number;\n /** HEAD's commit subject — the Create PR dialog's default title. Null on an unborn branch. */\n headSubject: string | null;\n /** HEAD's SHA; null on an unborn branch. */\n headSha: string | null;\n}\n\n/** Server scopes. The UI adds `turn` (from the chat's own events). */\nexport type VerseGitDiffScope = 'working' | 'branch';\nexport type VerseReviewScope = 'turn' | VerseGitDiffScope;\nexport type VerseGitFileStatus = 'M' | 'A' | 'D' | 'R' | 'U';\n\nexport interface VerseGitDiffFile {\n path: string;\n /** Previous path for a rename. */\n oldPath: string | null;\n status: VerseGitFileStatus;\n additions: number;\n deletions: number;\n binary: boolean;\n}\n\nexport interface VerseGitPatch {\n path: string;\n /** Unified diff text, possibly cut at VERSE_GIT_PATCH_MAX_BYTES. */\n text: string;\n truncated: boolean;\n}\n\n/** GET /api/verse/git/diff?root=&scope=working|branch[&file=] — `patch` only when `file` was asked. */\nexport interface VerseGitDiffResponse {\n root: string;\n scope: VerseGitDiffScope;\n base: string | null;\n files: VerseGitDiffFile[];\n patch: VerseGitPatch | null;\n}\n\n/** POST /api/verse/git/commit — no `paths` = every change. */\nexport interface VerseGitCommitRequest {\n root: string;\n message: string;\n paths?: string[];\n}\n/** POST /api/verse/git/push */\nexport interface VerseGitPushRequest {\n root: string;\n}\n/** POST /api/verse/git/pr — pushes first (gh refuses an unpushed branch), then opens the PR. */\nexport interface VerseGitPrRequest {\n root: string;\n title: string;\n body?: string;\n draft?: boolean;\n base?: string;\n}\n/**\n * POST /api/verse/git/pr/merge — refused unless every check passes AND GitHub's\n * head SHA still equals `headSha` (no merging a commit the operator never\n * saw). Never `--admin`.\n */\nexport interface VerseGitMergeRequest {\n root: string;\n number: number;\n headSha: string;\n}\n/** POST /api/verse/git/worktree */\nexport interface VerseGitWorktreeRequest {\n root: string;\n name: string;\n}\nexport interface VerseGitWorktreeResponse {\n path: string;\n branch: string;\n}\n/** Every git POST answers with the fresh status (and the PR when one was opened or merged). A busy root is a 409. */\nexport interface VerseGitActionResponse {\n ok: true;\n status: VerseGitStatus;\n pr: VerseGitPr | null;\n}\n\n// ===========================================================================\n// §8 Apps & accounts (C6; the static catalog is apps-catalog.ts)\n// ===========================================================================\n\nexport const VERSE_APPS_PATH = '/api/verse/apps';\n\nexport const VERSE_APP_GROUPS = ['accounts', 'desktop', 'terminal-agents', 'local-models', 'mcp-servers'] as const;\nexport type VerseAppGroupId = (typeof VERSE_APP_GROUPS)[number];\n\nexport type VerseAppHealthState = 'ok' | 'warn' | 'error' | 'off' | 'unknown';\n\n/** A dot WITH a word — never colour alone. */\nexport interface VerseAppHealth {\n state: VerseAppHealthState;\n /** \"connected\", \"not installed\", \"8.5 tok/s\", \"signed out\". */\n label: string;\n}\n\nexport type VerseAppActionKind = 'launch' | 'reconnect' | 'fix' | 'edit-budget' | 'restore' | 'add-mcp';\n\nexport interface VerseAppAction {\n kind: VerseAppActionKind;\n label: string;\n /** The command it runs or shows (argv, never a shell string; never a secret). */\n command: string[] | null;\n /** Disabled WITH a reason, never hidden. */\n disabledReason: string | null;\n}\n\n/**\n * A toggle that changes ANOTHER app's settings (Claude Desktop \"Use Ollama\n * models\", Hermes Desktop). Turning it either way needs `confirm: true` and the\n * confirmation shows `command` and `restoreCommand` verbatim.\n */\nexport interface VerseAppToggle {\n enabled: boolean;\n command: string[];\n restoreCommand: string[];\n}\n\nexport interface VerseAppRow {\n id: string;\n name: string;\n /** 32px tile letter(s). */\n monogram: string;\n /** Tints the tile for seats and engine-backed agents; null = neutral tile. */\n engine: VerseEngine | null;\n description: string;\n health: VerseAppHealth;\n /** Null = not checked (e.g. a seat row). */\n installed: boolean | null;\n version: string | null;\n /** The copy pill (⧉ → ✓ for 1.2 s). */\n copy: { label: string; text: string } | null;\n /** `ollama launch <id>`, only when the INSTALLED ollama's `launch --help` lists it. */\n ollamaLaunch: string[] | null;\n toggle: VerseAppToggle | null;\n actions: VerseAppAction[];\n /** For seat rows: the Verse seat it represents. */\n seatId: string | null;\n /** Second line: \"5h 62% · weekly 40% · resets Thu · reserved for you 40%\". */\n detail: string | null;\n}\n\nexport interface VerseAppGroup {\n id: VerseAppGroupId;\n title: string;\n /** A standing caveat shown under the header (MCP: claude and local seats load none). */\n caveat: string | null;\n apps: VerseAppRow[];\n}\n\n/** GET /api/verse/apps (POST /api/verse/apps/refresh re-probes and answers the same). */\nexport interface VerseAppsResponse {\n checkedAt: string;\n /** Where binary detection looked: the login shell's PATH, or the fallback dirs (see login-path.ts). */\n pathSource: 'login-shell' | 'fallback';\n groups: VerseAppGroup[];\n}\n\n/** POST /api/verse/apps/:id/toggle — `confirm: true` is required; anything else is a 400. */\nexport interface VerseAppToggleRequest {\n enabled: boolean;\n confirm: true;\n}\n\n/**\n * POST /api/verse/apps/:id/launch (C6 addition) — opens the agent in a\n * visible Terminal.app window at `root` (a chat folder or discovered\n * project). The command is resolved server-side from the catalog and the\n * installed binaries; `model` is an installed Ollama tag and implies Ollama.\n */\nexport interface VerseAppLaunchRequest {\n root: string;\n via?: VerseTerminalLaunchVia;\n model?: string;\n}\nexport interface VerseAppLaunchResponse {\n ok: true;\n opened: 'terminal-app';\n /** The command as the operator would type it (bare argv[0]). */\n command: string[];\n}\n\n// ===========================================================================\n// §9 Route families C0 mounts (verse-api.ts WORKBENCH mount table)\n// ===========================================================================\n\n/**\n * Every Track B / C route family, its owner, the module and export the specs\n * name, and the path prefixes it — and ONLY it — answers. The mount in\n * verse-api.ts routes by these prefixes (segment boundary: `/api/verse/git`\n * owns `/api/verse/git/status`, never `/api/verse/github`), so a module can\n * neither shadow another family nor a V1 route, and a family whose module has\n * not landed is a plain 404 that touches nothing else.\n *\n * `/api/verse/fleet/history` is A8's (Track A) and `/api/verse/fleet` is the\n * V2.2 control route: Track B's live fleet is `/api/verse/fleet/live` ONLY.\n */\nexport const WORKBENCH_ROUTE_FAMILIES = [\n {\n id: 'activity',\n owner: 'C1',\n module: 'src/core/verse/activity-api.ts',\n handler: 'handleActivityApi',\n prefixes: ['/api/verse/activity', '/api/verse/session-meta'],\n },\n {\n id: 'session-controls',\n owner: 'C3',\n module: 'src/core/verse/session-controls-api.ts',\n handler: 'handleSessionControlsApi',\n prefixes: ['/api/verse/session-controls', '/api/verse/attachments', '/api/verse/queue', '/api/verse/files'],\n },\n {\n id: 'terminal',\n owner: 'C4',\n module: 'src/core/verse/terminal-api.ts',\n handler: 'handleTerminalApi',\n prefixes: ['/api/verse/terminal'],\n },\n {\n id: 'preview',\n owner: 'C4',\n module: 'src/core/verse/preview-api.ts',\n handler: 'handlePreviewApi',\n prefixes: ['/api/verse/preview'],\n },\n { id: 'git', owner: 'C5', module: 'src/core/verse/git-api.ts', handler: 'handleGitApi', prefixes: ['/api/verse/git'] },\n { id: 'apps', owner: 'C6', module: 'src/core/verse/apps-api.ts', handler: 'handleAppsApi', prefixes: ['/api/verse/apps'] },\n {\n id: 'authority',\n owner: 'B-U1',\n module: 'src/core/verse/authority-api.ts',\n handler: 'handleAuthorityApi',\n prefixes: ['/api/verse/authority'],\n },\n {\n id: 'overnight',\n owner: 'B-U5',\n module: 'src/core/verse/overnight-api.ts',\n handler: 'handleOvernightApi',\n prefixes: ['/api/verse/overnight'],\n },\n {\n id: 'fleet-live',\n owner: 'B-U5',\n module: 'src/core/verse/fleet-live-api.ts',\n handler: 'handleFleetLiveApi',\n prefixes: ['/api/verse/fleet/live'],\n },\n {\n id: 'leader',\n owner: 'B-U8',\n module: 'src/core/verse/leader-api.ts',\n handler: 'handleLeaderApi',\n prefixes: ['/api/verse/leader'],\n },\n {\n id: 'learning',\n owner: 'B-U9',\n module: 'src/core/verse/learning-api.ts',\n handler: 'handleLearningApi',\n prefixes: ['/api/verse/learning'],\n },\n] as const satisfies readonly {\n id: string;\n owner: string;\n module: string;\n handler: string;\n prefixes: readonly string[];\n}[];\n\nexport type WorkbenchRouteFamily = (typeof WORKBENCH_ROUTE_FAMILIES)[number];\nexport type WorkbenchRouteFamilyId = WorkbenchRouteFamily['id'];\n\n/** Exact path or path + '/…' — the only prefix rule the mount uses. */\nexport function routePrefixOwns(prefix: string, path: string): boolean {\n return path === prefix || path.startsWith(`${prefix}/`);\n}\n\n/** The family that owns `path`, or null. Prefixes never overlap (tested), so at most one matches. */\nexport function workbenchFamilyFor(path: string): WorkbenchRouteFamily | null {\n for (const family of WORKBENCH_ROUTE_FAMILIES) {\n if (family.prefixes.some((prefix) => routePrefixOwns(prefix, path))) return family;\n }\n return null;\n}\n","/**\n * routes/verse/resources/resources-store.ts — is the Resources drawer open,\n * and is it pinned (unit 3.11 C6).\n *\n * overlay ~360px over the surface; Esc, an outside click or ⌘. closes it,\n * focus is trapped inside and returned on close;\n * pinned a docked right column that shrinks the surface beside it.\n *\n * PERSISTED under its own key, one writer (this module), every access in a\n * try/catch: a private window without storage still runs the drawer. The\n * shell's `ashlr.verse.ui.v3` blob is not touched — its `resourcesOpen`\n * field belongs to an older panel and has one writer already.\n *\n * On load the drawer comes back open only when it was PINNED open: a docked\n * column is a layout preference, whereas an overlay restored on reload would\n * steal focus from whatever the operator came back to do.\n *\n * Kept tiny and framework-free apart from one hook: this file is imported by\n * the shell, so it is on the chat first-paint path.\n */\nimport { useSyncExternalStore } from 'react';\nimport type { ResourcesSummary } from './resources-model.js';\n\nexport const RESOURCES_STORAGE_KEY = 'ashlr.verse.resources.v1';\n\nexport interface ResourcesUiState {\n open: boolean;\n pinned: boolean;\n /** The always-on resource bar in the rail foot (3.11.1). On by default; persisted. */\n bar: boolean;\n /**\n * The handle's dot, written by the lazily loaded summary probe\n * (resources-summary.tsx). NOT persisted; null until the probe has read.\n */\n summary: ResourcesSummary | null;\n}\n\nfunction load(): ResourcesUiState {\n try {\n const raw = JSON.parse(localStorage.getItem(RESOURCES_STORAGE_KEY) ?? 'null') as Partial<ResourcesUiState> | null;\n const pinned = raw?.pinned === true;\n return { pinned, open: pinned && raw?.open === true, bar: raw?.bar !== false, summary: null };\n } catch {\n return { open: false, pinned: false, bar: true, summary: null };\n }\n}\n\nlet state: ResourcesUiState = load();\nconst listeners = new Set<() => void>();\n\nfunction patch(delta: Partial<ResourcesUiState>): void {\n const next = { ...state, ...delta };\n if (next.open === state.open && next.pinned === state.pinned && next.bar === state.bar && next.summary === state.summary) return;\n const persist = next.open !== state.open || next.pinned !== state.pinned || next.bar !== state.bar;\n state = next;\n if (persist) {\n try {\n localStorage.setItem(RESOURCES_STORAGE_KEY, JSON.stringify({ open: next.open, pinned: next.pinned, bar: next.bar }));\n } catch {\n /* best-effort */\n }\n }\n for (const l of [...listeners]) l();\n}\n\nexport function getResourcesUi(): ResourcesUiState {\n return state;\n}\n\nexport function openResources(): void {\n patch({ open: true });\n}\n\nexport function closeResources(): void {\n patch({ open: false });\n}\n\nexport function toggleResources(): void {\n patch({ open: !state.open });\n}\n\nexport function setResourcesPinned(pinned: boolean): void {\n patch({ pinned });\n}\n\n/** Show or hide the always-on resource bar in the rail foot. */\nexport function setResourcesBar(bar: boolean): void {\n patch({ bar });\n}\n\nexport function setResourcesSummary(summary: ResourcesSummary | null): void {\n const same = summary !== null && state.summary !== null && summary.tone === state.summary.tone && summary.spoken === state.summary.spoken;\n if (!same) patch({ summary });\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function useResourcesUi(): ResourcesUiState {\n return useSyncExternalStore(subscribe, getResourcesUi, getResourcesUi);\n}\n\n/** Test hygiene: re-read storage as a fresh page load would. */\nexport function reloadResourcesUiForTest(): void {\n state = load();\n for (const l of [...listeners]) l();\n}\n","/**\n * routes/verse/shell/preloaded.tsx — chunks that start downloading when the\n * module DECLARING them evaluates, and render synchronously once they are in.\n *\n * Moved out of sections/ChatSection.tsx so the shell (guarded-action.tsx)\n * can keep its dialogs off the chat first-paint path the same way the Chat\n * section keeps its workspace, chat list and dialogs off it. A preloaded\n * dialog that has landed mounts in the same render that opens it, exactly as\n * a static import would; one opened in the few milliseconds before its chunk\n * lands shows a moment later.\n */\nimport { lazy, useEffect, useState, type ComponentType } from 'react';\n\n/**\n * A component in its own chunk whose download starts the moment the module\n * declaring it evaluates — not when React first renders it — so it streams in\n * parallel with the chat's first paint instead of after it.\n *\n * WHY (SPEC-310A §1: chat first-paint critical JS ≤ 350 KB, measured by\n * scripts/check-first-paint-budget.mjs). React + react-dom alone are ~220 KB\n * of that; the chat list, the transcript, the composer and the markdown\n * renderer were another ~290 KB in this chunk. The section's frame (its\n * grid, the banners, every mutation and key handler) paints from this chunk;\n * the workspace and the chat list paint a beat later from theirs, over\n * skeletons in their own shape so nothing jumps.\n *\n * Once the module is in, every later mount renders it synchronously (no\n * Suspense flash when you come back to Chat). Which of the two a mount uses\n * is fixed for that mount's life: swapping the lazy wrapper for the loaded\n * component mid-life would remount the subtree and drop its state — the\n * composer's draft, the transcript's scroll.\n */\nexport function preloadedLazy<P extends object>(load: () => Promise<ComponentType<P>>): { Slot: ComponentType<P>; ready: () => Promise<ComponentType<P>> } {\n let loaded: ComponentType<P> | null = null;\n let pending: Promise<ComponentType<P>> | null = null;\n const ready = (): Promise<ComponentType<P>> => {\n // A failed download is not cached: the next mount (or retry) asks again.\n pending ??= load().then((c) => (loaded = c), (err: unknown) => { pending = null; throw err; });\n return pending;\n };\n const Lazy = lazy(() => ready().then((c) => ({ default: c })));\n function Slot(props: P) {\n const [Ready] = useState<ComponentType<P> | null>(() => loaded);\n return Ready ? <Ready {...props} /> : <Lazy {...props} />;\n }\n if (typeof window !== 'undefined') void ready().catch(() => undefined);\n return { Slot, ready };\n}\n\n/**\n * A module (not a component) on the same terms as preloadedLazy: fetched when\n * the declaring module evaluates, read synchronously once in. useLoaded() re-renders\n * its caller once, when it lands; a failed load leaves it null (the values it\n * would have computed stay empty) and is retried on the next mount.\n */\nexport function preloadedModule<M>(load: () => Promise<M>): { ready: () => Promise<M>; useLoaded: () => M | null } {\n let loaded: M | null = null;\n let pending: Promise<M> | null = null;\n const ready = (): Promise<M> => {\n pending ??= load().then((m) => (loaded = m), (err: unknown) => { pending = null; throw err; });\n return pending;\n };\n function useLoaded(): M | null {\n const [mod, setMod] = useState<{ current: M | null }>(() => ({ current: loaded }));\n useEffect(() => {\n if (mod.current) return undefined;\n let live = true;\n ready().then((m) => { if (live) setMod({ current: m }); }, () => undefined);\n return () => { live = false; };\n }, [mod]);\n return mod.current;\n }\n if (typeof window !== 'undefined') void ready().catch(() => undefined);\n return { ready, useLoaded };\n}\n","/**\n * routes/verse/shell/guarded-action.tsx — confirmation FIRST, then the\n * mutation token, then the action (unit C1; SPEC-310C §1 \"Guarded actions ask\n * for confirmation, then the mutation token\").\n *\n * The palette and the drawer's single-letter keys are the fastest way to do\n * anything in Verse, which is exactly why they must not be a shortcut around\n * the confirm step the regular UI requires. Every guarded path — a catalog\n * command with a `guard`, a Needs-you approve / reject / veto — goes through\n * ONE host, so the order can never differ between two entry points:\n *\n * confirm ──Cancel──▶ nothing happened\n * │ Confirm\n * ├─ token needed and not held ─▶ MutationTokenDialog ──Cancel──▶ nothing\n * │ │ unlocked\n * ▼ ▼\n * running (the confirm dialog shows \"Working…\")\n * ├─ ok ─▶ closed (the caller's own onDone decides the toast)\n * └─ error ─▶ back to confirm, with the sentence; Confirm retries\n *\n * Why confirm before token (the old ApprovalDetail asked for the token\n * first): an operator who presses A by mistake should be able to back out at\n * the FIRST prompt, which should describe what is about to happen — not a\n * prompt asking for a secret.\n */\nimport { Suspense, useSyncExternalStore, type ComponentProps, type ReactNode } from 'react';\nimport type { MutationTokenDialog as MutationTokenDialogComponent } from '../../../components/auth/MutationTokenDialog.js';\nimport { hasMutationHold } from '../../../data/auth-store.js';\nimport { ApiError, DispatchDisabledError } from '../../../data/client.js';\nimport type { ConfirmDialog as ConfirmDialogComponent } from '../../inbox/ConfirmDialog.js';\nimport { preloadedLazy } from './preloaded.js';\n\n/**\n * The two dialogs are preloaded, not static imports: GuardHost is mounted by\n * the shell at first paint but draws nothing until a guarded action starts,\n * and the dialogs (with the dialog primitive and its focus trap) were ~4 KB\n * of chat first-paint critical JS. Their download starts when this module\n * evaluates, so the first guarded action finds them in and they mount in the\n * same render that opens them.\n */\nconst ConfirmDialogModule = preloadedLazy<ComponentProps<typeof ConfirmDialogComponent>>(\n () => import('../../inbox/ConfirmDialog.js').then((m) => m.ConfirmDialog),\n);\nconst ConfirmDialog = ConfirmDialogModule.Slot;\nconst TokenDialogModule = preloadedLazy<ComponentProps<typeof MutationTokenDialogComponent>>(\n () => import('../../../components/auth/MutationTokenDialog.js').then((m) => m.MutationTokenDialog),\n);\nconst MutationTokenDialog = TokenDialogModule.Slot;\n\n/** Resolves once both dialogs are in (tests await it; the app never needs to). */\nexport function preloadGuardDialogs(): Promise<unknown> {\n return Promise.all([ConfirmDialogModule.ready(), TokenDialogModule.ready()]);\n}\n\nexport interface GuardedRequest {\n title: string;\n body: ReactNode;\n confirmLabel: string;\n destructive: boolean;\n /** Ask for the mutation token before running (every server write does). */\n token: boolean;\n /** Why the token is needed, in the token dialog's own words. */\n tokenReason?: string;\n run: () => Promise<void>;\n /**\n * For actions the regular UI does not confirm either (Mark read, Reconnect\n * with no item copy): straight to the token (if needed), then run; a\n * failure goes to `onError` only, since there is no dialog to show it in.\n */\n skipConfirm?: boolean;\n onDone?: () => void;\n /** Called with the sentence when the run fails (after the dialog shows it). */\n onError?: (message: string) => void;\n}\n\ntype Phase = 'confirm' | 'token' | 'running';\n\ninterface GuardState {\n request: GuardedRequest | null;\n phase: Phase;\n error: string | null;\n}\n\nconst IDLE: GuardState = { request: null, phase: 'confirm', error: null };\nlet state: GuardState = IDLE;\nconst listeners = new Set<() => void>();\n\nfunction set(next: GuardState): void {\n state = next;\n for (const l of [...listeners]) l();\n}\n\n/** Start a guarded action. A second request while one is open replaces nothing: it is ignored. */\nexport function requestGuarded(request: GuardedRequest): boolean {\n if (state.request) return false;\n if (request.skipConfirm) {\n if (request.token && !hasMutationHold()) set({ request, phase: 'token', error: null });\n else void execute(request);\n return true;\n }\n set({ request, phase: 'confirm', error: null });\n return true;\n}\n\nexport function isGuardOpen(): boolean {\n return state.request !== null;\n}\n\n/** Test hygiene. */\nexport function resetGuard(): void {\n set(IDLE);\n}\n\n/** Operator language for a failed write — never a stack, never a path. */\nexport function describeActionError(err: unknown): string {\n if (err instanceof DispatchDisabledError) return 'This server was started without dispatch, so actions are unavailable. Run `ashlr verse` to act.';\n if (err instanceof ApiError) {\n if (err.status === 401) return 'The mutation token was rejected. Unlock again with the token `ashlr verse` printed.';\n if (err.status === 409) return err.detail ?? 'Something changed underneath this action. Refresh and try again.';\n return err.detail ?? `The server refused (HTTP ${err.status}).`;\n }\n return err instanceof Error && err.message ? err.message : 'Something went wrong.';\n}\n\nasync function execute(request: GuardedRequest): Promise<void> {\n set({ request, phase: 'running', error: null });\n try {\n await request.run();\n set(IDLE);\n request.onDone?.();\n } catch (err) {\n const message = describeActionError(err);\n set(request.skipConfirm ? IDLE : { request, phase: 'confirm', error: message });\n request.onError?.(message);\n }\n}\n\nfunction confirm(): void {\n const { request } = state;\n if (!request || state.phase === 'running') return;\n if (request.token && !hasMutationHold()) {\n set({ request, phase: 'token', error: null });\n return;\n }\n void execute(request);\n}\n\nfunction cancel(): void {\n if (state.phase === 'running') return;\n set(IDLE);\n}\n\nfunction tokenClosed(): void {\n const { request } = state;\n if (!request) return;\n // MutationTokenDialog closes synchronously after storing the token, so a\n // live read says whether this close was \"unlocked\" or \"cancelled\".\n if (hasMutationHold()) void execute(request);\n else set(IDLE);\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function useGuardState(): GuardState {\n return useSyncExternalStore(subscribe, () => state, () => state);\n}\n\n/** Mounted once by the shell. */\nexport function GuardHost() {\n const { request, phase, error } = useGuardState();\n if (!request) return null;\n return (\n <Suspense fallback={null}>\n <ConfirmDialog\n open={phase !== 'token' && !request.skipConfirm}\n onClose={cancel}\n title={request.title}\n body={request.body}\n confirmLabel={error ? 'Try again' : request.confirmLabel}\n destructive={request.destructive}\n busy={phase === 'running'}\n error={error}\n onConfirm={confirm}\n />\n <MutationTokenDialog\n open={phase === 'token'}\n onClose={tokenClosed}\n reason={request.tokenReason ?? 'This action changes state on this machine and requires the dispatch token.'}\n tokenHelp=\"the mutation token ashlr verse printed\"\n />\n </Suspense>\n );\n}\n","/**\n * routes/verse/shell/anchor-requests.ts — the shell's one listener for\n * VERSE_ANCHOR_EVENT (\"go to that card\"); the reveal itself is\n * shell/reveal-anchor.ts.\n *\n * WHY SEPARATE: VerseApp installs this listener at first paint, but nothing\n * is revealed until an operator follows a link into a surface. The reveal\n * logic (and command/nav.ts with it) is preloaded instead of imported: its\n * download starts when this module evaluates, and once it is in a request\n * is revealed synchronously, exactly as with a static import. A request\n * that arrives before it lands is revealed when it does — the reveal waits\n * for its element for up to ANCHOR_WAIT_MS anyway.\n */\nimport type { VerseAnchorRequest } from '../verse-ui-store.js';\n\ntype RevealModule = typeof import('./reveal-anchor.js');\n\nlet loaded: RevealModule | null = null;\nlet pending: Promise<RevealModule> | null = null;\nfunction loadReveal(): Promise<RevealModule> {\n // A failed download is not cached: the next request asks again.\n pending ??= import('./reveal-anchor.js').then((m) => (loaded = m), (err: unknown) => { pending = null; throw err; });\n return pending;\n}\nif (typeof window !== 'undefined') void loadReveal().catch(() => undefined);\n\nfunction reveal(request: VerseAnchorRequest): void {\n if (loaded) {\n void loaded.revealAnchor(request);\n return;\n }\n void loadReveal().then((m) => m.revealAnchor(request), () => undefined);\n}\n\n/** The shell's one listener. Returns the unsubscribe. */\nexport function subscribeAnchorRequests(eventName: string): () => void {\n if (typeof window === 'undefined') return () => {};\n function onAnchor(event: Event): void {\n const detail = (event as CustomEvent<unknown>).detail;\n if (typeof detail !== 'object' || detail === null) return;\n const { section, anchor } = detail as Record<string, unknown>;\n if (typeof section !== 'string' || typeof anchor !== 'string' || anchor.length === 0 || anchor.length > 200) return;\n reveal({ section: section as VerseAnchorRequest['section'], anchor });\n }\n window.addEventListener(eventName, onAnchor);\n return () => window.removeEventListener(eventName, onAnchor);\n}\n","/**\n * routes/verse/shell/viewport.ts — the workbench's width classes (unit C0).\n *\n * ONE set of breakpoints for every 3.10 surface, so the dock, the drawer, the\n * surfaces and the composer footer all change shape at the same widths\n * (SPEC-310C §1–§5):\n *\n * compact < 480 the phone layout the 375 screenshots show: one column,\n * bottom sheets (dock at 75vh), the composer footer folds\n * its pickers into a sheet, charts render at width 375.\n * medium 480–1023 the dock and the Needs-you drawer become sheets over\n * the chat instead of columns beside it.\n * wide ≥ 1024 the full workbench: rail, sidebar, transcript, dock.\n *\n * Read through matchMedia — the browser's own answer, which is also what the\n * test support mocks — never by polling window size.\n */\nimport { useMemo, useSyncExternalStore } from 'react';\n\nexport const VIEWPORT_BREAKPOINTS = Object.freeze({\n /** Below this width: the compact (phone) layout. */\n compact: 480,\n /** At or above this width: the wide workbench (dock as a column). */\n wide: 1024,\n});\n\nexport type ViewportClass = 'compact' | 'medium' | 'wide';\n\n/**\n * The two queries every class derives from. `max-width` uses .98 so the\n * compact and medium ranges meet without overlapping at fractional widths\n * (the same convention Bootstrap and Tailwind use for zoomed displays).\n */\nexport const VIEWPORT_QUERIES = Object.freeze({\n compact: `(max-width: ${VIEWPORT_BREAKPOINTS.compact - 0.02}px)`,\n wide: `(min-width: ${VIEWPORT_BREAKPOINTS.wide}px)`,\n});\n\n/** The class a width falls in (pure; for layout math and tests). */\nexport function viewportClassFor(width: number): ViewportClass {\n if (width < VIEWPORT_BREAKPOINTS.compact) return 'compact';\n if (width < VIEWPORT_BREAKPOINTS.wide) return 'medium';\n return 'wide';\n}\n\nfunction currentClass(): ViewportClass {\n if (typeof window === 'undefined') return 'wide';\n if (typeof window.matchMedia === 'function') {\n if (window.matchMedia(VIEWPORT_QUERIES.compact).matches) return 'compact';\n return window.matchMedia(VIEWPORT_QUERIES.wide).matches ? 'wide' : 'medium';\n }\n // jsdom without a mock, and old webviews: the window's own width.\n return viewportClassFor(window.innerWidth || VIEWPORT_BREAKPOINTS.wide);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n if (typeof window === 'undefined') return () => {};\n if (typeof window.matchMedia !== 'function') {\n window.addEventListener('resize', onChange);\n return () => window.removeEventListener('resize', onChange);\n }\n const lists = [window.matchMedia(VIEWPORT_QUERIES.compact), window.matchMedia(VIEWPORT_QUERIES.wide)];\n for (const list of lists) list.addEventListener('change', onChange);\n return () => {\n for (const list of lists) list.removeEventListener('change', onChange);\n };\n}\n\nexport interface ViewportState {\n viewport: ViewportClass;\n /** < 480: phone layout. */\n compact: boolean;\n /** ≥ 1024: the full workbench. */\n wide: boolean;\n}\n\n/** The current width class; re-renders only when the CLASS changes, not on every resize. */\nexport function useViewport(): ViewportState {\n const viewport = useSyncExternalStore(subscribe, currentClass, () => 'wide' as const);\n return useMemo(() => ({ viewport, compact: viewport === 'compact', wide: viewport === 'wide' }), [viewport]);\n}\n\n/** Non-hook read, for event handlers and effects. */\nexport function readViewport(): ViewportClass {\n return currentClass();\n}\n","/**\n * routes/verse/shell/dock-catalog.ts — the chat dock's panes, layout rules\n * and persisted state (unit C0; SPEC-310C §3).\n *\n * The dock is C2's container; its panes belong to four units (C2 Tasks and\n * Context, C4 Terminal and Preview, C5 Review). This file is what they agree\n * on: which panes exist, which command opens each, which render through a\n * slot (slots.tsx, so the container never imports another unit's code), how\n * wide the dock may be, when it becomes a sheet, and the shape it persists.\n *\n * Pure data and pure functions — no React.\n */\nimport type { CommandId } from './command-catalog.js';\nimport { VIEWPORT_BREAKPOINTS } from './viewport.js';\n\nexport const DOCK_PANES = [\n { id: 'terminal', label: 'Terminal', owner: 'C4', commandId: 'dock.terminal', slot: 'terminal-pane' },\n { id: 'preview', label: 'Preview', owner: 'C4', commandId: 'dock.preview', slot: 'preview-pane' },\n { id: 'diff', label: 'Review', owner: 'C5', commandId: 'dock.diff', slot: 'diff-pane' },\n { id: 'tasks', label: 'Tasks', owner: 'C2', commandId: null, slot: null },\n { id: 'context', label: 'Context', owner: 'C2', commandId: null, slot: null },\n] as const satisfies readonly {\n id: string;\n label: string;\n owner: string;\n /** The catalog command that opens (and focuses) this pane; null = opened from its own affordance. */\n commandId: CommandId | null;\n /** Rendered through this slot when the pane is another unit's; null = C2's own component. */\n slot: 'terminal-pane' | 'preview-pane' | 'diff-pane' | null;\n}[];\n\nexport type DockPaneId = (typeof DOCK_PANES)[number]['id'];\nexport const DOCK_PANE_IDS: readonly DockPaneId[] = DOCK_PANES.map((p) => p.id);\n\nexport function isDockPaneId(value: unknown): value is DockPaneId {\n return typeof value === 'string' && (DOCK_PANE_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Layout rules. Widths in CSS px, measured against the WINDOW, not the chat\n * column: \"resizable from 320px to 60% of the window\".\n */\nexport const DOCK_LAYOUT = Object.freeze({\n minWidth: 320,\n maxWidthFraction: 0.6,\n /** A new dock opens this wide (the 440 of the SPEC-310C shell sketch). */\n defaultWidth: 440,\n /** Below this window width the dock is a sheet over the chat, not a column. */\n sheetBelow: VIEWPORT_BREAKPOINTS.wide,\n /** Below this window width it is a bottom sheet… */\n bottomSheetBelow: VIEWPORT_BREAKPOINTS.compact,\n /** …this tall, in vh. */\n bottomSheetHeightVh: 75,\n /** The top pane's share of the height in a vertical split. */\n splitRatio: Object.freeze({ min: 0.2, max: 0.8, default: 0.5 }),\n});\n\nexport type DockPresentation = 'column' | 'sheet' | 'bottom-sheet';\n\n/** How the dock is drawn at a given window width. */\nexport function dockPresentation(windowWidth: number): DockPresentation {\n if (windowWidth < DOCK_LAYOUT.bottomSheetBelow) return 'bottom-sheet';\n if (windowWidth < DOCK_LAYOUT.sheetBelow) return 'sheet';\n return 'column';\n}\n\n/**\n * A persisted or dragged width clamped for this window. The floor wins over\n * the 60% cap on a window too narrow to honour both (it is a sheet there\n * anyway), so the result is never below minWidth.\n */\nexport function clampDockWidth(width: unknown, windowWidth: number): number {\n const n = typeof width === 'number' && Number.isFinite(width) ? Math.round(width) : DOCK_LAYOUT.defaultWidth;\n const max = Math.max(DOCK_LAYOUT.minWidth, Math.floor(windowWidth * DOCK_LAYOUT.maxWidthFraction));\n return Math.min(max, Math.max(DOCK_LAYOUT.minWidth, n));\n}\n\n// ---------------------------------------------------------------------------\n// Persisted state\n// ---------------------------------------------------------------------------\n\n/** The v2 key C1 migrates FROM (verse-ui-store.ts). */\nexport const VERSE_UI_STORAGE_KEY_V2 = 'ashlr.verse.ui.v2';\n/**\n * The v3 key (SPEC-310C §3 \"State persists in ashlr.verse.ui.v3\"). ONE blob,\n * written by C1's verse-ui-store; the dock's state is its `dock` field, read\n * back through sanitizeDockState — so two writers never race for the key.\n */\nexport const VERSE_UI_STORAGE_KEY = 'ashlr.verse.ui.v3';\n\nexport interface DockState {\n open: boolean;\n /** Last dragged width; clamp with clampDockWidth at render (it depends on the window). */\n width: number;\n /** Open panes, in tab order. */\n tabs: DockPaneId[];\n /** The pane shown (on top, when split); null only when there are no tabs. */\n active: DockPaneId | null;\n /** A second pane shown BELOW `active` (\"Preview over Terminal\"); never equal to it. */\n splitWith: DockPaneId | null;\n /** `active`'s share of the height when split. */\n splitRatio: number;\n}\n\nexport const DEFAULT_DOCK_STATE: Readonly<DockState> = Object.freeze({\n open: false,\n width: DOCK_LAYOUT.defaultWidth,\n tabs: [],\n active: null,\n splitWith: null,\n splitRatio: DOCK_LAYOUT.splitRatio.default,\n});\n\n/**\n * Read a persisted `dock` field without trusting it: unknown panes dropped,\n * duplicates collapsed, `active`/`splitWith` forced to open tabs, numbers\n * clamped. Anything unreadable falls back to the default, never throws — a\n * hand-edited or future-version blob must not take the chat down.\n */\nexport function sanitizeDockState(value: unknown): DockState {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return { ...DEFAULT_DOCK_STATE, tabs: [] };\n const raw = value as Record<string, unknown>;\n const tabs: DockPaneId[] = [];\n if (Array.isArray(raw['tabs'])) {\n for (const tab of raw['tabs']) if (isDockPaneId(tab) && !tabs.includes(tab)) tabs.push(tab);\n }\n const active: DockPaneId | null = isDockPaneId(raw['active']) && tabs.includes(raw['active']) ? raw['active'] : (tabs[0] ?? null);\n const splitWith: DockPaneId | null =\n isDockPaneId(raw['splitWith']) && tabs.includes(raw['splitWith']) && raw['splitWith'] !== active ? raw['splitWith'] : null;\n const width = typeof raw['width'] === 'number' && Number.isFinite(raw['width']) && raw['width'] >= DOCK_LAYOUT.minWidth\n ? Math.round(raw['width'])\n : DOCK_LAYOUT.defaultWidth;\n const ratio = typeof raw['splitRatio'] === 'number' && Number.isFinite(raw['splitRatio']) ? raw['splitRatio'] : DOCK_LAYOUT.splitRatio.default;\n return {\n open: raw['open'] === true && tabs.length > 0,\n width,\n tabs,\n active,\n splitWith,\n splitRatio: Math.min(DOCK_LAYOUT.splitRatio.max, Math.max(DOCK_LAYOUT.splitRatio.min, ratio)),\n };\n}\n","/**\n * routes/verse/dock/dock-store.ts — the chat dock's state and the one-shot\n * requests aimed at its panes (unit C2; contract: shell/dock-catalog.ts).\n *\n * PERSISTENCE. The dock's state belongs in `ashlr.verse.ui.v3`'s `dock`\n * field, and that blob has ONE writer — C1's verse-ui-store — so two stores\n * never race for the key (dock-catalog.ts). This store therefore never\n * writes the key. It:\n * - hydrates itself, read-only, from the v3 blob's `dock` field on first\n * read (through sanitizeDockState — a hand-edited blob cannot break the\n * chat);\n * - exposes getDockState / subscribeDockState, which C1's store folds into\n * the blob it writes (`dock: getDockState()`, re-written on change);\n * - accepts hydrateDockState(raw) when C1 prefers to push the field in.\n *\n * REQUESTS. \"Run in terminal\", a ± count opening the branch diff, Preview's\n * dev-server Start: each is a one-shot request with a nonce (slots.tsx\n * contracts) — the same request twice is two opens. They live here, not in\n * React state, because they are raised from deep inside the transcript\n * (CommandOutput) and consumed by a pane in another subtree.\n *\n * Framework-free; the React glue is useDock() at the bottom.\n */\nimport { useSyncExternalStore } from 'react';\nimport {\n DOCK_LAYOUT,\n VERSE_UI_STORAGE_KEY,\n sanitizeDockState,\n type DockPaneId,\n type DockState,\n} from '../shell/dock-catalog.js';\nimport type { DiffPaneRequest, PreviewOpenRequest, TerminalOpenRequest } from '../shell/slots.js';\n\n/**\n * A terminal request as the dock carries it. C0's TerminalOpenRequest now\n * names the Apps [Launch ▸] fields (`via`, `model`) itself, so this is a\n * plain alias — kept so TerminalPane and the Apps launcher keep one name for\n * \"what the dock hands the Terminal pane\". The server resolves the actual\n * command either way, never the page.\n */\nexport type TerminalRequest = TerminalOpenRequest;\n\nexport interface DockRequests {\n terminal: TerminalRequest | null;\n preview: PreviewOpenRequest | null;\n diff: (DiffPaneRequest & { nonce: number }) | null;\n}\n\nexport interface DockSnapshot {\n state: DockState;\n requests: DockRequests;\n}\n\nconst NO_REQUESTS: DockRequests = { terminal: null, preview: null, diff: null };\n\nlet snapshot: DockSnapshot | null = null;\nlet nonce = 0;\nconst listeners = new Set<() => void>();\n\nfunction readPersisted(): DockState {\n try {\n const raw = localStorage.getItem(VERSE_UI_STORAGE_KEY);\n if (!raw) return sanitizeDockState(null);\n const parsed = JSON.parse(raw) as { dock?: unknown } | null;\n return sanitizeDockState(parsed && typeof parsed === 'object' ? parsed.dock : null);\n } catch {\n return sanitizeDockState(null);\n }\n}\n\nfunction current(): DockSnapshot {\n if (snapshot === null) snapshot = { state: readPersisted(), requests: NO_REQUESTS };\n return snapshot;\n}\n\nfunction emit(next: DockSnapshot): void {\n snapshot = next;\n for (const listener of [...listeners]) listener();\n}\n\nfunction setState(next: DockState): void {\n emit({ ...current(), state: next });\n}\n\nexport function subscribeDockState(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getDockSnapshot(): DockSnapshot {\n return current();\n}\n\n/** The persistable part — what C1's store writes as the v3 blob's `dock` field. */\nexport function getDockState(): DockState {\n return current().state;\n}\n\n/** C1 may push the persisted field in (on load, or when another tab wrote it). */\nexport function hydrateDockState(raw: unknown): void {\n setState(sanitizeDockState(raw));\n}\n\n/** Test seam: back to defaults with no requests; next read re-hydrates from storage. */\nexport function resetDockStore(): void {\n snapshot = null;\n nonce = 0;\n for (const listener of [...listeners]) listener();\n}\n\n// ---------------------------------------------------------------------------\n// Transitions (pure over DockState, exported for tests)\n// ---------------------------------------------------------------------------\n\n/** Open `pane` (adding its tab) and make it the visible one. */\nexport function withPaneOpen(state: DockState, pane: DockPaneId): DockState {\n const tabs = state.tabs.includes(pane) ? state.tabs : [...state.tabs, pane];\n // Opening the pane that is the split's lower half swaps it to the top.\n const splitWith = state.splitWith === pane ? (state.active !== pane ? state.active : null) : state.splitWith;\n return { ...state, open: true, tabs, active: pane, splitWith: splitWith === pane ? null : splitWith };\n}\n\n/** The pane's toggle: open & focus it, or — when it is already the visible pane — close the dock. */\nexport function withPaneToggled(state: DockState, pane: DockPaneId): DockState {\n if (state.open && (state.active === pane || state.splitWith === pane)) return { ...state, open: false };\n return withPaneOpen(state, pane);\n}\n\nexport function withTabClosed(state: DockState, pane: DockPaneId): DockState {\n const tabs = state.tabs.filter((t) => t !== pane);\n const splitWith = state.splitWith === pane ? null : state.splitWith;\n let active = state.active;\n if (active === pane) {\n // The split's lower pane moves up; otherwise the neighbour tab takes over.\n const index = state.tabs.indexOf(pane);\n active = splitWith ?? tabs[Math.min(index, tabs.length - 1)] ?? null;\n }\n return {\n ...state,\n tabs,\n active,\n splitWith: splitWith === active ? null : splitWith,\n open: tabs.length > 0 && state.open,\n };\n}\n\n/** Split `pane` BELOW the active one (\"Preview over Terminal\"); null un-splits. */\nexport function withSplit(state: DockState, pane: DockPaneId | null): DockState {\n if (pane === null) return { ...state, splitWith: null };\n if (state.active === null || pane === state.active) return state;\n const tabs = state.tabs.includes(pane) ? state.tabs : [...state.tabs, pane];\n return { ...state, open: true, tabs, splitWith: pane };\n}\n\nexport function withWidth(state: DockState, width: number): DockState {\n if (!Number.isFinite(width)) return state;\n return { ...state, width: Math.max(DOCK_LAYOUT.minWidth, Math.round(width)) };\n}\n\nexport function withSplitRatio(state: DockState, ratio: number): DockState {\n if (!Number.isFinite(ratio)) return state;\n const { min, max } = DOCK_LAYOUT.splitRatio;\n return { ...state, splitRatio: Math.min(max, Math.max(min, ratio)) };\n}\n\n// ---------------------------------------------------------------------------\n// Actions\n// ---------------------------------------------------------------------------\n\nexport function openDockPane(pane: DockPaneId): void {\n setState(withPaneOpen(current().state, pane));\n}\n\nexport function toggleDockPane(pane: DockPaneId): void {\n setState(withPaneToggled(current().state, pane));\n}\n\n/** ⌘\\ — show or hide the dock as it was; an empty dock opens on Tasks. */\nexport function toggleDock(): void {\n const state = current().state;\n if (state.open) setState({ ...state, open: false });\n else if (state.tabs.length === 0) setState(withPaneOpen(state, 'tasks'));\n else setState({ ...state, open: true, active: state.active ?? state.tabs[0]! });\n}\n\nexport function closeDock(): void {\n const state = current().state;\n if (state.open) setState({ ...state, open: false });\n}\n\nexport function closeDockTab(pane: DockPaneId): void {\n setState(withTabClosed(current().state, pane));\n}\n\nexport function splitDock(pane: DockPaneId | null): void {\n setState(withSplit(current().state, pane));\n}\n\nexport function setDockWidth(width: number): void {\n setState(withWidth(current().state, width));\n}\n\nexport function setDockSplitRatio(ratio: number): void {\n setState(withSplitRatio(current().state, ratio));\n}\n\n/** Open the Terminal pane with a request (a tab at a root, a pasted command, an app launch). */\nexport function requestTerminal(request: Omit<TerminalRequest, 'nonce'>): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'terminal'), requests: { ...snap.requests, terminal: { ...request, nonce } } });\n}\n\n/**\n * The same request, but with Terminal as the LOWER half of a split under the\n * pane that is showing (Preview's dev-server Start: the page stays on top and\n * appears when its port answers, the server's output runs underneath — the\n * \"Preview over Terminal\" layout). With nothing showing, or when Terminal is\n * the pane on top, it is a plain open. In the bottom sheet the split's lower\n * pane is mounted but hidden, so the request still runs.\n */\nexport function requestTerminalBelow(request: Omit<TerminalRequest, 'nonce'>): void {\n const snap = current();\n const top = snap.state.open ? snap.state.active : null;\n if (top === null || top === 'terminal') {\n requestTerminal(request);\n return;\n }\n nonce += 1;\n emit({ state: withSplit(snap.state, 'terminal'), requests: { ...snap.requests, terminal: { ...request, nonce } } });\n}\n\nexport function requestPreview(request: Omit<PreviewOpenRequest, 'nonce'>): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'preview'), requests: { ...snap.requests, preview: { ...request, nonce } } });\n}\n\nexport function requestDiff(request: DiffPaneRequest): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'diff'), requests: { ...snap.requests, diff: { ...request, nonce } } });\n}\n\n/**\n * Requests are per chat: switching chats must not replay the last chat's\n * terminal paste. Callers clear on a SWITCH only — never on the Chat\n * section's first mount, which is exactly when an Apps [Launch ▸] (raised on\n * another surface, then `setVerseSection('chat')`) is waiting to be served.\n */\nexport function clearDockRequests(): void {\n const snap = current();\n if (snap.requests === NO_REQUESTS) return;\n emit({ ...snap, requests: NO_REQUESTS });\n}\n\nexport function useDock(): DockSnapshot {\n return useSyncExternalStore(subscribeDockState, getDockSnapshot, getDockSnapshot);\n}\n","/**\n * routes/verse/shell/nav-history.ts — ⌘[ / ⌘] through surfaces AND chats\n * (unit C1; SPEC-310C §1 \"Keep-alive\").\n *\n * One linear history, like a browser's: visiting a place after going back\n * drops the forward entries. A chat is a place of its own — Chat ▸ A and\n * Chat ▸ B are two entries — so \"back\" after jumping from one chat to another\n * returns to the first chat, not to whatever surface was before Chat.\n *\n * Pure: the ui store holds the value and decides when to push.\n */\nimport type { WorkbenchSectionId } from '../../../../core/verse/workbench-types.js';\n\nexport interface NavEntry {\n section: WorkbenchSectionId;\n /** The chat open at the time; only meaningful when `section` is chat. */\n sessionId: string | null;\n}\n\nexport interface NavHistory {\n entries: readonly NavEntry[];\n /** Index of the current entry; -1 only for an empty history. */\n index: number;\n}\n\n/** Bounded: a long day of switching must not grow without limit. */\nexport const NAV_HISTORY_LIMIT = 50;\n\nexport const EMPTY_NAV_HISTORY: NavHistory = Object.freeze({ entries: Object.freeze([]) as readonly NavEntry[], index: -1 });\n\nfunction sameEntry(a: NavEntry | undefined, b: NavEntry): boolean {\n return !!a && a.section === b.section && (a.section !== 'chat' || a.sessionId === b.sessionId);\n}\n\n/** Visit `entry`: drop the forward stack, skip a no-op repeat, cap the length. */\nexport function pushNav(history: NavHistory, entry: NavEntry): NavHistory {\n const normalised: NavEntry = { section: entry.section, sessionId: entry.section === 'chat' ? entry.sessionId : null };\n const current = history.entries[history.index];\n if (sameEntry(current, normalised)) return history;\n // \"Chat, no chat open yet\" followed by the chat that then opened is ONE\n // visit: refine the entry in place (dropping any forward stack) rather\n // than leaving a back-stop that shows an empty chat.\n if (current && current.section === 'chat' && current.sessionId === null && normalised.section === 'chat') {\n const entries = [...history.entries.slice(0, history.index), normalised];\n return { entries, index: entries.length - 1 };\n }\n const kept = history.entries.slice(0, history.index + 1);\n const entries = [...kept, normalised].slice(-NAV_HISTORY_LIMIT);\n return { entries, index: entries.length - 1 };\n}\n\nexport function canGoBack(history: NavHistory): boolean {\n return history.index > 0;\n}\n\nexport function canGoForward(history: NavHistory): boolean {\n return history.index >= 0 && history.index < history.entries.length - 1;\n}\n\n/** Step `delta` (−1 back, +1 forward). Null when there is nowhere to go. */\nexport function stepNav(history: NavHistory, delta: -1 | 1): { history: NavHistory; entry: NavEntry } | null {\n const index = history.index + delta;\n const entry = history.entries[index];\n if (!entry) return null;\n return { history: { entries: history.entries, index }, entry };\n}\n\n/**\n * ⌃Tab through recent chats: the most-recently-used list, current chat first.\n * Consecutive presses inside `windowMs` keep walking the SAME snapshot of\n * the list (the Alt-Tab rule), so ⌃Tab ⌃Tab reaches the third-most-recent\n * chat instead of bouncing between two.\n */\nexport interface RecentCycle {\n order: readonly string[];\n position: number;\n at: number;\n}\n\nexport const RECENT_CYCLE_WINDOW_MS = 1_500;\nexport const RECENT_CHATS_LIMIT = 10;\n\nexport function cycleRecent(\n recent: readonly string[],\n cycle: RecentCycle | null,\n direction: 1 | -1,\n now: number,\n windowMs: number = RECENT_CYCLE_WINDOW_MS,\n): { sessionId: string; cycle: RecentCycle } | null {\n const fresh = !cycle || now - cycle.at > windowMs;\n const order = fresh ? recent : cycle!.order;\n if (order.length < 2) return null;\n const from = fresh ? 0 : cycle!.position;\n const position = (from + direction + order.length) % order.length;\n return { sessionId: order[position]!, cycle: { order, position, at: now } };\n}\n\n/** Move `sessionId` to the front of the MRU list. */\nexport function touchRecent(recent: readonly string[], sessionId: string, limit: number = RECENT_CHATS_LIMIT): string[] {\n return [sessionId, ...recent.filter((id) => id !== sessionId)].slice(0, limit);\n}\n","/**\n * routes/verse/shell/section-modules.ts — every `sections/*Section.tsx` that\n * exists at build time, lazily (unit C1).\n *\n * The rail's surfaces are written by other owners and land at different\n * times (C7's Command/Fleet/Growth/Mind, C6's Apps). A glob yields real\n * per-section code splitting for a module that is present and a missing KEY\n * — not an unresolved import — for one that is not, so the shell builds and\n * runs at every stage of the landing.\n *\n * Lives here rather than in VerseApp.tsx so the framework-free ui store can\n * ask \"has Command landed?\" (for the first-launch-of-the-day rule) without\n * importing the shell component. Keys are re-based to `./sections/<Module>.tsx`\n * — relative to routes/verse, the form VerseApp and VerseConsoleApp's\n * first-paint preload have always used.\n *\n * THE COST OF A GLOB, paid once: a finished section outside `sections/` is\n * invisible, with nothing failing to say so (MCP shipped that way for a\n * release). VerseApp.test.tsx walks this map for every VERSE_SECTIONS entry.\n */\ntype Importer = () => Promise<unknown>;\n\nconst RAW: Record<string, Importer> = import.meta.glob('../sections/*Section.tsx');\n\nexport const SECTION_MODULES: Record<string, Importer> = Object.fromEntries(\n Object.entries(RAW).map(([key, load]) => [key.replace(/^\\.\\.\\//, './'), load]),\n);\n\n/** The importer for one module name (`ChatSection`), or undefined when it has not landed. */\nexport function sectionImporter(module: string, modules: Record<string, Importer> = SECTION_MODULES): Importer | undefined {\n return modules[`./sections/${module}.tsx`];\n}\n","/**\n * routes/verse/verse-ui-store.ts — the workbench shell's own state (unit C1):\n * which surface is on screen and which stay mounted behind it, the overlays\n * (⌘K palette, ⌘J Needs you, ⌘/ shortcuts), back/forward history, recent\n * chats, and the layout preferences the chat reads. Framework-free (one\n * `useSyncExternalStore` hook in useVerseUi.ts), same split as verse-store.ts.\n *\n * PERSISTED under `ashlr.verse.ui.v3` — ONE blob with ONE writer (this\n * module). The dock's state is its `dock` field, owned by C2's dock-store and\n * folded in here on every change (dock-catalog.ts: two writers must never\n * race for the key).\n *\n * v2 → v3 MIGRATION (SPEC-310C §0.1). 3.10 renumbered the rail: ⌘1 Command …\n * ⌘5 Chat, with Settings / Apps / Usage in the gear tray. A saved v2 section\n * migrates through workbench-types `migrateSectionId`: autonomy → fleet,\n * approvals → Command WITH the Needs-you drawer open, mcp → apps. The v2 key\n * is left in place (a downgrade still finds its own state) and is never read\n * again once v3 exists.\n *\n * LAUNCH RULE. The first launch each local day opens Command — the overnight\n * digest is the point of opening the app in the morning. Later launches\n * return to the last surface. A surface whose module has not landed yet is\n * never forced (the rule falls through to the saved section).\n *\n * NOT PERSISTED, on purpose: the overlays, `command` (a one-shot hand-off to\n * the chat), history, the keep-alive list and `activeSessionId` — all\n * evidence about this page's life, not preferences.\n *\n * NOT HERE: appearance (theme, accent, density — data/appearance-store.ts owns\n * <html>), the reasoning display (chat/reasoning-pref.ts, C2), chat panel\n * widths (chat-panel-sizing.ts, C2), and the desktop preferences (system-wide\n * hotkey, notifications) — the desktop app stores those itself\n * (desktop_prefs.rs) and reports what actually took effect (a chord another\n * app holds comes back unregistered), so a copy here could only disagree\n * with it. Settings ▸ Desktop reads app/desktop-shell.ts useDesktopState().\n */\nimport {\n migrateSectionId,\n NEEDS_YOU_CATEGORIES,\n type NeedsYouCategory,\n type WorkbenchSectionId,\n} from '../../../core/verse/workbench-types.js';\nimport { getDockState, subscribeDockState } from './dock/dock-store.js';\nimport { VERSE_UI_STORAGE_KEY as V3_KEY, VERSE_UI_STORAGE_KEY_V2 as V2_KEY } from './shell/dock-catalog.js';\nimport {\n cycleRecent,\n pushNav,\n stepNav,\n touchRecent,\n RECENT_CHATS_LIMIT,\n type NavHistory,\n type RecentCycle,\n} from './shell/nav-history.js';\nimport { sectionImporter } from './shell/section-modules.js';\n\n// ===========================================================================\n// Sections\n// ===========================================================================\n\nexport type VerseSectionId = WorkbenchSectionId;\n\nexport interface VerseSectionEntry {\n id: VerseSectionId;\n label: string;\n /** The file `sections/<module>.tsx` the shell mounts (and first paint preloads). */\n module: string;\n /**\n * Modules to mount while `module` has not landed: Fleet shows the legacy\n * Autonomy panels until C7's FleetSection arrives. Never a guess at a\n * different surface. (Apps no longer falls back to MCP: C6's AppsSection\n * folds MCP in, and a stale MCP page under the \"Apps & Accounts\" name would\n * hide the seats and agents the operator went there for.)\n */\n fallbackModules?: readonly string[];\n /** Rail (⌘1–⌘5) or gear tray. */\n placement: 'rail' | 'tray';\n /** One line for the onboarding tour and the palette. */\n blurb: string;\n}\n\n/**\n * Rail order IS the ⌘1–⌘5 order (SPEC-310B §6); tray entries follow. A\n * section missing from this list is unreachable however complete it is.\n */\nexport const VERSE_SECTIONS: readonly VerseSectionEntry[] = [\n { id: 'command', label: 'Command', module: 'CommandSection', placement: 'rail', blurb: 'What the fleet did, what needs you, and the autonomy switch — the morning read.' },\n { id: 'fleet', label: 'Fleet', module: 'FleetSection', fallbackModules: ['AutonomySection'], placement: 'rail', blurb: 'Every lane live: what is building, what the gates refused, and why each seat was chosen.' },\n { id: 'growth', label: 'Growth', module: 'GrowthSection', placement: 'rail', blurb: 'Is the fleet getting better? Merges, cost per merge and the experiments behind them.' },\n { id: 'mind', label: 'Mind', module: 'MindSection', placement: 'rail', blurb: \"The Leader's memos, what came of each move, and what the reasoning shows.\" },\n { id: 'chat', label: 'Chat', module: 'ChatSection', placement: 'rail', blurb: 'Talk to a seat. Chats are grouped by project and resume where they stopped.' },\n { id: 'settings', label: 'Settings', module: 'SettingsSection', placement: 'tray', blurb: 'Theme, chat, desktop and keyboard — and this tour again.' },\n { id: 'apps', label: 'Apps & Accounts', module: 'AppsSection', placement: 'tray', blurb: 'Seats, terminal agents, local models and MCP servers in one list.' },\n { id: 'usage', label: 'Usage', module: 'UsageSection', placement: 'tray', blurb: 'Which account you can actually use right now, and what it costs.' },\n];\n\nexport const RAIL_SECTIONS: readonly VerseSectionEntry[] = VERSE_SECTIONS.filter((s) => s.placement === 'rail');\nexport const TRAY_SECTIONS: readonly VerseSectionEntry[] = VERSE_SECTIONS.filter((s) => s.placement === 'tray');\n\nconst SECTION_IDS = new Set<string>(VERSE_SECTIONS.map((s) => s.id));\n\nexport function sectionEntry(id: VerseSectionId): VerseSectionEntry {\n return VERSE_SECTIONS.find((s) => s.id === id)!;\n}\n\n/** The module that will actually mount for `id` (primary first, then fallbacks); null when none landed. */\nexport function landedModule(id: VerseSectionId, landed: (module: string) => boolean = (m) => sectionImporter(m) !== undefined): string | null {\n const entry = sectionEntry(id);\n for (const module of [entry.module, ...(entry.fallbackModules ?? [])]) if (landed(module)) return module;\n return null;\n}\n\n// ===========================================================================\n// State\n// ===========================================================================\n\n/**\n * One-shot shell → chat requests. The chat surface is lazily mounted and takes\n * no props, so the shell raises a command here and the chat consumes it by\n * nonce, then clears it.\n * new-chat ⌘N / \"New chat on…\" — optional seat or project prefill\n * quick-switcher (pre-3.10; kept until C2 retires QuickSwitcher)\n * open-session the palette, the drawer, ⌘[ ⌘], ⌃Tab, a notification\n * focus-composer the system-wide hotkey (⌃⌥Space)\n */\nexport type VerseCommandName = 'new-chat' | 'quick-switcher' | 'open-session' | 'focus-composer';\n\nexport interface VerseCommand {\n name: VerseCommandName;\n /** Monotonic; a section reacts when it sees a nonce it has not handled. */\n nonce: number;\n sessionId?: string;\n seatId?: string;\n projectPath?: string;\n}\n\nexport type VerseCommandArgs = Pick<VerseCommand, 'sessionId' | 'seatId' | 'projectPath'>;\n\nexport type VerseOverlay = 'palette' | 'needs-you' | 'shortcuts' | null;\nexport type NeedsYouSplit = 'all' | NeedsYouCategory;\nexport const NEEDS_YOU_SPLITS: readonly NeedsYouSplit[] = ['all', ...NEEDS_YOU_CATEGORIES];\n\nexport interface VerseUiState {\n section: VerseSectionId;\n /** Rail shows labels beside its icons (⌘⇧\\). Defaults collapsed: the 56px rail is shipped muscle memory. */\n railExpanded: boolean;\n sidebarWidth: number;\n sidebarCollapsed: boolean;\n resourcesOpen: boolean;\n resourcesWidth: number;\n /** The last this-many palette actions run (empty query shows them). */\n recentActions: readonly string[];\n /** Most-recently-used chats, current first (⌃Tab). */\n recentChats: readonly string[];\n // ── not persisted ──\n /** Surfaces kept mounted (hidden + inert) behind the current one, most recent first. */\n mounted: readonly VerseSectionId[];\n overlay: VerseOverlay;\n needsYouSplit: NeedsYouSplit;\n /** An item the drawer should select when it opens (from the palette or a notification). */\n needsYouFocus: string | null;\n history: NavHistory;\n /** The chat open in the Chat surface, as reported by the chat (or by the shell when it opened one). */\n activeSessionId: string | null;\n command: VerseCommand | null;\n /** Set when a v2 blob was migrated this load: the shell announces \"Chat moved to ⌘5\" once. */\n announceChatMoved: boolean;\n}\n\nexport const VERSE_SIDEBAR = { min: 240, max: 360, def: 264 } as const;\nexport const VERSE_RESOURCES = { min: 260, max: 480, def: 320 } as const;\n/** Surfaces kept alive besides Chat (which, once visited, always stays). */\nexport const KEEP_ALIVE_SURFACES = 3;\nexport const RECENT_ACTIONS_LIMIT = 5;\n\nexport const VERSE_UI_STORAGE_KEY = V3_KEY;\nexport const VERSE_UI_STORAGE_KEY_V2 = V2_KEY;\n\nconst DEFAULTS: VerseUiState = {\n section: 'chat',\n railExpanded: false,\n sidebarWidth: VERSE_SIDEBAR.def,\n sidebarCollapsed: false,\n resourcesOpen: true,\n resourcesWidth: VERSE_RESOURCES.def,\n recentActions: [],\n recentChats: [],\n mounted: ['chat'],\n overlay: null,\n needsYouSplit: 'all',\n needsYouFocus: null,\n history: { entries: [{ section: 'chat', sessionId: null }], index: 0 },\n activeSessionId: null,\n command: null,\n announceChatMoved: false,\n};\n\nexport function clampWidth(value: unknown, range: { min: number; max: number; def: number }): number {\n const n = Number(value);\n if (!Number.isFinite(n) || n <= 0) return range.def;\n return Math.max(range.min, Math.min(range.max, Math.round(n)));\n}\n\n/** Local calendar day, `YYYY-MM-DD` — \"first launch each day\" is the operator's day, not UTC's. */\nexport function localDay(ms: number): string {\n const d = new Date(ms);\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\ninterface PersistedV3 {\n version: 3;\n section: VerseSectionId;\n railExpanded: boolean;\n sidebarWidth: number;\n sidebarCollapsed: boolean;\n resourcesOpen: boolean;\n resourcesWidth: number;\n recentActions: string[];\n recentChats: string[];\n lastLaunchDay: string | null;\n chatMovedAnnounced: boolean;\n dock: unknown;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction stringList(value: unknown, limit: number, re: RegExp): string[] {\n if (!Array.isArray(value)) return [];\n const out: string[] = [];\n for (const v of value) {\n if (typeof v === 'string' && re.test(v) && !out.includes(v)) out.push(v);\n if (out.length >= limit) break;\n }\n return out;\n}\n\nfunction readJson(key: string): Record<string, unknown> | null {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as unknown;\n return isObject(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/** Bookkeeping that is persisted but is not UI state. */\nlet lastLaunchDay: string | null = null;\nlet chatMovedAnnounced = false;\n\nexport interface LoadOptions {\n now?: number;\n /** Has this module landed? (Injected by tests; the build's glob otherwise.) */\n landed?: (module: string) => boolean;\n}\n\n/**\n * Read v3, else migrate v2, else defaults — then apply the launch rule.\n * Total: a hand-edited or future blob falls back field by field.\n */\nexport function loadVerseUi(options: LoadOptions = {}): VerseUiState {\n const now = options.now ?? Date.now();\n const landed = options.landed ?? ((m: string) => sectionImporter(m) !== undefined);\n const v3 = readJson(V3_KEY);\n const v2 = v3 ? null : readJson(V2_KEY);\n const src = v3 ?? v2 ?? {};\n\n let section: VerseSectionId = DEFAULTS.section;\n let overlay: VerseOverlay = null;\n const migration = migrateSectionId(src['section']);\n if (migration && SECTION_IDS.has(migration.section)) {\n section = migration.section;\n if (migration.openNeedsYou && !v3) overlay = 'needs-you';\n }\n\n lastLaunchDay = v3 && typeof v3['lastLaunchDay'] === 'string' ? v3['lastLaunchDay'] : null;\n chatMovedAnnounced = v3 ? v3['chatMovedAnnounced'] === true : false;\n\n const today = localDay(now);\n // First launch of the day → Command, when Command exists in this build.\n if (lastLaunchDay !== today && landedModule('command', landed) !== null) section = 'command';\n lastLaunchDay = today;\n\n const state: VerseUiState = {\n ...DEFAULTS,\n section,\n railExpanded: src['railExpanded'] === true,\n sidebarWidth: clampWidth(src['sidebarWidth'], VERSE_SIDEBAR),\n sidebarCollapsed: src['sidebarCollapsed'] === true,\n resourcesOpen: src['resourcesOpen'] !== false,\n resourcesWidth: clampWidth(src['resourcesWidth'], VERSE_RESOURCES),\n recentActions: stringList(src['recentActions'], RECENT_ACTIONS_LIMIT, /^[a-z][a-z0-9.-]{0,63}$/),\n recentChats: stringList(src['recentChats'], RECENT_CHATS_LIMIT, /^[\\w.-]{1,200}$/),\n mounted: [section],\n overlay,\n history: { entries: [{ section, sessionId: null }], index: 0 },\n // A v2 blob means this operator had ⌘1 = Chat in their hands yesterday.\n announceChatMoved: v2 !== null && !chatMovedAnnounced,\n };\n return state;\n}\n\nlet state: VerseUiState = DEFAULTS;\nlet nonce = 0;\nlet recentCycle: RecentCycle | null = null;\nconst listeners = new Set<() => void>();\n\nfunction persist(next: VerseUiState): void {\n const payload: PersistedV3 = {\n version: 3,\n section: next.section,\n railExpanded: next.railExpanded,\n sidebarWidth: next.sidebarWidth,\n sidebarCollapsed: next.sidebarCollapsed,\n resourcesOpen: next.resourcesOpen,\n resourcesWidth: next.resourcesWidth,\n recentActions: [...next.recentActions],\n recentChats: [...next.recentChats],\n lastLaunchDay,\n chatMovedAnnounced,\n dock: getDockState(),\n };\n try {\n localStorage.setItem(V3_KEY, JSON.stringify(payload));\n } catch {\n /* best-effort: a private window without storage still runs the app */\n }\n}\n\nconst PERSISTED_FIELDS = [\n 'section',\n 'railExpanded',\n 'sidebarWidth',\n 'sidebarCollapsed',\n 'resourcesOpen',\n 'resourcesWidth',\n 'recentActions',\n 'recentChats',\n] as const satisfies readonly (keyof VerseUiState)[];\n\n/** Snapshot identity only changes when something actually changed. */\nfunction patch(delta: Partial<VerseUiState>): void {\n const next = { ...state, ...delta };\n if ((Object.keys(delta) as (keyof VerseUiState)[]).every((k) => Object.is(state[k], next[k]))) return;\n const shouldPersist = PERSISTED_FIELDS.some((k) => !Object.is(state[k], next[k]));\n state = next;\n if (shouldPersist) persist(next);\n for (const l of [...listeners]) l();\n}\n\nfunction init(): void {\n state = loadVerseUi();\n // Write the migrated / launch-ruled state straight away, so the day's first\n // launch is recorded even if the operator closes the app without a click.\n persist(state);\n}\n\ninit();\n\n// C2's dock changes are folded into the blob as they happen.\nlet lastDockJson = '';\nsubscribeDockState(() => {\n let json = '';\n try { json = JSON.stringify(getDockState()); } catch { json = ''; }\n if (json === lastDockJson) return;\n lastDockJson = json;\n persist(state);\n});\n\nexport function subscribeVerseUi(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getVerseUiState(): VerseUiState {\n return state;\n}\n\n// ===========================================================================\n// Navigation\n// ===========================================================================\n\nfunction withMounted(mounted: readonly VerseSectionId[], section: VerseSectionId): VerseSectionId[] {\n // The last KEEP_ALIVE_SURFACES surfaces other than Chat, most recent first…\n const chatVisited = section === 'chat' || mounted.includes('chat');\n const others = mounted.filter((id) => id !== 'chat' && id !== section);\n const nonChat = (section === 'chat' ? others : [section, ...others]).slice(0, KEEP_ALIVE_SURFACES);\n const alive: VerseSectionId[] = section === 'chat' ? ['chat', ...nonChat] : nonChat;\n // …and Chat, once visited, always: its streams, scroll and draft are the\n // most expensive state in the app to rebuild.\n if (chatVisited && !alive.includes('chat')) alive.push('chat');\n return alive;\n}\n\nfunction go(section: VerseSectionId, options: { history: boolean }): void {\n if (!SECTION_IDS.has(section)) return;\n const history = options.history\n ? pushNav(state.history, { section, sessionId: section === 'chat' ? state.activeSessionId : null })\n : state.history;\n patch({ section, mounted: withMounted(state.mounted, section), history });\n}\n\n/** The window event that asks the shell to reveal an anchor on a surface (shell/reveal-anchor.ts). */\nexport const VERSE_ANCHOR_EVENT = 'ashlr:verse-anchor';\n\nexport interface VerseAnchorRequest {\n section: VerseSectionId;\n /** A surface's own anchor key (\"memo:m-7\", \"seat:claude-a\"); the element carries `c7-anchor-<key>` or `data-verse-anchor`. */\n anchor: string;\n}\n\n/**\n * Switch surface; with `anchor`, also ask the shell to scroll that card or\n * row into view once the surface has painted. The anchor travels as a window\n * event rather than store state because it is a one-shot gesture about DOM\n * that may not exist yet (a lazy chunk), and every unit that owns a surface\n * can dispatch the same event without importing the shell.\n */\nexport function setVerseSection(section: VerseSectionId, anchor: string | null = null): void {\n go(section, { history: true });\n if (!anchor || typeof window === 'undefined') return;\n const detail: VerseAnchorRequest = { section, anchor };\n window.dispatchEvent(new CustomEvent(VERSE_ANCHOR_EVENT, { detail }));\n}\n\n/** ⌘[ / ⌘]. Returns false when there is nowhere to go. */\nexport function stepVerseHistory(delta: -1 | 1): boolean {\n const step = stepNav(state.history, delta);\n if (!step) return false;\n patch({ history: step.history });\n go(step.entry.section, { history: false });\n const target = step.entry.sessionId;\n if (step.entry.section === 'chat' && target && target !== state.activeSessionId) {\n raise('open-session', { sessionId: target }, false);\n // Set directly, NOT through setVerseActiveSession: that would push a new\n // history entry and drop the forward stack this step just walked.\n patch({ activeSessionId: target, recentChats: touchRecent(state.recentChats, target) });\n }\n return true;\n}\n\n/**\n * The chat reports which chat is open (C2: call on every selection). Feeds\n * back/forward, ⌃Tab's recent list, and the drawer's \"you are looking at it\".\n */\nexport function setVerseActiveSession(sessionId: string | null): void {\n if (sessionId === state.activeSessionId) return;\n const recentChats = sessionId ? touchRecent(state.recentChats, sessionId) : state.recentChats;\n const history = sessionId && state.section === 'chat' ? pushNav(state.history, { section: 'chat', sessionId }) : state.history;\n patch({ activeSessionId: sessionId, recentChats, history });\n}\n\n/** ⌃Tab / ⌃⇧Tab. Returns the chat opened, or null when there is no other recent chat. */\nexport function cycleVerseRecentChat(direction: 1 | -1, now: number = Date.now()): string | null {\n const recent = state.activeSessionId ? touchRecent(state.recentChats, state.activeSessionId) : [...state.recentChats];\n const step = cycleRecent(recent, recentCycle, direction, now);\n if (!step) return null;\n recentCycle = step.cycle;\n openVerseSession(step.sessionId);\n return step.sessionId;\n}\n\n// ===========================================================================\n// Chat hand-off\n// ===========================================================================\n\n/** The chat's own selection key (ChatSection, C2) — see openVerseSession. */\nconst CHAT_SELECTED_KEY = 'ashlr.verse.selected.v1';\n\nfunction raise(name: VerseCommandName, args: VerseCommandArgs, switchTo: boolean): void {\n nonce += 1;\n const command: VerseCommand = { name, nonce, ...args };\n if (switchTo && state.section !== 'chat') {\n go('chat', { history: true });\n }\n patch({ command });\n}\n\n/** ⌘N, \"New chat on…\", the palette's chat actions: switch to Chat and hand it a one-shot request. */\nexport function requestVerseCommand(name: VerseCommandName, args: VerseCommandArgs = {}): void {\n raise(name, args, true);\n}\n\n/**\n * Open a chat by id from anywhere (palette, drawer, a notification, ⌃Tab).\n * Raises `open-session` for a mounted chat surface; when Chat is not mounted\n * yet it also writes the chat's own selection key, so the surface opens on\n * that chat when it mounts (the command then confirms it).\n */\nexport function openVerseSession(sessionId: string): void {\n if (!/^[\\w.-]{1,200}$/.test(sessionId)) return;\n if (!state.mounted.includes('chat')) {\n try { localStorage.setItem(CHAT_SELECTED_KEY, sessionId); } catch { /* the command still carries it */ }\n }\n raise('open-session', { sessionId }, true);\n setVerseActiveSession(sessionId);\n}\n\nexport function clearVerseCommand(): void {\n patch({ command: null });\n}\n\n// ===========================================================================\n// Overlays\n// ===========================================================================\n\nexport function openVerseOverlay(overlay: Exclude<VerseOverlay, null>): void {\n patch({ overlay });\n}\n\nexport function closeVerseOverlay(): void {\n patch({ overlay: null, needsYouFocus: null });\n}\n\nexport function toggleVerseOverlay(overlay: Exclude<VerseOverlay, null>): void {\n patch({ overlay: state.overlay === overlay ? null : overlay, needsYouFocus: null });\n}\n\n/** ⌘J — optionally on a split, optionally selecting one item. */\nexport function openVerseNeedsYou(options: { split?: NeedsYouSplit; focusId?: string | null } = {}): void {\n patch({\n overlay: 'needs-you',\n needsYouSplit: options.split ?? state.needsYouSplit,\n needsYouFocus: options.focusId ?? null,\n });\n}\n\nexport function setVerseNeedsYouSplit(split: NeedsYouSplit): void {\n if (NEEDS_YOU_SPLITS.includes(split)) patch({ needsYouSplit: split });\n}\n\n// ===========================================================================\n// Preferences\n// ===========================================================================\n\nexport function setVerseRailExpanded(expanded: boolean): void {\n patch({ railExpanded: expanded });\n}\n\nexport function toggleVerseRail(): void {\n patch({ railExpanded: !state.railExpanded });\n}\n\nexport function setVerseSidebarWidth(width: number): void {\n patch({ sidebarWidth: clampWidth(width, VERSE_SIDEBAR) });\n}\n\nexport function setVerseSidebarCollapsed(collapsed: boolean): void {\n patch({ sidebarCollapsed: collapsed });\n}\n\nexport function toggleVerseSidebar(): void {\n patch({ sidebarCollapsed: !state.sidebarCollapsed });\n}\n\nexport function setVerseResourcesOpen(open: boolean): void {\n patch({ resourcesOpen: open });\n}\n\nexport function setVerseResourcesWidth(width: number): void {\n patch({ resourcesWidth: clampWidth(width, VERSE_RESOURCES) });\n}\n\n/** The palette ran a catalog action: remember it for the empty-query list. */\nexport function recordVerseAction(commandId: string): void {\n if (!/^[a-z][a-z0-9.-]{0,63}$/.test(commandId)) return;\n patch({ recentActions: [commandId, ...state.recentActions.filter((id) => id !== commandId)].slice(0, RECENT_ACTIONS_LIMIT) });\n}\n\n/** The \"Chat moved to ⌘5\" toast was shown: never again. */\nexport function acknowledgeChatMoved(): void {\n chatMovedAnnounced = true;\n if (state.announceChatMoved) patch({ announceChatMoved: false });\n persist(state);\n}\n\n// ===========================================================================\n// Test hygiene\n// ===========================================================================\n\n/** Back to defaults, storage cleared. Does NOT re-run the launch rule. */\nexport function resetVerseUi(): void {\n state = DEFAULTS;\n nonce = 0;\n recentCycle = null;\n lastLaunchDay = null;\n chatMovedAnnounced = false;\n seatMemory = null;\n adviceDismissals.clear();\n try {\n localStorage.removeItem(V3_KEY);\n localStorage.removeItem(V2_KEY);\n localStorage.removeItem(SEAT_STORAGE_KEY);\n } catch {\n /* ignore */\n }\n for (const l of [...listeners]) l();\n}\n\n/** Re-read storage as a fresh page load would (migration + launch rule). */\nexport function reloadVerseUiForTest(options: LoadOptions = {}): void {\n state = loadVerseUi(options);\n persist(state);\n for (const l of [...listeners]) l();\n}\n\n// ---------------------------------------------------------------------------\n// Last-used seat, per project\n// ---------------------------------------------------------------------------\n\n/**\n * Which seat + model a project's last chat was started on.\n *\n * WHY THIS IS PERSISTED. A new chat is seat-bound at creation, and the two\n * ways to start one — the sidebar \"+\" and ⌘N — both carry the CURRENT\n * session's seat forward. That works right up until there is no current\n * session (first load, or after deleting the last chat), where both fall\n * through to `defaultSeatChoice()`, which walks ENGINE_ORDER and lands on\n * Claude: the scarcest and most expensive account, chosen by alphabet rather\n * than by intent. Remembering the last seat per project makes \"New chat\" mean\n * the same thing across a reload as it does within one.\n *\n * NOT part of `VerseUiState`: a per-project memory that grows with the\n * project list has no business in a snapshot compared by identity on every\n * render.\n */\nexport interface VerseSeatMemory {\n seatId: string;\n model: string;\n}\n\nconst SEAT_STORAGE_KEY = 'ashlr.verse.seats.v1';\n/** Bounded so a long-lived browser cannot accumulate an unbounded map. */\nconst SEAT_MEMORY_LIMIT = 50;\n\n/** Lazily read, then held in memory; `null` means \"not read yet\". */\nlet seatMemory: Record<string, VerseSeatMemory> | null = null;\n\nfunction readSeatMemory(): Record<string, VerseSeatMemory> {\n if (seatMemory) return seatMemory;\n const out: Record<string, VerseSeatMemory> = {};\n try {\n const raw = localStorage.getItem(SEAT_STORAGE_KEY);\n const parsed = raw ? (JSON.parse(raw) as unknown) : null;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n for (const [path, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue;\n const { seatId, model } = value as Record<string, unknown>;\n if (typeof seatId !== 'string' || seatId.length === 0) continue;\n if (typeof model !== 'string' || model.length === 0) continue;\n out[path] = { seatId, model };\n }\n }\n } catch {\n /* a private window without storage still runs the app */\n }\n seatMemory = out;\n return out;\n}\n\n/**\n * The seat a chat on `projectPath` was last started with, or the most recent\n * seat on any project when that project has no record yet — a brand-new\n * project inherits the operator's current working habit rather than resetting\n * to the default engine. Null only when nothing has ever been started.\n */\nexport function lastVerseSeat(projectPath?: string | null): VerseSeatMemory | null {\n const memory = readSeatMemory();\n if (projectPath) {\n const exact = memory[projectPath];\n if (exact) return exact;\n }\n // Insertion order is recency order: rememberVerseSeat re-inserts on write.\n const keys = Object.keys(memory);\n const newest = keys[keys.length - 1];\n return newest ? memory[newest] ?? null : null;\n}\n\n/** Record the seat a chat was just created with. Never throws. */\nexport function rememberVerseSeat(projectPath: string, seat: VerseSeatMemory): void {\n if (!projectPath || !seat.seatId || !seat.model) return;\n const memory = readSeatMemory();\n // Delete before re-inserting so key order stays newest-last.\n delete memory[projectPath];\n memory[projectPath] = { seatId: seat.seatId, model: seat.model };\n const keys = Object.keys(memory);\n for (const stale of keys.slice(0, Math.max(0, keys.length - SEAT_MEMORY_LIMIT))) {\n delete memory[stale];\n }\n try {\n localStorage.setItem(SEAT_STORAGE_KEY, JSON.stringify(memory));\n } catch {\n /* best-effort */\n }\n}\n\nexport const VERSE_SEAT_STORAGE_KEY = SEAT_STORAGE_KEY;\n\n// ---------------------------------------------------------------------------\n// Dismissed context advice, per session (NOT persisted)\n// ---------------------------------------------------------------------------\n\n/**\n * Which context-advice notes (\"continue in a fresh chat\", \"expansive may\n * help\") the operator waved off, per session.\n *\n * WHY IN MEMORY ONLY. Advice is evidence about the session as it is NOW: a\n * dismissal restored from storage after a reload would silence a note whose\n * evidence has since grown. Held here rather than in component state so that\n * switching surfaces and back does not bring back a note just dismissed.\n *\n * The key names the EVIDENCE (ContextAdvice composes it from the advice level\n * and the compaction count), so a note returns on its own when it escalates.\n */\nconst adviceDismissals = new Map<string, Set<string>>();\n/** Bounded like the other per-session memories. */\nconst ADVICE_SESSION_LIMIT = 50;\n\nexport function dismissVerseAdvice(sessionId: string, key: string): void {\n if (!sessionId || !key) return;\n const set = adviceDismissals.get(sessionId) ?? new Set<string>();\n set.add(key);\n // Re-insert so Map order stays recency order, then trim the oldest.\n adviceDismissals.delete(sessionId);\n adviceDismissals.set(sessionId, set);\n for (const stale of [...adviceDismissals.keys()].slice(0, Math.max(0, adviceDismissals.size - ADVICE_SESSION_LIMIT))) {\n adviceDismissals.delete(stale);\n }\n}\n\nexport function isVerseAdviceDismissed(sessionId: string, key: string): boolean {\n return adviceDismissals.get(sessionId)?.has(key) ?? false;\n}\n","/**\n * routes/verse/composer/composer-keys.ts — the composer's keys, read from\n * C0's key table (shell/command-keys.ts, the keys half of the command\n * catalog the palette, the shortcuts overlay and the native menu also\n * read), never re-declared here (unit C3).\n */\nimport {\n chordMatches,\n commandChord,\n formatChord,\n keyBinding,\n type KeyEventLike,\n} from '../shell/command-keys.js';\n\n/** The composer commands this unit implements (command-catalog ids). */\nexport const COMPOSER_COMMAND_IDS = [\n 'composer.permission',\n 'composer.model',\n 'composer.effort',\n 'composer.attach',\n 'composer.cloud',\n 'composer.send',\n 'composer.stop-and-send',\n 'composer.stop',\n] as const;\nexport type ComposerCommandId = (typeof COMPOSER_COMMAND_IDS)[number];\n\n/**\n * The window event the palette (C1) and the native menu bridge dispatch to\n * run a catalog command on the surface that owns it. Defined in C0's\n * command-keys.ts (beside the ids it carries); re-exported here so the\n * composer's importers keep one import site.\n */\nexport { WORKBENCH_COMMAND_EVENT } from '../shell/command-keys.js';\n\n/** Does this key event press the catalog chord for `id`? */\nexport function pressesCommand(event: KeyEventLike, id: ComposerCommandId): boolean {\n const binding = keyBinding(id);\n return binding !== null && binding.keys.some((chord) => chordMatches(event, chord));\n}\n\n/** The chord for `id` as the platform prints it (\"⇧⌘M\" / \"Ctrl+Shift+M\"); '' when unbound. */\nexport function shortcutLabel(id: ComposerCommandId): string {\n const chord = commandChord(id);\n return chord ? formatChord(chord) : '';\n}\n","/**\n * routes/verse/shell/command-bus.ts — how a catalog command reaches the unit\n * that owns its behaviour (unit C1; the keys and titles are C0's\n * command-catalog.ts).\n *\n * The catalog is one table; the behaviour behind it is spread over units:\n * the shell runs the global commands, the chat (C2) runs `dock.*` and\n * `chat.*`, the composer (C3) runs `composer.*`. The palette, the desktop\n * menu bridge and the shell's key handler all call `runCommand(id)`; the\n * owning unit registers a handler for the ids it serves:\n *\n * useCommandHandler('dock.terminal', () => openDockPane('terminal'));\n *\n * A handler may return `false` to decline (e.g. a composer command with no\n * chat open), and the most recently registered handler for an id is asked\n * first — a mounted chat outranks nothing, a focused one outranks a hidden\n * one because it registers last.\n *\n * DELIVERY LATER. A chat command run from another surface (⌘K \"Terminal\"\n * while on Fleet) has no handler until Chat mounts. The shell switches to\n * Chat and parks the command with `runCommandWhenReady`; the first handler to\n * register for that id within the window receives it. Parked commands expire\n * — a command that runs 20 s after the operator asked for it is a surprise,\n * not a feature.\n *\n * Framework-free except the one hook at the bottom.\n */\nimport { useEffect, useRef } from 'react';\n\n/** What a palette argument (Tab) filled in, or what a caller passes along. */\nexport interface CommandArgument {\n kind: 'seat' | 'project' | 'section' | 'session';\n id: string;\n label: string;\n}\n\nexport interface CommandInvocation {\n argument?: CommandArgument;\n /** Where it came from — handlers may word a toast differently for a key press. */\n via?: 'palette' | 'key' | 'menu' | 'button';\n}\n\n/** Return false to decline (the next handler is asked); anything else = handled. */\nexport type CommandHandler = (invocation: CommandInvocation) => void | boolean;\n\nconst handlers = new Map<string, CommandHandler[]>();\n\ninterface Parked {\n id: string;\n invocation: CommandInvocation;\n until: number;\n}\n\nlet parked: Parked | null = null;\n\nexport const PARKED_COMMAND_TTL_MS = 4_000;\n\nfunction deliverParked(id: string): void {\n if (!parked || parked.id !== id) return;\n if (Date.now() > parked.until) {\n parked = null;\n return;\n }\n const { invocation } = parked;\n parked = null;\n // After the registering effect has finished: the handler's own component\n // is mounted, but its sibling effects may not have run yet.\n queueMicrotask(() => {\n runCommand(id, invocation);\n });\n}\n\n/** Register `handler` for command `id`. Returns the unregister function. */\nexport function registerCommandHandler(id: string, handler: CommandHandler): () => void {\n const list = handlers.get(id) ?? [];\n list.push(handler);\n handlers.set(id, list);\n deliverParked(id);\n return () => {\n const current = handlers.get(id);\n if (!current) return;\n const index = current.lastIndexOf(handler);\n if (index >= 0) current.splice(index, 1);\n if (current.length === 0) handlers.delete(id);\n };\n}\n\nexport function hasCommandHandler(id: string): boolean {\n return (handlers.get(id)?.length ?? 0) > 0;\n}\n\n/** Run `id` now. True when a handler took it. */\nexport function runCommand(id: string, invocation: CommandInvocation = {}): boolean {\n const list = handlers.get(id);\n if (!list) return false;\n for (const handler of [...list].reverse()) {\n try {\n if (handler(invocation) !== false) return true;\n } catch (err) {\n // A broken handler must not take the palette or the key handler down.\n console.error(`[verse] command ${id} failed`, err);\n return true;\n }\n }\n return false;\n}\n\n/** Run `id` now if someone serves it, else hold it for the next handler to register (within the TTL). */\nexport function runCommandWhenReady(id: string, invocation: CommandInvocation = {}, ttlMs: number = PARKED_COMMAND_TTL_MS): boolean {\n if (runCommand(id, invocation)) return true;\n parked = { id, invocation, until: Date.now() + ttlMs };\n return false;\n}\n\n/** Test hygiene. */\nexport function resetCommandBus(): void {\n handlers.clear();\n parked = null;\n}\n\n/**\n * Serve command `id` while mounted (and `enabled`). The latest `handler` is\n * always the one called, so an inline closure does not re-register each render.\n */\nexport function useCommandHandler(id: string, handler: CommandHandler, enabled = true): void {\n const ref = useRef(handler);\n ref.current = handler;\n useEffect(() => {\n if (!enabled) return;\n return registerCommandHandler(id, (invocation) => ref.current(invocation));\n }, [id, enabled]);\n}\n","/**\n * routes/verse/shell/run-command.ts — \"run catalog command X\" from anywhere:\n * a key, the palette, the native menu, a button (unit C1).\n *\n * - guarded commands (Stop running chats…, Stop the fleet…) go through\n * confirm → token → run (guarded-action.tsx), with the runner defined\n * by the SHELL (guarded-runners.ts, loaded when a run is confirmed):\n * they are shell actions, and a guard must never depend on some other\n * unit remembering to confirm;\n * - chat commands run on the chat's handler, and when Chat is not on\n * screen the shell switches to it and parks the command until the chat\n * registers (command-bus runCommandWhenReady);\n * - the composer's commands (`composer.*`, C3) are answered by the Composer\n * through the `ashlr:command` window event, not the bus — so they are\n * DISPATCHED to it, after switching to Chat and waiting for a composer to\n * exist (deliverComposerCommand);\n * - surface commands (`surface: 'command'` in the catalog — the autonomy\n * switch, the grant sheet, budget modes) run on the surface that owns\n * their dialogs: the shell brings it forward and parks the command until\n * it registers, exactly like a chat command;\n * - everything else runs on its registered handler.\n *\n * Palette-listed commands are remembered for the empty-query \"Recent\" list.\n */\nimport { useEffect } from 'react';\nimport { useToast } from '../../../components/primitives/Toast.js';\nimport { cycleTheme } from '../../../data/theme-store.js';\nimport {\n closeVerseOverlay,\n cycleVerseRecentChat,\n getVerseUiState,\n openVerseNeedsYou,\n recordVerseAction,\n requestVerseCommand,\n setVerseSection,\n stepVerseHistory,\n toggleVerseOverlay,\n toggleVerseRail,\n type VerseSectionId,\n} from '../verse-ui-store.js';\nimport { COMPOSER_COMMAND_IDS, WORKBENCH_COMMAND_EVENT } from '../composer/composer-keys.js';\nimport { PARKED_COMMAND_TTL_MS, registerCommandHandler, runCommand, runCommandWhenReady, type CommandInvocation } from './command-bus.js';\nimport { commandCatalogLoaded, keyBinding, loadedCommand } from './command-keys.js';\nimport { requestGuarded } from './guarded-action.js';\n\nexport type ShellNotify = (message: string, tone?: 'neutral' | 'success' | 'danger') => void;\n\nlet notify: ShellNotify = () => {};\n\n/** The shell installs its toast here (run-command has no React context). */\nexport function setShellNotifier(fn: ShellNotify | null): void {\n notify = fn ?? (() => {});\n}\n\n/** Toast through whatever the shell installed (guarded-runners.ts reports with it). */\nexport function shellNotify(message: string, tone?: 'neutral' | 'success' | 'danger'): void {\n notify(message, tone);\n}\n\n/**\n * The guarded catalog commands the shell runs itself. Their runners\n * (guarded-runners.ts: a cancel per running chat, the fleet stop and its\n * toast) are a dynamic import — they run only after the operator confirmed\n * (and unlocked), never at first paint, so they stay out of the chat\n * first-paint critical JS. guarded-runners.test.ts pins that this list and\n * the runner table name the same ids.\n */\nexport const SHELL_GUARDED_COMMAND_IDS: ReadonlySet<string> = new Set(['chats.stop-all', 'fleet.stop']);\n\n/** The composer's message box inside the Chat surface (C3's Composer names it \"Message\"). */\nfunction composerPresent(): boolean {\n return document.querySelector('[data-surface=\"chat\"] textarea[aria-label=\"Message\"]') !== null;\n}\n\nfunction dispatchComposerCommand(id: string): void {\n window.dispatchEvent(new CustomEvent(WORKBENCH_COMMAND_EVENT, { detail: { id } }));\n}\n\n/**\n * How long after the composer's box appears before the command is sent. The\n * box is in the DOM at React's commit; the Composer's window listener is a\n * passive effect that runs a moment LATER. Firing on the mutation itself can\n * beat the listener and the command vanishes, so the dispatch waits a beat.\n */\nexport const COMPOSER_LISTEN_GRACE_MS = 50;\n\n/**\n * Run a `composer.*` command on the Composer. With a composer already mounted\n * (even behind another surface: keep-alive leaves its listener live) it goes\n * now. Otherwise Chat is opened and the command waits — within the same TTL\n * as a parked bus command — for a composer to mount. A command that finds no\n * composer in time is dropped: running it 20 s later would be a surprise.\n */\nexport function deliverComposerCommand(id: string, ttlMs: number = PARKED_COMMAND_TTL_MS): void {\n if (typeof document === 'undefined') return;\n if (getVerseUiState().section !== 'chat') setVerseSection('chat');\n if (composerPresent()) {\n dispatchComposerCommand(id);\n return;\n }\n let done = false;\n const finish = (found: boolean) => {\n if (done) return;\n done = true;\n observer.disconnect();\n window.clearTimeout(expiry);\n if (found) window.setTimeout(() => dispatchComposerCommand(id), COMPOSER_LISTEN_GRACE_MS);\n };\n const observer = new MutationObserver(() => {\n if (composerPresent()) finish(true);\n });\n observer.observe(document.body, { childList: true, subtree: true });\n const expiry = window.setTimeout(() => finish(false), ttlMs);\n}\n\nconst COMPOSER_IDS: ReadonlySet<string> = new Set(COMPOSER_COMMAND_IDS);\n\n/**\n * Run catalog command `id`. Returns false when nothing could run it.\n *\n * A key, the menu or a rail button names a KEYED command, and its keys and\n * scope are here from first paint (command-keys.ts). The rest of the entry —\n * title, palette group, guard, serving surface — is the full catalog's,\n * which every caller naming a keyless command (the palette, onboarding) has\n * loaded, and the warm-up fetches after first paint anyway. Should one\n * arrive before it, the catalog is loaded and the command run then.\n */\nexport function executeCatalogCommand(id: string, invocation: CommandInvocation = {}): boolean {\n const full = loadedCommand(id);\n const command = full ?? keyBinding(id);\n if (!command) {\n if (commandCatalogLoaded()) return false;\n void import('./command-catalog.js').then(\n () => { if (loadedCommand(id)) executeCatalogCommand(id, invocation); },\n (err) => console.error('[verse] command catalog failed to load', err),\n );\n return true;\n }\n if (full && full.group !== null && invocation.via === 'palette') recordVerseAction(full.id);\n\n if (full?.guard) {\n if (!SHELL_GUARDED_COMMAND_IDS.has(full.id)) return runCommand(id, invocation);\n const commandId = full.id;\n const { confirm, token } = full.guard;\n requestGuarded({\n title: confirm.title,\n body: confirm.body,\n confirmLabel: confirm.confirmLabel,\n destructive: confirm.destructive,\n token,\n tokenReason: `${full.title.replace(/…$/, '')} requires the dispatch token.`,\n run: () => import('./guarded-runners.js').then((m) => m.runGuardedShellCommand(commandId)),\n });\n return true;\n }\n\n if (full?.surface) {\n // Its handler (and the Touch ID sheet / token dialog it may open) lives on\n // that surface. Going there first means the operator sees the switch move\n // — or the sheet it opened — rather than a change behind another surface.\n if (getVerseUiState().section !== full.surface) setVerseSection(full.surface);\n runCommandWhenReady(id, invocation);\n return true;\n }\n\n if (COMPOSER_IDS.has(command.id)) {\n // A bus handler (should C3 ever register one) outranks the event.\n if (runCommand(id, invocation)) return true;\n deliverComposerCommand(id);\n return true;\n }\n\n if (command.scope === 'chat' || command.scope === 'composer') {\n if (runCommand(id, invocation)) return true;\n // Explicitly asked for a chat action: going to Chat is the point.\n if (getVerseUiState().section !== 'chat') setVerseSection('chat');\n runCommandWhenReady(id, invocation);\n return true;\n }\n\n if (runCommand(id, invocation)) return true;\n const unavailable = (title: string) => notify(`${title} isn't available here yet.`, 'neutral');\n if (full) unavailable(full.title);\n else void import('./command-catalog.js').then((m) => unavailable(m.findCommand(id)?.title ?? id), () => unavailable(id));\n return false;\n}\n\n\n/**\n * The GLOBAL commands the shell itself serves (every other id belongs to the\n * unit that owns the behaviour). Returns the unregister function.\n */\nexport function registerShellCommandHandlers(): () => void {\n const surfaces: Array<[string, VerseSectionId]> = [\n ['surface.command', 'command'],\n ['surface.fleet', 'fleet'],\n ['surface.growth', 'growth'],\n ['surface.mind', 'mind'],\n ['surface.chat', 'chat'],\n ['section.settings', 'settings'],\n ['section.apps', 'apps'],\n ['section.usage', 'usage'],\n ];\n const offs = [\n ...surfaces.map(([id, section]) => registerCommandHandler(id, () => setVerseSection(section))),\n registerCommandHandler('palette.open', () => toggleVerseOverlay('palette')),\n registerCommandHandler('needs-you.open', (inv) => {\n // The menu / a notification OPENS it; the key toggles.\n if (inv.via !== 'menu' && getVerseUiState().overlay === 'needs-you') closeVerseOverlay();\n else openVerseNeedsYou();\n }),\n registerCommandHandler('shortcuts.open', () => toggleVerseOverlay('shortcuts')),\n registerCommandHandler('history.back', () => { stepVerseHistory(-1); }),\n registerCommandHandler('history.forward', () => { stepVerseHistory(1); }),\n registerCommandHandler('chat.recent-next', () => { cycleVerseRecentChat(1); }),\n registerCommandHandler('chat.recent-prev', () => { cycleVerseRecentChat(-1); }),\n registerCommandHandler('rail.toggle-labels', () => toggleVerseRail()),\n registerCommandHandler('chat.new', () => requestVerseCommand('new-chat')),\n registerCommandHandler('chat.new-on', (inv) => {\n const seat = inv.argument?.kind === 'seat' ? inv.argument.id : undefined;\n requestVerseCommand('new-chat', seat ? { seatId: seat } : {});\n }),\n registerCommandHandler('appearance.toggle-theme', () => cycleTheme()),\n registerCommandHandler('app.summon', () => requestVerseCommand('focus-composer')),\n // The clipboard code loads when asked for (copy-setup.ts), not at first paint.\n registerCommandHandler('autonomy.copy-setup', () => { void import('./copy-setup.js').then((m) => m.copyAutonomySetupCommand()); }),\n ];\n return () => offs.forEach((off) => off());\n}\n\n/** Mounted once by the shell: the global handlers, and toasts for run-command. */\nexport function useShellCommands(): void {\n const toast = useToast();\n useEffect(() => {\n setShellNotifier(toast.show);\n return () => setShellNotifier(null);\n }, [toast.show]);\n useEffect(() => registerShellCommandHandlers(), []);\n}\n","/**\n * routes/verse/shell/section-visibility.tsx — \"is this surface on screen?\"\n * for the keep-alive shell (unit C0; SPEC-310C §1).\n *\n * 3.10 keeps the last three surfaces MOUNTED (hidden with `hidden` + `inert`)\n * so switching back is instant and keeps scroll, selection and filters. The\n * price is that a hidden surface's polls keep running: Fleet's live swimlane\n * polling every few seconds behind a chat the operator is reading. The shell\n * (C1) wraps each mounted surface in <SectionVisibilityProvider visible>, and\n * every surface polls through usePollWhileVisible — so a hidden surface, or a\n * hidden window, costs nothing.\n *\n * Outside any provider a section counts as visible: components rendered on\n * their own (tests, the legacy sections before C1 lands) behave as today.\n */\nimport { createContext, useContext, useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react';\n\nconst SectionVisibleContext = createContext<boolean>(true);\n\nexport function SectionVisibilityProvider({ visible, children }: { visible: boolean; children: ReactNode }) {\n // A hidden surface nested in a visible one is hidden; a visible one nested\n // in a hidden one is hidden too.\n const parent = useContext(SectionVisibleContext);\n return <SectionVisibleContext.Provider value={parent && visible}>{children}</SectionVisibleContext.Provider>;\n}\n\n/** True while the surface this component lives in is the one on screen. */\nexport function useSectionVisible(): boolean {\n return useContext(SectionVisibleContext);\n}\n\nfunction documentVisible(): boolean {\n return typeof document === 'undefined' || document.visibilityState !== 'hidden';\n}\n\nfunction subscribeDocument(onChange: () => void): () => void {\n if (typeof document === 'undefined') return () => {};\n document.addEventListener('visibilitychange', onChange);\n return () => document.removeEventListener('visibilitychange', onChange);\n}\n\n/** True while the window itself is visible (not minimised, not a background tab). */\nexport function useDocumentVisible(): boolean {\n return useSyncExternalStore(subscribeDocument, documentVisible, () => true);\n}\n\n/**\n * The performance budget's floor (SPEC-310A §0: \"no new polling faster than\n * 2 s\"). Enforced here, in the one place surfaces poll from, rather than by\n * review.\n */\nexport const MIN_POLL_INTERVAL_MS = 2_000;\n\nexport interface PollOptions {\n /** Poll at all (e.g. only while a panel is expanded). Default true. */\n enabled?: boolean;\n /**\n * Tick once as soon as the surface becomes visible again, so data that went\n * stale while it was hidden is refreshed on sight instead of one interval\n * later. Default true. (The first mount is the caller's own fetch.)\n */\n refreshOnShow?: boolean;\n}\n\n/**\n * Run `tick` every `intervalMs` (at least MIN_POLL_INTERVAL_MS) while the\n * section AND the document are visible. The latest `tick` is always the one\n * called, so passing an inline closure does not restart the interval.\n */\nexport function usePollWhileVisible(tick: () => void, intervalMs: number, options: PollOptions = {}): void {\n const sectionVisible = useSectionVisible();\n const docVisible = useDocumentVisible();\n const visible = sectionVisible && docVisible;\n const active = (options.enabled ?? true) && visible;\n const refreshOnShow = options.refreshOnShow ?? true;\n const every = Math.max(MIN_POLL_INTERVAL_MS, Number.isFinite(intervalMs) ? intervalMs : MIN_POLL_INTERVAL_MS);\n\n const tickRef = useRef(tick);\n tickRef.current = tick;\n // VISIBILITY history, not activity: turning `enabled` on is the caller's\n // own decision (and it fetches for itself); coming back into view is not.\n const wasVisible = useRef<boolean | null>(null);\n\n useEffect(() => {\n const previouslyVisible = wasVisible.current;\n wasVisible.current = visible;\n if (!active) return;\n // Back in view after being hidden: catch up now rather than one interval late.\n if (previouslyVisible === false && refreshOnShow) tickRef.current();\n const id = setInterval(() => tickRef.current(), every);\n return () => clearInterval(id);\n }, [active, visible, every, refreshOnShow]);\n}\n","/**\n * routes/verse/shell/skeletons.tsx — what a surface looks like while its\n * chunk loads (unit C1; SPEC-310C §1 \"Surfaces show skeletons while\n * loading\"). The pre-3.10 fallback was a bare \"Loading…\" line; a skeleton in\n * the surface's own shape means the layout does not jump when it lands.\n *\n * Also the designed state for a surface whose module is not in this build\n * (`SurfaceNotInBuild`): it names the surface in operator language — never a\n * source path (the old MissingSection printed `routes/verse/sections/X.tsx`).\n */\nimport { SkeletonCard, SkeletonLine, SkeletonRow } from '../../../components/primitives/Skeleton.js';\nimport type { VerseSectionId } from '../verse-ui-store.js';\nimport styles from './skeletons.module.css';\n\nconst DASHBOARD: ReadonlySet<VerseSectionId> = new Set(['command', 'fleet', 'growth', 'mind']);\n\nexport function SurfaceSkeleton({ section, label }: { section: VerseSectionId; label: string }) {\n return (\n <div className={styles.frame} role=\"status\" aria-label={`Loading ${label}`} data-skeleton={section}>\n <div className={styles.strip}>\n <SkeletonLine width=\"140px\" />\n </div>\n {section === 'chat' ? (\n <div className={styles.chat}>\n <div className={styles.chatList}>\n {Array.from({ length: 8 }, (_, i) => (\n <SkeletonLine key={i} width={`${70 + ((i * 13) % 25)}%`} />\n ))}\n </div>\n <div className={styles.chatBody}>\n <SkeletonLine width=\"42%\" />\n <SkeletonLine width=\"88%\" />\n <SkeletonLine width=\"76%\" />\n <SkeletonLine width=\"64%\" />\n </div>\n </div>\n ) : DASHBOARD.has(section) ? (\n <div className={styles.grid}>\n <div className={styles.wide}><SkeletonLine width=\"60%\" /></div>\n {Array.from({ length: 5 }, (_, i) => (\n <div key={i} className={styles.kpi}><SkeletonCard /></div>\n ))}\n <div className={styles.half}><SkeletonCard /></div>\n <div className={styles.half}><SkeletonCard /></div>\n </div>\n ) : (\n <div className={styles.column}>\n {Array.from({ length: 6 }, (_, i) => (\n <SkeletonRow key={i} />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n/** A surface this build does not ship yet. */\nexport function SurfaceNotInBuild({ label, blurb }: { label: string; blurb: string }) {\n return (\n <div className={styles.missing} role=\"status\">\n <h1 className={styles.missingTitle}>{label} isn't in this build yet</h1>\n <p className={styles.missingBody}>{blurb}</p>\n <p className={styles.missingBody}>It appears here as soon as it ships. Everything else in Verse works normally.</p>\n </div>\n );\n}\n","/**\n * routes/verse/shell/useActivity.ts — the shell's one CURSOR loop over\n * GET /api/verse/activity (unit C1; route: core/verse/activity-api.ts).\n *\n * The rail badges, the palette's \"Needs you\" and running chats, the Needs-you\n * drawer and the in-app \"Finished / Failed\" toasts all read this ONE store,\n * so the whole shell costs one request per tick however many things show a\n * count. (The chat list polls the same route WITHOUT a cursor for its\n * present-tense view — chat/use-chat-activity.ts, C2.)\n *\n * CADENCE. Every 5 s while the window is visible, every 30 s while it is\n * hidden (the badges still need to be right when it comes back, and the tab\n * title count; C8's Rust owns native notifications). Never under the 2 s\n * floor (section-visibility MIN_POLL_INTERVAL_MS). Polls only while\n * something is subscribed.\n *\n * HONESTY. A 404 means the activity module is not in this build →\n * `unavailable`, and every badge renders \"unknown\" (no dot), never zero. A\n * failed poll keeps the last good data and says `stale` — a badge that\n * flashes to 0 on one dropped request would read as a false all-clear.\n * Any other failure carries the server's own reason in `error` (the route\n * answers `{ code, error }` when it could not read, e.g. a 503), so a\n * surface can say \"could not read the inbox: …\" instead of calling a broken\n * server \"not in this build\".\n *\n * COMPLETIONS arrive once per cursor; `onActivityCompletions` listeners get\n * each batch exactly once. The first poll of a page carries none (history is\n * not news).\n */\nimport { useSyncExternalStore } from 'react';\nimport type { VerseActivityCompletion, VerseActivityResponse } from '../../../../core/verse/workbench-types.js';\nimport { VERSE_ACTIVITY_PATH } from '../../../../core/verse/workbench-types.js';\nimport { ApiError, apiGet, readFailureReason } from '../../../data/client.js';\nimport { MIN_POLL_INTERVAL_MS } from './section-visibility.js';\n\nexport const ACTIVITY_POLL_VISIBLE_MS = 5_000;\nexport const ACTIVITY_POLL_HIDDEN_MS = 30_000;\n\nexport type ActivityStatus = 'idle' | 'loading' | 'ready' | 'stale' | 'unavailable';\n\nexport interface ActivityState {\n status: ActivityStatus;\n data: VerseActivityResponse | null;\n /** ms epoch of the last successful poll. */\n updatedAt: number | null;\n /** Why the last poll failed (null after a success, and for a 404 — that is `unavailable`, not a failure). */\n error?: string | null;\n}\n\ntype Fetcher = (path: string, signal?: AbortSignal) => Promise<VerseActivityResponse>;\n\nlet fetcher: Fetcher = (path, signal) => apiGet<VerseActivityResponse>(path, signal);\nlet snapshot: ActivityState = { status: 'idle', data: null, updatedAt: null, error: null };\nlet cursor: string | null = null;\nlet timer: ReturnType<typeof setTimeout> | null = null;\nlet inFlight: Promise<void> | null = null;\nlet subscribers = 0;\nconst listeners = new Set<() => void>();\nconst completionListeners = new Set<(batch: VerseActivityCompletion[]) => void>();\n\nfunction emit(next: ActivityState): void {\n snapshot = next;\n for (const l of [...listeners]) l();\n}\n\nfunction hidden(): boolean {\n return typeof document !== 'undefined' && document.visibilityState === 'hidden';\n}\n\nfunction schedule(): void {\n if (timer) clearTimeout(timer);\n timer = null;\n if (subscribers === 0) return;\n const every = Math.max(MIN_POLL_INTERVAL_MS, hidden() ? ACTIVITY_POLL_HIDDEN_MS : ACTIVITY_POLL_VISIBLE_MS);\n timer = setTimeout(() => {\n void refreshActivity();\n }, every);\n}\n\n/** Poll now (joins one already running). Resolves when it has settled; never rejects. */\nexport function refreshActivity(): Promise<void> {\n if (inFlight) return inFlight;\n if (snapshot.status === 'idle') emit({ ...snapshot, status: 'loading' });\n const path = cursor ? `${VERSE_ACTIVITY_PATH}?since=${encodeURIComponent(cursor)}` : VERSE_ACTIVITY_PATH;\n inFlight = (async () => {\n try {\n const data = await fetcher(path);\n const firstPoll = cursor === null;\n cursor = typeof data.cursor === 'string' ? data.cursor : null;\n emit({ status: 'ready', data, updatedAt: Date.now(), error: null });\n if (!firstPoll && Array.isArray(data.completions) && data.completions.length > 0) {\n for (const l of [...completionListeners]) {\n try { l(data.completions); } catch { /* a listener must not stop the loop */ }\n }\n }\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n emit({ status: 'unavailable', data: null, updatedAt: snapshot.updatedAt, error: null });\n } else if (err instanceof ApiError && err.status === 400 && cursor) {\n // A cursor the server no longer accepts: start over rather than stall.\n cursor = null;\n emit({ ...snapshot, status: snapshot.data ? 'stale' : 'loading' });\n } else {\n emit({ ...snapshot, status: snapshot.data ? 'stale' : 'unavailable', error: readFailureReason(err) });\n }\n } finally {\n inFlight = null;\n schedule();\n }\n })();\n return inFlight;\n}\n\nfunction onVisibility(): void {\n // Back in view: catch up now rather than up to 30 s late.\n if (!hidden() && subscribers > 0) void refreshActivity();\n else schedule();\n}\n\nfunction start(): void {\n subscribers += 1;\n if (subscribers === 1) {\n if (typeof document !== 'undefined') document.addEventListener('visibilitychange', onVisibility);\n void refreshActivity();\n }\n}\n\nfunction stop(): void {\n subscribers = Math.max(0, subscribers - 1);\n if (subscribers > 0) return;\n if (timer) clearTimeout(timer);\n timer = null;\n if (typeof document !== 'undefined') document.removeEventListener('visibilitychange', onVisibility);\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n start();\n return () => {\n listeners.delete(listener);\n stop();\n };\n}\n\nexport function getActivityState(): ActivityState {\n return snapshot;\n}\n\n/** The shell's activity. Subscribing starts the poll; the last unsubscribe stops it. */\nexport function useActivity(): ActivityState {\n return useSyncExternalStore(subscribe, getActivityState, getActivityState);\n}\n\n/** Each batch of newly finished turns, exactly once. Returns the unsubscribe. */\nexport function onActivityCompletions(listener: (batch: VerseActivityCompletion[]) => void): () => void {\n completionListeners.add(listener);\n return () => completionListeners.delete(listener);\n}\n\n/** Test seam: inject the fetch, clear state (and any timer). */\nexport function resetActivityForTest(next?: Fetcher): void {\n if (timer) clearTimeout(timer);\n timer = null;\n inFlight = null;\n cursor = null;\n subscribers = 0;\n listeners.clear();\n completionListeners.clear();\n fetcher = next ?? ((path, signal) => apiGet<VerseActivityResponse>(path, signal));\n snapshot = { status: 'idle', data: null, updatedAt: null, error: null };\n}\n","/**\n * routes/verse/useVerseUi.ts — the React glue over verse-ui-store.ts (same\n * split as useVerseSession.ts over verse-store.ts: the store stays\n * framework-free, this file is the only place that knows about React).\n */\nimport { useSyncExternalStore } from 'react';\nimport { getVerseUiState, subscribeVerseUi, type VerseUiState } from './verse-ui-store.js';\n\nexport function useVerseUi(): VerseUiState {\n return useSyncExternalStore(subscribeVerseUi, getVerseUiState, getVerseUiState);\n}\n","/**\n * components/primitives/icon-base.tsx — the icon geometry (16px box, 1.5px\n * stroke, round caps/joins, currentColor) and the few glyphs the Verse rail\n * draws at first paint. Everything here is re-exported by icons.tsx, which\n * remains the console's icon set and the import site for everyone else.\n *\n * WHY SPLIT: icons.tsx holds every glyph any surface uses, and the rail is on\n * the chat first-paint path; importing it there put the whole set (~4 KB) in\n * the chat first-paint critical JS (SPEC-310A §1). Add a glyph here only when\n * a first-paint module draws it.\n *\n * Every icon is decorative by default (`aria-hidden`); pass `title` only when\n * the icon is the sole carrier of meaning (it then becomes `role=\"img\"`).\n */\nimport type { ReactNode, SVGProps } from 'react';\n\nexport interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'children' | 'viewBox'> {\n /** Edge length in px. Defaults to 16 — the design language's icon size. */\n size?: number;\n /** Accessible name. Omit for decorative icons (the default). */\n title?: string;\n}\n\nexport function Icon({ size = 16, title, children, ...rest }: IconProps & { children: ReactNode }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n focusable=\"false\"\n {...(title ? { role: 'img' } : { 'aria-hidden': true })}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {children}\n </svg>\n );\n}\n\n/** Chat — the transcript section. */\nexport const IconChat = (p: IconProps) => (\n <Icon {...p}>\n <path d=\"M2.5 11.5V4.25A1.75 1.75 0 0 1 4.25 2.5h7.5a1.75 1.75 0 0 1 1.75 1.75v4.5a1.75 1.75 0 0 1-1.75 1.75H5.5L2.5 13.5v-2Z\" />\n </Icon>\n);\n\n/** Inbox — approvals. */\nexport const IconInbox = (p: IconProps) => (\n <Icon {...p}>\n <path d=\"M2 9.5 3.6 3.4A1.5 1.5 0 0 1 5.05 2.3h5.9a1.5 1.5 0 0 1 1.45 1.1L14 9.5\" />\n <path d=\"M2 9.5h3l.8 1.6h4.4l.8-1.6h3v2.6A1.9 1.9 0 0 1 12.1 14H3.9A1.9 1.9 0 0 1 2 12.1V9.5Z\" />\n </Icon>\n);\n","/**\n * routes/verse/rail-icons.tsx — the icons the shell rail draws at first\n * paint: the rail sections' glyphs, Needs you, the gear and the Verse mark.\n * The gear tray's glyphs (Settings, Apps, Usage) are drawn only once the\n * tray opens, so they live in verse-icons.tsx with SECTION_ICON.\n *\n * Split from verse-icons.tsx (which re-exports all of these) because the\n * rail is on the chat first-paint path: importing verse-icons there pulled\n * every Verse glyph and the whole shared icon set into the chat first-paint\n * critical JS (SPEC-310A §1). This module imports only icon-base.tsx, the\n * shared set's first-paint subset. See verse-icons.tsx for why each glyph is\n * drawn here or aliased onto the shared set.\n */\nimport type { ComponentType, ReactNode } from 'react';\nimport { IconChat, IconInbox, type IconProps } from '../../components/primitives/icon-base.js';\nimport type { VerseSectionId } from './verse-ui-store.js';\n\nexport const ChatIcon = IconChat;\n\n/** The Verse chrome geometry (verse-icons.tsx draws its local glyphs with it too). */\nexport function Icon({ size = 16, children, ...rest }: IconProps & { children: ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n width={size}\n height={size}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n );\n}\n\n/**\n * The mark in the rail head: the Ashlr.AI keystone \"A\" (traced from the brand\n * mark, ashlar-landing public/logos/ashlar-mark.png). The legs take the ink\n * colour so it reads in light and dark themes; the core keeps the brand blue\n * (#2563EB). Not a section icon — it never gets a color state.\n */\nexport function VerseMark(props: IconProps) {\n const { size = 20, ...rest } = props;\n return (\n <svg viewBox=\"54 67 146 124\" width={size} height={size} aria-hidden=\"true\" focusable=\"false\" {...rest}>\n <path fill=\"currentColor\" d=\"M106 76H123V115H100L113 154L99 179H72L59 156Z M130 76H147L194 156L181 179H154L140 154L153 115H130Z\" />\n <path fill=\"#2563EB\" d=\"M110 121H143L133 152H120Z\" />\n </svg>\n );\n}\n\n/** Command — the morning read: a dashboard of unequal panes. */\nexport function CommandIcon(props: IconProps) {\n return (\n <Icon {...props}>\n <rect x=\"2.5\" y=\"2.5\" width=\"5\" height=\"6\" rx=\"1\" />\n <rect x=\"8.5\" y=\"2.5\" width=\"5\" height=\"3.5\" rx=\"1\" />\n <rect x=\"8.5\" y=\"7.5\" width=\"5\" height=\"6\" rx=\"1\" />\n <rect x=\"2.5\" y=\"10\" width=\"5\" height=\"3.5\" rx=\"1\" />\n </Icon>\n );\n}\n\n/** Fleet — lanes of work, each with its runner (the live swimlane). */\nexport function FleetIcon(props: IconProps) {\n return (\n <Icon {...props}>\n <path d=\"M2.5 4.5h5M2.5 8h9M2.5 11.5h3.5\" />\n <circle cx=\"10\" cy=\"4.5\" r=\"1.3\" />\n <circle cx=\"13.2\" cy=\"8\" r=\"1.3\" />\n <circle cx=\"8.5\" cy=\"11.5\" r=\"1.3\" />\n </Icon>\n );\n}\n\n/** Growth — a trend that climbs. */\nexport function GrowthIcon(props: IconProps) {\n return (\n <Icon {...props}>\n <path d=\"M2.5 12.5 6.5 8.5l2.5 2.5 4.5-5\" />\n <path d=\"M10.5 6h3v3\" />\n </Icon>\n );\n}\n\n/** Mind — the Leader's thinking: a lit bulb. */\nexport function MindIcon(props: IconProps) {\n return (\n <Icon {...props}>\n <path d=\"M8 2.5a4 4 0 0 0-2.4 7.2c.3.25.4.6.4 1v.8h4v-.8c0-.4.1-.75.4-1A4 4 0 0 0 8 2.5Z\" />\n <path d=\"M6.5 13.5h3\" />\n </Icon>\n );\n}\n\n/** The gear tray (Settings, Apps, Usage, Shortcuts). */\nexport function GearIcon(props: IconProps) {\n return (\n <Icon {...props}>\n <circle cx=\"8\" cy=\"8\" r=\"2.1\" />\n <path d=\"M8 1.8v1.7M8 12.5v1.7M14.2 8h-1.7M3.5 8H1.8M12.4 3.6l-1.2 1.2M4.8 11.2l-1.2 1.2M12.4 12.4l-1.2-1.2M4.8 4.8 3.6 3.6\" />\n <circle cx=\"8\" cy=\"8\" r=\"4.4\" />\n </Icon>\n );\n}\n\n/** Needs you — the one inbox (⌘J). */\nexport const NeedsYouIcon = IconInbox;\n\n/** The rail sections' glyphs (SECTION_ICON in verse-icons.tsx adds the tray's). */\nexport const RAIL_ICON: Readonly<Partial<Record<VerseSectionId, ComponentType<IconProps>>>> = {\n command: CommandIcon,\n fleet: FleetIcon,\n growth: GrowthIcon,\n mind: MindIcon,\n chat: ChatIcon,\n};\n","/**\n * THE ONE PERCENT RULE, dependency-free.\n *\n * A measured share of a window (or anything capped at 100) printed the same\n * way everywhere: a whole percent, except that a real reading under 1% is\n * \"<1%\" (never \"0%\", which reads as \"untouched\") and one just short of 100 is\n * \"99%\" (never a rounded \"100%\", which reads as \"spent\"). `used` is 0–100 and\n * is clamped; not a number is \"—\".\n *\n * This module imports nothing so the rail, composer and chat header can use\n * it without pulling panel code into chat first paint. `autonomy/format.ts`\n * `percentText` delegates here, so there is one rule, not two.\n */\nexport function usedPercentText(used: number | null | undefined): string {\n if (typeof used !== 'number' || !Number.isFinite(used)) return '—';\n const clamped = Math.max(0, Math.min(100, used));\n if (clamped > 0 && clamped < 1) return '<1%';\n if (clamped > 99 && clamped < 100) return '99%';\n return `${Math.round(clamped)}%`;\n}\n","/**\n * routes/verse/VerseApp.tsx — the Verse 3.10 workbench shell (unit C1;\n * SPEC-310C §1, SPEC-310B §6).\n *\n * [rail] Command ⌘1 · Fleet ⌘2 · Growth ⌘3 · Mind ⌘4 · Chat ⌘5\n * foot: Needs you (⌘J) · the scarcest seat's capacity ring · ⚙ tray\n * [main] the current surface, with up to three recent surfaces (and Chat,\n * once visited) kept MOUNTED behind it — hidden + inert\n * [dock] the Resources drawer when PINNED (3.11 C6): a third grid track\n * that shrinks [main]. Unpinned it floats over the surface, opened\n * from the right-edge tab, the rail, ⌘. or ⌘K \"Open Resources\".\n * overlays ⌘K palette · ⌘J Needs-you drawer · ⌘/ shortcuts · the guard\n *\n * KEEP-ALIVE. Switching surfaces used to unmount the one you left: the\n * approvals selection, the autonomy scroll and the chat's streams were\n * rebuilt every time. Now a left surface stays mounted, `hidden` (no paint,\n * no layout) and `inert` (no focus, no clicks, out of the accessibility\n * tree), inside a SectionVisibilityProvider that tells its polls to stop\n * (C0's usePollWhileVisible). Coming back is instant and exactly where you\n * were. The list is bounded (verse-ui-store KEEP_ALIVE_SURFACES).\n *\n * KEYS come from C0's command catalog — the shell handles the GLOBAL ones\n * (surfaces, palette, drawer, shortcuts, history, recent chats, rail labels,\n * new chat, theme) through the command bus, so the palette, the native menu\n * and a key press all run the same handler. Chat and composer keys belong to\n * C2 / C3 and are never bound here. While the palette is open it owns every\n * key; while a dialog the shell did not open is up (a confirmation, the\n * token prompt, New chat), global keys stand down.\n *\n * SECTIONS resolve through `shell/section-modules.ts`'s glob, one lazy chunk\n * each; a surface whose module has not landed shows a designed \"not in this\n * build\" state (no source paths), and Fleet falls back to the legacy Autonomy\n * panels until C7 lands its own.\n *\n * NATIVE (C8). The desktop app's menu, tray, clicked notifications and the\n * system-wide hotkey arrive through app/desktop-shell.ts\n * `subscribeShellCommands` — the one web seam to the desktop app — already\n * parsed by the catalog: `open-needs-you`, `new-chat`, `focus-composer`,\n * `open-session:<id>` (and the 3.9 menu's two). Anchors (\"go to that card\")\n * arrive as VERSE_ANCHOR_EVENT and are revealed by shell/reveal-anchor.ts\n * (listened for by shell/anchor-requests.ts).\n */\nimport { Suspense, lazy, useEffect, useMemo, useRef, useState, type ComponentType } from 'react';\nimport { reportThemeToShell, subscribeShellCommands } from '../../app/desktop-shell.js';\nimport { RouteErrorBoundary } from '../../components/primitives/RouteErrorBoundary.js';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport { useToast } from '../../components/primitives/Toast.js';\nimport { getMutationToken } from '../../data/auth-store.js';\nimport { queryGateStats } from '../../data/cache.js';\nimport { apiPost } from '../../data/client.js';\nimport { useTheme } from '../../data/hooks.js';\nimport { VERSE_ACTIVITY_SEEN_PATH, type VerseActivityCompletion } from '../../../core/verse/workbench-types.js';\nimport { useResourcesUi } from './resources/resources-store.js';\nimport { commandChord, detectKeyPlatform, formatChord, matchKey } from './shell/command-keys.js';\nimport { GuardHost } from './shell/guarded-action.js';\nimport type { RailBadge } from './shell/RailStatus.js';\nimport { subscribeAnchorRequests } from './shell/anchor-requests.js';\nimport { executeCatalogCommand, useShellCommands } from './shell/run-command.js';\nimport { SectionVisibilityProvider } from './shell/section-visibility.js';\nimport { SECTION_MODULES, sectionImporter } from './shell/section-modules.js';\nimport { SurfaceNotInBuild, SurfaceSkeleton } from './shell/skeletons.js';\nimport { onActivityCompletions, useActivity } from './shell/useActivity.js';\nimport { useViewport } from './shell/viewport.js';\nimport type { WarmupOptions } from './shell/warmup.js';\nimport { useVerseUi } from './useVerseUi.js';\n// rail-icons, not verse-icons: only the rail's glyphs belong in first paint.\nimport { GearIcon, NeedsYouIcon, RAIL_ICON, VerseMark } from './rail-icons.js';\nimport {\n acknowledgeChatMoved,\n closeVerseOverlay,\n getVerseUiState,\n landedModule,\n openVerseSession,\n RAIL_SECTIONS,\n sectionEntry,\n setVerseSection,\n VERSE_ANCHOR_EVENT,\n VERSE_SECTIONS,\n type VerseSectionId,\n} from './verse-ui-store.js';\nimport styles from './VerseApp.module.css';\nimport { usedPercentText } from './percent-text.js';\n\nexport { SECTION_MODULES };\n\n/**\n * The designed state for a surface whose module has not landed. Exported so\n * it can be tested directly (which surfaces exist changes as owners land).\n */\nexport function MissingSection({ label, blurb }: { label: string; blurb?: string }) {\n return <SurfaceNotInBuild label={label} blurb={blurb ?? ''} />;\n}\n\n/**\n * Resolve one section to a lazy component: the primary module, else its\n * fallback, else the designed missing state. A module that throws while\n * loading is logged to the console (never printed to the operator: messages\n * can carry paths) and shown as missing, never as a torn-down shell.\n */\nfunction sectionLoader(id: VerseSectionId): () => Promise<{ default: ComponentType }> {\n const entry = sectionEntry(id);\n return async () => {\n const module = landedModule(id);\n const importer = module ? sectionImporter(module) : undefined;\n if (!module || !importer) return { default: () => <MissingSection label={entry.label} blurb={entry.blurb} /> };\n try {\n const mod = (await importer()) as Record<string, unknown>;\n const exported = mod[module] ?? mod.default;\n if (typeof exported === 'function') return { default: exported as ComponentType };\n console.error(`[verse] ${module} exports no ${module} component`);\n } catch (err) {\n console.error(`[verse] ${entry.label} failed to load`, err);\n }\n return { default: () => <MissingSection label={entry.label} blurb={entry.blurb} /> };\n };\n}\n\n/** One load per section for the life of the tab — shared by its lazy component and the idle prefetch. */\nconst sectionLoads = new Map<VerseSectionId, Promise<{ default: ComponentType }>>();\n/** Sections whose module has arrived, mounted directly (no Suspense round-trip, so no skeleton frame). */\nconst loadedSections = new Map<VerseSectionId, ComponentType>();\n\nfunction loadSection(id: VerseSectionId): Promise<{ default: ComponentType }> {\n let load = sectionLoads.get(id);\n if (!load) {\n load = sectionLoader(id)().then((mod) => {\n loadedSections.set(id, mod.default);\n return mod;\n });\n sectionLoads.set(id, load);\n }\n return load;\n}\n\n/** One lazy component per section, for the life of the tab. */\nconst SECTION_COMPONENTS = new Map<VerseSectionId, ComponentType>(\n VERSE_SECTIONS.map((s) => [s.id, lazy(() => loadSection(s.id))] as const),\n);\n\n// ---------------------------------------------------------------------------\n// Off the first-paint path (review 3.10 d1)\n// ---------------------------------------------------------------------------\n//\n// WHY: every module this file imports statically is in the chunk a cold chat\n// paint must download and parse before anything shows (SPEC-310A §1: chat\n// critical JS ≤ 350 KB, SPEC-310C \"xterm, the charts and the palette each load\n// as separate lazy chunks\"). The overlays render only while open, onboarding\n// only on a first run, and the rail's badges and capacity ring only once the\n// activity / capacity reads answer — none of them can draw anything at first\n// paint, so none of them may cost first-paint bytes. Each is its own chunk,\n// fetched after first paint (the overlays on idle, below) so ⌘K still opens\n// without a visible wait.\n\nconst importPalette = () => import('./shell/CommandPalette.js');\nconst importDrawer = () => import('./shell/NeedsYouDrawer.js');\nconst importShortcuts = () => import('./shell/ShortcutsOverlay.js');\nconst importGearTray = () => import('./shell/GearTray.js');\n// The first-run tour's store and its flow: the gate reads the one and loads the other.\nconst importOnboarding = () => import('./onboarding/OnboardingGate.js');\nconst importRailStatus = () => import('./shell/RailStatus.js');\nconst importWarmup = () => import('./shell/warmup.js');\n// Resources (3.11 C6): the edge tab, rail button, ⌘. handler and the drawer\n// (itself a further chunk) — see resources/ResourcesChrome.tsx for why.\nconst importResources = () => import('./resources/ResourcesChrome.js');\n\nconst CommandPalette = lazy(() => importPalette().then((m) => ({ default: m.CommandPalette })));\nconst NeedsYouDrawer = lazy(() => importDrawer().then((m) => ({ default: m.NeedsYouDrawer })));\nconst ShortcutsOverlay = lazy(() => importShortcuts().then((m) => ({ default: m.ShortcutsOverlay })));\nconst GearTray = lazy(() => importGearTray().then((m) => ({ default: m.GearTray })));\nconst OnboardingGate = lazy(() => importOnboarding().then((m) => ({ default: m.OnboardingGate })));\nconst ResourcesChrome = lazy(() => importResources().then((m) => ({ default: m.ResourcesChrome })));\nconst ResourcesRailButton = lazy(() => importResources().then((m) => ({ default: m.ResourcesRailButton })));\nconst ResourcesBar = lazy(() => importResources().then((m) => ({ default: m.ResourcesBar })));\n\n/**\n * The after-first-paint warm-up (shell/warmup.ts): the overlay chunks, then\n * each rail surface not yet open — its chunk (the same load its lazy\n * component uses, so the first visit mounts it directly) and the reads it\n * opens with — one piece at a time, and only while the operator is idle: no\n * input for a quiet period and none of their reads in flight.\n *\n * The scheduler and the step list are a lazy chunk; this trigger is all the\n * warm-up costs chat first paint. Returns a cancel (called on unmount; safe\n * before the chunk has even arrived).\n */\nexport function prefetchAfterFirstPaint(options?: WarmupOptions): () => void {\n const readsInFlight = () => {\n const gate = queryGateStats();\n return gate.active + gate.queued > 0;\n };\n let live = true;\n let cancel: (() => void) | null = null;\n void importWarmup().then(\n (m) => {\n if (live) cancel = m.warmUpAfterFirstPaint({ loadSection, overlays: [importPalette, importDrawer, importShortcuts, () => importResources().then((r) => r.preloadResourcesDrawer())], readsInFlight }, options);\n },\n () => undefined,\n );\n return () => {\n live = false;\n cancel?.();\n };\n}\n\ntype RailStatusModule = typeof import('./shell/RailStatus.js');\nlet railStatusModule: RailStatusModule | null = null;\n\n/**\n * The rail badge / capacity module, loaded after first paint. RailStatus\n * pulls the usage contract and capacity-strip model (~30 KB) for the ring;\n * the badges it draws depend on the activity read, which is not back at first\n * paint either, so waiting for the module costs nothing visible. Null until\n * loaded — the rail then draws no badge, which RailStatus already defines as\n * \"not known yet\", never \"zero\".\n */\nfunction useRailStatusModule(): RailStatusModule | null {\n const [mod, setMod] = useState<RailStatusModule | null>(railStatusModule);\n useEffect(() => {\n if (mod) return undefined;\n let alive = true;\n void importRailStatus().then(\n (m) => {\n railStatusModule = m;\n if (alive) setMod(m);\n },\n (err) => console.error('[verse] rail status failed to load', err),\n );\n return () => {\n alive = false;\n };\n }, [mod]);\n return mod;\n}\n\n/** A dialog the shell did not open (a confirmation, the token prompt, New chat) is up. */\nfunction foreignModalOpen(): boolean {\n return [...document.querySelectorAll('[aria-modal=\"true\"]')].some(\n (el) => !el.hasAttribute('data-verse-overlay') && !el.querySelector('[data-verse-overlay]'),\n );\n}\n\nconst COMPLETION_TOAST_LIMIT = 3;\n\nexport function VerseApp() {\n const ui = useVerseUi();\n const theme = useTheme();\n const toast = useToast();\n const activity = useActivity();\n const { compact } = useViewport();\n const platform = useMemo(() => detectKeyPlatform(), []);\n const gearRef = useRef<HTMLButtonElement>(null);\n const [trayOpen, setTrayOpen] = useState(false);\n const data = activity.data;\n const rail = useRailStatusModule();\n const resources = useResourcesUi();\n\n useEffect(() => prefetchAfterFirstPaint(), []);\n // The gear tray is fetched right after mount (not on idle) and then stays\n // MOUNTED closed, exactly as before it was split out: its open effect\n // schedules the first item's focus on a frame, and mounting it only on the\n // click shifted that frame behind the operator's first arrow key.\n const [gearReady, setGearReady] = useState(false);\n useEffect(() => {\n let alive = true;\n void importGearTray().then(\n () => { if (alive) setGearReady(true); },\n (err) => console.error('[verse] settings tray failed to load', err),\n );\n return () => { alive = false; };\n }, []);\n\n // ── the global commands the shell serves, and run-command's toasts ─────\n useShellCommands();\n\n // ── global keys ─────────────────────────────────────────────────────────\n useEffect(() => {\n function onKey(event: KeyboardEvent) {\n if (event.defaultPrevented) return;\n if (getVerseUiState().overlay === 'palette') return;\n if (foreignModalOpen()) return;\n const command = matchKey(event, ['global']);\n if (!command) return;\n // preventDefault BEFORE running: the chat's key fallback and the\n // transcript's ⌥↑/⌥↓ skip default-prevented events (C2), which is what\n // keeps one press from running two handlers. stopPropagation is not\n // used — other listeners still see the event, they just know it is taken.\n event.preventDefault();\n executeCatalogCommand(command.id, { via: 'key' });\n }\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, []);\n\n // ── the desktop shell: menu, tray, notifications, hotkey ────────────────\n useEffect(\n () =>\n subscribeShellCommands((command) => {\n if (command.kind === 'open-session') {\n closeVerseOverlay();\n openVerseSession(command.sessionId);\n return;\n }\n // The tray's \"New chat\" and the hotkey's \"focus the composer\" must\n // land in the composer: an open palette or shortcuts sheet would keep\n // focus (and every key) for itself. The drawer opener, the theme and\n // Settings leave overlays alone — open-needs-you REPLACES the overlay.\n if (command.name === 'new-chat' || command.name === 'focus-composer') closeVerseOverlay();\n executeCatalogCommand(command.commandId, { via: 'menu' });\n }),\n [],\n );\n // \"Go to that card\": whoever raises it (drawer, Command, Mind), the shell reveals it.\n useEffect(() => subscribeAnchorRequests(VERSE_ANCHOR_EVENT), []);\n // Report the PAINTED theme so the native window pre-paints its background\n // on the next cold launch instead of flashing white. No-op in a browser.\n useEffect(() => { reportThemeToShell(theme.theme); }, [theme.theme]);\n\n // ── one-time announcements ──────────────────────────────────────────────\n useEffect(() => {\n if (!ui.announceChatMoved) return;\n const chat = commandChord('surface.chat');\n toast.show(`Chat moved to ${chat ? formatChord(chat, platform) : '⌘5'} — Command, Fleet, Growth and Mind come first now.`);\n acknowledgeChatMoved();\n }, [ui.announceChatMoved, toast, platform]);\n\n // ── in-app \"Finished / Failed\" (the window is visible; C8 notifies when it is not) ─\n useEffect(\n () =>\n onActivityCompletions((batch: VerseActivityCompletion[]) => {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return;\n const s = getVerseUiState();\n const news = batch.filter((c) => !(s.section === 'chat' && s.activeSessionId === c.sessionId));\n const failed = news.filter((c) => c.outcome === 'failed');\n for (const c of failed.slice(0, COMPLETION_TOAST_LIMIT)) toast.show(`Failed: ${c.title}`, 'danger');\n const done = news.filter((c) => c.outcome === 'ok');\n if (done.length > COMPLETION_TOAST_LIMIT) toast.show(`${done.length} chats finished.`, 'success');\n else for (const c of done) toast.show(`Finished: ${c.title}`, 'success');\n }),\n [toast],\n );\n\n // ── Mind's dot clears when Mind is opened ───────────────────────────────\n const [mindSeenLocal, setMindSeenLocal] = useState<string | null>(null);\n const latestMemoAt = data?.mind?.latestMemoAt ?? null;\n useEffect(() => {\n if (ui.section !== 'mind' || !data?.mind?.unseen || !latestMemoAt) return;\n setMindSeenLocal(latestMemoAt);\n // Best-effort on the server too (it needs a held token; reading Mind is\n // not worth a token prompt — the local mark already clears the dot).\n const token = getMutationToken();\n if (token) void apiPost<unknown>(VERSE_ACTIVITY_SEEN_PATH, { surface: 'mind' }, token).catch(() => undefined);\n }, [ui.section, data?.mind?.unseen, latestMemoAt]);\n\n const expanded = ui.railExpanded && !compact;\n const needsYouCount = data ? data.counts.needsYou : null;\n // Settings, Apps & Accounts and Usage open from the gear, so on those pages\n // the gear IS the current rail item — for assistive tech too (aria-current,\n // and the page's name, since one gear stands for three pages), not only\n // through the data-active styling.\n const currentPage = sectionEntry(ui.section);\n const gearPage = currentPage.placement === 'tray' ? currentPage : null;\n const BadgeMark = rail?.RailBadgeMark ?? null;\n const shortcut = (id: string) => {\n const chord = commandChord(id);\n return chord ? formatChord(chord, platform) : undefined;\n };\n\n return (\n <div\n className={styles.shell}\n data-rail={expanded ? 'expanded' : 'collapsed'}\n data-compact={compact || undefined}\n data-resources={resources.open && resources.pinned && !compact ? 'docked' : undefined}\n >\n <nav className={styles.rail} data-rail={expanded ? 'expanded' : 'collapsed'} aria-label=\"Verse sections\">\n {/*\n Desktop shell: the window's top 48px is overlaid by the OS title bar\n and the traffic lights. The rail clears it in CSS; this strip makes\n the cleared space drag the window. 0px tall in a browser, where\n `--app-titlebar-height` is not set. See desktop/README.md.\n */}\n <span className={styles.railDragStrip} data-app-region=\"drag\" aria-hidden=\"true\" />\n <Tooltip label=\"Ashlr Verse\" placement=\"right\" disabled={expanded}>\n <span className={styles.mark}>\n <VerseMark />\n <span className=\"visually-hidden\">Ashlr Verse</span>\n </span>\n </Tooltip>\n <ul className={styles.railList}>\n {RAIL_SECTIONS.map((entry) => {\n const IconComponent = RAIL_ICON[entry.id];\n // Mind's dot also clears on sight in THIS window, whether or not\n // the server-side mark could be written (it needs a held token).\n const mindSeenHere = entry.id === 'mind' && latestMemoAt !== null && mindSeenLocal === latestMemoAt;\n const badge: RailBadge | null = mindSeenHere || !rail ? null : rail.railBadgeFor(entry.id, data);\n const active = ui.section === entry.id;\n const keys = shortcut(`surface.${entry.id}`);\n return (\n <li key={entry.id}>\n {/* The name stays on the button; badges ride it so they are announced, not just drawn. */}\n <Tooltip label={entry.label} shortcut={keys} placement=\"right\" disabled={expanded || compact}>\n <button\n type=\"button\"\n className={styles.railButton}\n aria-current={active ? 'page' : undefined}\n data-section={entry.id}\n aria-label={`${entry.label}${badge?.spoken ?? ''}`}\n onClick={() => setVerseSection(entry.id)}\n >\n <span className={styles.railIcon}>\n {IconComponent ? <IconComponent /> : null}\n {badge && BadgeMark ? <BadgeMark badge={badge} /> : null}\n </span>\n {expanded || compact ? <span className={styles.railLabel}>{entry.label}</span> : null}\n {expanded && keys ? <span className={styles.railKey} aria-hidden=\"true\">{keys}</span> : null}\n </button>\n </Tooltip>\n </li>\n );\n })}\n </ul>\n <div className={styles.railFoot}>\n <Tooltip label=\"Needs you\" shortcut={shortcut('needs-you.open')} placement=\"right\" disabled={expanded || compact}>\n <button\n type=\"button\"\n className={styles.railButton}\n aria-label={needsYouCount === null ? 'Needs you' : `Needs you, ${needsYouCount}`}\n aria-expanded={ui.overlay === 'needs-you'}\n data-needs-you={needsYouCount ?? undefined}\n onClick={() => executeCatalogCommand('needs-you.open', { via: 'button' })}\n >\n <span className={styles.railIcon}>\n <NeedsYouIcon />\n {needsYouCount && BadgeMark ? <BadgeMark badge={{ kind: 'count', count: needsYouCount, spoken: '', tone: 'warning' }} /> : null}\n </span>\n {expanded || compact ? <span className={styles.railLabel}>{compact ? 'Inbox' : 'Needs you'}</span> : null}\n </button>\n </Tooltip>\n {compact ? null : (\n <Suspense fallback={null}>\n <ResourcesRailButton expanded={expanded} buttonClass={styles.railButton} iconClass={styles.railIcon} labelClass={styles.railLabel} />\n </Suspense>\n )}\n {/* 3.11.1: the always-on resource bar (logos + batteries) replaces the\n single capacity ring while it is on; off brings the ring back. */}\n {compact ? null : resources.bar ? (\n <Suspense fallback={null}>\n <ResourcesBar expanded={expanded} />\n </Suspense>\n ) : rail ? <RailCapacityButton rail={rail} expanded={expanded} /> : null}\n <Tooltip label=\"Settings and more\" placement=\"right\" disabled={expanded || compact || trayOpen}>\n <button\n ref={gearRef}\n type=\"button\"\n className={styles.railButton}\n aria-label={gearPage ? `Settings and more (${gearPage.label} open)` : 'Settings and more'}\n aria-haspopup=\"menu\"\n aria-expanded={trayOpen}\n aria-current={gearPage ? 'page' : undefined}\n data-gear\n data-active={gearPage ? true : undefined}\n onClick={() => setTrayOpen((v) => !v)}\n >\n <span className={styles.railIcon}><GearIcon /></span>\n {expanded || compact ? <span className={styles.railLabel}>{compact ? 'More' : 'Settings'}</span> : null}\n </button>\n </Tooltip>\n </div>\n </nav>\n\n {/*\n The ONE <main id=\"main-content\"> for the whole shell (SkipToContent\n focuses it). Every mounted surface lives inside it; only the current\n one is visible.\n */}\n <main className={styles.section} data-section={ui.section} id=\"main-content\" tabIndex={-1}>\n {ui.mounted.map((id) => (\n <SurfaceHost key={id} id={id} active={id === ui.section} />\n ))}\n </main>\n\n {/* Lazy chunks (see \"Off the first-paint path\"); each mounts only while it can draw. */}\n <Suspense fallback={null}>\n {gearReady || trayOpen ? <GearTray open={trayOpen} anchorRef={gearRef} onClose={() => setTrayOpen(false)} compact={compact} /> : null}\n </Suspense>\n <Suspense fallback={null}>\n <ResourcesChrome compact={compact} />\n </Suspense>\n <Suspense fallback={null}>\n {ui.overlay === 'palette' ? <CommandPalette /> : null}\n {ui.overlay === 'needs-you' ? <NeedsYouDrawer /> : null}\n {ui.overlay === 'shortcuts' ? <ShortcutsOverlay /> : null}\n </Suspense>\n <GuardHost />\n {/*\n First run only, outside the surfaces: a docked card, not a modal, so\n the rail and the surface stay usable while it is open.\n */}\n <Suspense fallback={null}><OnboardingGate /></Suspense>\n </div>\n );\n}\n\n/**\n * The rail foot's capacity ring for the scarcest seat. Its own component so\n * `useRailCapacity` (a hook from the lazily loaded RailStatus module) is only\n * called once that module is here, always in the same place.\n */\nfunction RailCapacityButton({ rail, expanded }: { rail: RailStatusModule; expanded: boolean }) {\n const capacity = rail.useRailCapacity();\n if (!capacity) return null;\n const { CapacityRing, describeRailCapacity } = rail;\n return (\n <Tooltip label={describeRailCapacity(capacity)} placement=\"right\" disabled={expanded}>\n <button\n type=\"button\"\n className={styles.railButton}\n aria-label={`Capacity — ${describeRailCapacity(capacity)}`}\n data-capacity={Math.round(capacity.usedPercent)}\n onClick={() => setVerseSection('apps', `seat:${capacity.seatId}`)}\n >\n <span className={styles.railIcon}>\n <CapacityRing badge={capacity} />\n </span>\n {expanded ? (\n <span className={styles.railLabel}>\n {capacity.label} {capacity.limitReached ? 'limit' : usedPercentText(capacity.usedPercent)}\n </span>\n ) : null}\n </button>\n </Tooltip>\n );\n}\n\nfunction SurfaceHost({ id, active }: { id: VerseSectionId; active: boolean }) {\n // A section the idle prefetch already loaded mounts directly: React.lazy\n // would still suspend once on its first render — even with the module in\n // hand — and commit the skeleton, the very flash the prefetch exists to\n // remove. Chosen ONCE per host, so a load that lands while this surface is\n // up never swaps the component type under it (that would remount the\n // surface and drop its state).\n const [Section] = useState<ComponentType>(() => loadedSections.get(id) ?? SECTION_COMPONENTS.get(id)!);\n const entry = sectionEntry(id);\n return (\n <div className={styles.surface} data-surface={id} hidden={!active} inert={!active}>\n <SectionVisibilityProvider visible={active}>\n <RouteErrorBoundary resetKey={id}>\n <Suspense fallback={<SurfaceSkeleton section={id} label={entry.label} />}>\n <Section />\n </Suspense>\n </RouteErrorBoundary>\n </SectionVisibilityProvider>\n </div>\n );\n}\n","/**\n * routes/verse/verse-queries.ts — every /api/verse/* call the Verse console\n * makes. Reads are QueryDefs consumed through useQuery (cache-backed, so a\n * sidebar refresh never blanks the list); writes are wrapped like\n * data/mutations.ts's helpers — they pull the held mutation token from\n * auth-store, touch the hold on success, and invalidate the cache keys they\n * obviously affect. Nothing here ever sees launcher commands or env: the\n * server strips those before any payload leaves it (docs/VERSE-CONTRACT-V1).\n */\nimport type {\n VerseAutonomyScopeView,\n VerseBootstrap,\n VerseCreateSessionRequest,\n VerseRootPriority,\n VerseSession,\n VerseSessionDetail,\n VerseSessionRootsResponse,\n VerseTurnRequest,\n VerseTurnResponse,\n VerseWorkspace,\n VerseWorkspaceCreateRequest,\n VerseWorkspacesResponse,\n VerseWorkspaceUpdateRequest,\n} from '../../data/api-types.js';\nimport type {\n VerseActivityResponse,\n VerseActivitySeenRequest,\n VerseSessionMeta,\n VerseSessionMetaResponse,\n VerseSessionMetaUpdate,\n} from '../../../core/verse/workbench-types.js';\nimport { VERSE_ACTIVITY_PATH, VERSE_ACTIVITY_SEEN_PATH, VERSE_SESSION_META_PATH } from '../../../core/verse/workbench-types.js';\nimport { getMutationToken, touchMutationHold } from '../../data/auth-store.js';\nimport { ApiError, apiGet, apiPost } from '../../data/client.js';\nimport { invalidate } from '../../data/cache.js';\nimport type { QueryDef } from '../../data/queries.js';\n\nexport const VERSE_BOOTSTRAP_KEY = 'verse-bootstrap';\nexport const VERSE_SESSIONS_KEY = 'verse-sessions';\n\nexport const verseBootstrapQuery: QueryDef<VerseBootstrap> = {\n key: VERSE_BOOTSTRAP_KEY,\n fetch: (signal) => apiGet<VerseBootstrap>('/api/verse/bootstrap', signal),\n};\n\nexport const verseSessionsQuery: QueryDef<VerseSession[]> = {\n key: VERSE_SESSIONS_KEY,\n fetch: (signal) => apiGet<VerseSession[]>('/api/verse/sessions', signal),\n};\n\nexport function verseSessionPath(sessionId: string, suffix = ''): string {\n return `/api/verse/sessions/${encodeURIComponent(sessionId)}${suffix}`;\n}\n\nexport function fetchVerseSessionDetail(sessionId: string, signal?: AbortSignal): Promise<VerseSessionDetail> {\n return apiGet<VerseSessionDetail>(verseSessionPath(sessionId), signal);\n}\n\nexport class VerseMutationLockedError extends Error {\n constructor() {\n super('Unlock actions with the mutation token before chatting.');\n this.name = 'VerseMutationLockedError';\n }\n}\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new VerseMutationLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\n/** Sidebar + resources panel both read these; refresh them after any write. */\nexport function invalidateVerseLists(): void {\n invalidate(VERSE_SESSIONS_KEY);\n invalidate(VERSE_BOOTSTRAP_KEY);\n}\n\nexport async function createVerseSession(req: VerseCreateSessionRequest): Promise<VerseSession> {\n const session = await post<VerseSession>('/api/verse/sessions', req);\n invalidateVerseLists();\n return session;\n}\n\nexport async function sendVerseTurn(sessionId: string, text: string): Promise<VerseTurnResponse> {\n const body: VerseTurnRequest = { text };\n const response = await post<VerseTurnResponse>(verseSessionPath(sessionId, '/turns'), body);\n invalidate(VERSE_SESSIONS_KEY);\n return response;\n}\n\nexport async function cancelVerseTurn(sessionId: string): Promise<void> {\n await post<{ ok: true }>(verseSessionPath(sessionId, '/cancel'), {});\n invalidate(VERSE_SESSIONS_KEY);\n}\n\nexport async function deleteVerseSession(sessionId: string): Promise<void> {\n await post<{ ok: true }>(verseSessionPath(sessionId, '/delete'), {});\n invalidateVerseLists();\n}\n\nexport async function renameVerseSession(sessionId: string, title: string): Promise<VerseSession> {\n const session = await post<VerseSession>(verseSessionPath(sessionId, '/rename'), { title });\n invalidate(VERSE_SESSIONS_KEY);\n return session;\n}\n\n// ---------------------------------------------------------------------------\n// Workspaces (V2.2)\n// ---------------------------------------------------------------------------\n\nexport const VERSE_WORKSPACES_KEY = 'verse-workspaces';\nexport const VERSE_AUTONOMY_SCOPE_KEY = 'verse-autonomy-scope';\n\nexport const verseWorkspacesQuery: QueryDef<VerseWorkspacesResponse> = {\n key: VERSE_WORKSPACES_KEY,\n fetch: (signal) => apiGet<VerseWorkspacesResponse>('/api/verse/workspaces', signal),\n};\n\n/**\n * Enrolled repos, ordered by section focus and priority. Kept on its own key\n * from `verseScopeQuery` (which is the raw registry) so a re-rank refreshes\n * the ORDER without re-reading enrollment — and so it is obvious in the cache\n * that these are two different facts.\n */\nexport const verseAutonomyScopeQuery: QueryDef<VerseAutonomyScopeView> = {\n key: VERSE_AUTONOMY_SCOPE_KEY,\n fetch: (signal) => apiGet<VerseAutonomyScopeView>('/api/verse/autonomy-scope', signal),\n};\n\n/**\n * Per-root identity for one session: branch, dirty state, remote, whether the\n * engine can reach each root and whether the autonomous lane would.\n *\n * Deliberately NOT cached by session id alone at module scope — a branch goes\n * stale the moment a turn commits, so the panel refetches it per open.\n */\nexport function fetchVerseSessionRoots(sessionId: string, signal?: AbortSignal): Promise<VerseSessionRootsResponse> {\n return apiGet<VerseSessionRootsResponse>(verseSessionPath(sessionId, '/roots'), signal);\n}\n\nfunction invalidateWorkspaces(): void {\n invalidate(VERSE_WORKSPACES_KEY);\n invalidate(VERSE_AUTONOMY_SCOPE_KEY);\n invalidate(VERSE_BOOTSTRAP_KEY);\n}\n\nexport async function createVerseWorkspace(req: VerseWorkspaceCreateRequest): Promise<VerseWorkspace> {\n const workspace = await post<VerseWorkspace>('/api/verse/workspaces', req);\n invalidateWorkspaces();\n return workspace;\n}\n\nexport async function updateVerseWorkspace(id: string, patch: VerseWorkspaceUpdateRequest): Promise<VerseWorkspace> {\n const workspace = await post<VerseWorkspace>(`/api/verse/workspaces/${encodeURIComponent(id)}/update`, patch);\n invalidateWorkspaces();\n return workspace;\n}\n\nexport async function deleteVerseWorkspace(id: string): Promise<void> {\n await post<{ ok: true }>(`/api/verse/workspaces/${encodeURIComponent(id)}/delete`, {});\n invalidateWorkspaces();\n}\n\nexport async function setVerseRootPriority(path: string, priority: VerseRootPriority): Promise<void> {\n await post<unknown>('/api/verse/workspaces/priority', { path, priority });\n invalidateWorkspaces();\n}\n\nexport async function setVerseFocusSection(sectionId: string | null): Promise<void> {\n await post<unknown>('/api/verse/workspaces/focus', { sectionId });\n invalidateWorkspaces();\n}\n\n// ---------------------------------------------------------------------------\n// 3.10 — activity + per-chat meta (C1's routes; C2's sidebar reads them)\n// ---------------------------------------------------------------------------\n//\n// Both route families are mounted lazily (C0's mount table): on a build where\n// C1's module has not landed they answer a plain 404. That is \"this server\n// has no activity yet\", not an error the sidebar should paint red — so a 404\n// reads as `null` and every consumer falls back to what the session list\n// alone can say (running dot from `status`, no pins, no unread).\n\nexport const VERSE_ACTIVITY_KEY = 'verse-activity';\nexport const VERSE_SESSION_META_KEY = 'verse-session-meta';\n\nasync function getOrNull<T>(path: string, signal?: AbortSignal): Promise<T | null> {\n try {\n return await apiGet<T>(path, signal);\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) return null;\n throw err;\n }\n}\n\n/**\n * The poll-shaped read (no `since`): running chats with their live line and\n * the Needs-you items. Completions are C1's concern (its cursor loop feeds\n * the rail and the native notifications); the sidebar needs only the present.\n */\nexport const verseActivityQuery: QueryDef<VerseActivityResponse | null> = {\n key: VERSE_ACTIVITY_KEY,\n fetch: (signal) => getOrNull<VerseActivityResponse>(VERSE_ACTIVITY_PATH, signal),\n};\n\nexport const verseSessionMetaQuery: QueryDef<VerseSessionMetaResponse | null> = {\n key: VERSE_SESSION_META_KEY,\n fetch: (signal) => getOrNull<VerseSessionMetaResponse>(VERSE_SESSION_META_PATH, signal),\n};\n\n/** Pin / archive one chat. Unknown keys are a 400 on the server, so only the two known ones are ever sent. */\nexport async function setVerseSessionMeta(sessionId: string, update: VerseSessionMetaUpdate): Promise<VerseSessionMeta> {\n const body: VerseSessionMetaUpdate = {};\n if (typeof update.pinned === 'boolean') body.pinned = update.pinned;\n if (typeof update.archived === 'boolean') body.archived = update.archived;\n const meta = await post<VerseSessionMeta>(`${VERSE_SESSION_META_PATH}/${encodeURIComponent(sessionId)}`, body);\n invalidate(VERSE_SESSION_META_KEY);\n return meta;\n}\n\n/**\n * \"The operator has read this chat up to `turnCount`.\" Best-effort and SILENT:\n * it runs only when a token is already held (opening a chat must never raise\n * the token dialog), and a failure is swallowed — the sidebar keeps its own\n * optimistic read state, so the unread dot clears either way.\n */\nexport async function markVerseSessionSeen(sessionId: string, turnCount: number): Promise<boolean> {\n const token = getMutationToken();\n if (!token) return false;\n const body: VerseActivitySeenRequest = { sessionId, turnCount };\n try {\n // apiPost, not post(): reading a chat is not operator ACTIVITY, so it\n // must not extend the mutation hold the way a real write does.\n await apiPost<unknown>(VERSE_ACTIVITY_SEEN_PATH, body, token);\n return true;\n } catch {\n return false;\n }\n}\n","export default \"__VITE_ASSET__VRAku6fjghkApIISiBWPzg__\"","export default \"__VITE_ASSET__WPGYjwIlzWVNM1bYhOc83w__\"","/**\n * app/VerseConsoleApp.tsx — isolated bootstrap for /verse (pattern:\n * ResourcePoolConsoleApp). No global dashboard shell, router, or observer\n * stream: the read session is probed against /api/verse/bootstrap, host-\n * injected tokens (desktop wrapper) are adopted before that probe, and the\n * full-bleed VerseApp takes over once authenticated.\n *\n * V3.10 — first paint without a serial wait. The chunk of the section the\n * shell will mount (Chat, unless the operator left another one open) used to\n * start downloading only AFTER the bootstrap probe answered: index →\n * this module → probe → section chunk, one round trip after another. Now\n * this module starts that import the moment it evaluates, in parallel with\n * the probe (Vite shares the in-flight import with the lazy() that mounts\n * it later), and preloads the Latin font subsets the first paint needs (the\n * display face only once the signed-in app is what will paint).\n *\n * V3.10 — a restarted desktop sidecar is re-adopted at once. The desktop\n * shell restarts a crashed server in the background, hands the new tokens to\n * this page as `window.__ASHLR_TOKENS__` and fires `ashlr:sidecar-restarted`\n * (desktop/src-tauri/src/main.rs token_handoff_script). The old cookie died\n * with the old server, so without listening the page would sit until its\n * next 401 → failed renewal → SessionGate → adoption.\n */\nimport { lazy, Suspense, useEffect } from 'react';\nimport { ToastProvider } from '../components/primitives/Toast.js';\nimport { adoptInjectedTokens, markCheckComplete } from '../data/auth-store.js';\nimport { getQuerySnapshot, runQuery } from '../data/cache.js';\nimport { useAuthPhase } from '../data/hooks.js';\nimport { SECTION_MODULES, VerseApp } from '../routes/verse/VerseApp.js';\nimport { invalidateVerseLists, verseBootstrapQuery } from '../routes/verse/verse-queries.js';\nimport { getVerseUiState, VERSE_SECTIONS } from '../routes/verse/verse-ui-store.js';\nimport uiFontLatin from '../design/fonts/AshlrSans-latin.woff2?url';\nimport displayFontLatin from '../design/fonts/SpaceGrotesk-latin.woff2?url';\nimport { SkipToContent } from './SkipToContent.js';\nimport styles from './VerseConsoleApp.module.css';\nimport '../design/global.css';\n\n/**\n * Preload the Latin font subsets: the UI face always (every state's first\n * text is set in it), the display face (Space Grotesk: headings, numerals)\n * only when the signed-in app is what will paint. On the signed-out connect\n * screen the display face serves one heading, drawn only after the auth probe\n * answers and the gate's chunk arrives; preloaded at page start it routinely\n * sat unused past the browser's few-second window and drew \"preloaded but\n * not used\" in the console. Idempotent.\n */\nexport function preloadVerseFonts(doc: Document | null, display: boolean): void {\n if (!doc?.head) return;\n for (const href of display ? [uiFontLatin, displayFontLatin] : [uiFontLatin]) {\n const present = [...doc.head.querySelectorAll('link[rel=\"preload\"]')].some((l) => l.getAttribute('href') === href);\n if (present) continue;\n const link = doc.createElement('link');\n link.rel = 'preload';\n link.as = 'font';\n link.type = 'font/woff2';\n // Font preloads are CORS requests even same-origin; without this the\n // browser fetches the file twice.\n link.crossOrigin = 'anonymous';\n link.href = href;\n doc.head.append(link);\n }\n}\n\n/**\n * Start what the first paint will need, without waiting for the auth probe.\n * Everything here is a cache warm-up: a failure is ignored, because the real\n * mount (VerseApp's sectionLoader, the CSS @font-face) repeats the request\n * and owns the error handling.\n *\n * `signedIn` is the best guess available before the probe: the desktop shell\n * injected its tokens, so the chat is what paints. Otherwise the display face\n * waits for the authenticated phase (VerseConsoleApp below).\n */\nexport function preloadVerseFirstPaint(\n doc: Document | null = typeof document === 'undefined' ? null : document,\n signedIn: boolean = typeof window !== 'undefined' && window.__ASHLR_TOKENS__ !== undefined,\n): void {\n const section = VERSE_SECTIONS.find((s) => s.id === getVerseUiState().section);\n const importer = section ? SECTION_MODULES[`./sections/${section.module}.tsx`] : undefined;\n if (importer) void importer().catch(() => undefined);\n preloadVerseFonts(doc, signedIn);\n}\n\npreloadVerseFirstPaint();\n\n/**\n * The session gate is a dynamic import: a signed-in cold load (the common\n * case — the desktop shell injects its tokens) never draws it, and as a\n * static import it cost ~3 KB of chat first-paint critical JS (SPEC-310A §1).\n * `preloadSessionGate` starts the download while the auth probe is in flight,\n * so an unauthenticated answer finds the chunk already there instead of\n * paying a second round trip. Vite shares the in-flight import with lazy().\n */\nconst importSessionGate = () => import('../components/auth/SessionGate.js');\nconst SessionGate = lazy(() => importSessionGate().then((m) => ({ default: m.SessionGate })));\nfunction preloadSessionGate(): void {\n void importSessionGate().catch(() => undefined);\n}\n\n/** DOM event the desktop shell fires after handing a restarted sidecar's tokens to this page. */\nexport const SIDECAR_RESTARTED_EVENT = 'ashlr:sidecar-restarted';\n\n/**\n * Re-adopt host tokens whenever the desktop shell says its sidecar restarted.\n * Returns the disposer.\n *\n * Installed for the page's whole life, whatever the auth phase: the event may\n * land while the SessionGate is showing (adoption then replaces the prompt,\n * because establishReadSession flips the phase) or while the app looks\n * authenticated on a dead cookie. After a successful adoption the lists are\n * refetched — the restarted server may have marked turns interrupted — and\n * the live streams reconnect on their own: their next attempt now carries a\n * valid cookie. A failed adoption changes nothing; the ordinary 401 path\n * still ends at the SessionGate. The event carries no data, so a page script\n * firing it can at most make the page re-read tokens that are already there.\n */\nexport function listenForSidecarRestart(target: Pick<Window, 'addEventListener' | 'removeEventListener'> = window): () => void {\n let inFlight = false;\n let again = false;\n let disposed = false;\n const onRestart = (): void => {\n if (disposed) return;\n // Restarts in quick succession: adoption reads `window.__ASHLR_TOKENS__`\n // when it STARTS, so a second restart during an exchange left newer\n // tokens behind it. Run exactly once more after the current exchange\n // instead of racing a second one for the cookie.\n if (inFlight) {\n again = true;\n return;\n }\n inFlight = true;\n void adoptInjectedTokens()\n .then((adopted) => { if (adopted) invalidateVerseLists(); })\n .catch(() => undefined)\n .finally(() => {\n inFlight = false;\n if (again) {\n again = false;\n onRestart();\n }\n });\n };\n target.addEventListener(SIDECAR_RESTARTED_EVENT, onRestart);\n return () => {\n disposed = true;\n target.removeEventListener(SIDECAR_RESTARTED_EVENT, onRestart);\n };\n}\n\nexport function VerseConsoleApp() {\n const phase = useAuthPhase();\n useEffect(() => listenForSidecarRestart(), []);\n useEffect(() => {\n if (phase === 'authenticated') preloadVerseFonts(document, true);\n if (phase !== 'checking') return;\n preloadSessionGate();\n let cancelled = false;\n void adoptInjectedTokens().then((adopted) => {\n if (cancelled || adopted) return;\n return runQuery(verseBootstrapQuery.key, verseBootstrapQuery.fetch).then(() => {\n if (!cancelled) markCheckComplete(getQuerySnapshot(verseBootstrapQuery.key).status === 'success');\n });\n });\n return () => { cancelled = true; };\n }, [phase]);\n if (phase === 'checking') return <p className={styles.checking} role=\"status\">Checking for an existing Verse session…</p>;\n if (phase === 'unauthenticated') {\n return (\n <Suspense fallback={<p className={styles.checking} role=\"status\">Checking for an existing Verse session…</p>}>\n <SessionGate heading=\"Connect to Ashlr Verse\" command=\"ashlr verse\" subject=\"Ashlr Verse\" mutationField />\n </Suspense>\n );\n }\n return (\n <ToastProvider>\n <SkipToContent />\n <div className={styles.shell}><VerseApp /></div>\n </ToastProvider>\n );\n}\n"],"file":"VerseConsoleApp-DXDv1aOi.js"}