@ashlr/hub 3.11.3 → 3.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (556) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +44 -18
  3. package/dist/authority-surface.json +72 -57
  4. package/dist/build-identity.json +2 -2
  5. package/dist/cli/authority.d.ts +39 -0
  6. package/dist/cli/authority.js +222 -55
  7. package/dist/cli/authority.js.map +1 -1
  8. package/dist/cli/help.js +1 -1
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/verse.d.ts +3 -0
  11. package/dist/cli/verse.js +10 -0
  12. package/dist/cli/verse.js.map +1 -1
  13. package/dist/core/authority/types.d.ts +2 -2
  14. package/dist/core/cloud/cloud-api.js +1 -1
  15. package/dist/core/cloud/cloud-api.js.map +1 -1
  16. package/dist/core/cloud/delivery-contract.d.ts +1 -1
  17. package/dist/core/cloud/delivery-contract.js +25 -15
  18. package/dist/core/cloud/delivery-contract.js.map +1 -1
  19. package/dist/core/cloud/store.js +6 -0
  20. package/dist/core/cloud/store.js.map +1 -1
  21. package/dist/core/cloud/tracker.d.ts +2 -0
  22. package/dist/core/cloud/tracker.js +140 -37
  23. package/dist/core/cloud/tracker.js.map +1 -1
  24. package/dist/core/cloud/types.d.ts +9 -1
  25. package/dist/core/cloud/types.js.map +1 -1
  26. package/dist/core/comms/dispatch.d.ts +5 -0
  27. package/dist/core/comms/dispatch.js +12 -1
  28. package/dist/core/comms/dispatch.js.map +1 -1
  29. package/dist/core/comms/requests.d.ts +27 -1
  30. package/dist/core/comms/requests.js +50 -1
  31. package/dist/core/comms/requests.js.map +1 -1
  32. package/dist/core/daemon/loop.d.ts +14 -0
  33. package/dist/core/daemon/loop.js +35 -0
  34. package/dist/core/daemon/loop.js.map +1 -1
  35. package/dist/core/fabric/claude-usage.js +23 -1
  36. package/dist/core/fabric/claude-usage.js.map +1 -1
  37. package/dist/core/fleet/dispatch-router.d.ts +7 -0
  38. package/dist/core/fleet/dispatch-router.js +4 -1
  39. package/dist/core/fleet/dispatch-router.js.map +1 -1
  40. package/dist/core/fleet/tick-hooks-live.d.ts +5 -1
  41. package/dist/core/fleet/tick-hooks-live.js +10 -1
  42. package/dist/core/fleet/tick-hooks-live.js.map +1 -1
  43. package/dist/core/local-runtime/llama/config.d.ts +14 -0
  44. package/dist/core/local-runtime/llama/config.js +20 -2
  45. package/dist/core/local-runtime/llama/config.js.map +1 -1
  46. package/dist/core/local-runtime/llama/process.d.ts +19 -0
  47. package/dist/core/local-runtime/llama/process.js +67 -17
  48. package/dist/core/local-runtime/llama/process.js.map +1 -1
  49. package/dist/core/local-runtime/llama/supervisor.js +14 -3
  50. package/dist/core/local-runtime/llama/supervisor.js.map +1 -1
  51. package/dist/core/observability/rollup.d.ts +12 -0
  52. package/dist/core/observability/rollup.js +37 -2
  53. package/dist/core/observability/rollup.js.map +1 -1
  54. package/dist/core/observability/usage-source.js +49 -1
  55. package/dist/core/observability/usage-source.js.map +1 -1
  56. package/dist/core/resources/connection-monitor.js +47 -4
  57. package/dist/core/resources/connection-monitor.js.map +1 -1
  58. package/dist/core/resources/engineering-mission-store.d.ts +1 -1
  59. package/dist/core/routing/headroom.js +7 -2
  60. package/dist/core/routing/headroom.js.map +1 -1
  61. package/dist/core/routing/router.d.ts +48 -0
  62. package/dist/core/routing/router.js +129 -13
  63. package/dist/core/routing/router.js.map +1 -1
  64. package/dist/core/run/best-of-n.d.ts +7 -0
  65. package/dist/core/run/best-of-n.js +3 -0
  66. package/dist/core/run/best-of-n.js.map +1 -1
  67. package/dist/core/run/harness-dispatch.d.ts +147 -0
  68. package/dist/core/run/harness-dispatch.js +234 -0
  69. package/dist/core/run/harness-dispatch.js.map +1 -0
  70. package/dist/core/run/orchestrator.js +3 -0
  71. package/dist/core/run/orchestrator.js.map +1 -1
  72. package/dist/core/run/provider-client.d.ts +8 -0
  73. package/dist/core/run/provider-client.js +4 -0
  74. package/dist/core/run/provider-client.js.map +1 -1
  75. package/dist/core/run/sandboxed-engine.d.ts +8 -0
  76. package/dist/core/run/sandboxed-engine.js +60 -7
  77. package/dist/core/run/sandboxed-engine.js.map +1 -1
  78. package/dist/core/types.d.ts +14 -0
  79. package/dist/core/types.js.map +1 -1
  80. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  81. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +181 -5
  82. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +180 -4
  83. package/dist/core/universe/firm-demo.d.ts +1 -1
  84. package/dist/core/universe/firm-graph.d.ts +2 -2
  85. package/dist/core/util/scrub.js +16 -3
  86. package/dist/core/util/scrub.js.map +1 -1
  87. package/dist/core/verse/accounts.d.ts +9 -1
  88. package/dist/core/verse/accounts.js +52 -11
  89. package/dist/core/verse/accounts.js.map +1 -1
  90. package/dist/core/verse/apps-api.d.ts +12 -0
  91. package/dist/core/verse/apps-api.js +22 -4
  92. package/dist/core/verse/apps-api.js.map +1 -1
  93. package/dist/core/verse/apps.js +22 -12
  94. package/dist/core/verse/apps.js.map +1 -1
  95. package/dist/core/verse/local-models.js +3 -7
  96. package/dist/core/verse/local-models.js.map +1 -1
  97. package/dist/core/verse/model-display-name.d.ts +34 -0
  98. package/dist/core/verse/model-display-name.js +125 -0
  99. package/dist/core/verse/model-display-name.js.map +1 -0
  100. package/dist/core/verse/seats.d.ts +11 -2
  101. package/dist/core/verse/seats.js +42 -18
  102. package/dist/core/verse/seats.js.map +1 -1
  103. package/dist/core/verse/transient-events.d.ts +21 -0
  104. package/dist/core/verse/transient-events.js +12 -0
  105. package/dist/core/verse/transient-events.js.map +1 -0
  106. package/dist/core/verse/types.d.ts +6 -13
  107. package/dist/core/verse/types.js +5 -36
  108. package/dist/core/verse/types.js.map +1 -1
  109. package/dist/core/web/public/next/assets/ActionMenu-DffwPklw.js +2 -0
  110. package/dist/core/web/public/next/assets/{ActionMenu-FxXeRbPx.js.map → ActionMenu-DffwPklw.js.map} +1 -1
  111. package/dist/core/web/public/next/assets/Advanced-CxzE6ekM.js +2 -0
  112. package/dist/core/web/public/next/assets/Advanced-CxzE6ekM.js.map +1 -0
  113. package/dist/core/web/public/next/assets/App-DFlp-vdv.js +7 -0
  114. package/dist/core/web/public/next/assets/App-DFlp-vdv.js.map +1 -0
  115. package/dist/core/web/public/next/assets/AppsSection-Ct2O605-.js +2 -0
  116. package/dist/core/web/public/next/assets/AppsSection-Ct2O605-.js.map +1 -0
  117. package/dist/core/web/public/next/assets/AreaTrend-o-GLl2Is.js +2 -0
  118. package/dist/core/web/public/next/assets/{AreaTrend-CDxrQU4b.js.map → AreaTrend-o-GLl2Is.js.map} +1 -1
  119. package/dist/core/web/public/next/assets/AutonomyOffState-CXhXpsim.js +2 -0
  120. package/dist/core/web/public/next/assets/AutonomyOffState-CXhXpsim.js.map +1 -0
  121. package/dist/core/web/public/next/assets/AutonomyOffState-DmbE71vI.css +1 -0
  122. package/dist/core/web/public/next/assets/AutonomySection-BTg7yVa1.js +2 -0
  123. package/dist/core/web/public/next/assets/{AutonomySection-DuQkHGD_.js.map → AutonomySection-BTg7yVa1.js.map} +1 -1
  124. package/dist/core/web/public/next/assets/BranchBar-BjPFtGsj.js +2 -0
  125. package/dist/core/web/public/next/assets/{BranchBar-CLsvOcyK.js.map → BranchBar-BjPFtGsj.js.map} +1 -1
  126. package/dist/core/web/public/next/assets/BudgetControl-ng6swoZp.js +2 -0
  127. package/dist/core/web/public/next/assets/{BudgetControl-TL3FeZ1D.js.map → BudgetControl-ng6swoZp.js.map} +1 -1
  128. package/dist/core/web/public/next/assets/{Button-HHXgoXGr.js → Button-OjVJSrFq.js} +2 -2
  129. package/dist/core/web/public/next/assets/{Button-HHXgoXGr.js.map → Button-OjVJSrFq.js.map} +1 -1
  130. package/dist/core/web/public/next/assets/CapacityStrip-Bx4c3dxD.js +2 -0
  131. package/dist/core/web/public/next/assets/{CapacityStrip-qReu0RaF.js.map → CapacityStrip-Bx4c3dxD.js.map} +1 -1
  132. package/dist/core/web/public/next/assets/{useChartWidth-BW6SwYIK.css → ChartFrame-BVuBe6RP.css} +1 -1
  133. package/dist/core/web/public/next/assets/ChartFrame-B_w2Siqg.js +2 -0
  134. package/dist/core/web/public/next/assets/ChartFrame-B_w2Siqg.js.map +1 -0
  135. package/dist/core/web/public/next/assets/ChatSection-Bisj9R6E.js +3 -0
  136. package/dist/core/web/public/next/assets/ChatSection-Bisj9R6E.js.map +1 -0
  137. package/dist/core/web/public/next/assets/CloudBudgetForm-5Gf4iAok.js +2 -0
  138. package/dist/core/web/public/next/assets/CloudBudgetForm-5Gf4iAok.js.map +1 -0
  139. package/dist/core/web/public/next/assets/CommandPalette-iTpk6I8r.js +2 -0
  140. package/dist/core/web/public/next/assets/CommandPalette-iTpk6I8r.js.map +1 -0
  141. package/dist/core/web/public/next/assets/CommandSection-CvAVd1w4.css +1 -0
  142. package/dist/core/web/public/next/assets/CommandSection-D8ZihzvE.js +2 -0
  143. package/dist/core/web/public/next/assets/CommandSection-D8ZihzvE.js.map +1 -0
  144. package/dist/core/web/public/next/assets/ConfirmDialog-_S9iE-cJ.js +2 -0
  145. package/dist/core/web/public/next/assets/{ConfirmDialog-DLrLKM0c.js.map → ConfirmDialog-_S9iE-cJ.js.map} +1 -1
  146. package/dist/core/web/public/next/assets/DeleteChatDialog-KUYUygiA.js +2 -0
  147. package/dist/core/web/public/next/assets/DeleteChatDialog-KUYUygiA.js.map +1 -0
  148. package/dist/core/web/public/next/assets/Dialog-BuIaG6HF.js +2 -0
  149. package/dist/core/web/public/next/assets/{Dialog-BS9Dxi3G.js.map → Dialog-BuIaG6HF.js.map} +1 -1
  150. package/dist/core/web/public/next/assets/DiffPane-De4IQt82.js +2 -0
  151. package/dist/core/web/public/next/assets/{DiffPane-TOmUf3b2.js.map → DiffPane-De4IQt82.js.map} +1 -1
  152. package/dist/core/web/public/next/assets/DiffViewer-B75t6Ile.js +2 -0
  153. package/dist/core/web/public/next/assets/{DiffViewer-BpH56302.js.map → DiffViewer-B75t6Ile.js.map} +1 -1
  154. package/dist/core/web/public/next/assets/DockHost-BPqyhtCP.js +2 -0
  155. package/dist/core/web/public/next/assets/DockHost-BPqyhtCP.js.map +1 -0
  156. package/dist/core/web/public/next/assets/{EmptyState-BqR5YAmf.js → EmptyState-DXkT4vhW.js} +2 -2
  157. package/dist/core/web/public/next/assets/{EmptyState-BqR5YAmf.js.map → EmptyState-DXkT4vhW.js.map} +1 -1
  158. package/dist/core/web/public/next/assets/{Epistemic-dhvDTwAZ.js → Epistemic-CfqFe_aP.js} +2 -2
  159. package/dist/core/web/public/next/assets/{Epistemic-dhvDTwAZ.js.map → Epistemic-CfqFe_aP.js.map} +1 -1
  160. package/dist/core/web/public/next/assets/FleetSection-B7f-XBiX.js +3 -0
  161. package/dist/core/web/public/next/assets/FleetSection-B7f-XBiX.js.map +1 -0
  162. package/dist/core/web/public/next/assets/Funnel-pXYySwXQ.js +2 -0
  163. package/dist/core/web/public/next/assets/{Funnel-D2u7Wlzc.js.map → Funnel-pXYySwXQ.js.map} +1 -1
  164. package/dist/core/web/public/next/assets/GearTray-gj6lT7ba.js +2 -0
  165. package/dist/core/web/public/next/assets/{GearTray-Cb2npoxM.js.map → GearTray-gj6lT7ba.js.map} +1 -1
  166. package/dist/core/web/public/next/assets/GrowthSection-D8e9m9Wy.js +2 -0
  167. package/dist/core/web/public/next/assets/{GrowthSection-C5aDPs8B.js.map → GrowthSection-D8e9m9Wy.js.map} +1 -1
  168. package/dist/core/web/public/next/assets/HandoffDialog-VfKxKhxF.js +2 -0
  169. package/dist/core/web/public/next/assets/{HandoffDialog-BF3AYg-X.js.map → HandoffDialog-VfKxKhxF.js.map} +1 -1
  170. package/dist/core/web/public/next/assets/Input-u1YR_Ogl.js +2 -0
  171. package/dist/core/web/public/next/assets/{Input-B5dmGLZx.js.map → Input-u1YR_Ogl.js.map} +1 -1
  172. package/dist/core/web/public/next/assets/LeaderCard-BDELBrnx.js +2 -0
  173. package/dist/core/web/public/next/assets/LeaderCard-BDELBrnx.js.map +1 -0
  174. package/dist/core/web/public/next/assets/MatrixHeatmap-NKLF2srR.js +2 -0
  175. package/dist/core/web/public/next/assets/{MatrixHeatmap-BgEUs2Eu.js.map → MatrixHeatmap-NKLF2srR.js.map} +1 -1
  176. package/dist/core/web/public/next/assets/McpSection-D1tDvlRa.js +2 -0
  177. package/dist/core/web/public/next/assets/{McpSection-DZkiOuoq.js.map → McpSection-D1tDvlRa.js.map} +1 -1
  178. package/dist/core/web/public/next/assets/MessageMarkdown-DlfLEJxu.js +3 -0
  179. package/dist/core/web/public/next/assets/{MessageMarkdown-BauY8ZTY.js.map → MessageMarkdown-DlfLEJxu.js.map} +1 -1
  180. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DLBNLfsn.js → MessageMarkdownRenderer-BYZzI115.js} +4 -4
  181. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DLBNLfsn.js.map → MessageMarkdownRenderer-BYZzI115.js.map} +1 -1
  182. package/dist/core/web/public/next/assets/{Meter-DNeocJnD.js → Meter-BSVhU43Q.js} +2 -2
  183. package/dist/core/web/public/next/assets/{Meter-DNeocJnD.js.map → Meter-BSVhU43Q.js.map} +1 -1
  184. package/dist/core/web/public/next/assets/MindSection-lqV2e_Sq.js +2 -0
  185. package/dist/core/web/public/next/assets/MindSection-lqV2e_Sq.js.map +1 -0
  186. package/dist/core/web/public/next/assets/MutationTokenDialog-CLGP72g6.js +2 -0
  187. package/dist/core/web/public/next/assets/{MutationTokenDialog-mxrw0Yvy.js.map → MutationTokenDialog-CLGP72g6.js.map} +1 -1
  188. package/dist/core/web/public/next/assets/NeedsYouDrawer-D992XTOn.js +2 -0
  189. package/dist/core/web/public/next/assets/{NeedsYouDrawer-CO-Balfp.js.map → NeedsYouDrawer-D992XTOn.js.map} +1 -1
  190. package/dist/core/web/public/next/assets/NeedsYouList-eBOlJr0b.js +2 -0
  191. package/dist/core/web/public/next/assets/{NeedsYouList-CzDZbzRD.js.map → NeedsYouList-eBOlJr0b.js.map} +1 -1
  192. package/dist/core/web/public/next/assets/NewChatDialog-DoYTH26M.js +3 -0
  193. package/dist/core/web/public/next/assets/NewChatDialog-DoYTH26M.js.map +1 -0
  194. package/dist/core/web/public/next/assets/OnboardingFlow-D3yfITK1.js +2 -0
  195. package/dist/core/web/public/next/assets/OnboardingFlow-D3yfITK1.js.map +1 -0
  196. package/dist/core/web/public/next/assets/OnboardingFlow-HUNQGQuU.css +1 -0
  197. package/dist/core/web/public/next/assets/OnboardingGate-DrDEVBqw.js +3 -0
  198. package/dist/core/web/public/next/assets/OnboardingGate-DrDEVBqw.js.map +1 -0
  199. package/dist/core/web/public/next/assets/PreviewPane-ULN673zm.js +2 -0
  200. package/dist/core/web/public/next/assets/{PreviewPane-BS-CleCd.js.map → PreviewPane-ULN673zm.js.map} +1 -1
  201. package/dist/core/web/public/next/assets/{ProviderLogo-C5mBg8i0.js → ProviderLogo-8WaUjTlN.js} +2 -2
  202. package/dist/core/web/public/next/assets/{ProviderLogo-C5mBg8i0.js.map → ProviderLogo-8WaUjTlN.js.map} +1 -1
  203. package/dist/core/web/public/next/assets/RailStatus-Ckw9nvYU.js +2 -0
  204. package/dist/core/web/public/next/assets/{RailStatus-cPRyOmrk.js.map → RailStatus-Ckw9nvYU.js.map} +1 -1
  205. package/dist/core/web/public/next/assets/{RefreshIndicator-CMwSCiz5.js → RefreshIndicator-DGubF4vR.js} +2 -2
  206. package/dist/core/web/public/next/assets/{RefreshIndicator-CMwSCiz5.js.map → RefreshIndicator-DGubF4vR.js.map} +1 -1
  207. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BFi1opsC.js → ResourcePoolConsoleApp-BahEaY4d.js} +4 -4
  208. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BFi1opsC.js.map → ResourcePoolConsoleApp-BahEaY4d.js.map} +1 -1
  209. package/dist/core/web/public/next/assets/ResourcesChrome-C2ZrA7la.js +3 -0
  210. package/dist/core/web/public/next/assets/ResourcesChrome-C2ZrA7la.js.map +1 -0
  211. package/dist/core/web/public/next/assets/ResourcesDrawer-BC93fzwQ.js +2 -0
  212. package/dist/core/web/public/next/assets/ResourcesDrawer-BC93fzwQ.js.map +1 -0
  213. package/dist/core/web/public/next/assets/ResourcesDrawer-BCFJ2Uq8.css +1 -0
  214. package/dist/core/web/public/next/assets/RouteErrorBoundary-BvhHNpE_.js +2 -0
  215. package/dist/core/web/public/next/assets/{RouteErrorBoundary-DYtwc2ja.js.map → RouteErrorBoundary-BvhHNpE_.js.map} +1 -1
  216. package/dist/core/web/public/next/assets/RunInCloudAction-Dywfnw0C.js +2 -0
  217. package/dist/core/web/public/next/assets/{RunInCloudAction-BEGEEM8O.js.map → RunInCloudAction-Dywfnw0C.js.map} +1 -1
  218. package/dist/core/web/public/next/assets/SeatSelector-CH7hTgwy.js +3 -0
  219. package/dist/core/web/public/next/assets/{SeatSelector-CYHRR7Wp.js.map → SeatSelector-CH7hTgwy.js.map} +1 -1
  220. package/dist/core/web/public/next/assets/Segmented-Wynbg0F5.js +2 -0
  221. package/dist/core/web/public/next/assets/{Segmented-BE53BenI.js.map → Segmented-Wynbg0F5.js.map} +1 -1
  222. package/dist/core/web/public/next/assets/Select-DMyLCkId.js +2 -0
  223. package/dist/core/web/public/next/assets/{Select-BwSGkHCS.js.map → Select-DMyLCkId.js.map} +1 -1
  224. package/dist/core/web/public/next/assets/SessionGate-8RtQFWeZ.js +2 -0
  225. package/dist/core/web/public/next/assets/{SessionGate-e0dFJm9d.js.map → SessionGate-8RtQFWeZ.js.map} +1 -1
  226. package/dist/core/web/public/next/assets/SessionInsightChip-zBJbKv2L.js +2 -0
  227. package/dist/core/web/public/next/assets/{SessionInsightChip-CxlSyueJ.js.map → SessionInsightChip-zBJbKv2L.js.map} +1 -1
  228. package/dist/core/web/public/next/assets/SessionSearch-DfJIcxxM.js +2 -0
  229. package/dist/core/web/public/next/assets/{SessionSearch-_yp7S2TP.js.map → SessionSearch-DfJIcxxM.js.map} +1 -1
  230. package/dist/core/web/public/next/assets/SettingsSection-Da9RYyH5.js +2 -0
  231. package/dist/core/web/public/next/assets/SettingsSection-Da9RYyH5.js.map +1 -0
  232. package/dist/core/web/public/next/assets/Sheet-DhXyIDmq.js +2 -0
  233. package/dist/core/web/public/next/assets/{Sheet-DVP2Mgy6.js.map → Sheet-DhXyIDmq.js.map} +1 -1
  234. package/dist/core/web/public/next/assets/ShortcutsOverlay-Bi5f-wUw.js +2 -0
  235. package/dist/core/web/public/next/assets/{ShortcutsOverlay-BcIcoClX.js.map → ShortcutsOverlay-Bi5f-wUw.js.map} +1 -1
  236. package/dist/core/web/public/next/assets/Sidebar-BuO3sViG.js +3 -0
  237. package/dist/core/web/public/next/assets/Sidebar-BuO3sViG.js.map +1 -0
  238. package/dist/core/web/public/next/assets/Slider-Bd9YFWEV.js +2 -0
  239. package/dist/core/web/public/next/assets/{Slider-BypUWjpH.js.map → Slider-Bd9YFWEV.js.map} +1 -1
  240. package/dist/core/web/public/next/assets/StatTile-DScTd3c-.js +2 -0
  241. package/dist/core/web/public/next/assets/StatTile-DScTd3c-.js.map +1 -0
  242. package/dist/core/web/public/next/assets/{StatusBadge-HCZp416_.js → StatusBadge-DQ1UtQAJ.js} +2 -2
  243. package/dist/core/web/public/next/assets/{StatusBadge-HCZp416_.js.map → StatusBadge-DQ1UtQAJ.js.map} +1 -1
  244. package/dist/core/web/public/next/assets/StepBand-BB3IJxhr.js +2 -0
  245. package/dist/core/web/public/next/assets/{StepBand-Duz1PUes.js.map → StepBand-BB3IJxhr.js.map} +1 -1
  246. package/dist/core/web/public/next/assets/Surface-DWMtQe3Q.js +2 -0
  247. package/dist/core/web/public/next/assets/Surface-DWMtQe3Q.js.map +1 -0
  248. package/dist/core/web/public/next/assets/Surface-e1z6GdZt.css +1 -0
  249. package/dist/core/web/public/next/assets/Swimlane-DcENqtdp.js +2 -0
  250. package/dist/core/web/public/next/assets/{Swimlane-DGcIcnNA.js.map → Swimlane-DcENqtdp.js.map} +1 -1
  251. package/dist/core/web/public/next/assets/{Switch-iveI-8NY.js → Switch-CenChEYf.js} +2 -2
  252. package/dist/core/web/public/next/assets/{Switch-iveI-8NY.js.map → Switch-CenChEYf.js.map} +1 -1
  253. package/dist/core/web/public/next/assets/{Tag-DH15CUJp.js → Tag-D4k5E3u0.js} +2 -2
  254. package/dist/core/web/public/next/assets/{Tag-DH15CUJp.js.map → Tag-D4k5E3u0.js.map} +1 -1
  255. package/dist/core/web/public/next/assets/TerminalPane-DCQTukcE.js +7 -0
  256. package/dist/core/web/public/next/assets/TerminalPane-DCQTukcE.js.map +1 -0
  257. package/dist/core/web/public/next/assets/UniverseConsoleApp-DsuWT0pi.js +2 -0
  258. package/dist/core/web/public/next/assets/{UniverseConsoleApp-C8yQ0AaR.js.map → UniverseConsoleApp-DsuWT0pi.js.map} +1 -1
  259. package/dist/core/web/public/next/assets/{UniverseView-_nPURPqe.js → UniverseView-DyI5ApsA.js} +3 -3
  260. package/dist/core/web/public/next/assets/{UniverseView-_nPURPqe.js.map → UniverseView-DyI5ApsA.js.map} +1 -1
  261. package/dist/core/web/public/next/assets/UsageSection-Imr28nwK.js +3 -0
  262. package/dist/core/web/public/next/assets/UsageSection-Imr28nwK.js.map +1 -0
  263. package/dist/core/web/public/next/assets/VerseConsoleApp-DeuBEvAO.js +3 -0
  264. package/dist/core/web/public/next/assets/VerseConsoleApp-DeuBEvAO.js.map +1 -0
  265. package/dist/core/web/public/next/assets/Workspace-CpxBO98E.js +10 -0
  266. package/dist/core/web/public/next/assets/Workspace-CpxBO98E.js.map +1 -0
  267. package/dist/core/web/public/next/assets/actions-BmIf9avl.css +1 -0
  268. package/dist/core/web/public/next/assets/actions-DE8NoWV_.js +2 -0
  269. package/dist/core/web/public/next/assets/actions-DE8NoWV_.js.map +1 -0
  270. package/dist/core/web/public/next/assets/apps-model-CFGCUu9p.js +2 -0
  271. package/dist/core/web/public/next/assets/apps-model-CFGCUu9p.js.map +1 -0
  272. package/dist/core/web/public/next/assets/authority-model-CmnfJzmM.js +2 -0
  273. package/dist/core/web/public/next/assets/authority-model-CmnfJzmM.js.map +1 -0
  274. package/dist/core/web/public/next/assets/binding-left-DZpq5sLf.js +2 -0
  275. package/dist/core/web/public/next/assets/binding-left-DZpq5sLf.js.map +1 -0
  276. package/dist/core/web/public/next/assets/{budget-queries-V3wcJVK_.js → budget-queries-DjrPRFwl.js} +2 -2
  277. package/dist/core/web/public/next/assets/{budget-queries-V3wcJVK_.js.map → budget-queries-DjrPRFwl.js.map} +1 -1
  278. package/dist/core/web/public/next/assets/capacity-strip-model-C7cdQnuK.js +2 -0
  279. package/dist/core/web/public/next/assets/capacity-strip-model-C7cdQnuK.js.map +1 -0
  280. package/dist/core/web/public/next/assets/charts-DUD_jprE.js +2 -0
  281. package/dist/core/web/public/next/assets/charts-DUD_jprE.js.map +1 -0
  282. package/dist/core/web/public/next/assets/charts-DhKTrQLw.css +1 -0
  283. package/dist/core/web/public/next/assets/clipboard-DkZdqlxf.js +2 -0
  284. package/dist/core/web/public/next/assets/clipboard-DkZdqlxf.js.map +1 -0
  285. package/dist/core/web/public/next/assets/cloud-D-HXms6q.css +1 -0
  286. package/dist/core/web/public/next/assets/cloud-model-DoxBMDvx.js +2 -0
  287. package/dist/core/web/public/next/assets/cloud-model-DoxBMDvx.js.map +1 -0
  288. package/dist/core/web/public/next/assets/cloud-queries-CTGvO-k-.js +2 -0
  289. package/dist/core/web/public/next/assets/cloud-queries-CTGvO-k-.js.map +1 -0
  290. package/dist/core/web/public/next/assets/cloud.module-DOP-N4Ch.js +2 -0
  291. package/dist/core/web/public/next/assets/cloud.module-DOP-N4Ch.js.map +1 -0
  292. package/dist/core/web/public/next/assets/command-catalog-CmrHNFzh.js +2 -0
  293. package/dist/core/web/public/next/assets/command-catalog-CmrHNFzh.js.map +1 -0
  294. package/dist/core/web/public/next/assets/context-model-2Owp2jYF.js +3 -0
  295. package/dist/core/web/public/next/assets/context-model-2Owp2jYF.js.map +1 -0
  296. package/dist/core/web/public/next/assets/context-model-9xywbXxX.js +2 -0
  297. package/dist/core/web/public/next/assets/context-model-9xywbXxX.js.map +1 -0
  298. package/dist/core/web/public/next/assets/{context-queries-CrPcHWTz.js → context-queries-wvcRnV-I.js} +2 -2
  299. package/dist/core/web/public/next/assets/{context-queries-CrPcHWTz.js.map → context-queries-wvcRnV-I.js.map} +1 -1
  300. package/dist/core/web/public/next/assets/{control-queries-CmgBgKV7.js → control-queries-DB6Kh7UN.js} +2 -2
  301. package/dist/core/web/public/next/assets/{control-queries-CmgBgKV7.js.map → control-queries-DB6Kh7UN.js.map} +1 -1
  302. package/dist/core/web/public/next/assets/copy-setup-CJQhFl-4.js +2 -0
  303. package/dist/core/web/public/next/assets/copy-setup-CJQhFl-4.js.map +1 -0
  304. package/dist/core/web/public/next/assets/dock-icons-BBUGl4tE.js +2 -0
  305. package/dist/core/web/public/next/assets/{dock-icons-CUh8dOxJ.js.map → dock-icons-BBUGl4tE.js.map} +1 -1
  306. package/dist/core/web/public/next/assets/fleet-model-BRfRWkZl.js +2 -0
  307. package/dist/core/web/public/next/assets/fleet-model-BRfRWkZl.js.map +1 -0
  308. package/dist/core/web/public/next/assets/fleet-queries-tU6UEXN2.js +2 -0
  309. package/dist/core/web/public/next/assets/fleet-queries-tU6UEXN2.js.map +1 -0
  310. package/dist/core/web/public/next/assets/{focus-trap-HtcoXeEq.js → focus-trap-BGOceFvj.js} +2 -2
  311. package/dist/core/web/public/next/assets/{focus-trap-HtcoXeEq.js.map → focus-trap-BGOceFvj.js.map} +1 -1
  312. package/dist/core/web/public/next/assets/format-DEAaVF6X.js +2 -0
  313. package/dist/core/web/public/next/assets/format-DEAaVF6X.js.map +1 -0
  314. package/dist/core/web/public/next/assets/{git-model-bk_RXh3y.js → git-model-53iRLInY.js} +2 -2
  315. package/dist/core/web/public/next/assets/{git-model-bk_RXh3y.js.map → git-model-53iRLInY.js.map} +1 -1
  316. package/dist/core/web/public/next/assets/global-CXHHMz9o.js +2 -0
  317. package/dist/core/web/public/next/assets/{global-oEl9Qj8u.js.map → global-CXHHMz9o.js.map} +1 -1
  318. package/dist/core/web/public/next/assets/{guarded-runners-LHLXmXIk.js → guarded-runners-B8zFVBVZ.js} +2 -2
  319. package/dist/core/web/public/next/assets/{guarded-runners-LHLXmXIk.js.map → guarded-runners-B8zFVBVZ.js.map} +1 -1
  320. package/dist/core/web/public/next/assets/icons-D4s-jlxX.js +2 -0
  321. package/dist/core/web/public/next/assets/icons-D4s-jlxX.js.map +1 -0
  322. package/dist/core/web/public/next/assets/{index-BVxOlseC.js → index-BG177cPN.js} +3 -3
  323. package/dist/core/web/public/next/assets/{index-BVxOlseC.js.map → index-BG177cPN.js.map} +1 -1
  324. package/dist/core/web/public/next/assets/live-model-CEf01wrW.js +2 -0
  325. package/dist/core/web/public/next/assets/live-model-CEf01wrW.js.map +1 -0
  326. package/dist/core/web/public/next/assets/local-model-C9xNi9fN.js +2 -0
  327. package/dist/core/web/public/next/assets/local-model-C9xNi9fN.js.map +1 -0
  328. package/dist/core/web/public/next/assets/mind.module-D_xk6HIQ.js +2 -0
  329. package/dist/core/web/public/next/assets/{mind.module-gTGAuJt1.js.map → mind.module-D_xk6HIQ.js.map} +1 -1
  330. package/dist/core/web/public/next/assets/mutations-CbTSofVH.js +2 -0
  331. package/dist/core/web/public/next/assets/{mutations-C5S7okTW.js.map → mutations-CbTSofVH.js.map} +1 -1
  332. package/dist/core/web/public/next/assets/nav-CmZcbPFJ.js +2 -0
  333. package/dist/core/web/public/next/assets/{nav-NVtnuG7A.js.map → nav-CmZcbPFJ.js.map} +1 -1
  334. package/dist/core/web/public/next/assets/{overnight-queries-DGm3qL1b.js → overnight-queries-CR0b_j-V.js} +2 -2
  335. package/dist/core/web/public/next/assets/{overnight-queries-DGm3qL1b.js.map → overnight-queries-CR0b_j-V.js.map} +1 -1
  336. package/dist/core/web/public/next/assets/primitives-DNI-uJBd.js +1 -0
  337. package/dist/core/web/public/next/assets/{queries-CYisSAAB.js → queries-rvRsAGW2.js} +2 -2
  338. package/dist/core/web/public/next/assets/{queries-CYisSAAB.js.map → queries-rvRsAGW2.js.map} +1 -1
  339. package/dist/core/web/public/next/assets/reasoning-pref-DMPQkujg.js +2 -0
  340. package/dist/core/web/public/next/assets/{reasoning-pref-BUcFF_5G.js.map → reasoning-pref-DMPQkujg.js.map} +1 -1
  341. package/dist/core/web/public/next/assets/{reveal-anchor-D-tV8xau.js → reveal-anchor-BKk2wZZZ.js} +2 -2
  342. package/dist/core/web/public/next/assets/{reveal-anchor-D-tV8xau.js.map → reveal-anchor-BKk2wZZZ.js.map} +1 -1
  343. package/dist/core/web/public/next/assets/seat-subscription-CLn2ReSZ.js +2 -0
  344. package/dist/core/web/public/next/assets/{seat-subscription-B-f9ViU2.js.map → seat-subscription-CLn2ReSZ.js.map} +1 -1
  345. package/dist/core/web/public/next/assets/{sidebar-model-DQ-FyBHW.js → sidebar-model-CjQLvdag.js} +2 -2
  346. package/dist/core/web/public/next/assets/{sidebar-model-DQ-FyBHW.js.map → sidebar-model-CjQLvdag.js.map} +1 -1
  347. package/dist/core/web/public/next/assets/slots-DRxNosZg.js +3 -0
  348. package/dist/core/web/public/next/assets/{slots-BhP8b8tf.js.map → slots-DRxNosZg.js.map} +1 -1
  349. package/dist/core/web/public/next/assets/surface-data-DshUC_TU.js +2 -0
  350. package/dist/core/web/public/next/assets/surface-data-DshUC_TU.js.map +1 -0
  351. package/dist/core/web/public/next/assets/surface-prefetch-DO8r4ttQ.js +2 -0
  352. package/dist/core/web/public/next/assets/surface-prefetch-DO8r4ttQ.js.map +1 -0
  353. package/dist/core/web/public/next/assets/{tasks-model-BKDNqmmt.js → tasks-model-BKPFZAmZ.js} +2 -2
  354. package/dist/core/web/public/next/assets/{tasks-model-BKDNqmmt.js.map → tasks-model-BKPFZAmZ.js.map} +1 -1
  355. package/dist/core/web/public/next/assets/{terminal-icons-D2xe-aM1.js → terminal-icons-DoeQkP3y.js} +2 -2
  356. package/dist/core/web/public/next/assets/{terminal-icons-D2xe-aM1.js.map → terminal-icons-DoeQkP3y.js.map} +1 -1
  357. package/dist/core/web/public/next/assets/transient-events-nTLhMBND.js +2 -0
  358. package/dist/core/web/public/next/assets/transient-events-nTLhMBND.js.map +1 -0
  359. package/dist/core/web/public/next/assets/types-D5XOdyw8.js.map +1 -1
  360. package/dist/core/web/public/next/assets/{usage-queries-BZuAANlI.js → usage-queries-MnUeYzwp.js} +2 -2
  361. package/dist/core/web/public/next/assets/{usage-queries-BZuAANlI.js.map → usage-queries-MnUeYzwp.js.map} +1 -1
  362. package/dist/core/web/public/next/assets/{use-guarded-action-Cf4MeNBa.js → use-guarded-action-P12MfNwU.js} +2 -2
  363. package/dist/core/web/public/next/assets/{use-guarded-action-Cf4MeNBa.js.map → use-guarded-action-P12MfNwU.js.map} +1 -1
  364. package/dist/core/web/public/next/assets/{use-ticker-D9yi49my.js → use-ticker-DIvAZ4Ey.js} +2 -2
  365. package/dist/core/web/public/next/assets/{use-ticker-D9yi49my.js.map → use-ticker-DIvAZ4Ey.js.map} +1 -1
  366. package/dist/core/web/public/next/assets/{use-token-gate-DqrSXdX6.js → use-token-gate-DfEja7Wr.js} +2 -2
  367. package/dist/core/web/public/next/assets/{use-token-gate-DqrSXdX6.js.map → use-token-gate-DfEja7Wr.js.map} +1 -1
  368. package/dist/core/web/public/next/assets/useOnboarding-CkOHadqC.js +2 -0
  369. package/dist/core/web/public/next/assets/useOnboarding-CkOHadqC.js.map +1 -0
  370. package/dist/core/web/public/next/assets/useSeatsRefresh-BDlbDlIU.js +2 -0
  371. package/dist/core/web/public/next/assets/useSeatsRefresh-BDlbDlIU.js.map +1 -0
  372. package/dist/core/web/public/next/assets/useVerseTranscript-BGtA55k2.js +2 -0
  373. package/dist/core/web/public/next/assets/{useVerseTranscript-CDYrrWOO.js.map → useVerseTranscript-BGtA55k2.js.map} +1 -1
  374. package/dist/core/web/public/next/assets/verse-icons-wOet9JZy.js +2 -0
  375. package/dist/core/web/public/next/assets/verse-icons-wOet9JZy.js.map +1 -0
  376. package/dist/core/web/public/next/assets/verse-model-CSzzdyu2.js +2 -0
  377. package/dist/core/web/public/next/assets/verse-model-CSzzdyu2.js.map +1 -0
  378. package/dist/core/web/public/next/assets/verse-store-DNVRuiJL.js +2 -0
  379. package/dist/core/web/public/next/assets/verse-store-DNVRuiJL.js.map +1 -0
  380. package/dist/core/web/public/next/assets/warmup-CecO_Nk0.js +3 -0
  381. package/dist/core/web/public/next/assets/{warmup-HG5vYbsN.js.map → warmup-CecO_Nk0.js.map} +1 -1
  382. package/dist/core/web/public/next/assets/why-seat-model-D68ZA7G8.js +2 -0
  383. package/dist/core/web/public/next/assets/why-seat-model-D68ZA7G8.js.map +1 -0
  384. package/dist/core/web/public/next/assets/{workspace-model-CbhdPhei.js → workspace-model-BdIVybKr.js} +2 -2
  385. package/dist/core/web/public/next/assets/{workspace-model-CbhdPhei.js.map → workspace-model-BdIVybKr.js.map} +1 -1
  386. package/dist/core/web/public/next/index.html +1 -1
  387. package/dist/release-dependency-inventory.json +1 -1
  388. package/docs/README.md +2 -1
  389. package/package.json +1 -1
  390. package/dist/core/web/public/next/assets/ActionMenu-FxXeRbPx.js +0 -2
  391. package/dist/core/web/public/next/assets/Advanced-D_eS4472.js +0 -2
  392. package/dist/core/web/public/next/assets/Advanced-D_eS4472.js.map +0 -1
  393. package/dist/core/web/public/next/assets/App-Cj4zKbG3.js +0 -7
  394. package/dist/core/web/public/next/assets/App-Cj4zKbG3.js.map +0 -1
  395. package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js +0 -2
  396. package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js.map +0 -1
  397. package/dist/core/web/public/next/assets/AreaTrend-CDxrQU4b.js +0 -2
  398. package/dist/core/web/public/next/assets/AutonomySection-DuQkHGD_.js +0 -2
  399. package/dist/core/web/public/next/assets/BranchBar-CLsvOcyK.js +0 -2
  400. package/dist/core/web/public/next/assets/BudgetControl-TL3FeZ1D.js +0 -2
  401. package/dist/core/web/public/next/assets/BurnDown-sFXkUGdB.js +0 -2
  402. package/dist/core/web/public/next/assets/BurnDown-sFXkUGdB.js.map +0 -1
  403. package/dist/core/web/public/next/assets/CapacityStrip-qReu0RaF.js +0 -2
  404. package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js +0 -3
  405. package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js.map +0 -1
  406. package/dist/core/web/public/next/assets/CloudBudgetForm-DJbiT1wi.js +0 -2
  407. package/dist/core/web/public/next/assets/CloudBudgetForm-DJbiT1wi.js.map +0 -1
  408. package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js +0 -2
  409. package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js.map +0 -1
  410. package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js +0 -2
  411. package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js.map +0 -1
  412. package/dist/core/web/public/next/assets/ConfirmDialog-DLrLKM0c.js +0 -2
  413. package/dist/core/web/public/next/assets/Dialog-BS9Dxi3G.js +0 -2
  414. package/dist/core/web/public/next/assets/DiffPane-TOmUf3b2.js +0 -2
  415. package/dist/core/web/public/next/assets/DiffViewer-BpH56302.js +0 -2
  416. package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js +0 -2
  417. package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js.map +0 -1
  418. package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js +0 -3
  419. package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js.map +0 -1
  420. package/dist/core/web/public/next/assets/Funnel-D2u7Wlzc.js +0 -2
  421. package/dist/core/web/public/next/assets/GearTray-Cb2npoxM.js +0 -2
  422. package/dist/core/web/public/next/assets/GrowthSection-C5aDPs8B.js +0 -2
  423. package/dist/core/web/public/next/assets/HandoffDialog-BF3AYg-X.js +0 -2
  424. package/dist/core/web/public/next/assets/Input-B5dmGLZx.js +0 -2
  425. package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js +0 -2
  426. package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js.map +0 -1
  427. package/dist/core/web/public/next/assets/MatrixHeatmap-BgEUs2Eu.js +0 -2
  428. package/dist/core/web/public/next/assets/McpSection-DZkiOuoq.js +0 -2
  429. package/dist/core/web/public/next/assets/MessageMarkdown-BauY8ZTY.js +0 -3
  430. package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js +0 -2
  431. package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js.map +0 -1
  432. package/dist/core/web/public/next/assets/MutationTokenDialog-mxrw0Yvy.js +0 -2
  433. package/dist/core/web/public/next/assets/NeedsYouDrawer-CO-Balfp.js +0 -2
  434. package/dist/core/web/public/next/assets/NeedsYouList-CzDZbzRD.js +0 -2
  435. package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js +0 -3
  436. package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js.map +0 -1
  437. package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js +0 -2
  438. package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js.map +0 -1
  439. package/dist/core/web/public/next/assets/OnboardingFlow-DSpPj8UE.css +0 -1
  440. package/dist/core/web/public/next/assets/PreviewPane-BS-CleCd.js +0 -2
  441. package/dist/core/web/public/next/assets/RailStatus-cPRyOmrk.js +0 -2
  442. package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js +0 -3
  443. package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js.map +0 -1
  444. package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js +0 -2
  445. package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js.map +0 -1
  446. package/dist/core/web/public/next/assets/ResourcesDrawer-CSKIO_wZ.css +0 -1
  447. package/dist/core/web/public/next/assets/RouteErrorBoundary-DYtwc2ja.js +0 -2
  448. package/dist/core/web/public/next/assets/RunInCloudAction-BEGEEM8O.js +0 -2
  449. package/dist/core/web/public/next/assets/SeatSelector-CYHRR7Wp.js +0 -3
  450. package/dist/core/web/public/next/assets/Segmented-BE53BenI.js +0 -2
  451. package/dist/core/web/public/next/assets/Select-BwSGkHCS.js +0 -2
  452. package/dist/core/web/public/next/assets/SessionGate-e0dFJm9d.js +0 -2
  453. package/dist/core/web/public/next/assets/SessionInsightChip-CxlSyueJ.js +0 -2
  454. package/dist/core/web/public/next/assets/SessionSearch-_yp7S2TP.js +0 -2
  455. package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js +0 -2
  456. package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js.map +0 -1
  457. package/dist/core/web/public/next/assets/Sheet-DVP2Mgy6.js +0 -2
  458. package/dist/core/web/public/next/assets/ShortcutsOverlay-BcIcoClX.js +0 -2
  459. package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js +0 -3
  460. package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js.map +0 -1
  461. package/dist/core/web/public/next/assets/Slider-BypUWjpH.js +0 -2
  462. package/dist/core/web/public/next/assets/StepBand-Duz1PUes.js +0 -2
  463. package/dist/core/web/public/next/assets/Surface-B75lVfzm.js +0 -2
  464. package/dist/core/web/public/next/assets/Surface-B75lVfzm.js.map +0 -1
  465. package/dist/core/web/public/next/assets/Surface-DMca0gAe.css +0 -1
  466. package/dist/core/web/public/next/assets/Swimlane-DGcIcnNA.js +0 -2
  467. package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js +0 -7
  468. package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js.map +0 -1
  469. package/dist/core/web/public/next/assets/UniverseConsoleApp-C8yQ0AaR.js +0 -2
  470. package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js +0 -2
  471. package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js.map +0 -1
  472. package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js +0 -3
  473. package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js.map +0 -1
  474. package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js +0 -10
  475. package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js.map +0 -1
  476. package/dist/core/web/public/next/assets/actions-Ddfg2iLl.css +0 -1
  477. package/dist/core/web/public/next/assets/actions-tWevL9HV.js +0 -2
  478. package/dist/core/web/public/next/assets/actions-tWevL9HV.js.map +0 -1
  479. package/dist/core/web/public/next/assets/apps-model-CDFvmsQV.js +0 -2
  480. package/dist/core/web/public/next/assets/apps-model-CDFvmsQV.js.map +0 -1
  481. package/dist/core/web/public/next/assets/capacity-strip-model-BDbmQ3e3.js +0 -2
  482. package/dist/core/web/public/next/assets/capacity-strip-model-BDbmQ3e3.js.map +0 -1
  483. package/dist/core/web/public/next/assets/charts-CfAjuKMv.js +0 -2
  484. package/dist/core/web/public/next/assets/charts-CfAjuKMv.js.map +0 -1
  485. package/dist/core/web/public/next/assets/charts-uSX6xVYN.css +0 -1
  486. package/dist/core/web/public/next/assets/cloud-C1KPNRHC.css +0 -1
  487. package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js +0 -2
  488. package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js.map +0 -1
  489. package/dist/core/web/public/next/assets/cloud.module-ascz58c3.js +0 -2
  490. package/dist/core/web/public/next/assets/cloud.module-ascz58c3.js.map +0 -1
  491. package/dist/core/web/public/next/assets/command-bus-xYZoK9nQ.js +0 -2
  492. package/dist/core/web/public/next/assets/command-bus-xYZoK9nQ.js.map +0 -1
  493. package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js +0 -2
  494. package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js.map +0 -1
  495. package/dist/core/web/public/next/assets/composer-keys-su0g1h9o.js +0 -2
  496. package/dist/core/web/public/next/assets/composer-keys-su0g1h9o.js.map +0 -1
  497. package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js +0 -2
  498. package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js.map +0 -1
  499. package/dist/core/web/public/next/assets/context-model-CIBmSugV.js +0 -2
  500. package/dist/core/web/public/next/assets/context-model-CIBmSugV.js.map +0 -1
  501. package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js +0 -2
  502. package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js.map +0 -1
  503. package/dist/core/web/public/next/assets/dock-icons-CUh8dOxJ.js +0 -2
  504. package/dist/core/web/public/next/assets/dock-store-B2vlxqPu.js +0 -2
  505. package/dist/core/web/public/next/assets/dock-store-B2vlxqPu.js.map +0 -1
  506. package/dist/core/web/public/next/assets/fleet-model-7i-FbzKI.js +0 -2
  507. package/dist/core/web/public/next/assets/fleet-model-7i-FbzKI.js.map +0 -1
  508. package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js +0 -2
  509. package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js.map +0 -1
  510. package/dist/core/web/public/next/assets/format-BkWdOQDL.js +0 -2
  511. package/dist/core/web/public/next/assets/format-BkWdOQDL.js.map +0 -1
  512. package/dist/core/web/public/next/assets/global-oEl9Qj8u.js +0 -2
  513. package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js +0 -3
  514. package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js.map +0 -1
  515. package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js +0 -2
  516. package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js.map +0 -1
  517. package/dist/core/web/public/next/assets/icons-DEQa2rTx.js +0 -2
  518. package/dist/core/web/public/next/assets/icons-DEQa2rTx.js.map +0 -1
  519. package/dist/core/web/public/next/assets/live-model-bmsJgnYD.js +0 -2
  520. package/dist/core/web/public/next/assets/live-model-bmsJgnYD.js.map +0 -1
  521. package/dist/core/web/public/next/assets/local-model-DFyjKX0b.js +0 -2
  522. package/dist/core/web/public/next/assets/local-model-DFyjKX0b.js.map +0 -1
  523. package/dist/core/web/public/next/assets/mind.module-gTGAuJt1.js +0 -2
  524. package/dist/core/web/public/next/assets/mutations-C5S7okTW.js +0 -2
  525. package/dist/core/web/public/next/assets/nav-NVtnuG7A.js +0 -2
  526. package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js +0 -2
  527. package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js.map +0 -1
  528. package/dist/core/web/public/next/assets/primitives-DyCKYSBG.js +0 -1
  529. package/dist/core/web/public/next/assets/reasoning-pref-BUcFF_5G.js +0 -2
  530. package/dist/core/web/public/next/assets/seat-subscription-B-f9ViU2.js +0 -2
  531. package/dist/core/web/public/next/assets/section-visibility-Yctxu-if.js +0 -2
  532. package/dist/core/web/public/next/assets/section-visibility-Yctxu-if.js.map +0 -1
  533. package/dist/core/web/public/next/assets/slots-BhP8b8tf.js +0 -3
  534. package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js +0 -2
  535. package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js.map +0 -1
  536. package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js +0 -2
  537. package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js.map +0 -1
  538. package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js +0 -2
  539. package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js.map +0 -1
  540. package/dist/core/web/public/next/assets/useSeatsRefresh-DgFy-Ph4.js +0 -2
  541. package/dist/core/web/public/next/assets/useSeatsRefresh-DgFy-Ph4.js.map +0 -1
  542. package/dist/core/web/public/next/assets/useVerseTranscript-CDYrrWOO.js +0 -2
  543. package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js +0 -2
  544. package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js.map +0 -1
  545. package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js +0 -2
  546. package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js.map +0 -1
  547. package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js +0 -3
  548. package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js.map +0 -1
  549. package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js +0 -3
  550. package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js.map +0 -1
  551. package/dist/core/web/public/next/assets/viewport-nTubFCwf.js +0 -2
  552. package/dist/core/web/public/next/assets/viewport-nTubFCwf.js.map +0 -1
  553. package/dist/core/web/public/next/assets/warmup-HG5vYbsN.js +0 -3
  554. package/dist/core/web/public/next/assets/workbench-types-DzN-5trg.js +0 -2
  555. package/dist/core/web/public/next/assets/workbench-types-DzN-5trg.js.map +0 -1
  556. /package/dist/core/web/public/next/assets/{BurnDown-BuLMsJMx.css → StatTile-BuLMsJMx.css} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"verse-store-b_Lek_c5.js","names":[],"sources":["../../../../../../src/core/verse/types.ts","../../../../../../src/web-ui/routes/verse/verse-store.ts"],"sourcesContent":["/**\n * Ashlr Verse — interactive multi-turn session contract (V1).\n *\n * A Verse session binds one project directory to one execution seat\n * (engine + account + model). Each user turn spawns ONE vendor CLI process\n * that resumes the vendor-native conversation by id, streams normalized\n * events, and exits. No long-lived stdin is held between turns.\n *\n * Seats:\n * claude -> `claude -p ... --output-format stream-json` via a native-profile launcher (account pinned by CLAUDE_CONFIG_DIR)\n * local -> the plain `claude` binary with ANTHROPIC_BASE_URL pointed at Ollama (Anthropic-compatible API); model = Ollama tag\n * codex -> `codex exec --json` / `codex exec resume <threadId> --json` via a native-profile launcher (CODEX_HOME pinned)\n * grok -> `grok -p ... --output-format streaming-messages-json` via a native-profile launcher (GROK_HOME pinned)\n *\n * Nothing in this module reads secrets. Account identity is pinned by the\n * launcher command recorded in ~/.ashlr/account-connections/connections.json.\n */\n\nexport type VerseEngine = 'claude' | 'codex' | 'grok' | 'local';\n\nexport type VerseSessionStatus = 'idle' | 'running' | 'error';\n\n/** One selectable execution seat shown in the model/account picker. */\nexport interface VerseSeat {\n /** Stable id, e.g. `claude`, `codex-personal`, `grok`, `local:qwen3-coder-next:ctx64k`. */\n id: string;\n engine: VerseEngine;\n /** Human label, e.g. \"Claude Code\", \"Personal Codex\", \"Grok\", \"Qwen3-Coder-Next (local)\". */\n label: string;\n /** Account id from connections.json for native seats; `local` for Ollama seats. */\n accountId: string;\n /** Models this seat can run. First entry is the default. */\n models: VerseModelOption[];\n /** Best-known context window in tokens for the default model (null when unknown). */\n contextWindow: number | null;\n /** Latest health/usage evidence, if any. Never an admission input in V1. */\n health: VerseSeatHealth;\n /**\n * V2.1 ADDITIVE. The subscription capacity behind this seat, so the seat\n * list can render a meter without a second request.\n *\n * Absent (`undefined`) for LOCAL seats: an Ollama tag has no subscription\n * and no provider window. Its real constraint is machine memory, which is\n * reported by `VerseBootstrap.localRuntime`, not by a quota. Absent on a\n * native seat means the server could not build a record for that account\n * at all — `health.state` says what it does know.\n */\n capacity?: VerseSeatCapacity;\n /**\n * V3.9 ADDITIVE. The version of the CLI binary this seat is PINNED to (its\n * native-profile launcher execs one exact file). Absent when unknown and on\n * local seats. A pinned binary never updates itself, so a model that needs a\n * newer CLI is listed with `unavailableReason` rather than silently failing.\n */\n cliVersion?: string;\n /**\n * V3.9 ADDITIVE. Plain-language facts about this seat's context setup the UI\n * must show rather than imply (binary skew, catalog not yet fetched, …).\n * Absent when there is nothing to say, so ordinary seats keep their shape.\n */\n notes?: string[];\n}\n\n/**\n * Where a context-window figure came from, so nothing on screen implies more\n * certainty than it has (docs/VERSE-CONTEXT.md §1).\n *\n * - `runtime` — the CLI reported it for THIS turn (claude/grok\n * `result.modelUsage.<m>.contextWindow`, codex rollout\n * `token_count.info.model_context_window`, Ollama\n * `/api/ps`). Wins over everything else.\n * - `provider-catalog` — the seat's own served catalog on disk (codex/grok\n * `native-state/models_cache.json`).\n * - `cli-catalog` — read out of the pinned CLI binary's embedded catalog\n * and recorded here with the version it was read from.\n * - `documented` — the provider's published model table.\n * - `fallback` — a named default; the UI marks it as an estimate.\n */\nexport type VerseWindowSource = 'runtime' | 'provider-catalog' | 'cli-catalog' | 'documented' | 'fallback';\n\n/**\n * How much context a session is allowed to accumulate before the CLI compacts.\n *\n * - `standard` — the default. Evidence-based budget per engine: Claude 1M\n * models compact near 400k instead of ~967k; Codex, Grok and\n * local run their native windows.\n * - `expansive` — opt-in. Claude runs its full native window (CLI `auto`),\n * Codex raises `model_context_window` to the model's catalog\n * maximum (872k on GPT-6 / GPT-5.6). Costs more usage per turn,\n * buys simultaneous visibility for coupled, cross-cutting work.\n *\n * A mode that does not exist for a model (Grok, local, 200k Claude models,\n * GPT-5.5) is simply absent from that model's option — never faked.\n */\nexport type VerseContextMode = 'standard' | 'expansive';\n\nexport const VERSE_CONTEXT_MODES: readonly VerseContextMode[] = ['standard', 'expansive'];\n\n/** One mode's budget for a model on a seat. */\nexport interface VerseContextBudget {\n /** The window the CLI measures against in this mode (the meter's denominator). */\n contextWindow: number;\n /** Token count at which the CLI auto-compacts in this mode; null when unknown. */\n autoCompactAt: number | null;\n /**\n * The RAW window value the CLI must be TOLD to reach this budget, when that\n * differs from `contextWindow` — codex measures against 95% of the window it\n * is configured with, so expansive GPT-6 is `providerWindow: 872_000`\n * (`-c model_context_window=872000`) but `contextWindow: 828_400`.\n */\n providerWindow?: number | null;\n}\n\nexport interface VerseModelOption {\n id: string;\n label: string;\n /** Standard-mode window the CLI measures against (codex: the 95% effective window). */\n contextWindow: number | null;\n /**\n * V3.9 ADDITIVE — every field below is optional so records and fixtures\n * written before it keep validating. Absent means \"not known\", never zero.\n */\n /** Standard-mode auto-compaction point. */\n autoCompactAt?: number | null;\n /** Present ONLY when this model on this seat has a real expansive mode. */\n expansive?: VerseContextBudget | null;\n /** The CLI's default max output for this model, when known. */\n maxOutputTokens?: number | null;\n windowSource?: VerseWindowSource;\n /** Minimum CLI version that knows this model id (e.g. '2.1.280'); null when any. */\n minCliVersion?: string | null;\n /**\n * Why this model cannot run on this seat right now, in plain language (e.g.\n * \"needs Claude Code 2.1.280; this seat runs 2.1.257\"). Null/absent = runnable.\n * Listed rather than hidden so the operator learns WHY a model is missing.\n */\n unavailableReason?: string | null;\n}\n\nexport interface VerseSeatHealth {\n state: 'ready' | 'degraded' | 'unavailable' | 'unknown';\n /** e.g. \"5h window 72% used, resets 14:00\" — plain text, already sanitized. */\n summary: string | null;\n windows: Array<{ id: string; usedPercent: number | null; resetsAt: string | null }>;\n observedAt: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Seat capacity (V2.1, additive) — see docs/VERSE-TELEMETRY-V2.md, which is\n// the authority for every honesty rule encoded in these shapes.\n//\n// This file is imported by the BROWSER bundle, so it may not import\n// `core/verse/accounts.ts` (node:fs). The shapes below therefore restate the\n// provider-record shapes structurally. `seats.ts` assigns the account record's\n// values straight into them, so a divergence in `core/verse/accounts.ts` stops\n// `seats.ts` compiling — that assignment is the drift guard.\n// ---------------------------------------------------------------------------\n\n/** One provider-reported quota window, as carried on a seat. */\nexport interface VerseSeatWindow {\n id: string;\n /** Provider-reported percent, or null for NO SIGNAL — which is not zero. */\n usedPercent: number | null;\n /**\n * Machine-readable reset instant. STRUCTURALLY ALWAYS NULL for Claude: that\n * provider publishes only a sentence, which lives in `resetDescription` and\n * is rendered verbatim. The server never synthesizes an instant from it.\n * (3.10.1: the Command burn-down chart may place the reset on its time axis\n * via `resetInstantFromWords` in web-ui command-model, but only for the\n * collector's exact \"Mon D at H:MMam (Area/Zone)\" form and only when the\n * result lands within one window of now; anything else stays words.)\n */\n resetsAt: string | null;\n /** The provider's own reset wording, verbatim (\"resets Sep 25 at 7pm (America/New_York)\"). */\n resetDescription: string | null;\n /**\n * TRUE only when the provider explicitly FLAGGED the limit. Such a window's\n * `usedPercent` is the SENTINEL 100 — a denial, not a measurement — and must\n * be labelled \"limit reached\", never \"100% used\".\n */\n limitReached: boolean;\n /** False when `usedPercent` is a flagged sentinel rather than a reading. */\n measured: boolean;\n}\n\n/**\n * Codex credits, INDEPENDENT of the window: a fully used weekly window with a\n * spendable balance is not blocked. `null` is \"no signal\", never \"no credits\".\n */\nexport interface VerseSeatCredits {\n hasCredits: boolean;\n unlimited: boolean;\n /** Provider-reported decimal string, kept verbatim — never rounded. */\n balance: string | null;\n}\n\n/**\n * The coarse \"can I use this seat right now\" verdict. Deliberately coarse: the\n * exact numbers are in `windows`, and a verdict that pretended to more\n * precision than the providers give would be a lie.\n *\n * - `ready` — a reading exists and the binding window has headroom.\n * - `tight` — the binding window is at/above the warn threshold, OR it is\n * spent while Codex credits remain spendable (not blocked).\n * - `exhausted` — the binding window is spent and nothing else is left.\n * - `signed-out` — the account is not authenticated; reconnect it.\n * - `unknown` — NO window carried a percent. Not zero, not healthy.\n */\nexport type VerseSeatUsability = 'ready' | 'tight' | 'exhausted' | 'signed-out' | 'unknown';\n\n/**\n * Where a seat's windows came from, so nothing implies freshness it lacks.\n * Mirrors `VerseEvidenceSource` in `core/verse/accounts.ts`.\n */\nexport type VerseSeatEvidenceSource = 'collector' | 'shared-evidence' | 'baseline' | 'none';\n\n/** Everything the seat list needs to draw a capacity meter honestly. */\nexport interface VerseSeatCapacity {\n /** e.g. \"max\", \"pro\", \"SuperGrok\". Null when the provider gave none. */\n planType: string | null;\n /**\n * THE CONSTRAINT THAT ACTUALLY BLOCKS WORK: the window with the highest\n * `usedPercent`. Null when no window carried a percent at all.\n */\n binding: VerseSeatWindow | null;\n /** Every window the provider reported, in provider order. */\n windows: VerseSeatWindow[];\n /** Codex only; null for every other provider and when no signal exists. */\n credits: VerseSeatCredits | null;\n usability: VerseSeatUsability;\n observedAt: string | null;\n evidenceSource: VerseSeatEvidenceSource;\n /** Plain-language provider facts the UI must show instead of implying a fault. */\n notes: string[];\n}\n\nexport interface VerseProject {\n path: string;\n name: string;\n /** From ~/.ashlr/enrollment.json when present. */\n enrolled: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Workspaces (V2.2, ADDITIVE) — see docs/VERSE-WORKSPACES.md §1.\n//\n// A session bound to ONE directory cannot express \"this service plus the\n// shared library it depends on\", which is most real work. A workspace is a\n// NAMED set of roots with exactly one marked primary.\n//\n// `VerseSession.projectPath` keeps its meaning — it IS the primary root — so\n// every session record, adapter and test written before workspaces existed\n// still loads and still runs. Nothing rewrites saved chat history.\n//\n// ENROLLMENT IS NOT PART OF THIS SHAPE ON PURPOSE. A workspace says what a\n// session may REACH; `~/.ashlr/enrollment.json` says what the AUTONOMOUS lane\n// may mutate, and the two are deliberately separate registries. Membership in\n// a workspace grants no enrollment: `enrolled` below is READ from the\n// registry per root and is never written by anything in this feature.\n// ---------------------------------------------------------------------------\n\n/** One directory in a workspace. Exactly one root in a workspace is primary. */\nexport interface VerseWorkspaceRoot {\n /** Absolute, physically resolved path (symlinks followed). */\n path: string;\n /** Display name; defaults to the basename. */\n name: string;\n /**\n * The session's cwd and the value `projectPath` carries. Exactly one root\n * per workspace has this set.\n */\n primary: boolean;\n}\n\n/**\n * A named set of roots.\n *\n * ONE TYPE, TWO USES, DELIBERATELY. An interactive session binds to a\n * workspace to decide what it can reach; the autonomous lane reads the same\n * workspace as a SECTION — \"these repositories collaborate, work on them\n * together\" — when `section` is true. They were not split because the thing\n * being named is identical in both: a set of directories that belong to one\n * piece of work. Two registries of named repo groups would drift.\n *\n * What is NOT on this type is priority. A repo may belong to several\n * workspaces, so a per-membership rank would give one repo several\n * contradictory ranks. Priority is therefore an attribute OF THE REPO, held\n * once per path in the same registry — see `VerseRootPriority`.\n */\nexport interface VerseWorkspace {\n id: string;\n name: string;\n /** Primary first, then the extras in the order they were added. */\n roots: VerseWorkspaceRoot[];\n /**\n * Offer this workspace to the autonomous lane as a section.\n *\n * GRANTS NOTHING. The fleet still reaches only what\n * `~/.ashlr/enrollment.json` permits; a section can group and order those\n * repos and nothing else. A member that is not enrolled stays unreachable.\n */\n section: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Relative standing of one repository in the autonomous lane's attention.\n *\n * ORDERING ONLY, NEVER ADMISSION. `critical` on an unenrolled repo is still\n * refused by `assertMayMutate`; `low` on an enrolled one is still permitted.\n * An ordinal rather than a number because the operator's judgement here is\n * \"this matters more than that\", and a 0-100 score would invent a precision\n * nobody holds.\n *\n * Absent means `normal`, which is how every repo already behaves — so a\n * registry with no priorities at all reproduces today's flat list exactly.\n */\nexport type VerseRootPriority = 'critical' | 'high' | 'normal' | 'low';\n\nexport const VERSE_ROOT_PRIORITIES: readonly VerseRootPriority[] =\n ['critical', 'high', 'normal', 'low'];\n\nexport const VERSE_DEFAULT_ROOT_PRIORITY: VerseRootPriority = 'normal';\n\n/** Sort weight; lower sorts first. Never a budget, never a multiplier. */\nexport const VERSE_ROOT_PRIORITY_RANK: Record<VerseRootPriority, number> = {\n critical: 0,\n high: 1,\n normal: 2,\n low: 3,\n};\n\n/** One enrolled repository as the autonomy scope view ranks it. */\nexport interface VerseAutonomyScopeEntry {\n path: string;\n name: string;\n priority: VerseRootPriority;\n /** Sections (workspaces with `section: true`) this repo belongs to. */\n sections: Array<{ id: string; name: string }>;\n /** True when a focus section is set and this repo is outside it. */\n outsideFocus: boolean;\n}\n\n/**\n * The autonomous lane's scope, ORDERED.\n *\n * Built by INTERSECTING section membership with the enrollment registry, so\n * it can only ever be a subset of what enrollment already allows. Sections\n * add ordering and grouping; they never add reach.\n */\nexport interface VerseAutonomyScopeView {\n focusSectionId: string | null;\n focusSectionName: string | null;\n /** Enrolled repos, most important first. */\n entries: VerseAutonomyScopeEntry[];\n /**\n * Paths a section names that are NOT enrolled. Listed so the blast radius\n * is legible in both directions — these are exactly the repos a section\n * does NOT give the fleet.\n */\n unenrolledSectionRoots: string[];\n}\n\n/**\n * Per-root git identity. A turn that edits two repos produces two diffs, so\n * the transcript has to be able to attribute each file to the right one.\n *\n * `remote` is the canonical `owner/repo` for a GitHub origin and null for\n * anything else — deliberately NOT the raw remote URL, which can carry a\n * token in its userinfo and would then reach an API response.\n */\nexport interface VerseRootGit {\n branch: string;\n /** Count of porcelain entries; 0 is clean. */\n dirty: number;\n ahead: number;\n behind: number;\n remote: string | null;\n}\n\n/** A root as the client sees it: identity, reachability and honest scope. */\nexport interface VerseRootStatus {\n path: string;\n name: string;\n primary: boolean;\n exists: boolean;\n /**\n * Read live from the enrollment registry. FALSE means the interactive lane\n * still works on this root while the autonomous lane refuses it — being in\n * a workspace never changes this.\n */\n enrolled: boolean;\n /** Null when the root is not a git repository, or git is unavailable. */\n git: VerseRootGit | null;\n /**\n * False when the session's engine has no way to be granted this root (Grok's\n * CLI exposes `--cwd` and nothing else). The root is still listed, because\n * hiding it would misrepresent what the workspace is.\n */\n reachable: boolean;\n}\n\n/** GET /api/verse/sessions/:id/roots */\nexport interface VerseSessionRootsResponse {\n sessionId: string;\n workspaceId: string | null;\n workspaceName: string | null;\n roots: VerseRootStatus[];\n /**\n * Plain-language facts the UI must show rather than imply: an unreachable\n * root on a Grok seat, or a root the autonomous lane will refuse.\n */\n notes: string[];\n}\n\n/** GET /api/verse/workspaces */\nexport interface VerseWorkspacesResponse {\n workspaces: VerseWorkspace[];\n /** Live per-root facts, keyed by workspace id, so one read draws the list. */\n status: Record<string, VerseRootStatus[]>;\n /** Per-repo priority, keyed by canonical path. Absent key means `normal`. */\n priorities: Record<string, VerseRootPriority>;\n focusSectionId: string | null;\n}\n\n/** POST /api/verse/workspaces */\nexport interface VerseWorkspaceCreateRequest {\n name: string;\n /** Absolute paths. The first is the primary. */\n roots: string[];\n /** Also offer this set to the autonomous lane as a section. Default false. */\n section?: boolean;\n}\n\n/** POST /api/verse/workspaces/:id/update */\nexport interface VerseWorkspaceUpdateRequest {\n name?: string;\n /** When present, replaces the root set wholesale. First entry is primary. */\n roots?: string[];\n section?: boolean;\n}\n\n/** POST /api/verse/workspaces/priority */\nexport interface VerseRootPriorityRequest {\n path: string;\n priority: VerseRootPriority;\n}\n\n/** POST /api/verse/workspaces/focus — `sectionId: null` clears the focus. */\nexport interface VerseFocusSectionRequest {\n sectionId: string | null;\n}\n\n/** Hard cap on roots per workspace — a blast radius, not a UI limit. */\nexport const VERSE_MAX_WORKSPACE_ROOTS = 8;\n\n/**\n * Every root a session can reach, primary first.\n *\n * Pure and total: a record written before workspaces existed has no\n * `extraRoots` and yields exactly `[projectPath]`, which is what every caller\n * assumed before this existed.\n */\nexport function verseSessionRoots(\n session: Pick<VerseSession, 'projectPath' | 'extraRoots'>,\n): string[] {\n const out = [session.projectPath];\n for (const root of session.extraRoots ?? []) {\n if (typeof root === 'string' && root.length > 0 && !out.includes(root)) out.push(root);\n }\n return out;\n}\n\nexport interface VerseUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n /**\n * Prompt size of the most recent assistant call = live context occupancy.\n * Stored UNCLAMPED: a reading above the window is information (the CLI is\n * about to compact or overflow), and only the UI decides how to draw it.\n */\n contextTokens: number;\n contextWindow: number | null;\n /** V3.9 ADDITIVE. Where `contextWindow` came from; absent on old records. */\n contextWindowSource?: VerseWindowSource;\n /** V3.9 ADDITIVE. Auto-compaction point for the session's current mode. */\n autoCompactAt?: number | null;\n /**\n * V3.9 ADDITIVE. False when `contextTokens` is an UPPER BOUND rather than a\n * measurement (codex before its rollout is readable: the turn total sums\n * every model call). Absent means exact.\n */\n contextTokensExact?: boolean;\n}\n\nexport interface VerseSession {\n id: string;\n title: string;\n /**\n * The PRIMARY root: the process cwd and the folder every pre-workspace\n * client already reads. Unchanged in meaning, which is why no session\n * record ever has to be rewritten.\n */\n projectPath: string;\n /**\n * V2.2 ADDITIVE. Roots BEYOND the primary, pinned at creation time.\n *\n * Absent on every record written before workspaces existed, and absent on\n * a single-root session today — so the on-disk shape of an ordinary chat\n * does not change at all.\n *\n * PINNED, not referenced: a session keeps the roots it was created with\n * even if the named workspace is later edited or deleted, for the same\n * reason `launch.anthropicBaseUrl` is pinned — a conversation must not\n * silently gain reach because config moved underneath it.\n */\n extraRoots?: string[];\n /** V2.2 ADDITIVE. Provenance of the pinned roots; absent for an ad-hoc set. */\n workspaceId?: string;\n workspaceName?: string;\n engine: VerseEngine;\n accountId: string;\n seatId: string;\n model: string;\n /** Vendor conversation id used for resume (claude/grok: our uuid; codex: thread id from output). */\n nativeSessionId: string | null;\n createdAt: string;\n updatedAt: string;\n status: VerseSessionStatus;\n turnCount: number;\n usage: VerseUsage;\n lastError: string | null;\n /**\n * V3.9 ADDITIVE. The session's context budget. Every record written by 3.9+\n * carries it explicitly, so ABSENT means a pre-3.9 record: the engine\n * materializes it on first touch — `expansive` for a Claude 1M model (that\n * is the native ~967k compaction those chats always ran with, so upgrading\n * never forces a paid compaction), `standard` otherwise (= native).\n * Changeable mid-session (POST /sessions/:id/context-mode): it changes CLI\n * flags, not prompt content — but switching DOWN below the current\n * occupancy makes the CLI compact on its next turn.\n */\n contextMode?: VerseContextMode;\n /** V3.9 ADDITIVE. Native compactions observed so far (claude/grok compact_boundary, codex rollout `compacted`). */\n compactionCount?: number;\n /** V3.9 ADDITIVE. Set when this session was started as a handoff from another. */\n handoffFrom?: { sessionId: string; title: string };\n /**\n * V3.9 ADDITIVE. Shared project memory was offered to this session's CLI at\n * creation. Pinned, like the roots: a conversation must not silently gain a\n * writable directory because a preference moved underneath it.\n */\n memoryEnabled?: boolean;\n /**\n * V3.10 ADDITIVE (contract: unit C0; written by C3's engine). The operator's\n * per-chat effort and permission choices, applied from the NEXT turn — a\n * running turn keeps the argv it was launched with. Absent (every record\n * before 3.10, and any key never set) = the engine default: permission mode\n * `accept-edits`, effort = the CLI's own default.\n *\n * A MODEL change is deliberately NOT stored here: it rewrites `model` above,\n * which every launch already reads — one field, one truth.\n */\n controls?: VerseSessionControls;\n}\n\n/**\n * V3.10. Permission modes a chat can run in (SPEC-310C §0.6), in picker order.\n * - `plan` — the CLI plans and edits nothing.\n * - `accept-edits` — the default: edits applied without asking.\n * - `auto` — the CLI's own auto mode.\n * - `bypass` — every check skipped. Shown red and confirmed PER CHAT:\n * never a default, never inherited by a new chat.\n * Adapters map these to each CLI's spelling (claude `acceptEdits` /\n * `bypassPermissions`, grok `dontAsk`, …). An engine that cannot honour one\n * reports it unavailable WITH a reason rather than approximating it. The\n * in-page \"Ask\" mode (answering permission prompts in the UI) is 3.11.\n */\nexport const VERSE_PERMISSION_MODES = ['plan', 'accept-edits', 'auto', 'bypass'] as const;\nexport type VersePermissionMode = (typeof VERSE_PERMISSION_MODES)[number];\nexport const VERSE_DEFAULT_PERMISSION_MODE: VersePermissionMode = 'accept-edits';\n\n/**\n * V3.10. Reasoning effort, the union across engines — claude `--effort`\n * (low · medium · high · xhigh · max) and codex `model_reasoning_effort`\n * (minimal · low · medium · high · xhigh). Each seat offers only the subset\n * its CLI accepts; the rest come back unavailable with a reason.\n */\nexport const VERSE_EFFORTS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const;\nexport type VerseEffort = (typeof VERSE_EFFORTS)[number];\n\n/** V3.10. `VerseSession.controls` — absent key = engine default (see there). */\nexport interface VerseSessionControls {\n effort?: VerseEffort;\n permissionMode?: VersePermissionMode;\n}\n\n/** Normalized event stream. `seq` is monotonic per session and is the SSE id. */\nexport type VerseEvent =\n | { seq: number; at: string; type: 'user-message'; turnId: string; text: string }\n | { seq: number; at: string; type: 'turn-started'; turnId: string; pid: number | null }\n | { seq: number; at: string; type: 'text-delta'; turnId: string; text: string }\n | { seq: number; at: string; type: 'assistant-message'; turnId: string; text: string }\n /**\n * Reasoning the CLI chose to show. V3.10 ADDITIVE optional fields (absent on\n * older records = unknown, never \"false\"/\"0\"):\n * - `redacted` the CLI sent a signature-only / redacted block, so `text` is\n * empty but the model DID think (the UI says \"Thought 12s\").\n * - `durationMs` wall time the block took to stream, when measured.\n * - `kind` see VerseThinkingKind.\n */\n | {\n seq: number;\n at: string;\n type: 'thinking';\n turnId: string;\n text: string;\n redacted?: boolean;\n durationMs?: number;\n kind?: VerseThinkingKind;\n }\n | { seq: number; at: string; type: 'tool-use'; turnId: string; toolUseId: string; name: string; input: unknown }\n | { seq: number; at: string; type: 'tool-result'; turnId: string; toolUseId: string; output: string; isError: boolean }\n | { seq: number; at: string; type: 'usage'; turnId: string; usage: VerseUsage }\n | { seq: number; at: string; type: 'turn-done'; turnId: string; ok: boolean; nativeSessionId: string | null; durationMs: number }\n /**\n * `code` (V3.10 ADDITIVE, optional) is a stable machine key for errors the UI\n * or engine acts on — see VERSE_ERROR_CODES. Absent = uncategorised.\n */\n | { seq: number; at: string; type: 'error'; turnId: string | null; message: string; code?: string }\n | { seq: number; at: string; type: 'cancelled'; turnId: string }\n /**\n * V3.9. The CLI compacted its own conversation. Token counts are the CLI's\n * own (`compact_metadata.pre_tokens/post_tokens` on claude/grok). Codex\n * rollouts mark only THAT a compaction happened, so its counts are the last\n * `token_count` reading before the marker and the first one after it —\n * measured, but by Verse, not stated by the CLI; its `durationMs` is null.\n * Any count may be null when no reading brackets the compaction.\n */\n | {\n seq: number;\n at: string;\n type: 'compaction';\n turnId: string | null;\n trigger: 'auto' | 'manual';\n preTokens: number | null;\n postTokens: number | null;\n durationMs: number | null;\n }\n /**\n * V3.9. A context-occupancy READING that is not a usage delta — e.g. codex's\n * rollout `token_count` (exact last-call prompt size and the window the CLI\n * measured it against), polled mid-turn and read again after it. Replaces\n * the session's `contextTokens`; never summed. The engine fills\n * `autoCompactAt` for the window in force.\n */\n | {\n seq: number;\n at: string;\n type: 'context';\n turnId: string | null;\n contextTokens: number;\n contextWindow: number | null;\n exact: boolean;\n autoCompactAt?: number | null;\n /**\n * Where `contextWindow` came from. A CLI reading is `runtime`; the event\n * the engine writes after a MODE SWITCH carries the new budget's catalog\n * source, so a client never relabels a catalog figure as a measurement.\n * Absent on events from before this field existed (treat as `runtime`).\n */\n contextWindowSource?: VerseWindowSource;\n }\n // ── V3.10 TRANSIENT events ────────────────────────────────────────────────\n // Never written to the session log; fanned out to live SSE listeners only\n // (see VERSE_TRANSIENT_EVENT_TYPES for the wire rules). A reload therefore\n // never shows them — everything they convey is either ephemeral (a spinner)\n // or re-stated by a persisted event at turn end (`thinking`, `usage`).\n /** A chunk of streamed reasoning text for the in-flight thinking block. */\n | { seq: number; at: string; type: 'thinking-delta'; turnId: string; text: string }\n /**\n * The CLI reports reasoning volume without text (claude\n * `system/thinking_tokens`). An ESTIMATE, rate-limited by the adapter.\n */\n | { seq: number; at: string; type: 'thinking-progress'; turnId: string; estimatedTokens: number }\n /**\n * What the running turn is doing right now. `tool` names the running tool\n * when phase is `tool`. `outTokens`/`tokPerSec` are absent when unmeasured.\n */\n | {\n seq: number;\n at: string;\n type: 'progress';\n turnId: string;\n phase: VerseProgressPhase;\n tool?: string;\n elapsedMs: number;\n outTokens?: number;\n tokPerSec?: number;\n }\n /** A human-readable notice about the turn's plumbing (API retry, preflight, no-output watchdog). */\n | { seq: number; at: string; type: 'status'; turnId: string | null; kind: VerseStatusKind; message: string }\n // ── V3.10 PERSISTED events ────────────────────────────────────────────────\n /**\n * The engine recovered from a lost native conversation (e.g. the vendor\n * thread vanished) by starting a new native session seeded with the handoff\n * note (`how: 'handoff'`) or a bare new session (`new-native-session`).\n */\n | { seq: number; at: string; type: 'recovered'; turnId: string | null; how: VerseRecoveryHow; message: string }\n /**\n * The log hit its cap and events with `seq < droppedBefore` were dropped at\n * a TURN boundary (never mid-turn). `turnId` is always null — it is present\n * only so code reading `event.turnId` across the union keeps typechecking.\n */\n | { seq: number; at: string; type: 'history-truncated'; turnId: null; droppedBefore: number };\n\nexport type VerseEventType = VerseEvent['type'];\n\n/** V3.10. `summary` = vendor-summarised reasoning, `raw` = verbatim, `progress` = a heading/placeholder only. */\nexport type VerseThinkingKind = 'summary' | 'raw' | 'progress';\n\n/** V3.10. Phase of a running turn, carried by the transient `progress` event. */\nexport type VerseProgressPhase = 'thinking' | 'tool' | 'writing' | 'waiting';\n\n/** V3.10. What a transient `status` notice is about. */\nexport type VerseStatusKind = 'retry' | 'preflight' | 'watchdog';\n\n/** V3.10. How a `recovered` event restored the conversation. */\nexport type VerseRecoveryHow = 'new-native-session' | 'handoff';\n\n/**\n * V3.10. Known `error.code` values. `code` stays `string` on the wire so an\n * older client never rejects a newer code; these are the ones Verse acts on.\n * - `native-thread-missing` — the vendor conversation to resume no longer exists.\n * - `session-in-use` — the CLI refused because the native session id is locked.\n */\nexport const VERSE_ERROR_CODES = ['native-thread-missing', 'session-in-use'] as const;\nexport type VerseErrorCode = (typeof VERSE_ERROR_CODES)[number];\n\n/**\n * V3.10. Event types that are NEVER persisted — emitted to live SSE listeners\n * only. Wire rules (A5 server, A6 client):\n * - a transient event carries `seq` = the session's LAST PERSISTED seq (the\n * counter does not advance), so the stored log stays gap-free and a server\n * restart can never reissue a seq a client already saw;\n * - its SSE frame has NO `id:` line, so the browser's Last-Event-ID resume\n * cursor only ever points at persisted events;\n * - clients must not dedupe, store or resume by a transient event's seq.\n * Browser-safe (plain Set), imported by the web bundle.\n */\nconst TRANSIENT_EVENT_TYPE_LIST = [\n 'thinking-delta',\n 'thinking-progress',\n 'progress',\n 'status',\n] as const satisfies readonly VerseEventType[];\n\nexport const VERSE_TRANSIENT_EVENT_TYPES: ReadonlySet<VerseEventType> = new Set<VerseEventType>(TRANSIENT_EVENT_TYPE_LIST);\n\nexport type VerseTransientEventType = (typeof TRANSIENT_EVENT_TYPE_LIST)[number];\nexport type VerseTransientEvent = Extract<VerseEvent, { type: VerseTransientEventType }>;\nexport type VersePersistedEvent = Exclude<VerseEvent, { type: VerseTransientEventType }>;\n\n/** V3.10. Type guard over VERSE_TRANSIENT_EVENT_TYPES. */\nexport function isTransientVerseEvent(event: VerseEvent): event is VerseTransientEvent {\n return VERSE_TRANSIENT_EVENT_TYPES.has(event.type);\n}\n\n/**\n * Which endpoint a LOCAL seat sends its turns to.\n *\n * - `ollama` — the historical lane: the plain `claude` binary with\n * ANTHROPIC_BASE_URL pointed straight at Ollama's\n * Anthropic-compatible endpoint on :11434. Ollama\n * serialises this model architecture, so a fleet of local\n * agents on this lane is a queue (docs/LOCAL-FLEET.md).\n * - `llama-server` — the same binary pointed at the local Anthropic\n * normalising proxy that fronts llama-server. That proxy\n * is the ONLY way to reach llama-server's parallel slots\n * and the prompt-cache fix, because Qwen3.8's chat\n * template rejects Claude Code's request shape unless the\n * proxy rewrites it on the way through.\n *\n * DISPATCH ONLY. Model DISCOVERY stays on Ollama in both lanes — llama-server\n * implements neither `/api/tags` nor `/api/show`, which is where every local\n * seat's identity, context window and `tools` capability comes from.\n */\nexport type VerseLocalDispatch = 'ollama' | 'llama-server';\n\n/** What a client is told about this machine's local serving situation. */\nexport interface VerseLocalRuntimeSummary {\n /** Ollama reachable? Drives the local seats and an inline hint when false. */\n ollama: { reachable: boolean; baseUrl: string; models: string[] };\n /**\n * Where local seats DISPATCH turns, reported only when that is NOT the\n * default Ollama lane.\n *\n * Absent means `ollama`, i.e. exactly what every existing client already\n * assumes — so opting in is visible on the wire and NOT opting in changes no\n * payload at all.\n */\n dispatch?: { lane: VerseLocalDispatch; baseUrl: string };\n}\n\n/** GET /api/verse/bootstrap */\nexport interface VerseBootstrap {\n seats: VerseSeat[];\n projects: VerseProject[];\n sessions: VerseSession[];\n /** Whether POST routes are enabled on this server (`ashlr serve --allow-dispatch` / `ashlr verse`). */\n dispatchEnabled: boolean;\n localRuntime: VerseLocalRuntimeSummary;\n}\n\n/**\n * GET /api/verse/seats (V2.1)\n *\n * WHY THIS ROUTE EXISTS: `bootstrap` is a MOUNT-TIME snapshot. A client that\n * opened the app before the collector's first cycle finished would show\n * \"unknown\" forever. This is the same seat list, cheap enough to poll, and it\n * always reflects the collector's CURRENT state rather than a cached one.\n */\nexport interface VerseSeatsResponse {\n /** When this body was built. */\n sampledAt: string;\n seats: VerseSeat[];\n localRuntime: VerseBootstrap['localRuntime'];\n}\n\n/**\n * POST /api/verse/sessions\n *\n * Two spellings, never mixed (the API rejects the mix rather than silently\n * preferring one):\n * - `workspaceId` alone — the named workspace supplies primary and extras.\n * - `projectPath` (+ optional `extraRoots`) — an ad-hoc set, which is also\n * exactly the pre-workspace request shape.\n */\nexport interface VerseCreateSessionRequest {\n projectPath: string;\n seatId: string;\n model?: string;\n title?: string;\n /** V2.2 ADDITIVE. Absolute paths beyond the primary. */\n extraRoots?: string[];\n /** V2.2 ADDITIVE. Bind to a named workspace instead of naming paths. */\n workspaceId?: string;\n /**\n * SERVER-FILLED. The API resolves this from the registry once `workspaceId`\n * is validated, and never reads it off the request body — so a session can\n * not be labelled with a workspace name that is not the one it was built\n * from.\n */\n workspaceName?: string;\n /**\n * V3.9 ADDITIVE. Absent → the seat's preferred mode (preferences.json), else\n * `standard`. `expansive` on a model with no expansive budget is rejected.\n */\n contextMode?: VerseContextMode;\n /**\n * V3.9 ADDITIVE. Start this session as a handoff from an existing one. The\n * server resolves the source's title itself (never from the body) and pins\n * `handoffFrom`; the handoff TEXT is sent by the client as turn 1, after the\n * operator has read it — nothing is spent on their behalf.\n */\n handoffFromSessionId?: string;\n}\n\n/** POST /api/verse/sessions/:id/turns */\nexport interface VerseTurnRequest {\n text: string;\n}\nexport interface VerseTurnResponse {\n turnId: string;\n session: VerseSession;\n}\n\n/** GET /api/verse/sessions/:id */\nexport interface VerseSessionDetail {\n session: VerseSession;\n events: VerseEvent[];\n}\n\n/** Adapter input: how to launch one turn. Pure data, produced by the engine, consumed by the spawner. */\nexport interface VerseTurnLaunch {\n argv: string[];\n cwd: string;\n /** Extra env layered over a minimal base (PATH/HOME/TMPDIR/LANG). Never contains secrets. */\n env: Record<string, string>;\n /** Text written to stdin (codex); null when the prompt is in argv. */\n stdin: string | null;\n}\n\nexport const VERSE_MAX_TURN_TEXT_BYTES = 64 * 1024;\nexport const VERSE_MAX_EVENTS_PER_SESSION = 20_000;\nexport const VERSE_TURN_TIMEOUT_MS = 30 * 60 * 1000;\n/**\n * LAST-RESORT fallbacks, used only when neither the runtime, the seat's own\n * catalog, nor the verified per-model table (core/verse/model-windows.ts) knows\n * the model. Each mirrors what the CLI ITSELF assumes for an unknown id, so the\n * meter never claims more than the CLI will allow:\n *\n * - claude 200_000 — Claude Code's `Mg()` default for an unrecognized model.\n * Every KNOWN Claude id carries its real window (1M for the 5.x family,\n * Opus 4.8 and Sonnet 5) in the per-model table instead.\n * - codex 258_400 — 272_000 × the 95% `effective_context_window_percent`\n * every codex catalog entry carries; the figure codex's own rollouts report\n * as `model_context_window` (65,440 of 65,440 observed events).\n * - grok 500_000 — `context_window` for every grok-4.x id in the seat catalog.\n * - local 65_536 — the num_ctx the default local tag pins.\n */\nexport const VERSE_DEFAULT_CONTEXT_WINDOWS: Record<string, number> = {\n claude: 200_000,\n codex: 258_400,\n grok: 500_000,\n local: 65_536,\n};\n\n// ---------------------------------------------------------------------------\n// V3.9 context orchestration — request/response shapes (docs/VERSE-CONTEXT.md)\n// ---------------------------------------------------------------------------\n\n/** GET /api/verse/preferences — the operator's standing context choices. */\nexport interface VersePreferences {\n version: 1;\n /** Per-seat default context mode for NEW sessions. Absent = `standard`. */\n seats: Record<string, { contextMode?: VerseContextMode }>;\n /** Shared project memory. On by default; listed canonical paths opt out. */\n memory: { enabled: boolean; disabledProjects: string[] };\n /**\n * V3.10 ADDITIVE. Stream vendor-summarised reasoning on non-local seats\n * (claude `--thinking-display summarized`). Absent = on (Mason's default).\n */\n thinkingDisplay?: boolean;\n}\n\n/**\n * POST /api/verse/preferences — exactly ONE of the three forms per request, so\n * a body can never change more than the operator clicked.\n */\nexport type VersePreferencesUpdate =\n | { seatId: string; contextMode: VerseContextMode }\n | { memoryEnabled: boolean }\n | { projectPath: string; memoryEnabled: boolean };\n\n/** POST /api/verse/sessions/:id/context-mode */\nexport interface VerseContextModeRequest {\n mode: VerseContextMode;\n}\n\n/** POST /api/verse/sessions/:id/handoff-preview */\nexport interface VerseHandoffPreviewRequest {\n /**\n * Include the latest assistant message verbatim (capped) as the agent's own\n * summary — used after \"Ask this seat to summarize\" produced one.\n */\n includeLastAssistant?: boolean;\n /** Optional operator focus line, e.g. \"finish the migration of X\". ≤ 500 chars. */\n focus?: string;\n}\n\n/** Deterministic, zero-spend handoff note built from the session's event log. */\nexport interface VerseHandoffPreview {\n sourceSessionId: string;\n sourceTitle: string;\n /** The exact text to send as turn 1 of the new session. ≤ VERSE_HANDOFF_MAX_CHARS. */\n text: string;\n stats: {\n chars: number;\n /** chars / 4, rounded up — the same estimator the fit badges use. */\n estTokens: number;\n turnsCovered: number;\n filesTouched: number;\n /** Sections dropped to respect the cap, by name; empty when nothing was cut. */\n truncated: string[];\n };\n}\n\nexport const VERSE_HANDOFF_MAX_CHARS = 12_000;\n\n/**\n * The canned turn behind \"Ask this seat to summarize first\". Shared by the UI\n * (which sends it as an ordinary turn through the normal spend chokepoint) and\n * the handoff builder (which must NOT carry it into the new session as the\n * operator's \"latest ask\" — the new chat would otherwise be told to write a\n * handoff note instead of continuing the work).\n *\n * Written so the reply is useful as turn 1 of a session that has NONE of this\n * context. It asks for no tool use on purpose: a summary that starts editing\n * files is not a summary, and a read-only turn is also the cheapest one.\n */\nexport const VERSE_HANDOFF_SUMMARY_REQUEST = [\n 'This conversation is about to continue in a fresh session that has none of its context.',\n 'Write the handoff note that new session should start from. Do not edit files or run commands for this — only write the note.',\n '',\n 'Cover, concretely:',\n '- The goal, and what \"done\" looks like.',\n '- Decisions made so far, each with the reason it was made.',\n '- Current state: what is finished, what is half-done, what is untested.',\n '- Files changed and anything not yet committed.',\n '- How to verify the work (exact commands).',\n '- Open questions, risks, and approaches already tried that did not work.',\n '',\n 'Keep it under 600 words. Prefer file paths and commands over prose.',\n].join('\\n');\n\n/** GET /api/verse/context-fit — how big the reachable code is, in tokens. */\nexport interface VerseContextFitRoot {\n path: string;\n /** Tracked text files counted (git ls-files, binaries and >1 MB files skipped). */\n files: number;\n bytes: number;\n estTokens: number;\n /** True when the scan hit its file or time cap; estTokens is then a floor. */\n truncated: boolean;\n}\n\nexport interface VerseContextFit {\n roots: VerseContextFitRoot[];\n totalEstTokens: number;\n /** Always 'bytes/4' today; named so a better estimator can be introduced honestly. */\n estimator: 'bytes/4';\n sampledAt: string;\n}\n\n/**\n * Per-model verdict for a working set (see context-math.ts `fitVerdict`):\n * fits in standard, tight in standard, fits only in expansive, or too big for\n * any single context (split the work).\n */\nexport type VerseFitVerdict = 'fits' | 'tight' | 'expansive' | 'split';\n\n/** GET /api/verse/search?q=&limit= — keyword search over past sessions. Zero spend. */\nexport interface VerseSearchHit {\n sessionId: string;\n title: string;\n projectPath: string;\n engine: VerseEngine;\n seq: number;\n at: string;\n kind: 'user' | 'assistant';\n /** ≤ 240 chars around the first match, whitespace-collapsed. */\n snippet: string;\n score: number;\n}\n\nexport interface VerseSearchResponse {\n query: string;\n hits: VerseSearchHit[];\n /** Sessions actually scanned (the scan is bounded; see session-search.ts). */\n scannedSessions: number;\n truncated: boolean;\n}\n\n/** GET/POST /api/verse/memory — shared project memory for one project. */\nexport interface VerseProjectMemory {\n projectPath: string;\n enabled: boolean;\n /** Contents of MEMORY.md ('' when absent). */\n content: string;\n bytes: number;\n updatedAt: string | null;\n /** Other files the agents created in the memory directory (names only). */\n files: string[];\n /**\n * Present (true) only when `content` as SENT differs from the bytes on disk:\n * the public-JSON sanitizer rewrote the home path to `~` or replaced a\n * secret-looking value with `[REDACTED]`. The file itself is untouched; a\n * save that would write placeholders over real values is refused (409\n * VERSE_MEMORY_REDACTED), so the editor must say so before the operator types.\n */\n contentSanitized?: boolean;\n}\n\n/** POST /api/verse/memory */\nexport interface VerseProjectMemoryWrite {\n projectPath: string;\n content: string;\n}\n\nexport const VERSE_MEMORY_MAX_BYTES = 64 * 1024;\n\n/**\n * POST /api/verse/memory's own JSON body cap: JSON escaping can double the\n * content (every newline becomes `\\\\n`), plus room for the other fields. Every\n * other route keeps the 64 KiB default. Shared so the browser's pre-check can\n * never refuse a save the server would accept.\n */\nexport const VERSE_MEMORY_BODY_MAX_BYTES = 2 * VERSE_MEMORY_MAX_BYTES + 8 * 1024;\n\n/** Cap on the memory block appended to each session's system prompt. */\nexport const VERSE_MEMORY_BLOCK_MAX_BYTES = 6 * 1024;\n","/**\n * routes/verse/verse-store.ts — per-session transcript state for the Verse\n * console. Framework-free (useSyncExternalStore glue lives in\n * useVerseSession.ts, the live stream in session-stream.ts), same split as\n * data/cache.ts + data/hooks.ts.\n *\n * One entry per session id holds the ordered, seq-deduplicated event log,\n * the latest session record we know of, the stream connection state, and —\n * V3.10 — the LIVE state of a running turn (reasoning streaming in, what the\n * turn is doing, retry/watchdog notices), which is fed by transient events\n * that are never persisted and never enter the event log.\n * Snapshots are replaced wholesale on every change (Object.is semantics —\n * see DESIGN.md §3 for the bug that rule prevents).\n *\n * V3.10 DATA PATH. The previous store merged every event with a Map + spread\n * + sort and told every subscriber about it: replaying 5k events took 2.45 s\n * and 10k took 17 s, and every streamed token re-rendered the whole Chat\n * section. Now:\n *\n * - `applyVerseEvents` applies a BATCH: an event past the newest seq is a\n * plain append (one array copy per batch, not per event), an older one\n * is binary-searched (duplicate → dropped, gap → inserted in place);\n * - subscribers are per session (`subscribeVerseSession`) and read one of\n * three snapshots that change independently:\n * · the HEAD (`getVerseSessionHead`) — session record, stream state\n * and the event log as of the last STRUCTURAL event. A text-delta\n * alone does not republish it, so the sidebar, header and resources\n * panel do not re-render per token;\n * · the TRANSCRIPT (`getVerseTranscript`, verse-transcript.ts) —\n * derived lazily, per turn segment, with unchanged turns returned as\n * the same objects so the renderer can skip them;\n * · the LIVE state (`getVerseLive`) — transient signals only.\n * - the stream (session-stream.ts) calls `applyVerseEvents` once per\n * animation frame, so a burst of frames costs one render.\n *\n * The transcript derivation (`buildTranscript`, `getVerseTranscript`,\n * `groupTranscriptItems`) lives in verse-transcript.ts: this store is on the\n * chat first-paint path and the derivation is not (only lazy chunks render a\n * transcript). Its types are re-exported below so type imports keep working;\n * never re-export a VALUE from there, or the derivation rides along again.\n */\nimport type { VerseEvent, VerseSession, VerseUsage } from '../../data/api-types.js';\nimport {\n isTransientVerseEvent,\n type VerseProgressPhase,\n type VerseStatusKind,\n type VerseTransientEvent,\n type VerseWindowSource,\n} from '../../../core/verse/types.js';\n// compaction-point, not context-math: this store is on the chat first-paint\n// path and needs only this formula (context-math re-exports it).\nimport { reconcileAutoCompactAt } from '../../../core/verse/compaction-point.js';\n\nexport type {\n BuildTranscriptOptions,\n ToolGroupItem,\n ToolGroupMember,\n Transcript,\n TranscriptCache,\n TranscriptItem,\n TranscriptRenderItem,\n TranscriptSegment,\n} from './verse-transcript.js';\n\nexport type VerseStreamState = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed';\n\n// ---------------------------------------------------------------------------\n// Live (transient) state\n// ---------------------------------------------------------------------------\n\n/** The reasoning block currently streaming in (`thinking-delta` / `thinking-progress`). */\nexport interface VerseLiveThinking {\n turnId: string;\n /** Streamed text so far; empty when the CLI reports only a token estimate. */\n text: string;\n /** Client clock (ms) when this block's first signal arrived. */\n startedAt: number;\n /** Latest `thinking-progress` estimate; null when the CLI sent none (unknown, never 0). */\n estimatedTokens: number | null;\n}\n\nexport interface VerseLiveProgress {\n phase: VerseProgressPhase;\n tool: string | null;\n /** Server-measured turn wall time when the frame was sent. */\n elapsedMs: number;\n outTokens: number | null;\n tokPerSec: number | null;\n /** Client clock (ms) the frame arrived, so elapsed can keep counting between frames. */\n receivedAt: number;\n}\n\nexport interface VerseLiveNotice {\n kind: VerseStatusKind;\n message: string;\n at: string;\n receivedAt: number;\n}\n\nexport interface VerseLiveState {\n /** The running turn these signals belong to; null when none is running. */\n turnId: string | null;\n /** Client clock (ms) the running turn started, from its `turn-started` stamp when parseable. */\n startedAt: number | null;\n progress: VerseLiveProgress | null;\n thinking: VerseLiveThinking | null;\n /** The latest plumbing notice (API retry, preflight, no-output watchdog); cleared when output resumes. */\n notice: VerseLiveNotice | null;\n /** Last turn that settled, so a late transient frame for it cannot resurrect the live line. */\n settledTurnId: string | null;\n}\n\n/** What this tab measured about a reasoning block while it streamed — keyed by the persisted `thinking` event's seq. */\nexport interface VerseThinkingStat {\n durationMs: number | null;\n estimatedTokens: number | null;\n}\n\nconst EMPTY_LIVE: VerseLiveState = {\n turnId: null,\n startedAt: null,\n progress: null,\n thinking: null,\n notice: null,\n settledTurnId: null,\n};\n\n// ---------------------------------------------------------------------------\n// Entries, heads and subscriptions\n// ---------------------------------------------------------------------------\n\nexport interface VerseSessionState {\n sessionId: string;\n session: VerseSession | null;\n events: VerseEvent[];\n /** Highest PERSISTED seq held — the resume cursor. Transient frames never move it. */\n lastSeq: number;\n loaded: boolean;\n loadError: string | null;\n stream: VerseStreamState;\n /** V3.10 transient signals of the running turn. */\n live: VerseLiveState;\n}\n\n/**\n * Everything about a session EXCEPT its live signals, republished only on\n * structural change. `events` is the log as of the last non-`text-delta`\n * event: streamed tokens reach the transcript through its own subscription,\n * so a consumer of the head (session record, meters, activity stats) never\n * re-renders per token. Code that needs every delta reads\n * `getVerseSessionState` or `getVerseTranscript` instead.\n */\nexport type VerseSessionHead = Omit<VerseSessionState, 'live'>;\n\nconst EMPTY: Omit<VerseSessionState, 'sessionId'> = {\n session: null,\n events: [],\n lastSeq: 0,\n loaded: false,\n loadError: null,\n stream: 'idle',\n live: EMPTY_LIVE,\n};\n\nconst sessions = new Map<string, VerseSessionState>();\nconst heads = new Map<string, VerseSessionHead>();\nconst thinkingStats = new Map<string, Map<number, VerseThinkingStat>>();\nconst listeners = new Set<() => void>();\nconst sessionListeners = new Map<string, Set<() => void>>();\n\nexport type VerseStoreLifecycle = { kind: 'reset' } | { kind: 'forget'; sessionId: string };\nconst lifecycleListeners = new Set<(event: VerseStoreLifecycle) => void>();\n\nfunction notify(sessionId: string | null): void {\n if (sessionId !== null) {\n const scoped = sessionListeners.get(sessionId);\n if (scoped) for (const l of [...scoped]) l();\n } else {\n for (const set of [...sessionListeners.values()]) for (const l of [...set]) l();\n }\n for (const l of [...listeners]) l();\n}\n\n/** Every change to every session. Prefer `subscribeVerseSession` in components. */\nexport function subscribeVerseStore(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\n/** Changes to ONE session only — what a component showing that session subscribes to. */\nexport function subscribeVerseSession(sessionId: string, listener: () => void): () => void {\n let set = sessionListeners.get(sessionId);\n if (!set) {\n set = new Set();\n sessionListeners.set(sessionId, set);\n }\n set.add(listener);\n return () => {\n const current = sessionListeners.get(sessionId);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) sessionListeners.delete(sessionId);\n };\n}\n\n/**\n * Reset / forget notifications, so the live-stream registry can close the\n * connection of a chat that was deleted or of a store that was cleared on\n * logout (and so tests never inherit a previous test's stream).\n */\nexport function subscribeVerseStoreLifecycle(listener: (event: VerseStoreLifecycle) => void): () => void {\n lifecycleListeners.add(listener);\n return () => lifecycleListeners.delete(listener);\n}\n\nfunction headOf(state: VerseSessionState): VerseSessionHead {\n return {\n sessionId: state.sessionId,\n session: state.session,\n events: state.events,\n lastSeq: state.lastSeq,\n loaded: state.loaded,\n loadError: state.loadError,\n stream: state.stream,\n };\n}\n\nfunction entryFor(sessionId: string): VerseSessionState {\n let e = sessions.get(sessionId);\n if (!e) {\n e = { sessionId, ...EMPTY };\n sessions.set(sessionId, e);\n heads.set(sessionId, headOf(e));\n }\n return e;\n}\n\n/**\n * Store `next` and republish the head when anything it carries changed.\n * `structural` = the event log gained something other than a text-delta.\n */\nfunction commit(sessionId: string, next: VerseSessionState, structural: boolean): void {\n sessions.set(sessionId, next);\n const head = heads.get(sessionId);\n if (\n !head || structural || head.session !== next.session || head.loaded !== next.loaded ||\n head.loadError !== next.loadError || head.stream !== next.stream\n ) {\n heads.set(sessionId, headOf(next));\n }\n}\n\nfunction patch(sessionId: string, delta: Partial<VerseSessionState>, structural = false): void {\n const current = entryFor(sessionId);\n commit(sessionId, { ...current, ...delta }, structural);\n notify(sessionId);\n}\n\nconst NONE: VerseSessionState = { sessionId: '', ...EMPTY };\nconst NONE_HEAD: VerseSessionHead = headOf(NONE);\n\n/** Stable reference until the entry changes, so useSyncExternalStore is happy. */\nexport function getVerseSessionState(sessionId: string | null): VerseSessionState {\n if (!sessionId) return NONE;\n return entryFor(sessionId);\n}\n\n/** The head snapshot (see VerseSessionHead) — stable across text-deltas. */\nexport function getVerseSessionHead(sessionId: string | null): VerseSessionHead {\n if (!sessionId) return NONE_HEAD;\n entryFor(sessionId);\n return heads.get(sessionId)!;\n}\n\n/** Live signals of the running turn; the same object until one of them changes. */\nexport function getVerseLive(sessionId: string | null): VerseLiveState {\n if (!sessionId) return EMPTY_LIVE;\n return entryFor(sessionId).live;\n}\n\n/**\n * What this tab measured about the session's reasoning blocks while they\n * streamed (read by verse-transcript.ts when it derives the transcript).\n */\nexport function getVerseThinkingStats(sessionId: string): ReadonlyMap<number, VerseThinkingStat> | undefined {\n return thinkingStats.get(sessionId);\n}\n\n/** Merge a fetched detail (session + full event log) into the entry. Events\n * already known (same seq) are kept once; the union stays sorted by seq. The\n * detail fetch can race a live stream, so a `turn-done` that streamed in\n * meanwhile wins over the older `running` snapshot. A refetch that brings\n * nothing new changes nothing — no re-render. */\nexport function seedVerseSession(sessionId: string, session: VerseSession, events: readonly VerseEvent[]): void {\n const current = entryFor(sessionId);\n const merged = mergeEvents(current.events, events);\n const settled = settledStatus(session, merged);\n const nextSession = current.session && sameSession(current.session, settled) ? current.session : settled;\n const eventsChanged = merged !== current.events;\n const live = eventsChanged || nextSession !== current.session ? liveFromLog(current.live, merged, nextSession) : current.live;\n if (!eventsChanged && nextSession === current.session && current.loaded && current.loadError === null && live === current.live) return;\n commit(sessionId, {\n ...current,\n session: nextSession,\n events: merged,\n lastSeq: merged.length ? Math.max(current.lastSeq, merged[merged.length - 1]!.seq) : current.lastSeq,\n loaded: true,\n loadError: null,\n live,\n }, eventsChanged);\n notify(sessionId);\n}\n\nexport function setVerseLoadError(sessionId: string, message: string): void {\n patch(sessionId, { loadError: message, loaded: true });\n}\n\nexport function setVerseStreamState(sessionId: string, stream: VerseStreamState): void {\n if (entryFor(sessionId).stream === stream) return;\n patch(sessionId, { stream });\n}\n\n/**\n * Replace the session record (after a turn POST / rename / list refresh).\n * `turnId` is the turn the snapshot claims is running (POST …/turns returns\n * it): when the log already holds that turn's `turn-done`/`cancelled` — a\n * spawn failure settles in the same tick as the 202 — the snapshot is stale\n * and must not drag the composer back to running.\n */\nexport function setVerseSession(sessionId: string, session: VerseSession, turnId?: string): void {\n const current = entryFor(sessionId);\n patch(sessionId, { session: settledStatus(session, current.events, turnId) });\n}\n\n/**\n * Reconcile a `running` snapshot against the event log. Without a `turnId`\n * the running turn is the last `user-message`/`turn-started` in the log; a\n * later `turn-done`/`cancelled` means the server already settled it. With a\n * `turnId`, only that turn's terminal event counts (its `user-message` may\n * not have streamed in yet, so a previous turn's `turn-done` proves nothing).\n * Status follows the same rules applyVerseEvent uses: cancelled → idle,\n * `turn-done ok:false` → error only when an `error` event was logged for it.\n */\nexport function settledStatus(session: VerseSession, events: readonly VerseEvent[], turnId?: string): VerseSession {\n if (session.status !== 'running') return session;\n let anchorSeq = -1;\n let anchorTurn: string | null = turnId ?? null;\n if (!turnId) {\n for (const e of events) {\n if (e.type === 'user-message' || e.type === 'turn-started') {\n anchorSeq = e.seq;\n anchorTurn = e.turnId;\n }\n }\n if (anchorTurn === null) return session;\n }\n let terminal: Extract<VerseEvent, { type: 'turn-done' | 'cancelled' }> | null = null;\n let lastError: string | null = null;\n for (const e of events) {\n if (e.seq <= anchorSeq) continue;\n if (e.type === 'error' && e.turnId === anchorTurn) lastError = e.message;\n if ((e.type === 'turn-done' || e.type === 'cancelled') && e.turnId === anchorTurn) terminal = e;\n }\n if (!terminal) return session;\n const failed = terminal.type === 'turn-done' && !terminal.ok && lastError !== null;\n return {\n ...session,\n status: failed ? 'error' : 'idle',\n lastError: failed ? lastError : null,\n updatedAt: terminal.at > session.updatedAt ? terminal.at : session.updatedAt,\n };\n}\n\n/** Optimistic status flip (e.g. right after POST …/turns returns 202). */\nexport function setVerseSessionStatus(sessionId: string, status: VerseSession['status']): void {\n const current = entryFor(sessionId);\n if (!current.session || current.session.status === status) return;\n patch(sessionId, { session: { ...current.session, status } });\n}\n\nexport interface VerseApplyResult {\n /** Persisted events newly added to the log (duplicates excluded). */\n applied: number;\n /** A newly added event settled a turn (`turn-done` / `cancelled` / `error`) — the lists may be stale. */\n settled: boolean;\n /** A transient frame changed the live state. */\n liveChanged: boolean;\n}\n\n/**\n * Apply a BATCH of live events in arrival order, then notify once.\n *\n * Persisted events: duplicates (a stream that replays from the start, or a\n * resume that overlaps the detail fetch) are dropped by seq; an event past\n * the newest seq is appended; an older unseen one is inserted in place.\n * Session-level side effects: `usage` and `context` update the live context\n * meter, `compaction` counts a native compaction, `turn-started` flips\n * status to running, `turn-done`/`cancelled`/`error` settle it, and a codex\n * `turn-done.nativeSessionId` is captured.\n *\n * Transient events (VERSE_TRANSIENT_EVENT_TYPES) never enter the log and\n * never move `lastSeq`: they carry the last PERSISTED seq, so deduping them\n * by seq would drop every one of them. They only update `live`.\n */\nexport function applyVerseEvents(sessionId: string, incoming: readonly VerseEvent[]): VerseApplyResult {\n const current = entryFor(sessionId);\n let events: VerseEvent[] = current.events;\n let copied = false;\n let lastSeq = current.lastSeq;\n let session = current.session;\n let live = current.live;\n let applied = 0;\n let settled = false;\n let structural = false;\n let liveChanged = false;\n const now = Date.now();\n\n for (const event of incoming) {\n if (isTransientVerseEvent(event)) {\n const next = applyTransient(live, event, now);\n if (next !== live) {\n live = next;\n liveChanged = true;\n }\n continue;\n }\n if (!Number.isFinite(event.seq)) continue;\n const tail = events.length > 0 ? events[events.length - 1]!.seq : -Infinity;\n if (event.seq > tail) {\n if (!copied) {\n events = events.slice();\n copied = true;\n }\n events.push(event);\n } else {\n const at = seqIndex(events, event.seq);\n if (at.found) continue;\n if (!copied) {\n events = events.slice();\n copied = true;\n }\n events.splice(at.index, 0, event);\n }\n applied += 1;\n lastSeq = Math.max(lastSeq, event.seq);\n if (event.type !== 'text-delta') structural = true;\n if (event.type === 'turn-done' || event.type === 'cancelled' || event.type === 'error') settled = true;\n if (session) session = sessionAfter(session, event);\n const nextLive = livePersisted(live, event, now, sessionId);\n if (nextLive !== live) live = nextLive;\n }\n\n if (applied === 0 && !liveChanged) return { applied: 0, settled: false, liveChanged: false };\n commit(sessionId, { ...current, events, lastSeq, session, live }, structural);\n notify(sessionId);\n return { applied, settled, liveChanged };\n}\n\n/**\n * Apply one live event (the single-event form of applyVerseEvents). Returns\n * true when the log gained the event, or — for a transient frame — when the\n * live state changed.\n */\nexport function applyVerseEvent(sessionId: string, event: VerseEvent): boolean {\n const result = applyVerseEvents(sessionId, [event]);\n return result.applied > 0 || result.liveChanged;\n}\n\n/** A persisted event's effect on the session record (the old applyVerseEvent switch). */\nfunction sessionAfter(session: VerseSession, event: VerseEvent): VerseSession {\n switch (event.type) {\n case 'turn-started':\n return { ...session, status: 'running', lastError: null, updatedAt: event.at };\n case 'usage':\n return { ...session, usage: applyUsageFrame(session, event.usage), updatedAt: event.at };\n case 'context':\n return { ...session, usage: applyContextReading(session, event), updatedAt: event.at };\n case 'compaction':\n // Counted exactly as the server counts it (session-engine), so a reload\n // shows the same number the live stream did. Occupancy is NOT touched\n // here: the next `usage`/`context` reading is the measurement.\n return { ...session, compactionCount: (session.compactionCount ?? 0) + 1, updatedAt: event.at };\n case 'turn-done':\n return {\n ...session,\n status: event.ok ? 'idle' : session.lastError ? 'error' : 'idle',\n turnCount: session.turnCount + 1,\n nativeSessionId: event.nativeSessionId ?? session.nativeSessionId,\n updatedAt: event.at,\n };\n case 'error':\n return { ...session, status: 'error', lastError: event.message, updatedAt: event.at };\n case 'cancelled':\n return { ...session, status: 'idle', updatedAt: event.at };\n default:\n return session;\n }\n}\n\nfunction stampMs(at: string, fallback: number): number {\n const parsed = Date.parse(at);\n return Number.isFinite(parsed) ? parsed : fallback;\n}\n\n/** Enter (or stay in) the live context of `turnId`. */\nfunction liveForTurn(live: VerseLiveState, turnId: string, startedAt: number): VerseLiveState {\n if (live.turnId === turnId) return live;\n return { ...EMPTY_LIVE, turnId, startedAt, settledTurnId: live.settledTurnId };\n}\n\nfunction applyTransient(live: VerseLiveState, event: VerseTransientEvent, now: number): VerseLiveState {\n // A frame for a turn that already settled (late delivery after turn-done)\n // must not bring the live line back.\n if (event.turnId !== null && event.turnId === live.settledTurnId) return live;\n const base = event.turnId !== null ? liveForTurn(live, event.turnId, now) : live;\n switch (event.type) {\n case 'thinking-delta': {\n if (event.text.length === 0) return base;\n const block = base.thinking && base.thinking.turnId === event.turnId\n ? { ...base.thinking, text: base.thinking.text + event.text }\n : { turnId: event.turnId, text: event.text, startedAt: now, estimatedTokens: null };\n return { ...base, thinking: block, notice: null };\n }\n case 'thinking-progress': {\n const block = base.thinking && base.thinking.turnId === event.turnId\n ? { ...base.thinking, estimatedTokens: event.estimatedTokens }\n : { turnId: event.turnId, text: '', startedAt: now, estimatedTokens: event.estimatedTokens };\n return { ...base, thinking: block };\n }\n case 'progress':\n return {\n ...base,\n progress: {\n phase: event.phase,\n tool: typeof event.tool === 'string' && event.tool.length > 0 ? event.tool : null,\n elapsedMs: event.elapsedMs,\n outTokens: event.outTokens ?? null,\n tokPerSec: event.tokPerSec ?? null,\n receivedAt: now,\n },\n };\n case 'status':\n return { ...base, notice: { kind: event.kind, message: event.message, at: event.at, receivedAt: now } };\n default:\n return base;\n }\n}\n\n/** A persisted event's effect on the live state. */\nfunction livePersisted(live: VerseLiveState, event: VerseEvent, now: number, sessionId: string): VerseLiveState {\n switch (event.type) {\n case 'turn-started':\n if (event.turnId === live.settledTurnId) return live;\n return live.turnId === event.turnId\n ? { ...live, startedAt: stampMs(event.at, live.startedAt ?? now) }\n : { ...EMPTY_LIVE, turnId: event.turnId, startedAt: stampMs(event.at, now), settledTurnId: live.settledTurnId };\n case 'thinking': {\n // The persisted block supersedes the streamed one. What this tab\n // measured while it streamed (duration, the CLI's token estimate) is\n // kept beside the log, by seq, so the finished block can still say\n // \"Thought 12s · ~1.8k tok\" — the event itself is immutable.\n const streamed = live.thinking && live.thinking.turnId === event.turnId ? live.thinking : null;\n if (streamed) {\n let stats = thinkingStats.get(sessionId);\n if (!stats) {\n stats = new Map();\n thinkingStats.set(sessionId, stats);\n }\n stats.set(event.seq, {\n durationMs: typeof event.durationMs === 'number' ? event.durationMs : Math.max(0, now - streamed.startedAt),\n estimatedTokens: streamed.estimatedTokens,\n });\n }\n return streamed || live.notice ? { ...live, thinking: streamed ? null : live.thinking, notice: null } : live;\n }\n case 'text-delta':\n case 'assistant-message':\n case 'tool-use':\n case 'tool-result':\n // Output resumed: a retry went through, the watchdog's silence ended.\n return live.notice && (event.turnId === live.turnId || live.turnId === null) ? { ...live, notice: null } : live;\n case 'turn-done':\n case 'cancelled':\n return { ...EMPTY_LIVE, settledTurnId: event.turnId };\n default:\n return live;\n }\n}\n\n/**\n * Live state implied by a freshly merged log: a chat opened (or reloaded)\n * mid-turn still shows its live line, timed from the turn's own stamp; a\n * log whose last turn is settled shows none.\n */\nfunction liveFromLog(live: VerseLiveState, events: readonly VerseEvent[], session: VerseSession | null): VerseLiveState {\n let openTurn: Extract<VerseEvent, { type: 'turn-started' }> | null = null;\n let settledTurnId: string | null = live.settledTurnId;\n for (let i = events.length - 1; i >= 0; i -= 1) {\n const e = events[i]!;\n if (e.type === 'turn-done' || e.type === 'cancelled') {\n settledTurnId = e.turnId;\n break;\n }\n if (e.type === 'turn-started') {\n openTurn = e;\n break;\n }\n }\n if (!openTurn) {\n if (live.turnId !== null && live.turnId === settledTurnId) return { ...EMPTY_LIVE, settledTurnId };\n // No turn-started in the log yet (the POST just went out): keep whatever\n // the stream already told us; nothing in the log contradicts it.\n return live.settledTurnId === settledTurnId ? live : { ...live, settledTurnId };\n }\n if (session && session.status !== 'running') return live.turnId === null ? live : { ...EMPTY_LIVE, settledTurnId: live.settledTurnId };\n if (live.turnId === openTurn.turnId) return live;\n return { ...EMPTY_LIVE, turnId: openTurn.turnId, startedAt: stampMs(openTurn.at, Date.now()), settledTurnId: live.settledTurnId };\n}\n\n/**\n * Fold one `usage` frame into the session record. The frame carries THIS\n * turn's counts; the record keeps the session totals (server: session-engine\n * applyUsage). Occupancy and the window are live values, not sums.\n *\n * V3.9 fields are adopted only when the frame carries them (spread-when-\n * present), so a pre-3.9 server's frames leave the record's shape unchanged.\n * The one exception is a local seat: Verse SETS that window\n * (`CLAUDE_CODE_MAX_CONTEXT_TOKENS`), so a window a frame reports without\n * provenance is not allowed to replace it — the server ignores runtime\n * windows on local for the same reason.\n */\nexport function applyUsageFrame(session: VerseSession, frame: VerseUsage): VerseUsage {\n const prev = session.usage;\n const next: VerseUsage = {\n ...prev,\n inputTokens: prev.inputTokens + frame.inputTokens,\n outputTokens: prev.outputTokens + frame.outputTokens,\n cacheReadTokens: prev.cacheReadTokens + frame.cacheReadTokens,\n cacheCreationTokens: prev.cacheCreationTokens + frame.cacheCreationTokens,\n contextTokens: frame.contextTokens,\n };\n const frameWindow = typeof frame.contextWindow === 'number' && frame.contextWindow > 0 ? frame.contextWindow : null;\n const trusted = frame.contextWindowSource !== undefined || session.engine !== 'local';\n if (frameWindow !== null && trusted) next.contextWindow = frameWindow;\n if (frame.contextWindowSource !== undefined) next.contextWindowSource = frame.contextWindowSource;\n if (frame.autoCompactAt !== undefined) next.autoCompactAt = frame.autoCompactAt;\n // The server marks an EXACT reading by OMITTING the key (it only writes\n // `false`), so a resolved frame without it must clear an earlier \"≤\" — a\n // frame from a pre-3.9 server (no V3.9 fields at all) leaves it alone.\n if (frame.contextTokensExact === false) next.contextTokensExact = false;\n else if (frame.contextWindowSource !== undefined) delete next.contextTokensExact;\n return next;\n}\n\n/** The provenance values a frame may carry; anything else is ignored, never stored. */\nconst WINDOW_SOURCES: ReadonlySet<string> = new Set<VerseWindowSource>(['runtime', 'provider-catalog', 'cli-catalog', 'documented', 'fallback']);\n\nfunction frameWindowSource(value: unknown): VerseWindowSource | null {\n return typeof value === 'string' && WINDOW_SOURCES.has(value) ? (value as VerseWindowSource) : null;\n}\n\n/**\n * Apply a `context` reading: it REPLACES occupancy (never summed). When it\n * names a window, the window's provenance is the one the EVENT states\n * (`contextWindowSource`, V3.9): the engine also emits a `context` event\n * after a MODE SWITCH, carrying the new mode's CATALOG budget, and stamping\n * that 'runtime' made the tooltip say \"reported by the CLI on the last turn\"\n * for a number no CLI reported — and pinned it ahead of later catalog\n * corrections (sessionContextBudget precedence 1). Only an event from a\n * server that predates the field (no source at all) is taken as a runtime\n * reading, which is what every such event was.\n *\n * The engine fills `autoCompactAt` for the window in force; a frame without\n * it (an older server) is reconciled from the previous budget rather than\n * left pointing at a compaction point that belonged to a different window.\n */\nexport function applyContextReading(\n session: VerseSession,\n event: Extract<VerseEvent, { type: 'context' }>,\n): VerseUsage {\n const prev = session.usage;\n const next: VerseUsage = {\n ...prev,\n contextTokens: Math.max(0, Math.floor(event.contextTokens)),\n contextTokensExact: event.exact,\n };\n const window = typeof event.contextWindow === 'number' && event.contextWindow > 0 ? event.contextWindow : null;\n const stated = frameWindowSource(event.contextWindowSource);\n // Local windows are SET by Verse (CLAUDE_CODE_MAX_CONTEXT_TOKENS), not\n // measured: a window without provenance is not allowed to move one (see\n // applyUsageFrame). A V3.9 event that states its source is the engine's\n // own record of the window it just launched with, so it is adopted.\n if (window === null || (session.engine === 'local' && stated === null)) {\n if (event.autoCompactAt !== undefined && event.autoCompactAt !== null) next.autoCompactAt = event.autoCompactAt;\n return next;\n }\n next.contextWindow = window;\n next.contextWindowSource = stated ?? 'runtime';\n if (event.autoCompactAt !== undefined) {\n next.autoCompactAt = event.autoCompactAt;\n } else if (window !== prev.contextWindow) {\n next.autoCompactAt = reconcileAutoCompactAt({\n engine: session.engine,\n runtimeWindow: window,\n budget: prev.contextWindow ? { contextWindow: prev.contextWindow, autoCompactAt: prev.autoCompactAt ?? null } : null,\n });\n }\n return next;\n}\n\n/**\n * When this chat last talked to its provider — the newest `usage`,\n * `turn-done`, `cancelled` or TURN-attributed `context` event — or null when\n * the log holds none (no turn yet, or the log is not loaded).\n *\n * WHY NOT `session.updatedAt`: the idle-cache advice (\"the prompt cache has\n * likely expired\") asks how long the PROVIDER has gone without a request,\n * and `updatedAt` moves on things that never reach a provider: a rename, a\n * context-mode switch (whose `context` event has `turnId: null`), a reload's\n * record save. Measured from `updatedAt`, one rename silenced the warning\n * for another hour while the cache stayed cold.\n */\nexport function lastTurnActivityAt(events: readonly VerseEvent[]): string | null {\n let latest: string | null = null;\n for (const e of events) {\n const turnActivity = e.type === 'usage' || e.type === 'turn-done' || e.type === 'cancelled' ||\n (e.type === 'context' && e.turnId !== null);\n if (turnActivity && typeof e.at === 'string' && (latest === null || e.at > latest)) latest = e.at;\n }\n return latest;\n}\n\nexport function forgetVerseSession(sessionId: string): void {\n const existed = sessions.delete(sessionId);\n heads.delete(sessionId);\n thinkingStats.delete(sessionId);\n for (const l of [...lifecycleListeners]) l({ kind: 'forget', sessionId });\n if (existed) notify(sessionId);\n}\n\n/** Test/logout hygiene. */\nexport function resetVerseStore(): void {\n sessions.clear();\n heads.clear();\n thinkingStats.clear();\n for (const l of [...lifecycleListeners]) l({ kind: 'reset' });\n notify(null);\n}\n\n/** Position of `seq` in a seq-sorted log (binary search). */\nfunction seqIndex(events: readonly VerseEvent[], seq: number): { found: boolean; index: number } {\n let lo = 0;\n let hi = events.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const s = events[mid]!.seq;\n if (s === seq) return { found: true, index: mid };\n if (s < seq) lo = mid + 1;\n else hi = mid;\n }\n return { found: false, index: lo };\n}\n\n/** Sorted by seq, one event per seq (first occurrence wins), non-finite seqs dropped. */\nfunction normalizeEvents(events: readonly VerseEvent[]): VerseEvent[] {\n let ascending = true;\n for (let i = 0; i < events.length; i += 1) {\n const seq = events[i]!.seq;\n if (!Number.isFinite(seq) || (i > 0 && seq <= events[i - 1]!.seq)) {\n ascending = false;\n break;\n }\n }\n if (ascending) return events.slice();\n const bySeq = new Map<number, VerseEvent>();\n for (const e of events) if (Number.isFinite(e.seq) && !bySeq.has(e.seq)) bySeq.set(e.seq, e);\n return [...bySeq.values()].sort((a, b) => a.seq - b.seq);\n}\n\n/**\n * Union of two logs by seq, existing events winning. Returns `existing`\n * itself when `incoming` adds nothing, so a refetch that brought no news is\n * not a change. Linear in the two lengths (both are seq-sorted).\n */\nfunction mergeEvents(existing: VerseEvent[], incomingRaw: readonly VerseEvent[]): VerseEvent[] {\n if (incomingRaw.length === 0) return existing;\n const incoming = normalizeEvents(incomingRaw);\n if (incoming.length === 0) return existing;\n if (existing.length === 0) return incoming;\n if (incoming[0]!.seq > existing[existing.length - 1]!.seq) return existing.concat(incoming);\n const out: VerseEvent[] = [];\n let added = 0;\n let i = 0;\n let j = 0;\n while (i < existing.length || j < incoming.length) {\n const a = existing[i];\n const b = incoming[j];\n if (b === undefined || (a !== undefined && a.seq < b.seq)) {\n out.push(a!);\n i += 1;\n } else if (a === undefined || b.seq < a.seq) {\n out.push(b);\n added += 1;\n j += 1;\n } else {\n out.push(a);\n i += 1;\n j += 1;\n }\n }\n return added === 0 ? existing : out;\n}\n\nfunction shallowEqualValue(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object' || Array.isArray(a) !== Array.isArray(b)) return false;\n const ka = Object.keys(a as object);\n const kb = Object.keys(b as object);\n if (ka.length !== kb.length) return false;\n for (const k of ka) {\n if (!Object.is((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k])) return false;\n }\n return true;\n}\n\n/** Same record, one level deep — a refetched but unchanged session must not re-render its chat. */\nfunction sameSession(a: VerseSession, b: VerseSession): boolean {\n if (a === b) return true;\n const ka = Object.keys(a) as Array<keyof VerseSession>;\n if (ka.length !== Object.keys(b).length) return false;\n return ka.every((k) => shallowEqualValue(a[k], b[k]));\n}\n\n\n/**\n * \"123k\" / \"1.2M\" — compact token counts for the meter and usage rows.\n *\n * The unit is chosen AFTER rounding: choosing it first printed 999,500–\n * 999,999 as \"1000k\" (and 999.5–999.9 as \"1000\"), a band a 1M Claude chat in\n * Expansive can actually reach.\n */\nexport function formatTokens(n: number | null | undefined): string {\n if (n === null || n === undefined || !Number.isFinite(n)) return '—';\n if (Math.round(n) < 1000) return String(Math.round(n));\n if (Math.round(n / 1000) < 1000) return `${Math.round(n / 1000)}k`;\n return `${(n / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`;\n}\n"],"mappings":"mDA+kBA,IAAa,EAAgB,CAAC,UAAW,MAAO,SAAU,OAAQ,QAAS,KAAK,EAyKnE,EAA2D,IAAI,IAAoB,CAN9F,iBACA,oBACA,WACA,QAG8F,CAAyB,EAOzH,SAAgB,EAAsB,EAAiD,CACrF,OAAO,EAA4B,IAAI,EAAM,IAAI,CACnD,CA+HA,IAAa,EAA4B,MAqF5B,EAA0B,KAa1B,EAAgC,CAC3C,0FACA,+HACA,GACA,qBACA,0CACA,6DACA,0EACA,kDACA,6CACA,2EACA,GACA,qEACF,CAAC,CAAC,KAAK;CAAI,EA4EE,EAAyB,MAQzB,EAA8B,OAG9B,EAA+B,KCh9BtC,EAA6B,CACjC,OAAQ,KACR,UAAW,KACX,SAAU,KACV,SAAU,KACV,OAAQ,KACR,cAAe,IACjB,EA6BM,EAA8C,CAClD,QAAS,KACT,OAAQ,CAAC,EACT,QAAS,EACT,OAAQ,GACR,UAAW,KACX,OAAQ,OACR,KAAM,CACR,EAEM,EAAW,IAAI,IACf,EAAQ,IAAI,IACZ,EAAgB,IAAI,IACpB,EAAY,IAAI,IAChB,EAAmB,IAAI,IAGvB,EAAqB,IAAI,IAE/B,SAAS,EAAO,EAAgC,CAC9C,GAAI,IAAc,KAAM,CACtB,IAAM,EAAS,EAAiB,IAAI,CAAS,EAC7C,GAAI,EAAQ,IAAK,IAAM,IAAK,CAAC,GAAG,CAAM,EAAG,EAAE,CAC7C,MACE,IAAK,IAAM,IAAO,CAAC,GAAG,EAAiB,OAAO,CAAC,EAAG,IAAK,IAAM,IAAK,CAAC,GAAG,CAAG,EAAG,EAAE,EAEhF,IAAK,IAAM,IAAK,CAAC,GAAG,CAAS,EAAG,EAAE,CACpC,CAGA,SAAgB,EAAoB,EAAkC,CAEpE,OADA,EAAU,IAAI,CAAQ,MACT,EAAU,OAAO,CAAQ,CACxC,CAGA,SAAgB,EAAsB,EAAmB,EAAkC,CACzF,IAAI,EAAM,EAAiB,IAAI,CAAS,EAMxC,OALK,IACH,EAAM,IAAI,IACV,EAAiB,IAAI,EAAW,CAAG,GAErC,EAAI,IAAI,CAAQ,MACH,CACX,IAAM,EAAU,EAAiB,IAAI,CAAS,EACzC,IACL,EAAQ,OAAO,CAAQ,EACnB,EAAQ,OAAS,GAAG,EAAiB,OAAO,CAAS,EAC3D,CACF,CAOA,SAAgB,EAA6B,EAA4D,CAEvG,OADA,EAAmB,IAAI,CAAQ,MAClB,EAAmB,OAAO,CAAQ,CACjD,CAEA,SAAS,EAAO,EAA4C,CAC1D,MAAO,CACL,UAAW,EAAM,UACjB,QAAS,EAAM,QACf,OAAQ,EAAM,OACd,QAAS,EAAM,QACf,OAAQ,EAAM,OACd,UAAW,EAAM,UACjB,OAAQ,EAAM,MAChB,CACF,CAEA,SAAS,EAAS,EAAsC,CACtD,IAAI,EAAI,EAAS,IAAI,CAAS,EAM9B,OALK,IACH,EAAI,CAAE,YAAW,GAAG,CAAM,EAC1B,EAAS,IAAI,EAAW,CAAC,EACzB,EAAM,IAAI,EAAW,EAAO,CAAC,CAAC,GAEzB,CACT,CAMA,SAAS,EAAO,EAAmB,EAAyB,EAA2B,CACrF,EAAS,IAAI,EAAW,CAAI,EAC5B,IAAM,EAAO,EAAM,IAAI,CAAS,GAE9B,CAAC,GAAQ,GAAc,EAAK,UAAY,EAAK,SAAW,EAAK,SAAW,EAAK,QAC7E,EAAK,YAAc,EAAK,WAAa,EAAK,SAAW,EAAK,SAE1D,EAAM,IAAI,EAAW,EAAO,CAAI,CAAC,CAErC,CAEA,SAAS,EAAM,EAAmB,EAAmC,EAAa,GAAa,CAE7F,EAAO,EAAW,CAAE,GADJ,EAAS,CACF,EAAS,GAAG,CAAM,EAAG,CAAU,EACtD,EAAO,CAAS,CAClB,CAEA,IAAM,EAA0B,CAAE,UAAW,GAAI,GAAG,CAAM,EACpD,EAA8B,EAAO,CAAI,EAG/C,SAAgB,EAAqB,EAA6C,CAEhF,OADK,EACE,EAAS,CAAS,EADF,CAEzB,CAGA,SAAgB,EAAoB,EAA4C,CAG9E,OAFK,GACL,EAAS,CAAS,EACX,EAAM,IAAI,CAAS,GAFH,CAGzB,CAGA,SAAgB,EAAa,EAA0C,CAErE,OADK,EACE,EAAS,CAAS,CAAC,CAAC,KADJ,CAEzB,CAMA,SAAgB,EAAsB,EAAuE,CAC3G,OAAO,EAAc,IAAI,CAAS,CACpC,CAOA,SAAgB,EAAiB,EAAmB,EAAuB,EAAqC,CAC9G,IAAM,EAAU,EAAS,CAAS,EAC5B,EAAS,EAAY,EAAQ,OAAQ,CAAM,EAC3C,EAAU,EAAc,EAAS,CAAM,EACvC,EAAc,EAAQ,SAAW,GAAY,EAAQ,QAAS,CAAO,EAAI,EAAQ,QAAU,EAC3F,EAAgB,IAAW,EAAQ,OACnC,EAAO,GAAiB,IAAgB,EAAQ,QAAU,EAAY,EAAQ,KAAM,EAAQ,CAAW,EAAI,EAAQ,MACpH,GAAiB,IAAgB,EAAQ,SAAW,GAAQ,QAAU,EAAQ,YAAc,MAAQ,IAAS,EAAQ,QAC1H,EAAO,EAAW,CAChB,GAAG,EACH,QAAS,EACT,OAAQ,EACR,QAAS,EAAO,OAAS,KAAK,IAAI,EAAQ,QAAS,EAAO,EAAO,OAAS,EAAE,CAAE,GAAG,EAAI,EAAQ,QAC7F,OAAQ,GACR,UAAW,KACX,MACF,EAAG,CAAa,EAChB,EAAO,CAAS,EAClB,CAEA,SAAgB,EAAkB,EAAmB,EAAuB,CAC1E,EAAM,EAAW,CAAE,UAAW,EAAS,OAAQ,EAAK,CAAC,CACvD,CAEA,SAAgB,EAAoB,EAAmB,EAAgC,CACjF,EAAS,CAAS,CAAC,CAAC,SAAW,GACnC,EAAM,EAAW,CAAE,QAAO,CAAC,CAC7B,CASA,SAAgB,EAAgB,EAAmB,EAAuB,EAAuB,CAE/F,EAAM,EAAW,CAAE,QAAS,EAAc,EAD1B,EAAS,CAC0B,CAAA,CAAQ,OAAQ,CAAM,CAAE,CAAC,CAC9E,CAWA,SAAgB,EAAc,EAAuB,EAA+B,EAA+B,CACjH,GAAI,EAAQ,SAAW,UAAW,OAAO,EACzC,IAAI,EAAY,GACZ,EAA4B,GAAU,KAC1C,GAAI,CAAC,EAAQ,CACX,IAAK,IAAM,KAAK,GACV,EAAE,OAAS,gBAAkB,EAAE,OAAS,kBAC1C,EAAY,EAAE,IACd,EAAa,EAAE,QAGnB,GAAI,IAAe,KAAM,OAAO,CAClC,CACA,IAAI,EAA4E,KAC5E,EAA2B,KAC/B,IAAK,IAAM,KAAK,EACV,EAAE,KAAO,IACT,EAAE,OAAS,SAAW,EAAE,SAAW,IAAY,EAAY,EAAE,UAC5D,EAAE,OAAS,aAAe,EAAE,OAAS,cAAgB,EAAE,SAAW,IAAY,EAAW,IAEhG,GAAI,CAAC,EAAU,OAAO,EACtB,IAAM,EAAS,EAAS,OAAS,aAAe,CAAC,EAAS,IAAM,IAAc,KAC9E,MAAO,CACL,GAAG,EACH,OAAQ,EAAS,QAAU,OAC3B,UAAW,EAAS,EAAY,KAChC,UAAW,EAAS,GAAK,EAAQ,UAAY,EAAS,GAAK,EAAQ,SACrE,CACF,CAGA,SAAgB,EAAsB,EAAmB,EAAsC,CAC7F,IAAM,EAAU,EAAS,CAAS,EAC7B,EAAQ,SAAW,EAAQ,QAAQ,SAAW,GACnD,EAAM,EAAW,CAAE,QAAS,CAAE,GAAG,EAAQ,QAAS,QAAO,CAAE,CAAC,CAC9D,CA0BA,SAAgB,EAAiB,EAAmB,EAAmD,CACrG,IAAM,EAAU,EAAS,CAAS,EAC9B,EAAuB,EAAQ,OAC/B,EAAS,GACT,EAAU,EAAQ,QAClB,EAAU,EAAQ,QAClB,EAAO,EAAQ,KACf,EAAU,EACV,EAAU,GACV,EAAa,GACb,EAAc,GACZ,EAAM,KAAK,IAAI,EAErB,IAAK,IAAM,KAAS,EAAU,CAC5B,GAAI,EAAsB,CAAK,EAAG,CAChC,IAAM,EAAO,EAAe,EAAM,EAAO,CAAG,EACxC,IAAS,IACX,EAAO,EACP,EAAc,IAEhB,QACF,CACA,GAAI,CAAC,OAAO,SAAS,EAAM,GAAG,EAAG,SACjC,IAAM,EAAO,EAAO,OAAS,EAAI,EAAO,EAAO,OAAS,EAAE,CAAE,IAAM,KAClE,GAAI,EAAM,IAAM,EACd,AAEE,KADA,EAAS,EAAO,MAAM,EACb,IAEX,EAAO,KAAK,CAAK,MACZ,CACL,IAAM,EAAK,EAAS,EAAQ,EAAM,GAAG,EACrC,GAAI,EAAG,MAAO,SACd,AAEE,KADA,EAAS,EAAO,MAAM,EACb,IAEX,EAAO,OAAO,EAAG,MAAO,EAAG,CAAK,CAClC,CACA,GAAW,EACX,EAAU,KAAK,IAAI,EAAS,EAAM,GAAG,EACjC,EAAM,OAAS,eAAc,EAAa,KAC1C,EAAM,OAAS,aAAe,EAAM,OAAS,aAAe,EAAM,OAAS,WAAS,EAAU,IAClG,AAAa,IAAU,EAAa,EAAS,CAAK,EAClD,IAAM,EAAW,EAAc,EAAM,EAAO,EAAK,CAAS,EACtD,IAAa,IAAM,EAAO,EAChC,CAKA,OAHI,IAAY,GAAK,CAAC,EAAoB,CAAE,QAAS,EAAG,QAAS,GAAO,YAAa,EAAM,GAC3F,EAAO,EAAW,CAAE,GAAG,EAAS,SAAQ,UAAS,UAAS,MAAK,EAAG,CAAU,EAC5E,EAAO,CAAS,EACT,CAAE,UAAS,UAAS,aAAY,EACzC,CAaA,SAAS,EAAa,EAAuB,EAAiC,CAC5E,OAAQ,EAAM,KAAd,CACE,IAAK,eACH,MAAO,CAAE,GAAG,EAAS,OAAQ,UAAW,UAAW,KAAM,UAAW,EAAM,EAAG,EAC/E,IAAK,QACH,MAAO,CAAE,GAAG,EAAS,MAAO,EAAgB,EAAS,EAAM,KAAK,EAAG,UAAW,EAAM,EAAG,EACzF,IAAK,UACH,MAAO,CAAE,GAAG,EAAS,MAAO,EAAoB,EAAS,CAAK,EAAG,UAAW,EAAM,EAAG,EACvF,IAAK,aAIH,MAAO,CAAE,GAAG,EAAS,iBAAkB,EAAQ,iBAAmB,GAAK,EAAG,UAAW,EAAM,EAAG,EAChG,IAAK,YACH,MAAO,CACL,GAAG,EACH,OAAQ,EAAM,GAAK,OAAS,EAAQ,UAAY,QAAU,OAC1D,UAAW,EAAQ,UAAY,EAC/B,gBAAiB,EAAM,iBAAmB,EAAQ,gBAClD,UAAW,EAAM,EACnB,EACF,IAAK,QACH,MAAO,CAAE,GAAG,EAAS,OAAQ,QAAS,UAAW,EAAM,QAAS,UAAW,EAAM,EAAG,EACtF,IAAK,YACH,MAAO,CAAE,GAAG,EAAS,OAAQ,OAAQ,UAAW,EAAM,EAAG,EAC3D,QACE,OAAO,CACX,CACF,CAEA,SAAS,EAAQ,EAAY,EAA0B,CACrD,IAAM,EAAS,KAAK,MAAM,CAAE,EAC5B,OAAO,OAAO,SAAS,CAAM,EAAI,EAAS,CAC5C,CAGA,SAAS,EAAY,EAAsB,EAAgB,EAAmC,CAE5F,OADI,EAAK,SAAW,EAAe,EAC5B,CAAE,GAAG,EAAY,SAAQ,YAAW,cAAe,EAAK,aAAc,CAC/E,CAEA,SAAS,EAAe,EAAsB,EAA4B,EAA6B,CAGrG,GAAI,EAAM,SAAW,MAAQ,EAAM,SAAW,EAAK,cAAe,OAAO,EACzE,IAAM,EAAO,EAAM,SAAW,KAA8C,EAAvC,EAAY,EAAM,EAAM,OAAQ,CAAG,EACxE,OAAQ,EAAM,KAAd,CACE,IAAK,iBAAkB,CACrB,GAAI,EAAM,KAAK,SAAW,EAAG,OAAO,EACpC,IAAM,EAAQ,EAAK,UAAY,EAAK,SAAS,SAAW,EAAM,OAC1D,CAAE,GAAG,EAAK,SAAU,KAAM,EAAK,SAAS,KAAO,EAAM,IAAK,EAC1D,CAAE,OAAQ,EAAM,OAAQ,KAAM,EAAM,KAAM,UAAW,EAAK,gBAAiB,IAAK,EACpF,MAAO,CAAE,GAAG,EAAM,SAAU,EAAO,OAAQ,IAAK,CAClD,CACA,IAAK,oBAAqB,CACxB,IAAM,EAAQ,EAAK,UAAY,EAAK,SAAS,SAAW,EAAM,OAC1D,CAAE,GAAG,EAAK,SAAU,gBAAiB,EAAM,eAAgB,EAC3D,CAAE,OAAQ,EAAM,OAAQ,KAAM,GAAI,UAAW,EAAK,gBAAiB,EAAM,eAAgB,EAC7F,MAAO,CAAE,GAAG,EAAM,SAAU,CAAM,CACpC,CACA,IAAK,WACH,MAAO,CACL,GAAG,EACH,SAAU,CACR,MAAO,EAAM,MACb,KAAM,OAAO,EAAM,MAAS,UAAY,EAAM,KAAK,OAAS,EAAI,EAAM,KAAO,KAC7E,UAAW,EAAM,UACjB,UAAW,EAAM,WAAa,KAC9B,UAAW,EAAM,WAAa,KAC9B,WAAY,CACd,CACF,EACF,IAAK,SACH,MAAO,CAAE,GAAG,EAAM,OAAQ,CAAE,KAAM,EAAM,KAAM,QAAS,EAAM,QAAS,GAAI,EAAM,GAAI,WAAY,CAAI,CAAE,EACxG,QACE,OAAO,CACX,CACF,CAGA,SAAS,EAAc,EAAsB,EAAmB,EAAa,EAAmC,CAC9G,OAAQ,EAAM,KAAd,CACE,IAAK,eAEH,OADI,EAAM,SAAW,EAAK,cAAsB,EACzC,EAAK,SAAW,EAAM,OACzB,CAAE,GAAG,EAAM,UAAW,EAAQ,EAAM,GAAI,EAAK,WAAa,CAAG,CAAE,EAC/D,CAAE,GAAG,EAAY,OAAQ,EAAM,OAAQ,UAAW,EAAQ,EAAM,GAAI,CAAG,EAAG,cAAe,EAAK,aAAc,EAClH,IAAK,WAAY,CAKf,IAAM,EAAW,EAAK,UAAY,EAAK,SAAS,SAAW,EAAM,OAAS,EAAK,SAAW,KAC1F,GAAI,EAAU,CACZ,IAAI,EAAQ,EAAc,IAAI,CAAS,EAClC,IACH,EAAQ,IAAI,IACZ,EAAc,IAAI,EAAW,CAAK,GAEpC,EAAM,IAAI,EAAM,IAAK,CACnB,WAAY,OAAO,EAAM,YAAe,SAAW,EAAM,WAAa,KAAK,IAAI,EAAG,EAAM,EAAS,SAAS,EAC1G,gBAAiB,EAAS,eAC5B,CAAC,CACH,CACA,OAAO,GAAY,EAAK,OAAS,CAAE,GAAG,EAAM,SAAU,EAAW,KAAO,EAAK,SAAU,OAAQ,IAAK,EAAI,CAC1G,CACA,IAAK,aACL,IAAK,oBACL,IAAK,WACL,IAAK,cAEH,OAAO,EAAK,SAAW,EAAM,SAAW,EAAK,QAAU,EAAK,SAAW,MAAQ,CAAE,GAAG,EAAM,OAAQ,IAAK,EAAI,EAC7G,IAAK,YACL,IAAK,YACH,MAAO,CAAE,GAAG,EAAY,cAAe,EAAM,MAAO,EACtD,QACE,OAAO,CACX,CACF,CAOA,SAAS,EAAY,EAAsB,EAA+B,EAA8C,CACtH,IAAI,EAAiE,KACjE,EAA+B,EAAK,cACxC,IAAK,IAAI,EAAI,EAAO,OAAS,EAAG,GAAK,EAAG,IAAQ,CAC9C,IAAM,EAAI,EAAO,GACjB,GAAI,EAAE,OAAS,aAAe,EAAE,OAAS,YAAa,CACpD,EAAgB,EAAE,OAClB,KACF,CACA,GAAI,EAAE,OAAS,eAAgB,CAC7B,EAAW,EACX,KACF,CACF,CASA,OARK,EAMD,GAAW,EAAQ,SAAW,UAAkB,EAAK,SAAW,KAAO,EAAO,CAAE,GAAG,EAAY,cAAe,EAAK,aAAc,EACjI,EAAK,SAAW,EAAS,OAAe,EACrC,CAAE,GAAG,EAAY,OAAQ,EAAS,OAAQ,UAAW,EAAQ,EAAS,GAAI,KAAK,IAAI,CAAC,EAAG,cAAe,EAAK,aAAc,EAP1H,EAAK,SAAW,MAAQ,EAAK,SAAW,EAAsB,CAAE,GAAG,EAAY,eAAc,EAG1F,EAAK,gBAAkB,EAAgB,EAAO,CAAE,GAAG,EAAM,eAAc,CAKlF,CAcA,SAAgB,EAAgB,EAAuB,EAA+B,CACpF,IAAM,EAAO,EAAQ,MACf,EAAmB,CACvB,GAAG,EACH,YAAa,EAAK,YAAc,EAAM,YACtC,aAAc,EAAK,aAAe,EAAM,aACxC,gBAAiB,EAAK,gBAAkB,EAAM,gBAC9C,oBAAqB,EAAK,oBAAsB,EAAM,oBACtD,cAAe,EAAM,aACvB,EACM,EAAc,OAAO,EAAM,eAAkB,UAAY,EAAM,cAAgB,EAAI,EAAM,cAAgB,KACzG,EAAU,EAAM,sBAAwB,IAAA,IAAa,EAAQ,SAAW,QAS9E,OARI,IAAgB,MAAQ,IAAS,EAAK,cAAgB,GACtD,EAAM,sBAAwB,IAAA,KAAW,EAAK,oBAAsB,EAAM,qBAC1E,EAAM,gBAAkB,IAAA,KAAW,EAAK,cAAgB,EAAM,eAI9D,EAAM,qBAAuB,GAAO,EAAK,mBAAqB,GACzD,EAAM,sBAAwB,IAAA,IAAW,OAAO,EAAK,mBACvD,CACT,CAGA,IAAM,EAAsC,IAAI,IAAuB,CAAC,UAAW,mBAAoB,cAAe,aAAc,UAAU,CAAC,EAE/I,SAAS,EAAkB,EAA0C,CACnE,OAAO,OAAO,GAAU,UAAY,EAAe,IAAI,CAAK,EAAK,EAA8B,IACjG,CAiBA,SAAgB,EACd,EACA,EACY,CACZ,IAAM,EAAO,EAAQ,MACf,EAAmB,CACvB,GAAG,EACH,cAAe,KAAK,IAAI,EAAG,KAAK,MAAM,EAAM,aAAa,CAAC,EAC1D,mBAAoB,EAAM,KAC5B,EACM,EAAS,OAAO,EAAM,eAAkB,UAAY,EAAM,cAAgB,EAAI,EAAM,cAAgB,KACpG,EAAS,EAAkB,EAAM,mBAAmB,EAoB1D,OAfI,IAAW,MAAS,EAAQ,SAAW,SAAW,IAAW,MAC3D,EAAM,gBAAkB,IAAA,IAAa,EAAM,gBAAkB,OAAM,EAAK,cAAgB,EAAM,eAC3F,IAET,EAAK,cAAgB,EACrB,EAAK,oBAAsB,GAAU,UACjC,EAAM,gBAAkB,IAAA,GAEjB,IAAW,EAAK,gBACzB,EAAK,cAAgB,EAAuB,CAC1C,OAAQ,EAAQ,OAChB,cAAe,EACf,OAAQ,EAAK,cAAgB,CAAE,cAAe,EAAK,cAAe,cAAe,EAAK,eAAiB,IAAK,EAAI,IAClH,CAAC,GAND,EAAK,cAAgB,EAAM,cAQtB,EACT,CAcA,SAAgB,EAAmB,EAA8C,CAC/E,IAAI,EAAwB,KAC5B,IAAK,IAAM,KAAK,GACO,EAAE,OAAS,SAAW,EAAE,OAAS,aAAe,EAAE,OAAS,aAC7E,EAAE,OAAS,WAAa,EAAE,SAAW,OACpB,OAAO,EAAE,IAAO,WAAa,IAAW,MAAQ,EAAE,GAAK,KAAS,EAAS,EAAE,IAEjG,OAAO,CACT,CAEA,SAAgB,EAAmB,EAAyB,CAC1D,IAAM,EAAU,EAAS,OAAO,CAAS,EACzC,EAAM,OAAO,CAAS,EACtB,EAAc,OAAO,CAAS,EAC9B,IAAK,IAAM,IAAK,CAAC,GAAG,CAAkB,EAAG,EAAE,CAAE,KAAM,SAAU,WAAU,CAAC,EACpE,GAAS,EAAO,CAAS,CAC/B,CAYA,SAAS,EAAS,EAA+B,EAAgD,CAC/F,IAAI,EAAK,EACL,EAAK,EAAO,OAChB,KAAO,EAAK,GAAI,CACd,IAAM,EAAO,EAAK,IAAQ,EACpB,EAAI,EAAO,EAAI,CAAE,IACvB,GAAI,IAAM,EAAK,MAAO,CAAE,MAAO,GAAM,MAAO,CAAI,EAC5C,EAAI,EAAK,EAAK,EAAM,EACnB,EAAK,CACZ,CACA,MAAO,CAAE,MAAO,GAAO,MAAO,CAAG,CACnC,CAGA,SAAS,EAAgB,EAA6C,CACpE,IAAI,EAAY,GAChB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAG,CACzC,IAAM,EAAM,EAAO,EAAE,CAAE,IACvB,GAAI,CAAC,OAAO,SAAS,CAAG,GAAM,EAAI,GAAK,GAAO,EAAO,EAAI,EAAE,CAAE,IAAM,CACjE,EAAY,GACZ,KACF,CACF,CACA,GAAI,EAAW,OAAO,EAAO,MAAM,EACnC,IAAM,EAAQ,IAAI,IAClB,IAAK,IAAM,KAAK,EAAY,OAAO,SAAS,EAAE,GAAG,GAAK,CAAC,EAAM,IAAI,EAAE,GAAG,GAAG,EAAM,IAAI,EAAE,IAAK,CAAC,EAC3F,MAAO,CAAC,GAAG,EAAM,OAAO,CAAC,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,IAAM,EAAE,GAAG,CACzD,CAOA,SAAS,EAAY,EAAwB,EAAkD,CAC7F,GAAI,EAAY,SAAW,EAAG,OAAO,EACrC,IAAM,EAAW,EAAgB,CAAW,EAC5C,GAAI,EAAS,SAAW,EAAG,OAAO,EAClC,GAAI,EAAS,SAAW,EAAG,OAAO,EAClC,GAAI,EAAS,EAAE,CAAE,IAAM,EAAS,EAAS,OAAS,EAAE,CAAE,IAAK,OAAO,EAAS,OAAO,CAAQ,EAC1F,IAAM,EAAoB,CAAC,EACvB,EAAQ,EACR,EAAI,EACJ,EAAI,EACR,KAAO,EAAI,EAAS,QAAU,EAAI,EAAS,QAAQ,CACjD,IAAM,EAAI,EAAS,GACb,EAAI,EAAS,GACf,IAAM,IAAA,IAAc,IAAM,IAAA,IAAa,EAAE,IAAM,EAAE,KACnD,EAAI,KAAK,CAAE,EACX,GAAK,GACI,IAAM,IAAA,IAAa,EAAE,IAAM,EAAE,KACtC,EAAI,KAAK,CAAC,EACV,GAAS,EACT,GAAK,IAEL,EAAI,KAAK,CAAC,EACV,GAAK,EACL,GAAK,EAET,CACA,OAAO,IAAU,EAAI,EAAW,CAClC,CAEA,SAAS,GAAkB,EAAY,EAAqB,CAC1D,GAAI,OAAO,GAAG,EAAG,CAAC,EAAG,MAAO,GAC5B,GAAI,CAAC,GAAK,CAAC,GAAK,OAAO,GAAM,UAAY,OAAO,GAAM,UAAY,MAAM,QAAQ,CAAC,IAAM,MAAM,QAAQ,CAAC,EAAG,MAAO,GAChH,IAAM,EAAK,OAAO,KAAK,CAAW,EAC5B,EAAK,OAAO,KAAK,CAAW,EAClC,GAAI,EAAG,SAAW,EAAG,OAAQ,MAAO,GACpC,IAAK,IAAM,KAAK,EACd,GAAI,CAAC,OAAO,GAAI,EAA8B,GAAK,EAA8B,EAAE,EAAG,MAAO,GAE/F,MAAO,EACT,CAGA,SAAS,GAAY,EAAiB,EAA0B,CAC9D,GAAI,IAAM,EAAG,MAAO,GACpB,IAAM,EAAK,OAAO,KAAK,CAAC,EAExB,OADI,EAAG,SAAW,OAAO,KAAK,CAAC,CAAC,CAAC,QAC1B,EAAG,MAAO,GAAM,GAAkB,EAAE,GAAI,EAAE,EAAE,CAAC,CACtD,CAUA,SAAgB,GAAa,EAAsC,CAIjE,OAHI,GAAM,MAA2B,CAAC,OAAO,SAAS,CAAC,EAAU,IAC7D,KAAK,MAAM,CAAC,EAAI,IAAa,OAAO,KAAK,MAAM,CAAC,CAAC,EACjD,KAAK,MAAM,EAAI,GAAI,EAAI,IAAa,GAAG,KAAK,MAAM,EAAI,GAAI,EAAE,GACzD,IAAI,EAAI,IAAA,CAAW,QAAQ,CAAC,CAAC,CAAC,QAAQ,OAAQ,EAAE,EAAE,EAC3D"}
@@ -1,3 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AppsSection-Ddu_RCQE.js","assets/index-BVxOlseC.js","assets/global-oEl9Qj8u.js","assets/global-BlI2BYa1.css","assets/Dialog-BS9Dxi3G.js","assets/focus-trap-HtcoXeEq.js","assets/Dialog-tQ8kFulD.css","assets/MutationTokenDialog-mxrw0Yvy.js","assets/MutationTokenDialog-CrfJ0PdD.css","assets/workbench-types-DzN-5trg.js","assets/dock-store-B2vlxqPu.js","assets/viewport-nTubFCwf.js","assets/section-visibility-Yctxu-if.js","assets/VerseConsoleApp-DTjDpGCd.js","assets/RouteErrorBoundary-DYtwc2ja.js","assets/RouteErrorBoundary-DdxhTdZ6.css","assets/command-catalog-Bb_PxC5w.js","assets/guarded-action-OzqcPjmh.js","assets/preloaded-Cwf9rsnZ.js","assets/composer-keys-su0g1h9o.js","assets/command-bus-xYZoK9nQ.js","assets/icon-base-DvM1eI5V.js","assets/VerseConsoleApp-Cxqr-3qc.css","assets/CapacityStrip-qReu0RaF.js","assets/ProviderLogo-C5mBg8i0.js","assets/format-BkWdOQDL.js","assets/format-DKrNjdwD.js","assets/verse-model-DyCwZF-M.js","assets/compaction-point-VL9oegWS.js","assets/Tag-DH15CUJp.js","assets/Tag-D0rAEciH.css","assets/budget-queries-V3wcJVK_.js","assets/useSeatsRefresh-DgFy-Ph4.js","assets/capacity-strip-model-BDbmQ3e3.js","assets/seat-subscription-B-f9ViU2.js","assets/context-model-BC-HOmPo.js","assets/verse-store-b_Lek_c5.js","assets/CapacityStrip-Cc28CyWk.css","assets/Button-HHXgoXGr.js","assets/Button-DrnKsvnQ.css","assets/icons-DEQa2rTx.js","assets/use-token-gate-DqrSXdX6.js","assets/slots-BhP8b8tf.js","assets/EmptyState-BqR5YAmf.js","assets/EmptyState-B3YUPGaa.css","assets/Switch-iveI-8NY.js","assets/Switch-CU044EaE.css","assets/BudgetControl-TL3FeZ1D.js","assets/Segmented-BE53BenI.js","assets/Segmented-BnjHbKzH.css","assets/Slider-BypUWjpH.js","assets/Slider-AgAdMFpo.css","assets/BudgetControl-CmG1JOWK.css","assets/Select-BwSGkHCS.js","assets/Select-DSu5ghId.css","assets/Input-B5dmGLZx.js","assets/Input-BQ3PR065.css","assets/Sheet-DVP2Mgy6.js","assets/Sheet-ChxlNivX.css","assets/apps-model-CDFvmsQV.js","assets/AppsSection-D88QlDpG.css","assets/AutonomySection-DuQkHGD_.js","assets/Advanced-D_eS4472.js","assets/Epistemic-dhvDTwAZ.js","assets/Epistemic-OaGnEJkn.css","assets/StatusBadge-HCZp416_.js","assets/StatusBadge-BVi0IBb9.css","assets/ConfirmDialog-DLrLKM0c.js","assets/ConfirmDialog-Dx15x0Ho.css","assets/RefreshIndicator-CMwSCiz5.js","assets/RefreshIndicator-CVv22U_j.css","assets/workspace-model-CbhdPhei.js","assets/Meter-DNeocJnD.js","assets/Meter-CYt9KpM0.css","assets/control-queries-CmgBgKV7.js","assets/mutations-C5S7okTW.js","assets/use-ticker-D9yi49my.js","assets/fleet-model-7i-FbzKI.js","assets/fleet-queries-DzGoqq8X.js","assets/overnight-queries-DGm3qL1b.js","assets/use-guarded-action-Cf4MeNBa.js","assets/Advanced-Bywv_iYR.css","assets/ChatSection-DZuZfTaC.js","assets/ChatSection-C9obUsLL.css","assets/CommandSection-rIeMK5Ch.js","assets/useChartWidth-owwodNVz.js","assets/colors-BZ3a9wFf.js","assets/useChartWidth-BW6SwYIK.css","assets/Swimlane-DGcIcnNA.js","assets/Swimlane-o6_Zsrnr.css","assets/BurnDown-sFXkUGdB.js","assets/BurnDown-BuLMsJMx.css","assets/types-D5XOdyw8.js","assets/cloud.module-ascz58c3.js","assets/cloud-C1KPNRHC.css","assets/cloud-queries-Br_r0UAz.js","assets/nav-NVtnuG7A.js","assets/surface-data-DTI7LjvH.js","assets/actions-tWevL9HV.js","assets/actions-Ddfg2iLl.css","assets/dark-since-DOMvtB_G.js","assets/Surface-B75lVfzm.js","assets/Surface-DMca0gAe.css","assets/live-model-bmsJgnYD.js","assets/LeaderCard-DZrmjDbI.js","assets/NeedsYouList-CzDZbzRD.js","assets/NeedsYouList-CruOt4JT.css","assets/CloudBudgetForm-DJbiT1wi.js","assets/FleetSection-ViAb549H.js","assets/Funnel-D2u7Wlzc.js","assets/FleetSection-CFiWeOEs.css","assets/GrowthSection-C5aDPs8B.js","assets/queries-CYisSAAB.js","assets/AreaTrend-CDxrQU4b.js","assets/StepBand-Duz1PUes.js","assets/StepBand-Dz_9Kryb.css","assets/calendar-day-BxBYHfX1.js","assets/McpSection-DZkiOuoq.js","assets/MindSection-BvJXFjyX.js","assets/MatrixHeatmap-BgEUs2Eu.js","assets/MatrixHeatmap-Bmnvo1l1.css","assets/mind.module-gTGAuJt1.js","assets/mind-B9PzLcl0.css","assets/SettingsSection-wGnpWeGW.js","assets/reasoning-pref-BUcFF_5G.js","assets/primitives-DyCKYSBG.js","assets/ShortcutsOverlay-BcIcoClX.js","assets/ShortcutsOverlay-QSjxmW0a.css","assets/SettingsSection-B8yD34ZM.css","assets/UsageSection-CqxRGJ40.js","assets/charts-CfAjuKMv.js","assets/charts-uSX6xVYN.css","assets/context-model-CIBmSugV.js","assets/usage-queries-BZuAANlI.js","assets/local-model-DFyjKX0b.js","assets/UsageSection-DsB-Jqyl.css"])))=>i.map(i=>d[i]);
2
- import{t as e}from"./index-BVxOlseC.js";import{r as t,x as n}from"./workbench-types-DzN-5trg.js";import{f as r,i,v as a,y as o}from"./dock-store-B2vlxqPu.js";Object.freeze({entries:Object.freeze([]),index:-1});function s(e,t){return!!e&&e.section===t.section&&(e.section!==`chat`||e.sessionId===t.sessionId)}function c(e,t){let n={section:t.section,sessionId:t.section===`chat`?t.sessionId:null},r=e.entries[e.index];if(s(r,n))return e;if(r&&r.section===`chat`&&r.sessionId===null&&n.section===`chat`){let t=[...e.entries.slice(0,e.index),n];return{entries:t,index:t.length-1}}let i=[...e.entries.slice(0,e.index+1),n].slice(-50);return{entries:i,index:i.length-1}}function l(e,t){let n=e.index+t,r=e.entries[n];return r?{history:{entries:e.entries,index:n},entry:r}:null}var u=1500;function d(e,t,n,r,i=u){let a=!t||r-t.at>i,o=a?e:t.order;if(o.length<2)return null;let s=((a?0:t.position)+n+o.length)%o.length;return{sessionId:o[s],cycle:{order:o,position:s,at:r}}}function f(e,t,n=10){return[t,...e.filter(e=>e!==t)].slice(0,n)}var p=Object.fromEntries(Object.entries(Object.assign({"../sections/AppsSection.tsx":()=>e(()=>import(`./AppsSection-Ddu_RCQE.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60])),"../sections/AutonomySection.tsx":()=>e(()=>import(`./AutonomySection-DuQkHGD_.js`),__vite__mapDeps([61,1,62,2,3,63,64,65,66,7,4,5,6,8,67,68,69,70,13,14,15,16,9,17,18,11,19,20,12,21,22,48,49,25,26,27,28,71,72,73,53,40,54,74,75,76,77,78,79,80,81])),"../sections/ChatSection.tsx":()=>e(()=>import(`./ChatSection-DZuZfTaC.js`).then(e=>e.t),__vite__mapDeps([82,1,2,3,14,15,16,18,10,11,20,12,13,9,17,19,21,22,36,28,38,39,83])),"../sections/CommandSection.tsx":()=>e(()=>import(`./CommandSection-rIeMK5Ch.js`),__vite__mapDeps([84,1,2,3,5,4,6,65,66,69,70,85,86,26,24,87,88,89,90,91,16,11,12,13,14,15,9,17,18,19,20,21,22,27,28,48,49,25,29,30,31,38,39,40,72,73,45,46,47,7,8,33,34,35,36,41,50,51,52,92,93,94,95,76,80,74,75,96,97,55,56,57,58,98,99,100,101,102,103,104,105,106,107])),"../sections/FleetSection.tsx":()=>e(()=>import(`./FleetSection-ViAb549H.js`),__vite__mapDeps([108,1,2,3,69,70,88,85,86,26,24,87,89,109,11,12,25,13,14,15,16,9,17,18,19,20,21,22,27,28,29,30,31,38,39,40,93,35,36,92,94,95,76,79,74,75,96,97,98,4,5,6,7,8,80,99,100,101,102,103,110])),"../sections/GrowthSection.tsx":()=>e(()=>import(`./GrowthSection-C5aDPs8B.js`),__vite__mapDeps([111,1,2,3,112,69,70,86,26,113,85,24,87,109,114,115,12,76,97,13,14,15,16,9,17,18,11,19,20,21,22,74,75,116,100,101,102])),"../sections/McpSection.tsx":()=>e(()=>import(`./McpSection-DZkiOuoq.js`),__vite__mapDeps([117,1,0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60])),"../sections/MindSection.tsx":()=>e(()=>import(`./MindSection-BvJXFjyX.js`),__vite__mapDeps([118,1,2,3,69,70,113,85,86,26,24,87,119,120,11,12,13,14,15,16,9,17,18,19,20,21,22,25,27,28,96,97,74,75,121,71,116,122,98,4,5,6,7,8,38,39,80,99,101,102,104,76])),"../sections/SettingsSection.tsx":()=>e(()=>import(`./SettingsSection-wGnpWeGW.js`),__vite__mapDeps([123,1,2,3,4,5,6,65,66,7,8,14,15,16,13,9,17,18,11,19,20,12,21,22,48,49,36,28,29,30,35,27,25,26,38,39,40,124,41,72,73,50,51,45,46,53,54,55,56,125,63,64,69,70,43,44,57,58,126,127,128])),"../sections/UsageSection.tsx":()=>e(()=>import(`./UsageSection-CqxRGJ40.js`),__vite__mapDeps([129,1,2,3,63,64,65,66,112,85,86,26,24,87,130,88,89,90,91,113,109,114,115,119,120,131,12,13,14,15,16,9,17,18,11,19,20,21,22,27,28,36,25,132,23,29,30,31,32,33,34,35,37,40,72,73,92,93,94,95,77,116,133,134,98,4,5,6,7,8,38,39,80,74,75,99,107,45,46,55,56,135]))})).map(([e,t])=>[e.replace(/^\.\.\//,`./`),t]));function m(e,t=p){return t[`./sections/${e}.tsx`]}var h=[{id:`command`,label:`Command`,module:`CommandSection`,placement:`rail`,blurb:`What the fleet did, what needs you, and the autonomy switch — the morning read.`},{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.`},{id:`growth`,label:`Growth`,module:`GrowthSection`,placement:`rail`,blurb:`Is the fleet getting better? Merges, cost per merge and the experiments behind them.`},{id:`mind`,label:`Mind`,module:`MindSection`,placement:`rail`,blurb:`The Leader's memos, what came of each move, and what the reasoning shows.`},{id:`chat`,label:`Chat`,module:`ChatSection`,placement:`rail`,blurb:`Talk to a seat. Chats are grouped by project and resume where they stopped.`},{id:`settings`,label:`Settings`,module:`SettingsSection`,placement:`tray`,blurb:`Theme, chat, desktop and keyboard — and this tour again.`},{id:`apps`,label:`Apps & Accounts`,module:`AppsSection`,placement:`tray`,blurb:`Seats, terminal agents, local models and MCP servers in one list.`},{id:`usage`,label:`Usage`,module:`UsageSection`,placement:`tray`,blurb:`Which account you can actually use right now, and what it costs.`}],g=h.filter(e=>e.placement===`rail`);h.filter(e=>e.placement===`tray`);var _=new Set(h.map(e=>e.id));function v(e){return h.find(t=>t.id===e)}function y(e,t=e=>m(e)!==void 0){let n=v(e);for(let e of[n.module,...n.fallbackModules??[]])if(t(e))return e;return null}var b=[`all`,...t],x={min:240,max:360,def:264},S={min:260,max:480,def:320},C={section:`chat`,railExpanded:!1,sidebarWidth:x.def,sidebarCollapsed:!1,resourcesOpen:!0,resourcesWidth:S.def,recentActions:[],recentChats:[],mounted:[`chat`],overlay:null,needsYouSplit:`all`,needsYouFocus:null,history:{entries:[{section:`chat`,sessionId:null}],index:0},activeSessionId:null,command:null,announceChatMoved:!1};function w(e,t){let n=Number(e);return!Number.isFinite(n)||n<=0?t.def:Math.max(t.min,Math.min(t.max,Math.round(n)))}function ee(e){let t=new Date(e),n=e=>String(e).padStart(2,`0`);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())}`}function te(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function T(e,t,n){if(!Array.isArray(e))return[];let r=[];for(let i of e)if(typeof i==`string`&&n.test(i)&&!r.includes(i)&&r.push(i),r.length>=t)break;return r}function E(e){try{let t=localStorage.getItem(e);if(!t)return null;let n=JSON.parse(t);return te(n)?n:null}catch{return null}}var D=null,O=!1;function k(e={}){let t=e.now??Date.now(),r=e.landed??(e=>m(e)!==void 0),i=E(a),s=i?null:E(o),c=i??s??{},l=C.section,u=null,d=n(c.section);d&&_.has(d.section)&&(l=d.section,d.openNeedsYou&&!i&&(u=`needs-you`)),D=i&&typeof i.lastLaunchDay==`string`?i.lastLaunchDay:null,O=i?i.chatMovedAnnounced===!0:!1;let f=ee(t);return D!==f&&y(`command`,r)!==null&&(l=`command`),D=f,{...C,section:l,railExpanded:c.railExpanded===!0,sidebarWidth:w(c.sidebarWidth,x),sidebarCollapsed:c.sidebarCollapsed===!0,resourcesOpen:c.resourcesOpen!==!1,resourcesWidth:w(c.resourcesWidth,S),recentActions:T(c.recentActions,5,/^[a-z][a-z0-9.-]{0,63}$/),recentChats:T(c.recentChats,10,/^[\w.-]{1,200}$/),mounted:[l],overlay:u,history:{entries:[{section:l,sessionId:null}],index:0},announceChatMoved:s!==null&&!O}}var A=C,j=0,M=null,N=new Set;function P(e){let t={version:3,section:e.section,railExpanded:e.railExpanded,sidebarWidth:e.sidebarWidth,sidebarCollapsed:e.sidebarCollapsed,resourcesOpen:e.resourcesOpen,resourcesWidth:e.resourcesWidth,recentActions:[...e.recentActions],recentChats:[...e.recentChats],lastLaunchDay:D,chatMovedAnnounced:O,dock:i()};try{localStorage.setItem(a,JSON.stringify(t))}catch{}}var ne=[`section`,`railExpanded`,`sidebarWidth`,`sidebarCollapsed`,`resourcesOpen`,`resourcesWidth`,`recentActions`,`recentChats`];function F(e){let t={...A,...e};if(Object.keys(e).every(e=>Object.is(A[e],t[e])))return;let n=ne.some(e=>!Object.is(A[e],t[e]));A=t,n&&P(t);for(let e of[...N])e()}function I(){A=k(),P(A)}I();var L=``;r(()=>{let e=``;try{e=JSON.stringify(i())}catch{e=``}e!==L&&(L=e,P(A))});function R(e){return N.add(e),()=>N.delete(e)}function re(){return A}function ie(e,t){let n=t===`chat`||e.includes(`chat`),r=e.filter(e=>e!==`chat`&&e!==t),i=(t===`chat`?r:[t,...r]).slice(0,3),a=t===`chat`?[`chat`,...i]:i;return n&&!a.includes(`chat`)&&a.push(`chat`),a}function z(e,t){if(!_.has(e))return;let n=t.history?c(A.history,{section:e,sessionId:e===`chat`?A.activeSessionId:null}):A.history;F({section:e,mounted:ie(A.mounted,e),history:n})}var B=`ashlr:verse-anchor`;function V(e,t=null){if(z(e,{history:!0}),!t||typeof window>`u`)return;let n={section:e,anchor:t};window.dispatchEvent(new CustomEvent(B,{detail:n}))}function H(e){let t=l(A.history,e);if(!t)return!1;F({history:t.history}),z(t.entry.section,{history:!1});let n=t.entry.sessionId;return t.entry.section===`chat`&&n&&n!==A.activeSessionId&&(K(`open-session`,{sessionId:n},!1),F({activeSessionId:n,recentChats:f(A.recentChats,n)})),!0}function U(e){e!==A.activeSessionId&&F({activeSessionId:e,recentChats:e?f(A.recentChats,e):A.recentChats,history:e&&A.section===`chat`?c(A.history,{section:`chat`,sessionId:e}):A.history})}function W(e,t=Date.now()){let n=d(A.activeSessionId?f(A.recentChats,A.activeSessionId):[...A.recentChats],M,e,t);return n?(M=n.cycle,J(n.sessionId),n.sessionId):null}var G=`ashlr.verse.selected.v1`;function K(e,t,n){j+=1;let r={name:e,nonce:j,...t};n&&A.section!==`chat`&&z(`chat`,{history:!0}),F({command:r})}function q(e,t={}){K(e,t,!0)}function J(e){if(/^[\w.-]{1,200}$/.test(e)){if(!A.mounted.includes(`chat`))try{localStorage.setItem(G,e)}catch{}K(`open-session`,{sessionId:e},!0),U(e)}}function ae(){F({command:null})}function oe(e){F({overlay:e})}function se(){F({overlay:null,needsYouFocus:null})}function ce(e){F({overlay:A.overlay===e?null:e,needsYouFocus:null})}function le(e={}){F({overlay:`needs-you`,needsYouSplit:e.split??A.needsYouSplit,needsYouFocus:e.focusId??null})}function ue(e){b.includes(e)&&F({needsYouSplit:e})}function de(){F({railExpanded:!A.railExpanded})}function fe(e){F({sidebarCollapsed:e})}function Y(e){/^[a-z][a-z0-9.-]{0,63}$/.test(e)&&F({recentActions:[e,...A.recentActions.filter(t=>t!==e)].slice(0,5)})}function pe(){O=!0,A.announceChatMoved&&F({announceChatMoved:!1}),P(A)}var X=`ashlr.verse.seats.v1`,me=50,Z=null;function Q(){if(Z)return Z;let e={};try{let t=localStorage.getItem(X),n=t?JSON.parse(t):null;if(n&&typeof n==`object`&&!Array.isArray(n))for(let[t,r]of Object.entries(n)){if(!r||typeof r!=`object`||Array.isArray(r))continue;let{seatId:n,model:i}=r;typeof n==`string`&&n.length!==0&&typeof i==`string`&&i.length!==0&&(e[t]={seatId:n,model:i})}}catch{}return Z=e,e}function he(e){let t=Q();if(e){let n=t[e];if(n)return n}let n=Object.keys(t),r=n[n.length-1];return r?t[r]??null:null}function ge(e,t){if(!e||!t.seatId||!t.model)return;let n=Q();delete n[e],n[e]={seatId:t.seatId,model:t.model};let r=Object.keys(n);for(let e of r.slice(0,Math.max(0,r.length-me)))delete n[e];try{localStorage.setItem(X,JSON.stringify(n))}catch{}}var $=new Map,_e=50;function ve(e,t){if(!e||!t)return;let n=$.get(e)??new Set;n.add(t),$.delete(e),$.set(e,n);for(let e of[...$.keys()].slice(0,Math.max(0,$.size-_e)))$.delete(e)}function ye(e,t){return $.get(e)?.has(t)??!1}export{m as A,V as C,ce as D,R as E,de as O,ue as S,H as T,Y as _,pe as a,v as b,W as c,ye as d,y as f,J as g,oe as h,h as i,p as k,ve as l,le as m,g as n,ae as o,he as p,B as r,se as s,b as t,re as u,ge as v,fe as w,U as x,q as y};
3
- //# sourceMappingURL=verse-ui-store-62DxpVHS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../../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"],"sourcesContent":["/**\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"],"file":"verse-ui-store-62DxpVHS.js"}
@@ -1,2 +0,0 @@
1
- import{a as e,s as t}from"./index-BVxOlseC.js";var n=t(e(),1),r=Object.freeze({compact:480,wide:1024}),i=Object.freeze({compact:`(max-width: ${r.compact-.02}px)`,wide:`(min-width: ${r.wide}px)`});function a(e){return e<r.compact?`compact`:e<r.wide?`medium`:`wide`}function o(){return typeof window>`u`?`wide`:typeof window.matchMedia==`function`?window.matchMedia(i.compact).matches?`compact`:window.matchMedia(i.wide).matches?`wide`:`medium`:a(window.innerWidth||r.wide)}function s(e){if(typeof window>`u`)return()=>{};if(typeof window.matchMedia!=`function`)return window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e);let t=[window.matchMedia(i.compact),window.matchMedia(i.wide)];for(let n of t)n.addEventListener(`change`,e);return()=>{for(let n of t)n.removeEventListener(`change`,e)}}function c(){let e=(0,n.useSyncExternalStore)(s,o,()=>`wide`);return(0,n.useMemo)(()=>({viewport:e,compact:e===`compact`,wide:e===`wide`}),[e])}export{c as n,r as t};
2
- //# sourceMappingURL=viewport-nTubFCwf.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"viewport-nTubFCwf.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/viewport.ts"],"sourcesContent":["/**\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"],"mappings":"8DAmBa,EAAuB,OAAO,OAAO,CAEhD,QAAS,IAET,KAAM,IACR,CAAC,EASY,EAAmB,OAAO,OAAO,CAC5C,QAAS,eAAe,EAAqB,QAAU,IAAK,KAC5D,KAAM,eAAe,EAAqB,KAAK,IACjD,CAAC,EAGD,SAAgB,EAAiB,EAA8B,CAG7D,OAFI,EAAQ,EAAqB,QAAgB,UAC7C,EAAQ,EAAqB,KAAa,SACvC,MACT,CAEA,SAAS,GAA8B,CAOrC,OANI,OAAO,OAAW,IAAoB,OACtC,OAAO,OAAO,YAAe,WAC3B,OAAO,WAAW,EAAiB,OAAO,CAAC,CAAC,QAAgB,UACzD,OAAO,WAAW,EAAiB,IAAI,CAAC,CAAC,QAAU,OAAS,SAG9D,EAAiB,OAAO,YAAc,EAAqB,IAAI,CACxE,CAEA,SAAS,EAAU,EAAkC,CACnD,GAAI,OAAO,OAAW,IAAa,UAAa,CAAC,EACjD,GAAI,OAAO,OAAO,YAAe,WAE/B,OADA,OAAO,iBAAiB,SAAU,CAAQ,MAC7B,OAAO,oBAAoB,SAAU,CAAQ,EAE5D,IAAM,EAAQ,CAAC,OAAO,WAAW,EAAiB,OAAO,EAAG,OAAO,WAAW,EAAiB,IAAI,CAAC,EACpG,IAAK,IAAM,KAAQ,EAAO,EAAK,iBAAiB,SAAU,CAAQ,EAClE,UAAa,CACX,IAAK,IAAM,KAAQ,EAAO,EAAK,oBAAoB,SAAU,CAAQ,CACvE,CACF,CAWA,SAAgB,GAA6B,CAC3C,IAAM,GAAA,EAAW,EAAA,qBAAA,CAAqB,EAAW,MAAoB,MAAe,EACpF,OAAA,EAAO,EAAA,QAAA,MAAe,CAAE,WAAU,QAAS,IAAa,UAAW,KAAM,IAAa,MAAO,GAAI,CAAC,CAAQ,CAAC,CAC7G"}
@@ -1,3 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/surface-prefetch-CdUEBFrl.js","assets/global-oEl9Qj8u.js","assets/index-BVxOlseC.js","assets/global-BlI2BYa1.css","assets/queries-CYisSAAB.js","assets/VerseConsoleApp-DTjDpGCd.js","assets/RouteErrorBoundary-DYtwc2ja.js","assets/RouteErrorBoundary-DdxhTdZ6.css","assets/command-catalog-Bb_PxC5w.js","assets/workbench-types-DzN-5trg.js","assets/guarded-action-OzqcPjmh.js","assets/preloaded-Cwf9rsnZ.js","assets/viewport-nTubFCwf.js","assets/verse-ui-store-62DxpVHS.js","assets/dock-store-B2vlxqPu.js","assets/composer-keys-su0g1h9o.js","assets/command-bus-xYZoK9nQ.js","assets/section-visibility-Yctxu-if.js","assets/icon-base-DvM1eI5V.js","assets/VerseConsoleApp-Cxqr-3qc.css","assets/budget-queries-V3wcJVK_.js","assets/cloud-queries-Br_r0UAz.js","assets/types-D5XOdyw8.js","assets/overnight-queries-DGm3qL1b.js","assets/format-BkWdOQDL.js","assets/format-DKrNjdwD.js","assets/verse-model-DyCwZF-M.js","assets/compaction-point-VL9oegWS.js","assets/control-queries-CmgBgKV7.js","assets/mutations-C5S7okTW.js","assets/surface-data-DTI7LjvH.js"])))=>i.map(i=>d[i]);
2
- import{t as e}from"./index-BVxOlseC.js";import{n as t,u as n}from"./verse-ui-store-62DxpVHS.js";var r=[`pointerdown`,`keydown`,`wheel`,`touchstart`],i={capture:!0,passive:!0};function a(e={}){let t=e.win??window,n=e.doc??document,a=e.now??(()=>Date.now()),o=e.quietMs??2e3,s=Math.max(1,e.pollMs??250),c=e.busy??(()=>!1),l=e.timeoutMs??3e3,u=!1,d=a(),f=new Set,p=()=>{d=a()};for(let e of r)n.addEventListener(e,p,i);let m=()=>d+o-a(),h=()=>n.visibilityState!==`hidden`&&m()<=0&&!c();function g(){return u?Promise.resolve(!1):new Promise(e=>{let r=()=>void 0,i=()=>{r(),e(!1)},o=()=>{f.delete(i),e(!0)};f.add(i);let u=()=>{if(r=()=>void 0,n.visibilityState===`hidden`){let e=()=>{n.visibilityState!==`hidden`&&(n.removeEventListener(`visibilitychange`,e),d=a(),u())};n.addEventListener(`visibilitychange`,e),r=()=>n.removeEventListener(`visibilitychange`,e);return}let e=m();if(e>0||c()){let n=t.setTimeout(u,e>0?e:s);r=()=>t.clearTimeout(n);return}if(typeof t.requestIdleCallback==`function`){let e=t.requestIdleCallback(()=>h()?o():u(),{timeout:l});r=()=>t.cancelIdleCallback?.(e);return}o()};u()})}function _(e){return u?Promise.resolve(!1):new Promise(n=>{let r=0,i=()=>{t.clearTimeout(r),n(!1)};r=t.setTimeout(()=>{f.delete(i),n(!0)},e),f.add(i)})}function v(){if(!u){u=!0;for(let e of r)n.removeEventListener(e,p,i);for(let e of[...f])e();f.clear()}}return{wait:g,pause:_,cancel:v}}function o(e,t={}){let n=a(t),r=t.gapMs??400;return(async()=>{for(let[t,i]of e.entries()){if(t>0&&r>0&&!await n.pause(r)||!await n.wait())return;try{await i(n)}catch{}}n.cancel()})(),n.cancel}var s=()=>e(()=>import(`./surface-prefetch-CdUEBFrl.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]));function c(e,r={}){let i=e=>async()=>{await e()},a=t=>async r=>{if(n().mounted.includes(t))return;await e.loadSection(t);let{prefetchSurfaceData:i}=await s();await i(t,{beforeEach:()=>r.wait()})};return o([...e.overlays.map(i),...t.map(e=>a(e.id))],{busy:e.readsInFlight,...r})}export{c as warmUpAfterFirstPaint};
3
- //# sourceMappingURL=warmup-HG5vYbsN.js.map
@@ -1,2 +0,0 @@
1
- var e=[`command`,`fleet`,`growth`,`mind`,`chat`],t=[`settings`,`apps`,`usage`],n=[...e,...t];function r(e){return typeof e==`string`&&n.includes(e)}var i={chat:{section:`chat`,openNeedsYou:!1},autonomy:{section:`fleet`,openNeedsYou:!1},approvals:{section:`command`,openNeedsYou:!0},usage:{section:`usage`,openNeedsYou:!1},settings:{section:`settings`,openNeedsYou:!1},mcp:{section:`apps`,openNeedsYou:!1}};function a(e){return typeof e==`string`?Object.prototype.hasOwnProperty.call(i,e)?i[e]:r(e)?{section:e,openNeedsYou:!1}:null:null}var o={claude:`C`,codex:`X`,grok:`G`,local:`L`},s=[`approvals`,`fleet`,`chats`,`accounts`],c={approval:`approvals`,"owner-lane-pr":`approvals`,"class-c":`approvals`,"veto-window":`fleet`,"leader-question":`fleet`,"owner-hold":`fleet`,quarantine:`fleet`,revert:`fleet`,grant:`fleet`,kill:`fleet`,"chat-failed":`chats`,"queue-held":`chats`,reconnect:`accounts`,repin:`accounts`},l={approve:`A`,reject:`R`,veto:`V`,done:`E`};function u(e){return typeof e==`string`&&e.length<=512&&e.startsWith(`/api/`)&&!e.startsWith(`//`)&&!e.includes(`..`)&&!/[\s\\]/.test(e)}var d=`/api/verse/activity`,f=`/api/verse/activity/seen`,p=`/api/verse/session-meta`,m=`/api/verse/session-controls`,h=`/api/verse/session-controls/defaults`,g=`/api/verse/attachments`,_=`/api/verse/queue`,v=`/api/verse/files`,y=8388608,b=`/api/verse/terminal`,x=16384,S=`/api/verse/preview/targets`,C=`/api/verse/preview/raw`;function w(e){let t;try{t=new URL(e)}catch{return!1}return t.protocol===`http:`&&(t.hostname===`127.0.0.1`||t.hostname===`localhost`)&&t.username===``&&t.password===``}var T=`/api/verse/apps`;export{x as _,d as a,u as b,g as c,C as d,S as f,p as g,h,c as i,y as l,m,l as n,f as o,_ as p,s as r,T as s,o as t,v as u,b as v,a as x,w as y};
2
- //# sourceMappingURL=workbench-types-DzN-5trg.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workbench-types-DzN-5trg.js","names":[],"sources":["../../../../../../src/core/verse/workbench-types.ts"],"sourcesContent":["/**\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"],"mappings":"AAwCA,IAAa,EAAqB,CAAC,UAAW,QAAS,SAAU,OAAQ,MAAM,EAIlE,EAA0B,CAAC,WAAY,OAAQ,OAAO,EAKtD,EAAuD,CAAC,GAAG,EAAoB,GAAG,CAAuB,EAEtH,SAAgB,EAAqB,EAA6C,CAChF,OAAO,OAAO,GAAU,UAAa,EAA4C,SAAS,CAAK,CACjG,CAeA,IAAa,EAAqF,CAChG,KAAM,CAAE,QAAS,OAAQ,aAAc,EAAM,EAC7C,SAAU,CAAE,QAAS,QAAS,aAAc,EAAM,EAClD,UAAW,CAAE,QAAS,UAAW,aAAc,EAAK,EACpD,MAAO,CAAE,QAAS,QAAS,aAAc,EAAM,EAC/C,SAAU,CAAE,QAAS,WAAY,aAAc,EAAM,EACrD,IAAK,CAAE,QAAS,OAAQ,aAAc,EAAM,CAC9C,EAOA,SAAgB,EAAiB,EAAyC,CAKxE,OAJI,OAAO,GAAU,SACjB,OAAO,UAAU,eAAe,KAAK,EAA0B,CAAK,EAC/D,EAAyB,GAE3B,EAAqB,CAAK,EAAI,CAAE,QAAS,EAAO,aAAc,EAAM,EAAI,KAJzC,IAKxC,CAOA,IAAa,EAAwE,CACnF,OAAQ,IACR,MAAO,IACP,KAAM,IACN,MAAO,GACT,EA6Ca,EAAuB,CAAC,YAAa,QAAS,QAAS,UAAU,EAIjE,EAA4E,CACvF,SAAU,YACV,gBAAiB,YACjB,UAAW,YACX,cAAe,QACf,kBAAmB,QACnB,aAAc,QACd,WAAY,QACZ,OAAQ,QACR,MAAO,QACP,KAAM,QACN,cAAe,QACf,aAAc,QACd,UAAW,WACX,MAAO,UACT,EAgBa,EAA8F,CACzG,QAAS,IACT,OAAQ,IACR,KAAM,IACN,KAAM,GACR,EAmHA,SAAgB,EAAe,EAA+B,CAC5D,OAAO,OAAO,GAAS,UAClB,EAAK,QAAU,KACf,EAAK,WAAW,OAAO,GACvB,CAAC,EAAK,WAAW,IAAI,GACrB,CAAC,EAAK,SAAS,IAAI,GACnB,CAAC,SAAS,KAAK,CAAI,CAC1B,CA2EA,IAAa,EAAsB,sBACtB,EAA2B,2BAC3B,EAA0B,0BA8K1B,EAA8B,8BAC9B,EAAsC,uCACtC,EAAyB,yBACzB,EAAmB,mBACnB,EAAmB,mBAwEnB,EAA6B,QAoE7B,EAAsB,sBAGtB,EAAiC,MAyFjC,EAA6B,6BAC7B,EAAyB,yBAgEtC,SAAgB,EAAqB,EAAwB,CAC3D,IAAI,EACJ,GAAI,CACF,EAAM,IAAI,IAAI,CAAK,CACrB,MAAQ,CACN,MAAO,EACT,CACA,OAAO,EAAI,WAAa,UAClB,EAAI,WAAa,aAAe,EAAI,WAAa,cAClD,EAAI,WAAa,IACjB,EAAI,WAAa,EACxB,CAkLA,IAAa,EAAkB"}