@ashlr/hub 3.11.3 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (558) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +44 -18
  3. package/dist/authority-surface.json +74 -59
  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/trust-roots.js +7 -1
  14. package/dist/core/authority/trust-roots.js.map +1 -1
  15. package/dist/core/authority/types.d.ts +2 -2
  16. package/dist/core/cloud/cloud-api.js +1 -1
  17. package/dist/core/cloud/cloud-api.js.map +1 -1
  18. package/dist/core/cloud/delivery-contract.d.ts +1 -1
  19. package/dist/core/cloud/delivery-contract.js +25 -15
  20. package/dist/core/cloud/delivery-contract.js.map +1 -1
  21. package/dist/core/cloud/store.js +6 -0
  22. package/dist/core/cloud/store.js.map +1 -1
  23. package/dist/core/cloud/tracker.d.ts +2 -0
  24. package/dist/core/cloud/tracker.js +140 -37
  25. package/dist/core/cloud/tracker.js.map +1 -1
  26. package/dist/core/cloud/types.d.ts +9 -1
  27. package/dist/core/cloud/types.js.map +1 -1
  28. package/dist/core/comms/dispatch.d.ts +5 -0
  29. package/dist/core/comms/dispatch.js +12 -1
  30. package/dist/core/comms/dispatch.js.map +1 -1
  31. package/dist/core/comms/requests.d.ts +27 -1
  32. package/dist/core/comms/requests.js +50 -1
  33. package/dist/core/comms/requests.js.map +1 -1
  34. package/dist/core/daemon/loop.d.ts +14 -0
  35. package/dist/core/daemon/loop.js +35 -0
  36. package/dist/core/daemon/loop.js.map +1 -1
  37. package/dist/core/fabric/claude-usage.js +23 -1
  38. package/dist/core/fabric/claude-usage.js.map +1 -1
  39. package/dist/core/fleet/dispatch-router.d.ts +7 -0
  40. package/dist/core/fleet/dispatch-router.js +4 -1
  41. package/dist/core/fleet/dispatch-router.js.map +1 -1
  42. package/dist/core/fleet/tick-hooks-live.d.ts +5 -1
  43. package/dist/core/fleet/tick-hooks-live.js +10 -1
  44. package/dist/core/fleet/tick-hooks-live.js.map +1 -1
  45. package/dist/core/local-runtime/llama/config.d.ts +14 -0
  46. package/dist/core/local-runtime/llama/config.js +20 -2
  47. package/dist/core/local-runtime/llama/config.js.map +1 -1
  48. package/dist/core/local-runtime/llama/process.d.ts +19 -0
  49. package/dist/core/local-runtime/llama/process.js +67 -17
  50. package/dist/core/local-runtime/llama/process.js.map +1 -1
  51. package/dist/core/local-runtime/llama/supervisor.js +14 -3
  52. package/dist/core/local-runtime/llama/supervisor.js.map +1 -1
  53. package/dist/core/observability/rollup.d.ts +12 -0
  54. package/dist/core/observability/rollup.js +37 -2
  55. package/dist/core/observability/rollup.js.map +1 -1
  56. package/dist/core/observability/usage-source.js +49 -1
  57. package/dist/core/observability/usage-source.js.map +1 -1
  58. package/dist/core/resources/connection-monitor.js +47 -4
  59. package/dist/core/resources/connection-monitor.js.map +1 -1
  60. package/dist/core/resources/engineering-mission-store.d.ts +1 -1
  61. package/dist/core/routing/headroom.js +7 -2
  62. package/dist/core/routing/headroom.js.map +1 -1
  63. package/dist/core/routing/router.d.ts +48 -0
  64. package/dist/core/routing/router.js +129 -13
  65. package/dist/core/routing/router.js.map +1 -1
  66. package/dist/core/run/best-of-n.d.ts +7 -0
  67. package/dist/core/run/best-of-n.js +3 -0
  68. package/dist/core/run/best-of-n.js.map +1 -1
  69. package/dist/core/run/harness-dispatch.d.ts +147 -0
  70. package/dist/core/run/harness-dispatch.js +234 -0
  71. package/dist/core/run/harness-dispatch.js.map +1 -0
  72. package/dist/core/run/orchestrator.js +3 -0
  73. package/dist/core/run/orchestrator.js.map +1 -1
  74. package/dist/core/run/provider-client.d.ts +8 -0
  75. package/dist/core/run/provider-client.js +4 -0
  76. package/dist/core/run/provider-client.js.map +1 -1
  77. package/dist/core/run/sandboxed-engine.d.ts +8 -0
  78. package/dist/core/run/sandboxed-engine.js +60 -7
  79. package/dist/core/run/sandboxed-engine.js.map +1 -1
  80. package/dist/core/types.d.ts +14 -0
  81. package/dist/core/types.js.map +1 -1
  82. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  83. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +188 -6
  84. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +187 -5
  85. package/dist/core/universe/firm-demo.d.ts +1 -1
  86. package/dist/core/universe/firm-graph.d.ts +2 -2
  87. package/dist/core/util/scrub.js +16 -3
  88. package/dist/core/util/scrub.js.map +1 -1
  89. package/dist/core/verse/accounts.d.ts +9 -1
  90. package/dist/core/verse/accounts.js +52 -11
  91. package/dist/core/verse/accounts.js.map +1 -1
  92. package/dist/core/verse/apps-api.d.ts +12 -0
  93. package/dist/core/verse/apps-api.js +22 -4
  94. package/dist/core/verse/apps-api.js.map +1 -1
  95. package/dist/core/verse/apps.js +22 -12
  96. package/dist/core/verse/apps.js.map +1 -1
  97. package/dist/core/verse/local-models.js +3 -7
  98. package/dist/core/verse/local-models.js.map +1 -1
  99. package/dist/core/verse/model-display-name.d.ts +34 -0
  100. package/dist/core/verse/model-display-name.js +125 -0
  101. package/dist/core/verse/model-display-name.js.map +1 -0
  102. package/dist/core/verse/seats.d.ts +11 -2
  103. package/dist/core/verse/seats.js +42 -18
  104. package/dist/core/verse/seats.js.map +1 -1
  105. package/dist/core/verse/transient-events.d.ts +21 -0
  106. package/dist/core/verse/transient-events.js +12 -0
  107. package/dist/core/verse/transient-events.js.map +1 -0
  108. package/dist/core/verse/types.d.ts +6 -13
  109. package/dist/core/verse/types.js +5 -36
  110. package/dist/core/verse/types.js.map +1 -1
  111. package/dist/core/web/public/next/assets/ActionMenu-B4-39f2S.js +2 -0
  112. package/dist/core/web/public/next/assets/{ActionMenu-FxXeRbPx.js.map → ActionMenu-B4-39f2S.js.map} +1 -1
  113. package/dist/core/web/public/next/assets/Advanced-B-LqYXvd.js +2 -0
  114. package/dist/core/web/public/next/assets/Advanced-B-LqYXvd.js.map +1 -0
  115. package/dist/core/web/public/next/assets/App-E2zCDRxm.js +7 -0
  116. package/dist/core/web/public/next/assets/App-E2zCDRxm.js.map +1 -0
  117. package/dist/core/web/public/next/assets/AppsSection-8wXPPhZb.js +2 -0
  118. package/dist/core/web/public/next/assets/AppsSection-8wXPPhZb.js.map +1 -0
  119. package/dist/core/web/public/next/assets/AreaTrend-BFimExYn.js +2 -0
  120. package/dist/core/web/public/next/assets/{AreaTrend-CDxrQU4b.js.map → AreaTrend-BFimExYn.js.map} +1 -1
  121. package/dist/core/web/public/next/assets/AutonomyOffState-B5eEkmiz.js +2 -0
  122. package/dist/core/web/public/next/assets/AutonomyOffState-B5eEkmiz.js.map +1 -0
  123. package/dist/core/web/public/next/assets/AutonomyOffState-DmbE71vI.css +1 -0
  124. package/dist/core/web/public/next/assets/AutonomySection-B46igJKR.js +2 -0
  125. package/dist/core/web/public/next/assets/{AutonomySection-DuQkHGD_.js.map → AutonomySection-B46igJKR.js.map} +1 -1
  126. package/dist/core/web/public/next/assets/BranchBar-BRkrEQGs.js +2 -0
  127. package/dist/core/web/public/next/assets/{BranchBar-CLsvOcyK.js.map → BranchBar-BRkrEQGs.js.map} +1 -1
  128. package/dist/core/web/public/next/assets/BudgetControl-T_RjPrw9.js +2 -0
  129. package/dist/core/web/public/next/assets/{BudgetControl-TL3FeZ1D.js.map → BudgetControl-T_RjPrw9.js.map} +1 -1
  130. package/dist/core/web/public/next/assets/{Button-HHXgoXGr.js → Button-BhO-WKPp.js} +2 -2
  131. package/dist/core/web/public/next/assets/{Button-HHXgoXGr.js.map → Button-BhO-WKPp.js.map} +1 -1
  132. package/dist/core/web/public/next/assets/CapacityStrip-CkFz5vq2.js +2 -0
  133. package/dist/core/web/public/next/assets/{CapacityStrip-qReu0RaF.js.map → CapacityStrip-CkFz5vq2.js.map} +1 -1
  134. package/dist/core/web/public/next/assets/{useChartWidth-BW6SwYIK.css → ChartFrame-BVuBe6RP.css} +1 -1
  135. package/dist/core/web/public/next/assets/ChartFrame-SygZ7B3x.js +2 -0
  136. package/dist/core/web/public/next/assets/ChartFrame-SygZ7B3x.js.map +1 -0
  137. package/dist/core/web/public/next/assets/ChatSection-D6WybBpB.js +3 -0
  138. package/dist/core/web/public/next/assets/ChatSection-D6WybBpB.js.map +1 -0
  139. package/dist/core/web/public/next/assets/CloudBudgetForm-Bd91iStN.js +2 -0
  140. package/dist/core/web/public/next/assets/CloudBudgetForm-Bd91iStN.js.map +1 -0
  141. package/dist/core/web/public/next/assets/CommandPalette-B6SgDJH-.js +2 -0
  142. package/dist/core/web/public/next/assets/CommandPalette-B6SgDJH-.js.map +1 -0
  143. package/dist/core/web/public/next/assets/CommandSection-CvAVd1w4.css +1 -0
  144. package/dist/core/web/public/next/assets/CommandSection-DgMGoepd.js +2 -0
  145. package/dist/core/web/public/next/assets/CommandSection-DgMGoepd.js.map +1 -0
  146. package/dist/core/web/public/next/assets/ConfirmDialog-Bv4hdK2T.js +2 -0
  147. package/dist/core/web/public/next/assets/{ConfirmDialog-DLrLKM0c.js.map → ConfirmDialog-Bv4hdK2T.js.map} +1 -1
  148. package/dist/core/web/public/next/assets/DeleteChatDialog-CH1Ix8Eh.js +2 -0
  149. package/dist/core/web/public/next/assets/DeleteChatDialog-CH1Ix8Eh.js.map +1 -0
  150. package/dist/core/web/public/next/assets/Dialog-BSsIFNqD.js +2 -0
  151. package/dist/core/web/public/next/assets/{Dialog-BS9Dxi3G.js.map → Dialog-BSsIFNqD.js.map} +1 -1
  152. package/dist/core/web/public/next/assets/DiffPane-BpNuJmth.js +2 -0
  153. package/dist/core/web/public/next/assets/{DiffPane-TOmUf3b2.js.map → DiffPane-BpNuJmth.js.map} +1 -1
  154. package/dist/core/web/public/next/assets/DiffViewer-CJarUvs0.js +2 -0
  155. package/dist/core/web/public/next/assets/{DiffViewer-BpH56302.js.map → DiffViewer-CJarUvs0.js.map} +1 -1
  156. package/dist/core/web/public/next/assets/DockHost-DW9q0NdR.js +2 -0
  157. package/dist/core/web/public/next/assets/DockHost-DW9q0NdR.js.map +1 -0
  158. package/dist/core/web/public/next/assets/{EmptyState-BqR5YAmf.js → EmptyState-DPrd2aak.js} +2 -2
  159. package/dist/core/web/public/next/assets/{EmptyState-BqR5YAmf.js.map → EmptyState-DPrd2aak.js.map} +1 -1
  160. package/dist/core/web/public/next/assets/{Epistemic-dhvDTwAZ.js → Epistemic-D7MxSmED.js} +2 -2
  161. package/dist/core/web/public/next/assets/{Epistemic-dhvDTwAZ.js.map → Epistemic-D7MxSmED.js.map} +1 -1
  162. package/dist/core/web/public/next/assets/FleetSection-BdZN6rGS.js +3 -0
  163. package/dist/core/web/public/next/assets/FleetSection-BdZN6rGS.js.map +1 -0
  164. package/dist/core/web/public/next/assets/Funnel-BeNQZWv2.js +2 -0
  165. package/dist/core/web/public/next/assets/{Funnel-D2u7Wlzc.js.map → Funnel-BeNQZWv2.js.map} +1 -1
  166. package/dist/core/web/public/next/assets/GearTray-CIPtzeIx.js +2 -0
  167. package/dist/core/web/public/next/assets/{GearTray-Cb2npoxM.js.map → GearTray-CIPtzeIx.js.map} +1 -1
  168. package/dist/core/web/public/next/assets/GrowthSection-iiQg6wvK.js +2 -0
  169. package/dist/core/web/public/next/assets/{GrowthSection-C5aDPs8B.js.map → GrowthSection-iiQg6wvK.js.map} +1 -1
  170. package/dist/core/web/public/next/assets/HandoffDialog-DrQ6ofuy.js +2 -0
  171. package/dist/core/web/public/next/assets/{HandoffDialog-BF3AYg-X.js.map → HandoffDialog-DrQ6ofuy.js.map} +1 -1
  172. package/dist/core/web/public/next/assets/Input-C0MQxEM6.js +2 -0
  173. package/dist/core/web/public/next/assets/{Input-B5dmGLZx.js.map → Input-C0MQxEM6.js.map} +1 -1
  174. package/dist/core/web/public/next/assets/LeaderCard-BRr3_yE-.js +2 -0
  175. package/dist/core/web/public/next/assets/LeaderCard-BRr3_yE-.js.map +1 -0
  176. package/dist/core/web/public/next/assets/MatrixHeatmap-DZvZeWXL.js +2 -0
  177. package/dist/core/web/public/next/assets/{MatrixHeatmap-BgEUs2Eu.js.map → MatrixHeatmap-DZvZeWXL.js.map} +1 -1
  178. package/dist/core/web/public/next/assets/McpSection-BRuCcTjT.js +2 -0
  179. package/dist/core/web/public/next/assets/{McpSection-DZkiOuoq.js.map → McpSection-BRuCcTjT.js.map} +1 -1
  180. package/dist/core/web/public/next/assets/MessageMarkdown-bRvD5nSc.js +3 -0
  181. package/dist/core/web/public/next/assets/{MessageMarkdown-BauY8ZTY.js.map → MessageMarkdown-bRvD5nSc.js.map} +1 -1
  182. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DLBNLfsn.js → MessageMarkdownRenderer-FjjkPKA2.js} +4 -4
  183. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DLBNLfsn.js.map → MessageMarkdownRenderer-FjjkPKA2.js.map} +1 -1
  184. package/dist/core/web/public/next/assets/{Meter-DNeocJnD.js → Meter-CFNLpwA-.js} +2 -2
  185. package/dist/core/web/public/next/assets/{Meter-DNeocJnD.js.map → Meter-CFNLpwA-.js.map} +1 -1
  186. package/dist/core/web/public/next/assets/MindSection-BTIiQS8P.js +2 -0
  187. package/dist/core/web/public/next/assets/MindSection-BTIiQS8P.js.map +1 -0
  188. package/dist/core/web/public/next/assets/MutationTokenDialog-T7gv3a9r.js +2 -0
  189. package/dist/core/web/public/next/assets/{MutationTokenDialog-mxrw0Yvy.js.map → MutationTokenDialog-T7gv3a9r.js.map} +1 -1
  190. package/dist/core/web/public/next/assets/NeedsYouDrawer-lYN4G_aM.js +2 -0
  191. package/dist/core/web/public/next/assets/{NeedsYouDrawer-CO-Balfp.js.map → NeedsYouDrawer-lYN4G_aM.js.map} +1 -1
  192. package/dist/core/web/public/next/assets/NeedsYouList-BgH8ezqf.js +2 -0
  193. package/dist/core/web/public/next/assets/{NeedsYouList-CzDZbzRD.js.map → NeedsYouList-BgH8ezqf.js.map} +1 -1
  194. package/dist/core/web/public/next/assets/NewChatDialog-Db5Xjrk8.js +3 -0
  195. package/dist/core/web/public/next/assets/NewChatDialog-Db5Xjrk8.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/OnboardingFlow-LoY-zuSe.js +2 -0
  198. package/dist/core/web/public/next/assets/OnboardingFlow-LoY-zuSe.js.map +1 -0
  199. package/dist/core/web/public/next/assets/OnboardingGate-C4VXn7BJ.js +3 -0
  200. package/dist/core/web/public/next/assets/OnboardingGate-C4VXn7BJ.js.map +1 -0
  201. package/dist/core/web/public/next/assets/PreviewPane-BPBbcROb.js +2 -0
  202. package/dist/core/web/public/next/assets/{PreviewPane-BS-CleCd.js.map → PreviewPane-BPBbcROb.js.map} +1 -1
  203. package/dist/core/web/public/next/assets/{ProviderLogo-C5mBg8i0.js → ProviderLogo-CT2cSfkg.js} +2 -2
  204. package/dist/core/web/public/next/assets/{ProviderLogo-C5mBg8i0.js.map → ProviderLogo-CT2cSfkg.js.map} +1 -1
  205. package/dist/core/web/public/next/assets/RailStatus-Vgl0GYtd.js +2 -0
  206. package/dist/core/web/public/next/assets/{RailStatus-cPRyOmrk.js.map → RailStatus-Vgl0GYtd.js.map} +1 -1
  207. package/dist/core/web/public/next/assets/{RefreshIndicator-CMwSCiz5.js → RefreshIndicator-CSyqpY_q.js} +2 -2
  208. package/dist/core/web/public/next/assets/{RefreshIndicator-CMwSCiz5.js.map → RefreshIndicator-CSyqpY_q.js.map} +1 -1
  209. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BFi1opsC.js → ResourcePoolConsoleApp-BX2nkdmZ.js} +4 -4
  210. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BFi1opsC.js.map → ResourcePoolConsoleApp-BX2nkdmZ.js.map} +1 -1
  211. package/dist/core/web/public/next/assets/ResourcesChrome-B2U2lO1H.js +3 -0
  212. package/dist/core/web/public/next/assets/ResourcesChrome-B2U2lO1H.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/ResourcesDrawer-DlUKOjl9.js +2 -0
  215. package/dist/core/web/public/next/assets/ResourcesDrawer-DlUKOjl9.js.map +1 -0
  216. package/dist/core/web/public/next/assets/RouteErrorBoundary-BaC7YVzR.js +2 -0
  217. package/dist/core/web/public/next/assets/{RouteErrorBoundary-DYtwc2ja.js.map → RouteErrorBoundary-BaC7YVzR.js.map} +1 -1
  218. package/dist/core/web/public/next/assets/RunInCloudAction-D5PDHJPE.js +2 -0
  219. package/dist/core/web/public/next/assets/{RunInCloudAction-BEGEEM8O.js.map → RunInCloudAction-D5PDHJPE.js.map} +1 -1
  220. package/dist/core/web/public/next/assets/SeatSelector-Cg75LInq.js +3 -0
  221. package/dist/core/web/public/next/assets/{SeatSelector-CYHRR7Wp.js.map → SeatSelector-Cg75LInq.js.map} +1 -1
  222. package/dist/core/web/public/next/assets/Segmented-CametjLt.js +2 -0
  223. package/dist/core/web/public/next/assets/{Segmented-BE53BenI.js.map → Segmented-CametjLt.js.map} +1 -1
  224. package/dist/core/web/public/next/assets/Select-De52fghU.js +2 -0
  225. package/dist/core/web/public/next/assets/{Select-BwSGkHCS.js.map → Select-De52fghU.js.map} +1 -1
  226. package/dist/core/web/public/next/assets/SessionGate-CzlcNq3O.js +2 -0
  227. package/dist/core/web/public/next/assets/{SessionGate-e0dFJm9d.js.map → SessionGate-CzlcNq3O.js.map} +1 -1
  228. package/dist/core/web/public/next/assets/SessionInsightChip-Bk_Owa7u.js +2 -0
  229. package/dist/core/web/public/next/assets/{SessionInsightChip-CxlSyueJ.js.map → SessionInsightChip-Bk_Owa7u.js.map} +1 -1
  230. package/dist/core/web/public/next/assets/SessionSearch-C6C8DHN8.js +2 -0
  231. package/dist/core/web/public/next/assets/{SessionSearch-_yp7S2TP.js.map → SessionSearch-C6C8DHN8.js.map} +1 -1
  232. package/dist/core/web/public/next/assets/SettingsSection-mI9wQYZg.js +2 -0
  233. package/dist/core/web/public/next/assets/SettingsSection-mI9wQYZg.js.map +1 -0
  234. package/dist/core/web/public/next/assets/Sheet-CJhK9Dqv.js +2 -0
  235. package/dist/core/web/public/next/assets/{Sheet-DVP2Mgy6.js.map → Sheet-CJhK9Dqv.js.map} +1 -1
  236. package/dist/core/web/public/next/assets/ShortcutsOverlay-DDW-i9Fc.js +2 -0
  237. package/dist/core/web/public/next/assets/{ShortcutsOverlay-BcIcoClX.js.map → ShortcutsOverlay-DDW-i9Fc.js.map} +1 -1
  238. package/dist/core/web/public/next/assets/Sidebar-DT7ykaqS.js +3 -0
  239. package/dist/core/web/public/next/assets/Sidebar-DT7ykaqS.js.map +1 -0
  240. package/dist/core/web/public/next/assets/Slider-Bwd9HfXl.js +2 -0
  241. package/dist/core/web/public/next/assets/{Slider-BypUWjpH.js.map → Slider-Bwd9HfXl.js.map} +1 -1
  242. package/dist/core/web/public/next/assets/StatTile-DPZ5yyaU.js +2 -0
  243. package/dist/core/web/public/next/assets/StatTile-DPZ5yyaU.js.map +1 -0
  244. package/dist/core/web/public/next/assets/{StatusBadge-HCZp416_.js → StatusBadge-DHKRtttH.js} +2 -2
  245. package/dist/core/web/public/next/assets/{StatusBadge-HCZp416_.js.map → StatusBadge-DHKRtttH.js.map} +1 -1
  246. package/dist/core/web/public/next/assets/StepBand-DQykLkk_.js +2 -0
  247. package/dist/core/web/public/next/assets/{StepBand-Duz1PUes.js.map → StepBand-DQykLkk_.js.map} +1 -1
  248. package/dist/core/web/public/next/assets/Surface-D--LXgQm.js +2 -0
  249. package/dist/core/web/public/next/assets/Surface-D--LXgQm.js.map +1 -0
  250. package/dist/core/web/public/next/assets/Surface-e1z6GdZt.css +1 -0
  251. package/dist/core/web/public/next/assets/Swimlane-BzH6OCee.js +2 -0
  252. package/dist/core/web/public/next/assets/{Swimlane-DGcIcnNA.js.map → Swimlane-BzH6OCee.js.map} +1 -1
  253. package/dist/core/web/public/next/assets/{Switch-iveI-8NY.js → Switch-PMixU6b0.js} +2 -2
  254. package/dist/core/web/public/next/assets/{Switch-iveI-8NY.js.map → Switch-PMixU6b0.js.map} +1 -1
  255. package/dist/core/web/public/next/assets/{Tag-DH15CUJp.js → Tag-fordBsrJ.js} +2 -2
  256. package/dist/core/web/public/next/assets/{Tag-DH15CUJp.js.map → Tag-fordBsrJ.js.map} +1 -1
  257. package/dist/core/web/public/next/assets/TerminalPane-BSnGdUuF.js +7 -0
  258. package/dist/core/web/public/next/assets/TerminalPane-BSnGdUuF.js.map +1 -0
  259. package/dist/core/web/public/next/assets/UniverseConsoleApp-DtXFhAhw.js +2 -0
  260. package/dist/core/web/public/next/assets/{UniverseConsoleApp-C8yQ0AaR.js.map → UniverseConsoleApp-DtXFhAhw.js.map} +1 -1
  261. package/dist/core/web/public/next/assets/{UniverseView-_nPURPqe.js → UniverseView-sWm3pnTW.js} +3 -3
  262. package/dist/core/web/public/next/assets/{UniverseView-_nPURPqe.js.map → UniverseView-sWm3pnTW.js.map} +1 -1
  263. package/dist/core/web/public/next/assets/UsageSection-CiD1DbuF.js +3 -0
  264. package/dist/core/web/public/next/assets/UsageSection-CiD1DbuF.js.map +1 -0
  265. package/dist/core/web/public/next/assets/VerseConsoleApp-BCY5uSok.js +3 -0
  266. package/dist/core/web/public/next/assets/VerseConsoleApp-BCY5uSok.js.map +1 -0
  267. package/dist/core/web/public/next/assets/Workspace-BQSZFSeO.js +10 -0
  268. package/dist/core/web/public/next/assets/Workspace-BQSZFSeO.js.map +1 -0
  269. package/dist/core/web/public/next/assets/actions-BmIf9avl.css +1 -0
  270. package/dist/core/web/public/next/assets/actions-Ci6I4C0l.js +2 -0
  271. package/dist/core/web/public/next/assets/actions-Ci6I4C0l.js.map +1 -0
  272. package/dist/core/web/public/next/assets/apps-model-DlwnfqwJ.js +2 -0
  273. package/dist/core/web/public/next/assets/apps-model-DlwnfqwJ.js.map +1 -0
  274. package/dist/core/web/public/next/assets/authority-model-BFuPezSo.js +2 -0
  275. package/dist/core/web/public/next/assets/authority-model-BFuPezSo.js.map +1 -0
  276. package/dist/core/web/public/next/assets/binding-left-DZpq5sLf.js +2 -0
  277. package/dist/core/web/public/next/assets/binding-left-DZpq5sLf.js.map +1 -0
  278. package/dist/core/web/public/next/assets/{budget-queries-V3wcJVK_.js → budget-queries-DsAjH8ac.js} +2 -2
  279. package/dist/core/web/public/next/assets/{budget-queries-V3wcJVK_.js.map → budget-queries-DsAjH8ac.js.map} +1 -1
  280. package/dist/core/web/public/next/assets/capacity-strip-model-Dn1cNOhz.js +2 -0
  281. package/dist/core/web/public/next/assets/capacity-strip-model-Dn1cNOhz.js.map +1 -0
  282. package/dist/core/web/public/next/assets/charts-C_xJbJiw.js +2 -0
  283. package/dist/core/web/public/next/assets/charts-C_xJbJiw.js.map +1 -0
  284. package/dist/core/web/public/next/assets/charts-DhKTrQLw.css +1 -0
  285. package/dist/core/web/public/next/assets/clipboard-DkZdqlxf.js +2 -0
  286. package/dist/core/web/public/next/assets/clipboard-DkZdqlxf.js.map +1 -0
  287. package/dist/core/web/public/next/assets/cloud-D-HXms6q.css +1 -0
  288. package/dist/core/web/public/next/assets/cloud-model-CX407gPY.js +2 -0
  289. package/dist/core/web/public/next/assets/cloud-model-CX407gPY.js.map +1 -0
  290. package/dist/core/web/public/next/assets/cloud-queries-QAhpExlz.js +2 -0
  291. package/dist/core/web/public/next/assets/cloud-queries-QAhpExlz.js.map +1 -0
  292. package/dist/core/web/public/next/assets/cloud.module-DOP-N4Ch.js +2 -0
  293. package/dist/core/web/public/next/assets/cloud.module-DOP-N4Ch.js.map +1 -0
  294. package/dist/core/web/public/next/assets/command-catalog-CF0cQQac.js +2 -0
  295. package/dist/core/web/public/next/assets/command-catalog-CF0cQQac.js.map +1 -0
  296. package/dist/core/web/public/next/assets/context-model-Co4wh3SM.js +2 -0
  297. package/dist/core/web/public/next/assets/context-model-Co4wh3SM.js.map +1 -0
  298. package/dist/core/web/public/next/assets/context-model-DV1zCfAm.js +3 -0
  299. package/dist/core/web/public/next/assets/context-model-DV1zCfAm.js.map +1 -0
  300. package/dist/core/web/public/next/assets/{context-queries-CrPcHWTz.js → context-queries-CktFQqSS.js} +2 -2
  301. package/dist/core/web/public/next/assets/{context-queries-CrPcHWTz.js.map → context-queries-CktFQqSS.js.map} +1 -1
  302. package/dist/core/web/public/next/assets/{control-queries-CmgBgKV7.js → control-queries-DAvlh_YM.js} +2 -2
  303. package/dist/core/web/public/next/assets/{control-queries-CmgBgKV7.js.map → control-queries-DAvlh_YM.js.map} +1 -1
  304. package/dist/core/web/public/next/assets/copy-setup-CqRSfELB.js +2 -0
  305. package/dist/core/web/public/next/assets/copy-setup-CqRSfELB.js.map +1 -0
  306. package/dist/core/web/public/next/assets/dock-icons-BFuBbqJl.js +2 -0
  307. package/dist/core/web/public/next/assets/{dock-icons-CUh8dOxJ.js.map → dock-icons-BFuBbqJl.js.map} +1 -1
  308. package/dist/core/web/public/next/assets/fleet-model-Dfd5JyLR.js +2 -0
  309. package/dist/core/web/public/next/assets/fleet-model-Dfd5JyLR.js.map +1 -0
  310. package/dist/core/web/public/next/assets/fleet-queries-RtMTd5rR.js +2 -0
  311. package/dist/core/web/public/next/assets/fleet-queries-RtMTd5rR.js.map +1 -0
  312. package/dist/core/web/public/next/assets/{focus-trap-HtcoXeEq.js → focus-trap-BdrbWfC4.js} +2 -2
  313. package/dist/core/web/public/next/assets/{focus-trap-HtcoXeEq.js.map → focus-trap-BdrbWfC4.js.map} +1 -1
  314. package/dist/core/web/public/next/assets/format-Dk4Fmxax.js +2 -0
  315. package/dist/core/web/public/next/assets/format-Dk4Fmxax.js.map +1 -0
  316. package/dist/core/web/public/next/assets/{git-model-bk_RXh3y.js → git-model-_BnI_Ogh.js} +2 -2
  317. package/dist/core/web/public/next/assets/{git-model-bk_RXh3y.js.map → git-model-_BnI_Ogh.js.map} +1 -1
  318. package/dist/core/web/public/next/assets/global-CNNGMril.js +2 -0
  319. package/dist/core/web/public/next/assets/{global-oEl9Qj8u.js.map → global-CNNGMril.js.map} +1 -1
  320. package/dist/core/web/public/next/assets/{guarded-runners-LHLXmXIk.js → guarded-runners-DPh2d32e.js} +2 -2
  321. package/dist/core/web/public/next/assets/{guarded-runners-LHLXmXIk.js.map → guarded-runners-DPh2d32e.js.map} +1 -1
  322. package/dist/core/web/public/next/assets/icons-0haWy6nh.js +2 -0
  323. package/dist/core/web/public/next/assets/icons-0haWy6nh.js.map +1 -0
  324. package/dist/core/web/public/next/assets/{index-BVxOlseC.js → index-QesfXBsb.js} +3 -3
  325. package/dist/core/web/public/next/assets/{index-BVxOlseC.js.map → index-QesfXBsb.js.map} +1 -1
  326. package/dist/core/web/public/next/assets/live-model-De-q0ZFK.js +2 -0
  327. package/dist/core/web/public/next/assets/live-model-De-q0ZFK.js.map +1 -0
  328. package/dist/core/web/public/next/assets/local-model-C9xNi9fN.js +2 -0
  329. package/dist/core/web/public/next/assets/local-model-C9xNi9fN.js.map +1 -0
  330. package/dist/core/web/public/next/assets/mind.module-BrK2nFOL.js +2 -0
  331. package/dist/core/web/public/next/assets/{mind.module-gTGAuJt1.js.map → mind.module-BrK2nFOL.js.map} +1 -1
  332. package/dist/core/web/public/next/assets/mutations-DTCtKCn_.js +2 -0
  333. package/dist/core/web/public/next/assets/{mutations-C5S7okTW.js.map → mutations-DTCtKCn_.js.map} +1 -1
  334. package/dist/core/web/public/next/assets/nav-KmGX0CVz.js +2 -0
  335. package/dist/core/web/public/next/assets/{nav-NVtnuG7A.js.map → nav-KmGX0CVz.js.map} +1 -1
  336. package/dist/core/web/public/next/assets/{overnight-queries-DGm3qL1b.js → overnight-queries-mlO0xzsr.js} +2 -2
  337. package/dist/core/web/public/next/assets/{overnight-queries-DGm3qL1b.js.map → overnight-queries-mlO0xzsr.js.map} +1 -1
  338. package/dist/core/web/public/next/assets/primitives-D0Fb3TtS.js +1 -0
  339. package/dist/core/web/public/next/assets/{queries-CYisSAAB.js → queries-CyBE8nsW.js} +2 -2
  340. package/dist/core/web/public/next/assets/{queries-CYisSAAB.js.map → queries-CyBE8nsW.js.map} +1 -1
  341. package/dist/core/web/public/next/assets/reasoning-pref-CaoA4_Vl.js +2 -0
  342. package/dist/core/web/public/next/assets/{reasoning-pref-BUcFF_5G.js.map → reasoning-pref-CaoA4_Vl.js.map} +1 -1
  343. package/dist/core/web/public/next/assets/{reveal-anchor-D-tV8xau.js → reveal-anchor-BP1VqnPU.js} +2 -2
  344. package/dist/core/web/public/next/assets/{reveal-anchor-D-tV8xau.js.map → reveal-anchor-BP1VqnPU.js.map} +1 -1
  345. package/dist/core/web/public/next/assets/seat-subscription-C5Tvr0WQ.js +2 -0
  346. package/dist/core/web/public/next/assets/{seat-subscription-B-f9ViU2.js.map → seat-subscription-C5Tvr0WQ.js.map} +1 -1
  347. package/dist/core/web/public/next/assets/{sidebar-model-DQ-FyBHW.js → sidebar-model-B5Qk95k_.js} +2 -2
  348. package/dist/core/web/public/next/assets/{sidebar-model-DQ-FyBHW.js.map → sidebar-model-B5Qk95k_.js.map} +1 -1
  349. package/dist/core/web/public/next/assets/slots-DHKQDoE6.js +3 -0
  350. package/dist/core/web/public/next/assets/{slots-BhP8b8tf.js.map → slots-DHKQDoE6.js.map} +1 -1
  351. package/dist/core/web/public/next/assets/surface-data-CchClvHo.js +2 -0
  352. package/dist/core/web/public/next/assets/surface-data-CchClvHo.js.map +1 -0
  353. package/dist/core/web/public/next/assets/surface-prefetch-BWr0LBQn.js +2 -0
  354. package/dist/core/web/public/next/assets/surface-prefetch-BWr0LBQn.js.map +1 -0
  355. package/dist/core/web/public/next/assets/{tasks-model-BKDNqmmt.js → tasks-model-D6WFVULI.js} +2 -2
  356. package/dist/core/web/public/next/assets/{tasks-model-BKDNqmmt.js.map → tasks-model-D6WFVULI.js.map} +1 -1
  357. package/dist/core/web/public/next/assets/{terminal-icons-D2xe-aM1.js → terminal-icons-BvCvuXk2.js} +2 -2
  358. package/dist/core/web/public/next/assets/{terminal-icons-D2xe-aM1.js.map → terminal-icons-BvCvuXk2.js.map} +1 -1
  359. package/dist/core/web/public/next/assets/transient-events-nTLhMBND.js +2 -0
  360. package/dist/core/web/public/next/assets/transient-events-nTLhMBND.js.map +1 -0
  361. package/dist/core/web/public/next/assets/types-D5XOdyw8.js.map +1 -1
  362. package/dist/core/web/public/next/assets/{usage-queries-BZuAANlI.js → usage-queries-D1SU1t7r.js} +2 -2
  363. package/dist/core/web/public/next/assets/{usage-queries-BZuAANlI.js.map → usage-queries-D1SU1t7r.js.map} +1 -1
  364. package/dist/core/web/public/next/assets/{use-guarded-action-Cf4MeNBa.js → use-guarded-action-BZmBv7CJ.js} +2 -2
  365. package/dist/core/web/public/next/assets/{use-guarded-action-Cf4MeNBa.js.map → use-guarded-action-BZmBv7CJ.js.map} +1 -1
  366. package/dist/core/web/public/next/assets/{use-ticker-D9yi49my.js → use-ticker-CkJ4-0wU.js} +2 -2
  367. package/dist/core/web/public/next/assets/{use-ticker-D9yi49my.js.map → use-ticker-CkJ4-0wU.js.map} +1 -1
  368. package/dist/core/web/public/next/assets/{use-token-gate-DqrSXdX6.js → use-token-gate-CYcfk_GS.js} +2 -2
  369. package/dist/core/web/public/next/assets/{use-token-gate-DqrSXdX6.js.map → use-token-gate-CYcfk_GS.js.map} +1 -1
  370. package/dist/core/web/public/next/assets/useOnboarding-CujRS6PK.js +2 -0
  371. package/dist/core/web/public/next/assets/useOnboarding-CujRS6PK.js.map +1 -0
  372. package/dist/core/web/public/next/assets/useSeatsRefresh-BEaQdkZs.js +2 -0
  373. package/dist/core/web/public/next/assets/useSeatsRefresh-BEaQdkZs.js.map +1 -0
  374. package/dist/core/web/public/next/assets/useVerseTranscript-BQ0J6Xhx.js +2 -0
  375. package/dist/core/web/public/next/assets/{useVerseTranscript-CDYrrWOO.js.map → useVerseTranscript-BQ0J6Xhx.js.map} +1 -1
  376. package/dist/core/web/public/next/assets/verse-icons-DndgEgKS.js +2 -0
  377. package/dist/core/web/public/next/assets/verse-icons-DndgEgKS.js.map +1 -0
  378. package/dist/core/web/public/next/assets/verse-model-MMySehjj.js +2 -0
  379. package/dist/core/web/public/next/assets/verse-model-MMySehjj.js.map +1 -0
  380. package/dist/core/web/public/next/assets/verse-store-DNVRuiJL.js +2 -0
  381. package/dist/core/web/public/next/assets/verse-store-DNVRuiJL.js.map +1 -0
  382. package/dist/core/web/public/next/assets/warmup-CNEH_-be.js +3 -0
  383. package/dist/core/web/public/next/assets/{warmup-HG5vYbsN.js.map → warmup-CNEH_-be.js.map} +1 -1
  384. package/dist/core/web/public/next/assets/why-seat-model-jlDTwYSc.js +2 -0
  385. package/dist/core/web/public/next/assets/why-seat-model-jlDTwYSc.js.map +1 -0
  386. package/dist/core/web/public/next/assets/{workspace-model-CbhdPhei.js → workspace-model-Dukb2wwv.js} +2 -2
  387. package/dist/core/web/public/next/assets/{workspace-model-CbhdPhei.js.map → workspace-model-Dukb2wwv.js.map} +1 -1
  388. package/dist/core/web/public/next/index.html +1 -1
  389. package/dist/release-dependency-inventory.json +1 -1
  390. package/docs/README.md +2 -1
  391. package/package.json +1 -1
  392. package/dist/core/web/public/next/assets/ActionMenu-FxXeRbPx.js +0 -2
  393. package/dist/core/web/public/next/assets/Advanced-D_eS4472.js +0 -2
  394. package/dist/core/web/public/next/assets/Advanced-D_eS4472.js.map +0 -1
  395. package/dist/core/web/public/next/assets/App-Cj4zKbG3.js +0 -7
  396. package/dist/core/web/public/next/assets/App-Cj4zKbG3.js.map +0 -1
  397. package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js +0 -2
  398. package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js.map +0 -1
  399. package/dist/core/web/public/next/assets/AreaTrend-CDxrQU4b.js +0 -2
  400. package/dist/core/web/public/next/assets/AutonomySection-DuQkHGD_.js +0 -2
  401. package/dist/core/web/public/next/assets/BranchBar-CLsvOcyK.js +0 -2
  402. package/dist/core/web/public/next/assets/BudgetControl-TL3FeZ1D.js +0 -2
  403. package/dist/core/web/public/next/assets/BurnDown-sFXkUGdB.js +0 -2
  404. package/dist/core/web/public/next/assets/BurnDown-sFXkUGdB.js.map +0 -1
  405. package/dist/core/web/public/next/assets/CapacityStrip-qReu0RaF.js +0 -2
  406. package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js +0 -3
  407. package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js.map +0 -1
  408. package/dist/core/web/public/next/assets/CloudBudgetForm-DJbiT1wi.js +0 -2
  409. package/dist/core/web/public/next/assets/CloudBudgetForm-DJbiT1wi.js.map +0 -1
  410. package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js +0 -2
  411. package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js.map +0 -1
  412. package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js +0 -2
  413. package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js.map +0 -1
  414. package/dist/core/web/public/next/assets/ConfirmDialog-DLrLKM0c.js +0 -2
  415. package/dist/core/web/public/next/assets/Dialog-BS9Dxi3G.js +0 -2
  416. package/dist/core/web/public/next/assets/DiffPane-TOmUf3b2.js +0 -2
  417. package/dist/core/web/public/next/assets/DiffViewer-BpH56302.js +0 -2
  418. package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js +0 -2
  419. package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js.map +0 -1
  420. package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js +0 -3
  421. package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js.map +0 -1
  422. package/dist/core/web/public/next/assets/Funnel-D2u7Wlzc.js +0 -2
  423. package/dist/core/web/public/next/assets/GearTray-Cb2npoxM.js +0 -2
  424. package/dist/core/web/public/next/assets/GrowthSection-C5aDPs8B.js +0 -2
  425. package/dist/core/web/public/next/assets/HandoffDialog-BF3AYg-X.js +0 -2
  426. package/dist/core/web/public/next/assets/Input-B5dmGLZx.js +0 -2
  427. package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js +0 -2
  428. package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js.map +0 -1
  429. package/dist/core/web/public/next/assets/MatrixHeatmap-BgEUs2Eu.js +0 -2
  430. package/dist/core/web/public/next/assets/McpSection-DZkiOuoq.js +0 -2
  431. package/dist/core/web/public/next/assets/MessageMarkdown-BauY8ZTY.js +0 -3
  432. package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js +0 -2
  433. package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js.map +0 -1
  434. package/dist/core/web/public/next/assets/MutationTokenDialog-mxrw0Yvy.js +0 -2
  435. package/dist/core/web/public/next/assets/NeedsYouDrawer-CO-Balfp.js +0 -2
  436. package/dist/core/web/public/next/assets/NeedsYouList-CzDZbzRD.js +0 -2
  437. package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js +0 -3
  438. package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js.map +0 -1
  439. package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js +0 -2
  440. package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js.map +0 -1
  441. package/dist/core/web/public/next/assets/OnboardingFlow-DSpPj8UE.css +0 -1
  442. package/dist/core/web/public/next/assets/PreviewPane-BS-CleCd.js +0 -2
  443. package/dist/core/web/public/next/assets/RailStatus-cPRyOmrk.js +0 -2
  444. package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js +0 -3
  445. package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js.map +0 -1
  446. package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js +0 -2
  447. package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js.map +0 -1
  448. package/dist/core/web/public/next/assets/ResourcesDrawer-CSKIO_wZ.css +0 -1
  449. package/dist/core/web/public/next/assets/RouteErrorBoundary-DYtwc2ja.js +0 -2
  450. package/dist/core/web/public/next/assets/RunInCloudAction-BEGEEM8O.js +0 -2
  451. package/dist/core/web/public/next/assets/SeatSelector-CYHRR7Wp.js +0 -3
  452. package/dist/core/web/public/next/assets/Segmented-BE53BenI.js +0 -2
  453. package/dist/core/web/public/next/assets/Select-BwSGkHCS.js +0 -2
  454. package/dist/core/web/public/next/assets/SessionGate-e0dFJm9d.js +0 -2
  455. package/dist/core/web/public/next/assets/SessionInsightChip-CxlSyueJ.js +0 -2
  456. package/dist/core/web/public/next/assets/SessionSearch-_yp7S2TP.js +0 -2
  457. package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js +0 -2
  458. package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js.map +0 -1
  459. package/dist/core/web/public/next/assets/Sheet-DVP2Mgy6.js +0 -2
  460. package/dist/core/web/public/next/assets/ShortcutsOverlay-BcIcoClX.js +0 -2
  461. package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js +0 -3
  462. package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js.map +0 -1
  463. package/dist/core/web/public/next/assets/Slider-BypUWjpH.js +0 -2
  464. package/dist/core/web/public/next/assets/StepBand-Duz1PUes.js +0 -2
  465. package/dist/core/web/public/next/assets/Surface-B75lVfzm.js +0 -2
  466. package/dist/core/web/public/next/assets/Surface-B75lVfzm.js.map +0 -1
  467. package/dist/core/web/public/next/assets/Surface-DMca0gAe.css +0 -1
  468. package/dist/core/web/public/next/assets/Swimlane-DGcIcnNA.js +0 -2
  469. package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js +0 -7
  470. package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js.map +0 -1
  471. package/dist/core/web/public/next/assets/UniverseConsoleApp-C8yQ0AaR.js +0 -2
  472. package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js +0 -2
  473. package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js.map +0 -1
  474. package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js +0 -3
  475. package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js.map +0 -1
  476. package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js +0 -10
  477. package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js.map +0 -1
  478. package/dist/core/web/public/next/assets/actions-Ddfg2iLl.css +0 -1
  479. package/dist/core/web/public/next/assets/actions-tWevL9HV.js +0 -2
  480. package/dist/core/web/public/next/assets/actions-tWevL9HV.js.map +0 -1
  481. package/dist/core/web/public/next/assets/apps-model-CDFvmsQV.js +0 -2
  482. package/dist/core/web/public/next/assets/apps-model-CDFvmsQV.js.map +0 -1
  483. package/dist/core/web/public/next/assets/capacity-strip-model-BDbmQ3e3.js +0 -2
  484. package/dist/core/web/public/next/assets/capacity-strip-model-BDbmQ3e3.js.map +0 -1
  485. package/dist/core/web/public/next/assets/charts-CfAjuKMv.js +0 -2
  486. package/dist/core/web/public/next/assets/charts-CfAjuKMv.js.map +0 -1
  487. package/dist/core/web/public/next/assets/charts-uSX6xVYN.css +0 -1
  488. package/dist/core/web/public/next/assets/cloud-C1KPNRHC.css +0 -1
  489. package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js +0 -2
  490. package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js.map +0 -1
  491. package/dist/core/web/public/next/assets/cloud.module-ascz58c3.js +0 -2
  492. package/dist/core/web/public/next/assets/cloud.module-ascz58c3.js.map +0 -1
  493. package/dist/core/web/public/next/assets/command-bus-xYZoK9nQ.js +0 -2
  494. package/dist/core/web/public/next/assets/command-bus-xYZoK9nQ.js.map +0 -1
  495. package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js +0 -2
  496. package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js.map +0 -1
  497. package/dist/core/web/public/next/assets/composer-keys-su0g1h9o.js +0 -2
  498. package/dist/core/web/public/next/assets/composer-keys-su0g1h9o.js.map +0 -1
  499. package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js +0 -2
  500. package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js.map +0 -1
  501. package/dist/core/web/public/next/assets/context-model-CIBmSugV.js +0 -2
  502. package/dist/core/web/public/next/assets/context-model-CIBmSugV.js.map +0 -1
  503. package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js +0 -2
  504. package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js.map +0 -1
  505. package/dist/core/web/public/next/assets/dock-icons-CUh8dOxJ.js +0 -2
  506. package/dist/core/web/public/next/assets/dock-store-B2vlxqPu.js +0 -2
  507. package/dist/core/web/public/next/assets/dock-store-B2vlxqPu.js.map +0 -1
  508. package/dist/core/web/public/next/assets/fleet-model-7i-FbzKI.js +0 -2
  509. package/dist/core/web/public/next/assets/fleet-model-7i-FbzKI.js.map +0 -1
  510. package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js +0 -2
  511. package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js.map +0 -1
  512. package/dist/core/web/public/next/assets/format-BkWdOQDL.js +0 -2
  513. package/dist/core/web/public/next/assets/format-BkWdOQDL.js.map +0 -1
  514. package/dist/core/web/public/next/assets/global-oEl9Qj8u.js +0 -2
  515. package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js +0 -3
  516. package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js.map +0 -1
  517. package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js +0 -2
  518. package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js.map +0 -1
  519. package/dist/core/web/public/next/assets/icons-DEQa2rTx.js +0 -2
  520. package/dist/core/web/public/next/assets/icons-DEQa2rTx.js.map +0 -1
  521. package/dist/core/web/public/next/assets/live-model-bmsJgnYD.js +0 -2
  522. package/dist/core/web/public/next/assets/live-model-bmsJgnYD.js.map +0 -1
  523. package/dist/core/web/public/next/assets/local-model-DFyjKX0b.js +0 -2
  524. package/dist/core/web/public/next/assets/local-model-DFyjKX0b.js.map +0 -1
  525. package/dist/core/web/public/next/assets/mind.module-gTGAuJt1.js +0 -2
  526. package/dist/core/web/public/next/assets/mutations-C5S7okTW.js +0 -2
  527. package/dist/core/web/public/next/assets/nav-NVtnuG7A.js +0 -2
  528. package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js +0 -2
  529. package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js.map +0 -1
  530. package/dist/core/web/public/next/assets/primitives-DyCKYSBG.js +0 -1
  531. package/dist/core/web/public/next/assets/reasoning-pref-BUcFF_5G.js +0 -2
  532. package/dist/core/web/public/next/assets/seat-subscription-B-f9ViU2.js +0 -2
  533. package/dist/core/web/public/next/assets/section-visibility-Yctxu-if.js +0 -2
  534. package/dist/core/web/public/next/assets/section-visibility-Yctxu-if.js.map +0 -1
  535. package/dist/core/web/public/next/assets/slots-BhP8b8tf.js +0 -3
  536. package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js +0 -2
  537. package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js.map +0 -1
  538. package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js +0 -2
  539. package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js.map +0 -1
  540. package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js +0 -2
  541. package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js.map +0 -1
  542. package/dist/core/web/public/next/assets/useSeatsRefresh-DgFy-Ph4.js +0 -2
  543. package/dist/core/web/public/next/assets/useSeatsRefresh-DgFy-Ph4.js.map +0 -1
  544. package/dist/core/web/public/next/assets/useVerseTranscript-CDYrrWOO.js +0 -2
  545. package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js +0 -2
  546. package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js.map +0 -1
  547. package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js +0 -2
  548. package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js.map +0 -1
  549. package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js +0 -3
  550. package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js.map +0 -1
  551. package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js +0 -3
  552. package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js.map +0 -1
  553. package/dist/core/web/public/next/assets/viewport-nTubFCwf.js +0 -2
  554. package/dist/core/web/public/next/assets/viewport-nTubFCwf.js.map +0 -1
  555. package/dist/core/web/public/next/assets/warmup-HG5vYbsN.js +0 -3
  556. package/dist/core/web/public/next/assets/workbench-types-DzN-5trg.js +0 -2
  557. package/dist/core/web/public/next/assets/workbench-types-DzN-5trg.js.map +0 -1
  558. /package/dist/core/web/public/next/assets/{BurnDown-BuLMsJMx.css → StatTile-BuLMsJMx.css} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../../src/web-ui/routes/verse/chat/composer-bridge.ts","../../../../../../src/web-ui/routes/verse/chat/use-session-roots.ts","../../../../../../src/web-ui/routes/verse/chat/transcript-jump.ts","../../../../../../src/web-ui/routes/verse/verse-events.ts","../../../../../../src/web-ui/routes/verse/session-stream.ts","../../../../../../src/web-ui/routes/verse/useVerseSession.ts","../../../../../../src/web-ui/routes/verse/chat/use-chat-activity.ts","../../../../../../src/web-ui/routes/verse/chat-panel-sizing.ts","../../../../../../src/web-ui/routes/verse/ChatResizer.tsx","../../../../../../src/web-ui/routes/verse/sections/ChatSection.tsx"],"sourcesContent":["/**\n * routes/verse/chat/composer-bridge.ts — how the chat's other surfaces put\n * text INTO the composer without sending it (unit C2).\n *\n * Three dock actions draft into the message box and leave the send to the\n * operator (the first spend is always their press):\n * - Terminal ▸ \"Send selection to chat\" — the selection as a fenced block;\n * - Review ▸ \"Add to message\" — `path:line: note`;\n * - anything later that \"drafts a follow-up\".\n *\n * The composer is C3's component and owns its text state. The clean seam is\n * for it to register an inserter here (`registerComposerInserter`) — that is\n * a cross-unit request. Until it does, the fallback types into the composer's\n * textarea the way a paste would: the native value setter plus an `input`\n * event, which React's controlled textarea treats as the operator's own\n * edit (so its draft persistence, cost hint and undo stack all see it).\n * Either way the text is APPENDED on its own paragraph and never sent.\n */\n\nexport type ComposerInserter = (text: string) => void;\n\nconst inserters = new Map<string, ComposerInserter>();\n\n/** C3: `useEffect(() => registerComposerInserter(sessionId, insert), [sessionId])`. Returns the unregister. */\nexport function registerComposerInserter(sessionId: string, insert: ComposerInserter): () => void {\n inserters.set(sessionId, insert);\n return () => {\n if (inserters.get(sessionId) === insert) inserters.delete(sessionId);\n };\n}\n\n/** Join an insertion onto existing text: its own paragraph, never glued to a half-typed word. */\nexport function appendParagraph(current: string, text: string): string {\n const addition = text.replace(/\\s+$/, '');\n if (!current.trim()) return addition;\n return `${current.replace(/\\s+$/, '')}\\n\\n${addition}`;\n}\n\n/** The composer's message box in the chat surface (C3's `aria-label=\"Message\"`). */\nfunction findComposerBox(root: ParentNode = document): HTMLTextAreaElement | null {\n const node = root.querySelector('textarea[aria-label=\"Message\"]');\n return node instanceof HTMLTextAreaElement ? node : null;\n}\n\n/**\n * Append `text` to `sessionId`'s composer and focus it. False when there is\n * no composer to write into (no chat open, or it is disabled) — the caller\n * then says so rather than dropping the text silently.\n */\nexport function insertIntoComposer(sessionId: string, text: string): boolean {\n if (!text.trim()) return false;\n const registered = inserters.get(sessionId);\n if (registered) {\n registered(text);\n return true;\n }\n const box = findComposerBox();\n if (!box || box.disabled) return false;\n const next = appendParagraph(box.value, text);\n const setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;\n if (setter) setter.call(box, next);\n else box.value = next;\n box.dispatchEvent(new Event('input', { bubbles: true }));\n box.focus();\n const end = box.value.length;\n try {\n box.setSelectionRange(end, end);\n } catch {\n /* a detached or hidden box has no selection; the text is in either way */\n }\n return true;\n}\n","/**\n * routes/verse/chat/use-session-roots.ts — one read of a chat's roots for\n * everything that needs them (unit C2): the header's `repo › branch`\n * breadcrumb, the Context pane's Folders list, and the roots handed to the\n * Branch bar, Terminal, Preview and Review slots.\n *\n * Before 3.10 only SessionRoots read `/sessions/:id/roots`, fetching on its\n * own. With four consumers that would be four requests per chat switch (each\n * one `git status` per root on the server), so the read goes through the\n * shared query cache under one key per chat. A branch goes stale the moment\n * a turn commits, so the read is refreshed whenever the chat's turnCount\n * moves — exactly as SessionRoots' `refreshKey` did.\n */\nimport { useEffect, useMemo, useRef } from 'react';\nimport type { VerseSession, VerseSessionRootsResponse } from '../../../data/api-types.js';\nimport { useQuery, useRefetch } from '../../../data/hooks.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { fetchVerseSessionRoots } from '../verse-queries.js';\n\nexport function sessionRootsKey(sessionId: string): string {\n return `verse-session-roots:${sessionId}`;\n}\n\nconst NO_SESSION: QueryDef<VerseSessionRootsResponse | null> = {\n key: 'verse-session-roots:none',\n fetch: async () => null,\n};\n\nexport interface SessionRootsView {\n data: VerseSessionRootsResponse | null;\n error: string | null;\n /** Roots in priority order: the primary first, then the pinned extras. Known before the read lands. */\n roots: readonly string[];\n /** The primary root's branch, when it is a git repo and the read landed. */\n branch: string | null;\n}\n\n/** Roots as the session RECORD states them — usable before (and without) the status read. */\nexport function recordRoots(session: Pick<VerseSession, 'projectPath' | 'extraRoots'> | null): string[] {\n if (!session) return [];\n return [session.projectPath, ...(session.extraRoots ?? []).filter((r) => r !== session.projectPath)];\n}\n\nexport function useSessionRoots(session: Pick<VerseSession, 'id' | 'projectPath' | 'extraRoots' | 'turnCount'> | null): SessionRootsView {\n const sessionId = session?.id ?? null;\n const def = useMemo<QueryDef<VerseSessionRootsResponse | null>>(\n () => (sessionId\n ? { key: sessionRootsKey(sessionId), fetch: (signal) => fetchVerseSessionRoots(sessionId, signal) }\n : NO_SESSION),\n [sessionId],\n );\n const query = useQuery(def);\n const refetch = useRefetch(def);\n\n // A finished turn may have committed or switched branch: re-read. The\n // first value for a chat is the mount's own fetch, not a change.\n const turnCount = session?.turnCount ?? 0;\n const seen = useRef<{ id: string | null; turns: number }>({ id: sessionId, turns: turnCount });\n useEffect(() => {\n const prev = seen.current;\n seen.current = { id: sessionId, turns: turnCount };\n if (prev.id === sessionId && prev.turns !== turnCount && sessionId) refetch();\n }, [sessionId, turnCount, refetch]);\n\n // A reply without a roots list (an older server, a stub) is treated as no\n // reading at all rather than trusted into a crash.\n const raw = query.data ?? null;\n const data = raw !== null && Array.isArray(raw.roots) ? raw : null;\n const fromRecord = useMemo(() => recordRoots(session), [session?.projectPath, session?.extraRoots]); // eslint-disable-line react-hooks/exhaustive-deps -- the two fields ARE the dependency\n const roots = useMemo(() => {\n if (!data || data.roots.length === 0) return fromRecord;\n const primary = data.roots.filter((r) => r.primary).map((r) => r.path);\n const rest = data.roots.filter((r) => !r.primary).map((r) => r.path);\n return [...primary, ...rest];\n }, [data, fromRecord]);\n const primary = data?.roots.find((r) => r.primary) ?? data?.roots[0] ?? null;\n return {\n data,\n error: query.status === 'error' ? query.error?.message ?? 'could not read this chat’s folders' : null,\n roots,\n branch: primary?.git?.branch ?? null,\n };\n}\n","/**\n * routes/verse/chat/transcript-jump.ts — \"scroll the open transcript to this\n * tool call\", from outside the transcript (unit C2).\n *\n * The dock's Tasks pane lists this turn's calls and a click must land on\n * the call in the transcript — but the pane is a different subtree (and, as\n * a sheet, a different layer). The mounted Transcript subscribes here and\n * performs the jump with its own machinery (opening a folded activity group,\n * the locator flash, focus). A tiny module, so the dock does not import the\n * transcript.\n */\ntype JumpListener = (anchorId: string) => boolean;\n\nconst listeners = new Set<JumpListener>();\n\nexport function onTranscriptJump(listener: JumpListener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\n/** True when a mounted transcript took the jump. */\nexport function requestTranscriptJump(anchorId: string): boolean {\n let handled = false;\n for (const listener of [...listeners]) if (listener(anchorId)) handled = true;\n return handled;\n}\n\n// ⌘F from the palette (\"Find in chat\"): focus the transcript's own search.\nconst findListeners = new Set<() => boolean>();\n\nexport function onTranscriptFind(listener: () => boolean): () => void {\n findListeners.add(listener);\n return () => findListeners.delete(listener);\n}\n\nexport function requestTranscriptFind(): boolean {\n let handled = false;\n for (const listener of [...findListeners]) if (listener()) handled = true;\n return handled;\n}\n\n// ⌥↑ / ⌥↓ routed through the command bus (palette, native menu).\nconst stepListeners = new Set<(delta: 1 | -1) => boolean>();\n\nexport function onTranscriptStep(listener: (delta: 1 | -1) => boolean): () => void {\n stepListeners.add(listener);\n return () => stepListeners.delete(listener);\n}\n\nexport function requestTranscriptStep(delta: 1 | -1): boolean {\n let handled = false;\n for (const listener of [...stepListeners]) if (listener(delta)) handled = true;\n return handled;\n}\n","/**\n * routes/verse/verse-events.ts — the Verse console's SSE vocabulary and its\n * sidebar channel, since scoped consoles never join data/sse.ts's global feed:\n *\n * - VERSE_EVENT_TYPES + parseVerseEventFrame: every VerseEvent name a\n * session stream can carry, and the frame validation both streams share.\n * The per-session stream itself (resume by `?after=`, one store update\n * per animation frame, ref-counted connections) lives in\n * session-stream.ts (V3.10); the replay-from-zero opener that used to sit\n * here had no callers left and was removed.\n * - openVerseListChannel: one EventSource against /api/events for the\n * `verse-sessions` list digest, which just invalidates the sidebar's\n * cache keys. It subscribes with `?topics=verse-sessions` (V3.10) so the\n * server skips the dashboard snapshot and the other groups this console\n * would only discard.\n *\n * Every URL carries the per-tab client proof as `?client=` — the same\n * construction data/client.ts's eventsUrl() uses, because EventSource cannot\n * send the proof as a header (see read-session.ts readSessionClientProof).\n */\nimport type { VerseEvent, VerseEventType } from '../../data/api-types.js';\nimport { getAuthSnapshot, getReadClientProof } from '../../data/auth-store.js';\nimport { invalidateVerseLists } from './verse-queries.js';\n\n/**\n * Every VerseEvent name the stream can carry. EventSource only dispatches\n * NAMED events to listeners registered by name, so a type missing here is\n * silently dropped — the check below turns a new union member into a compile\n * error in this file instead of a meter that never moves.\n */\nconst EVENT_TYPES = [\n 'user-message',\n 'turn-started',\n 'text-delta',\n 'assistant-message',\n 'thinking',\n 'tool-use',\n 'tool-result',\n 'usage',\n 'turn-done',\n 'error',\n 'cancelled',\n // V3.9 — a server that predates them simply never sends these names.\n 'compaction',\n 'context',\n // V3.10 transient (SSE only, never replayed — see VERSE_TRANSIENT_EVENT_TYPES\n // in core/verse/types.ts). They carry the last PERSISTED seq, so a store that\n // dedupes by seq and does not know them yet simply drops them.\n 'thinking-delta',\n 'thinking-progress',\n 'progress',\n 'status',\n // V3.10 persisted.\n 'recovered',\n 'history-truncated',\n] as const satisfies readonly VerseEventType[];\n\ntype UnlistedEventType = Exclude<VerseEventType, (typeof EVENT_TYPES)[number]>;\n// Fails to compile when VerseEventType gains a member EVENT_TYPES does not list.\nconst EVENT_TYPES_EXHAUSTIVE: [UnlistedEventType] extends [never] ? true : never = true;\nvoid EVENT_TYPES_EXHAUSTIVE;\n\nexport const VERSE_EVENT_TYPES: readonly VerseEventType[] = EVENT_TYPES;\n\nfunction backoffMs(attempt: number): number {\n return Math.min(1000 * 2 ** attempt, 15_000);\n}\n\nconst nullableCount = (v: unknown): boolean => v === null || (typeof v === 'number' && Number.isFinite(v) && v >= 0);\n\n/**\n * The two V3.9 frames feed arithmetic (the meter, the compaction count), so a\n * malformed one is dropped here rather than turning the meter into `NaN`.\n * Older event types keep the original seq/type-only check.\n */\nfunction v39FieldsValid(e: Record<string, unknown>): boolean {\n if (e.type === 'context') {\n return typeof e.contextTokens === 'number' && Number.isFinite(e.contextTokens) && e.contextTokens >= 0 &&\n typeof e.exact === 'boolean' && nullableCount(e.contextWindow) &&\n (e.autoCompactAt === undefined || nullableCount(e.autoCompactAt));\n }\n if (e.type === 'compaction') {\n return (e.trigger === 'auto' || e.trigger === 'manual') &&\n nullableCount(e.preTokens) && nullableCount(e.postTokens) && nullableCount(e.durationMs);\n }\n return true;\n}\n\nconst optionalCount = (v: unknown): boolean => v === undefined || (typeof v === 'number' && Number.isFinite(v) && v >= 0);\n\n/**\n * V3.10 frames that feed arithmetic or a live label (elapsed, tok/s, token\n * estimate) — same rule as v39FieldsValid: malformed → dropped, never NaN.\n */\nfunction v310FieldsValid(e: Record<string, unknown>): boolean {\n switch (e.type) {\n case 'thinking-delta':\n return typeof e.text === 'string';\n case 'thinking-progress':\n return e.estimatedTokens !== undefined && optionalCount(e.estimatedTokens);\n case 'progress':\n return (e.phase === 'thinking' || e.phase === 'tool' || e.phase === 'writing' || e.phase === 'waiting') &&\n typeof e.elapsedMs === 'number' && Number.isFinite(e.elapsedMs) && e.elapsedMs >= 0 &&\n (e.tool === undefined || typeof e.tool === 'string') &&\n optionalCount(e.outTokens) && optionalCount(e.tokPerSec);\n case 'status':\n return (e.kind === 'retry' || e.kind === 'preflight' || e.kind === 'watchdog') && typeof e.message === 'string';\n case 'history-truncated':\n return typeof e.droppedBefore === 'number' && Number.isFinite(e.droppedBefore);\n default:\n return true;\n }\n}\n\n/** Exported for tests: one SSE frame's data → a VerseEvent, or null when malformed. */\nexport function parseVerseEventFrame(raw: string): VerseEvent | null {\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object') return null;\n const candidate = parsed as Partial<VerseEvent>;\n if (typeof candidate.seq !== 'number' || typeof candidate.type !== 'string') return null;\n if (!v39FieldsValid(parsed as Record<string, unknown>)) return null;\n if (!v310FieldsValid(parsed as Record<string, unknown>)) return null;\n return parsed as VerseEvent;\n } catch {\n return null;\n }\n}\n\n/** The only /api/events group the Verse console listens to. */\nexport const VERSE_LIST_TOPICS = 'verse-sessions';\n\n/**\n * The sidebar channel's URL. `topics` narrows the server to the one group\n * this console listens to; `client` stays last, as in eventsUrl(). Without\n * `topics` the server sends every group — the historical request, which is\n * also what the refusal fallback below falls back to.\n */\nexport function verseListEventsUrl(withTopics = true): string {\n const topics = withTopics ? `topics=${VERSE_LIST_TOPICS}&` : '';\n return `/api/events?${topics}client=${encodeURIComponent(getReadClientProof())}`;\n}\n\n/**\n * Set once this page has PROVEN that the server refuses `?topics=`: a\n * connection with it failed before it ever opened, and the same channel\n * without it then opened. The read-session boundary on SSE paths used to\n * answer 401 to any parameter besides `client`, and a sidebar that never\n * updates is far worse than one that is sent (and discards) extra groups.\n * Page-lifetime only — the same rule session-stream.ts applies to `?after=`:\n * a server that gains support is used again after a reload.\n */\nlet topicsRefused = false;\n\n/** Tests: forget a proven refusal. */\nexport function resetVerseListChannelCapabilities(): void {\n topicsRefused = false;\n}\n\n/**\n * Sidebar digest channel: listens only for `verse-sessions` on /api/events\n * and refreshes the session list + bootstrap when it fires.\n */\nexport function openVerseListChannel(): () => void {\n let source: EventSource | null = null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let disposed = false;\n /** This connection is the no-`topics` retry that tests whether `topics` was refused. */\n let probing = false;\n\n const connect = () => {\n if (disposed || typeof EventSource === 'undefined') return;\n if (getAuthSnapshot().phase !== 'authenticated') return;\n const withTopics = !topicsRefused && !probing;\n const es = new EventSource(verseListEventsUrl(withTopics), { withCredentials: true });\n let opened = false;\n source = es;\n es.onopen = () => {\n opened = true;\n attempt = 0;\n if (probing) {\n topicsRefused = true;\n probing = false;\n }\n };\n es.addEventListener('verse-sessions', () => invalidateVerseLists());\n es.onerror = () => {\n es.close();\n if (source === es) source = null;\n if (disposed || getAuthSnapshot().phase !== 'authenticated') return;\n if (!opened && withTopics) {\n // Refused before it opened, with `topics`: find out at once whether\n // the parameter is what was refused (see topicsRefused). One probe,\n // no delay — a real outage fails the probe too and falls through to\n // the ordinary backoff below.\n probing = true;\n connect();\n return;\n }\n probing = false;\n timer = setTimeout(() => {\n timer = null;\n attempt += 1;\n connect();\n }, backoffMs(attempt));\n };\n };\n\n connect();\n\n return () => {\n disposed = true;\n if (timer) clearTimeout(timer);\n timer = null;\n source?.close();\n source = null;\n };\n}\n","/**\n * routes/verse/session-stream.ts — the client data path for ONE open chat\n * (V3.10): load it once, resume its live stream from where the store left\n * off, batch what streams in to one store update per animation frame, and\n * keep the connection across section switches.\n *\n * WHY THIS EXISTS (measured before 3.10, scratchpad perf/):\n * - opening a chat raced a full detail fetch against an EventSource that\n * replayed the whole log from seq 0, and every replayed frame was applied\n * and rendered on its own — 2.45 s for a 5k-event chat, 17 s for 10k;\n * - every reconnect (at least one per 15-minute read session) replayed the\n * whole log again, because a hand-made EventSource sends no Last-Event-ID;\n * - every replayed `turn-done` refetched the session list and bootstrap;\n * - leaving the Chat section closed the stream, so coming back replayed.\n *\n * THE PATH NOW\n * 1. `openVerseSession(id)` fetches the detail ONCE when the store has not\n * loaded the chat, seeds the store, and only then attaches the stream —\n * so the two never race.\n * 2. The stream URL carries `?after=<lastSeq>` (the highest PERSISTED seq\n * the store holds). The server replays only what is newer; a server that\n * predates `after` replays everything and the store's seq dedupe drops\n * it. A server whose auth boundary REFUSES the extra parameter is\n * detected once and served without it (afterRefused). A dropped\n * connection is left to the browser's own reconnect, which resumes by\n * Last-Event-ID.\n * 3. Frames are queued and applied with ONE `applyVerseEvents` call per\n * animation frame (a timer backs rAF up, since a hidden tab gets none).\n * Transient frames (reasoning deltas, progress) ride the same queue in\n * arrival order.\n * 4. The session list is invalidated only when a batch really settled a\n * turn — never for a replayed duplicate.\n * 5. Streams are ref-counted. When the last user lets go (section switch,\n * another chat selected) the connection lingers for IDLE_CLOSE_MS so\n * switching back is instant and costs no replay; at most MAX_IDLE\n * lingering connections are kept, because they share the server's SSE\n * connection cap with /api/events.\n *\n * Auth: every URL carries the per-tab client proof as `?client=` (EventSource\n * cannot send it as a header — see server.ts readSessionClientProof), and a\n * stream never reconnects once the read session is gone.\n */\nimport type { VerseEvent } from '../../data/api-types.js';\nimport { getAuthSnapshot, getReadClientProof } from '../../data/auth-store.js';\nimport { parseVerseEventFrame, VERSE_EVENT_TYPES } from './verse-events.js';\nimport { fetchVerseSessionDetail, invalidateVerseLists, verseSessionPath } from './verse-queries.js';\nimport {\n applyVerseEvents,\n getVerseSessionState,\n seedVerseSession,\n setVerseLoadError,\n setVerseStreamState,\n subscribeVerseStoreLifecycle,\n} from './verse-store.js';\n\n/** How long a released stream stays connected before it is closed. */\nexport const IDLE_CLOSE_MS = 90_000;\n/** Released-but-lingering streams kept at once (they count against the server's SSE cap). */\nexport const MAX_IDLE_STREAMS = 1;\n/** Fallback flush when requestAnimationFrame does not fire (hidden tab, occluded window). */\nconst FRAME_FALLBACK_MS = 250;\n\n/**\n * Per-session stream URL: `?after=<cursor>&client=<proof>`. `after` is\n * omitted for cursor 0 — seqs start at 1, so \"after 0\" and \"from the start\"\n * are the same request, and the bare form is what a server without `after`\n * support has always seen.\n */\nexport function verseSessionStreamUrl(sessionId: string, after: number): string {\n const cursor = Number.isSafeInteger(after) && after > 0 ? `after=${after}&` : '';\n return `${verseSessionPath(sessionId, '/events')}?${cursor}client=${encodeURIComponent(getReadClientProof())}`;\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(1000 * 2 ** attempt, 15_000);\n}\n\n// ---------------------------------------------------------------------------\n// Frame scheduling\n// ---------------------------------------------------------------------------\n\ntype FrameScheduler = (flush: () => void) => () => void;\n\n/**\n * True inside a React `act()` test environment. There, `act()` is the test\n * declaring \"everything this block causes must be visible after it\", and a\n * frame-deferred flush would land after the assertions — so frames are\n * applied as they arrive, exactly as React applies its own scheduled work\n * inside act. Production never sets the flag.\n */\nfunction inActEnvironment(): boolean {\n return (globalThis as { IS_REACT_ACT_ENVIRONMENT?: unknown }).IS_REACT_ACT_ENVIRONMENT === true;\n}\n\nconst animationFrameScheduler: FrameScheduler = (flush) => {\n let done = false;\n const run = () => {\n if (done) return;\n done = true;\n if (raf !== null && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(raf);\n clearTimeout(timer);\n flush();\n };\n const hidden = typeof document !== 'undefined' && document.visibilityState === 'hidden';\n const raf = !hidden && typeof requestAnimationFrame === 'function' ? requestAnimationFrame(run) : null;\n const timer = setTimeout(run, raf === null ? 0 : FRAME_FALLBACK_MS);\n return () => {\n done = true;\n if (raf !== null && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(raf);\n clearTimeout(timer);\n };\n};\n\nlet customScheduler: FrameScheduler | null = null;\n\n/**\n * Tests: replace the frame scheduler (null restores the default). A scheduler\n * that never calls `flush` lets a test assert what is queued; calling it\n * later simulates the frame.\n */\nexport function setVerseFrameScheduler(scheduler: FrameScheduler | null): void {\n customScheduler = scheduler;\n}\n\nfunction scheduleFrame(flush: () => void): (() => void) | null {\n if (customScheduler) return customScheduler(flush);\n if (inActEnvironment()) {\n flush();\n return null;\n }\n return animationFrameScheduler(flush);\n}\n\n/**\n * Collects frames for one session and applies them once per frame. Returns\n * whether the flushed batch settled a turn, for the list invalidation.\n */\nclass FrameQueue {\n private queue: VerseEvent[] = [];\n private cancel: (() => void) | null = null;\n private scheduled = false;\n\n constructor(private readonly sessionId: string) {}\n\n push(event: VerseEvent): void {\n this.queue.push(event);\n if (this.scheduled) return;\n this.scheduled = true;\n const cancel = scheduleFrame(() => this.flush());\n // A synchronous scheduler (act environment) has already flushed.\n if (this.scheduled) this.cancel = cancel;\n }\n\n /** Apply everything queued now. Safe to call with nothing queued. */\n flush(): void {\n this.scheduled = false;\n this.cancel?.();\n this.cancel = null;\n if (this.queue.length === 0) return;\n const batch = this.queue;\n this.queue = [];\n const result = applyVerseEvents(this.sessionId, batch);\n if (result.settled) invalidateVerseLists();\n }\n\n drop(): void {\n this.scheduled = false;\n this.cancel?.();\n this.cancel = null;\n this.queue = [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// One resumable stream\n// ---------------------------------------------------------------------------\n\ninterface LiveStream {\n close(): void;\n /** Tear down and reconnect from the current cursor (Retry). */\n restart(): void;\n}\n\n/**\n * Set once this page has PROVEN that the server refuses `?after=`: a\n * connection with the cursor closed before it opened, and the same stream\n * without it then opened. A read-session boundary that accepts only the\n * `?client=` proof on SSE paths answers 401 to any extra parameter, and a\n * chat that cannot connect at all is far worse than one that replays (the\n * store drops what it already holds). Page-lifetime only: a server that gains\n * support is used again after a reload.\n */\nlet afterRefused = false;\n\n/** Tests: forget a proven refusal. */\nexport function resetVerseStreamCapabilities(): void {\n afterRefused = false;\n}\n\n/** `EventSource.CONNECTING` — the browser is reconnecting by itself. */\nconst ES_CONNECTING = 0;\n\nfunction openResumableStream(sessionId: string): LiveStream {\n let source: EventSource | null = null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let disposed = false;\n /** This connection is the no-`after` retry that tests whether the cursor was refused. */\n let probing = false;\n const queue = new FrameQueue(sessionId);\n\n const connect = () => {\n if (disposed || typeof EventSource === 'undefined') return;\n if (getAuthSnapshot().phase !== 'authenticated') return;\n setVerseStreamState(sessionId, attempt === 0 ? 'connecting' : 'reconnecting');\n // The cursor is read at CONNECT time, after everything already received\n // was applied, so a reconnect asks only for what it has not seen.\n const cursor = afterRefused || probing ? 0 : getVerseSessionState(sessionId).lastSeq;\n const es = new EventSource(verseSessionStreamUrl(sessionId, cursor), { withCredentials: true });\n let opened = false;\n source = es;\n es.onopen = () => {\n opened = true;\n attempt = 0;\n if (probing) {\n afterRefused = true;\n probing = false;\n }\n setVerseStreamState(sessionId, 'open');\n };\n for (const type of VERSE_EVENT_TYPES) {\n es.addEventListener(type, (evt) => {\n if (source !== es) return;\n const event = parseVerseEventFrame((evt as MessageEvent<string>).data);\n if (event) queue.push(event);\n });\n }\n es.onerror = (evt) => {\n // A server-sent event NAMED `error` (a VerseEvent.type) also dispatches\n // through onerror because onerror is the handler for event type \"error\".\n // It arrives as a MessageEvent with data; a real transport failure does\n // not. Without this guard every vendor error replayed on reconnect would\n // tear the stream down again, forever.\n if (typeof MessageEvent !== 'undefined' && evt instanceof MessageEvent) return;\n // Everything received before the drop moves the cursor first.\n queue.flush();\n if (disposed) return;\n // A dropped connection (network blip, server restart, the server\n // closing a congested stream) is retried by the BROWSER, which sends\n // Last-Event-ID — the last persisted seq, since transient frames carry\n // no id — so it resumes without a replay. Only a refused one (an HTTP\n // error) ends up CLOSED and is ours to retry.\n if (typeof es.readyState === 'number' && es.readyState === ES_CONNECTING) {\n setVerseStreamState(sessionId, 'reconnecting');\n return;\n }\n es.close();\n if (source === es) source = null;\n setVerseStreamState(sessionId, 'reconnecting');\n if (getAuthSnapshot().phase !== 'authenticated') return;\n if (!opened && cursor > 0 && !probing) {\n // Refused before it opened, with a cursor: find out at once whether\n // the cursor is what was refused (see afterRefused).\n probing = true;\n connect();\n return;\n }\n probing = false;\n timer = setTimeout(() => {\n timer = null;\n attempt += 1;\n connect();\n }, backoffMs(attempt));\n };\n };\n\n const teardown = () => {\n if (timer) clearTimeout(timer);\n timer = null;\n source?.close();\n source = null;\n };\n\n connect();\n\n return {\n close() {\n if (disposed) return;\n disposed = true;\n teardown();\n // Frames already received still belong in the store (a lingering stream\n // closing mid-turn must not lose its last tokens).\n queue.flush();\n queue.drop();\n setVerseStreamState(sessionId, 'closed');\n },\n restart() {\n if (disposed) return;\n teardown();\n queue.flush();\n attempt = 0;\n connect();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Ref-counted registry\n// ---------------------------------------------------------------------------\n\ninterface RegistryEntry {\n stream: LiveStream;\n refs: number;\n idleTimer: ReturnType<typeof setTimeout> | null;\n /** When the last reference was released (for evicting the oldest lingering stream). */\n releasedAt: number;\n}\n\nconst registry = new Map<string, RegistryEntry>();\n\nfunction closeEntry(sessionId: string): void {\n const entry = registry.get(sessionId);\n if (!entry) return;\n registry.delete(sessionId);\n if (entry.idleTimer) clearTimeout(entry.idleTimer);\n entry.stream.close();\n}\n\nfunction evictIdle(): void {\n const idle = [...registry.entries()].filter(([, e]) => e.refs === 0).sort((a, b) => a[1].releasedAt - b[1].releasedAt);\n while (idle.length > MAX_IDLE_STREAMS) closeEntry(idle.shift()![0]);\n}\n\n/**\n * Hold the live stream for `sessionId` open. Returns the release. The first\n * acquire connects (resuming after the store's cursor); later ones share it.\n */\nexport function acquireVerseSessionStream(sessionId: string): () => void {\n let entry = registry.get(sessionId);\n if (entry) {\n entry.refs += 1;\n if (entry.idleTimer) clearTimeout(entry.idleTimer);\n entry.idleTimer = null;\n } else {\n entry = { stream: openResumableStream(sessionId), refs: 1, idleTimer: null, releasedAt: 0 };\n registry.set(sessionId, entry);\n }\n const held = entry;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (registry.get(sessionId) !== held) return;\n held.refs -= 1;\n if (held.refs > 0) return;\n held.releasedAt = Date.now();\n held.idleTimer = setTimeout(() => {\n if (registry.get(sessionId) === held && held.refs === 0) closeEntry(sessionId);\n }, IDLE_CLOSE_MS);\n evictIdle();\n };\n}\n\n/** Close every live stream (logout; tests). */\nexport function closeAllVerseSessionStreams(): void {\n for (const id of [...registry.keys()]) closeEntry(id);\n}\n\n/** Diagnostics/tests: the sessions with a stream held open, and their reference counts. */\nexport function verseStreamRegistrySnapshot(): Array<{ sessionId: string; refs: number }> {\n return [...registry.entries()].map(([sessionId, e]) => ({ sessionId, refs: e.refs }));\n}\n\n// A deleted chat has no stream to keep; a cleared store (logout, test reset)\n// has no chats at all.\nsubscribeVerseStoreLifecycle((event) => {\n if (event.kind === 'reset') closeAllVerseSessionStreams();\n else closeEntry(event.sessionId);\n});\n\n// ---------------------------------------------------------------------------\n// Opening a chat\n// ---------------------------------------------------------------------------\n\nexport interface OpenVerseSessionOptions {\n /** Re-fetch the detail and reconnect even when the store already holds the chat (the transcript's Retry). */\n reload?: boolean;\n}\n\n/**\n * Show one chat: load it (once) and keep its live stream attached until the\n * returned disposer runs.\n *\n * - Not loaded yet → fetch the detail, seed the store, THEN attach the\n * stream from the seeded cursor. No race, no full replay.\n * - Already loaded (switching back) → attach at once from the cursor; a\n * stream still lingering from the last visit has nothing to catch up.\n * - `reload` → refetch the detail and restart the connection.\n */\nexport function openVerseSession(sessionId: string, options: OpenVerseSessionOptions = {}): () => void {\n const controller = new AbortController();\n let release: (() => void) | null = null;\n let disposed = false;\n\n const attach = () => {\n if (disposed || release) return;\n release = acquireVerseSessionStream(sessionId);\n };\n\n const state = getVerseSessionState(sessionId);\n const needsLoad = options.reload === true || !state.loaded || state.loadError !== null;\n\n if (!needsLoad) {\n attach();\n } else {\n if (options.reload) registry.get(sessionId)?.stream.restart();\n void fetchVerseSessionDetail(sessionId, controller.signal)\n .then((detail) => {\n if (controller.signal.aborted) return;\n seedVerseSession(sessionId, detail.session, detail.events);\n attach();\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setVerseLoadError(sessionId, err instanceof Error ? err.message : 'Could not load this chat.');\n });\n }\n\n return () => {\n disposed = true;\n controller.abort();\n release?.();\n release = null;\n };\n}\n","/**\n * routes/verse/useVerseSession.ts — the only React glue over verse-store /\n * session-stream (same split as data/hooks.ts over cache.ts + sse.ts).\n *\n * Three hooks, three snapshots, three render frequencies (V3.10):\n *\n * useVerseSession(id) the HEAD: session record, load/stream state and\n * the event log as of the last structural event.\n * Owns opening the chat. Does NOT change per\n * streamed token, so the Chat section, sidebar,\n * header and resources panel stop re-rendering at\n * token rate.\n * useVerseTranscript(id) the derived transcript — changes at most once per\n * animation frame while text streams. Lives in\n * useVerseTranscript.ts: the derivation it reads is\n * kept off the chat first-paint path.\n * useVerseLive(id) transient signals of the running turn\n * (reasoning, progress, retry/watchdog notices).\n *\n * Each subscribes to its own session only, so a stream feeding a lingering\n * chat in the background re-renders nothing that shows another one.\n */\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';\nimport { openVerseSession } from './session-stream.js';\nimport {\n getVerseLive,\n getVerseSessionHead,\n subscribeVerseSession,\n type Transcript,\n type VerseLiveState,\n type VerseSessionHead,\n} from './verse-store.js';\n\nexport interface VerseSessionView extends VerseSessionHead {\n /**\n * A transcript supplied WITH the view — only a hand-built view (a test, a\n * preview) carries one. The live app leaves it unset and the workspace\n * subscribes to the transcript itself (useVerseTranscript), which is what\n * keeps a streamed token from re-rendering everything that holds the view.\n */\n transcript?: Transcript;\n}\n\nconst noop = () => () => {};\n\nexport function useSessionSubscription(sessionId: string | null) {\n return useCallback(\n (listener: () => void) => (sessionId ? subscribeVerseSession(sessionId, listener) : noop()),\n [sessionId],\n );\n}\n\n/**\n * @param reload bump to re-fetch the detail + reconnect for the same id\n * (the transcript's Retry button).\n */\nexport function useVerseSession(sessionId: string | null, reload = 0): VerseSessionView {\n const subscribe = useSessionSubscription(sessionId);\n const head = useSyncExternalStore(\n subscribe,\n () => getVerseSessionHead(sessionId),\n () => getVerseSessionHead(sessionId),\n );\n\n // Only a BUMP of `reload` for the chat already open means Retry; the\n // counter itself is never reset by the host, so \"reload > 0\" would turn\n // every later selection into a full refetch.\n const lastOpened = useRef<{ sessionId: string | null; reload: number }>({ sessionId: null, reload });\n useEffect(() => {\n const previous = lastOpened.current;\n lastOpened.current = { sessionId, reload };\n if (!sessionId) return undefined;\n return openVerseSession(sessionId, { reload: previous.sessionId === sessionId && previous.reload !== reload });\n }, [sessionId, reload]);\n\n return head;\n}\n\n/** Live (never persisted) signals of the chat's running turn. */\nexport function useVerseLive(sessionId: string | null): VerseLiveState {\n const subscribe = useSessionSubscription(sessionId);\n return useSyncExternalStore(\n subscribe,\n () => getVerseLive(sessionId),\n () => getVerseLive(sessionId),\n );\n}\n","/**\n * routes/verse/chat/use-chat-activity.ts — the Chat surface's view of C1's\n * activity + session-meta routes (unit C2).\n *\n * WHY NOT C1's useActivity: that hook runs the shell's CURSOR loop (it owns\n * completions, the rail badges and the native \"Finished:\" notifications).\n * The chat list needs only the present tense — who is running, what they are\n * doing, what needs the operator — so it reads the same route without a\n * cursor, through the shared query cache under VERSE_ACTIVITY_KEY. Any other\n * reader of that key (the rail, a future pane) shares the one request.\n *\n * Polls every 5 s ONLY while the Chat surface and the window are visible\n * (C0's usePollWhileVisible), and catches up the moment it is shown again.\n * Activity answers in < 5 ms from memory (SPEC-310C budget), so this is\n * cheap; it is still never faster than the 2 s floor.\n *\n * Also owns this tab's \"seen\" map: opening a chat clears its unread dot NOW,\n * whether or not the server-side POST can run (it needs a held token).\n */\nimport { useCallback, useSyncExternalStore } from 'react';\nimport { useQuery, useRefetch } from '../../../data/hooks.js';\nimport { usePollWhileVisible } from '../shell/section-visibility.js';\nimport { markVerseSessionSeen, verseActivityQuery, verseSessionMetaQuery } from '../verse-queries.js';\n\nexport const CHAT_ACTIVITY_POLL_MS = 5_000;\n\n// ---- local seen map --------------------------------------------------------\n\nlet seen = new Map<string, number>();\nconst seenListeners = new Set<() => void>();\n\nfunction subscribeSeen(listener: () => void): () => void {\n seenListeners.add(listener);\n return () => seenListeners.delete(listener);\n}\n\nfunction getSeen(): ReadonlyMap<string, number> {\n return seen;\n}\n\n/** Record that the operator saw `sessionId` up to `turnCount`, locally and (best-effort) on the server. */\nexport function noteSessionSeen(sessionId: string, turnCount: number): void {\n if ((seen.get(sessionId) ?? -1) >= turnCount) return;\n seen = new Map(seen).set(sessionId, turnCount);\n for (const listener of [...seenListeners]) listener();\n void markVerseSessionSeen(sessionId, turnCount);\n}\n\n/** Test seam. */\nexport function resetLocalSeen(): void {\n seen = new Map();\n for (const listener of [...seenListeners]) listener();\n}\n\nexport function useLocalSeen(): ReadonlyMap<string, number> {\n return useSyncExternalStore(subscribeSeen, getSeen, getSeen);\n}\n\n// ---- queries ---------------------------------------------------------------\n\nexport function useChatActivity() {\n const activity = useQuery(verseActivityQuery, { freshMs: CHAT_ACTIVITY_POLL_MS - 500 });\n const meta = useQuery(verseSessionMetaQuery);\n const refetchActivity = useRefetch(verseActivityQuery);\n const tick = useCallback(() => refetchActivity(), [refetchActivity]);\n usePollWhileVisible(tick, CHAT_ACTIVITY_POLL_MS);\n const localSeen = useLocalSeen();\n return {\n /** null = the route is not mounted on this server (or has not answered yet). */\n activity: activity.data ?? null,\n meta: meta.data ?? null,\n localSeen,\n };\n}\n","/**\n * routes/verse/chat-panel-sizing.ts — how wide the two side panels of the\n * Chat section are, and what the viewport will actually let them be.\n *\n * WHY THIS IS NOT `ashlr.verse.ui.v2`. That key's shape is the shell contract\n * (docs/VERSE-CONTRACT-V2.md) and verse-ui-store.test pins it field for\n * field; the same precedent that put `resources-collapse.ts` under its own\n * key applies here. This module also needs something the shell store cannot\n * express: a width is not a single number but a PAIR — what the operator\n * asked for, and what the window is currently wide enough to give them.\n *\n * DESIRED vs EFFECTIVE. A sidebar dragged to 420px on a 1900px display must\n * not strand the transcript when the same window is later 1100px wide. So the\n * operator's choice (`desired`) is what persists, and `effective` is that\n * choice re-fitted to the container on every layout change. Shrinking the\n * window narrows the panel; widening it gives the width back, because the\n * transient value was never written down.\n *\n * Pure aside from the two localStorage functions, and framework-free — the\n * React glue lives in ChatResizer.tsx, the same split as\n * verse-ui-store.ts / useVerseUi.ts.\n */\n\nexport type ChatPanelSide = 'sidebar' | 'resources';\n\nexport interface ChatPanelRange {\n readonly min: number;\n readonly max: number;\n readonly def: number;\n}\n\n/**\n * Minimum and maximum are both load-bearing. The minimum is the width below\n * which the panel stops being a panel and becomes a sliver of truncated\n * text; the maximum keeps a wide display from turning the transcript into a\n * column. Defaults match what the shell has always opened at, so nothing\n * moves for an operator who never drags anything.\n */\nexport const CHAT_PANEL_RANGES: Readonly<Record<ChatPanelSide, ChatPanelRange>> = {\n sidebar: { min: 220, max: 420, def: 264 },\n resources: { min: 240, max: 520, def: 320 },\n};\n\n/**\n * The transcript's floor. Below `--measure-read` (720px) reading is already\n * compromised; 480px is where the composer's own controls start wrapping, and\n * it is the point past which a side panel must give width back instead of\n * taking more. The narrow-width media queries in ChatSection.module.css take\n * over below that — this number only decides who yields first.\n */\nexport const MIN_TRANSCRIPT_WIDTH = 480;\n\n/** The 1px hairline track each visible resizer occupies. */\nconst HAIRLINE = 1;\n\n/** Arrow-key step, and the coarse step Shift asks for. */\nexport const CHAT_PANEL_STEP = 16;\nexport const CHAT_PANEL_STEP_COARSE = 64;\n\nexport const CHAT_PANEL_SIZING_KEY = 'ashlr.verse.panels.v1';\n\n/**\n * Read-only migration source. Panel widths lived in the shell store before\n * they were draggable; seeding from it once means an upgrade does not reset\n * a width the operator had already chosen. Never written to from here.\n */\nconst LEGACY_SHELL_KEY = 'ashlr.verse.ui.v2';\n\nexport interface ChatPanelWidths {\n readonly sidebar: number;\n readonly resources: number;\n}\n\n/** What the chat grid can currently afford. */\nexport interface ChatPanelFit {\n /**\n * Width in px of the chat grid itself (NOT the window): rail excluded,\n * because the rail is not the section's to spend. Zero or unknown — jsdom,\n * a hidden section, the first paint — disables viewport fitting entirely\n * rather than guessing, so a width is never clamped against a measurement\n * that does not exist.\n */\n readonly containerWidth: number;\n readonly sidebarVisible: boolean;\n readonly resourcesVisible: boolean;\n}\n\nexport const UNKNOWN_FIT: ChatPanelFit = {\n containerWidth: 0,\n sidebarVisible: true,\n resourcesVisible: true,\n};\n\nexport interface ChatPanelSizing {\n /** The operator's choice. This is what persists. */\n readonly desired: ChatPanelWidths;\n /** That choice, re-fitted to the current container. This is what renders. */\n readonly effective: ChatPanelWidths;\n}\n\n/**\n * Clamp one width into its own range. Nonsense — NaN, a string, a negative\n * number out of a hand-edited localStorage entry — resolves to the default\n * rather than to the minimum, so a corrupt value reads as \"never chosen\".\n */\nexport function clampPanelWidth(side: ChatPanelSide, value: unknown): number {\n const range = CHAT_PANEL_RANGES[side];\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/**\n * Re-fit a chosen pair of widths to the container.\n *\n * The resources panel yields first: it is the optional one (it has a toggle,\n * the sidebar's collapse is an overlay at phone width), so on a narrowing\n * window the operator loses reference material before they lose navigation.\n * Neither panel is ever pushed below its own minimum — past that point the\n * media queries hide a panel outright, which is a better answer than a 40px\n * stub of clipped text.\n */\nexport function fitChatPanels(widths: ChatPanelWidths, fit: ChatPanelFit): ChatPanelWidths {\n let sidebar = clampPanelWidth('sidebar', widths.sidebar);\n let resources = clampPanelWidth('resources', widths.resources);\n if (!Number.isFinite(fit.containerWidth) || fit.containerWidth <= 0) return { sidebar, resources };\n\n const hairlines = (fit.sidebarVisible ? HAIRLINE : 0) + (fit.resourcesVisible ? HAIRLINE : 0);\n const budget = fit.containerWidth - MIN_TRANSCRIPT_WIDTH - hairlines;\n const spent = (fit.sidebarVisible ? sidebar : 0) + (fit.resourcesVisible ? resources : 0);\n let over = spent - budget;\n if (over <= 0) return { sidebar, resources };\n\n if (fit.resourcesVisible) {\n const give = Math.min(over, resources - CHAT_PANEL_RANGES.resources.min);\n resources -= give;\n over -= give;\n }\n if (over > 0 && fit.sidebarVisible) {\n const give = Math.min(over, sidebar - CHAT_PANEL_RANGES.sidebar.min);\n sidebar -= give;\n }\n return { sidebar, resources };\n}\n\n// ---------------------------------------------------------------------------\n// Persistence\n// ---------------------------------------------------------------------------\n\nexport function defaultChatPanelWidths(): ChatPanelWidths {\n return { sidebar: CHAT_PANEL_RANGES.sidebar.def, resources: CHAT_PANEL_RANGES.resources.def };\n}\n\n/** Parse either this module's payload or the legacy shell payload. */\nexport function parseChatPanelWidths(raw: string | null): ChatPanelWidths | null {\n if (raw === null || raw === '') return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null;\n const record = parsed as Record<string, unknown>;\n // `sidebarWidth`/`resourcesWidth` is the legacy shell spelling.\n const sidebar = record.sidebar ?? record.sidebarWidth;\n const resources = record.resources ?? record.resourcesWidth;\n if (sidebar === undefined && resources === undefined) return null;\n return {\n sidebar: clampPanelWidth('sidebar', sidebar),\n resources: clampPanelWidth('resources', resources),\n };\n}\n\nexport function readChatPanelWidths(): ChatPanelWidths {\n try {\n const own = parseChatPanelWidths(localStorage.getItem(CHAT_PANEL_SIZING_KEY));\n if (own) return own;\n // First run after the upgrade: inherit whatever the shell store had.\n return parseChatPanelWidths(localStorage.getItem(LEGACY_SHELL_KEY)) ?? defaultChatPanelWidths();\n } catch {\n return defaultChatPanelWidths();\n }\n}\n\nexport function writeChatPanelWidths(widths: ChatPanelWidths): void {\n try {\n localStorage.setItem(CHAT_PANEL_SIZING_KEY, JSON.stringify({\n sidebar: widths.sidebar,\n resources: widths.resources,\n }));\n } catch {\n /* a private window without storage still runs the app */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Store\n// ---------------------------------------------------------------------------\n\nlet desired: ChatPanelWidths = readChatPanelWidths();\nlet fit: ChatPanelFit = UNKNOWN_FIT;\nlet snapshot: ChatPanelSizing = { desired, effective: fitChatPanels(desired, fit) };\nconst listeners = new Set<() => void>();\n\nfunction sameWidths(a: ChatPanelWidths, b: ChatPanelWidths): boolean {\n return a.sidebar === b.sidebar && a.resources === b.resources;\n}\n\n/** Snapshot identity changes only when a number actually changed. */\nfunction publish(): void {\n const effective = fitChatPanels(desired, fit);\n if (sameWidths(snapshot.desired, desired) && sameWidths(snapshot.effective, effective)) return;\n snapshot = { desired, effective };\n for (const listener of listeners) listener();\n}\n\nexport function subscribeChatPanels(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function getChatPanelSizing(): ChatPanelSizing {\n return snapshot;\n}\n\n/**\n * Publish what the layout can afford. Called on mount, on window resize and\n * whenever a panel is shown or hidden. Deliberately does NOT persist: a\n * narrow window borrows width, it does not take it.\n */\nexport function setChatPanelFit(next: ChatPanelFit): void {\n if (\n fit.containerWidth === next.containerWidth\n && fit.sidebarVisible === next.sidebarVisible\n && fit.resourcesVisible === next.resourcesVisible\n ) return;\n fit = next;\n publish();\n}\n\nfunction commit(next: ChatPanelWidths): void {\n if (sameWidths(desired, next)) return;\n desired = next;\n writeChatPanelWidths(desired);\n publish();\n}\n\n/** A drag or a direct set. Range-clamped; the fit is applied on read. */\nexport function setChatPanelWidth(side: ChatPanelSide, value: number): void {\n commit({ ...desired, [side]: clampPanelWidth(side, value) });\n}\n\n/**\n * An arrow key. Steps from the EFFECTIVE width, so a keypress moves the\n * handle the operator can see rather than an off-screen remembered one.\n */\nexport function nudgeChatPanelWidth(side: ChatPanelSide, delta: number): void {\n setChatPanelWidth(side, snapshot.effective[side] + delta);\n}\n\n/** Double-click, and the Home/End extremes. */\nexport function resetChatPanelWidth(side: ChatPanelSide): void {\n setChatPanelWidth(side, CHAT_PANEL_RANGES[side].def);\n}\n\n/** Test hygiene — mirrors resetVerseUi(). */\nexport function resetChatPanelSizing(): void {\n desired = defaultChatPanelWidths();\n fit = UNKNOWN_FIT;\n snapshot = { desired, effective: fitChatPanels(desired, fit) };\n try {\n localStorage.removeItem(CHAT_PANEL_SIZING_KEY);\n } catch {\n /* ignore */\n }\n for (const listener of listeners) listener();\n}\n\n/** Re-read storage into the store (a fresh tab, or a test that seeded a key). */\nexport function reloadChatPanelSizing(): void {\n desired = readChatPanelWidths();\n snapshot = { desired, effective: fitChatPanels(desired, fit) };\n for (const listener of listeners) listener();\n}\n","/**\n * routes/verse/ChatResizer.tsx — the drag handle between the chat\n * transcript and a side panel.\n *\n * A 1px hairline that behaves like a 9px target: the hairline is the design\n * (DESIGN-V2 has no chrome), the grab area is the ergonomics. Three things\n * about it are not obvious and are all deliberate:\n *\n * 1. IT IS A CONTROL, NOT A DECORATION. `role=\"separator\"` with a tabindex is\n * the ARIA window-splitter pattern: arrow keys move it, Home/End take it\n * to its limits, and a double-click restores the default. A pointer-only\n * resizer simply does not exist for a keyboard operator.\n *\n * 2. IT MUST NOT EAT THE macOS DRAG STRIP. The grab area overhangs ±4px into\n * the panels either side, and the top 48px of those panels is the window's\n * own drag region (Sidebar `.headDragStrip`, Workspace `.header`\n * data-app-region=\"drag\"). It is painted after them, so without the\n * `--app-titlebar-height` inset in the stylesheet it would steal 4px of\n * the strip AND turn the macOS double-click-to-zoom into a width reset.\n * See desktop/README.md \"Desktop shell contract\".\n *\n * 3. THE DRAG IS RELEASED FROM `window`, NOT FROM THE ELEMENT. Pointer\n * capture is requested, but it is not relied on: a capture that fails, a\n * pointer that leaves the window, a button released over another app and\n * a mid-drag unmount all have to end the drag, because a stuck drag state\n * (everything selects, the cursor sticks) is the most visible bug this\n * control can have.\n */\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore, type KeyboardEvent, type PointerEvent } from 'react';\nimport {\n CHAT_PANEL_RANGES,\n CHAT_PANEL_STEP,\n CHAT_PANEL_STEP_COARSE,\n getChatPanelSizing,\n nudgeChatPanelWidth,\n resetChatPanelWidth,\n setChatPanelWidth,\n subscribeChatPanels,\n type ChatPanelSide,\n type ChatPanelSizing,\n} from './chat-panel-sizing.js';\n\n/** The React glue over chat-panel-sizing.ts (same split as useVerseUi.ts). */\nexport function useChatPanelSizing(): ChatPanelSizing {\n return useSyncExternalStore(subscribeChatPanels, getChatPanelSizing, getChatPanelSizing);\n}\n\n/**\n * Marks the body for the duration of a drag so the stylesheet can suppress\n * selection app-wide. An attribute rather than an inline style: it survives\n * React re-renders, it is one line of CSS, and a test can see it.\n */\nconst BODY_FLAG = 'verseResizing';\n\nfunction markBody(on: boolean): void {\n try {\n if (on) document.body.dataset[BODY_FLAG] = 'true';\n else delete document.body.dataset[BODY_FLAG];\n } catch {\n /* a detached document in a test teardown is not worth throwing over */\n }\n}\n\nexport interface ChatResizerProps {\n side: ChatPanelSide;\n /** The control's accessible name — what it resizes, in the operator's words. */\n label: string;\n className?: string;\n}\n\ninterface DragState {\n pointerId: number;\n originX: number;\n startWidth: number;\n}\n\nexport function ChatResizer({ side, label, className }: ChatResizerProps) {\n const sizing = useChatPanelSizing();\n const range = CHAT_PANEL_RANGES[side];\n const width = sizing.effective[side];\n const drag = useRef<DragState | null>(null);\n const [dragging, setDragging] = useState(false);\n\n /** The one way a drag ends. Idempotent, so every path can call it. */\n const endDrag = useCallback(() => {\n if (!drag.current) return;\n drag.current = null;\n setDragging(false);\n markBody(false);\n }, []);\n\n const onPointerDown = useCallback((event: PointerEvent<HTMLDivElement>) => {\n // Primary button only: a right-click here belongs to the context menu,\n // and a middle-click must not start a drag nobody asked for.\n if (event.button !== 0) return;\n event.preventDefault();\n drag.current = { pointerId: event.pointerId, originX: event.clientX, startWidth: width };\n setDragging(true);\n markBody(true);\n // Best-effort: it keeps the cursor and the events on the handle while the\n // pointer wanders. The window listeners below are the actual contract.\n try {\n event.currentTarget.setPointerCapture?.(event.pointerId);\n } catch {\n /* unsupported, or the pointer is already gone — the window listeners cover it */\n }\n }, [width]);\n\n // Move/end listeners live on the window for the whole drag, so a pointer\n // that leaves the viewport or a button released over another window still\n // lands here. Unmounting mid-drag runs the cleanup and clears the body flag.\n useEffect(() => {\n if (!dragging) return undefined;\n\n const onMove = (event: globalThis.PointerEvent) => {\n const state = drag.current;\n if (!state || event.pointerId !== state.pointerId) return;\n const delta = event.clientX - state.originX;\n // The sidebar grows to the right, the resources panel to the left.\n setChatPanelWidth(side, side === 'sidebar' ? state.startWidth + delta : state.startWidth - delta);\n };\n const onEnd = (event: globalThis.PointerEvent) => {\n const state = drag.current;\n if (state && event.pointerId !== state.pointerId) return;\n endDrag();\n };\n /** Escape puts the width back where the drag started. */\n const onKey = (event: globalThis.KeyboardEvent) => {\n const state = drag.current;\n if (!state || event.key !== 'Escape') return;\n setChatPanelWidth(side, state.startWidth);\n endDrag();\n };\n // The window losing focus (⌘-tab mid-drag, an OS dialog) never delivers a\n // pointerup, so treat it as one rather than staying stuck.\n const onBlur = () => endDrag();\n\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onEnd);\n window.addEventListener('pointercancel', onEnd);\n window.addEventListener('keydown', onKey);\n window.addEventListener('blur', onBlur);\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onEnd);\n window.removeEventListener('pointercancel', onEnd);\n window.removeEventListener('keydown', onKey);\n window.removeEventListener('blur', onBlur);\n markBody(false);\n };\n }, [dragging, side, endDrag]);\n\n const onKeyDown = useCallback((event: KeyboardEvent<HTMLDivElement>) => {\n const step = event.shiftKey ? CHAT_PANEL_STEP_COARSE : CHAT_PANEL_STEP;\n // Left/Right are screen directions, so which one GROWS depends on which\n // edge of the transcript this handle is.\n const toward = side === 'sidebar' ? 1 : -1;\n switch (event.key) {\n case 'ArrowRight':\n nudgeChatPanelWidth(side, step * toward);\n break;\n case 'ArrowLeft':\n nudgeChatPanelWidth(side, -step * toward);\n break;\n case 'Home':\n setChatPanelWidth(side, range.min);\n break;\n case 'End':\n setChatPanelWidth(side, range.max);\n break;\n case 'Enter':\n resetChatPanelWidth(side);\n break;\n default:\n return;\n }\n event.preventDefault();\n }, [side, range.min, range.max]);\n\n return (\n <div\n className={className}\n data-verse-resizer={side}\n data-dragging={dragging ? 'true' : undefined}\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={label}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n aria-valuenow={width}\n aria-valuetext={`${width} pixels`}\n tabIndex={0}\n onPointerDown={onPointerDown}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n onLostPointerCapture={endDrag}\n onKeyDown={onKeyDown}\n // The thing people reach for after over-dragging. In the desktop app a\n // double-click on a DRAG region zooms the window instead — which is why\n // the stylesheet keeps this handle's grab area out of the title bar.\n onDoubleClick={() => resetChatPanelWidth(side)}\n />\n );\n}\n","/**\n * routes/verse/sections/ChatSection.tsx — the Chat surface (⌘5):\n *\n * [sessions 264 | transcript ≤720 | dock 440 (tabs, or two panes split)]\n *\n * plus the new-chat dialog, the delete confirmation, and every mutation the\n * chat surface makes (SPEC-310C §2–§3, unit C2).\n *\n * Takes NO props (the shell contract): the shell lazily mounts it. The ways\n * INTO it from elsewhere in the workbench arrive two ways, both C1's:\n * - the shell's one-shot command (verse-ui-store): `new-chat` (⌘N, \"New\n * chat on…\", with a seat or project), `open-session` (a Needs-you item, a\n * notification, ⌃Tab, ⌘[ / ⌘]), `focus-composer` (⌃⌥Space);\n * - the command bus (shell/command-bus.ts): this section serves the\n * catalog's chat-scope commands — `dock.*`, `chat.sidebar`, `chat.find`,\n * `chat.turn-prev/next` — for the palette, the native menu and keys.\n * And it reports which chat is open (setVerseActiveSession), which feeds\n * ⌃Tab's recent list and back/forward.\n *\n * 3.10 changes, briefly:\n * - the resources column became the DOCK (dock/Dock.tsx): Terminal,\n * Preview and Review through C0's slots, Tasks and Context of its own;\n * below 1024px it is a sheet, below 480px a bottom sheet;\n * - the chat list gained filters, pins, archive, unread and a live second\n * line (C1's activity + session-meta routes, tolerated when absent);\n * - the ⌘K chat switcher is gone — ⌘K is the shell's command palette;\n * - the chat-scope keys (⌘\\ ⌃` ⌃⇧` ⇧⌘B ⇧⌘D ⌘B) run the same handlers,\n * matched from C0's catalog while this surface is visible — and only when\n * the shell's own key handler has not already taken the event.\n *\n * Mutations go through one guard: if no mutation token is held,\n * MutationTokenDialog opens and the action re-runs once unlocked.\n */\nimport { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState, type ComponentProps, type CSSProperties } from 'react';\nimport type { VerseCreateSessionRequest, VerseSession } from '../../../data/api-types.js';\nimport type { MutationTokenDialog as MutationTokenDialogComponent } from '../../../components/auth/MutationTokenDialog.js';\nimport { useToast } from '../../../components/primitives/Toast.js';\nimport { clearReadSession } from '../../../data/auth-store.js';\nimport { ApiError, DispatchDisabledError } from '../../../data/client.js';\nimport { useMutationHold, useQuery, useRefresh } from '../../../data/hooks.js';\nimport type { DeleteChatDialog as DeleteChatDialogComponent } from '../chat/DeleteChatDialog.js';\nimport { insertIntoComposer } from '../chat/composer-bridge.js';\nimport { forgetComposerMemory } from '../chat/composer-memory.js';\nimport type { ChatTask } from '../chat/tasks-model.js';\nimport { requestTranscriptFind, requestTranscriptStep } from '../chat/transcript-jump.js';\nimport { noteSessionSeen, useChatActivity } from '../chat/use-chat-activity.js';\nimport { useSessionRoots } from '../chat/use-session-roots.js';\nimport { ChatResizer, useChatPanelSizing } from '../ChatResizer.js';\nimport { CHAT_PANEL_RANGES, MIN_TRANSCRIPT_WIDTH, setChatPanelFit } from '../chat-panel-sizing.js';\nimport { clearDockRequests, requestTerminal, toggleDock, toggleDockPane, useDock } from '../dock/dock-store.js';\nimport type { SeatChoice } from '../SeatSelector.js';\nimport { clampDockWidth, DOCK_LAYOUT, dockPresentation } from '../shell/dock-catalog.js';\nimport { useCommandHandler } from '../shell/command-bus.js';\nimport { matchKey } from '../shell/command-keys.js';\nimport { preloadedLazy, preloadedModule } from '../shell/preloaded.js';\nimport { useSectionVisible } from '../shell/section-visibility.js';\nimport type { TurnFileChange } from '../shell/slots.js';\nimport type { Sidebar as SidebarComponent, SidebarRowActions } from '../Sidebar.js';\nimport { SkeletonLine } from '../../../components/primitives/Skeleton.js';\nimport { useVerseSession } from '../useVerseSession.js';\nimport { useVerseUi } from '../useVerseUi.js';\nimport { openVerseListChannel } from '../verse-events.js';\nimport {\n cancelVerseTurn,\n createVerseSession,\n deleteVerseSession,\n renameVerseSession,\n sendVerseTurn,\n setVerseSessionMeta,\n verseBootstrapQuery,\n verseSessionsQuery,\n verseWorkspacesQuery,\n VerseMutationLockedError,\n} from '../verse-queries.js';\nimport { forgetVerseSession, setVerseSession, setVerseSessionStatus } from '../verse-store.js';\nimport {\n clearVerseCommand,\n lastVerseSeat,\n rememberVerseSeat,\n setVerseActiveSession,\n setVerseSection,\n setVerseSidebarCollapsed,\n} from '../verse-ui-store.js';\nimport type { Workspace as WorkspaceComponent } from '../Workspace.js';\nimport styles from './ChatSection.module.css';\n\n// Loaded on first use, not with the chat: the dock starts closed and the\n// new-chat dialog opens on request (SPEC-310C budget: chat critical JS).\nconst DockHost = lazy(() => import('../dock/DockHost.js').then((m) => ({ default: m.DockHost })));\nconst NewChatDialog = lazy(() => import('../NewChatDialog.js').then((m) => ({ default: m.NewChatDialog })));\n\nconst WorkspaceModule = preloadedLazy<ComponentProps<typeof WorkspaceComponent>>(() => import('../Workspace.js').then((m) => m.Workspace));\nconst Workspace = WorkspaceModule.Slot;\nconst SidebarModule = preloadedLazy<ComponentProps<typeof SidebarComponent>>(() => import('../Sidebar.js').then((m) => m.Sidebar));\nconst Sidebar = SidebarModule.Slot;\n/**\n * The delete confirmation and the token prompt draw nothing until an\n * operator acts, and as static imports they (with the dialog primitive and\n * its focus trap, ~4 KB, and the button primitive) sat in the chat\n * first-paint critical JS. Preloaded (chat/DeleteChatDialog.tsx):\n * by the time anyone can click Delete the chunk is in and they mount in the\n * same render that opens them; the two share the dialog primitive's chunk.\n */\nconst DeleteDialogModule = preloadedLazy<ComponentProps<typeof DeleteChatDialogComponent>>(() => import('../chat/DeleteChatDialog.js').then((m) => m.DeleteChatDialog));\nconst DeleteChatDialog = DeleteDialogModule.Slot;\nconst TokenDialogModule = preloadedLazy<ComponentProps<typeof MutationTokenDialogComponent>>(\n () => import('../../../components/auth/MutationTokenDialog.js').then((m) => m.MutationTokenDialog),\n);\nconst MutationTokenDialog = TokenDialogModule.Slot;\n\n/**\n * The two derivations only the workspace's tasks row and the dock read\n * (tool-semantics, sidebar-model and line-diff come with them, ~12 KB). Both\n * consumers are lazy chunks that import these modules themselves, so the\n * download is shared and the values are in before either consumer mounts;\n * until then there is honestly nothing to show.\n */\nconst DERIVATIONS = preloadedModule(() => Promise.all([import('../chat/tasks-model.js'), import('../chat/turn-files.js')])\n .then(([tasks, files]) => ({ otherRunningChats: tasks.otherRunningChats, lastTurnFiles: files.lastTurnFiles })));\n/** Seat lookups for ⌘N \"New chat on…\" — the only first-paint-path use of verse-model (5.6 KB). */\nconst SEAT_MODEL = preloadedModule(() => import('../verse-model.js').then((m) => ({ firstRunnableModel: m.firstRunnableModel, seatById: m.seatById })));\nconst NO_TASKS: readonly ChatTask[] = [];\nconst NO_TURN_FILES: TurnFileChange[] = [];\n\n/**\n * Resolves when the chat's own chunks (workspace, chat list) are in. Tests\n * await it so a mount renders the whole surface synchronously; nothing in the\n * app needs to — the Suspense skeletons cover the gap.\n */\nexport function preloadChatSurface(): Promise<unknown> {\n return Promise.all([\n WorkspaceModule.ready(), SidebarModule.ready(), DeleteDialogModule.ready(), TokenDialogModule.ready(), DERIVATIONS.ready(), SEAT_MODEL.ready(),\n ]);\n}\n\n/**\n * The chat list's shape while its chunk loads. A <nav> like the real one, so\n * the grid rules written against `.chat > nav` (collapsed = display:none, the\n * phone-width overlay) hold for it too — but NOT named \"Chats\": that name is\n * the loaded list's, and a placeholder answering to it would be a lie.\n */\nfunction SidebarSkeleton() {\n return (\n <nav className={styles.sidebarSkeleton} aria-busy=\"true\" aria-label=\"Loading chat list\">\n {[64, 82, 58, 76, 70].map((w) => <SkeletonLine key={w} width={`${w}%`} />)}\n </nav>\n );\n}\n\n/** The workspace column's shape while its chunk loads: header, a few lines, the composer bar. */\nfunction WorkspaceSkeleton() {\n return (\n <div className={styles.workspaceSkeleton} role=\"status\" aria-label=\"Loading chat\">\n <div className={styles.skeletonHeader}><SkeletonLine width=\"32%\" /></div>\n <div className={styles.skeletonBody}>\n <SkeletonLine width=\"72%\" />\n <SkeletonLine width=\"88%\" />\n <SkeletonLine width=\"64%\" />\n </div>\n <div className={styles.skeletonComposer}><SkeletonLine width=\"100%\" /></div>\n </div>\n );\n}\n\nconst SELECTED_KEY = 'ashlr.verse.selected.v1';\n\nfunction loadSelected(): string | null {\n try {\n return localStorage.getItem(SELECTED_KEY);\n } catch {\n return null;\n }\n}\n\n/**\n * Every chat-surface failure in one sentence, branching on the route's CODE\n * before its status.\n *\n * A 409 is not always \"busy\": POST …/turns also answers 409\n * VERSE_MODEL_UNAVAILABLE when the chat's model cannot run on its seat.\n * Mapping every 409 to \"a turn is already running\" told the operator to press\n * a Stop that does nothing, with the server's actual reason thrown away.\n */\nexport function describeChatError(err: unknown): string {\n if (err instanceof DispatchDisabledError) return 'This server was started without dispatch — run `ashlr verse` to chat.';\n if (err instanceof ApiError) {\n if (err.code === 'VERSE_MODEL_UNAVAILABLE') {\n const why = err.detail ?? \"This chat's model cannot run on its seat.\";\n return `${why.replace(/\\.?$/, '.')} Continue in a fresh chat on a model this seat can run, or re-pin the seat's CLI (\\`ashlr resources profile repin\\`).`;\n }\n if (err.status === 409 && (err.code === null || err.code === 'VERSE_SESSION_BUSY')) return 'A turn is already running in this chat. Stop it first.';\n if (err.status === 413) return 'That message is too large (64 KB max).';\n if (err.status === 401) return 'Mutation token was rejected. Unlock again with the token ashlr verse printed.';\n return err.detail ?? err.message;\n }\n return err instanceof Error ? err.message : 'Something went wrong.';\n}\n\n/** The window's width, for the dock's presentation and its 60% cap. */\nfunction useWindowWidth(): number {\n const [width, setWidth] = useState(() => (typeof window === 'undefined' ? 1440 : window.innerWidth || 1440));\n useEffect(() => {\n const onResize = () => setWidth(window.innerWidth || 1440);\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }, []);\n return width;\n}\n\n/** ⌃⌥Space: the composer's message box, focused (C3's `aria-label=\"Message\"`). */\nfunction focusComposer(): void {\n // After the section has painted (the command may be what mounted it).\n requestAnimationFrame(() => {\n const box = document.querySelector<HTMLTextAreaElement>('textarea[aria-label=\"Message\"]');\n box?.focus();\n });\n}\n\n/** Is the key event aimed at an overlay that owns its own keys (a dialog, a menu, the palette)? */\nfunction inOverlay(target: EventTarget | null): boolean {\n return target instanceof Element && target.closest('[role=\"dialog\"], [role=\"menu\"], [role=\"listbox\"], [aria-modal=\"true\"]') !== null;\n}\n\nexport function ChatSection() {\n const bootstrap = useQuery(verseBootstrapQuery);\n const sessionsQuery = useQuery(verseSessionsQuery);\n const refetchSessions = useRefresh(verseSessionsQuery);\n const refetchBootstrap = useRefresh(verseBootstrapQuery);\n const hold = useMutationHold();\n const toast = useToast();\n const ui = useVerseUi();\n const visible = useSectionVisible();\n const dock = useDock();\n const chatActivity = useChatActivity();\n const windowWidth = useWindowWidth();\n\n const [selectedId, setSelectedIdState] = useState<string | null>(loadSelected);\n const [query, setQuery] = useState('');\n const [newChat, setNewChat] = useState<{ open: boolean; projectPath?: string | null; seat?: SeatChoice | null }>({ open: false });\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [reload, setReload] = useState(0);\n const [tokenPrompt, setTokenPrompt] = useState<{ open: boolean; reason: string }>({ open: false, reason: '' });\n const [handoffFor, setHandoffFor] = useState<string | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<VerseSession | null>(null);\n const [chatWidth, setChatWidth] = useState(0);\n const pendingAction = useRef<{ run: () => void; cancel: () => void } | null>(null);\n const chatRef = useRef<HTMLDivElement>(null);\n const panels = useChatPanelSizing();\n\n const seats = useMemo(() => bootstrap.data?.seats ?? [], [bootstrap.data]);\n const projects = useMemo(() => bootstrap.data?.projects ?? [], [bootstrap.data]);\n // Workspaces come from their own route (live per-root status for the dialog).\n const workspacesQuery = useQuery(verseWorkspacesQuery);\n const workspaces = useMemo(() => workspacesQuery.data?.workspaces ?? [], [workspacesQuery.data]);\n const dispatchEnabled = bootstrap.data?.dispatchEnabled ?? true;\n\n const view = useVerseSession(selectedId, reload);\n const roots = useSessionRoots(view.session);\n\n // Sidebar list: the sessions query, else bootstrap's copy, with the live\n // store record overlaid for the open chat so its running dot is immediate.\n const sessions = useMemo<VerseSession[]>(() => {\n const base = sessionsQuery.data ?? bootstrap.data?.sessions ?? [];\n if (!view.session) return base;\n const live = view.session;\n return base.some((s) => s.id === live.id) ? base.map((s) => (s.id === live.id ? live : s)) : [live, ...base];\n }, [sessionsQuery.data, bootstrap.data, view.session]);\n\n const setSelectedId = useCallback((id: string | null) => {\n setSelectedIdState(id);\n try {\n if (id) localStorage.setItem(SELECTED_KEY, id);\n else localStorage.removeItem(SELECTED_KEY);\n } catch {\n /* best-effort */\n }\n }, []);\n\n // A remembered selection that no longer exists (deleted elsewhere) is dropped once the list is known.\n useEffect(() => {\n if (!selectedId || !sessionsQuery.data) return;\n if (!sessionsQuery.data.some((s) => s.id === selectedId) && !view.session) setSelectedId(null);\n }, [selectedId, sessionsQuery.data, view.session, setSelectedId]);\n\n // Sidebar digest channel (verse-sessions on /api/events).\n useEffect(() => openVerseListChannel(), []);\n\n // ---- recency + read state -------------------------------------------------\n // Pane requests (a pasted command, a diff to open) belong to the chat they\n // were raised in: switching chats drops them. A SWITCH only — the first run\n // of this effect is the section mounting, and that is exactly when Apps'\n // [Launch ▸] (raised on another surface, which then brings Chat forward)\n // has a terminal request waiting; clearing it here left an empty dock.\n const requestsFor = useRef(selectedId);\n useEffect(() => {\n setVerseActiveSession(selectedId);\n if (requestsFor.current !== selectedId) {\n requestsFor.current = selectedId;\n clearDockRequests();\n }\n }, [selectedId]);\n\n // Reading a chat — its turns up to now — while this surface is on screen.\n const openTurnCount = view.session?.turnCount ?? null;\n useEffect(() => {\n if (!visible || !selectedId || openTurnCount === null) return;\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return;\n noteSessionSeen(selectedId, openTurnCount);\n }, [visible, selectedId, openTurnCount]);\n\n // ---- mutation guard ---------------------------------------------------------\n const withToken = useCallback(<T,>(reason: string, action: () => Promise<T>): Promise<T | null> => {\n if (hold.hasHold) return action();\n return new Promise<T | null>((resolve, reject) => {\n pendingAction.current = {\n // Pass rejections through: a parked action that fails after the token\n // unlock must reach its caller's catch, not leave it awaiting forever.\n run: () => { action().then(resolve, reject); },\n cancel: () => resolve(null),\n };\n setTokenPrompt({ open: true, reason });\n });\n }, [hold.hasHold]);\n\n const fail = useCallback((err: unknown) => {\n if (err instanceof VerseMutationLockedError) return;\n toast.show(describeChatError(err), 'danger');\n }, [toast]);\n\n // ---- actions ----------------------------------------------------------------\n /**\n * ONE resolution of what \"new chat\" pre-fills, for every entry point (the\n * sidebar \"+\", ⌘N, the empty state, the palette's \"New chat on…\"): an\n * explicit prefill wins, the open session is next, and the per-project\n * seat memory is the floor.\n */\n const openNewChat = useCallback((prefill: { projectPath?: string | null; seat?: SeatChoice | null } = {}) => {\n setCreateError(null);\n const current = view.session;\n const projectPath = prefill.projectPath !== undefined ? prefill.projectPath : current?.projectPath ?? null;\n // The open session's seat only carries forward onto its OWN project.\n const fromCurrent = current && projectPath === current.projectPath\n ? { seatId: current.seatId, model: current.model }\n : null;\n const seat = prefill.seat ?? fromCurrent ?? lastVerseSeat(projectPath);\n setNewChat({ open: true, projectPath, seat });\n }, [view.session]);\n\n const create = useCallback(async (req: VerseCreateSessionRequest) => {\n await withToken('Starting a chat spawns an agent process for this project.', async () => {\n setCreating(true);\n setCreateError(null);\n try {\n const session = await createVerseSession(req);\n rememberVerseSeat(session.projectPath, { seatId: session.seatId, model: session.model });\n setVerseSession(session.id, session);\n setNewChat({ open: false });\n setSelectedId(session.id);\n toast.show(`Started “${session.title}”`, 'success');\n } catch (err) {\n setCreateError(describeChatError(err));\n } finally {\n setCreating(false);\n }\n });\n }, [withToken, setSelectedId, toast]);\n\n const send = useCallback(async (text: string) => {\n const id = selectedId;\n if (!id) return false;\n const result = await withToken('Sending a message runs the agent against this project.', async () => {\n setVerseSessionStatus(id, 'running');\n try {\n const response = await sendVerseTurn(id, text);\n // The turn may already have settled by the time the 202 is applied;\n // the store checks the log for this turnId before trusting `running`.\n setVerseSession(id, response.session, response.turnId);\n return true;\n } catch (err) {\n setVerseSessionStatus(id, 'idle');\n fail(err);\n return false;\n }\n });\n return result === true;\n }, [selectedId, withToken, fail]);\n\n const stop = useCallback((id: string | null = selectedId) => {\n if (!id) return;\n void withToken('Stopping the running turn.', async () => {\n try {\n await cancelVerseTurn(id);\n } catch (err) {\n fail(err);\n }\n });\n }, [selectedId, withToken, fail]);\n\n const renameChat = useCallback(async (id: string, title: string) => {\n const result = await withToken('Renaming a chat.', async () => {\n try {\n const session = await renameVerseSession(id, title);\n setVerseSession(id, session);\n return true;\n } catch (err) {\n fail(err);\n return false;\n }\n });\n return result === true;\n }, [withToken, fail]);\n\n const removeChat = useCallback(async (id: string) => {\n const result = await withToken('Deleting a chat removes its transcript from disk.', async () => {\n try {\n await deleteVerseSession(id);\n forgetVerseSession(id);\n // The transcript is gone from disk; the verbatim prompts must not\n // survive it in browser storage under a dead session id.\n forgetComposerMemory(id);\n if (id === selectedId) setSelectedId(null);\n toast.show('Chat deleted.', 'neutral');\n return true;\n } catch (err) {\n fail(err);\n return false;\n }\n });\n return result === true;\n }, [withToken, fail, setSelectedId, toast, selectedId]);\n\n const setMeta = useCallback((id: string, update: { pinned?: boolean; archived?: boolean }, reason: string) => {\n void withToken(reason, async () => {\n try {\n await setVerseSessionMeta(id, update);\n } catch (err) {\n fail(err);\n }\n });\n }, [withToken, fail]);\n\n /** Sessions are seat-bound: a different seat means a new chat on the same project. */\n const changeSeat = useCallback((choice: SeatChoice) => {\n const current = view.session;\n if (!current) return;\n openNewChat({ projectPath: current.projectPath, seat: choice });\n toast.show('Chats are bound to one seat — starting a new chat on the same project.', 'neutral');\n }, [view.session, openNewChat, toast]);\n\n const requestDelete = useCallback((id: string) => {\n const target = sessions.find((s) => s.id === id) ?? null;\n if (target) setDeleteTarget(target);\n }, [sessions]);\n\n const startHandoff = useCallback((id: string) => {\n if (id !== selectedId) setSelectedId(id);\n setHandoffFor(id);\n }, [selectedId, setSelectedId]);\n\n const selectFromList = useCallback((id: string) => {\n setSelectedId(id);\n // At phone width the list floats over the chat; picking a chat closes it.\n if (window.matchMedia?.('(max-width: 760px)')?.matches === true) setVerseSidebarCollapsed(true);\n }, [setSelectedId]);\n\n // ---- commands: the shell's hand-offs, the bus, and the chat's own keys ------\n const toggleSidebar = useCallback(() => setVerseSidebarCollapsed(!ui.sidebarCollapsed), [ui.sidebarCollapsed]);\n // ⌃` : a terminal, focused — the active tab, else one at the chat's root\n // (TerminalPane resolves an empty request that way). Pressed again while\n // Terminal is on screen it hides it, like every editor's ⌃`.\n const terminalShown = dock.state.open && (dock.state.active === 'terminal' || dock.state.splitWith === 'terminal');\n const openTerminal = useCallback(() => {\n if (terminalShown) toggleDockPane('terminal');\n else requestTerminal({});\n }, [terminalShown]);\n // ⌃⇧` : always a NEW tab, in the active tab's root (else the chat's primary).\n const newTerminalTab = useCallback(() => requestTerminal({ newTab: true }), []);\n\n // The shell's one-shot command, consumed once by nonce.\n const handledCommand = useRef(0);\n const seatModel = SEAT_MODEL.useLoaded();\n useEffect(() => {\n const command = ui.command;\n if (!command || command.nonce === handledCommand.current) return;\n // \"New chat on…\" resolves its seat through verse-model, which loads just\n // after first paint: leave the command pending (unconsumed) until it is\n // in — this effect re-runs when it lands.\n if (command.name === 'new-chat' && !seatModel) return;\n handledCommand.current = command.nonce;\n switch (command.name) {\n case 'open-session':\n if (command.sessionId) setSelectedId(command.sessionId);\n break;\n case 'focus-composer':\n focusComposer();\n break;\n case 'new-chat': {\n // \"New chat on…\" carries a seat (and maybe a project); ⌘N carries neither.\n const { seatById, firstRunnableModel } = seatModel!;\n const seat = command.seatId ? seatById(seats, command.seatId) : undefined;\n // The model this project last used on that seat, else the seat's first runnable one.\n const remembered = lastVerseSeat(command.projectPath ?? null);\n const model = !seat ? undefined : remembered?.seatId === seat.id ? remembered.model : firstRunnableModel(seat)?.id;\n openNewChat({\n ...(command.projectPath !== undefined ? { projectPath: command.projectPath } : {}),\n ...(seat && model ? { seat: { seatId: seat.id, model } } : {}),\n });\n break;\n }\n default:\n // 'quick-switcher' (pre-3.10): ⌘K is the shell's palette now.\n break;\n }\n clearVerseCommand();\n }, [ui.command, openNewChat, seats, setSelectedId, seatModel]);\n\n // The catalog's chat-scope commands, for the palette, the menu and keys.\n const handlers: ReadonlyArray<[string, () => void | boolean]> = [\n ['chat.sidebar', toggleSidebar],\n ['chat.find', () => requestTranscriptFind()],\n ['chat.turn-prev', () => requestTranscriptStep(-1)],\n ['chat.turn-next', () => requestTranscriptStep(1)],\n ['dock.toggle', toggleDock],\n ['dock.terminal', openTerminal],\n ['dock.terminal-new', newTerminalTab],\n ['dock.preview', () => toggleDockPane('preview')],\n ['dock.diff', () => toggleDockPane('diff')],\n ];\n const handlerMap = new Map(handlers);\n useCommandHandler('chat.sidebar', handlerMap.get('chat.sidebar')!);\n useCommandHandler('chat.find', handlerMap.get('chat.find')!);\n useCommandHandler('chat.turn-prev', handlerMap.get('chat.turn-prev')!);\n useCommandHandler('chat.turn-next', handlerMap.get('chat.turn-next')!);\n useCommandHandler('dock.toggle', handlerMap.get('dock.toggle')!);\n useCommandHandler('dock.terminal', handlerMap.get('dock.terminal')!);\n useCommandHandler('dock.terminal-new', handlerMap.get('dock.terminal-new')!);\n useCommandHandler('dock.preview', handlerMap.get('dock.preview')!);\n useCommandHandler('dock.diff', handlerMap.get('dock.diff')!);\n\n // Keys, as a fallback: the shell's handler routes catalog keys through the\n // bus and marks the event handled (preventDefault); this listener acts only\n // on an event nobody took, and only while the surface is visible and no\n // overlay (a dialog, a menu, the palette) owns the keyboard. ⌘F and ⌥↑/↓\n // are the transcript's own listeners, which follow the same rule.\n const handlerRef = useRef(handlerMap);\n handlerRef.current = handlerMap;\n useEffect(() => {\n if (!visible) return undefined;\n const onKey = (event: KeyboardEvent) => {\n if (event.defaultPrevented || inOverlay(event.target)) return;\n const command = matchKey(event, ['chat']);\n if (!command || command.scope !== 'chat') return;\n if (command.id === 'chat.find' || command.id === 'chat.turn-prev' || command.id === 'chat.turn-next') return;\n const run = handlerRef.current.get(command.id);\n if (!run) return;\n event.preventDefault();\n run();\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [visible]);\n\n const dispatchOff = bootstrap.data ? !bootstrap.data.dispatchEnabled : false;\n const sidebarCollapsed = ui.sidebarCollapsed;\n\n // ---- layout -------------------------------------------------------------------\n const presentation = dockPresentation(windowWidth);\n const dockOpen = dock.state.open && dock.state.tabs.length > 0;\n const dockColumn = dockOpen && presentation === 'column';\n // The dock yields before the transcript does: never past the window's 60%,\n // and never so wide that the transcript (plus the chat list, when shown at\n // its minimum) drops under its floor. The floor for the dock is its 320px.\n const sidebarFloor = sidebarCollapsed ? 0 : CHAT_PANEL_RANGES.sidebar.min;\n const dockWidth = dockColumn\n ? Math.max(DOCK_LAYOUT.minWidth, Math.min(\n clampDockWidth(dock.state.width, windowWidth),\n chatWidth > 0 ? chatWidth - MIN_TRANSCRIPT_WIDTH - sidebarFloor : Number.POSITIVE_INFINITY,\n ))\n : 0;\n\n useEffect(() => {\n const node = chatRef.current;\n if (!node) return undefined;\n const measure = () => {\n setChatWidth(node.clientWidth);\n setChatPanelFit({\n // The dock's column is not the sidebar's to spend.\n containerWidth: Math.max(0, node.clientWidth - dockWidth),\n sidebarVisible: !sidebarCollapsed,\n resourcesVisible: false,\n });\n };\n measure();\n const observer = typeof ResizeObserver === 'function' ? new ResizeObserver(measure) : null;\n observer?.observe(node);\n window.addEventListener('resize', measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', measure);\n };\n }, [sidebarCollapsed, dockWidth]);\n\n const style = {\n '--verse-sidebar-width': `${panels.effective.sidebar}px`,\n '--verse-dock-width': `${dockWidth}px`,\n } as CSSProperties;\n\n // ---- dock data ------------------------------------------------------------------\n const derive = DERIVATIONS.useLoaded();\n const otherRunning = useMemo(\n () => (derive ? derive.otherRunningChats(sessions, chatActivity.activity, selectedId) : NO_TASKS),\n [derive, sessions, chatActivity.activity, selectedId],\n );\n const turnFiles = useMemo<TurnFileChange[]>(\n () => (derive ? derive.lastTurnFiles(view.events, roots.roots) : NO_TURN_FILES),\n [derive, roots.roots, view.events],\n );\n\n const actions: SidebarRowActions = {\n setPinned: (id, pinned) => setMeta(id, { pinned }, pinned ? 'Pinning a chat.' : 'Unpinning a chat.'),\n setArchived: (id, archived) => setMeta(id, { archived }, archived ? 'Archiving a chat.' : 'Unarchiving a chat.'),\n rename: renameChat,\n handoff: startHandoff,\n requestDelete,\n dispatchEnabled,\n };\n\n return (\n <div ref={chatRef} className={styles.chat} style={style}\n data-sidebar={sidebarCollapsed ? 'collapsed' : 'open'} data-dock={dockColumn ? 'open' : 'closed'}>\n <Suspense fallback={<SidebarSkeleton />}>\n <Sidebar sessions={sessions} sessionsStatus={sessionsQuery.status} sessionsError={sessionsQuery.error?.message ?? null}\n projects={projects} seats={seats} selectedId={selectedId} query={query} onQuery={setQuery}\n onSelect={selectFromList} onNew={() => openNewChat()}\n onRetry={() => { refetchSessions(); refetchBootstrap(); }}\n onCollapse={() => setVerseSidebarCollapsed(true)} onDisconnect={() => { void clearReadSession(); }}\n activity={chatActivity.activity} meta={chatActivity.meta} localSeen={chatActivity.localSeen} actions={actions} />\n </Suspense>\n {sidebarCollapsed ? null : <ChatResizer side=\"sidebar\" label=\"Resize chat list\" className={styles.resize} />}\n {/* At phone width the sidebar floats over the transcript; the scrim dismisses it. */}\n <button type=\"button\" className={styles.scrim} aria-label=\"Close chat list\" tabIndex={-1}\n onClick={() => setVerseSidebarCollapsed(true)} />\n <div className={styles.main}>\n {bootstrap.status === 'error' && !bootstrap.data ? (\n <div role=\"alert\" className={styles.banner}>\n <span>{bootstrap.error?.message ?? 'Could not load Verse.'}</span>\n <button type=\"button\" onClick={refetchBootstrap}>Retry</button>\n </div>\n ) : dispatchOff ? (\n <div role=\"status\" className={styles.bannerWarn}>\n Read-only: this server was started without dispatch. Run <code>ashlr verse</code> to send messages.\n </div>\n ) : null}\n <Suspense fallback={<WorkspaceSkeleton />}>\n <Workspace view={view} seats={seats} projects={projects} dispatchEnabled={dispatchEnabled} locked={!hold.hasHold}\n hasAnySessions={sessions.length > 0} onSend={send} onStop={() => stop()}\n onRename={(title) => (selectedId ? renameChat(selectedId, title) : Promise.resolve(false))}\n onRequestDelete={() => { if (selectedId) requestDelete(selectedId); }}\n onSeatChange={changeSeat} onNew={() => openNewChat()} onRetry={() => setReload((n) => n + 1)}\n sidebarCollapsed={sidebarCollapsed} onToggleSidebar={() => setVerseSidebarCollapsed(!sidebarCollapsed)}\n onOpenSession={setSelectedId}\n handoffOpen={handoffFor !== null && handoffFor === selectedId}\n onHandoffOpenChange={(open) => setHandoffFor(open ? selectedId : null)}\n otherRunning={otherRunning} />\n </Suspense>\n </div>\n {dockOpen ? (\n <Suspense fallback={null}>\n <DockHost presentation={presentation} windowWidth={windowWidth} columnWidth={dockWidth} session={view.session} seats={seats}\n events={view.events} roots={roots.roots} rootsData={roots.data} rootsError={roots.error} turnFiles={turnFiles}\n otherRunning={otherRunning} dispatchEnabled={dispatchEnabled} onOpenSession={setSelectedId}\n onHandoff={(id) => setHandoffFor(id)}\n // Accounts & capacity live in Apps & Accounts (C6) since 3.10.\n onOpenAccounts={() => setVerseSection('apps')}\n onSendToChat={(text) => { if (!selectedId || !insertIntoComposer(selectedId, text)) toast.show('Open a chat to send this to it.', 'neutral'); }}\n onAddToMessage={(text) => { if (!selectedId || !insertIntoComposer(selectedId, text)) toast.show('Open a chat to add this to its message.', 'neutral'); }} />\n </Suspense>\n ) : null}\n\n {newChat.open ? (\n <Suspense fallback={null}>\n <NewChatDialog open onClose={() => setNewChat({ open: false })} projects={projects} seats={seats} workspaces={workspaces}\n initialProjectPath={newChat.projectPath ?? null} initialSeat={newChat.seat ?? null} busy={creating} error={createError} onCreate={create}\n // The dialog's one write of its own (a seat's default context mode)\n // goes through the same token guard as every other chat mutation.\n runMutation={withToken} />\n </Suspense>\n ) : null}\n {/* Closed dialogs render nothing, so a not-yet-loaded one (fallback null) looks the same. */}\n <Suspense fallback={null}>\n <DeleteChatDialog target={deleteTarget} onClose={() => setDeleteTarget(null)} onDelete={removeChat}\n actionsClassName={styles.dialogActions} />\n <MutationTokenDialog open={tokenPrompt.open} reason={tokenPrompt.reason} tokenLabel=\"Mutation token\"\n tokenHelp=\"the mutation token ashlr verse printed\"\n onClose={() => {\n setTokenPrompt({ open: false, reason: '' });\n // The dialog calls onClose before onUnlocked on submit, so only treat\n // this as a dismissal if nothing has claimed the pending action by\n // the next tick.\n const pending = pendingAction.current;\n if (!pending) return;\n setTimeout(() => {\n if (pendingAction.current === pending) {\n pendingAction.current = null;\n pending.cancel();\n }\n }, 0);\n }}\n onUnlocked={() => {\n const pending = pendingAction.current;\n pendingAction.current = null;\n pending?.run();\n }} />\n </Suspense>\n </div>\n );\n}\n"],"file":"ChatSection-D6WybBpB.js"}
@@ -0,0 +1,2 @@
1
+ import{a as e,c as t,n}from"./index-QesfXBsb.js";import{b as r,n as i}from"./format-Dk4Fmxax.js";import{t as a}from"./Button-BhO-WKPp.js";import{t as o}from"./Switch-PMixU6b0.js";import{o as s,s as c,t as l}from"./cloud-model-CX407gPY.js";import{t as u}from"./cloud.module-DOP-N4Ch.js";import{t as d}from"./Input-C0MQxEM6.js";import{o as f,s as p}from"./why-seat-model-jlDTwYSc.js";function m(e,t){if(!e.resetsAt)return null;let n=Date.parse(e.resetsAt);return Number.isFinite(n)?n-t:null}function h(e,t,n){let r=t.id;if(e===`claude`){if(r===`five_hour`)return`session`;if(r===`seven_day`)return`weekly`;if(r.startsWith(`seven_day_`))return`model`}if(e===`grok`)return`weekly`;let i=m(t,n);if(e===`codex`){if(r.endsWith(`_secondary`))return`weekly`;if(r.endsWith(`_primary`))return i!==null&&i>189e5?`weekly`:`session`}return i!==null&&i<=189e5?`session`:`weekly`}var g=864e5,_=36e5;function v(e,t,n,r=0){let i=e.length-r,a=i-n;if(a<0||n<=0)return null;let o=0;for(let n=a;n<i;n++){let r=t(e[n]);if(r===null||!Number.isFinite(r))return null;o+=r}return o}function y(e,t,n){return e.slice(Math.max(0,e.length-n)).map(t)}function b(e){let t=e.filter(e=>e!==null&&Number.isFinite(e));return t.length?t.reduce((e,t)=>e+t,0)/t.length:null}function x(e){let t=e.meteredCostUsd;return typeof t==`number`&&Number.isFinite(t)&&t>=0?t:null}var ee=e=>e>0?`+${e}`:String(e),S=e=>`${e>0?`+`:``}${e.toFixed(1)}`,C=e=>`${e>0?`+`:e<0?`−`:``}$${Math.abs(e).toFixed(2)}`;function w(e){if(e===null||!Number.isFinite(e)||e<0)return`—`;let t=Math.round(e/6e4);if(t<60)return`${t}m`;let n=Math.floor(t/60);if(n<48)return t%60?`${n}h ${t%60}m`:`${n}h`;let r=Math.floor(n/24);return n%24?`${r}d ${n%24}h`:`${r}d`}function te(e){if(!e||e.repos.length===0)return[];let t=Math.max(...e.repos.map(e=>e.greenTrend.length));return Array.from({length:t},(n,r)=>b(e.repos.map(e=>e.greenTrend[r-(t-e.greenTrend.length)]??null)))}function T({fleet:e,history:t,learning:n,policy:r}){let i=t?.days??[],a=e=>e.merges.realized,o=x,s=e?.summary.merged7d??v(i,a,7),c=v(i,a,7,7),l=v(i,a,7),u=l!==null&&c!==null?l-c:null,d=e?.summary.postMergeGreenPct7d??null,f=te(e),p=f.length>=14?b(f.slice(-7)):null,m=f.length>=14?b(f.slice(-14,-7)):null,h=p!==null&&m!==null?p-m:null,g=v(i,o,7),_=v(i,o,7,7),T=r?r.spend.meteredUsdPerDay:null,E=g===null?`—`:`$${g.toFixed(2)}${T===null?``:` / $${(T*7).toFixed(0)}`}`,D=i.some(e=>x(e)!==null),O=T===null?`per-token APIs only`:T===0?`metered APIs off`:`cap $${T}/day`,k=D?O:`${O} · not reported yet`,A=n?.active??null,j=(A?.experimentId?n?.experiments.find(e=>e.id===A.experimentId)??null:null)?.lift??null;return[{id:`merged`,label:`Merged · 7d`,value:s===null?`—`:String(s),delta:u===null?null:{value:u,unit:Math.abs(u)===1?`merge`:`merges`,versus:`vs prior 7d`,goodWhenPositive:!0,format:ee},trend:i.length?y(i,a,14):void 0,trendLabel:`Merges per day, last 14 days`,caption:e?.summary.mergedToday==null?`fleet merges on default branches`:`${e.summary.mergedToday} today`},{id:`green`,label:`Post-merge green`,value:d===null?`—`:`${Math.round(d)}%`,delta:h===null?null:{value:h,unit:`pts`,versus:`vs prior 7d`,goodWhenPositive:!0,format:S},trend:f.length?f:void 0,trendLabel:`Post-merge green percent per day`,caption:d===null?`no completed post-merge watch yet`:`CI + suite 2 h after each merge`},{id:`cycle`,label:`Cycle time`,value:w(e?.summary.cycleTimeP50Ms7d??null),delta:null,trend:void 0,trendLabel:`Cycle time`,caption:`median, proposal → merge, 7d`},{id:`spend`,label:`Metered spend · 7d`,value:E,delta:g!==null&&_!==null?{value:g-_,versus:`vs prior 7d`,goodWhenPositive:!1,format:C}:null,trend:D?y(i,o,14):void 0,trendLabel:`Metered (per-token API) spend per day`,caption:k},{id:`lift`,label:`Lift`,value:j?`${S(j.mean)} pts`:`—`,delta:null,trend:void 0,trendLabel:`Harness lift`,caption:A?`harness ${A.id} vs baseline${j?` (95% ${S(j.ciLow)} to ${S(j.ciHigh)})`:``}`:`no harness adopted — defaults in force`}]}function E({fleet:e,history:t,learning:n}){let r=t?.days??[],i=e=>e.merges.realized,a=e?.summary.merged7d??v(r,i,7),o=v(r,i,7,7),s=n?.active??null,c=s?.experimentId?n?.experiments.find(e=>e.id===s.experimentId)?.lift??null:null;return(a??0)===0&&(o??0)===0&&(e?.summary.postMergeGreenPct7d??null)===null&&(e?.summary.cycleTimeP50Ms7d??null)===null&&(v(r,x,7)??0)===0&&c===null}var D=(e,t)=>e!==null&&Date.parse(e)>t;function O({lastLookedAt:e,fleet:t,leader:n,activity:r}){if(!e)return[];let i=Date.parse(e);if(!Number.isFinite(i))return[];let a=[],o=(t?.runs??[]).filter(e=>D(e.endedAt,i)),s=e=>o.filter(t=>t.outcome===e).length,c=s(`merged`),l=s(`reverted`),u=s(`failed`)+s(`refused`);c&&a.push({id:`merged`,text:`${c} merged`,tone:`success`}),l&&a.push({id:`reverted`,text:`${l} reverted`,tone:`danger`}),u&&a.push({id:`failed`,text:`${u} refused or failed`,tone:`warning`});let d=(n?.timeline??[]).filter(e=>Date.parse(e.at)>i&&e.status===`ok`).length;d&&a.push({id:`memos`,text:`${d} new memo${d===1?``:`s`}`,tone:`info`});let f=(r?.needsYou??[]).filter(e=>Date.parse(e.since)>i).length;return f&&a.push({id:`needs`,text:`${f} new for you`,tone:`warning`}),a}var k={high:0,warn:1,info:2};function A(e){return[...e].sort((e,t)=>k[e.severity]-k[t.severity]||Date.parse(t.since)-Date.parse(e.since))}function j(e,t){let n=new Map,r=(e,t)=>{typeof e==`string`&&typeof t==`string`&&t.trim().length>0&&n.set(e,t.trim())};for(let e of t?.seatInfo??[])r(e.seatId,e.label);for(let t of e??[])r(t.id,t.label);return n}function M(e){return e?Object.entries(e.sources).filter(([,e])=>e!==`ok`).map(([e])=>e):[]}function N(e,t){return`${e}#${t}`}var P=20;function F(e,t){let n=e.length;return n>=2&&e[n-1].used===t&&e[n-2].used===t}function I(e,t){if(!t)return{...e};let n=Date.parse(t.sampledAt);if(!Number.isFinite(n))return{...e};let r={...e};for(let e of t.headroom){let t=[[`session`,e.sessionUsedPercent],[`weekly`,e.weeklyUsedPercent]];for(let[i,a]of t){if(a===null||!Number.isFinite(a))continue;let t=N(e.seatId,i),o=r[t]??[];if(o.length&&o[o.length-1].t>=n)continue;let s=o.length&&a+P<o[o.length-1].used?[]:o;r[t]=[...F(s,a)?s.slice(0,-1):s,{t:n,used:a}].slice(-240)}}return r}function L(e,t){let n=typeof t?.generatedAt==`string`?Date.parse(t.generatedAt):NaN,r=new Map;for(let e of Array.isArray(t?.series)?t.series:[]){if(!e||typeof e.seatId!=`string`||e.window!==`session`&&e.window!==`weekly`||!Array.isArray(e.points))continue;let t=[];for(let n of e.points){if(!Array.isArray(n))continue;let[e,r]=n;typeof e==`number`&&Number.isFinite(e)&&typeof r==`number`&&Number.isFinite(r)&&r>=0&&r<=100&&t.push({t:e,used:r})}if(t.length===0)continue;let n=N(e.seatId,e.window);r.set(n,[...r.get(n)??[],...t])}let i={},a=new Set;for(let t of new Set([...Object.keys(e),...r.keys()])){let o=r.get(t)??[],s=e[t]??[];if(o.length>0){a.add(t);let e=Math.max(...o.map(e=>e.t)),r=Number.isFinite(n)?Math.max(n,e):e;s=s.filter(e=>e.t>r)}let c=new Map;for(let e of o)c.set(e.t,e);for(let e of s)c.set(e.t,e);let l=[...c.values()].sort((e,t)=>e.t-t.t),u=0;for(let e=1;e<l.length;e++)l[e].used+P<l[e-1].used&&(u=e);let d=[];for(let e of l.slice(u))F(d,e.used)&&d.pop(),d.push(e);i[t]=d.slice(-2e3)}return{readings:i,recorded:a}}var R={session:5*_,weekly:7*g};function z(e,t){if(!t||e.bindingWindow===null)return null;let n=100-t.reservePercent,r=t.maxSessionWindowPercent??100,i,a;e.bindingWindow===`weekly`||e.weeklyUsedPercent===null&&n<r?(i=n,a=`Reserved for you`):(i=r,a=`5-hour ceiling`);let o=100-i;return o>0?{value:o,label:a}:null}function B(e){let t=e===`session`?{weekday:`short`,hour:`numeric`,minute:`2-digit`}:{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`};return e=>{let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleString(`en-US`,t)}}function V(e,t,n,r){if(!e?.capacity||n===null||t===`local`)return null;for(let i of e.capacity.windows){let e=typeof i.resetDescription==`string`?i.resetDescription.trim():``;if(e.length!==0&&h(t,{id:i.id,usedPercent:i.usedPercent,resetsAt:i.resetsAt,resetDescription:i.resetDescription,limitReached:i.limitReached},r)===n)return e}return null}function H(e){return`resets ${e.replace(/^resets\s+/i,``)}`}var U=[`jan`,`feb`,`mar`,`apr`,`may`,`jun`,`jul`,`aug`,`sep`,`oct`,`nov`,`dec`],W=/^(?:resets\s+)?(?:([A-Za-z]{3}) ([1-9]|[12]\d|3[01]) at )?([1-9]|1[0-2])(?::([0-5]\d))?(am|pm) \(([A-Za-z0-9_+-]+(?:\/[A-Za-z0-9_+-]+){0,2})\)$/i;function G(e,t){let n=t.formatToParts(new Date(e)),r=e=>Number(n.find(t=>t.type===e)?.value),i={y:r(`year`),mo:r(`month`)-1,d:r(`day`),h:r(`hour`)%24,mi:r(`minute`)};return Object.values(i).every(Number.isFinite)?i:null}var K=e=>Date.UTC(e.y,e.mo,e.d,e.h,e.mi);function q(e,t){let n=K(e),r=n;for(let e=0;e<3;e++){let e=G(r,t);if(!e)return null;let i=n-K(e);if(i===0)return r;r+=i}return null}function J(e,t){let n=W.exec(e.trim());if(!n||!Number.isFinite(t))return null;let[,r,i,a,o,s,c]=n,l;try{l=new Intl.DateTimeFormat(`en-US`,{timeZone:c,hourCycle:`h23`,year:`numeric`,month:`numeric`,day:`numeric`,hour:`numeric`,minute:`numeric`})}catch{return null}let u=G(t,l);if(!u)return null;let d=Number(a)%12+(s.toLowerCase()===`pm`?12:0),f=o===void 0?0:Number(o),p=[];if(r!==void 0){let e=U.indexOf(r.toLowerCase()),t=Number(i);if(e<0)return null;for(let n of[u.y-1,u.y,u.y+1])new Date(Date.UTC(n,e,t)).getUTCDate()===t&&p.push({y:n,mo:e,d:t,h:d,mi:f})}else for(let e of[-1,0,1]){let t=new Date(Date.UTC(u.y,u.mo,u.d+e));p.push({y:t.getUTCFullYear(),mo:t.getUTCMonth(),d:t.getUTCDate(),h:d,mi:f})}let m=null;for(let e of p){let n=q(e,l);n!==null&&(m===null||Math.abs(n-t)<Math.abs(m-t))&&(m=n)}return m}var Y=9e5,X=Date.UTC(2e3,0,1);function ne(e,t,n,r){let i=e?Date.parse(e):NaN;if(Number.isFinite(i)&&i>=X)return{at:i,from:`provider`};if(t===null||n===null)return null;let a=J(t,r);return a===null||a<r-Y||a>r+n+Y?null:{at:a,from:`words`}}function re(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`&&e.trim().length>0).map(p):[]}var ie=6e4,ae=new Set([`reserve`,`session-ceiling`]);function Z(e){return typeof e==`string`?e.trim().replace(/^resets\s+/i,``):``}function oe(e){for(let t of e){if(!ae.has(t.kind))continue;let e=Z(t.resetDescription);if(e)return e}return null}function se(e,t,n){let i=typeof e.resetsAt==`string`?Date.parse(e.resetsAt):NaN;if(Number.isFinite(i))return t.resetAt!==null&&Math.abs(i-t.resetAt)<=ie?null:r(e.resetsAt,n);let a=Z(e.resetDescription);return!a||t.resetText!==null&&Z(t.resetText).toLowerCase()===a.toLowerCase()?null:a}function ce(e,t,n){if(!e)return null;let r=i(f(e.text,n));if(!r)return null;let a=se(e,t,n);return a?`${r} Resets ${a}.`:r}function le(e,t,n,r){if(!e)return[];let i=new Map(e.seatInfo.map(e=>[e.seatId,e])),a=new Map((n??[]).map(e=>[e.id,e])),o=Date.parse(e.sampledAt),s=Number.isFinite(o)?o:Date.now();return e.headroom.map(n=>{let o=i.get(n.seatId),c=o?.engine??`local`,l=n.bindingWindow?R[n.bindingWindow]:null,u=re(n.reasons),d=V(a.get(n.seatId),c,n.bindingWindow,s)??(n.bindingWindow===null||c===`local`?null:oe(u)),f=l===null?null:ne(n.resetAt,d,l,s),p=f&&l!==null?f.at-l:null,m=n.bindingWindow?N(n.seatId,n.bindingWindow):null,h=m?t[m]??[]:[];return{seatId:n.seatId,label:o?.label??n.seatId,engine:c,window:n.bindingWindow,points:h.filter(e=>p===null||e.t>=p).map(e=>({t:e.t,remaining:Math.max(0,100-e.used)})),start:p,resetAt:f?.at??null,resetFrom:f?.from??null,resetText:d,reservePercent:e.effective[n.seatId]?.reservePercent??0,line:z(n,e.effective[n.seatId]),enabled:e.effective[n.seatId]?.enabled??!1,free:o?.free??!1,eligible:n.eligibleForAutonomy,reason:ce(u[0],{resetAt:f?.at??null,resetText:d},s),recorded:m!==null&&(r?.has(m)??!1)}}).sort((e,t)=>Number(e.free)-Number(t.free)||e.label.localeCompare(t.label))}function ue(e){if(!e)return null;let t=e.seatInfo.find(e=>e.engine===`claude`);if(!t)return null;let n=e.effective[t.seatId];return n?{label:`Claude`,percent:n.reservePercent}:null}var Q=t(e(),1),$=n();function de({budget:e,onSave:t,onCancel:n,busy:r,disabled:i=!1,idPrefix:f}){let[p,m]=(0,Q.useState)(()=>s(e)),[h,g]=(0,Q.useState)({}),[_,v]=(0,Q.useState)(null),y=(0,Q.useRef)(!1);(0,Q.useEffect)(()=>{y.current||m(s(e))},[e]);let b=e=>{y.current=!0,v(null),m(t=>({...t,...e}))};return(0,$.jsxs)(`form`,{className:u.form,onSubmit:n=>{n.preventDefault();let r=c(p,e);if(!r.ok){g(r.errors);return}if(g({}),!r.changed){v(`Nothing changed.`);return}y.current=!1,t(r.update)},noValidate:!0,"aria-label":`Cloud budget`,children:[(0,$.jsx)(`div`,{className:u.fieldGrid,children:l.map(e=>(0,$.jsx)(d,{id:`${f}-${e.field}`,label:e.label,hint:e.hint,error:h[e.field],size:`sm`,inputMode:e.integer?`numeric`:`decimal`,prefix:e.money?`$`:void 0,value:p[e.field],disabled:i||r,onChange:t=>b({[e.field]:t.target.value})},e.field))}),(0,$.jsx)(o,{checked:p.selfImprove,onChange:e=>b({selfImprove:e}),label:`Verse may launch self-improvement tasks on its own`,disabled:i||r}),_?(0,$.jsx)(`p`,{className:u.muted,role:`status`,children:_}):null,(0,$.jsxs)(`div`,{className:u.formActions,children:[n?(0,$.jsx)(a,{variant:`ghost`,size:`sm`,onClick:n,disabled:r,children:`Cancel`}):null,(0,$.jsx)(a,{type:`submit`,variant:`primary`,size:`sm`,busy:r,disabled:i,children:`Save budget`})]})]})}export{ue as a,A as c,le as d,j as f,B as i,I as l,O as m,R as n,E as o,M as p,T as r,L as s,de as t,H as u};
2
+ //# sourceMappingURL=CloudBudgetForm-Bd91iStN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudBudgetForm-Bd91iStN.js","names":[],"sources":["../../../../../../src/core/routing/headroom.ts","../../../../../../src/web-ui/routes/verse/command/command-model.ts","../../../../../../src/web-ui/routes/verse/cloud/CloudBudgetForm.tsx"],"sourcesContent":["/**\n * Live headroom per seat — how much of each seat autonomy may still use under\n * its budget policy (V3.10 unit A9).\n *\n * INPUT is a `SeatCapacity`: the public Verse seat projection (the same\n * windows `GET /api/verse/accounts` serves — Claude `five_hour`/`seven_day`,\n * Codex `codex_<bucket>_{primary,secondary}`, Grok's billing period) reduced to\n * what budgeting needs. OUTPUT is the contract's `SeatHeadroom`, plus the\n * reopening time the router needs for `SeatExclusion.nextEligibleAt`.\n *\n * HONESTY RULES (docs/VERSE-TELEMETRY-V2.md, and Mason's decision):\n * - Unknown usage is NOT headroom. A paid seat with no reading, a reported\n * account window with a null percent, or a reading older than\n * `readingMaxAgeMs` is ineligible for autonomy. This is the fix for the old\n * Claude fail-open, where \"no signal\" was treated as \"go ahead\".\n * - A spent PER-MODEL window is not a spent account. Claude's\n * `seven_day_fable` can read 100% while `five_hour` reads 15%; only the\n * account-wide windows bind (seats.ts `seatUsability` has the same rule).\n * - Claude publishes no machine-readable reset. Its reset is quoted in the\n * provider's own words and never turned into a timestamp or a countdown.\n * - Codex credits are paid overage. Autonomy never spends them, so a spent\n * Codex window blocks autonomy even when a balance exists.\n *\n * WINDOW SEMANTICS\n * - `reservePercent` keeps that share of the LONG (weekly / billing-period)\n * window for Mason: autonomy stops at `100 − reserve` used. A seat that\n * reports only a short window applies the reserve to that one.\n * - `maxSessionWindowPercent` is the ceiling on the SHORT (5-hour) window, so\n * a live interactive session is never starved (\"never touch Claude while\n * its 5-hour window is above 70%\").\n * - The binding window is whichever of the two leaves autonomy LESS room.\n *\n * BROWSER-SAFE and PURE: type-only imports, no clock reads (callers pass\n * `nowMs`), no I/O.\n */\nimport type { VerseSeat, VerseSeatCapacity } from '../verse/types.js';\nimport type { BudgetEngine } from './policy.js';\nimport { reasonSentences } from './seat-reasons.js';\nimport type { SeatBudgetPolicy, SeatHeadroom, SeatReason } from './types.js';\n\n/** A reading older than this is too stale to spend against (the collector polls every 30 s when active). */\nexport const HEADROOM_READING_MAX_AGE_MS = 15 * 60_000;\n\n/**\n * A window whose reset is further away than this cannot be a 5-hour window,\n * whatever its id says. Codex's `primary` bucket is USUALLY the 5-hour window,\n * but on 2026-09-24 both accounts reported a `primary` resetting ~42 h out —\n * so the reset distance, when known, overrides the id.\n */\nexport const SESSION_WINDOW_MAX_MS = 5 * 60 * 60_000 + 15 * 60_000;\n\nexport type SeatWindowClass = 'session' | 'weekly' | 'model';\n\nexport interface CapacityWindow {\n id: string;\n /** Provider percent; null = NO SIGNAL (not zero). */\n usedPercent: number | null;\n /** Machine reset; always null for Claude. */\n resetsAt: string | null;\n /** The provider's own reset wording (Claude), verbatim. */\n resetDescription: string | null;\n /** Provider explicitly flagged the limit (a denial, not a measurement). */\n limitReached: boolean;\n}\n\n/** What the budget layer needs to know about one seat. JSON-safe; persisted in the capacity snapshot. */\nexport interface SeatCapacity {\n seatId: string;\n engine: BudgetEngine;\n label: string;\n /** True for local runtime seats: $0 and no provider window. */\n free: boolean;\n windows: CapacityWindow[];\n /** The account reports no usable login. */\n signedOut: boolean;\n /** Can a turn reach this seat at all? Null when unknown. */\n reachable: boolean | null;\n /** Default model's context window in tokens; null when unknown. */\n contextWindow: number | null;\n /** When the windows were read; null when never. */\n observedAt: string | null;\n /** Today's metered spend in USD; null when unknown or not metered. */\n spentTodayUsd: number | null;\n}\n\n/** `SeatHeadroom` plus the facts the router needs but the wire shape does not carry. */\nexport interface SeatAssessment {\n headroom: SeatHeadroom;\n /** When the blocking condition is expected to lift; null when unknown / never / not blocked. */\n reopensAt: string | null;\n /** True when usage is known to be spent (limit flagged or ≥ 100%) on an account window. */\n exhausted: boolean;\n /** True when usage evidence is missing, partial or stale. */\n unknownUsage: boolean;\n /** The sentences naming each spent account window (a subset of `headroom.reasons`). */\n spentReasons: string[];\n /** `headroom.reasons` as data (same order): each sentence without its reset clause, plus the reset. */\n details: SeatReason[];\n /** `spentReasons` as data. */\n spentDetails: SeatReason[];\n}\n\nexport interface AssessOptions {\n nowMs: number;\n readingMaxAgeMs?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Projection from the public seat shape\n// ---------------------------------------------------------------------------\n\n/**\n * Reduce a public `VerseSeat` to a `SeatCapacity`. `liveCapacity`, when given,\n * replaces the seat's own (possibly cached) capacity — the caller re-reads\n * telemetry per request exactly as verse-api's `liveSeats` does.\n */\nexport function capacityFromSeat(seat: VerseSeat, liveCapacity?: VerseSeatCapacity | null): SeatCapacity {\n const free = seat.engine === 'local';\n const capacity = liveCapacity === undefined ? seat.capacity ?? null : liveCapacity;\n // A failed account check may retain a verified window for display until its\n // original expiry. Autonomy cannot spend against that prior reading.\n const current = !free && capacity &&\n (capacity.usability === 'ready' || capacity.usability === 'tight' || capacity.usability === 'exhausted')\n ? capacity : null;\n const windows: CapacityWindow[] = current === null ? [] : current.windows.map((w) => ({\n id: w.id,\n usedPercent: typeof w.usedPercent === 'number' && Number.isFinite(w.usedPercent)\n ? Math.max(0, Math.min(100, w.usedPercent))\n : null,\n resetsAt: w.resetsAt,\n resetDescription: w.resetDescription,\n limitReached: w.limitReached === true,\n }));\n const signedOut = !free && capacity?.usability === 'signed-out';\n return {\n seatId: seat.id,\n engine: seat.engine,\n label: seat.label,\n free,\n windows,\n signedOut,\n // A local seat is only listed when the runtime answered discovery. A\n // native seat is reachable unless signed out; \"no reading\" is a usage\n // question, not a reachability one.\n reachable: free ? true : signedOut ? false : null,\n contextWindow: typeof seat.contextWindow === 'number' && seat.contextWindow > 0 ? seat.contextWindow : null,\n observedAt: current === null ? null : current.observedAt ?? seat.health.observedAt ?? null,\n spentTodayUsd: null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Window classification\n// ---------------------------------------------------------------------------\n\nfunction resetDistanceMs(window: CapacityWindow, nowMs: number): number | null {\n if (!window.resetsAt) return null;\n const at = Date.parse(window.resetsAt);\n return Number.isFinite(at) ? at - nowMs : null;\n}\n\n/** Which role a window plays for budgeting. Per-model windows never bind. */\nexport function classifyWindow(engine: BudgetEngine, window: CapacityWindow, nowMs: number): SeatWindowClass {\n const id = window.id;\n if (engine === 'claude') {\n if (id === 'five_hour') return 'session';\n if (id === 'seven_day') return 'weekly';\n if (id.startsWith('seven_day_')) return 'model';\n }\n if (engine === 'grok') return 'weekly';\n const distance = resetDistanceMs(window, nowMs);\n if (engine === 'codex') {\n if (id.endsWith('_secondary')) return 'weekly';\n if (id.endsWith('_primary')) return distance !== null && distance > SESSION_WINDOW_MAX_MS ? 'weekly' : 'session';\n }\n // Unknown id: only a reset we can SEE inside five hours makes it short.\n return distance !== null && distance <= SESSION_WINDOW_MAX_MS ? 'session' : 'weekly';\n}\n\n/** Human name for a window, for reasons. */\nexport function windowName(engine: BudgetEngine, window: CapacityWindow, cls: SeatWindowClass): string {\n if (cls === 'session') return '5-hour window';\n if (cls === 'model') {\n const model = window.id.startsWith('seven_day_') ? window.id.slice('seven_day_'.length) : window.id;\n return `${model.charAt(0).toUpperCase()}${model.slice(1)}-only weekly window`;\n }\n if (engine === 'grok' || window.id.endsWith('_monthly')) {\n return window.id.endsWith('_monthly') ? 'monthly billing window' : window.id.endsWith('_weekly') ? 'weekly window' : 'billing-period window';\n }\n return 'weekly window';\n}\n\n/**\n * A window's reset as data: the machine instant when there is one, else\n * Claude's own words, verbatim — never a synthesized time. `reasonSentence`\n * turns it back into the \" (resets …)\" clause the string form has always had.\n */\nfunction resetOf(window: CapacityWindow): Pick<SeatReason, 'resetsAt' | 'resetDescription'> {\n if (window.resetsAt) return { resetsAt: window.resetsAt, resetDescription: null };\n return { resetsAt: null, resetDescription: window.resetDescription ?? null };\n}\n\nfunction isSpent(window: CapacityWindow): boolean {\n return window.limitReached || (window.usedPercent !== null && window.usedPercent >= 100);\n}\n\nfunction pct(value: number): string {\n return `${Math.round(value)}%`;\n}\n\nfunction formatAge(ms: number): string {\n const minutes = Math.round(ms / 60_000);\n if (minutes < 120) return `${minutes} min`;\n const hours = Math.round(minutes / 60);\n return hours < 48 ? `${hours} h` : `${Math.round(hours / 24)} days`;\n}\n\n// ---------------------------------------------------------------------------\n// Assessment\n// ---------------------------------------------------------------------------\n\ninterface ClassedWindow {\n window: CapacityWindow;\n cls: SeatWindowClass;\n}\n\n/** The highest reading among `rows`, with its window; null when none carried a percent. */\nfunction peak(rows: ClassedWindow[]): ClassedWindow | null {\n let best: ClassedWindow | null = null;\n for (const row of rows) {\n if (row.window.usedPercent === null) continue;\n if (best === null || row.window.usedPercent > best.window.usedPercent!) best = row;\n }\n return best;\n}\n\n/**\n * When EVERY spent window in `rows` has reset — the seat is blocked until the\n * last of them does, so this is the latest reset. Null unless every row has a\n * machine-readable reset (one unknown reset makes the reopening unknown).\n */\nfunction lastReset(rows: ClassedWindow[]): string | null {\n const times: string[] = [];\n for (const row of rows) {\n const at = row.window.resetsAt;\n if (!at || !Number.isFinite(Date.parse(at))) return null;\n times.push(at);\n }\n times.sort((a, b) => Date.parse(b) - Date.parse(a));\n return times[0] ?? null;\n}\n\n/** Full assessment of one seat for AUTONOMOUS use under `policy`. Pure. */\nexport function assessSeat(capacity: SeatCapacity, policy: SeatBudgetPolicy, opts: AssessOptions): SeatAssessment {\n const { nowMs } = opts;\n const maxAge = opts.readingMaxAgeMs ?? HEADROOM_READING_MAX_AGE_MS;\n // Reasons are built as data; `headroom.reasons` is derived from them.\n const details: SeatReason[] = [];\n\n // ── Local: free, windowless, bounded only by reachability ──────────────\n if (capacity.free) {\n const reachable = capacity.reachable !== false;\n if (!policy.enabled) details.push({ kind: 'switched-off', text: 'Autonomy is switched off for this seat.' });\n if (!reachable) details.push({ kind: 'unreachable', text: 'The local model runtime is not reachable.' });\n const eligible = policy.enabled && reachable;\n if (eligible) details.push({ kind: 'headroom', text: 'Local model — free, with no usage window to protect.' });\n return {\n headroom: {\n seatId: capacity.seatId,\n sessionUsedPercent: null,\n weeklyUsedPercent: null,\n bindingWindow: null,\n // No window binds a local seat; \"all of it\" is the honest number while\n // the runtime answers, and unknown while it does not.\n autonomyHeadroomPercent: reachable ? 100 : null,\n resetAt: null,\n eligibleForAutonomy: eligible,\n reasons: reasonSentences(details),\n },\n reopensAt: null,\n exhausted: false,\n unknownUsage: false,\n spentReasons: [],\n details,\n spentDetails: [],\n };\n }\n\n // ── Paid seat ──────────────────────────────────────────────────────────\n const classed: ClassedWindow[] = capacity.windows.map((window) => ({\n window,\n cls: classifyWindow(capacity.engine, window, nowMs),\n }));\n const account = classed.filter((row) => row.cls !== 'model');\n const sessionRows = account.filter((row) => row.cls === 'session');\n const weeklyRows = account.filter((row) => row.cls === 'weekly');\n const sessionPeak = peak(sessionRows);\n const weeklyPeak = peak(weeklyRows);\n const sessionUsed = sessionPeak?.window.usedPercent ?? null;\n const weeklyUsed = weeklyPeak?.window.usedPercent ?? null;\n\n // Evidence quality.\n const observedMs = capacity.observedAt ? Date.parse(capacity.observedAt) : NaN;\n const ageMs = Number.isFinite(observedMs) ? nowMs - observedMs : null;\n const stale = ageMs === null || ageMs > maxAge;\n const hasReading = sessionUsed !== null || weeklyUsed !== null;\n const nullWindow = account.find((row) => row.window.usedPercent === null && !row.window.limitReached);\n\n // Ceilings. The reserve protects the LONG window; with no long window it\n // protects the short one. The session ceiling protects a live session.\n const reserveCeiling = 100 - policy.reservePercent;\n const sessionCeiling = policy.maxSessionWindowPercent ?? 100;\n // With no long window the short one carries BOTH limits; say which one bit.\n const sessionLimitIsReserve = weeklyRows.length === 0 && reserveCeiling < sessionCeiling;\n const sessionLimit = sessionLimitIsReserve ? reserveCeiling : sessionCeiling;\n let weeklyRoom: number | null = null;\n let sessionRoom: number | null = null;\n if (weeklyUsed !== null) weeklyRoom = reserveCeiling - weeklyUsed;\n if (sessionUsed !== null) sessionRoom = sessionLimit - sessionUsed;\n let bindingWindow: SeatHeadroom['bindingWindow'] = null;\n let room: number | null = null;\n if (weeklyRoom !== null && (sessionRoom === null || weeklyRoom <= sessionRoom)) {\n bindingWindow = 'weekly';\n room = weeklyRoom;\n } else if (sessionRoom !== null) {\n bindingWindow = 'session';\n room = sessionRoom;\n }\n const bindingRow = bindingWindow === 'weekly' ? weeklyPeak : bindingWindow === 'session' ? sessionPeak : null;\n\n // Blockers, most fundamental first. Every one gets a plain sentence.\n let reopensAt: string | null = null;\n const spentRows = account.filter((row) => isSpent(row.window));\n const exhausted = spentRows.length > 0;\n const unknownUsage = !hasReading || stale || nullWindow !== undefined;\n\n if (!policy.enabled) details.push({ kind: 'switched-off', text: 'Autonomy is switched off for this seat.' });\n if (capacity.signedOut) {\n details.push({ kind: 'signed-out', text: 'Signed out — reconnect this account before anything can run on it.' });\n }\n\n const spentDetails: SeatReason[] = [];\n if (exhausted) {\n for (const row of spentRows) {\n const used = row.window.limitReached ? 'limit reached' : `${pct(row.window.usedPercent!)} used`;\n spentDetails.push({\n kind: 'spent',\n text: `The ${windowName(capacity.engine, row.window, row.cls)} is spent — ${used}.`,\n ...resetOf(row.window),\n });\n }\n details.push(...spentDetails);\n reopensAt = lastReset(spentRows);\n }\n\n if (!hasReading) {\n details.push({\n kind: 'unknown-usage',\n text: account.length === 0\n ? 'No usage reading for this seat — unknown usage is not headroom, so autonomy stays off it.'\n : 'Its usage windows carried no percentage — unknown usage is not headroom, so autonomy stays off it.',\n });\n } else if (stale) {\n details.push({\n kind: 'unknown-usage',\n text: ageMs === null\n ? 'The usage reading has no timestamp — too uncertain to spend against.'\n : `The usage reading is ${formatAge(ageMs)} old — too stale to spend against (limit ${formatAge(maxAge)}).`,\n });\n } else if (nullWindow) {\n details.push({\n kind: 'unknown-usage',\n text: `The ${windowName(capacity.engine, nullWindow.window, nullWindow.cls)} carried no percentage — `\n + 'unknown usage is not headroom.',\n });\n }\n\n const usdCap = policy.dailyUsdCap;\n let overUsd = false;\n if (usdCap !== undefined) {\n if (capacity.spentTodayUsd === null) {\n overUsd = true;\n details.push({ kind: 'spend-cap', text: `A $${usdCap.toFixed(2)} daily cap is set but today's spend on this seat is unknown.` });\n } else if (capacity.spentTodayUsd >= usdCap) {\n overUsd = true;\n details.push({ kind: 'spend-cap', text: `Spent $${capacity.spentTodayUsd.toFixed(2)} of its $${usdCap.toFixed(2)} daily cap.` });\n }\n }\n\n // Ceiling reasons only matter when the reading itself is trustworthy.\n const ceilingBlocked = !exhausted && !unknownUsage && room !== null && room <= 0;\n if (ceilingBlocked && bindingRow) {\n const name = windowName(capacity.engine, bindingRow.window, bindingRow.cls);\n const used = pct(bindingRow.window.usedPercent!);\n if (bindingWindow === 'session' && !sessionLimitIsReserve) {\n details.push({\n kind: 'session-ceiling',\n text: `The ${name} is ${used} used; autonomy stops at ${pct(sessionLimit)} to protect your live session.`,\n ...resetOf(bindingRow.window),\n });\n } else {\n details.push({\n kind: 'reserve',\n text: `The ${name} is ${used} used; ${pct(policy.reservePercent)} is kept for you, so autonomy `\n + `stops at ${pct(reserveCeiling)}.`,\n ...resetOf(bindingRow.window),\n });\n }\n reopensAt = bindingRow.window.resetsAt;\n }\n\n // Model-scoped windows are facts worth showing, never blockers.\n for (const row of classed) {\n if (row.cls === 'model' && isSpent(row.window)) {\n details.push({\n kind: 'model-window',\n text: `The ${windowName(capacity.engine, row.window, row.cls)} is spent — it limits that model only, not the account.`,\n });\n }\n }\n\n const eligible = policy.enabled && !capacity.signedOut && !exhausted && !unknownUsage && !overUsd\n && room !== null && room > 0;\n if (eligible && bindingRow) {\n const name = windowName(capacity.engine, bindingRow.window, bindingRow.cls);\n const kept = bindingWindow === 'weekly' || sessionLimitIsReserve\n ? policy.reservePercent > 0 ? ` (${pct(policy.reservePercent)} kept for you)` : ''\n : sessionLimit < 100 ? ` (autonomy stops at ${pct(sessionLimit)} to protect your live session)` : '';\n details.unshift({ kind: 'headroom', text: `${pct(room!)} of the ${name} is left for autonomy${kept}.` });\n }\n\n return {\n headroom: {\n seatId: capacity.seatId,\n sessionUsedPercent: sessionUsed,\n weeklyUsedPercent: weeklyUsed,\n bindingWindow,\n autonomyHeadroomPercent: room === null ? null : Math.max(0, Math.round(room)),\n resetAt: bindingRow?.window.resetsAt ?? null,\n eligibleForAutonomy: eligible,\n reasons: reasonSentences(details),\n },\n // A seat that is merely switched off, signed out, or unknown has no\n // known reopening — only a window reset is a date we can name.\n reopensAt: !policy.enabled || capacity.signedOut || unknownUsage || overUsd ? null : reopensAt,\n exhausted,\n unknownUsage,\n spentReasons: reasonSentences(spentDetails),\n details,\n spentDetails,\n };\n}\n\n/** The contract's `SeatHeadroom` for one seat. */\nexport function computeHeadroom(capacity: SeatCapacity, policy: SeatBudgetPolicy, opts: AssessOptions): SeatHeadroom {\n return assessSeat(capacity, policy, opts).headroom;\n}\n","/**\n * routes/verse/command/command-model.ts — the pure numbers behind Command's\n * KPI row, \"Since you looked\" strip, seat burn-downs and the Needs-you card\n * (unit C7; SPEC-310B §6, SPEC-310C §5).\n *\n * Honesty (docs/VERSE-TELEMETRY-V2.md): a KPI whose source did not answer\n * is \"—\", and a delta is computed only when BOTH windows are fully known — a\n * week with one unreadable day has no honest \"vs prior 7d\". Framework-free;\n * tested directly.\n */\nimport type { FleetLiveSnapshotV1, FleetLiveRun } from '../../../../core/fleet/fleet-types.js';\nimport type { LeaderStateV1 } from '../../../../core/vision/leader-types.js';\nimport type { LearningStateV1 } from '../../../../core/learn/harness-types.js';\nimport type { NeedsYouItem, NeedsYouSeverity, VerseActivityResponse } from '../../../../core/verse/workbench-types.js';\nimport type { FleetHistoryDay, FleetHistoryResponse } from '../../../../core/verse/fleet-history-types.js';\nimport type { BudgetView } from '../../../../core/routing/policy.js';\nimport type { CapacityHistoryResponse } from '../../../../core/routing/capacity-history-types.js';\nimport type { SeatReason, SeatReasonKind } from '../../../../core/routing/types.js';\nimport type { VerseSeat } from '../../../../core/verse/types.js';\nimport { classifyWindow } from '../../../../core/routing/headroom.js';\nimport { describeResetAt } from '../../../../core/verse/seat-readiness.js';\nimport type { EffectivePolicy } from '../../../../core/authority/types.js';\nimport type { StatTileDelta } from '../../../components/charts/StatTile.js';\nimport { asSentence, localTimes, parseLegacyReason } from '../fleet/why-seat-model.js';\n\nconst DAY = 86_400_000;\nconst HOUR = 3_600_000;\n\n// ---------------------------------------------------------------------------\n// Windows over the daily history\n// ---------------------------------------------------------------------------\n\n/** Sum of `pick` over days [end-len, end); null if any day in the window is unknown or missing. */\nexport function windowSum(days: readonly FleetHistoryDay[], pick: (d: FleetHistoryDay) => number | null, len: number, endOffset = 0): number | null {\n const end = days.length - endOffset;\n const start = end - len;\n if (start < 0 || len <= 0) return null;\n let total = 0;\n for (let i = start; i < end; i++) {\n const v = pick(days[i]!);\n if (v === null || !Number.isFinite(v)) return null;\n total += v;\n }\n return total;\n}\n\n/** The last `len` values of `pick`, oldest first (nulls kept — the sparkline breaks there). */\nexport function lastValues(days: readonly FleetHistoryDay[], pick: (d: FleetHistoryDay) => number | null, len: number): (number | null)[] {\n return days.slice(Math.max(0, days.length - len)).map(pick);\n}\n\nfunction mean(values: readonly (number | null)[]): number | null {\n const known = values.filter((v): v is number => v !== null && Number.isFinite(v));\n return known.length ? known.reduce((a, b) => a + b, 0) / known.length : null;\n}\n\n// ---------------------------------------------------------------------------\n// KPI row\n// ---------------------------------------------------------------------------\n\nexport interface Kpi {\n id: 'merged' | 'green' | 'cycle' | 'spend' | 'lift';\n label: string;\n /** Display string; \"—\" when unknown. */\n value: string;\n delta: StatTileDelta | null;\n trend: (number | null)[] | undefined;\n trendLabel: string;\n caption: string;\n}\n\nexport interface KpiInputs {\n fleet: FleetLiveSnapshotV1 | null;\n history: FleetHistoryResponse | null;\n learning: LearningStateV1 | null;\n policy: EffectivePolicy | null;\n /**\n * Accepted but no longer read: the spend caption used to list every paid\n * seat's window usage, which the seat burn-downs already show. Kept so\n * callers need not change.\n */\n budget?: BudgetView | null;\n}\n\n/**\n * The per-token (metered API) part of a day's spend, when the server splits\n * it out. WHY not `estCostUsd`: that field prices EVERY run's tokens from a\n * static table — Claude/Codex/Grok subscription CLI runs included — so\n * showing it as \"spend vs the metered cap\" reported subscription work as a\n * budget breach (review 3.10 c10; the default grant's cap is $0/day).\n * Subscriptions are not billed per token: their cost is window usage, shown\n * as percent. fleet-history reports `meteredCostUsd` per day (per-token API\n * engines only — core/verse/fleet-history.ts `runBilling`); a server that\n * predates it leaves the figure unknown — \"—\", never the mixed estimate.\n */\nexport function meteredCost(d: FleetHistoryDay): number | null {\n const v = (d as FleetHistoryDay & { meteredCostUsd?: number | null }).meteredCostUsd;\n return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : null;\n}\n\nconst signedInt = (v: number) => (v > 0 ? `+${v}` : String(v));\nconst signedPts = (v: number) => `${v > 0 ? '+' : ''}${v.toFixed(1)}`;\nconst signedUsd = (v: number) => `${v > 0 ? '+' : v < 0 ? '−' : ''}$${Math.abs(v).toFixed(2)}`;\n\n/** \"2h 14m\", \"38m\", \"3d 2h\". */\nexport function formatSpan(ms: number | null): string {\n if (ms === null || !Number.isFinite(ms) || ms < 0) return '—';\n const m = Math.round(ms / 60_000);\n if (m < 60) return `${m}m`;\n const h = Math.floor(m / 60);\n if (h < 48) return m % 60 ? `${h}h ${m % 60}m` : `${h}h`;\n const d = Math.floor(h / 24);\n return h % 24 ? `${d}d ${h % 24}h` : `${d}d`;\n}\n\n/** Mean daily post-merge green % across repos, per day (oldest first, 14 days). */\nexport function greenTrend(fleet: FleetLiveSnapshotV1 | null): (number | null)[] {\n if (!fleet || fleet.repos.length === 0) return [];\n const len = Math.max(...fleet.repos.map((r) => r.greenTrend.length));\n return Array.from({ length: len }, (_, i) => mean(fleet.repos.map((r) => r.greenTrend[i - (len - r.greenTrend.length)] ?? null)));\n}\n\nexport function buildKpis({ fleet, history, learning, policy }: KpiInputs): Kpi[] {\n const days = history?.days ?? [];\n const merges = (d: FleetHistoryDay) => d.merges.realized;\n const spend = meteredCost;\n\n // Merged · 7d — the live summary when the daemon answers, else the history window.\n const merged7 = fleet?.summary.merged7d ?? windowSum(days, merges, 7);\n const mergedPrior = windowSum(days, merges, 7, 7);\n const mergedNow = windowSum(days, merges, 7);\n const mergedDelta = mergedNow !== null && mergedPrior !== null ? mergedNow - mergedPrior : null;\n\n // Post-merge green — the 7-day figure plus the 14-day repo trend.\n const green = fleet?.summary.postMergeGreenPct7d ?? null;\n const gTrend = greenTrend(fleet);\n const gNow = gTrend.length >= 14 ? mean(gTrend.slice(-7)) : null;\n const gPrior = gTrend.length >= 14 ? mean(gTrend.slice(-14, -7)) : null;\n const greenDelta = gNow !== null && gPrior !== null ? gNow - gPrior : null;\n\n // Metered spend vs cap — per-token API spend ONLY (see meteredCost).\n const spend7 = windowSum(days, spend, 7);\n const spendPrior = windowSum(days, spend, 7, 7);\n const capPerDay = policy ? policy.spend.meteredUsdPerDay : null;\n const spendValue = spend7 === null ? '—' : `$${spend7.toFixed(2)}${capPerDay !== null ? ` / $${(capPerDay * 7).toFixed(0)}` : ''}`;\n const meteredKnown = days.some((d) => meteredCost(d) !== null);\n // One short line. Subscriptions are window usage, not dollars: they live\n // on the seat burn-downs, not in this caption.\n const capWords = capPerDay === null ? 'per-token APIs only' : capPerDay === 0 ? 'metered APIs off' : `cap $${capPerDay}/day`;\n const spendCaption = meteredKnown ? capWords : `${capWords} · not reported yet`;\n\n // Lift — the active harness's own experiment (null = baseline in force).\n const active = learning?.active ?? null;\n const activeExp = active?.experimentId ? learning?.experiments.find((e) => e.id === active.experimentId) ?? null : null;\n const lift = activeExp?.lift ?? null;\n\n return [\n {\n id: 'merged',\n label: 'Merged · 7d',\n value: merged7 === null ? '—' : String(merged7),\n delta: mergedDelta === null ? null : { value: mergedDelta, unit: Math.abs(mergedDelta) === 1 ? 'merge' : 'merges', versus: 'vs prior 7d', goodWhenPositive: true, format: signedInt },\n trend: days.length ? lastValues(days, merges, 14) : undefined,\n trendLabel: 'Merges per day, last 14 days',\n caption: fleet?.summary.mergedToday != null ? `${fleet.summary.mergedToday} today` : 'fleet merges on default branches',\n },\n {\n id: 'green',\n label: 'Post-merge green',\n value: green === null ? '—' : `${Math.round(green)}%`,\n delta: greenDelta === null ? null : { value: greenDelta, unit: 'pts', versus: 'vs prior 7d', goodWhenPositive: true, format: signedPts },\n trend: gTrend.length ? gTrend : undefined,\n trendLabel: 'Post-merge green percent per day',\n caption: green === null ? 'no completed post-merge watch yet' : 'CI + suite 2 h after each merge',\n },\n {\n id: 'cycle',\n label: 'Cycle time',\n value: formatSpan(fleet?.summary.cycleTimeP50Ms7d ?? null),\n delta: null,\n trend: undefined,\n trendLabel: 'Cycle time',\n caption: 'median, proposal → merge, 7d',\n },\n {\n id: 'spend',\n label: 'Metered spend · 7d',\n value: spendValue,\n delta: spend7 !== null && spendPrior !== null ? { value: spend7 - spendPrior, versus: 'vs prior 7d', goodWhenPositive: false, format: signedUsd } : null,\n trend: meteredKnown ? lastValues(days, spend, 14) : undefined,\n trendLabel: 'Metered (per-token API) spend per day',\n caption: spendCaption,\n },\n {\n id: 'lift',\n label: 'Lift',\n value: lift ? `${signedPts(lift.mean)} pts` : '—',\n delta: null,\n trend: undefined,\n trendLabel: 'Harness lift',\n caption: active ? `harness ${active.id} vs baseline${lift ? ` (95% ${signedPts(lift.ciLow)} to ${signedPts(lift.ciHigh)})` : ''}` : 'no harness adopted — defaults in force',\n },\n ];\n}\n\n/**\n * True when the KPI row would say nothing: no merge this week or last, no\n * post-merge reading, no cycle time, no metered spend, no lift — \"0\", four\n * dashes and \"$0.00\". Command drops the row then while its \"Autonomy is off\"\n * banner explains why; a row with any real figure is always kept.\n */\nexport function kpisSayNothing({ fleet, history, learning }: Pick<KpiInputs, 'fleet' | 'history' | 'learning'>): boolean {\n const days = history?.days ?? [];\n const merges = (d: FleetHistoryDay) => d.merges.realized;\n const merged7 = fleet?.summary.merged7d ?? windowSum(days, merges, 7);\n const mergedPrior = windowSum(days, merges, 7, 7);\n const active = learning?.active ?? null;\n const lift = active?.experimentId ? (learning?.experiments.find((e) => e.id === active.experimentId)?.lift ?? null) : null;\n return (\n (merged7 ?? 0) === 0 &&\n (mergedPrior ?? 0) === 0 &&\n (fleet?.summary.postMergeGreenPct7d ?? null) === null &&\n (fleet?.summary.cycleTimeP50Ms7d ?? null) === null &&\n (windowSum(days, meteredCost, 7) ?? 0) === 0 &&\n lift === null\n );\n}\n\n// ---------------------------------------------------------------------------\n// Since you looked\n// ---------------------------------------------------------------------------\n\nexport interface SinceItem {\n id: string;\n text: string;\n tone: 'success' | 'danger' | 'warning' | 'neutral' | 'info';\n}\n\nexport interface SinceInputs {\n /** ISO of the previous visit; null = first visit (nothing is \"new\"). */\n lastLookedAt: string | null;\n fleet: FleetLiveSnapshotV1 | null;\n leader: LeaderStateV1 | null;\n activity: VerseActivityResponse | null;\n}\n\nconst after = (iso: string | null, t: number) => iso !== null && Date.parse(iso) > t;\n\n/** What changed since the operator last opened Command, newest kinds first. */\nexport function sinceYouLooked({ lastLookedAt, fleet, leader, activity }: SinceInputs): SinceItem[] {\n if (!lastLookedAt) return [];\n const t = Date.parse(lastLookedAt);\n if (!Number.isFinite(t)) return [];\n const out: SinceItem[] = [];\n const ended = (fleet?.runs ?? []).filter((r) => after(r.endedAt, t));\n const count = (o: FleetLiveRun['outcome']) => ended.filter((r) => r.outcome === o).length;\n const merged = count('merged');\n const reverted = count('reverted');\n const failed = count('failed') + count('refused');\n if (merged) out.push({ id: 'merged', text: `${merged} merged`, tone: 'success' });\n if (reverted) out.push({ id: 'reverted', text: `${reverted} reverted`, tone: 'danger' });\n if (failed) out.push({ id: 'failed', text: `${failed} refused or failed`, tone: 'warning' });\n const memos = (leader?.timeline ?? []).filter((m) => Date.parse(m.at) > t && m.status === 'ok').length;\n if (memos) out.push({ id: 'memos', text: `${memos} new memo${memos === 1 ? '' : 's'}`, tone: 'info' });\n const fresh = (activity?.needsYou ?? []).filter((i) => Date.parse(i.since) > t).length;\n if (fresh) out.push({ id: 'needs', text: `${fresh} new for you`, tone: 'warning' });\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Needs you (Command card)\n// ---------------------------------------------------------------------------\n\nconst SEVERITY_RANK: Record<NeedsYouSeverity, number> = { high: 0, warn: 1, info: 2 };\n\n/** Most severe first, then newest. Pure — the drawer (C1) owns the full list. */\nexport function rankNeedsYou(items: readonly NeedsYouItem[]): NeedsYouItem[] {\n return [...items].sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity] || Date.parse(b.since) - Date.parse(a.since));\n}\n\n/**\n * Seat id → the label every other surface names that seat by (\"Claude Max\",\n * never \"claude-a\"): the live roster when the cache holds it, else the\n * budget route's seat info (polled on Command, so present when the roster is\n * not). Both carry the same `VerseSeat.label` for the same id.\n */\nexport function seatNames(\n seats: readonly Pick<VerseSeat, 'id' | 'label'>[] | null | undefined,\n view: Pick<BudgetView, 'seatInfo'> | null | undefined,\n): Map<string, string> {\n const names = new Map<string, string>();\n const put = (id: unknown, label: unknown) => {\n if (typeof id === 'string' && typeof label === 'string' && label.trim().length > 0) names.set(id, label.trim());\n };\n for (const s of view?.seatInfo ?? []) put(s.seatId, s.label);\n for (const s of seats ?? []) put(s.id, s.label);\n return names;\n}\n\n/** Sources that did not answer — the card must not say \"All clear\" while any is listed. */\nexport function silentSources(activity: VerseActivityResponse | null): string[] {\n if (!activity) return [];\n return Object.entries(activity.sources).filter(([, state]) => state !== 'ok').map(([source]) => source);\n}\n\n// ---------------------------------------------------------------------------\n// Seat burn-downs\n// ---------------------------------------------------------------------------\n\nexport type SeatWindow = 'session' | 'weekly';\n\nexport interface SeatReading {\n t: number;\n /** Percent of the window used, 0–100. */\n used: number;\n}\n\n/**\n * Readings are kept per seat WINDOW, not per seat: the binding window can flip\n * between the 5-hour and the weekly window from one poll to the next, and a\n * line that mixed the two would draw a jump that never happened. Seat ids may\n * contain `:` and `/`, never `#` (the server keys its history the same way).\n */\nexport function seriesKey(seatId: string, window: SeatWindow): string {\n return `${seatId}#${window}`;\n}\n\n/**\n * The readings this page has seen since Verse opened, per seat window. The\n * budget route serves ONE reading per seat, so on their own these start the\n * burn-down empty on every load; `mergeSeatHistory` adds the server's recorded\n * history (GET /api/verse/budget/history — core/routing/capacity-history.ts,\n * written by the capacity publishers), so a reload keeps the whole window.\n * Memory only, bounded; never a guessed history.\n *\n * The bound counts CHANGES, not polls: a flat run keeps only its two ends\n * (see `recordReading`). At one poll per 30 s a plain 240-reading ring held\n * two hours — a sliver of a 7-day window — while window percent moves in\n * whole points a few times an hour.\n */\nexport const SEAT_READINGS_MAX = 240;\n/** A merged line (recorded history + this page's readings), flat runs collapsed: a week of changes fits. */\nexport const SEAT_HISTORY_MAX = 2000;\n/** A drop this large between consecutive readings is a window reset: the line starts again. */\nconst RESET_DROP = 20;\n\nfunction isFlatEnd(line: readonly SeatReading[], used: number): boolean {\n const n = line.length;\n return n >= 2 && line[n - 1]!.used === used && line[n - 2]!.used === used;\n}\n\nexport function recordReading(history: Readonly<Record<string, SeatReading[]>>, view: BudgetView | null): Record<string, SeatReading[]> {\n if (!view) return { ...history };\n const t = Date.parse(view.sampledAt);\n if (!Number.isFinite(t)) return { ...history };\n const next: Record<string, SeatReading[]> = { ...history };\n for (const h of view.headroom) {\n const windows: [SeatWindow, number | null][] = [['session', h.sessionUsedPercent], ['weekly', h.weeklyUsedPercent]];\n for (const [window, used] of windows) {\n if (used === null || !Number.isFinite(used)) continue;\n const key = seriesKey(h.seatId, window);\n const prev = next[key] ?? [];\n if (prev.length && prev[prev.length - 1]!.t >= t) continue;\n // A reset (used dropped sharply) starts a new window — drop the old one.\n const fresh = prev.length && used + RESET_DROP < prev[prev.length - 1]!.used ? [] : prev;\n // A flat run keeps only its first and latest reading: the line through\n // them is identical, and the ring then spans hours or days, not minutes.\n next[key] = [...(isFlatEnd(fresh, used) ? fresh.slice(0, -1) : fresh), { t, used }].slice(-SEAT_READINGS_MAX);\n }\n }\n return next;\n}\n\nexport interface MergedSeatHistory {\n /** One line per seat window, oldest first, starting after its last reset. */\n readings: Record<string, SeatReading[]>;\n /** Series keys the server's recorded history contributed readings to. */\n recorded: ReadonlySet<string>;\n}\n\n/**\n * The server's recorded history merged with this page's live readings, per\n * seat window: oldest first, one reading per instant, cut after the last\n * reset drop and with flat runs collapsed — the same rules `recordReading`\n * applies, so the line reads the same either way. `server` is untrusted wire\n * data: malformed series and points are skipped.\n *\n * ONE CLOCK PER STRETCH OF LINE (review 3.10.1). The two sources stamp the\n * same observation differently: a recorded row carries the provider\n * reading's `observedAt` (capacity-history.ts), a live reading the budget\n * route's `sampledAt` — when it was ASKED, which is later, and the budget\n * wire carries no `observedAt` to do better. Interleaved, the same value\n * lands at two instants and an older value can sort after a newer one: a\n * poll just after a reset still serving 85% drew 85 → 3 → 85 → 3. So the\n * recorded history is authoritative for every instant it covers — up to the\n * moment the server answered (`generatedAt`), or its newest row when that is\n * later or `generatedAt` is unreadable — and live readings only extend a\n * recorded line past that point. The server records the snapshot behind\n * each budget read (capacity-history-api.ts, throttled to 15 s), so a live\n * reading hidden this way is recorded at its own `observedAt` by the next\n * history read, and until then the next live poll carries the same value.\n * A live reading after `generatedAt` came from a collector that had already\n * seen every recorded observation, so the extension never runs backwards.\n * Both instants are the Verse server's own clock. A window with no recorded\n * series keeps every live reading.\n */\nexport function mergeSeatHistory(live: Readonly<Record<string, SeatReading[]>>, server: CapacityHistoryResponse | null): MergedSeatHistory {\n const generatedAt = typeof server?.generatedAt === 'string' ? Date.parse(server.generatedAt) : Number.NaN;\n const persisted = new Map<string, SeatReading[]>();\n for (const series of Array.isArray(server?.series) ? server!.series : []) {\n if (!series || typeof series.seatId !== 'string' || (series.window !== 'session' && series.window !== 'weekly') || !Array.isArray(series.points)) continue;\n const points: SeatReading[] = [];\n for (const p of series.points) {\n if (!Array.isArray(p)) continue;\n const [t, used] = p as unknown[];\n if (typeof t === 'number' && Number.isFinite(t) && typeof used === 'number' && Number.isFinite(used) && used >= 0 && used <= 100) points.push({ t, used });\n }\n if (points.length === 0) continue;\n const key = seriesKey(series.seatId, series.window);\n persisted.set(key, [...(persisted.get(key) ?? []), ...points]);\n }\n const readings: Record<string, SeatReading[]> = {};\n const recorded = new Set<string>();\n for (const key of new Set([...Object.keys(live), ...persisted.keys()])) {\n const fromServer = persisted.get(key) ?? [];\n let fromPage = live[key] ?? [];\n if (fromServer.length > 0) {\n recorded.add(key);\n const newestRecorded = Math.max(...fromServer.map((r) => r.t));\n const coveredTo = Number.isFinite(generatedAt) ? Math.max(generatedAt, newestRecorded) : newestRecorded;\n fromPage = fromPage.filter((r) => r.t > coveredTo);\n }\n // Recorded rows can repeat an instant (two series under one key); the\n // last one read stands. Live readings are strictly later than all of them.\n const byT = new Map<number, SeatReading>();\n for (const r of fromServer) byT.set(r.t, r);\n for (const r of fromPage) byT.set(r.t, r);\n const sorted = [...byT.values()].sort((a, b) => a.t - b.t);\n let start = 0;\n for (let i = 1; i < sorted.length; i++) if (sorted[i]!.used + RESET_DROP < sorted[i - 1]!.used) start = i;\n const line: SeatReading[] = [];\n for (const r of sorted.slice(start)) {\n if (isFlatEnd(line, r.used)) line.pop();\n line.push(r);\n }\n readings[key] = line.slice(-SEAT_HISTORY_MAX);\n }\n return { readings, recorded };\n}\n\nexport interface SeatBurn {\n seatId: string;\n label: string;\n engine: 'claude' | 'codex' | 'grok' | 'local';\n window: 'session' | 'weekly' | null;\n /** Remaining percent readings (100 − used), inside [start, resetAt] when the reset is known. */\n points: { t: number; remaining: number | null }[];\n /** `resetAt` minus the window's length; null exactly when `resetAt` is. */\n start: number | null;\n resetAt: number | null;\n /**\n * Where `resetAt` came from: the budget route's machine instant\n * ('provider'), the provider's own reset words read on the clock they name\n * ('words' — see `resetInstantFromWords`), or nowhere (null).\n */\n resetFrom: 'provider' | 'words' | null;\n /**\n * The provider's own reset wording for the binding window, verbatim\n * (\"Sep 25 at 7pm (America/New_York)\"), when the seat roster carries it.\n * Claude publishes ONLY this — never a machine instant. The card always\n * shows the words; when they name a clock time inside this window they\n * also place the reset (`resetFrom: 'words'`), otherwise the card draws the\n * readings with no reset point or projection.\n */\n resetText: string | null;\n reservePercent: number;\n /**\n * The line autonomy stops at IN THE WINDOW THIS CHART DRAWS, as remaining\n * percent (see `bindingLine`); null when that window has no limit.\n */\n line: { value: number; label: string } | null;\n enabled: boolean;\n free: boolean;\n eligible: boolean;\n /**\n * The router's first reason for this seat in words (`seatReasonText`):\n * complete sentences, local times, never the log form's \" (resets <ISO>).\"\n * clause. Null when the route gave none.\n */\n reason: string | null;\n /**\n * True when the server's recorded history contributed to this window's\n * line; false when every point is one this page saw since Verse opened.\n * Picks the card's note when the line starts late in the window.\n */\n recorded: boolean;\n}\n\n/** Length of each provider window, for the chart domain (reset − length → reset). */\nexport const WINDOW_MS = { session: 5 * HOUR, weekly: 7 * DAY } as const;\n\n/**\n * The stop line for the binding window, mirroring core/routing/headroom.ts\n * exactly (review 3.10 d2). The reserve protects the LONG (weekly) window;\n * the 5-hour window is capped by `maxSessionWindowPercent` — except when the\n * seat has no weekly window at all, where the short one carries both limits\n * and the tighter one wins. Drawing the weekly reserve on a 5-hour chart put\n * Mason's balanced cutoff at 60% used instead of 70% and made the verdict say\n * \"autonomy has stopped\" while the server still had the seat eligible.\n *\n * `weeklyUsedPercent === null` stands in for \"no weekly window\": a weekly\n * window whose reading is unknown makes the seat ineligible server-side\n * anyway (unknown usage is never headroom), and the card shows that reason.\n */\nexport function bindingLine(\n h: { bindingWindow: 'session' | 'weekly' | null; weeklyUsedPercent: number | null },\n policy: { reservePercent: number; maxSessionWindowPercent?: number } | undefined,\n): { value: number; label: string } | null {\n if (!policy || h.bindingWindow === null) return null;\n const reserveCeiling = 100 - policy.reservePercent;\n const sessionCeiling = policy.maxSessionWindowPercent ?? 100;\n let ceiling: number;\n let label: string;\n if (h.bindingWindow === 'weekly') {\n ceiling = reserveCeiling;\n label = 'Reserved for you';\n } else if (h.weeklyUsedPercent === null && reserveCeiling < sessionCeiling) {\n ceiling = reserveCeiling;\n label = 'Reserved for you';\n } else {\n ceiling = sessionCeiling;\n label = '5-hour ceiling';\n }\n const value = 100 - ceiling;\n return value > 0 ? { value, label } : null;\n}\n\n/**\n * Axis/verdict time format per window (review 3.10 c17). A weekly window\n * starts exactly seven days before it resets, so weekday + time alone printed\n * \"Sat 10:27 AM … Resets Sat 10:27 AM\"; the weekly form carries the date.\n * It carries ONLY the date (3.10.1): inside one week the weekday repeats what\n * the date says, and at a quarter-width card \"Fri, Sep 18 at 11:46 PM\" +\n * \"Resets Fri, Sep 25 at 11:46 PM\" were wider than the plot and collided.\n */\nexport function burnTimeFormat(window: 'session' | 'weekly' | null): (ms: number) => string {\n const opts: Intl.DateTimeFormatOptions =\n window === 'session'\n ? { weekday: 'short', hour: 'numeric', minute: '2-digit' }\n : { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' };\n return (ms) => {\n const d = new Date(ms);\n return Number.isNaN(d.getTime()) ? '' : d.toLocaleString('en-US', opts);\n };\n}\n\n/**\n * The provider's reset wording for the seat window that plays `binding`'s\n * role. WHY the seat roster and not the budget route: `SeatHeadroom` (the\n * frozen budget wire shape) carries only a machine `resetAt`, which is\n * structurally null for Claude — the provider's sentence lives on the seat's\n * capacity windows. The window is picked with the SAME classifier the server\n * used to choose the binding window (core/routing/headroom.ts), so the words\n * belong to the window the chart draws, not to a per-model window beside it.\n */\nexport function bindingResetText(\n seat: Pick<VerseSeat, 'capacity'> | undefined,\n engine: SeatBurn['engine'],\n binding: 'session' | 'weekly' | null,\n nowMs: number,\n): string | null {\n if (!seat?.capacity || binding === null || engine === 'local') return null;\n for (const w of seat.capacity.windows) {\n const text = typeof w.resetDescription === 'string' ? w.resetDescription.trim() : '';\n if (text.length === 0) continue;\n if (classifyWindow(engine, { id: w.id, usedPercent: w.usedPercent, resetsAt: w.resetsAt, resetDescription: w.resetDescription, limitReached: w.limitReached }, nowMs) === binding) return text;\n }\n return null;\n}\n\n/**\n * \"resets Sep 25 at 7pm (America/New_York)\" from the provider's words. The\n * collector strips the leading \"resets\" (claude-account-usage.ts) but other\n * paths keep it; either way the sentence itself is shown verbatim.\n */\nexport function resetWords(text: string): string {\n return `resets ${text.replace(/^resets\\s+/i, '')}`;\n}\n\nconst MONTHS: readonly string[] = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];\n\n/**\n * The collector's own reset grammar (core/resources/claude-account-usage.ts\n * `RESET`), with the leading \"resets\" optional because other paths keep it.\n * Anything outside it — a changed native format, free prose — stays words.\n */\nconst RESET_WORDS_RE = /^(?:resets\\s+)?(?:([A-Za-z]{3}) ([1-9]|[12]\\d|3[01]) at )?([1-9]|1[0-2])(?::([0-5]\\d))?(am|pm) \\(([A-Za-z0-9_+-]+(?:\\/[A-Za-z0-9_+-]+){0,2})\\)$/i;\n\ninterface WallClock { y: number; mo: number; d: number; h: number; mi: number }\n\nfunction wallClock(ms: number, fmt: Intl.DateTimeFormat): WallClock | null {\n const parts = fmt.formatToParts(new Date(ms));\n const n = (type: Intl.DateTimeFormatPartTypes) => Number(parts.find((p) => p.type === type)?.value);\n const c = { y: n('year'), mo: n('month') - 1, d: n('day'), h: n('hour') % 24, mi: n('minute') };\n return Object.values(c).every(Number.isFinite) ? c : null;\n}\n\nconst wallAsUtc = (c: WallClock) => Date.UTC(c.y, c.mo, c.d, c.h, c.mi);\n\n/**\n * The instant whose wall clock in `fmt`'s zone reads `want`: start from the\n * wall time as if it were UTC and correct by the zone's offset until the\n * clock agrees. Null when it never does — a time skipped by a DST jump.\n */\nfunction instantAt(want: WallClock, fmt: Intl.DateTimeFormat): number | null {\n const target = wallAsUtc(want);\n let at = target;\n for (let i = 0; i < 3; i++) {\n const seen = wallClock(at, fmt);\n if (!seen) return null;\n const drift = target - wallAsUtc(seen);\n if (drift === 0) return at;\n at += drift;\n }\n return null;\n}\n\n/**\n * The instant named by the provider's reset words — \"Sep 25 at 6:59pm\n * (America/New_York)\" or \"1:40am (America/New_York)\" — or null when the words\n * are not in the collector's grammar or name an unknown zone.\n *\n * WHY this is not \"synthesizing a countdown from a description\"\n * (core/verse/types.ts): the words ARE an instant — a wall-clock minute in a\n * named IANA zone — so reading them on that zone's clock adds nothing the\n * provider did not say. The only thing the words leave out is the year (or,\n * for \"1:40am\", the day), and the candidate nearest `nowMs` is taken; the\n * caller still refuses an instant outside the window's own span\n * (`seatBurns`), so a misread can never stretch or shift the chart by more\n * than the window it draws. Without this, Claude — the seat that matters\n * most — was the one card with no window, no pace line and no projection.\n */\nexport function resetInstantFromWords(text: string, nowMs: number): number | null {\n const m = RESET_WORDS_RE.exec(text.trim());\n if (!m || !Number.isFinite(nowMs)) return null;\n const [, month, day, hour, minute, meridiem, zone] = m;\n let fmt: Intl.DateTimeFormat;\n try {\n fmt = new Intl.DateTimeFormat('en-US', { timeZone: zone, hourCycle: 'h23', year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' });\n } catch {\n return null; // RangeError: not a zone this runtime knows.\n }\n const today = wallClock(nowMs, fmt);\n if (!today) return null;\n const h = (Number(hour) % 12) + (meridiem!.toLowerCase() === 'pm' ? 12 : 0);\n const mi = minute === undefined ? 0 : Number(minute);\n const candidates: WallClock[] = [];\n if (month !== undefined) {\n const mo = MONTHS.indexOf(month.toLowerCase());\n const d = Number(day);\n if (mo < 0) return null;\n for (const y of [today.y - 1, today.y, today.y + 1]) {\n // Feb 30 would roll into March — refuse it rather than move the reset.\n if (new Date(Date.UTC(y, mo, d)).getUTCDate() === d) candidates.push({ y, mo, d, h, mi });\n }\n } else {\n for (const k of [-1, 0, 1]) {\n const date = new Date(Date.UTC(today.y, today.mo, today.d + k));\n candidates.push({ y: date.getUTCFullYear(), mo: date.getUTCMonth(), d: date.getUTCDate(), h, mi });\n }\n }\n let best: number | null = null;\n for (const c of candidates) {\n const at = instantAt(c, fmt);\n if (at !== null && (best === null || Math.abs(at - nowMs) < Math.abs(best - nowMs))) best = at;\n }\n return best;\n}\n\n/**\n * How far a words-derived reset may sit in the past (a reading taken just\n * before the rollover) or beyond one window ahead (the provider rounds its\n * words to the minute or the hour).\n */\nconst WORDS_RESET_SLACK_MS = 15 * 60_000;\n\n/**\n * The binding window's reset instant: the budget route's machine instant when\n * there is one, else the provider's words read on their own clock — but only\n * when that instant can close the window the current reading sits in (no\n * more than one window ahead, no more than a few minutes past). Words that\n * fail either test stay words, and the card says the reset is not placed.\n */\n/** Earliest instant accepted as a real reset (2000-01-01T00:00:00Z). */\nconst PLAUSIBLE_RESET_FLOOR_MS = Date.UTC(2000, 0, 1);\n\nexport function bindingReset(\n machine: string | null,\n resetText: string | null,\n windowMs: number | null,\n nowMs: number,\n): { at: number; from: 'provider' | 'words' } | null {\n const at = machine ? Date.parse(machine) : NaN;\n // A placeholder instant (epoch 0 or anything before 2000) is not a reset —\n // fall through to the provider's words, as if no instant were sent.\n if (Number.isFinite(at) && at >= PLAUSIBLE_RESET_FLOOR_MS) return { at, from: 'provider' };\n if (resetText === null || windowMs === null) return null;\n const words = resetInstantFromWords(resetText, nowMs);\n if (words === null || words < nowMs - WORDS_RESET_SLACK_MS || words > nowMs + windowMs + WORDS_RESET_SLACK_MS) return null;\n return { at: words, from: 'words' };\n}\n\n// ---------------------------------------------------------------------------\n// Seat reasons, in words (3.10.1)\n// ---------------------------------------------------------------------------\n\n/**\n * The budget route's reasons as data. `SeatHeadroom.reasons` carries each\n * one in the router's LOG form — \"… so autonomy stops at 92% (resets\n * 2026-09-26T03:46:56.000Z).\" (core/routing/seat-reasons.ts\n * `reasonSentence`) — so the reset clause is split back off with the Fleet\n * surface's own parser: an instant → `resetsAt`, Claude's words →\n * `resetDescription`. Non-strings and blanks are skipped (wire data).\n */\nexport function seatReasons(reasons: readonly unknown[] | null | undefined): SeatReason[] {\n if (!Array.isArray(reasons)) return [];\n return reasons.filter((r): r is string => typeof r === 'string' && r.trim().length > 0).map(parseLegacyReason);\n}\n\n/** Two resets this close are the same one (the router and the chart read the same window). */\nconst SAME_RESET_MS = 60_000;\n\n/**\n * Reasons whose reset is, by construction, the BINDING window's: headroom.ts\n * attaches `resetOf(bindingRow.window)` to exactly these. A `spent` reason\n * can name the other window (a spent 5-hour window beside a weekly one that\n * is even further past its reserve), so it is never taken for the chart's.\n */\nconst BINDING_RESET_KINDS: ReadonlySet<SeatReasonKind> = new Set<SeatReasonKind>(['reserve', 'session-ceiling']);\n\n/** Claude's words without the \"resets\" some paths lead with. */\nfunction bareResetWords(text: string | null | undefined): string {\n return typeof text === 'string' ? text.trim().replace(/^resets\\s+/i, '') : '';\n}\n\n/**\n * The binding window's reset words from the router's own reasons — the\n * fallback when the roster (read from cache, never fetched here) has none,\n * so a card never says \"reset time not reported\" beside a reason that names\n * the reset.\n */\nexport function reasonResetText(reasons: readonly SeatReason[]): string | null {\n for (const r of reasons) {\n if (!BINDING_RESET_KINDS.has(r.kind)) continue;\n const words = bareResetWords(r.resetDescription);\n if (words) return words;\n }\n return null;\n}\n\n/**\n * When `reason` lifts, in the viewer's words — or null when the card already\n * says it: the chart's reset marker (\"Resets Sep 26, 11:46 PM\", a machine\n * instant or Claude's words placed on their clock) or the header's verbatim\n * words. A reset the card does NOT show (another window's) is kept: a machine\n * instant in the viewer's zone (`describeResetAt`, as Fleet and Accounts\n * word it), Claude's words verbatim.\n */\nfunction unshownReset(reason: SeatReason, card: { resetAt: number | null; resetText: string | null }, nowMs: number): string | null {\n const at = typeof reason.resetsAt === 'string' ? Date.parse(reason.resetsAt) : Number.NaN;\n if (Number.isFinite(at)) {\n if (card.resetAt !== null && Math.abs(at - card.resetAt) <= SAME_RESET_MS) return null;\n return describeResetAt(reason.resetsAt, nowMs);\n }\n const words = bareResetWords(reason.resetDescription);\n if (!words) return null;\n if (card.resetText !== null && bareResetWords(card.resetText).toLowerCase() === words.toLowerCase()) return null;\n return words;\n}\n\n/**\n * What a card says about why the router holds a seat back: the reason's own\n * sentence (any instant inside it in local time — Fleet's `localTimes`),\n * closed by exactly one full stop, then \"Resets ….\" only when the card does\n * not already show that reset (`unshownReset`). Never the log form: held-back\n * cards printed \"… stops at 92% (resets 2026-09-26T03:46:56.000Z).\" and, for\n * Claude, \"… (resets Sep 25 at 7pm (America/New_York)).\" verbatim.\n */\nexport function seatReasonText(reason: SeatReason | undefined, card: { resetAt: number | null; resetText: string | null }, nowMs: number): string | null {\n if (!reason) return null;\n const text = asSentence(localTimes(reason.text, nowMs));\n if (!text) return null;\n const when = unshownReset(reason, card, nowMs);\n return when ? `${text} Resets ${when}.` : text;\n}\n\n/**\n * One burn-down per seat, paid seats first (they are the ones that run out).\n * `history` is keyed by `seriesKey` (seat + window); each card draws its\n * BINDING window's line. `seats` (the live roster, optional) supplies the\n * provider's reset wording for seats that publish no machine reset time (see\n * `bindingResetText`), which also places their reset when it names a clock\n * time (`bindingReset`); without a roster, a reason held at the binding\n * window's reserve or ceiling supplies the same words (`reasonResetText`).\n * `recorded` (from `mergeSeatHistory`) names the lines the server's recorded\n * history contributed to. Each card's `reason` is the router's first reason\n * in words (`seatReasonText`).\n */\nexport function seatBurns(\n view: BudgetView | null,\n history: Readonly<Record<string, SeatReading[]>>,\n seats?: readonly VerseSeat[] | null,\n recorded?: ReadonlySet<string> | null,\n): SeatBurn[] {\n if (!view) return [];\n const info = new Map(view.seatInfo.map((s) => [s.seatId, s]));\n const roster = new Map((seats ?? []).map((s) => [s.id, s]));\n const sampled = Date.parse(view.sampledAt);\n const nowMs = Number.isFinite(sampled) ? sampled : Date.now();\n return view.headroom\n .map((h): SeatBurn => {\n const i = info.get(h.seatId);\n const engine = i?.engine ?? 'local';\n const windowMs = h.bindingWindow ? WINDOW_MS[h.bindingWindow] : null;\n const reasons = seatReasons(h.reasons);\n const resetText =\n bindingResetText(roster.get(h.seatId), engine, h.bindingWindow, nowMs) ??\n (h.bindingWindow === null || engine === 'local' ? null : reasonResetText(reasons));\n // A reset is only placed on a chart that has a window to put before it.\n const reset = windowMs === null ? null : bindingReset(h.resetAt, resetText, windowMs, nowMs);\n const start = reset && windowMs !== null ? reset.at - windowMs : null;\n const key = h.bindingWindow ? seriesKey(h.seatId, h.bindingWindow) : null;\n const readings = key ? history[key] ?? [] : [];\n return {\n seatId: h.seatId,\n label: i?.label ?? h.seatId,\n engine,\n window: h.bindingWindow,\n points: readings.filter((r) => start === null || r.t >= start).map((r) => ({ t: r.t, remaining: Math.max(0, 100 - r.used) })),\n start,\n resetAt: reset?.at ?? null,\n resetFrom: reset?.from ?? null,\n resetText,\n reservePercent: view.effective[h.seatId]?.reservePercent ?? 0,\n line: bindingLine(h, view.effective[h.seatId]),\n enabled: view.effective[h.seatId]?.enabled ?? false,\n free: i?.free ?? false,\n eligible: h.eligibleForAutonomy,\n reason: seatReasonText(reasons[0], { resetAt: reset?.at ?? null, resetText }, nowMs),\n recorded: key !== null && (recorded?.has(key) ?? false),\n };\n })\n .sort((a, b) => Number(a.free) - Number(b.free) || a.label.localeCompare(b.label));\n}\n\n/** \"Claude 40% reserved for you\" — the first enabled Claude seat's reserve, from the live budget. */\nexport function claudeReserve(view: BudgetView | null): { label: string; percent: number } | null {\n if (!view) return null;\n const seat = view.seatInfo.find((s) => s.engine === 'claude');\n if (!seat) return null;\n const policy = view.effective[seat.seatId];\n return policy ? { label: 'Claude', percent: policy.reservePercent } : null;\n}\n","/**\n * routes/verse/cloud/CloudBudgetForm.tsx — the cloud budget's fields, shared\n * by Command's \"Edit budget\" popover and the Usage \"Cloud credits\" panel\n * (3.11 unit C3), so both edit the same numbers the same way.\n *\n * Save sends ONE update with only the fields that changed (cloud-model\n * budgetPatch). The form re-seeds from the server's budget whenever that\n * budget changes and nothing has been typed, so a save elsewhere (the CLI,\n * the other surface) shows up instead of being silently overwritten.\n */\nimport { useEffect, useRef, useState, type FormEvent } from 'react';\nimport type { CloudBudgetUpdate, CloudBudgetV1 } from '../../../../core/cloud/types.js';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { Input } from '../../../components/primitives/Input.js';\nimport { Switch } from '../../../components/primitives/Switch.js';\nimport { BUDGET_FIELDS, budgetFormFrom, budgetPatch, type BudgetErrors, type BudgetForm } from './cloud-model.js';\nimport styles from './cloud.module.css';\n\nexport interface CloudBudgetFormProps {\n budget: CloudBudgetV1;\n /** Runs the save (through the caller's token gate); the form keeps its values until the new budget lands. */\n onSave: (update: CloudBudgetUpdate) => void;\n onCancel?: () => void;\n busy: boolean;\n disabled?: boolean;\n /** The id prefix, so two forms on one page never share field ids. */\n idPrefix: string;\n}\n\nexport function CloudBudgetForm({ budget, onSave, onCancel, busy, disabled = false, idPrefix }: CloudBudgetFormProps) {\n const [form, setForm] = useState<BudgetForm>(() => budgetFormFrom(budget));\n const [errors, setErrors] = useState<BudgetErrors>({});\n const [note, setNote] = useState<string | null>(null);\n const dirty = useRef(false);\n\n // A newer budget from the server replaces the fields only while the\n // operator has not started typing — never under their fingers.\n useEffect(() => {\n if (!dirty.current) setForm(budgetFormFrom(budget));\n }, [budget]);\n\n const edit = (patch: Partial<BudgetForm>) => {\n dirty.current = true;\n setNote(null);\n setForm((prev) => ({ ...prev, ...patch }));\n };\n\n const submit = (event: FormEvent) => {\n event.preventDefault();\n const result = budgetPatch(form, budget);\n if (!result.ok) {\n setErrors(result.errors);\n return;\n }\n setErrors({});\n if (!result.changed) {\n setNote('Nothing changed.');\n return;\n }\n dirty.current = false;\n onSave(result.update);\n };\n\n return (\n <form className={styles.form} onSubmit={submit} noValidate aria-label=\"Cloud budget\">\n <div className={styles.fieldGrid}>\n {BUDGET_FIELDS.map((spec) => (\n <Input\n key={spec.field}\n id={`${idPrefix}-${spec.field}`}\n label={spec.label}\n hint={spec.hint}\n error={errors[spec.field]}\n size=\"sm\"\n inputMode={spec.integer ? 'numeric' : 'decimal'}\n prefix={spec.money ? '$' : undefined}\n value={form[spec.field]}\n disabled={disabled || busy}\n onChange={(e) => edit({ [spec.field]: e.target.value } as Partial<BudgetForm>)}\n />\n ))}\n </div>\n <Switch\n checked={form.selfImprove}\n onChange={(next) => edit({ selfImprove: next })}\n label=\"Verse may launch self-improvement tasks on its own\"\n disabled={disabled || busy}\n />\n {note ? <p className={styles.muted} role=\"status\">{note}</p> : null}\n <div className={styles.formActions}>\n {onCancel ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={onCancel} disabled={busy}>\n Cancel\n </Button>\n ) : null}\n <Button type=\"submit\" variant=\"primary\" size=\"sm\" busy={busy} disabled={disabled}>\n Save budget\n </Button>\n </div>\n </form>\n );\n}\n"],"mappings":"8XA2JA,SAAS,EAAgB,EAAwB,EAA8B,CAC7E,GAAI,CAAC,EAAO,SAAU,OAAO,KAC7B,IAAM,EAAK,KAAK,MAAM,EAAO,QAAQ,EACrC,OAAO,OAAO,SAAS,CAAE,EAAI,EAAK,EAAQ,IAC5C,CAGA,SAAgB,EAAe,EAAsB,EAAwB,EAAgC,CAC3G,IAAM,EAAK,EAAO,GAClB,GAAI,IAAW,SAAU,CACvB,GAAI,IAAO,YAAa,MAAO,UAC/B,GAAI,IAAO,YAAa,MAAO,SAC/B,GAAI,EAAG,WAAW,YAAY,EAAG,MAAO,OAC1C,CACA,GAAI,IAAW,OAAQ,MAAO,SAC9B,IAAM,EAAW,EAAgB,EAAQ,CAAK,EAC9C,GAAI,IAAW,QAAS,CACtB,GAAI,EAAG,SAAS,YAAY,EAAG,MAAO,SACtC,GAAI,EAAG,SAAS,UAAU,EAAG,OAAO,IAAa,MAAQ,EAAA,MAAmC,SAAW,SACzG,CAEA,OAAO,IAAa,MAAQ,GAAA,MAAoC,UAAY,QAC9E,CCxJA,IAAM,EAAM,MACN,EAAO,KAOb,SAAgB,EAAU,EAAkC,EAA6C,EAAa,EAAY,EAAkB,CAClJ,IAAM,EAAM,EAAK,OAAS,EACpB,EAAQ,EAAM,EACpB,GAAI,EAAQ,GAAK,GAAO,EAAG,OAAO,KAClC,IAAI,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAO,EAAI,EAAK,IAAK,CAChC,IAAM,EAAI,EAAK,EAAK,EAAG,EACvB,GAAI,IAAM,MAAQ,CAAC,OAAO,SAAS,CAAC,EAAG,OAAO,KAC9C,GAAS,CACX,CACA,OAAO,CACT,CAGA,SAAgB,EAAW,EAAkC,EAA6C,EAAgC,CACxI,OAAO,EAAK,MAAM,KAAK,IAAI,EAAG,EAAK,OAAS,CAAG,CAAC,CAAC,CAAC,IAAI,CAAI,CAC5D,CAEA,SAAS,EAAK,EAAmD,CAC/D,IAAM,EAAQ,EAAO,OAAQ,GAAmB,IAAM,MAAQ,OAAO,SAAS,CAAC,CAAC,EAChF,OAAO,EAAM,OAAS,EAAM,QAAQ,EAAG,IAAM,EAAI,EAAG,CAAC,EAAI,EAAM,OAAS,IAC1E,CAyCA,SAAgB,EAAY,EAAmC,CAC7D,IAAM,EAAK,EAA2D,eACtE,OAAO,OAAO,GAAM,UAAY,OAAO,SAAS,CAAC,GAAK,GAAK,EAAI,EAAI,IACrE,CAEA,IAAM,GAAa,GAAe,EAAI,EAAI,IAAI,IAAM,OAAO,CAAC,EACtD,EAAa,GAAc,GAAG,EAAI,EAAI,IAAM,KAAK,EAAE,QAAQ,CAAC,IAC5D,EAAa,GAAc,GAAG,EAAI,EAAI,IAAM,EAAI,EAAI,IAAM,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAG3F,SAAgB,EAAW,EAA2B,CACpD,GAAI,IAAO,MAAQ,CAAC,OAAO,SAAS,CAAE,GAAK,EAAK,EAAG,MAAO,IAC1D,IAAM,EAAI,KAAK,MAAM,EAAK,GAAM,EAChC,GAAI,EAAI,GAAI,MAAO,GAAG,EAAE,GACxB,IAAM,EAAI,KAAK,MAAM,EAAI,EAAE,EAC3B,GAAI,EAAI,GAAI,OAAO,EAAI,GAAK,GAAG,EAAE,IAAI,EAAI,GAAG,GAAK,GAAG,EAAE,GACtD,IAAM,EAAI,KAAK,MAAM,EAAI,EAAE,EAC3B,OAAO,EAAI,GAAK,GAAG,EAAE,IAAI,EAAI,GAAG,GAAK,GAAG,EAAE,EAC5C,CAGA,SAAgB,GAAW,EAAsD,CAC/E,GAAI,CAAC,GAAS,EAAM,MAAM,SAAW,EAAG,MAAO,CAAC,EAChD,IAAM,EAAM,KAAK,IAAI,GAAG,EAAM,MAAM,IAAK,GAAM,EAAE,WAAW,MAAM,CAAC,EACnE,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAI,GAAI,EAAG,IAAM,EAAK,EAAM,MAAM,IAAK,GAAM,EAAE,WAAW,GAAK,EAAM,EAAE,WAAW,UAAY,IAAI,CAAC,CAAC,CAClI,CAEA,SAAgB,EAAU,CAAE,QAAO,UAAS,WAAU,UAA4B,CAChF,IAAM,EAAO,GAAS,MAAQ,CAAC,EACzB,EAAU,GAAuB,EAAE,OAAO,SAC1C,EAAQ,EAGR,EAAU,GAAO,QAAQ,UAAY,EAAU,EAAM,EAAQ,CAAC,EAC9D,EAAc,EAAU,EAAM,EAAQ,EAAG,CAAC,EAC1C,EAAY,EAAU,EAAM,EAAQ,CAAC,EACrC,EAAc,IAAc,MAAQ,IAAgB,KAAO,EAAY,EAAc,KAGrF,EAAQ,GAAO,QAAQ,qBAAuB,KAC9C,EAAS,GAAW,CAAK,EACzB,EAAO,EAAO,QAAU,GAAK,EAAK,EAAO,MAAM,EAAE,CAAC,EAAI,KACtD,EAAS,EAAO,QAAU,GAAK,EAAK,EAAO,MAAM,IAAK,EAAE,CAAC,EAAI,KAC7D,EAAa,IAAS,MAAQ,IAAW,KAAO,EAAO,EAAS,KAGhE,EAAS,EAAU,EAAM,EAAO,CAAC,EACjC,EAAa,EAAU,EAAM,EAAO,EAAG,CAAC,EACxC,EAAY,EAAS,EAAO,MAAM,iBAAmB,KACrD,EAAa,IAAW,KAAO,IAAM,IAAI,EAAO,QAAQ,CAAC,IAAI,IAAc,KAA6C,GAAtC,QAAQ,EAAY,EAAA,CAAG,QAAQ,CAAC,MAClH,EAAe,EAAK,KAAM,GAAM,EAAY,CAAC,IAAM,IAAI,EAGvD,EAAW,IAAc,KAAO,sBAAwB,IAAc,EAAI,mBAAqB,QAAQ,EAAU,MACjH,EAAe,EAAe,EAAW,GAAG,EAAS,qBAGrD,EAAS,GAAU,QAAU,KAE7B,GADY,GAAQ,aAAe,GAAU,YAAY,KAAM,GAAM,EAAE,KAAO,EAAO,YAAY,GAAK,KAAO,KAAA,EAC3F,MAAQ,KAEhC,MAAO,CACL,CACE,GAAI,SACJ,MAAO,cACP,MAAO,IAAY,KAAO,IAAM,OAAO,CAAO,EAC9C,MAAO,IAAgB,KAAO,KAAO,CAAE,MAAO,EAAa,KAAM,KAAK,IAAI,CAAW,IAAM,EAAI,QAAU,SAAU,OAAQ,cAAe,iBAAkB,GAAM,OAAQ,EAAU,EACpL,MAAO,EAAK,OAAS,EAAW,EAAM,EAAQ,EAAE,EAAI,IAAA,GACpD,WAAY,+BACZ,QAAS,GAAO,QAAQ,aAAe,KAA8C,mCAAvC,GAAG,EAAM,QAAQ,YAAY,OAC7E,EACA,CACE,GAAI,QACJ,MAAO,mBACP,MAAO,IAAU,KAAO,IAAM,GAAG,KAAK,MAAM,CAAK,EAAE,GACnD,MAAO,IAAe,KAAO,KAAO,CAAE,MAAO,EAAY,KAAM,MAAO,OAAQ,cAAe,iBAAkB,GAAM,OAAQ,CAAU,EACvI,MAAO,EAAO,OAAS,EAAS,IAAA,GAChC,WAAY,mCACZ,QAAS,IAAU,KAAO,oCAAsC,iCAClE,EACA,CACE,GAAI,QACJ,MAAO,aACP,MAAO,EAAW,GAAO,QAAQ,kBAAoB,IAAI,EACzD,MAAO,KACP,MAAO,IAAA,GACP,WAAY,aACZ,QAAS,8BACX,EACA,CACE,GAAI,QACJ,MAAO,qBACP,MAAO,EACP,MAAO,IAAW,MAAQ,IAAe,KAAO,CAAE,MAAO,EAAS,EAAY,OAAQ,cAAe,iBAAkB,GAAO,OAAQ,CAAU,EAAI,KACpJ,MAAO,EAAe,EAAW,EAAM,EAAO,EAAE,EAAI,IAAA,GACpD,WAAY,wCACZ,QAAS,CACX,EACA,CACE,GAAI,OACJ,MAAO,OACP,MAAO,EAAO,GAAG,EAAU,EAAK,IAAI,EAAE,MAAQ,IAC9C,MAAO,KACP,MAAO,IAAA,GACP,WAAY,eACZ,QAAS,EAAS,WAAW,EAAO,GAAG,cAAc,EAAO,SAAS,EAAU,EAAK,KAAK,EAAE,MAAM,EAAU,EAAK,MAAM,EAAE,GAAK,KAAO,wCACtI,CACF,CACF,CAQA,SAAgB,EAAe,CAAE,QAAO,UAAS,YAAwE,CACvH,IAAM,EAAO,GAAS,MAAQ,CAAC,EACzB,EAAU,GAAuB,EAAE,OAAO,SAC1C,EAAU,GAAO,QAAQ,UAAY,EAAU,EAAM,EAAQ,CAAC,EAC9D,EAAc,EAAU,EAAM,EAAQ,EAAG,CAAC,EAC1C,EAAS,GAAU,QAAU,KAC7B,EAAO,GAAQ,aAAgB,GAAU,YAAY,KAAM,GAAM,EAAE,KAAO,EAAO,YAAY,CAAC,EAAE,MAAQ,KAAQ,KACtH,OACG,GAAW,KAAO,IAClB,GAAe,KAAO,IACtB,GAAO,QAAQ,qBAAuB,QAAU,OAChD,GAAO,QAAQ,kBAAoB,QAAU,OAC7C,EAAU,EAAM,EAAa,CAAC,GAAK,KAAO,GAC3C,IAAS,IAEb,CAoBA,IAAM,GAAS,EAAoB,IAAc,IAAQ,MAAQ,KAAK,MAAM,CAAG,EAAI,EAGnF,SAAgB,EAAe,CAAE,eAAc,QAAO,SAAQ,YAAsC,CAClG,GAAI,CAAC,EAAc,MAAO,CAAC,EAC3B,IAAM,EAAI,KAAK,MAAM,CAAY,EACjC,GAAI,CAAC,OAAO,SAAS,CAAC,EAAG,MAAO,CAAC,EACjC,IAAM,EAAmB,CAAC,EACpB,GAAS,GAAO,MAAQ,CAAC,EAAA,CAAG,OAAQ,GAAM,EAAM,EAAE,QAAS,CAAC,CAAC,EAC7D,EAAS,GAA+B,EAAM,OAAQ,GAAM,EAAE,UAAY,CAAC,CAAC,CAAC,OAC7E,EAAS,EAAM,QAAQ,EACvB,EAAW,EAAM,UAAU,EAC3B,EAAS,EAAM,QAAQ,EAAI,EAAM,SAAS,EAC5C,GAAQ,EAAI,KAAK,CAAE,GAAI,SAAU,KAAM,GAAG,EAAO,SAAU,KAAM,SAAU,CAAC,EAC5E,GAAU,EAAI,KAAK,CAAE,GAAI,WAAY,KAAM,GAAG,EAAS,WAAY,KAAM,QAAS,CAAC,EACnF,GAAQ,EAAI,KAAK,CAAE,GAAI,SAAU,KAAM,GAAG,EAAO,oBAAqB,KAAM,SAAU,CAAC,EAC3F,IAAM,GAAS,GAAQ,UAAY,CAAC,EAAA,CAAG,OAAQ,GAAM,KAAK,MAAM,EAAE,EAAE,EAAI,GAAK,EAAE,SAAW,IAAI,CAAC,CAAC,OAC5F,GAAO,EAAI,KAAK,CAAE,GAAI,QAAS,KAAM,GAAG,EAAM,WAAW,IAAU,EAAI,GAAK,MAAO,KAAM,MAAO,CAAC,EACrG,IAAM,GAAS,GAAU,UAAY,CAAC,EAAA,CAAG,OAAQ,GAAM,KAAK,MAAM,EAAE,KAAK,EAAI,CAAC,CAAC,CAAC,OAEhF,OADI,GAAO,EAAI,KAAK,CAAE,GAAI,QAAS,KAAM,GAAG,EAAM,cAAe,KAAM,SAAU,CAAC,EAC3E,CACT,CAMA,IAAM,EAAkD,CAAE,KAAM,EAAG,KAAM,EAAG,KAAM,CAAE,EAGpF,SAAgB,EAAa,EAAgD,CAC3E,MAAO,CAAC,GAAG,CAAK,CAAC,CAAC,MAAM,EAAG,IAAM,EAAc,EAAE,UAAY,EAAc,EAAE,WAAa,KAAK,MAAM,EAAE,KAAK,EAAI,KAAK,MAAM,EAAE,KAAK,CAAC,CACrI,CAQA,SAAgB,EACd,EACA,EACqB,CACrB,IAAM,EAAQ,IAAI,IACZ,GAAO,EAAa,IAAmB,CACvC,OAAO,GAAO,UAAY,OAAO,GAAU,UAAY,EAAM,KAAK,CAAC,CAAC,OAAS,GAAG,EAAM,IAAI,EAAI,EAAM,KAAK,CAAC,CAChH,EACA,IAAK,IAAM,KAAK,GAAM,UAAY,CAAC,EAAG,EAAI,EAAE,OAAQ,EAAE,KAAK,EAC3D,IAAK,IAAM,KAAK,GAAS,CAAC,EAAG,EAAI,EAAE,GAAI,EAAE,KAAK,EAC9C,OAAO,CACT,CAGA,SAAgB,EAAc,EAAkD,CAE9E,OADK,EACE,OAAO,QAAQ,EAAS,OAAO,CAAC,CAAC,QAAQ,EAAG,KAAW,IAAU,IAAI,CAAC,CAAC,KAAK,CAAC,KAAY,CAAM,EADhF,CAAC,CAEzB,CAoBA,SAAgB,EAAU,EAAgB,EAA4B,CACpE,MAAO,GAAG,EAAO,GAAG,GACtB,CAmBA,IAAM,EAAa,GAEnB,SAAS,EAAU,EAA8B,EAAuB,CACtE,IAAM,EAAI,EAAK,OACf,OAAO,GAAK,GAAK,EAAK,EAAI,EAAE,CAAE,OAAS,GAAQ,EAAK,EAAI,EAAE,CAAE,OAAS,CACvE,CAEA,SAAgB,EAAc,EAAkD,EAAwD,CACtI,GAAI,CAAC,EAAM,MAAO,CAAE,GAAG,CAAQ,EAC/B,IAAM,EAAI,KAAK,MAAM,EAAK,SAAS,EACnC,GAAI,CAAC,OAAO,SAAS,CAAC,EAAG,MAAO,CAAE,GAAG,CAAQ,EAC7C,IAAM,EAAsC,CAAE,GAAG,CAAQ,EACzD,IAAK,IAAM,KAAK,EAAK,SAAU,CAC7B,IAAM,EAAyC,CAAC,CAAC,UAAW,EAAE,kBAAkB,EAAG,CAAC,SAAU,EAAE,iBAAiB,CAAC,EAClH,IAAK,GAAM,CAAC,EAAQ,KAAS,EAAS,CACpC,GAAI,IAAS,MAAQ,CAAC,OAAO,SAAS,CAAI,EAAG,SAC7C,IAAM,EAAM,EAAU,EAAE,OAAQ,CAAM,EAChC,EAAO,EAAK,IAAQ,CAAC,EAC3B,GAAI,EAAK,QAAU,EAAK,EAAK,OAAS,EAAE,CAAE,GAAK,EAAG,SAElD,IAAM,EAAQ,EAAK,QAAU,EAAO,EAAa,EAAK,EAAK,OAAS,EAAE,CAAE,KAAO,CAAC,EAAI,EAGpF,EAAK,GAAO,CAAC,GAAI,EAAU,EAAO,CAAI,EAAI,EAAM,MAAM,EAAG,EAAE,EAAI,EAAQ,CAAE,IAAG,MAAK,CAAC,CAAC,CAAC,MAAM,IAAkB,CAC9G,CACF,CACA,OAAO,CACT,CAmCA,SAAgB,EAAiB,EAA+C,EAA2D,CACzI,IAAM,EAAc,OAAO,GAAQ,aAAgB,SAAW,KAAK,MAAM,EAAO,WAAW,EAAI,IACzF,EAAY,IAAI,IACtB,IAAK,IAAM,KAAU,MAAM,QAAQ,GAAQ,MAAM,EAAI,EAAQ,OAAS,CAAC,EAAG,CACxE,GAAI,CAAC,GAAU,OAAO,EAAO,QAAW,UAAa,EAAO,SAAW,WAAa,EAAO,SAAW,UAAa,CAAC,MAAM,QAAQ,EAAO,MAAM,EAAG,SAClJ,IAAM,EAAwB,CAAC,EAC/B,IAAK,IAAM,KAAK,EAAO,OAAQ,CAC7B,GAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,SACvB,GAAM,CAAC,EAAG,GAAQ,EACd,OAAO,GAAM,UAAY,OAAO,SAAS,CAAC,GAAK,OAAO,GAAS,UAAY,OAAO,SAAS,CAAI,GAAK,GAAQ,GAAK,GAAQ,KAAK,EAAO,KAAK,CAAE,IAAG,MAAK,CAAC,CAC3J,CACA,GAAI,EAAO,SAAW,EAAG,SACzB,IAAM,EAAM,EAAU,EAAO,OAAQ,EAAO,MAAM,EAClD,EAAU,IAAI,EAAK,CAAC,GAAI,EAAU,IAAI,CAAG,GAAK,CAAC,EAAI,GAAG,CAAM,CAAC,CAC/D,CACA,IAAM,EAA0C,CAAC,EAC3C,EAAW,IAAI,IACrB,IAAK,IAAM,KAAO,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAI,EAAG,GAAG,EAAU,KAAK,CAAC,CAAC,EAAG,CACtE,IAAM,EAAa,EAAU,IAAI,CAAG,GAAK,CAAC,EACtC,EAAW,EAAK,IAAQ,CAAC,EAC7B,GAAI,EAAW,OAAS,EAAG,CACzB,EAAS,IAAI,CAAG,EAChB,IAAM,EAAiB,KAAK,IAAI,GAAG,EAAW,IAAK,GAAM,EAAE,CAAC,CAAC,EACvD,EAAY,OAAO,SAAS,CAAW,EAAI,KAAK,IAAI,EAAa,CAAc,EAAI,EACzF,EAAW,EAAS,OAAQ,GAAM,EAAE,EAAI,CAAS,CACnD,CAGA,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAK,EAAY,EAAI,IAAI,EAAE,EAAG,CAAC,EAC1C,IAAK,IAAM,KAAK,EAAU,EAAI,IAAI,EAAE,EAAG,CAAC,EACxC,IAAM,EAAS,CAAC,GAAG,EAAI,OAAO,CAAC,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,EAAI,EAAE,CAAC,EACrD,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAAS,EAAO,EAAE,CAAE,KAAO,EAAa,EAAO,EAAI,EAAE,CAAE,OAAM,EAAQ,GACxG,IAAM,EAAsB,CAAC,EAC7B,IAAK,IAAM,KAAK,EAAO,MAAM,CAAK,EAC5B,EAAU,EAAM,EAAE,IAAI,GAAG,EAAK,IAAI,EACtC,EAAK,KAAK,CAAC,EAEb,EAAS,GAAO,EAAK,MAAM,IAAiB,CAC9C,CACA,MAAO,CAAE,WAAU,UAAS,CAC9B,CAmDA,IAAa,EAAY,CAAE,QAAS,EAAI,EAAM,OAAQ,EAAI,CAAI,EAe9D,SAAgB,EACd,EACA,EACyC,CACzC,GAAI,CAAC,GAAU,EAAE,gBAAkB,KAAM,OAAO,KAChD,IAAM,EAAiB,IAAM,EAAO,eAC9B,EAAiB,EAAO,yBAA2B,IACrD,EACA,EACA,EAAE,gBAAkB,UAGb,EAAE,oBAAsB,MAAQ,EAAiB,GAF1D,EAAU,EACV,EAAQ,qBAKR,EAAU,EACV,EAAQ,kBAEV,IAAM,EAAQ,IAAM,EACpB,OAAO,EAAQ,EAAI,CAAE,QAAO,OAAM,EAAI,IACxC,CAUA,SAAgB,EAAe,EAA6D,CAC1F,IAAM,EACJ,IAAW,UACP,CAAE,QAAS,QAAS,KAAM,UAAW,OAAQ,SAAU,EACvD,CAAE,MAAO,QAAS,IAAK,UAAW,KAAM,UAAW,OAAQ,SAAU,EAC3E,MAAQ,IAAO,CACb,IAAM,EAAI,IAAI,KAAK,CAAE,EACrB,OAAO,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAI,GAAK,EAAE,eAAe,QAAS,CAAI,CACxE,CACF,CAWA,SAAgB,EACd,EACA,EACA,EACA,EACe,CACf,GAAI,CAAC,GAAM,UAAY,IAAY,MAAQ,IAAW,QAAS,OAAO,KACtE,IAAK,IAAM,KAAK,EAAK,SAAS,QAAS,CACrC,IAAM,EAAO,OAAO,EAAE,kBAAqB,SAAW,EAAE,iBAAiB,KAAK,EAAI,GAC9E,KAAK,SAAW,GAChB,EAAe,EAAQ,CAAE,GAAI,EAAE,GAAI,YAAa,EAAE,YAAa,SAAU,EAAE,SAAU,iBAAkB,EAAE,iBAAkB,aAAc,EAAE,YAAa,EAAG,CAAK,IAAM,EAAS,OAAO,CAC5L,CACA,OAAO,IACT,CAOA,SAAgB,EAAW,EAAsB,CAC/C,MAAO,UAAU,EAAK,QAAQ,cAAe,EAAE,GACjD,CAEA,IAAM,EAA4B,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,KAAK,EAO/G,EAAiB,mJAIvB,SAAS,EAAU,EAAY,EAA4C,CACzE,IAAM,EAAQ,EAAI,cAAc,IAAI,KAAK,CAAE,CAAC,EACtC,EAAK,GAAuC,OAAO,EAAM,KAAM,GAAM,EAAE,OAAS,CAAI,CAAC,EAAE,KAAK,EAC5F,EAAI,CAAE,EAAG,EAAE,MAAM,EAAG,GAAI,EAAE,OAAO,EAAI,EAAG,EAAG,EAAE,KAAK,EAAG,EAAG,EAAE,MAAM,EAAI,GAAI,GAAI,EAAE,QAAQ,CAAE,EAC9F,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,QAAQ,EAAI,EAAI,IACvD,CAEA,IAAM,EAAa,GAAiB,KAAK,IAAI,EAAE,EAAG,EAAE,GAAI,EAAE,EAAG,EAAE,EAAG,EAAE,EAAE,EAOtE,SAAS,EAAU,EAAiB,EAAyC,CAC3E,IAAM,EAAS,EAAU,CAAI,EACzB,EAAK,EACT,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAO,EAAU,EAAI,CAAG,EAC9B,GAAI,CAAC,EAAM,OAAO,KAClB,IAAM,EAAQ,EAAS,EAAU,CAAI,EACrC,GAAI,IAAU,EAAG,OAAO,EACxB,GAAM,CACR,CACA,OAAO,IACT,CAiBA,SAAgB,EAAsB,EAAc,EAA8B,CAChF,IAAM,EAAI,EAAe,KAAK,EAAK,KAAK,CAAC,EACzC,GAAI,CAAC,GAAK,CAAC,OAAO,SAAS,CAAK,EAAG,OAAO,KAC1C,GAAM,EAAG,EAAO,EAAK,EAAM,EAAQ,EAAU,GAAQ,EACjD,EACJ,GAAI,CACF,EAAM,IAAI,KAAK,eAAe,QAAS,CAAE,SAAU,EAAM,UAAW,MAAO,KAAM,UAAW,MAAO,UAAW,IAAK,UAAW,KAAM,UAAW,OAAQ,SAAU,CAAC,CACpK,MAAQ,CACN,OAAO,IACT,CACA,IAAM,EAAQ,EAAU,EAAO,CAAG,EAClC,GAAI,CAAC,EAAO,OAAO,KACnB,IAAM,EAAK,OAAO,CAAI,EAAI,IAAO,EAAU,YAAY,IAAM,KAAO,GAAK,GACnE,EAAK,IAAW,IAAA,GAAY,EAAI,OAAO,CAAM,EAC7C,EAA0B,CAAC,EACjC,GAAI,IAAU,IAAA,GAAW,CACvB,IAAM,EAAK,EAAO,QAAQ,EAAM,YAAY,CAAC,EACvC,EAAI,OAAO,CAAG,EACpB,GAAI,EAAK,EAAG,OAAO,KACnB,IAAK,IAAM,IAAK,CAAC,EAAM,EAAI,EAAG,EAAM,EAAG,EAAM,EAAI,CAAC,EAE5C,IAAI,KAAK,KAAK,IAAI,EAAG,EAAI,CAAC,CAAC,CAAC,CAAC,WAAW,IAAM,GAAG,EAAW,KAAK,CAAE,IAAG,KAAI,IAAG,IAAG,IAAG,CAAC,CAE5F,MACE,IAAK,IAAM,IAAK,CAAC,GAAI,EAAG,CAAC,EAAG,CAC1B,IAAM,EAAO,IAAI,KAAK,KAAK,IAAI,EAAM,EAAG,EAAM,GAAI,EAAM,EAAI,CAAC,CAAC,EAC9D,EAAW,KAAK,CAAE,EAAG,EAAK,eAAe,EAAG,GAAI,EAAK,YAAY,EAAG,EAAG,EAAK,WAAW,EAAG,IAAG,IAAG,CAAC,CACnG,CAEF,IAAI,EAAsB,KAC1B,IAAK,IAAM,KAAK,EAAY,CAC1B,IAAM,EAAK,EAAU,EAAG,CAAG,EACvB,IAAO,OAAS,IAAS,MAAQ,KAAK,IAAI,EAAK,CAAK,EAAI,KAAK,IAAI,EAAO,CAAK,KAAI,EAAO,EAC9F,CACA,OAAO,CACT,CAOA,IAAM,EAAuB,IAUvB,EAA2B,KAAK,IAAI,IAAM,EAAG,CAAC,EAEpD,SAAgB,GACd,EACA,EACA,EACA,EACmD,CACnD,IAAM,EAAK,EAAU,KAAK,MAAM,CAAO,EAAI,IAG3C,GAAI,OAAO,SAAS,CAAE,GAAK,GAAM,EAA0B,MAAO,CAAE,KAAI,KAAM,UAAW,EACzF,GAAI,IAAc,MAAQ,IAAa,KAAM,OAAO,KACpD,IAAM,EAAQ,EAAsB,EAAW,CAAK,EAEpD,OADI,IAAU,MAAQ,EAAQ,EAAQ,GAAwB,EAAQ,EAAQ,EAAW,EAA6B,KAC/G,CAAE,GAAI,EAAO,KAAM,OAAQ,CACpC,CAcA,SAAgB,GAAY,EAA8D,CAExF,OADK,MAAM,QAAQ,CAAO,EACnB,EAAQ,OAAQ,GAAmB,OAAO,GAAM,UAAY,EAAE,KAAK,CAAC,CAAC,OAAS,CAAC,CAAC,CAAC,IAAI,CAAiB,EADzE,CAAC,CAEvC,CAGA,IAAM,GAAgB,IAQhB,GAAmD,IAAI,IAAoB,CAAC,UAAW,iBAAiB,CAAC,EAG/G,SAAS,EAAe,EAAyC,CAC/D,OAAO,OAAO,GAAS,SAAW,EAAK,KAAK,CAAC,CAAC,QAAQ,cAAe,EAAE,EAAI,EAC7E,CAQA,SAAgB,GAAgB,EAA+C,CAC7E,IAAK,IAAM,KAAK,EAAS,CACvB,GAAI,CAAC,GAAoB,IAAI,EAAE,IAAI,EAAG,SACtC,IAAM,EAAQ,EAAe,EAAE,gBAAgB,EAC/C,GAAI,EAAO,OAAO,CACpB,CACA,OAAO,IACT,CAUA,SAAS,GAAa,EAAoB,EAA4D,EAA8B,CAClI,IAAM,EAAK,OAAO,EAAO,UAAa,SAAW,KAAK,MAAM,EAAO,QAAQ,EAAI,IAC/E,GAAI,OAAO,SAAS,CAAE,EAEpB,OADI,EAAK,UAAY,MAAQ,KAAK,IAAI,EAAK,EAAK,OAAO,GAAK,GAAsB,KAC3E,EAAgB,EAAO,SAAU,CAAK,EAE/C,IAAM,EAAQ,EAAe,EAAO,gBAAgB,EAGpD,MAFI,CAAC,GACD,EAAK,YAAc,MAAQ,EAAe,EAAK,SAAS,CAAC,CAAC,YAAY,IAAM,EAAM,YAAY,EAAU,KACrG,CACT,CAUA,SAAgB,GAAe,EAAgC,EAA4D,EAA8B,CACvJ,GAAI,CAAC,EAAQ,OAAO,KACpB,IAAM,EAAO,EAAW,EAAW,EAAO,KAAM,CAAK,CAAC,EACtD,GAAI,CAAC,EAAM,OAAO,KAClB,IAAM,EAAO,GAAa,EAAQ,EAAM,CAAK,EAC7C,OAAO,EAAO,GAAG,EAAK,UAAU,EAAK,GAAK,CAC5C,CAcA,SAAgB,GACd,EACA,EACA,EACA,EACY,CACZ,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,IAAM,EAAO,IAAI,IAAI,EAAK,SAAS,IAAK,GAAM,CAAC,EAAE,OAAQ,CAAC,CAAC,CAAC,EACtD,EAAS,IAAI,KAAK,GAAS,CAAC,EAAA,CAAG,IAAK,GAAM,CAAC,EAAE,GAAI,CAAC,CAAC,CAAC,EACpD,EAAU,KAAK,MAAM,EAAK,SAAS,EACnC,EAAQ,OAAO,SAAS,CAAO,EAAI,EAAU,KAAK,IAAI,EAC5D,OAAO,EAAK,SACT,IAAK,GAAgB,CACpB,IAAM,EAAI,EAAK,IAAI,EAAE,MAAM,EACrB,EAAS,GAAG,QAAU,QACtB,EAAW,EAAE,cAAgB,EAAU,EAAE,eAAiB,KAC1D,EAAU,GAAY,EAAE,OAAO,EAC/B,EACJ,EAAiB,EAAO,IAAI,EAAE,MAAM,EAAG,EAAQ,EAAE,cAAe,CAAK,IACpE,EAAE,gBAAkB,MAAQ,IAAW,QAAU,KAAO,GAAgB,CAAO,GAE5E,EAAQ,IAAa,KAAO,KAAO,GAAa,EAAE,QAAS,EAAW,EAAU,CAAK,EACrF,EAAQ,GAAS,IAAa,KAAO,EAAM,GAAK,EAAW,KAC3D,EAAM,EAAE,cAAgB,EAAU,EAAE,OAAQ,EAAE,aAAa,EAAI,KAC/D,EAAW,EAAM,EAAQ,IAAQ,CAAC,EAAI,CAAC,EAC7C,MAAO,CACL,OAAQ,EAAE,OACV,MAAO,GAAG,OAAS,EAAE,OACrB,SACA,OAAQ,EAAE,cACV,OAAQ,EAAS,OAAQ,GAAM,IAAU,MAAQ,EAAE,GAAK,CAAK,CAAC,CAAC,IAAK,IAAO,CAAE,EAAG,EAAE,EAAG,UAAW,KAAK,IAAI,EAAG,IAAM,EAAE,IAAI,CAAE,EAAE,EAC5H,QACA,QAAS,GAAO,IAAM,KACtB,UAAW,GAAO,MAAQ,KAC1B,YACA,eAAgB,EAAK,UAAU,EAAE,OAAO,EAAE,gBAAkB,EAC5D,KAAM,EAAY,EAAG,EAAK,UAAU,EAAE,OAAO,EAC7C,QAAS,EAAK,UAAU,EAAE,OAAO,EAAE,SAAW,GAC9C,KAAM,GAAG,MAAQ,GACjB,SAAU,EAAE,oBACZ,OAAQ,GAAe,EAAQ,GAAI,CAAE,QAAS,GAAO,IAAM,KAAM,WAAU,EAAG,CAAK,EACnF,SAAU,IAAQ,OAAS,GAAU,IAAI,CAAG,GAAK,GACnD,CACF,CAAC,CAAC,CACD,MAAM,EAAG,IAAM,OAAO,EAAE,IAAI,EAAI,OAAO,EAAE,IAAI,GAAK,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC,CACrF,CAGA,SAAgB,GAAc,EAAoE,CAChG,GAAI,CAAC,EAAM,OAAO,KAClB,IAAM,EAAO,EAAK,SAAS,KAAM,GAAM,EAAE,SAAW,QAAQ,EAC5D,GAAI,CAAC,EAAM,OAAO,KAClB,IAAM,EAAS,EAAK,UAAU,EAAK,QACnC,OAAO,EAAS,CAAE,MAAO,SAAU,QAAS,EAAO,cAAe,EAAI,IACxE,sBCj0BA,SAAgB,GAAgB,CAAE,SAAQ,SAAQ,WAAU,OAAM,WAAW,GAAO,YAAkC,CACpH,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,KAA2B,EAAe,CAAM,CAAC,EACnE,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAAuB,CAAC,CAAC,EAC/C,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAwB,IAAI,EAC9C,GAAA,EAAQ,EAAA,OAAA,CAAO,EAAK,GAI1B,EAAA,EAAA,UAAA,KAAgB,CACT,EAAM,SAAS,EAAQ,EAAe,CAAM,CAAC,CACpD,EAAG,CAAC,CAAM,CAAC,EAEX,IAAM,EAAQ,GAA+B,CAC3C,EAAM,QAAU,GAChB,EAAQ,IAAI,EACZ,EAAS,IAAU,CAAE,GAAG,EAAM,GAAG,CAAM,EAAE,CAC3C,EAkBA,OACE,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAO,KAAM,SAjBhB,GAAqB,CACnC,EAAM,eAAe,EACrB,IAAM,EAAS,EAAY,EAAM,CAAM,EACvC,GAAI,CAAC,EAAO,GAAI,CACd,EAAU,EAAO,MAAM,EACvB,MACF,CAEA,GADA,EAAU,CAAC,CAAC,EACR,CAAC,EAAO,QAAS,CACnB,EAAQ,kBAAkB,EAC1B,MACF,CACA,EAAM,QAAU,GAChB,EAAO,EAAO,MAAM,CACtB,EAGkD,WAAA,GAAW,aAAW,eAAtE,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAO,UACpB,SAAA,EAAc,IAAK,IAClB,EAAA,EAAA,IAAA,CAAC,EAAD,CAEE,GAAI,GAAG,EAAS,GAAG,EAAK,QACxB,MAAO,EAAK,MACZ,KAAM,EAAK,KACX,MAAO,EAAO,EAAK,OACnB,KAAK,KACL,UAAW,EAAK,QAAU,UAAY,UACtC,OAAQ,EAAK,MAAQ,IAAM,IAAA,GAC3B,MAAO,EAAK,EAAK,OACjB,SAAU,GAAY,EACtB,SAAW,GAAM,EAAK,EAAG,EAAK,OAAQ,EAAE,OAAO,KAAM,CAAwB,CAC9E,EAXM,EAAK,KAWX,CACF,CACE,CAAA,GACL,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,QAAS,EAAK,YACd,SAAW,GAAS,EAAK,CAAE,YAAa,CAAK,CAAC,EAC9C,MAAM,qDACN,SAAU,GAAY,CACvB,CAAA,EACA,GAAO,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAO,MAAO,KAAK,SAAU,SAAA,CAAQ,CAAA,EAAI,MAC/D,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAO,YAAvB,SAAA,CACG,GACC,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,QAAQ,QAAQ,KAAK,KAAK,QAAS,EAAU,SAAU,EAAM,SAAA,QAE7D,CAAA,EACN,MACJ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,KAAK,SAAS,QAAQ,UAAU,KAAK,KAAW,OAAgB,WAAU,SAAA,aAE1E,CAAA,CACL,GACD,GAEV"}
@@ -0,0 +1,2 @@
1
+ import{a as e,c as t,i as n,n as r}from"./index-QesfXBsb.js";import{p as i}from"./global-CNNGMril.js";import{t as a}from"./focus-trap-BdrbWfC4.js";import{t as o}from"./ProviderLogo-CT2cSfkg.js";import{Ht as s,I as c,N as l,S as u,T as d,X as f,_n as p,at as m,hn as h,rt as g,tt as _,z as v}from"./VerseConsoleApp-BCY5uSok.js";import{o as y}from"./verse-icons-DndgEgKS.js";import{a as b,n as x,r as S,s as C}from"./command-catalog-CF0cQQac.js";var w=t(e(),1),T=t(n(),1),E={...Object.fromEntries(x.map(e=>[e.id,e.label])),recent:`Recent`},D=[`needs-you`,`recent`,...x.map(e=>e.id).filter(e=>e!==`needs-you`)],O={"needs-you":5,recent:5,chats:8,actions:8,"go-to":6,"seats-apps":5,projects:5};function k(e,t){if(t===0)return!0;let n=e[t-1];return/[\s\-_/.:·›,(]/.test(n)||/[a-z]/.test(n)&&/[A-Z]/.test(e[t])}function A(e,t){let n=e.toLowerCase();if(!n)return 0;let r=t.toLowerCase(),i=r.indexOf(n);if(i>=0){let e=1e3-i*4;return k(t,i)&&(e+=400),i===0&&(e+=200),e}let a=0,o=0,s=-2;for(let e of n){let n=r.indexOf(e,o);if(n<0)return null;a+=n===s+1?30:5,k(t,n)&&(a+=20),a-=Math.min(20,n-o),s=n,o=n+1}return a}function j(e,t){let n=t.trim().split(/\s+/).filter(Boolean);if(n.length===0)return 0;let r=0;for(let t of n){let n=A(t,e.title);for(let r of[e.subtitle??``,...e.keywords]){if(!r)continue;let e=A(t,r);e!==null&&(n=Math.max(n??-1/0,e*.6))}if(n===null)return null;r+=n}return r}function M(e,t){let n=Math.max(0,Math.round((t-Date.parse(e))/1e3));if(!Number.isFinite(n))return`running`;if(n<60)return`running ${n}s`;let r=Math.floor(n/60);return r<60?`running ${r}m ${String(n%60).padStart(2,`0`)}s`:`running ${Math.floor(r/60)}h ${r%60}m`}function N(e,t){let n=Date.parse(e);if(!Number.isFinite(n))return``;let r=Math.round((t-n)/6e4);if(r<1)return`just now`;if(r<60)return`${r}m ago`;let i=Math.round(r/60);return i<36?`${i}h ago`:`${Math.round(i/24)}d ago`}function P(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??e}function F(e,t,n){let r=e.keys[0];return{key:`${n}:cmd:${e.id}`,kind:`command`,group:n,title:e.title,subtitle:e.scope===`chat`||e.scope===`composer`?`in Chat`:e.surface===`command`?`on Command`:null,shortcut:r?p(r,t):null,monogram:null,engine:null,keywords:[...e.keywords??[],e.section.toLowerCase()],argument:e.argument?{...e.argument}:null,tone:e.guard?.confirm.destructive?`danger`:`neutral`,payload:{kind:`command`,commandId:e.id}}}function I(e){let t=e.now??Date.now(),n=[];for(let t of e.needsYou)n.push({key:`needs-you:${t.id}`,kind:`needs-you`,group:`needs-you`,title:t.title,subtitle:t.subject.repo??t.subject.seatId??null,shortcut:null,monogram:t.subject.engine?s[t.subject.engine]:null,engine:t.subject.engine,keywords:[t.kind.replace(/-/g,` `),t.source,`needs you`],argument:null,tone:t.severity===`high`?`danger`:t.severity===`warn`?`warn`:`neutral`,payload:{kind:`needs-you`,itemId:t.id}});let r=new Map(e.running.map(e=>[e.sessionId,e])),i=[...e.sessions].sort((e,t)=>{let n=r.has(e.id)||e.status===`running`?0:1,i=r.has(t.id)||t.status===`running`?0:1;return n===i?e.updatedAt<t.updatedAt?1:e.updatedAt>t.updatedAt?-1:0:n-i});for(let e of i){let i=r.get(e.id),a=!!i||e.status===`running`,o=a?i?M(i.startedAt,t):`running`:e.status===`error`?`failed`:N(e.updatedAt,t);n.push({key:`chat:${e.id}`,kind:`chat`,group:`chats`,title:e.title||`Untitled chat`,subtitle:[P(e.projectPath),o].filter(Boolean).join(` · `),shortcut:null,monogram:s[e.engine],engine:e.engine,keywords:[e.model,e.seatId,P(e.projectPath)],argument:null,tone:a?`running`:e.status===`error`?`warn`:`neutral`,payload:{kind:`chat`,sessionId:e.id}})}for(let t of C(`actions`))n.push(F(t,e.platform,`actions`));for(let t of C(`go-to`))n.push(F(t,e.platform,`go-to`));for(let t of e.seats)n.push({key:`seat:${t.id}`,kind:`seat`,group:`seats-apps`,title:`New chat on ${t.label}`,subtitle:t.id,shortcut:null,monogram:s[t.engine],engine:t.engine,keywords:[t.engine,t.label,`seat`,`account`],argument:null,tone:`neutral`,payload:{kind:`seat`,seatId:t.id,label:t.label}});for(let t of e.projects)n.push({key:`project:${t.path}`,kind:`project`,group:`projects`,title:`New chat in ${t.name||P(t.path)}`,subtitle:t.enrolled?`enrolled in the fleet`:null,shortcut:null,monogram:null,engine:null,keywords:[t.name,P(t.path)],argument:null,tone:`neutral`,payload:{kind:`project`,path:t.path,name:t.name||P(t.path)}});return n}function L(e,t){let n=(t,n,r,i,a)=>({key:`arg:${e}:${t}`,kind:`argument`,group:`actions`,title:n,subtitle:r,shortcut:null,monogram:i,engine:a,keywords:[t],argument:null,tone:`neutral`,payload:{kind:`argument`,argument:{kind:e,id:t,label:n}}});switch(e){case`seat`:return t.seats.map(e=>n(e.id,e.label,e.id,s[e.engine],e.engine));case`project`:return t.projects.map(e=>n(e.path,e.name||P(e.path),null,null,null));case`session`:return t.sessions.map(e=>n(e.id,e.title||`Untitled chat`,P(e.projectPath),s[e.engine],e.engine));case`section`:return[];default:return[]}}function R(e,t,n,r){let i=t,a=null,o=i.trimStart().charAt(0);(o===`>`||o===`#`)&&(a=o,i=i.trimStart().slice(1));let s=a?S[a]:null,c=i.trim(),l=new Map,u=(e,t,n)=>{let r=l.get(e.group)??[];r.push({item:e,score:t,order:n}),l.set(e.group,r)};if(c===``&&!a){e.filter(e=>e.group===`needs-you`).slice(0,3).forEach((e,t)=>u(e,0,t));let t=n.map(e=>b(e)).filter(e=>e!==null&&e.group!==null);(t.length>0?t:[`chat.new`,`needs-you.open`,`shortcuts.open`].map(e=>b(e))).slice(0,5).forEach((e,t)=>u(F(e,r,`recent`),0,t))}else e.forEach((e,t)=>{if(s&&e.group!==s)return;let n=c===``?0:j(e,c);n!==null&&u(e,n,t)});let d=[],f=[];for(let e of D){let t=l.get(e);if(!t||t.length===0)continue;let n=t=>e===`chats`&&t.item.tone===`running`?0:1;t.sort((e,t)=>n(e)-n(t)||t.score-e.score||e.order-t.order);let r=a?50:O[e],i=t.slice(0,r);for(let e of i)f.push(e.score);d.push({id:e,label:E[e],items:i.map(e=>e.item)})}let p=0;return c!==``&&f.forEach((e,t)=>{e>f[p]&&(p=t)}),{groups:d,flat:d.flatMap(e=>e.items),prefix:a,best:p}}var z={backdrop:`_backdrop_qk7b5_4`,fade:`_fade_qk7b5_1`,panel:`_panel_qk7b5_16`,rise:`_rise_qk7b5_1`,field:`_field_qk7b5_29`,crumb:`_crumb_qk7b5_37`,input:`_input_qk7b5_49`,list:`_list_qk7b5_66`,group:`_group_qk7b5_74`,groupLabel:`_groupLabel_qk7b5_76`,item:`_item_qk7b5_86`,lead:`_lead_qk7b5_100`,monogram:`_monogram_qk7b5_109`,bullet:`_bullet_qk7b5_128`,text:`_text_qk7b5_136`,title:`_title_qk7b5_143`,subtitle:`_subtitle_qk7b5_153`,trail:`_trail_qk7b5_168`,key:`_key_qk7b5_175`,empty:`_empty_qk7b5_190`,foot:`_foot_qk7b5_197`},B=r();function V({onClose:e=f}){let t=l(),n=c(),r=i(d),s=i(u),p=(0,w.useMemo)(()=>h(),[]),[x,S]=(0,w.useState)(``),[C,E]=(0,w.useState)(0),[D,O]=(0,w.useState)(null),k=(0,w.useRef)(null),A=(0,w.useRef)(null),j=(0,w.useRef)(null),M=(0,w.useId)(),N=(0,w.useId)(),P=(0,w.useRef)(null);P.current=D;let F=(0,w.useCallback)(()=>{P.current?O(null):e()},[e]);a({open:!0,containerRef:k,onClose:F,initialFocusRef:A});let V=(0,w.useMemo)(()=>({needsYou:n.data?.needsYou??[],running:n.data?.running??[],sessions:r.data??s.data?.sessions??[],seats:s.data?.seats??[],projects:s.data?.projects??[],recentActions:t.recentActions,platform:p}),[n.data,r.data,s.data,t.recentActions,p]),H=(0,w.useMemo)(()=>D?.argument?{...R(L(D.argument.kind,V),`>${x}`,[],p),prefix:null}:R(I(V),x,t.recentActions,p),[D,V,x,t.recentActions,p]),U=(0,w.useRef)(!1);(0,w.useEffect)(()=>{if(U.current){U.current=!1,E(H.best);return}E(e=>H.flat.length===0?0:Math.min(e,H.flat.length-1))},[H]),(0,w.useEffect)(()=>{(j.current?.querySelector(`[data-index="${C}"]`))?.scrollIntoView?.({block:`nearest`})},[C]);function W(e){O(e),S(``),E(0)}function G(t){if(!t)return;let n=t.payload;if(n.kind===`command`){let t=b(n.commandId);if(t?.argument){W(t);return}e(),v(n.commandId,{via:`palette`});return}switch(e(),n.kind){case`needs-you`:_({split:`all`,focusId:n.itemId});break;case`chat`:g(n.sessionId);break;case`seat`:v(`chat.new-on`,{via:`palette`,argument:{kind:`seat`,id:n.seatId,label:n.label}});break;case`project`:m(`new-chat`,{projectPath:n.path});break;case`argument`:D&&v(D.id,{via:`palette`,argument:n.argument})}}function K(t){let n=H.flat.length,r=t.key;if((t.metaKey||t.ctrlKey)&&r.toLowerCase()===`k`){t.preventDefault(),e();return}switch(r){case`ArrowDown`:t.preventDefault(),n>0&&E(e=>(e+1)%n);return;case`ArrowUp`:t.preventDefault(),n>0&&E(e=>(e-1+n)%n);return;case`Home`:n>0&&(t.preventDefault(),E(0));return;case`End`:n>0&&(t.preventDefault(),E(n-1));return;case`Enter`:t.preventDefault(),G(H.flat[C]);return;case`Tab`:{t.preventDefault();let e=H.flat[C],n=e?.payload.kind===`command`?b(e.payload.commandId):null;n?.argument&&W(n);return}case`Backspace`:D&&x===``&&(t.preventDefault(),O(null));return;case`Escape`:t.preventDefault(),t.stopPropagation(),D?O(null):e();return;default:return}}let q=H.flat[C],J=D?.argument?`${D.argument.prompt}…`:`Search chats, actions and places`,Y=-1;return(0,T.createPortal)((0,B.jsx)(`div`,{className:z.backdrop,onMouseDown:t=>t.target===t.currentTarget&&e(),children:(0,B.jsxs)(`div`,{ref:k,className:z.panel,role:`dialog`,"aria-modal":`true`,"aria-labelledby":N,"data-verse-overlay":`palette`,children:[(0,B.jsx)(`h2`,{id:N,className:`visually-hidden`,children:`Command palette`}),(0,B.jsxs)(`div`,{className:z.field,children:[D?(0,B.jsxs)(`span`,{className:z.crumb,children:[D.title.replace(/…$/,``),(0,B.jsx)(`span`,{"aria-hidden":`true`,children:` ›`})]}):null,(0,B.jsx)(`input`,{ref:A,className:z.input,value:x,placeholder:J,"aria-label":D?.argument?D.argument.prompt:`Search commands`,role:`combobox`,"aria-expanded":`true`,"aria-controls":M,"aria-autocomplete":`list`,"aria-activedescendant":q?`${M}-${C}`:void 0,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,onChange:e=>{S(e.target.value),U.current=!0},onKeyDown:K})]}),(0,B.jsx)(`div`,{ref:j,id:M,className:z.list,role:`listbox`,"aria-label":`Results`,children:H.flat.length===0?(0,B.jsx)(`p`,{className:z.empty,children:D?`No ${D.argument?.prompt.toLowerCase()??`match`} matches “${x}”.`:`Nothing matches “${x.trim()}”.`}):H.groups.map(e=>(0,B.jsxs)(`div`,{role:`group`,"aria-label":D?D.argument?.prompt??e.label:e.label,className:z.group,children:[(0,B.jsx)(`div`,{className:z.groupLabel,"aria-hidden":`true`,children:D?D.argument?.prompt:e.label}),e.items.map(e=>{Y+=1;let t=Y;return(0,B.jsxs)(`div`,{id:`${M}-${t}`,"data-index":t,role:`option`,"aria-selected":t===C,className:z.item,"data-tone":e.tone,onMouseMove:()=>t!==C&&E(t),onMouseDown:e=>e.preventDefault(),onClick:()=>G(e),children:[(0,B.jsx)(`span`,{className:z.lead,"aria-hidden":`true`,children:e.monogram?(0,B.jsx)(`span`,{className:z.monogram,"data-engine":e.engine??void 0,children:e.engine?(0,B.jsx)(o,{engine:e.engine,size:13}):e.monogram}):(0,B.jsx)(`span`,{className:z.bullet,"data-kind":e.kind})}),(0,B.jsxs)(`span`,{className:z.text,children:[(0,B.jsx)(`span`,{className:z.title,children:e.title}),e.subtitle?(0,B.jsx)(`span`,{className:z.subtitle,children:e.subtitle}):null]}),(0,B.jsxs)(`span`,{className:z.trail,children:[e.argument?(0,B.jsx)(`kbd`,{className:z.key,children:`⇥`}):null,e.shortcut?(0,B.jsx)(`kbd`,{className:z.key,children:e.shortcut}):null]})]},e.key)})]},e.id))}),(0,B.jsxs)(`div`,{className:z.foot,"aria-hidden":`true`,children:[(0,B.jsxs)(`span`,{children:[(0,B.jsx)(`kbd`,{className:z.key,children:`↑↓`}),` move`]}),(0,B.jsxs)(`span`,{children:[(0,B.jsx)(`kbd`,{className:z.key,children:(0,B.jsx)(y,{})}),` run`]}),q?.argument?(0,B.jsxs)(`span`,{children:[(0,B.jsx)(`kbd`,{className:z.key,children:`⇥`}),` `,q.argument.prompt.toLowerCase()]}):null,(0,B.jsxs)(`span`,{children:[(0,B.jsx)(`kbd`,{className:z.key,children:`esc`}),` `,D?`back`:`close`]}),D?null:(0,B.jsxs)(`span`,{children:[(0,B.jsx)(`kbd`,{className:z.key,children:`>`}),` actions only · `,(0,B.jsx)(`kbd`,{className:z.key,children:`#`}),` chats only`]})]}),(0,B.jsx)(`p`,{className:`visually-hidden`,role:`status`,"aria-live":`polite`,children:H.flat.length===1?`1 result`:`${H.flat.length} results`})]})}),document.body)}export{V as CommandPalette};
2
+ //# sourceMappingURL=CommandPalette-B6SgDJH-.js.map