@ashlr/hub 3.11.0 → 3.11.3

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 (403) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/authority-surface.json +12 -7
  3. package/dist/build-identity.json +2 -2
  4. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  5. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +5 -4
  6. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +4 -3
  7. package/dist/core/universe/campaign.js +9 -1
  8. package/dist/core/universe/campaign.js.map +1 -1
  9. package/dist/core/universe/runner.d.ts +3 -0
  10. package/dist/core/universe/runner.js +7 -3
  11. package/dist/core/universe/runner.js.map +1 -1
  12. package/dist/core/verse/compaction-point.d.ts +55 -0
  13. package/dist/core/verse/compaction-point.js +75 -0
  14. package/dist/core/verse/compaction-point.js.map +1 -0
  15. package/dist/core/verse/context-math.d.ts +1 -38
  16. package/dist/core/verse/context-math.js +5 -59
  17. package/dist/core/verse/context-math.js.map +1 -1
  18. package/dist/core/web/public/next/assets/{ActionMenu-CQvNJvHY.js → ActionMenu-FxXeRbPx.js} +2 -2
  19. package/dist/core/web/public/next/assets/{ActionMenu-CQvNJvHY.js.map → ActionMenu-FxXeRbPx.js.map} +1 -1
  20. package/dist/core/web/public/next/assets/Advanced-D_eS4472.js +2 -0
  21. package/dist/core/web/public/next/assets/{Advanced-DEM93sKI.js.map → Advanced-D_eS4472.js.map} +1 -1
  22. package/dist/core/web/public/next/assets/App-Cj4zKbG3.js +7 -0
  23. package/dist/core/web/public/next/assets/{App-CimXmK0L.js.map → App-Cj4zKbG3.js.map} +1 -1
  24. package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js +2 -0
  25. package/dist/core/web/public/next/assets/{AppsSection-D6U-dSj6.js.map → AppsSection-Ddu_RCQE.js.map} +1 -1
  26. package/dist/core/web/public/next/assets/{AreaTrend-LD9lCPkT.js → AreaTrend-CDxrQU4b.js} +2 -2
  27. package/dist/core/web/public/next/assets/{AreaTrend-LD9lCPkT.js.map → AreaTrend-CDxrQU4b.js.map} +1 -1
  28. package/dist/core/web/public/next/assets/AutonomySection-DuQkHGD_.js +2 -0
  29. package/dist/core/web/public/next/assets/{AutonomySection-3V9pUyxm.js.map → AutonomySection-DuQkHGD_.js.map} +1 -1
  30. package/dist/core/web/public/next/assets/BranchBar-CLsvOcyK.js +2 -0
  31. package/dist/core/web/public/next/assets/{BranchBar-U8UdpfEB.js.map → BranchBar-CLsvOcyK.js.map} +1 -1
  32. package/dist/core/web/public/next/assets/{BudgetControl-DOTlMSmW.js → BudgetControl-TL3FeZ1D.js} +2 -2
  33. package/dist/core/web/public/next/assets/{BudgetControl-DOTlMSmW.js.map → BudgetControl-TL3FeZ1D.js.map} +1 -1
  34. package/dist/core/web/public/next/assets/{BurnDown-x4PWH9JF.js → BurnDown-sFXkUGdB.js} +2 -2
  35. package/dist/core/web/public/next/assets/{BurnDown-x4PWH9JF.js.map → BurnDown-sFXkUGdB.js.map} +1 -1
  36. package/dist/core/web/public/next/assets/{Button-DYv_KKxr.js → Button-HHXgoXGr.js} +2 -2
  37. package/dist/core/web/public/next/assets/{Button-DYv_KKxr.js.map → Button-HHXgoXGr.js.map} +1 -1
  38. package/dist/core/web/public/next/assets/CapacityStrip-qReu0RaF.js +2 -0
  39. package/dist/core/web/public/next/assets/CapacityStrip-qReu0RaF.js.map +1 -0
  40. package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js +3 -0
  41. package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js.map +1 -0
  42. package/dist/core/web/public/next/assets/{CloudBudgetForm-DJeJvBEu.js → CloudBudgetForm-DJbiT1wi.js} +2 -2
  43. package/dist/core/web/public/next/assets/{CloudBudgetForm-DJeJvBEu.js.map → CloudBudgetForm-DJbiT1wi.js.map} +1 -1
  44. package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js +2 -0
  45. package/dist/core/web/public/next/assets/{CommandPalette-FiD4N8XD.js.map → CommandPalette-zYLFZRnj.js.map} +1 -1
  46. package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js +2 -0
  47. package/dist/core/web/public/next/assets/{CommandSection-DN8KwH-X.js.map → CommandSection-rIeMK5Ch.js.map} +1 -1
  48. package/dist/core/web/public/next/assets/ConfirmDialog-DLrLKM0c.js +2 -0
  49. package/dist/core/web/public/next/assets/ConfirmDialog-DLrLKM0c.js.map +1 -0
  50. package/dist/core/web/public/next/assets/ConfirmDialog-Dx15x0Ho.css +1 -0
  51. package/dist/core/web/public/next/assets/Dialog-BS9Dxi3G.js +2 -0
  52. package/dist/core/web/public/next/assets/Dialog-BS9Dxi3G.js.map +1 -0
  53. package/dist/core/web/public/next/assets/Dialog-tQ8kFulD.css +1 -0
  54. package/dist/core/web/public/next/assets/{DiffPane-DLJ3gGGK.js → DiffPane-TOmUf3b2.js} +2 -2
  55. package/dist/core/web/public/next/assets/{DiffPane-DLJ3gGGK.js.map → DiffPane-TOmUf3b2.js.map} +1 -1
  56. package/dist/core/web/public/next/assets/{DiffViewer-B7NWPjIO.js → DiffViewer-BpH56302.js} +2 -2
  57. package/dist/core/web/public/next/assets/{DiffViewer-B7NWPjIO.js.map → DiffViewer-BpH56302.js.map} +1 -1
  58. package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js +2 -0
  59. package/dist/core/web/public/next/assets/{DockHost-DvR-hPhZ.js.map → DockHost-DzkzR-HD.js.map} +1 -1
  60. package/dist/core/web/public/next/assets/{EmptyState-DhNgDDrF.js → EmptyState-BqR5YAmf.js} +2 -2
  61. package/dist/core/web/public/next/assets/{EmptyState-DhNgDDrF.js.map → EmptyState-BqR5YAmf.js.map} +1 -1
  62. package/dist/core/web/public/next/assets/{Epistemic-uARPnx9x.js → Epistemic-dhvDTwAZ.js} +2 -2
  63. package/dist/core/web/public/next/assets/{Epistemic-uARPnx9x.js.map → Epistemic-dhvDTwAZ.js.map} +1 -1
  64. package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js +3 -0
  65. package/dist/core/web/public/next/assets/{FleetSection-D_zRBCub.js.map → FleetSection-ViAb549H.js.map} +1 -1
  66. package/dist/core/web/public/next/assets/{Funnel-CL8uHONq.js → Funnel-D2u7Wlzc.js} +2 -2
  67. package/dist/core/web/public/next/assets/{Funnel-CL8uHONq.js.map → Funnel-D2u7Wlzc.js.map} +1 -1
  68. package/dist/core/web/public/next/assets/GearTray-Cb2npoxM.js +2 -0
  69. package/dist/core/web/public/next/assets/{GearTray-C7k4l_BW.js.map → GearTray-Cb2npoxM.js.map} +1 -1
  70. package/dist/core/web/public/next/assets/{GrowthSection-DwdNSGj8.js → GrowthSection-C5aDPs8B.js} +2 -2
  71. package/dist/core/web/public/next/assets/{GrowthSection-DwdNSGj8.js.map → GrowthSection-C5aDPs8B.js.map} +1 -1
  72. package/dist/core/web/public/next/assets/HandoffDialog-BF3AYg-X.js +2 -0
  73. package/dist/core/web/public/next/assets/{HandoffDialog-FPVOqOMN.js.map → HandoffDialog-BF3AYg-X.js.map} +1 -1
  74. package/dist/core/web/public/next/assets/{Input-B9-VLcHS.js → Input-B5dmGLZx.js} +2 -2
  75. package/dist/core/web/public/next/assets/{Input-B9-VLcHS.js.map → Input-B5dmGLZx.js.map} +1 -1
  76. package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js +2 -0
  77. package/dist/core/web/public/next/assets/{LeaderCard-4R0BhDzT.js.map → LeaderCard-DZrmjDbI.js.map} +1 -1
  78. package/dist/core/web/public/next/assets/{MatrixHeatmap-DiuushcS.js → MatrixHeatmap-BgEUs2Eu.js} +2 -2
  79. package/dist/core/web/public/next/assets/{MatrixHeatmap-DiuushcS.js.map → MatrixHeatmap-BgEUs2Eu.js.map} +1 -1
  80. package/dist/core/web/public/next/assets/McpSection-DZkiOuoq.js +2 -0
  81. package/dist/core/web/public/next/assets/{McpSection-CJ__OJKl.js.map → McpSection-DZkiOuoq.js.map} +1 -1
  82. package/dist/core/web/public/next/assets/{MessageMarkdown-qWKyJvAQ.js → MessageMarkdown-BauY8ZTY.js} +3 -3
  83. package/dist/core/web/public/next/assets/{MessageMarkdown-qWKyJvAQ.js.map → MessageMarkdown-BauY8ZTY.js.map} +1 -1
  84. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-x05TrsrW.js → MessageMarkdownRenderer-DLBNLfsn.js} +2 -2
  85. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-x05TrsrW.js.map → MessageMarkdownRenderer-DLBNLfsn.js.map} +1 -1
  86. package/dist/core/web/public/next/assets/{Meter-D-hncpkO.js → Meter-DNeocJnD.js} +2 -2
  87. package/dist/core/web/public/next/assets/{Meter-D-hncpkO.js.map → Meter-DNeocJnD.js.map} +1 -1
  88. package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js +2 -0
  89. package/dist/core/web/public/next/assets/{MindSection-BjXHkQPS.js.map → MindSection-BvJXFjyX.js.map} +1 -1
  90. package/dist/core/web/public/next/assets/MutationTokenDialog-CrfJ0PdD.css +1 -0
  91. package/dist/core/web/public/next/assets/MutationTokenDialog-mxrw0Yvy.js +2 -0
  92. package/dist/core/web/public/next/assets/MutationTokenDialog-mxrw0Yvy.js.map +1 -0
  93. package/dist/core/web/public/next/assets/NeedsYouDrawer-CO-Balfp.js +2 -0
  94. package/dist/core/web/public/next/assets/{NeedsYouDrawer-rSd1ZHAR.js.map → NeedsYouDrawer-CO-Balfp.js.map} +1 -1
  95. package/dist/core/web/public/next/assets/{NeedsYouList-CG-nB1tr.js → NeedsYouList-CzDZbzRD.js} +2 -2
  96. package/dist/core/web/public/next/assets/{NeedsYouList-CG-nB1tr.js.map → NeedsYouList-CzDZbzRD.js.map} +1 -1
  97. package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js +3 -0
  98. package/dist/core/web/public/next/assets/{NewChatDialog-BeDQ1eW3.js.map → NewChatDialog-B8eJjlFf.js.map} +1 -1
  99. package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js +2 -0
  100. package/dist/core/web/public/next/assets/{OnboardingFlow-CkTZtt-m.js.map → OnboardingFlow-Cij6KWat.js.map} +1 -1
  101. package/dist/core/web/public/next/assets/PreviewPane-BS-CleCd.js +2 -0
  102. package/dist/core/web/public/next/assets/{PreviewPane-kzbbSK4u.js.map → PreviewPane-BS-CleCd.js.map} +1 -1
  103. package/dist/core/web/public/next/assets/ProviderLogo-C5mBg8i0.js +2 -0
  104. package/dist/core/web/public/next/assets/ProviderLogo-C5mBg8i0.js.map +1 -0
  105. package/dist/core/web/public/next/assets/{RailStatus-D-t2gPae.js → RailStatus-cPRyOmrk.js} +2 -2
  106. package/dist/core/web/public/next/assets/{RailStatus-D-t2gPae.js.map → RailStatus-cPRyOmrk.js.map} +1 -1
  107. package/dist/core/web/public/next/assets/{RefreshIndicator-BLnr9IOM.js → RefreshIndicator-CMwSCiz5.js} +2 -2
  108. package/dist/core/web/public/next/assets/{RefreshIndicator-BLnr9IOM.js.map → RefreshIndicator-CMwSCiz5.js.map} +1 -1
  109. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BeG0gBif.js → ResourcePoolConsoleApp-BFi1opsC.js} +5 -5
  110. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BeG0gBif.js.map → ResourcePoolConsoleApp-BFi1opsC.js.map} +1 -1
  111. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-CM_8pJs6.css → ResourcePoolConsoleApp-BX9JLkT4.css} +1 -1
  112. package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js +3 -0
  113. package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js.map +1 -0
  114. package/dist/core/web/public/next/assets/ResourcesChrome-uZEqC2EM.css +1 -0
  115. package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js +2 -0
  116. package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js.map +1 -0
  117. package/dist/core/web/public/next/assets/RouteErrorBoundary-DYtwc2ja.js +2 -0
  118. package/dist/core/web/public/next/assets/RouteErrorBoundary-DYtwc2ja.js.map +1 -0
  119. package/dist/core/web/public/next/assets/RouteErrorBoundary-DdxhTdZ6.css +1 -0
  120. package/dist/core/web/public/next/assets/{RunInCloudAction-C3LBp7C4.js → RunInCloudAction-BEGEEM8O.js} +2 -2
  121. package/dist/core/web/public/next/assets/{RunInCloudAction-C3LBp7C4.js.map → RunInCloudAction-BEGEEM8O.js.map} +1 -1
  122. package/dist/core/web/public/next/assets/SeatSelector-CYHRR7Wp.js +3 -0
  123. package/dist/core/web/public/next/assets/{SeatSelector-DpvvbjNm.js.map → SeatSelector-CYHRR7Wp.js.map} +1 -1
  124. package/dist/core/web/public/next/assets/{Segmented-BL40udoO.js → Segmented-BE53BenI.js} +2 -2
  125. package/dist/core/web/public/next/assets/{Segmented-BL40udoO.js.map → Segmented-BE53BenI.js.map} +1 -1
  126. package/dist/core/web/public/next/assets/{Select-FWLnAw_j.js → Select-BwSGkHCS.js} +2 -2
  127. package/dist/core/web/public/next/assets/{Select-FWLnAw_j.js.map → Select-BwSGkHCS.js.map} +1 -1
  128. package/dist/core/web/public/next/assets/SessionGate-DvMpXNpJ.css +1 -0
  129. package/dist/core/web/public/next/assets/SessionGate-e0dFJm9d.js +2 -0
  130. package/dist/core/web/public/next/assets/SessionGate-e0dFJm9d.js.map +1 -0
  131. package/dist/core/web/public/next/assets/SessionInsightChip-CxlSyueJ.js +2 -0
  132. package/dist/core/web/public/next/assets/{SessionInsightChip-CWye87Ju.js.map → SessionInsightChip-CxlSyueJ.js.map} +1 -1
  133. package/dist/core/web/public/next/assets/{SessionSearch-YvCHFUuN.js → SessionSearch-_yp7S2TP.js} +2 -2
  134. package/dist/core/web/public/next/assets/{SessionSearch-YvCHFUuN.js.map → SessionSearch-_yp7S2TP.js.map} +1 -1
  135. package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js +2 -0
  136. package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js.map +1 -0
  137. package/dist/core/web/public/next/assets/Sheet-DVP2Mgy6.js +2 -0
  138. package/dist/core/web/public/next/assets/{Sheet-DZKcQXQg.js.map → Sheet-DVP2Mgy6.js.map} +1 -1
  139. package/dist/core/web/public/next/assets/ShortcutsOverlay-BcIcoClX.js +2 -0
  140. package/dist/core/web/public/next/assets/{ShortcutsOverlay-BpDimdPq.js.map → ShortcutsOverlay-BcIcoClX.js.map} +1 -1
  141. package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js +3 -0
  142. package/dist/core/web/public/next/assets/{Sidebar-BoBzBEFX.js.map → Sidebar-DjQOqPbm.js.map} +1 -1
  143. package/dist/core/web/public/next/assets/{Slider-DUMo9HDD.js → Slider-BypUWjpH.js} +2 -2
  144. package/dist/core/web/public/next/assets/{Slider-DUMo9HDD.js.map → Slider-BypUWjpH.js.map} +1 -1
  145. package/dist/core/web/public/next/assets/{StatusBadge-BwnBNFQr.js → StatusBadge-HCZp416_.js} +2 -2
  146. package/dist/core/web/public/next/assets/{StatusBadge-BwnBNFQr.js.map → StatusBadge-HCZp416_.js.map} +1 -1
  147. package/dist/core/web/public/next/assets/{StepBand-PDXMIlRk.js → StepBand-Duz1PUes.js} +2 -2
  148. package/dist/core/web/public/next/assets/{StepBand-PDXMIlRk.js.map → StepBand-Duz1PUes.js.map} +1 -1
  149. package/dist/core/web/public/next/assets/{Surface-B3TZVoER.js → Surface-B75lVfzm.js} +2 -2
  150. package/dist/core/web/public/next/assets/{Surface-B3TZVoER.js.map → Surface-B75lVfzm.js.map} +1 -1
  151. package/dist/core/web/public/next/assets/{Swimlane-DR4OgON_.js → Swimlane-DGcIcnNA.js} +2 -2
  152. package/dist/core/web/public/next/assets/{Swimlane-DR4OgON_.js.map → Swimlane-DGcIcnNA.js.map} +1 -1
  153. package/dist/core/web/public/next/assets/{Switch-CcR5Llk5.js → Switch-iveI-8NY.js} +2 -2
  154. package/dist/core/web/public/next/assets/{Switch-CcR5Llk5.js.map → Switch-iveI-8NY.js.map} +1 -1
  155. package/dist/core/web/public/next/assets/{Tag-_-K1h7Hk.js → Tag-DH15CUJp.js} +2 -2
  156. package/dist/core/web/public/next/assets/{Tag-_-K1h7Hk.js.map → Tag-DH15CUJp.js.map} +1 -1
  157. package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js +7 -0
  158. package/dist/core/web/public/next/assets/{TerminalPane-_1aH4ndN.js.map → TerminalPane-MQT-veKZ.js.map} +1 -1
  159. package/dist/core/web/public/next/assets/UniverseConsoleApp-C8yQ0AaR.js +2 -0
  160. package/dist/core/web/public/next/assets/{UniverseConsoleApp-BUsq-inZ.js.map → UniverseConsoleApp-C8yQ0AaR.js.map} +1 -1
  161. package/dist/core/web/public/next/assets/{UniverseConsoleApp-Dh7A-hLD.css → UniverseConsoleApp-DfuT-wFw.css} +1 -1
  162. package/dist/core/web/public/next/assets/{UniverseView-Dogy_dsB.js → UniverseView-_nPURPqe.js} +4 -4
  163. package/dist/core/web/public/next/assets/{UniverseView-Dogy_dsB.js.map → UniverseView-_nPURPqe.js.map} +1 -1
  164. package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js +2 -0
  165. package/dist/core/web/public/next/assets/{UsageSection-DiX_dTyo.js.map → UsageSection-CqxRGJ40.js.map} +1 -1
  166. package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js +3 -0
  167. package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js.map +1 -0
  168. package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js +10 -0
  169. package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js.map +1 -0
  170. package/dist/core/web/public/next/assets/Workspace-Dwri2jFJ.css +1 -0
  171. package/dist/core/web/public/next/assets/actions-tWevL9HV.js +2 -0
  172. package/dist/core/web/public/next/assets/{actions-s2lfm_5k.js.map → actions-tWevL9HV.js.map} +1 -1
  173. package/dist/core/web/public/next/assets/budget-queries-V3wcJVK_.js +2 -0
  174. package/dist/core/web/public/next/assets/{budget-queries-wUshcovN.js.map → budget-queries-V3wcJVK_.js.map} +1 -1
  175. package/dist/core/web/public/next/assets/{capacity-strip-model-Bj_9ztzs.js → capacity-strip-model-BDbmQ3e3.js} +2 -2
  176. package/dist/core/web/public/next/assets/{capacity-strip-model-Bj_9ztzs.js.map → capacity-strip-model-BDbmQ3e3.js.map} +1 -1
  177. package/dist/core/web/public/next/assets/{charts-CMSgAUKn.js → charts-CfAjuKMv.js} +2 -2
  178. package/dist/core/web/public/next/assets/{charts-CMSgAUKn.js.map → charts-CfAjuKMv.js.map} +1 -1
  179. package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js +2 -0
  180. package/dist/core/web/public/next/assets/{cloud-queries-CnV1yaNG.js.map → cloud-queries-Br_r0UAz.js.map} +1 -1
  181. package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js → cloud.module-ascz58c3.js} +2 -2
  182. package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js.map → cloud.module-ascz58c3.js.map} +1 -1
  183. package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js → command-bus-xYZoK9nQ.js} +2 -2
  184. package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js.map → command-bus-xYZoK9nQ.js.map} +1 -1
  185. package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js +2 -0
  186. package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js.map +1 -0
  187. package/dist/core/web/public/next/assets/compaction-point-VL9oegWS.js +2 -0
  188. package/dist/core/web/public/next/assets/compaction-point-VL9oegWS.js.map +1 -0
  189. package/dist/core/web/public/next/assets/{composer-keys-DBRymxQn.js → composer-keys-su0g1h9o.js} +2 -2
  190. package/dist/core/web/public/next/assets/{composer-keys-DBRymxQn.js.map → composer-keys-su0g1h9o.js.map} +1 -1
  191. package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js +2 -0
  192. package/dist/core/web/public/next/assets/{context-model-Dwd49-dp.js.map → context-model-BC-HOmPo.js.map} +1 -1
  193. package/dist/core/web/public/next/assets/context-model-CIBmSugV.js +2 -0
  194. package/dist/core/web/public/next/assets/{context-model-MT17V86_.js.map → context-model-CIBmSugV.js.map} +1 -1
  195. package/dist/core/web/public/next/assets/context-queries-CrPcHWTz.js +2 -0
  196. package/dist/core/web/public/next/assets/{context-queries-xVKIeynP.js.map → context-queries-CrPcHWTz.js.map} +1 -1
  197. package/dist/core/web/public/next/assets/control-queries-CmgBgKV7.js +2 -0
  198. package/dist/core/web/public/next/assets/{control-queries-DOZR-D8G.js.map → control-queries-CmgBgKV7.js.map} +1 -1
  199. package/dist/core/web/public/next/assets/{dock-icons-8EtARwUm.js → dock-icons-CUh8dOxJ.js} +2 -2
  200. package/dist/core/web/public/next/assets/{dock-icons-8EtARwUm.js.map → dock-icons-CUh8dOxJ.js.map} +1 -1
  201. package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js → dock-store-B2vlxqPu.js} +2 -2
  202. package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js.map → dock-store-B2vlxqPu.js.map} +1 -1
  203. package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js → fleet-model-7i-FbzKI.js} +2 -2
  204. package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js.map → fleet-model-7i-FbzKI.js.map} +1 -1
  205. package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js +2 -0
  206. package/dist/core/web/public/next/assets/{fleet-queries-BSdRlUwJ.js.map → fleet-queries-DzGoqq8X.js.map} +1 -1
  207. package/dist/core/web/public/next/assets/focus-trap-HtcoXeEq.js +2 -0
  208. package/dist/core/web/public/next/assets/focus-trap-HtcoXeEq.js.map +1 -0
  209. package/dist/core/web/public/next/assets/{format-B8DAVcn2.js → format-BkWdOQDL.js} +2 -2
  210. package/dist/core/web/public/next/assets/{format-B8DAVcn2.js.map → format-BkWdOQDL.js.map} +1 -1
  211. package/dist/core/web/public/next/assets/git-model-bk_RXh3y.js +5 -0
  212. package/dist/core/web/public/next/assets/{git-model-CivuBY_D.js.map → git-model-bk_RXh3y.js.map} +1 -1
  213. package/dist/core/web/public/next/assets/global-BlI2BYa1.css +1 -0
  214. package/dist/core/web/public/next/assets/global-oEl9Qj8u.js +2 -0
  215. package/dist/core/web/public/next/assets/global-oEl9Qj8u.js.map +1 -0
  216. package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js +3 -0
  217. package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js.map +1 -0
  218. package/dist/core/web/public/next/assets/guarded-runners-LHLXmXIk.js +2 -0
  219. package/dist/core/web/public/next/assets/guarded-runners-LHLXmXIk.js.map +1 -0
  220. package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js +2 -0
  221. package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js.map +1 -0
  222. package/dist/core/web/public/next/assets/icons-DEQa2rTx.js +2 -0
  223. package/dist/core/web/public/next/assets/icons-DEQa2rTx.js.map +1 -0
  224. package/dist/core/web/public/next/assets/{index-oAAq8XJa.js → index-BVxOlseC.js} +3 -3
  225. package/dist/core/web/public/next/assets/{index-oAAq8XJa.js.map → index-BVxOlseC.js.map} +1 -1
  226. package/dist/core/web/public/next/assets/{live-model-DCw_IFRI.js → live-model-bmsJgnYD.js} +2 -2
  227. package/dist/core/web/public/next/assets/{live-model-DCw_IFRI.js.map → live-model-bmsJgnYD.js.map} +1 -1
  228. package/dist/core/web/public/next/assets/{mind.module-vnuJcwvD.js → mind.module-gTGAuJt1.js} +2 -2
  229. package/dist/core/web/public/next/assets/{mind.module-vnuJcwvD.js.map → mind.module-gTGAuJt1.js.map} +1 -1
  230. package/dist/core/web/public/next/assets/mutations-C5S7okTW.js +2 -0
  231. package/dist/core/web/public/next/assets/{mutations-Dpsql5t7.js.map → mutations-C5S7okTW.js.map} +1 -1
  232. package/dist/core/web/public/next/assets/nav-NVtnuG7A.js +2 -0
  233. package/dist/core/web/public/next/assets/nav-NVtnuG7A.js.map +1 -0
  234. package/dist/core/web/public/next/assets/overnight-queries-DGm3qL1b.js +2 -0
  235. package/dist/core/web/public/next/assets/{overnight-queries-X-44Gsw5.js.map → overnight-queries-DGm3qL1b.js.map} +1 -1
  236. package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js +2 -0
  237. package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js.map +1 -0
  238. package/dist/core/web/public/next/assets/primitives-DyCKYSBG.js +1 -0
  239. package/dist/core/web/public/next/assets/{queries-BA4rNGD-.js → queries-CYisSAAB.js} +2 -2
  240. package/dist/core/web/public/next/assets/{queries-BA4rNGD-.js.map → queries-CYisSAAB.js.map} +1 -1
  241. package/dist/core/web/public/next/assets/reasoning-pref-BUcFF_5G.js +2 -0
  242. package/dist/core/web/public/next/assets/{reasoning-pref-B9-DJUXP.js.map → reasoning-pref-BUcFF_5G.js.map} +1 -1
  243. package/dist/core/web/public/next/assets/reveal-anchor-D-tV8xau.js +2 -0
  244. package/dist/core/web/public/next/assets/reveal-anchor-D-tV8xau.js.map +1 -0
  245. package/dist/core/web/public/next/assets/seat-subscription-B-f9ViU2.js +2 -0
  246. package/dist/core/web/public/next/assets/{seat-subscription-CH6fW4fJ.js.map → seat-subscription-B-f9ViU2.js.map} +1 -1
  247. package/dist/core/web/public/next/assets/{section-visibility-qFqc_d4Z.js → section-visibility-Yctxu-if.js} +2 -2
  248. package/dist/core/web/public/next/assets/{section-visibility-qFqc_d4Z.js.map → section-visibility-Yctxu-if.js.map} +1 -1
  249. package/dist/core/web/public/next/assets/{sidebar-model-M1GaQU5l.js → sidebar-model-DQ-FyBHW.js} +2 -2
  250. package/dist/core/web/public/next/assets/{sidebar-model-M1GaQU5l.js.map → sidebar-model-DQ-FyBHW.js.map} +1 -1
  251. package/dist/core/web/public/next/assets/slots-BhP8b8tf.js +3 -0
  252. package/dist/core/web/public/next/assets/{slots-Ds9ZIqUL.js.map → slots-BhP8b8tf.js.map} +1 -1
  253. package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js +2 -0
  254. package/dist/core/web/public/next/assets/{surface-data-J52iIGMD.js.map → surface-data-DTI7LjvH.js.map} +1 -1
  255. package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js +2 -0
  256. package/dist/core/web/public/next/assets/{surface-prefetch-CgZ-LEJK.js.map → surface-prefetch-CdUEBFrl.js.map} +1 -1
  257. package/dist/core/web/public/next/assets/{tasks-model-ent8f0jZ.js → tasks-model-BKDNqmmt.js} +2 -2
  258. package/dist/core/web/public/next/assets/{tasks-model-ent8f0jZ.js.map → tasks-model-BKDNqmmt.js.map} +1 -1
  259. package/dist/core/web/public/next/assets/{terminal-icons-DJTK7QpR.js → terminal-icons-D2xe-aM1.js} +2 -2
  260. package/dist/core/web/public/next/assets/{terminal-icons-DJTK7QpR.js.map → terminal-icons-D2xe-aM1.js.map} +1 -1
  261. package/dist/core/web/public/next/assets/{usage-queries-Bn8ccqyN.js → usage-queries-BZuAANlI.js} +2 -2
  262. package/dist/core/web/public/next/assets/{usage-queries-Bn8ccqyN.js.map → usage-queries-BZuAANlI.js.map} +1 -1
  263. package/dist/core/web/public/next/assets/use-guarded-action-Cf4MeNBa.js +2 -0
  264. package/dist/core/web/public/next/assets/{use-guarded-action-C2HlSzkz.js.map → use-guarded-action-Cf4MeNBa.js.map} +1 -1
  265. package/dist/core/web/public/next/assets/{use-ticker-BNc1H4ZC.js → use-ticker-D9yi49my.js} +2 -2
  266. package/dist/core/web/public/next/assets/{use-ticker-BNc1H4ZC.js.map → use-ticker-D9yi49my.js.map} +1 -1
  267. package/dist/core/web/public/next/assets/use-token-gate-DqrSXdX6.js +2 -0
  268. package/dist/core/web/public/next/assets/{use-token-gate-B3IJL6bf.js.map → use-token-gate-DqrSXdX6.js.map} +1 -1
  269. package/dist/core/web/public/next/assets/useChartWidth-BW6SwYIK.css +1 -0
  270. package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js +2 -0
  271. package/dist/core/web/public/next/assets/{useChartWidth-lZ9PrQn0.js.map → useChartWidth-owwodNVz.js.map} +1 -1
  272. package/dist/core/web/public/next/assets/{useSeatsRefresh-CG4jczeL.js → useSeatsRefresh-DgFy-Ph4.js} +2 -2
  273. package/dist/core/web/public/next/assets/{useSeatsRefresh-CG4jczeL.js.map → useSeatsRefresh-DgFy-Ph4.js.map} +1 -1
  274. package/dist/core/web/public/next/assets/useVerseTranscript-CDYrrWOO.js +2 -0
  275. package/dist/core/web/public/next/assets/useVerseTranscript-CDYrrWOO.js.map +1 -0
  276. package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js +2 -0
  277. package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js.map +1 -0
  278. package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js +2 -0
  279. package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js.map +1 -0
  280. package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js +3 -0
  281. package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js.map +1 -0
  282. package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js +3 -0
  283. package/dist/core/web/public/next/assets/{verse-ui-store-zafyzXKD.js.map → verse-ui-store-62DxpVHS.js.map} +1 -1
  284. package/dist/core/web/public/next/assets/{viewport-BEqGUoTA.js → viewport-nTubFCwf.js} +2 -2
  285. package/dist/core/web/public/next/assets/{viewport-BEqGUoTA.js.map → viewport-nTubFCwf.js.map} +1 -1
  286. package/dist/core/web/public/next/assets/warmup-HG5vYbsN.js +3 -0
  287. package/dist/core/web/public/next/assets/{warmup-BR6pxAKs.js.map → warmup-HG5vYbsN.js.map} +1 -1
  288. package/dist/core/web/public/next/assets/{workspace-model-D_tWD5Wt.js → workspace-model-CbhdPhei.js} +2 -2
  289. package/dist/core/web/public/next/assets/{workspace-model-D_tWD5Wt.js.map → workspace-model-CbhdPhei.js.map} +1 -1
  290. package/dist/core/web/public/next/index.html +1 -1
  291. package/dist/release-dependency-inventory.json +1 -1
  292. package/package.json +5 -2
  293. package/dist/cli/activation.d.ts +0 -41
  294. package/dist/cli/activation.js +0 -485
  295. package/dist/cli/activation.js.map +0 -1
  296. package/dist/core/fleet/generated-repair-resolution.d.ts +0 -36
  297. package/dist/core/fleet/generated-repair-resolution.js +0 -245
  298. package/dist/core/fleet/generated-repair-resolution.js.map +0 -1
  299. package/dist/core/local-runtime/llama/port.d.ts +0 -17
  300. package/dist/core/local-runtime/llama/port.js +0 -47
  301. package/dist/core/local-runtime/llama/port.js.map +0 -1
  302. package/dist/core/local-runtime/llama/proxy-host.d.ts +0 -54
  303. package/dist/core/local-runtime/llama/proxy-host.js +0 -159
  304. package/dist/core/local-runtime/llama/proxy-host.js.map +0 -1
  305. package/dist/core/local-runtime/llama/proxy-process.d.ts +0 -148
  306. package/dist/core/local-runtime/llama/proxy-process.js +0 -250
  307. package/dist/core/local-runtime/llama/proxy-process.js.map +0 -1
  308. package/dist/core/local-runtime/llama/proxy-record.d.ts +0 -40
  309. package/dist/core/local-runtime/llama/proxy-record.js +0 -133
  310. package/dist/core/local-runtime/llama/proxy-record.js.map +0 -1
  311. package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +0 -118
  312. package/dist/core/local-runtime/llama/proxy-supervisor.js +0 -511
  313. package/dist/core/local-runtime/llama/proxy-supervisor.js.map +0 -1
  314. package/dist/core/web/public/next/assets/Advanced-DEM93sKI.js +0 -2
  315. package/dist/core/web/public/next/assets/App-CimXmK0L.js +0 -7
  316. package/dist/core/web/public/next/assets/AppsSection-D6U-dSj6.js +0 -2
  317. package/dist/core/web/public/next/assets/AutonomySection-3V9pUyxm.js +0 -2
  318. package/dist/core/web/public/next/assets/BranchBar-U8UdpfEB.js +0 -2
  319. package/dist/core/web/public/next/assets/CapacityStrip-C667yQe8.js +0 -2
  320. package/dist/core/web/public/next/assets/CapacityStrip-C667yQe8.js.map +0 -1
  321. package/dist/core/web/public/next/assets/ChatSection-CCcIPpOq.js +0 -3
  322. package/dist/core/web/public/next/assets/ChatSection-CCcIPpOq.js.map +0 -1
  323. package/dist/core/web/public/next/assets/CommandPalette-FiD4N8XD.js +0 -2
  324. package/dist/core/web/public/next/assets/CommandSection-DN8KwH-X.js +0 -2
  325. package/dist/core/web/public/next/assets/DockHost-DvR-hPhZ.js +0 -2
  326. package/dist/core/web/public/next/assets/FleetSection-D_zRBCub.js +0 -3
  327. package/dist/core/web/public/next/assets/GearTray-C7k4l_BW.js +0 -2
  328. package/dist/core/web/public/next/assets/HandoffDialog-FPVOqOMN.js +0 -2
  329. package/dist/core/web/public/next/assets/LeaderCard-4R0BhDzT.js +0 -2
  330. package/dist/core/web/public/next/assets/McpSection-CJ__OJKl.js +0 -2
  331. package/dist/core/web/public/next/assets/MindSection-BjXHkQPS.js +0 -2
  332. package/dist/core/web/public/next/assets/MutationTokenDialog-Cum_DqQZ.css +0 -1
  333. package/dist/core/web/public/next/assets/MutationTokenDialog-SkRxqV_I.js +0 -2
  334. package/dist/core/web/public/next/assets/MutationTokenDialog-SkRxqV_I.js.map +0 -1
  335. package/dist/core/web/public/next/assets/NeedsYouDrawer-rSd1ZHAR.js +0 -2
  336. package/dist/core/web/public/next/assets/NewChatDialog-BeDQ1eW3.js +0 -3
  337. package/dist/core/web/public/next/assets/OnboardingFlow-CkTZtt-m.js +0 -2
  338. package/dist/core/web/public/next/assets/PreviewPane-kzbbSK4u.js +0 -2
  339. package/dist/core/web/public/next/assets/ResourcesChrome-B3Z3O98t.css +0 -1
  340. package/dist/core/web/public/next/assets/ResourcesChrome-BzV6bkIa.js +0 -3
  341. package/dist/core/web/public/next/assets/ResourcesChrome-BzV6bkIa.js.map +0 -1
  342. package/dist/core/web/public/next/assets/ResourcesDrawer-o4ILHdep.js +0 -2
  343. package/dist/core/web/public/next/assets/ResourcesDrawer-o4ILHdep.js.map +0 -1
  344. package/dist/core/web/public/next/assets/RouteErrorBoundary-6RMd_aZg.css +0 -1
  345. package/dist/core/web/public/next/assets/RouteErrorBoundary-C5SxA2Dg.js +0 -2
  346. package/dist/core/web/public/next/assets/RouteErrorBoundary-C5SxA2Dg.js.map +0 -1
  347. package/dist/core/web/public/next/assets/SeatSelector-DpvvbjNm.js +0 -3
  348. package/dist/core/web/public/next/assets/SessionInsightChip-CWye87Ju.js +0 -2
  349. package/dist/core/web/public/next/assets/SettingsSection-Dr18upeE.js +0 -2
  350. package/dist/core/web/public/next/assets/SettingsSection-Dr18upeE.js.map +0 -1
  351. package/dist/core/web/public/next/assets/Sheet-DZKcQXQg.js +0 -2
  352. package/dist/core/web/public/next/assets/ShortcutsOverlay-BpDimdPq.js +0 -2
  353. package/dist/core/web/public/next/assets/Sidebar-BoBzBEFX.js +0 -3
  354. package/dist/core/web/public/next/assets/TerminalPane-_1aH4ndN.js +0 -7
  355. package/dist/core/web/public/next/assets/UniverseConsoleApp-BUsq-inZ.js +0 -2
  356. package/dist/core/web/public/next/assets/UsageSection-DiX_dTyo.js +0 -2
  357. package/dist/core/web/public/next/assets/VerseConsoleApp-WVeyJnW7.js +0 -3
  358. package/dist/core/web/public/next/assets/VerseConsoleApp-WVeyJnW7.js.map +0 -1
  359. package/dist/core/web/public/next/assets/Workspace-BYW9dm90.css +0 -1
  360. package/dist/core/web/public/next/assets/Workspace-BbxZHwQd.js +0 -10
  361. package/dist/core/web/public/next/assets/Workspace-BbxZHwQd.js.map +0 -1
  362. package/dist/core/web/public/next/assets/actions-s2lfm_5k.js +0 -2
  363. package/dist/core/web/public/next/assets/budget-queries-wUshcovN.js +0 -2
  364. package/dist/core/web/public/next/assets/cloud-queries-CnV1yaNG.js +0 -2
  365. package/dist/core/web/public/next/assets/command-catalog-BdtDH93o.js +0 -2
  366. package/dist/core/web/public/next/assets/command-catalog-BdtDH93o.js.map +0 -1
  367. package/dist/core/web/public/next/assets/context-model-Dwd49-dp.js +0 -2
  368. package/dist/core/web/public/next/assets/context-model-MT17V86_.js +0 -2
  369. package/dist/core/web/public/next/assets/context-queries-xVKIeynP.js +0 -2
  370. package/dist/core/web/public/next/assets/control-queries-DOZR-D8G.js +0 -2
  371. package/dist/core/web/public/next/assets/dock-panes-UKS-mXhr.js +0 -2
  372. package/dist/core/web/public/next/assets/dock-panes-UKS-mXhr.js.map +0 -1
  373. package/dist/core/web/public/next/assets/fleet-queries-BSdRlUwJ.js +0 -2
  374. package/dist/core/web/public/next/assets/git-model-CivuBY_D.js +0 -5
  375. package/dist/core/web/public/next/assets/global-Di2PUgj4.js +0 -2
  376. package/dist/core/web/public/next/assets/global-Di2PUgj4.js.map +0 -1
  377. package/dist/core/web/public/next/assets/global-_tOCFt9y.css +0 -1
  378. package/dist/core/web/public/next/assets/guarded-action-CvOHcqyH.js +0 -2
  379. package/dist/core/web/public/next/assets/guarded-action-CvOHcqyH.js.map +0 -1
  380. package/dist/core/web/public/next/assets/icons-B_yuAgdN.js +0 -2
  381. package/dist/core/web/public/next/assets/icons-B_yuAgdN.js.map +0 -1
  382. package/dist/core/web/public/next/assets/mutations-Dpsql5t7.js +0 -2
  383. package/dist/core/web/public/next/assets/overnight-queries-X-44Gsw5.js +0 -2
  384. package/dist/core/web/public/next/assets/primitives-BDIcUvob.js +0 -1
  385. package/dist/core/web/public/next/assets/reasoning-pref-B9-DJUXP.js +0 -2
  386. package/dist/core/web/public/next/assets/seat-subscription-CH6fW4fJ.js +0 -2
  387. package/dist/core/web/public/next/assets/slots-Ds9ZIqUL.js +0 -3
  388. package/dist/core/web/public/next/assets/surface-data-J52iIGMD.js +0 -2
  389. package/dist/core/web/public/next/assets/surface-prefetch-CgZ-LEJK.js +0 -2
  390. package/dist/core/web/public/next/assets/use-guarded-action-C2HlSzkz.js +0 -2
  391. package/dist/core/web/public/next/assets/use-token-gate-B3IJL6bf.js +0 -2
  392. package/dist/core/web/public/next/assets/useActivity-Bk3XgXjL.js +0 -2
  393. package/dist/core/web/public/next/assets/useActivity-Bk3XgXjL.js.map +0 -1
  394. package/dist/core/web/public/next/assets/useChartWidth-BAtVgn2n.css +0 -1
  395. package/dist/core/web/public/next/assets/useChartWidth-lZ9PrQn0.js +0 -2
  396. package/dist/core/web/public/next/assets/verse-model-D7NZQFAR.js +0 -2
  397. package/dist/core/web/public/next/assets/verse-model-D7NZQFAR.js.map +0 -1
  398. package/dist/core/web/public/next/assets/verse-queries-DvIJcn6Z.js +0 -2
  399. package/dist/core/web/public/next/assets/verse-queries-DvIJcn6Z.js.map +0 -1
  400. package/dist/core/web/public/next/assets/verse-store-DjBWj04q.js +0 -3
  401. package/dist/core/web/public/next/assets/verse-store-DjBWj04q.js.map +0 -1
  402. package/dist/core/web/public/next/assets/verse-ui-store-zafyzXKD.js +0 -3
  403. package/dist/core/web/public/next/assets/warmup-BR6pxAKs.js +0 -3
@@ -1 +1 @@
1
- {"version":3,"file":"overnight-queries-X-44Gsw5.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/autonomy/overnight-model.ts","../../../../../../src/web-ui/routes/verse/autonomy/overnight-queries.ts"],"sourcesContent":["/**\n * routes/verse/autonomy/overnight-model.ts — the pure half of the Overnight\n * panel: narrow the server's body, resolve the operator's stop rule, and\n * derive the one line that says how far the run is from stopping.\n *\n * Framework-free so it unit-tests without a DOM, and separate from the panel\n * because two of these functions are where the panel can most easily start\n * lying:\n *\n * - `projectOvernight` NARROWS, it does not cast. The route is being written\n * by another owner in parallel with this surface, so a field that drifts in\n * name or type has to degrade to `null` — which the panel renders as \"not\n * stated\" — rather than reach a render as `undefined` and crash, or reach\n * it as `0` and read as a fact. Same discipline as `fleet-model.ts`.\n * - `stopProgress` never invents a denominator. \"Until I pause it\" has no\n * progress, and drawing a bar for it would be a picture of a finish line\n * that does not exist.\n */\nimport { formatDuration, UNKNOWN } from './format.js';\nimport type {\n OvernightDiscard,\n OvernightGate,\n OvernightMerge,\n OvernightRun,\n OvernightStatus,\n OvernightStopRule,\n OvernightStopRuleKind,\n} from './overnight-contract.js';\n\n// ---------------------------------------------------------------------------\n// Narrowing\n// ---------------------------------------------------------------------------\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.trim() !== '' ? value : null;\n}\n\nfunction bool(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\n/** A count. Negative and non-finite are not counts, so they are not observed. */\nfunction count(value: unknown): number | null {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) return null;\n return Math.floor(value);\n}\n\nexport function projectStopRule(raw: unknown): OvernightStopRule | null {\n if (!isRecord(raw)) return null;\n const kind = str(raw['kind']);\n if (kind === 'until-paused') return { kind: 'until-paused' };\n if (kind === 'at-time') {\n const at = str(raw['at']);\n // An unparseable instant is worse than no rule: it would render as a\n // countdown to NaN. Refuse it here so the panel can say \"not stated\".\n if (at === null || Number.isNaN(Date.parse(at))) return null;\n return { kind: 'at-time', at };\n }\n if (kind === 'after-iterations') {\n const iterations = count(raw['iterations']);\n if (iterations === null || iterations <= 0) return null;\n return { kind: 'after-iterations', iterations };\n }\n return null;\n}\n\nfunction projectMerges(raw: unknown): OvernightMerge[] {\n if (!Array.isArray(raw)) return [];\n const out: OvernightMerge[] = [];\n for (const [index, item] of raw.entries()) {\n if (!isRecord(item)) continue;\n const repo = str(item['repo']);\n if (repo === null) continue;\n out.push({\n id: str(item['id']) ?? `${repo}:${index}`,\n repo,\n title: str(item['title']) ?? 'untitled change',\n at: str(item['at']),\n commit: str(item['commit']),\n });\n }\n return out;\n}\n\nfunction projectDiscards(raw: unknown): OvernightDiscard[] {\n if (!Array.isArray(raw)) return [];\n const out: OvernightDiscard[] = [];\n for (const [index, item] of raw.entries()) {\n if (!isRecord(item)) continue;\n const repo = str(item['repo']);\n if (repo === null) continue;\n out.push({\n id: str(item['id']) ?? `${repo}:${index}`,\n repo,\n title: str(item['title']) ?? 'untitled change',\n at: str(item['at']),\n // A discard with no stated reason is the one row an operator will ask\n // about in the morning, so it says that it has none rather than being\n // rendered as a blank cell.\n reason: str(item['reason']) ?? 'no reason recorded',\n });\n }\n return out;\n}\n\nfunction projectGate(raw: unknown): OvernightGate | null {\n if (!isRecord(raw)) return null;\n return {\n tests: bool(raw['tests']),\n lint: bool(raw['lint']),\n typecheck: bool(raw['typecheck']),\n autoMerge: bool(raw['autoMerge']),\n branch: str(raw['branch']),\n };\n}\n\nfunction projectRun(raw: unknown): OvernightRun | null {\n if (!isRecord(raw)) return null;\n const runId = str(raw['runId']);\n if (runId === null) return null;\n return {\n runId,\n startedAt: str(raw['startedAt']),\n stopRule: projectStopRule(raw['stopRule']),\n iterationsDone: count(raw['iterationsDone']),\n repo: str(raw['repo']),\n activity: str(raw['activity']),\n merged: projectMerges(raw['merged']),\n discarded: projectDiscards(raw['discarded']),\n };\n}\n\n/**\n * `GET /api/verse/overnight` → a status, or null when the body is not one.\n *\n * `armed` is the single field this refuses to guess: a body that does not say\n * whether a run is armed is not a status, because \"nothing is armed\" is\n * exactly the claim a person checks before closing the laptop.\n */\nexport function projectOvernight(raw: unknown): OvernightStatus | null {\n if (!isRecord(raw)) return null;\n const armed = bool(raw['armed']);\n if (armed === null) return null;\n return {\n armed,\n run: projectRun(raw['run']),\n repos: count(raw['repos']),\n // Present-but-invalid reads as null (\"not recorded\"); absent stays absent,\n // so an older server is not shown as having lost a count it never sent.\n ...(raw['mirrors'] !== undefined ? { mirrors: count(raw['mirrors']) } : {}),\n gate: projectGate(raw['gate']),\n };\n}\n\n// ---------------------------------------------------------------------------\n// The operator's side of the stop rule\n// ---------------------------------------------------------------------------\n\n/** Smallest and largest iteration cap the form will send. */\nexport const ITERATIONS_MIN = 1;\nexport const ITERATIONS_MAX = 500;\n\n/**\n * Turn an `<input type=\"time\">` value (\"23:30\") into the next occurrence of\n * that wall-clock time, as an absolute instant.\n *\n * The whole point of arming this at 11pm is that \"07:00\" means tomorrow\n * morning. Resolving it on the client, in the operator's own zone, is what\n * makes that unambiguous by the time it reaches a server that may not share\n * the zone — and rounds to the minute, so a run armed at 06:59:58 for 07:00\n * does not stop two seconds later.\n */\nexport function resolveStopAt(value: string, now: Date = new Date()): string | null {\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(value.trim());\n if (!match) return null;\n const hours = Number(match[1]);\n const minutes = Number(match[2]);\n if (!Number.isInteger(hours) || !Number.isInteger(minutes)) return null;\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;\n const at = new Date(now);\n at.setHours(hours, minutes, 0, 0);\n if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 1);\n return at.toISOString();\n}\n\n/** \"07:00\" as the operator's locale writes it — never a raw ISO string. */\nexport function formatStopClock(iso: string | null): string {\n if (!iso) return UNKNOWN;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return UNKNOWN;\n return new Date(ms).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n}\n\n/** True when the resolved instant is not today — \"07:00 tomorrow\". */\nexport function isNextDay(iso: string | null, now: Date = new Date()): boolean {\n if (!iso) return false;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return false;\n return new Date(ms).toDateString() !== now.toDateString();\n}\n\n/**\n * The sentence the Arm button is committing to, before the click.\n *\n * It restates the rule the operator chose in the operator's own units, so the\n * button is never the only description of what is about to be authorised.\n */\nexport function describeStopRule(rule: OvernightStopRule, now: Date = new Date()): string {\n switch (rule.kind) {\n case 'until-paused':\n return 'Runs until you pause it — no stop time, no iteration limit.';\n case 'at-time':\n return `Stops at ${formatStopClock(rule.at)}${isNextDay(rule.at, now) ? ' tomorrow' : ''}.`;\n case 'after-iterations':\n return `Stops after ${rule.iterations} ${rule.iterations === 1 ? 'iteration' : 'iterations'}.`;\n }\n}\n\n/** The short label each stop rule wears in the armed summary. */\nexport function stopRuleLabel(kind: OvernightStopRuleKind): string {\n switch (kind) {\n case 'until-paused':\n return 'Until paused';\n case 'at-time':\n return 'At a set time';\n case 'after-iterations':\n return 'After N iterations';\n }\n}\n\n// ---------------------------------------------------------------------------\n// How far to the stop condition\n// ---------------------------------------------------------------------------\n\nexport interface StopProgress {\n /** The rule, restated: \"Stops at 07:00\". */\n headline: string;\n /** How much is left: \"4h 12m left\", \"12 of 20 done\". */\n remaining: string;\n /** 0-100 for a meter, or null when there is no honest denominator. */\n percent: number | null;\n /** True once the condition is met and the loop should be winding down. */\n reached: boolean;\n}\n\n/**\n * How far the run is from its stop condition.\n *\n * `percent` is null for \"until I pause it\" on purpose: an open-ended run has\n * no finish line, and a bar with no denominator is the exact kind of confident\n * graphic this cockpit is not allowed to draw.\n */\nexport function stopProgress(\n rule: OvernightStopRule | null,\n run: OvernightRun | null,\n now: number = Date.now(),\n): StopProgress {\n if (rule === null) {\n return {\n headline: 'Stop rule not stated',\n remaining: UNKNOWN,\n percent: null,\n reached: false,\n };\n }\n\n if (rule.kind === 'until-paused') {\n return {\n headline: 'Runs until you pause it',\n remaining: 'no stop time, no iteration limit',\n percent: null,\n reached: false,\n };\n }\n\n if (rule.kind === 'at-time') {\n const at = Date.parse(rule.at);\n if (Number.isNaN(at)) {\n return { headline: 'Stop time not readable', remaining: UNKNOWN, percent: null, reached: false };\n }\n const headline = `Stops at ${formatStopClock(rule.at)}`;\n const left = at - now;\n if (left <= 0) {\n return { headline, remaining: 'stop time reached', percent: 100, reached: true };\n }\n const startedAt = run?.startedAt ? Date.parse(run.startedAt) : Number.NaN;\n // Elapsed over the run's whole window — which needs a start. Without one\n // the countdown is still true, so it is shown; only the bar is withheld.\n const percent =\n Number.isNaN(startedAt) || at <= startedAt\n ? null\n : Math.min(100, Math.max(0, Math.round(((now - startedAt) / (at - startedAt)) * 100)));\n return { headline, remaining: `${formatDuration(left)} left`, percent, reached: false };\n }\n\n const done = run?.iterationsDone;\n const headline = `Stops after ${rule.iterations} ${rule.iterations === 1 ? 'iteration' : 'iterations'}`;\n if (typeof done !== 'number') {\n return {\n headline,\n remaining: `${UNKNOWN} of ${rule.iterations} done`,\n percent: null,\n reached: false,\n };\n }\n return {\n headline,\n remaining: `${done} of ${rule.iterations} done`,\n percent: Math.min(100, Math.round((done / rule.iterations) * 100)),\n reached: done >= rule.iterations,\n };\n}\n\n/** \"3h 04m\" since the run started, or UNKNOWN when it never said. */\nexport function formatRunElapsed(startedAt: string | null | undefined, now: number = Date.now()): string {\n if (!startedAt) return UNKNOWN;\n const ms = Date.parse(startedAt);\n if (Number.isNaN(ms)) return UNKNOWN;\n return formatDuration(Math.max(0, now - ms));\n}\n\n/**\n * The checks named as one phrase: \"tests, lint and typecheck\".\n *\n * Only checks the server said `true` about are named. A `null` check is not\n * named as running AND not named as skipped — `gateCaveat` below says so in\n * words instead, because a gate an operator believes is running when it is not\n * is the single most expensive misreading this panel could cause.\n */\nexport function gateChecks(gate: OvernightGate | null): string[] {\n if (gate === null) return [];\n const named: string[] = [];\n if (gate.tests === true) named.push('tests');\n if (gate.lint === true) named.push('lint');\n if (gate.typecheck === true) named.push('typecheck');\n return named;\n}\n\n/** The checks the server would not commit to, if any. */\nexport function gateUnstated(gate: OvernightGate | null): string[] {\n if (gate === null) return ['tests', 'lint', 'typecheck'];\n const unstated: string[] = [];\n if (gate.tests === null) unstated.push('tests');\n if (gate.lint === null) unstated.push('lint');\n if (gate.typecheck === null) unstated.push('typecheck');\n return unstated;\n}\n\nexport function joinPhrase(parts: readonly string[]): string {\n if (parts.length === 0) return '';\n if (parts.length === 1) return parts[0]!;\n return `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]!}`;\n}\n","/**\n * routes/verse/autonomy/overnight-queries.ts — the one read and the one write\n * behind the Overnight panel: `GET /api/verse/overnight` and\n * `POST /api/verse/overnight`.\n *\n * Same contract as `control-queries.ts` and `fleet-queries.ts` — the read is a\n * QueryDef consumed through `useQuery` (cache-backed, so a poll never blanks a\n * populated panel); the write pulls the held mutation token from auth-store,\n * touches the hold on success, and invalidates exactly the keys it affects.\n * There is no new data layer here on purpose.\n *\n * TWO THINGS THAT ARE DELIBERATE:\n *\n * 1. **The read does not throw on absence.** The route does not exist yet —\n * four owners are building the engine, the permit, the budgets and the\n * local-only policy in parallel. A server without it answers 404, and 404\n * means \"this panel has no source yet\", not \"Autonomy is broken\". It comes\n * back as `OptionalFleetRead` (reused rather than re-declared, because two\n * shapes for \"a read that is allowed to be absent\" would be one too many)\n * and the panel renders a designed not-available state from `reason`.\n * 401 still propagates: an expired read session is an unauthorized state\n * for the whole surface, not a missing panel.\n *\n * 2. **There is no pause route here, and there must not be one.** The halt\n * this panel offers is the DAEMON-SCOPED pause — `~/.ashlr/daemon.paused`,\n * via `runDaemonAction('pause')` in control-queries.ts. Disarming stops\n * arming the next run; pausing stops the loop that is running now, and\n * they are different questions. Neither is `stopDaemon()`, which is\n * `setKill(true)` and therefore the emergency stop wearing a quieter name\n * (see the header of DaemonControls.tsx).\n */\nimport { ApiError, apiGet, apiPost } from '../../../data/client.js';\nimport { getMutationToken, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { useRefetch } from '../../../data/hooks.js';\nimport { useEffect } from 'react';\nimport { VERSE_CONTROL_KEY, VerseControlLockedError } from './control-queries.js';\nimport type { OptionalFleetRead } from './fleet-contract.js';\nimport type {\n OvernightAction,\n OvernightActionResult,\n OvernightStatus,\n OvernightStopRule,\n} from './overnight-contract.js';\nimport { projectOvernight } from './overnight-model.js';\n\nexport const VERSE_OVERNIGHT_KEY = 'verse-overnight';\nexport const OVERNIGHT_PATH = '/api/verse/overnight';\n\nfunction describeAbsence(err: unknown): string {\n if (err instanceof ApiError) {\n if (err.status === 404) {\n return `This build does not expose ${OVERNIGHT_PATH}, so an overnight run cannot be armed from here. It is a missing route, not a run that refused to start.`;\n }\n return `${OVERNIGHT_PATH} answered HTTP ${err.status}.`;\n }\n return `${OVERNIGHT_PATH} could not be reached.`;\n}\n\n/**\n * `GET /api/verse/overnight` — is a run armed, and what has it done.\n *\n * `value` is null both when the route was absent and when it answered\n * something this client could not narrow; `available` and `reason` are what\n * tell those two apart, and the panel words them differently.\n */\nexport const overnightQuery: QueryDef<OptionalFleetRead<OvernightStatus>> = {\n key: VERSE_OVERNIGHT_KEY,\n fetch: async (signal) => {\n try {\n const raw = await apiGet<unknown>(OVERNIGHT_PATH, signal);\n const value = projectOvernight(raw);\n if (value === null) {\n return {\n value: null,\n available: true,\n reason: `${OVERNIGHT_PATH} answered in a shape this client does not recognise, so nothing is shown rather than guessed.`,\n };\n }\n return { value, available: true, reason: null };\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) throw err;\n if (err instanceof DOMException && err.name === 'AbortError') throw err;\n return { value: null, available: false, reason: describeAbsence(err) };\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Writes\n// ---------------------------------------------------------------------------\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new VerseControlLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\nfunction invalidateOvernight(): void {\n invalidate(VERSE_OVERNIGHT_KEY);\n // Arming changes what the daemon will do next, and the control snapshot is\n // what the rest of the cockpit reads that from.\n invalidate(VERSE_CONTROL_KEY);\n}\n\n/** Arm an overnight run with exactly one stop rule. */\nexport async function armOvernight(stopRule: OvernightStopRule): Promise<OvernightActionResult> {\n const body: OvernightAction = { action: 'arm', stopRule };\n const result = await post<OvernightActionResult>(OVERNIGHT_PATH, body);\n invalidateOvernight();\n return result;\n}\n\n/**\n * Disarm: no further overnight run starts.\n *\n * This is NOT the halt. It stops the arrangement, not the loop — a run already\n * in flight keeps going until it is paused. The panel says so at the click.\n */\nexport async function disarmOvernight(): Promise<OvernightActionResult> {\n const body: OvernightAction = { action: 'disarm' };\n const result = await post<OvernightActionResult>(OVERNIGHT_PATH, body);\n invalidateOvernight();\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Polling\n// ---------------------------------------------------------------------------\n\n/**\n * Slower than the fleet's 4s: an overnight run's counters move on the scale of\n * whole iterations, and the thing an operator watches second by second (the\n * elapsed clock and the countdown) is derived locally from `useNow`.\n */\nexport const OVERNIGHT_POLL_MS = 15_000;\n\n/**\n * Keep the overnight status live while a run is armed and the tab is visible.\n *\n * Only while armed: a disarmed panel has one boolean to report and polling it\n * every fifteen seconds forever would be pure waste. Hidden tabs stop the\n * timer and take one immediate reading when they come back, so a panel\n * returned to in the morning is current on the first frame.\n */\nexport function useOvernightPolling(armed: boolean, intervalMs: number = OVERNIGHT_POLL_MS): void {\n const refetch = useRefetch(overnightQuery);\n\n useEffect(() => {\n if (!armed) return;\n let timer: ReturnType<typeof setInterval> | null = null;\n\n const start = (): void => {\n if (timer !== null) return;\n timer = setInterval(refetch, intervalMs);\n };\n const stop = (): void => {\n if (timer === null) return;\n clearInterval(timer);\n timer = null;\n };\n const onVisibility = (): void => {\n if (document.visibilityState === 'hidden') {\n stop();\n return;\n }\n refetch();\n start();\n };\n\n if (document.visibilityState !== 'hidden') start();\n document.addEventListener('visibilitychange', onVisibility);\n return () => {\n stop();\n document.removeEventListener('visibilitychange', onVisibility);\n };\n }, [armed, intervalMs, refetch]);\n}\n"],"mappings":"+OAiCA,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,CAAK,CAC5E,CAEA,SAAS,EAAI,EAA+B,CAC1C,OAAO,OAAO,GAAU,UAAY,EAAM,KAAK,IAAM,GAAK,EAAQ,IACpE,CAEA,SAAS,EAAK,EAAgC,CAC5C,OAAO,OAAO,GAAU,UAAY,EAAQ,IAC9C,CAGA,SAAS,EAAM,EAA+B,CAE5C,OADI,OAAO,GAAU,UAAY,CAAC,OAAO,SAAS,CAAK,GAAK,EAAQ,EAAU,KACvE,KAAK,MAAM,CAAK,CACzB,CAEA,SAAgB,EAAgB,EAAwC,CACtE,GAAI,CAAC,EAAS,CAAG,EAAG,OAAO,KAC3B,IAAM,EAAO,EAAI,EAAI,IAAO,EAC5B,GAAI,IAAS,eAAgB,MAAO,CAAE,KAAM,cAAe,EAC3D,GAAI,IAAS,UAAW,CACtB,IAAM,EAAK,EAAI,EAAI,EAAK,EAIxB,OADI,IAAO,MAAQ,OAAO,MAAM,KAAK,MAAM,CAAE,CAAC,EAAU,KACjD,CAAE,KAAM,UAAW,IAAG,CAC/B,CACA,GAAI,IAAS,mBAAoB,CAC/B,IAAM,EAAa,EAAM,EAAI,UAAa,EAE1C,OADI,IAAe,MAAQ,GAAc,EAAU,KAC5C,CAAE,KAAM,mBAAoB,YAAW,CAChD,CACA,OAAO,IACT,CAEA,SAAS,EAAc,EAAgC,CACrD,GAAI,CAAC,MAAM,QAAQ,CAAG,EAAG,MAAO,CAAC,EACjC,IAAM,EAAwB,CAAC,EAC/B,IAAK,GAAM,CAAC,EAAO,KAAS,EAAI,QAAQ,EAAG,CACzC,GAAI,CAAC,EAAS,CAAI,EAAG,SACrB,IAAM,EAAO,EAAI,EAAK,IAAO,EACzB,IAAS,MACb,EAAI,KAAK,CACP,GAAI,EAAI,EAAK,EAAK,GAAK,GAAG,EAAK,GAAG,IAClC,OACA,MAAO,EAAI,EAAK,KAAQ,GAAK,kBAC7B,GAAI,EAAI,EAAK,EAAK,EAClB,OAAQ,EAAI,EAAK,MAAS,CAC5B,CAAC,CACH,CACA,OAAO,CACT,CAEA,SAAS,EAAgB,EAAkC,CACzD,GAAI,CAAC,MAAM,QAAQ,CAAG,EAAG,MAAO,CAAC,EACjC,IAAM,EAA0B,CAAC,EACjC,IAAK,GAAM,CAAC,EAAO,KAAS,EAAI,QAAQ,EAAG,CACzC,GAAI,CAAC,EAAS,CAAI,EAAG,SACrB,IAAM,EAAO,EAAI,EAAK,IAAO,EACzB,IAAS,MACb,EAAI,KAAK,CACP,GAAI,EAAI,EAAK,EAAK,GAAK,GAAG,EAAK,GAAG,IAClC,OACA,MAAO,EAAI,EAAK,KAAQ,GAAK,kBAC7B,GAAI,EAAI,EAAK,EAAK,EAIlB,OAAQ,EAAI,EAAK,MAAS,GAAK,oBACjC,CAAC,CACH,CACA,OAAO,CACT,CAEA,SAAS,EAAY,EAAoC,CAEvD,OADK,EAAS,CAAG,EACV,CACL,MAAO,EAAK,EAAI,KAAQ,EACxB,KAAM,EAAK,EAAI,IAAO,EACtB,UAAW,EAAK,EAAI,SAAY,EAChC,UAAW,EAAK,EAAI,SAAY,EAChC,OAAQ,EAAI,EAAI,MAAS,CAC3B,EAP2B,IAQ7B,CAEA,SAAS,EAAW,EAAmC,CACrD,GAAI,CAAC,EAAS,CAAG,EAAG,OAAO,KAC3B,IAAM,EAAQ,EAAI,EAAI,KAAQ,EAE9B,OADI,IAAU,KAAa,KACpB,CACL,QACA,UAAW,EAAI,EAAI,SAAY,EAC/B,SAAU,EAAgB,EAAI,QAAW,EACzC,eAAgB,EAAM,EAAI,cAAiB,EAC3C,KAAM,EAAI,EAAI,IAAO,EACrB,SAAU,EAAI,EAAI,QAAW,EAC7B,OAAQ,EAAc,EAAI,MAAS,EACnC,UAAW,EAAgB,EAAI,SAAY,CAC7C,CACF,CASA,SAAgB,EAAiB,EAAsC,CACrE,GAAI,CAAC,EAAS,CAAG,EAAG,OAAO,KAC3B,IAAM,EAAQ,EAAK,EAAI,KAAQ,EAE/B,OADI,IAAU,KAAa,KACpB,CACL,QACA,IAAK,EAAW,EAAI,GAAM,EAC1B,MAAO,EAAM,EAAI,KAAQ,EAGzB,GAAI,EAAI,UAAe,IAAA,GAAiD,CAAC,EAAtC,CAAE,QAAS,EAAM,EAAI,OAAU,CAAE,EACpE,KAAM,EAAY,EAAI,IAAO,CAC/B,CACF,CAoBA,SAAgB,EAAc,EAAe,EAAY,IAAI,KAAuB,CAClF,IAAM,EAAQ,sBAAsB,KAAK,EAAM,KAAK,CAAC,EACrD,GAAI,CAAC,EAAO,OAAO,KACnB,IAAM,EAAQ,OAAO,EAAM,EAAE,EACvB,EAAU,OAAO,EAAM,EAAE,EAE/B,GADI,CAAC,OAAO,UAAU,CAAK,GAAK,CAAC,OAAO,UAAU,CAAO,GACrD,EAAQ,GAAK,EAAQ,IAAM,EAAU,GAAK,EAAU,GAAI,OAAO,KACnE,IAAM,EAAK,IAAI,KAAK,CAAG,EAGvB,OAFA,EAAG,SAAS,EAAO,EAAS,EAAG,CAAC,EAC5B,EAAG,QAAQ,GAAK,EAAI,QAAQ,GAAG,EAAG,QAAQ,EAAG,QAAQ,EAAI,CAAC,EACvD,EAAG,YAAY,CACxB,CAGA,SAAgB,EAAgB,EAA4B,CAC1D,GAAI,CAAC,EAAK,MAAA,IACV,IAAM,EAAK,KAAK,MAAM,CAAG,EAEzB,OADI,OAAO,MAAM,CAAE,EAAG,IACf,IAAI,KAAK,CAAE,CAAC,CAAC,mBAAmB,IAAA,GAAW,CAAE,KAAM,UAAW,OAAQ,SAAU,CAAC,CAC1F,CAGA,SAAgB,EAAU,EAAoB,EAAY,IAAI,KAAiB,CAC7E,GAAI,CAAC,EAAK,MAAO,GACjB,IAAM,EAAK,KAAK,MAAM,CAAG,EAEzB,MADA,CAAI,OAAO,MAAM,CAAE,GACZ,IAAI,KAAK,CAAE,CAAC,CAAC,aAAa,IAAM,EAAI,aAAa,CAC1D,CAQA,SAAgB,EAAiB,EAAyB,EAAY,IAAI,KAAgB,CACxF,OAAQ,EAAK,KAAb,CACE,IAAK,eACH,MAAO,8DACT,IAAK,UACH,MAAO,YAAY,EAAgB,EAAK,EAAE,IAAI,EAAU,EAAK,GAAI,CAAG,EAAI,YAAc,GAAG,GAC3F,IAAK,mBACH,MAAO,eAAe,EAAK,WAAW,GAAG,EAAK,aAAe,EAAI,YAAc,aAAa,EAChG,CACF,CAoCA,SAAgB,EACd,EACA,EACA,EAAc,KAAK,IAAI,EACT,CACd,GAAI,IAAS,KACX,MAAO,CACL,SAAU,uBACV,UAAA,IACA,QAAS,KACT,QAAS,EACX,EAGF,GAAI,EAAK,OAAS,eAChB,MAAO,CACL,SAAU,0BACV,UAAW,mCACX,QAAS,KACT,QAAS,EACX,EAGF,GAAI,EAAK,OAAS,UAAW,CAC3B,IAAM,EAAK,KAAK,MAAM,EAAK,EAAE,EAC7B,GAAI,OAAO,MAAM,CAAE,EACjB,MAAO,CAAE,SAAU,yBAA0B,UAAA,IAAoB,QAAS,KAAM,QAAS,EAAM,EAEjG,IAAM,EAAW,YAAY,EAAgB,EAAK,EAAE,IAC9C,EAAO,EAAK,EAClB,GAAI,GAAQ,EACV,MAAO,CAAE,WAAU,UAAW,oBAAqB,QAAS,IAAK,QAAS,EAAK,EAEjF,IAAM,EAAY,GAAK,UAAY,KAAK,MAAM,EAAI,SAAS,EAAI,IAGzD,EACJ,OAAO,MAAM,CAAS,GAAK,GAAM,EAC7B,KACA,KAAK,IAAI,IAAK,KAAK,IAAI,EAAG,KAAK,OAAQ,EAAM,IAAc,EAAK,GAAc,GAAG,CAAC,CAAC,EACzF,MAAO,CAAE,WAAU,UAAW,GAAG,EAAe,CAAI,EAAE,OAAQ,UAAS,QAAS,EAAM,CACxF,CAEA,IAAM,EAAO,GAAK,eACZ,EAAW,eAAe,EAAK,WAAW,GAAG,EAAK,aAAe,EAAI,YAAc,eASzF,OARI,OAAO,GAAS,SAQb,CACL,WACA,UAAW,GAAG,EAAK,MAAM,EAAK,WAAW,OACzC,QAAS,KAAK,IAAI,IAAK,KAAK,MAAO,EAAO,EAAK,WAAc,GAAG,CAAC,EACjE,QAAS,GAAQ,EAAK,UACxB,EAZS,CACL,WACA,UAAW,QAAiB,EAAK,WAAW,OAC5C,QAAS,KACT,QAAS,EACX,CAQJ,CAGA,SAAgB,EAAiB,EAAsC,EAAc,KAAK,IAAI,EAAW,CACvG,GAAI,CAAC,EAAW,MAAA,IAChB,IAAM,EAAK,KAAK,MAAM,CAAS,EAE/B,OADI,OAAO,MAAM,CAAE,EAAG,IACf,EAAe,KAAK,IAAI,EAAG,EAAM,CAAE,CAAC,CAC7C,CAUA,SAAgB,EAAW,EAAsC,CAC/D,GAAI,IAAS,KAAM,MAAO,CAAC,EAC3B,IAAM,EAAkB,CAAC,EAIzB,OAHI,EAAK,QAAU,IAAM,EAAM,KAAK,OAAO,EACvC,EAAK,OAAS,IAAM,EAAM,KAAK,MAAM,EACrC,EAAK,YAAc,IAAM,EAAM,KAAK,WAAW,EAC5C,CACT,CAGA,SAAgB,EAAa,EAAsC,CACjE,GAAI,IAAS,KAAM,MAAO,CAAC,QAAS,OAAQ,WAAW,EACvD,IAAM,EAAqB,CAAC,EAI5B,OAHI,EAAK,QAAU,MAAM,EAAS,KAAK,OAAO,EAC1C,EAAK,OAAS,MAAM,EAAS,KAAK,MAAM,EACxC,EAAK,YAAc,MAAM,EAAS,KAAK,WAAW,EAC/C,CACT,CAEA,SAAgB,EAAW,EAAkC,CAG3D,OAFI,EAAM,SAAW,EAAU,GAC3B,EAAM,SAAW,EAAU,EAAM,GAC9B,GAAG,EAAM,MAAM,EAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,EAAM,EAAM,OAAS,IACtE,CCrTA,IAAa,EAAsB,kBACtB,EAAiB,uBAE9B,SAAS,EAAgB,EAAsB,CAO7C,OANI,aAAe,EACb,EAAI,SAAW,IACV,8BAA8B,EAAe,0GAE/C,GAAG,EAAe,iBAAiB,EAAI,OAAO,GAEhD,GAAG,EAAe,uBAC3B,CASA,IAAa,EAA+D,CAC1E,IAAK,EACL,MAAO,KAAO,IAAW,CACvB,GAAI,CAEF,IAAM,EAAQ,EAAiB,MADb,EAAgB,EAAgB,CAAM,CACtB,EAQlC,OAPI,IAAU,KACL,CACL,MAAO,KACP,UAAW,GACX,OAAQ,GAAG,EAAe,8FAC5B,EAEK,CAAE,QAAO,UAAW,GAAM,OAAQ,IAAK,CAChD,OAAS,EAAK,CAEZ,GADI,aAAe,GAAY,EAAI,SAAW,KAC1C,aAAe,cAAgB,EAAI,OAAS,aAAc,MAAM,EACpE,MAAO,CAAE,MAAO,KAAM,UAAW,GAAO,OAAQ,EAAgB,CAAG,CAAE,CACvE,CACF,CACF,EAMA,eAAe,EAAQ,EAAc,EAA2B,CAC9D,IAAM,EAAQ,EAAiB,EAC/B,GAAI,CAAC,EAAO,MAAM,IAAI,EACtB,IAAM,EAAS,MAAM,EAAW,EAAM,EAAM,CAAK,EAEjD,OADA,EAAkB,EACX,CACT,CAEA,SAAS,GAA4B,CACnC,EAAW,CAAmB,EAG9B,EAAW,CAAiB,CAC9B,CAGA,eAAsB,EAAa,EAA6D,CAE9F,IAAM,EAAS,MAAM,EAA4B,EAAgB,CADjC,OAAQ,MAAO,UACkB,CAAI,EAErE,OADA,EAAoB,EACb,CACT,CAQA,eAAsB,GAAkD,CAEtE,IAAM,EAAS,MAAM,EAA4B,EAAgB,CADjC,OAAQ,QACyB,CAAI,EAErE,OADA,EAAoB,EACb,CACT,CAWA,IAAa,EAAoB,KAUjC,SAAgB,EAAoB,EAAgB,EAAqB,EAAyB,CAChG,IAAM,EAAU,EAAW,CAAc,GAEzC,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,CAAC,EAAO,OACZ,IAAI,EAA+C,KAE7C,MAAoB,CACpB,IAAU,OACd,EAAQ,YAAY,EAAS,CAAU,EACzC,EACM,MAAmB,CACnB,IAAU,OACd,cAAc,CAAK,EACnB,EAAQ,KACV,EACM,MAA2B,CAC/B,GAAI,SAAS,kBAAoB,SAAU,CACzC,EAAK,EACL,MACF,CACA,EAAQ,EACR,EAAM,CACR,EAIA,OAFI,SAAS,kBAAoB,UAAU,EAAM,EACjD,SAAS,iBAAiB,mBAAoB,CAAY,MAC7C,CACX,EAAK,EACL,SAAS,oBAAoB,mBAAoB,CAAY,CAC/D,CACF,EAAG,CAAC,EAAO,EAAY,CAAO,CAAC,CACjC"}
1
+ {"version":3,"file":"overnight-queries-DGm3qL1b.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/autonomy/overnight-model.ts","../../../../../../src/web-ui/routes/verse/autonomy/overnight-queries.ts"],"sourcesContent":["/**\n * routes/verse/autonomy/overnight-model.ts — the pure half of the Overnight\n * panel: narrow the server's body, resolve the operator's stop rule, and\n * derive the one line that says how far the run is from stopping.\n *\n * Framework-free so it unit-tests without a DOM, and separate from the panel\n * because two of these functions are where the panel can most easily start\n * lying:\n *\n * - `projectOvernight` NARROWS, it does not cast. The route is being written\n * by another owner in parallel with this surface, so a field that drifts in\n * name or type has to degrade to `null` — which the panel renders as \"not\n * stated\" — rather than reach a render as `undefined` and crash, or reach\n * it as `0` and read as a fact. Same discipline as `fleet-model.ts`.\n * - `stopProgress` never invents a denominator. \"Until I pause it\" has no\n * progress, and drawing a bar for it would be a picture of a finish line\n * that does not exist.\n */\nimport { formatDuration, UNKNOWN } from './format.js';\nimport type {\n OvernightDiscard,\n OvernightGate,\n OvernightMerge,\n OvernightRun,\n OvernightStatus,\n OvernightStopRule,\n OvernightStopRuleKind,\n} from './overnight-contract.js';\n\n// ---------------------------------------------------------------------------\n// Narrowing\n// ---------------------------------------------------------------------------\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.trim() !== '' ? value : null;\n}\n\nfunction bool(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\n/** A count. Negative and non-finite are not counts, so they are not observed. */\nfunction count(value: unknown): number | null {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) return null;\n return Math.floor(value);\n}\n\nexport function projectStopRule(raw: unknown): OvernightStopRule | null {\n if (!isRecord(raw)) return null;\n const kind = str(raw['kind']);\n if (kind === 'until-paused') return { kind: 'until-paused' };\n if (kind === 'at-time') {\n const at = str(raw['at']);\n // An unparseable instant is worse than no rule: it would render as a\n // countdown to NaN. Refuse it here so the panel can say \"not stated\".\n if (at === null || Number.isNaN(Date.parse(at))) return null;\n return { kind: 'at-time', at };\n }\n if (kind === 'after-iterations') {\n const iterations = count(raw['iterations']);\n if (iterations === null || iterations <= 0) return null;\n return { kind: 'after-iterations', iterations };\n }\n return null;\n}\n\nfunction projectMerges(raw: unknown): OvernightMerge[] {\n if (!Array.isArray(raw)) return [];\n const out: OvernightMerge[] = [];\n for (const [index, item] of raw.entries()) {\n if (!isRecord(item)) continue;\n const repo = str(item['repo']);\n if (repo === null) continue;\n out.push({\n id: str(item['id']) ?? `${repo}:${index}`,\n repo,\n title: str(item['title']) ?? 'untitled change',\n at: str(item['at']),\n commit: str(item['commit']),\n });\n }\n return out;\n}\n\nfunction projectDiscards(raw: unknown): OvernightDiscard[] {\n if (!Array.isArray(raw)) return [];\n const out: OvernightDiscard[] = [];\n for (const [index, item] of raw.entries()) {\n if (!isRecord(item)) continue;\n const repo = str(item['repo']);\n if (repo === null) continue;\n out.push({\n id: str(item['id']) ?? `${repo}:${index}`,\n repo,\n title: str(item['title']) ?? 'untitled change',\n at: str(item['at']),\n // A discard with no stated reason is the one row an operator will ask\n // about in the morning, so it says that it has none rather than being\n // rendered as a blank cell.\n reason: str(item['reason']) ?? 'no reason recorded',\n });\n }\n return out;\n}\n\nfunction projectGate(raw: unknown): OvernightGate | null {\n if (!isRecord(raw)) return null;\n return {\n tests: bool(raw['tests']),\n lint: bool(raw['lint']),\n typecheck: bool(raw['typecheck']),\n autoMerge: bool(raw['autoMerge']),\n branch: str(raw['branch']),\n };\n}\n\nfunction projectRun(raw: unknown): OvernightRun | null {\n if (!isRecord(raw)) return null;\n const runId = str(raw['runId']);\n if (runId === null) return null;\n return {\n runId,\n startedAt: str(raw['startedAt']),\n stopRule: projectStopRule(raw['stopRule']),\n iterationsDone: count(raw['iterationsDone']),\n repo: str(raw['repo']),\n activity: str(raw['activity']),\n merged: projectMerges(raw['merged']),\n discarded: projectDiscards(raw['discarded']),\n };\n}\n\n/**\n * `GET /api/verse/overnight` → a status, or null when the body is not one.\n *\n * `armed` is the single field this refuses to guess: a body that does not say\n * whether a run is armed is not a status, because \"nothing is armed\" is\n * exactly the claim a person checks before closing the laptop.\n */\nexport function projectOvernight(raw: unknown): OvernightStatus | null {\n if (!isRecord(raw)) return null;\n const armed = bool(raw['armed']);\n if (armed === null) return null;\n return {\n armed,\n run: projectRun(raw['run']),\n repos: count(raw['repos']),\n // Present-but-invalid reads as null (\"not recorded\"); absent stays absent,\n // so an older server is not shown as having lost a count it never sent.\n ...(raw['mirrors'] !== undefined ? { mirrors: count(raw['mirrors']) } : {}),\n gate: projectGate(raw['gate']),\n };\n}\n\n// ---------------------------------------------------------------------------\n// The operator's side of the stop rule\n// ---------------------------------------------------------------------------\n\n/** Smallest and largest iteration cap the form will send. */\nexport const ITERATIONS_MIN = 1;\nexport const ITERATIONS_MAX = 500;\n\n/**\n * Turn an `<input type=\"time\">` value (\"23:30\") into the next occurrence of\n * that wall-clock time, as an absolute instant.\n *\n * The whole point of arming this at 11pm is that \"07:00\" means tomorrow\n * morning. Resolving it on the client, in the operator's own zone, is what\n * makes that unambiguous by the time it reaches a server that may not share\n * the zone — and rounds to the minute, so a run armed at 06:59:58 for 07:00\n * does not stop two seconds later.\n */\nexport function resolveStopAt(value: string, now: Date = new Date()): string | null {\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(value.trim());\n if (!match) return null;\n const hours = Number(match[1]);\n const minutes = Number(match[2]);\n if (!Number.isInteger(hours) || !Number.isInteger(minutes)) return null;\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;\n const at = new Date(now);\n at.setHours(hours, minutes, 0, 0);\n if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 1);\n return at.toISOString();\n}\n\n/** \"07:00\" as the operator's locale writes it — never a raw ISO string. */\nexport function formatStopClock(iso: string | null): string {\n if (!iso) return UNKNOWN;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return UNKNOWN;\n return new Date(ms).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n}\n\n/** True when the resolved instant is not today — \"07:00 tomorrow\". */\nexport function isNextDay(iso: string | null, now: Date = new Date()): boolean {\n if (!iso) return false;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return false;\n return new Date(ms).toDateString() !== now.toDateString();\n}\n\n/**\n * The sentence the Arm button is committing to, before the click.\n *\n * It restates the rule the operator chose in the operator's own units, so the\n * button is never the only description of what is about to be authorised.\n */\nexport function describeStopRule(rule: OvernightStopRule, now: Date = new Date()): string {\n switch (rule.kind) {\n case 'until-paused':\n return 'Runs until you pause it — no stop time, no iteration limit.';\n case 'at-time':\n return `Stops at ${formatStopClock(rule.at)}${isNextDay(rule.at, now) ? ' tomorrow' : ''}.`;\n case 'after-iterations':\n return `Stops after ${rule.iterations} ${rule.iterations === 1 ? 'iteration' : 'iterations'}.`;\n }\n}\n\n/** The short label each stop rule wears in the armed summary. */\nexport function stopRuleLabel(kind: OvernightStopRuleKind): string {\n switch (kind) {\n case 'until-paused':\n return 'Until paused';\n case 'at-time':\n return 'At a set time';\n case 'after-iterations':\n return 'After N iterations';\n }\n}\n\n// ---------------------------------------------------------------------------\n// How far to the stop condition\n// ---------------------------------------------------------------------------\n\nexport interface StopProgress {\n /** The rule, restated: \"Stops at 07:00\". */\n headline: string;\n /** How much is left: \"4h 12m left\", \"12 of 20 done\". */\n remaining: string;\n /** 0-100 for a meter, or null when there is no honest denominator. */\n percent: number | null;\n /** True once the condition is met and the loop should be winding down. */\n reached: boolean;\n}\n\n/**\n * How far the run is from its stop condition.\n *\n * `percent` is null for \"until I pause it\" on purpose: an open-ended run has\n * no finish line, and a bar with no denominator is the exact kind of confident\n * graphic this cockpit is not allowed to draw.\n */\nexport function stopProgress(\n rule: OvernightStopRule | null,\n run: OvernightRun | null,\n now: number = Date.now(),\n): StopProgress {\n if (rule === null) {\n return {\n headline: 'Stop rule not stated',\n remaining: UNKNOWN,\n percent: null,\n reached: false,\n };\n }\n\n if (rule.kind === 'until-paused') {\n return {\n headline: 'Runs until you pause it',\n remaining: 'no stop time, no iteration limit',\n percent: null,\n reached: false,\n };\n }\n\n if (rule.kind === 'at-time') {\n const at = Date.parse(rule.at);\n if (Number.isNaN(at)) {\n return { headline: 'Stop time not readable', remaining: UNKNOWN, percent: null, reached: false };\n }\n const headline = `Stops at ${formatStopClock(rule.at)}`;\n const left = at - now;\n if (left <= 0) {\n return { headline, remaining: 'stop time reached', percent: 100, reached: true };\n }\n const startedAt = run?.startedAt ? Date.parse(run.startedAt) : Number.NaN;\n // Elapsed over the run's whole window — which needs a start. Without one\n // the countdown is still true, so it is shown; only the bar is withheld.\n const percent =\n Number.isNaN(startedAt) || at <= startedAt\n ? null\n : Math.min(100, Math.max(0, Math.round(((now - startedAt) / (at - startedAt)) * 100)));\n return { headline, remaining: `${formatDuration(left)} left`, percent, reached: false };\n }\n\n const done = run?.iterationsDone;\n const headline = `Stops after ${rule.iterations} ${rule.iterations === 1 ? 'iteration' : 'iterations'}`;\n if (typeof done !== 'number') {\n return {\n headline,\n remaining: `${UNKNOWN} of ${rule.iterations} done`,\n percent: null,\n reached: false,\n };\n }\n return {\n headline,\n remaining: `${done} of ${rule.iterations} done`,\n percent: Math.min(100, Math.round((done / rule.iterations) * 100)),\n reached: done >= rule.iterations,\n };\n}\n\n/** \"3h 04m\" since the run started, or UNKNOWN when it never said. */\nexport function formatRunElapsed(startedAt: string | null | undefined, now: number = Date.now()): string {\n if (!startedAt) return UNKNOWN;\n const ms = Date.parse(startedAt);\n if (Number.isNaN(ms)) return UNKNOWN;\n return formatDuration(Math.max(0, now - ms));\n}\n\n/**\n * The checks named as one phrase: \"tests, lint and typecheck\".\n *\n * Only checks the server said `true` about are named. A `null` check is not\n * named as running AND not named as skipped — `gateCaveat` below says so in\n * words instead, because a gate an operator believes is running when it is not\n * is the single most expensive misreading this panel could cause.\n */\nexport function gateChecks(gate: OvernightGate | null): string[] {\n if (gate === null) return [];\n const named: string[] = [];\n if (gate.tests === true) named.push('tests');\n if (gate.lint === true) named.push('lint');\n if (gate.typecheck === true) named.push('typecheck');\n return named;\n}\n\n/** The checks the server would not commit to, if any. */\nexport function gateUnstated(gate: OvernightGate | null): string[] {\n if (gate === null) return ['tests', 'lint', 'typecheck'];\n const unstated: string[] = [];\n if (gate.tests === null) unstated.push('tests');\n if (gate.lint === null) unstated.push('lint');\n if (gate.typecheck === null) unstated.push('typecheck');\n return unstated;\n}\n\nexport function joinPhrase(parts: readonly string[]): string {\n if (parts.length === 0) return '';\n if (parts.length === 1) return parts[0]!;\n return `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]!}`;\n}\n","/**\n * routes/verse/autonomy/overnight-queries.ts — the one read and the one write\n * behind the Overnight panel: `GET /api/verse/overnight` and\n * `POST /api/verse/overnight`.\n *\n * Same contract as `control-queries.ts` and `fleet-queries.ts` — the read is a\n * QueryDef consumed through `useQuery` (cache-backed, so a poll never blanks a\n * populated panel); the write pulls the held mutation token from auth-store,\n * touches the hold on success, and invalidates exactly the keys it affects.\n * There is no new data layer here on purpose.\n *\n * TWO THINGS THAT ARE DELIBERATE:\n *\n * 1. **The read does not throw on absence.** The route does not exist yet —\n * four owners are building the engine, the permit, the budgets and the\n * local-only policy in parallel. A server without it answers 404, and 404\n * means \"this panel has no source yet\", not \"Autonomy is broken\". It comes\n * back as `OptionalFleetRead` (reused rather than re-declared, because two\n * shapes for \"a read that is allowed to be absent\" would be one too many)\n * and the panel renders a designed not-available state from `reason`.\n * 401 still propagates: an expired read session is an unauthorized state\n * for the whole surface, not a missing panel.\n *\n * 2. **There is no pause route here, and there must not be one.** The halt\n * this panel offers is the DAEMON-SCOPED pause — `~/.ashlr/daemon.paused`,\n * via `runDaemonAction('pause')` in control-queries.ts. Disarming stops\n * arming the next run; pausing stops the loop that is running now, and\n * they are different questions. Neither is `stopDaemon()`, which is\n * `setKill(true)` and therefore the emergency stop wearing a quieter name\n * (see the header of DaemonControls.tsx).\n */\nimport { ApiError, apiGet, apiPost } from '../../../data/client.js';\nimport { getMutationToken, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { useRefetch } from '../../../data/hooks.js';\nimport { useEffect } from 'react';\nimport { VERSE_CONTROL_KEY, VerseControlLockedError } from './control-queries.js';\nimport type { OptionalFleetRead } from './fleet-contract.js';\nimport type {\n OvernightAction,\n OvernightActionResult,\n OvernightStatus,\n OvernightStopRule,\n} from './overnight-contract.js';\nimport { projectOvernight } from './overnight-model.js';\n\nexport const VERSE_OVERNIGHT_KEY = 'verse-overnight';\nexport const OVERNIGHT_PATH = '/api/verse/overnight';\n\nfunction describeAbsence(err: unknown): string {\n if (err instanceof ApiError) {\n if (err.status === 404) {\n return `This build does not expose ${OVERNIGHT_PATH}, so an overnight run cannot be armed from here. It is a missing route, not a run that refused to start.`;\n }\n return `${OVERNIGHT_PATH} answered HTTP ${err.status}.`;\n }\n return `${OVERNIGHT_PATH} could not be reached.`;\n}\n\n/**\n * `GET /api/verse/overnight` — is a run armed, and what has it done.\n *\n * `value` is null both when the route was absent and when it answered\n * something this client could not narrow; `available` and `reason` are what\n * tell those two apart, and the panel words them differently.\n */\nexport const overnightQuery: QueryDef<OptionalFleetRead<OvernightStatus>> = {\n key: VERSE_OVERNIGHT_KEY,\n fetch: async (signal) => {\n try {\n const raw = await apiGet<unknown>(OVERNIGHT_PATH, signal);\n const value = projectOvernight(raw);\n if (value === null) {\n return {\n value: null,\n available: true,\n reason: `${OVERNIGHT_PATH} answered in a shape this client does not recognise, so nothing is shown rather than guessed.`,\n };\n }\n return { value, available: true, reason: null };\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) throw err;\n if (err instanceof DOMException && err.name === 'AbortError') throw err;\n return { value: null, available: false, reason: describeAbsence(err) };\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Writes\n// ---------------------------------------------------------------------------\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new VerseControlLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\nfunction invalidateOvernight(): void {\n invalidate(VERSE_OVERNIGHT_KEY);\n // Arming changes what the daemon will do next, and the control snapshot is\n // what the rest of the cockpit reads that from.\n invalidate(VERSE_CONTROL_KEY);\n}\n\n/** Arm an overnight run with exactly one stop rule. */\nexport async function armOvernight(stopRule: OvernightStopRule): Promise<OvernightActionResult> {\n const body: OvernightAction = { action: 'arm', stopRule };\n const result = await post<OvernightActionResult>(OVERNIGHT_PATH, body);\n invalidateOvernight();\n return result;\n}\n\n/**\n * Disarm: no further overnight run starts.\n *\n * This is NOT the halt. It stops the arrangement, not the loop — a run already\n * in flight keeps going until it is paused. The panel says so at the click.\n */\nexport async function disarmOvernight(): Promise<OvernightActionResult> {\n const body: OvernightAction = { action: 'disarm' };\n const result = await post<OvernightActionResult>(OVERNIGHT_PATH, body);\n invalidateOvernight();\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Polling\n// ---------------------------------------------------------------------------\n\n/**\n * Slower than the fleet's 4s: an overnight run's counters move on the scale of\n * whole iterations, and the thing an operator watches second by second (the\n * elapsed clock and the countdown) is derived locally from `useNow`.\n */\nexport const OVERNIGHT_POLL_MS = 15_000;\n\n/**\n * Keep the overnight status live while a run is armed and the tab is visible.\n *\n * Only while armed: a disarmed panel has one boolean to report and polling it\n * every fifteen seconds forever would be pure waste. Hidden tabs stop the\n * timer and take one immediate reading when they come back, so a panel\n * returned to in the morning is current on the first frame.\n */\nexport function useOvernightPolling(armed: boolean, intervalMs: number = OVERNIGHT_POLL_MS): void {\n const refetch = useRefetch(overnightQuery);\n\n useEffect(() => {\n if (!armed) return;\n let timer: ReturnType<typeof setInterval> | null = null;\n\n const start = (): void => {\n if (timer !== null) return;\n timer = setInterval(refetch, intervalMs);\n };\n const stop = (): void => {\n if (timer === null) return;\n clearInterval(timer);\n timer = null;\n };\n const onVisibility = (): void => {\n if (document.visibilityState === 'hidden') {\n stop();\n return;\n }\n refetch();\n start();\n };\n\n if (document.visibilityState !== 'hidden') start();\n document.addEventListener('visibilitychange', onVisibility);\n return () => {\n stop();\n document.removeEventListener('visibilitychange', onVisibility);\n };\n }, [armed, intervalMs, refetch]);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{a as e,n as t,s as n}from"./index-BVxOlseC.js";var r=n(e(),1),i=t();function a(e){let t=null,n=null,a=()=>(n??=e().then(e=>t=e,e=>{throw n=null,e}),n),o=(0,r.lazy)(()=>a().then(e=>({default:e})));function s(e){let[n]=(0,r.useState)(()=>t);return n?(0,i.jsx)(n,{...e}):(0,i.jsx)(o,{...e})}return typeof window<`u`&&a().catch(()=>void 0),{Slot:s,ready:a}}function o(e){let t=null,n=null,i=()=>(n??=e().then(e=>t=e,e=>{throw n=null,e}),n);function a(){let[e,n]=(0,r.useState)(()=>({current:t}));return(0,r.useEffect)(()=>{if(e.current)return;let t=!0;return i().then(e=>{t&&n({current:e})},()=>void 0),()=>{t=!1}},[e]),e.current}return typeof window<`u`&&i().catch(()=>void 0),{ready:i,useLoaded:a}}export{o as n,a as t};
2
+ //# sourceMappingURL=preloaded-Cwf9rsnZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preloaded-Cwf9rsnZ.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/preloaded.tsx"],"sourcesContent":["/**\n * routes/verse/shell/preloaded.tsx — chunks that start downloading when the\n * module DECLARING them evaluates, and render synchronously once they are in.\n *\n * Moved out of sections/ChatSection.tsx so the shell (guarded-action.tsx)\n * can keep its dialogs off the chat first-paint path the same way the Chat\n * section keeps its workspace, chat list and dialogs off it. A preloaded\n * dialog that has landed mounts in the same render that opens it, exactly as\n * a static import would; one opened in the few milliseconds before its chunk\n * lands shows a moment later.\n */\nimport { lazy, useEffect, useState, type ComponentType } from 'react';\n\n/**\n * A component in its own chunk whose download starts the moment the module\n * declaring it evaluates — not when React first renders it — so it streams in\n * parallel with the chat's first paint instead of after it.\n *\n * WHY (SPEC-310A §1: chat first-paint critical JS ≤ 350 KB, measured by\n * scripts/check-first-paint-budget.mjs). React + react-dom alone are ~220 KB\n * of that; the chat list, the transcript, the composer and the markdown\n * renderer were another ~290 KB in this chunk. The section's frame (its\n * grid, the banners, every mutation and key handler) paints from this chunk;\n * the workspace and the chat list paint a beat later from theirs, over\n * skeletons in their own shape so nothing jumps.\n *\n * Once the module is in, every later mount renders it synchronously (no\n * Suspense flash when you come back to Chat). Which of the two a mount uses\n * is fixed for that mount's life: swapping the lazy wrapper for the loaded\n * component mid-life would remount the subtree and drop its state — the\n * composer's draft, the transcript's scroll.\n */\nexport function preloadedLazy<P extends object>(load: () => Promise<ComponentType<P>>): { Slot: ComponentType<P>; ready: () => Promise<ComponentType<P>> } {\n let loaded: ComponentType<P> | null = null;\n let pending: Promise<ComponentType<P>> | null = null;\n const ready = (): Promise<ComponentType<P>> => {\n // A failed download is not cached: the next mount (or retry) asks again.\n pending ??= load().then((c) => (loaded = c), (err: unknown) => { pending = null; throw err; });\n return pending;\n };\n const Lazy = lazy(() => ready().then((c) => ({ default: c })));\n function Slot(props: P) {\n const [Ready] = useState<ComponentType<P> | null>(() => loaded);\n return Ready ? <Ready {...props} /> : <Lazy {...props} />;\n }\n if (typeof window !== 'undefined') void ready().catch(() => undefined);\n return { Slot, ready };\n}\n\n/**\n * A module (not a component) on the same terms as preloadedLazy: fetched when\n * the declaring module evaluates, read synchronously once in. useLoaded() re-renders\n * its caller once, when it lands; a failed load leaves it null (the values it\n * would have computed stay empty) and is retried on the next mount.\n */\nexport function preloadedModule<M>(load: () => Promise<M>): { ready: () => Promise<M>; useLoaded: () => M | null } {\n let loaded: M | null = null;\n let pending: Promise<M> | null = null;\n const ready = (): Promise<M> => {\n pending ??= load().then((m) => (loaded = m), (err: unknown) => { pending = null; throw err; });\n return pending;\n };\n function useLoaded(): M | null {\n const [mod, setMod] = useState<{ current: M | null }>(() => ({ current: loaded }));\n useEffect(() => {\n if (mod.current) return undefined;\n let live = true;\n ready().then((m) => { if (live) setMod({ current: m }); }, () => undefined);\n return () => { live = false; };\n }, [mod]);\n return mod.current;\n }\n if (typeof window !== 'undefined') void ready().catch(() => undefined);\n return { ready, useLoaded };\n}\n"],"mappings":"2EAgCA,SAAgB,EAAgC,EAA2G,CACzJ,IAAI,EAAkC,KAClC,EAA4C,KAC1C,OAEJ,IAAY,EAAK,CAAC,CAAC,KAAM,GAAO,EAAS,EAAK,GAAiB,CAAkB,KAAhB,GAAU,KAAY,CAAK,CAAC,EACtF,GAEH,GAAA,EAAO,EAAA,KAAA,KAAW,EAAM,CAAC,CAAC,KAAM,IAAO,CAAE,QAAS,CAAE,EAAE,CAAC,EAC7D,SAAS,EAAK,EAAU,CACtB,GAAM,CAAC,IAAA,EAAS,EAAA,SAAA,KAAwC,CAAM,EAC9D,OAAO,GAAQ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAO,GAAI,CAAQ,CAAA,GAAI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,GAAI,CAAQ,CAAA,CAC1D,CAEA,OADI,OAAO,OAAW,KAAa,EAAW,CAAC,CAAC,UAAY,IAAA,EAAS,EAC9D,CAAE,OAAM,OAAM,CACvB,CAQA,SAAgB,EAAmB,EAAgF,CACjH,IAAI,EAAmB,KACnB,EAA6B,KAC3B,OACJ,IAAY,EAAK,CAAC,CAAC,KAAM,GAAO,EAAS,EAAK,GAAiB,CAAkB,KAAhB,GAAU,KAAY,CAAK,CAAC,EACtF,GAET,SAAS,GAAsB,CAC7B,GAAM,CAAC,EAAK,IAAA,EAAU,EAAA,SAAA,MAAuC,CAAE,QAAS,CAAO,EAAE,EAOjF,OANA,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,EAAI,QAAS,OACjB,IAAI,EAAO,GAEX,OADA,EAAM,CAAC,CAAC,KAAM,GAAM,CAAM,GAAM,EAAO,CAAE,QAAS,CAAE,CAAC,CAAG,MAAS,IAAA,EAAS,MAC7D,CAAE,EAAO,EAAO,CAC/B,EAAG,CAAC,CAAG,CAAC,EACD,EAAI,OACb,CAEA,OADI,OAAO,OAAW,KAAa,EAAW,CAAC,CAAC,UAAY,IAAA,EAAS,EAC9D,CAAE,QAAO,WAAU,CAC5B"}
@@ -0,0 +1 @@
1
+ import"./global-oEl9Qj8u.js";import"./focus-trap-HtcoXeEq.js";import"./Dialog-BS9Dxi3G.js";import"./Epistemic-dhvDTwAZ.js";import"./StatusBadge-HCZp416_.js";import"./RouteErrorBoundary-DYtwc2ja.js";import"./RefreshIndicator-CMwSCiz5.js";import"./VerseConsoleApp-DTjDpGCd.js";import"./Segmented-BE53BenI.js";import"./Tag-DH15CUJp.js";import"./Button-HHXgoXGr.js";import"./EmptyState-BqR5YAmf.js";import"./Meter-DNeocJnD.js";import"./Slider-BypUWjpH.js";import"./Switch-iveI-8NY.js";import"./Select-BwSGkHCS.js";import"./Input-B5dmGLZx.js";import"./Sheet-DVP2Mgy6.js";
@@ -1,2 +1,2 @@
1
- import{l as e}from"./global-Di2PUgj4.js";var t={key:`dashboard-snapshot`,fetch:t=>e(`/api/snapshot`,t)},n={key:`control-snapshot`,fetch:t=>e(`/api/control`,t)},r={key:`agent-os-snapshot`,fetch:t=>e(`/api/agent-os`,t)},i={key:`universe-overview`,fetch:t=>e(`/api/universe`,t)};function a(t){return{key:`universe-graph-${t}`,fetch:n=>e(`/api/universe/graph?universeId=${encodeURIComponent(t)}`,n)}}var o={key:`runs`,fetch:t=>e(`/api/runs`,t)};function s(t){return{key:`run-detail-${t}`,fetch:n=>e(`/api/run/${encodeURIComponent(t)}`,n)}}var c={key:`swarms`,fetch:t=>e(`/api/swarms`,t)};function l(t){return{key:`swarm-detail-${t}`,fetch:n=>e(`/api/swarm/${encodeURIComponent(t)}`,n)}}var u={key:`daemon`,fetch:t=>e(`/api/daemon-observation`,t)},d={key:`fleet`,fetch:t=>e(`/api/fleet`,t)},f={key:`fleet-activity`,fetch:t=>e(`/api/fleet-activity`,t)},p={key:`goals`,fetch:t=>e(`/api/goals`,t)},m={key:`vision-mission`,fetch:t=>e(`/api/vision/mission`,t)},h={key:`inbox`,fetch:t=>e(`/api/inbox`,t)};function g(t={}){let n=new URLSearchParams;t.status!==void 0&&n.set(`status`,t.status),t.since!==void 0&&n.set(`since`,t.since),t.limit!==void 0&&n.set(`limit`,String(t.limit));let r=n.toString();return{key:`inbox-list:${t.status??`pending`}:${t.since??``}:${t.limit??``}`,fetch:t=>e(`/api/inbox${r?`?${r}`:``}`,t)}}function _(t){return{key:`proposal-detail-${t}`,fetch:n=>e(`/api/inbox/${encodeURIComponent(t)}`,n)}}var v={key:`control-logs-observation`,fetch:t=>e(`/api/logs-observation?tail=200`,t)};function y(t){return{key:`pulse-${t}`,fetch:n=>e(`/api/pulse?window=${t}`,n)}}function b(e){return typeof e==`object`&&!!e&&`entry`in e&&`score`in e}function x(t){let n=t.trim();return{key:n?`genome-search-${n}`:`genome-list`,fetch:t=>e(n?`/api/genome?q=${encodeURIComponent(n)}`:`/api/genome`,t)}}function S(t){return{key:`models-${t}`,fetch:n=>e(`/api/models?window=${t}`,n)}}var C={key:`portfolio`,fetch:t=>e(`/api/portfolio`,t)};export{m as C,i as S,s as _,t as a,c as b,x as c,h as d,b as f,y as g,_ as h,u as i,p as l,C as m,v as n,f as o,S as p,n as r,d as s,r as t,g as u,o as v,a as x,l as y};
2
- //# sourceMappingURL=queries-BA4rNGD-.js.map
1
+ import{c as e}from"./global-oEl9Qj8u.js";var t={key:`dashboard-snapshot`,fetch:t=>e(`/api/snapshot`,t)},n={key:`control-snapshot`,fetch:t=>e(`/api/control`,t)},r={key:`agent-os-snapshot`,fetch:t=>e(`/api/agent-os`,t)},i={key:`universe-overview`,fetch:t=>e(`/api/universe`,t)};function a(t){return{key:`universe-graph-${t}`,fetch:n=>e(`/api/universe/graph?universeId=${encodeURIComponent(t)}`,n)}}var o={key:`runs`,fetch:t=>e(`/api/runs`,t)};function s(t){return{key:`run-detail-${t}`,fetch:n=>e(`/api/run/${encodeURIComponent(t)}`,n)}}var c={key:`swarms`,fetch:t=>e(`/api/swarms`,t)};function l(t){return{key:`swarm-detail-${t}`,fetch:n=>e(`/api/swarm/${encodeURIComponent(t)}`,n)}}var u={key:`daemon`,fetch:t=>e(`/api/daemon-observation`,t)},d={key:`fleet`,fetch:t=>e(`/api/fleet`,t)},f={key:`fleet-activity`,fetch:t=>e(`/api/fleet-activity`,t)},p={key:`goals`,fetch:t=>e(`/api/goals`,t)},m={key:`vision-mission`,fetch:t=>e(`/api/vision/mission`,t)},h={key:`inbox`,fetch:t=>e(`/api/inbox`,t)};function g(t={}){let n=new URLSearchParams;t.status!==void 0&&n.set(`status`,t.status),t.since!==void 0&&n.set(`since`,t.since),t.limit!==void 0&&n.set(`limit`,String(t.limit));let r=n.toString();return{key:`inbox-list:${t.status??`pending`}:${t.since??``}:${t.limit??``}`,fetch:t=>e(`/api/inbox${r?`?${r}`:``}`,t)}}function _(t){return{key:`proposal-detail-${t}`,fetch:n=>e(`/api/inbox/${encodeURIComponent(t)}`,n)}}var v={key:`control-logs-observation`,fetch:t=>e(`/api/logs-observation?tail=200`,t)};function y(t){return{key:`pulse-${t}`,fetch:n=>e(`/api/pulse?window=${t}`,n)}}function b(e){return typeof e==`object`&&!!e&&`entry`in e&&`score`in e}function x(t){let n=t.trim();return{key:n?`genome-search-${n}`:`genome-list`,fetch:t=>e(n?`/api/genome?q=${encodeURIComponent(n)}`:`/api/genome`,t)}}function S(t){return{key:`models-${t}`,fetch:n=>e(`/api/models?window=${t}`,n)}}var C={key:`portfolio`,fetch:t=>e(`/api/portfolio`,t)};export{m as C,i as S,s as _,t as a,c as b,x as c,h as d,b as f,y as g,_ as h,u as i,p as l,C as m,v as n,f as o,S as p,n as r,d as s,r as t,g as u,o as v,a as x,l as y};
2
+ //# sourceMappingURL=queries-CYisSAAB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queries-BA4rNGD-.js","names":[],"sources":["../../../../../../src/web-ui/data/queries.ts"],"sourcesContent":["/**\n * data/queries.ts — one typed accessor per backend resource. Each pairs a\n * stable cache key (also the SSE invalidation target — see sse.ts's\n * EVENT_TO_CACHE_KEYS) with the exact GET call and response type. Views\n * never call apiGet directly; they call these through useQuery (hooks.ts)\n * so caching, de-dupe, and SSE-driven refresh are automatic.\n */\nimport { apiGet } from './client.js';\nimport type { UniverseGraph } from '../../core/universe/graph-types.js';\nimport type {\n AgentOsSnapshotResponse,\n UniverseOverview,\n ControlLogEntry,\n ControlSnapshot,\n DashboardSnapshot,\n DecisionEntry,\n FleetActivitySnapshot,\n FleetStatus,\n Proposal,\n ProposalStatus,\n PublicDaemonObservation,\n RunState,\n SwarmRun,\n ActivityRollup,\n GenomeEntry,\n RecallHit,\n PortfolioSummary,\n ModelStatsReadResult,\n} from './api-types.js';\nimport type { SourceQuality } from './api-types.js';\n\nexport interface QueryDef<T> {\n key: string;\n fetch: (signal?: AbortSignal) => Promise<T>;\n}\n\nexport const dashboardSnapshotQuery: QueryDef<DashboardSnapshot> = {\n key: 'dashboard-snapshot',\n fetch: (signal) => apiGet<DashboardSnapshot>('/api/snapshot', signal),\n};\n\nexport const controlSnapshotQuery: QueryDef<ControlSnapshot> = {\n key: 'control-snapshot',\n fetch: (signal) => apiGet<ControlSnapshot>('/api/control', signal),\n};\n\nexport const agentOsSnapshotQuery: QueryDef<AgentOsSnapshotResponse> = {\n key: 'agent-os-snapshot',\n fetch: (signal) => apiGet<AgentOsSnapshotResponse>('/api/agent-os', signal),\n};\n\nexport const universeOverviewQuery: QueryDef<UniverseOverview> = {\n key: 'universe-overview',\n fetch: (signal) => apiGet<UniverseOverview>('/api/universe', signal),\n};\n\n/** Read only on demand; never invalidate this alongside the overview poll. */\nexport function universeGraphQuery(universeId: string): QueryDef<UniverseGraph> {\n return {\n key: `universe-graph-${universeId}`,\n fetch: (signal) => apiGet<UniverseGraph>(`/api/universe/graph?universeId=${encodeURIComponent(universeId)}`, signal),\n };\n}\n\n// NOTE: there is no standalone GET /api/visibility route — VisibilitySnapshot\n// is an optional field on DashboardSnapshot (`.visibility`), populated by\n// buildSnapshot() server-side. Read it off dashboardSnapshotQuery's data\n// rather than issuing a second request for it.\n\n/**\n * GET /api/runs returns the bare array (see src/core/web/api.ts:1121-1125,\n * `listRuns({limit:200})` -> `RunState[]`) — NOT `{runs: [...]}`. The prior\n * `RunSummary`/`runsQuery` pair here was speculative and didn't match the\n * real response shape; it was unused (FleetDashboardView reads its compact\n * run summaries off `DashboardSnapshot['runs']` instead, a different,\n * deliberately-thin shape). Replaced with the real, fully-typed contract.\n */\nexport const runsQuery: QueryDef<RunState[]> = {\n key: 'runs',\n fetch: (signal) => apiGet<RunState[]>('/api/runs', signal),\n};\n\nexport function runDetailQuery(id: string): QueryDef<RunState> {\n return {\n key: `run-detail-${id}`,\n fetch: (signal) => apiGet<RunState>(`/api/run/${encodeURIComponent(id)}`, signal),\n };\n}\n\nexport const swarmsQuery: QueryDef<SwarmRun[]> = {\n key: 'swarms',\n fetch: (signal) => apiGet<SwarmRun[]>('/api/swarms', signal),\n};\n\nexport function swarmDetailQuery(id: string): QueryDef<SwarmRun> {\n return {\n key: `swarm-detail-${id}`,\n fetch: (signal) => apiGet<SwarmRun>(`/api/swarm/${encodeURIComponent(id)}`, signal),\n };\n}\n\n/**\n * Same PublicDaemonObservation the backend embeds at ControlSnapshot.daemonObservation\n * — fetched standalone here because /control/daemon renders a fuller detail page\n * than DaemonPanel's dashboard-compact view and wants the raw observation\n * (including `ticks`) independent of whether the rest of /api/control succeeds.\n * Cache key 'daemon' deliberately matches sse.ts's existing (previously orphaned)\n * `daemon` / `daemon-observation` -> ['daemon'] mapping so this now actually\n * live-updates.\n */\nexport const daemonObservationQuery: QueryDef<PublicDaemonObservation> = {\n key: 'daemon',\n fetch: (signal) => apiGet<PublicDaemonObservation>('/api/daemon-observation', signal),\n};\n\nexport const fleetStatusQuery: QueryDef<FleetStatus> = {\n key: 'fleet',\n fetch: (signal) => apiGet<FleetStatus>('/api/fleet', signal),\n};\n\nexport const fleetActivityQuery: QueryDef<FleetActivitySnapshot> = {\n key: 'fleet-activity',\n fetch: (signal) => apiGet<FleetActivitySnapshot>('/api/fleet-activity', signal),\n};\n\n/**\n * GET /api/goals returns a bare array of ad-hoc per-goal projections built\n * inline in src/core/web/api.ts (`path === '/api/goals'`, ~line 1654) — there\n * is no exported backend type for this shape (unlike every other resource\n * here), so this interface is hand-written to match that handler exactly\n * rather than re-exported. Keep it in sync with api.ts if that handler's\n * shape changes.\n */\nexport interface GoalMilestoneSummary {\n title: string;\n status: string;\n order: number;\n}\n\nexport interface GoalSummary {\n id: string;\n objective: string;\n status: string;\n milestones: GoalMilestoneSummary[];\n progress: {\n fractionDone: number;\n counts: Record<string, number>;\n nextActionableId: string | null;\n };\n}\n\nexport const goalsQuery: QueryDef<GoalSummary[]> = {\n key: 'goals',\n fetch: (signal) => apiGet<GoalSummary[]>('/api/goals', signal),\n};\n\n/**\n * GET /api/vision/mission — buildVisionMissionSnapshot() in src/core/web/api.ts\n * is itself typed `Promise<Record<string, unknown>>` on the backend (no\n * exported interface to import), so this is a narrow hand-written contract\n * covering only the fields the Goals view actually reads: top-level `state`\n * (mirrors the SourceQuality.sourceState vocabulary) and the per-source\n * epistemic breakdown under `sources`. `briefing`/`preview`/`shadow` are\n * deliberately left as `unknown` — the view does not render briefing prose.\n */\nexport interface VisionMissionSourceStatus {\n sourceState: 'healthy' | 'degraded' | 'missing' | 'unknown';\n sourcePresent?: boolean;\n complete: boolean;\n reason?: string;\n}\n\nexport interface VisionMissionSnapshot {\n schemaVersion: number;\n state: string;\n authority: string;\n briefing: unknown;\n sources: {\n briefing: VisionMissionSourceStatus | null;\n goals: VisionMissionSourceStatus | null;\n enrollment: VisionMissionSourceStatus | null;\n proposals: VisionMissionSourceStatus | null;\n };\n}\n\nexport const visionMissionQuery: QueryDef<VisionMissionSnapshot> = {\n key: 'vision-mission',\n fetch: (signal) => apiGet<VisionMissionSnapshot>('/api/vision/mission', signal),\n};\n\nexport interface InboxProposal {\n id: string;\n title: string;\n kind: string;\n repo: string;\n origin: string;\n createdAt: string;\n}\n\nexport const inboxQuery: QueryDef<{ pending: number; proposals: InboxProposal[] }> = {\n key: 'inbox',\n fetch: (signal) => apiGet('/api/inbox', signal),\n};\n\n/**\n * `?status=&since=&limit=` view over GET /api/inbox (src/core/web/api.ts\n * INBOX_STATUS_QUERY_VALUES) — the history surface `inboxQuery` above never\n * had: it's hardcoded pending-only. `status: undefined` = server default\n * (pending); `'all'` = every status. Keyed per filter combo so switching\n * status/since/limit doesn't fight over one cache entry, and so sse.ts can\n * sweep every live combo (`invalidatePrefix('inbox-list:')`) on the 'inbox'\n * SSE event without knowing which combos are mounted.\n */\nexport type InboxHistoryStatus = ProposalStatus | 'all';\n\nexport interface InboxListFilters {\n status?: InboxHistoryStatus;\n /** ISO timestamp or YYYY-MM-DD, passed straight through to the server. */\n since?: string;\n limit?: number;\n}\n\nexport interface InboxListResponse {\n pending: number;\n total: number;\n /**\n * Full `Proposal[]`, not the narrow `InboxProposal` shape above — the\n * server has always sent full sanitized Proposal records here\n * (`listProposals()`); `InboxProposal` just under-typed it for the\n * original pending-badge use case. The history view needs `status` and\n * `decisionReason` per row (triage: \"why was this rejected\") without an\n * N+1 decisions-ledger read per row, and both live directly on Proposal.\n */\n proposals: Proposal[];\n /** True when more rows matched than were returned (hit the server cap). */\n truncated: boolean;\n filters: { status: InboxHistoryStatus; since: string | null; limit: number | null };\n}\n\nexport function inboxListQuery(filters: InboxListFilters = {}): QueryDef<InboxListResponse> {\n const params = new URLSearchParams();\n if (filters.status !== undefined) params.set('status', filters.status);\n if (filters.since !== undefined) params.set('since', filters.since);\n if (filters.limit !== undefined) params.set('limit', String(filters.limit));\n const qs = params.toString();\n return {\n key: `inbox-list:${filters.status ?? 'pending'}:${filters.since ?? ''}:${filters.limit ?? ''}`,\n fetch: (signal) => apiGet<InboxListResponse>(`/api/inbox${qs ? `?${qs}` : ''}`, signal),\n };\n}\n\n/**\n * Evidence behind a judge's verdict for one proposal — joined server-side\n * (src/core/web/api.ts GET /api/inbox/:id) from the decisions ledger, which\n * is the ONLY place `judgeReasonCode` lives (Proposal itself never carries\n * it). `judge-parse-failure`/`judge-network-failure` are infra failures,\n * never a considered `judge-review` — render them distinctly, never folded\n * together. When the ledger read is degraded or no row matches this\n * proposal, `sourceQuality` says so; render \"unknown\" via <Epistemic/>\n * rather than treating an empty `decisions` array as \"never judged\".\n */\nexport interface InboxDecisionEvidence {\n sourceQuality: SourceQuality;\n decisions: DecisionEntry[];\n}\n\n/**\n * GET /api/inbox/:id — full proposal detail including diff/taste/verdict\n * (src/core/web/api.ts:1240-1255, `loadProposal(id)`) plus `decisionEvidence`\n * (readDecisionsDetailed({proposalId}), added alongside the inbox history\n * view). Used by the work journal's \"why did it do that\" drill-in (M416) —\n * `/api/inbox` only ever returns pending proposals, but a decided\n * proposal's id (surfaced via fleet-activity's recentMerges/recentActions,\n * or via inboxListQuery's history view) still resolves here as long as its\n * on-disk record hasn't been pruned.\n */\nexport function proposalDetailQuery(id: string): QueryDef<Proposal & { decisionEvidence: InboxDecisionEvidence }> {\n return {\n key: `proposal-detail-${id}`,\n fetch: (signal) =>\n apiGet<Proposal & { decisionEvidence: InboxDecisionEvidence }>(`/api/inbox/${encodeURIComponent(id)}`, signal),\n };\n}\n\n/**\n * GET /api/logs-observation?tail=200 — the provenance-bearing sibling of\n * GET /api/logs (src/core/web/api.ts:~1543-1560): same daemon tick/merge\n * log, plus `sourceQuality` so a degraded/incomplete log can be rendered\n * honestly via <Epistemic/> instead of silently showing a truncated or\n * stale list as complete. `entries` is `null` when the source itself is not\n * healthy+complete (server-side contract, not a client guess). Used by the\n * work journal (M416) as one of several merged history sources — NOT a\n * substitute for full on-disk history: this is capped at 200 entries\n * server-side and only carries daemon tick/merge/info/dispatch events, not\n * raw run stdout or the ~672 historical proposals living only on disk.\n */\nexport interface ControlLogsObservation {\n entries: ControlLogEntry[] | null;\n sourceQuality: SourceQuality;\n}\n\nexport const controlLogsObservationQuery: QueryDef<ControlLogsObservation> = {\n key: 'control-logs-observation',\n fetch: (signal) => apiGet<ControlLogsObservation>('/api/logs-observation?tail=200', signal),\n};\n\n// ---------------------------------------------------------------------------\n// Intelligence + Portfolio (charts/dataviz work)\n// ---------------------------------------------------------------------------\n\nexport type PulseWindow = '1d' | '7d' | '30d';\n\n/** GET /api/pulse?window= — buildRollup() (src/core/observability/rollup.ts).\n * Parameterized like runDetailQuery/swarmDetailQuery above: a distinct cache\n * key per window so switching windows doesn't fight over one cache entry. */\nexport function pulseQuery(window: PulseWindow): QueryDef<ActivityRollup> {\n return {\n key: `pulse-${window}`,\n fetch: (signal) => apiGet<ActivityRollup>(`/api/pulse?window=${window}`, signal),\n };\n}\n\n/**\n * GET /api/genome[?q=] — src/core/web/api.ts:1189-1201. Two response shapes\n * depending on `q`: empty/absent `q` returns `GenomeEntry[]` (list mode,\n * `loadGenome()`); a non-empty `q` returns `RecallHit[]` (query mode,\n * `recall()`, `{entry, score, method}`). Use `isRecallHit` to discriminate —\n * the view can't know which shape it got from the type alone since both are\n * arrays. Keyed per query string so a debounced search doesn't stomp the\n * browse-mode cache entry.\n */\nexport type GenomeResult = GenomeEntry[] | RecallHit[];\n\nexport function isRecallHit(item: GenomeEntry | RecallHit): item is RecallHit {\n return typeof item === 'object' && item !== null && 'entry' in item && 'score' in item;\n}\n\nexport function genomeQuery(q: string): QueryDef<GenomeResult> {\n const trimmed = q.trim();\n return {\n key: trimmed ? `genome-search-${trimmed}` : 'genome-list',\n fetch: (signal) =>\n apiGet<GenomeResult>(trimmed ? `/api/genome?q=${encodeURIComponent(trimmed)}` : '/api/genome', signal),\n };\n}\n\nexport type ModelStatsWindow = '7d' | '30d' | 'all';\n\n/** GET /api/models?window= — computeModelStatsDetailed() joined per-model\n * economics (src/core/fleet/model-stats.ts). NOTE: a second, dead\n * `/api/models` handler later in api.ts's if-chain (local-model provider\n * probe off ControlSnapshot.models) is unreachable — this one always wins\n * since route matching is first-match. */\nexport function modelsQuery(window: ModelStatsWindow): QueryDef<ModelStatsReadResult & { window: ModelStatsWindow }> {\n return {\n key: `models-${window}`,\n fetch: (signal) => apiGet(`/api/models?window=${window}`, signal),\n };\n}\n\n/**\n * GET /api/portfolio — src/core/web/api.ts:1105-1109. Returns\n * `DashboardSnapshot.portfolio ?? null`: `null` on an older producer or\n * empty enrollment, not an error — render that as an honest \"not available\"\n * state, never as a zeroed chart.\n */\nexport const portfolioQuery: QueryDef<PortfolioSummary | null> = {\n key: 'portfolio',\n fetch: (signal) => apiGet<PortfolioSummary | null>('/api/portfolio', signal),\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"queries-CYisSAAB.js","names":[],"sources":["../../../../../../src/web-ui/data/queries.ts"],"sourcesContent":["/**\n * data/queries.ts — one typed accessor per backend resource. Each pairs a\n * stable cache key (also the SSE invalidation target — see sse.ts's\n * EVENT_TO_CACHE_KEYS) with the exact GET call and response type. Views\n * never call apiGet directly; they call these through useQuery (hooks.ts)\n * so caching, de-dupe, and SSE-driven refresh are automatic.\n */\nimport { apiGet } from './client.js';\nimport type { UniverseGraph } from '../../core/universe/graph-types.js';\nimport type {\n AgentOsSnapshotResponse,\n UniverseOverview,\n ControlLogEntry,\n ControlSnapshot,\n DashboardSnapshot,\n DecisionEntry,\n FleetActivitySnapshot,\n FleetStatus,\n Proposal,\n ProposalStatus,\n PublicDaemonObservation,\n RunState,\n SwarmRun,\n ActivityRollup,\n GenomeEntry,\n RecallHit,\n PortfolioSummary,\n ModelStatsReadResult,\n} from './api-types.js';\nimport type { SourceQuality } from './api-types.js';\n\nexport interface QueryDef<T> {\n key: string;\n fetch: (signal?: AbortSignal) => Promise<T>;\n}\n\nexport const dashboardSnapshotQuery: QueryDef<DashboardSnapshot> = {\n key: 'dashboard-snapshot',\n fetch: (signal) => apiGet<DashboardSnapshot>('/api/snapshot', signal),\n};\n\nexport const controlSnapshotQuery: QueryDef<ControlSnapshot> = {\n key: 'control-snapshot',\n fetch: (signal) => apiGet<ControlSnapshot>('/api/control', signal),\n};\n\nexport const agentOsSnapshotQuery: QueryDef<AgentOsSnapshotResponse> = {\n key: 'agent-os-snapshot',\n fetch: (signal) => apiGet<AgentOsSnapshotResponse>('/api/agent-os', signal),\n};\n\nexport const universeOverviewQuery: QueryDef<UniverseOverview> = {\n key: 'universe-overview',\n fetch: (signal) => apiGet<UniverseOverview>('/api/universe', signal),\n};\n\n/** Read only on demand; never invalidate this alongside the overview poll. */\nexport function universeGraphQuery(universeId: string): QueryDef<UniverseGraph> {\n return {\n key: `universe-graph-${universeId}`,\n fetch: (signal) => apiGet<UniverseGraph>(`/api/universe/graph?universeId=${encodeURIComponent(universeId)}`, signal),\n };\n}\n\n// NOTE: there is no standalone GET /api/visibility route — VisibilitySnapshot\n// is an optional field on DashboardSnapshot (`.visibility`), populated by\n// buildSnapshot() server-side. Read it off dashboardSnapshotQuery's data\n// rather than issuing a second request for it.\n\n/**\n * GET /api/runs returns the bare array (see src/core/web/api.ts:1121-1125,\n * `listRuns({limit:200})` -> `RunState[]`) — NOT `{runs: [...]}`. The prior\n * `RunSummary`/`runsQuery` pair here was speculative and didn't match the\n * real response shape; it was unused (FleetDashboardView reads its compact\n * run summaries off `DashboardSnapshot['runs']` instead, a different,\n * deliberately-thin shape). Replaced with the real, fully-typed contract.\n */\nexport const runsQuery: QueryDef<RunState[]> = {\n key: 'runs',\n fetch: (signal) => apiGet<RunState[]>('/api/runs', signal),\n};\n\nexport function runDetailQuery(id: string): QueryDef<RunState> {\n return {\n key: `run-detail-${id}`,\n fetch: (signal) => apiGet<RunState>(`/api/run/${encodeURIComponent(id)}`, signal),\n };\n}\n\nexport const swarmsQuery: QueryDef<SwarmRun[]> = {\n key: 'swarms',\n fetch: (signal) => apiGet<SwarmRun[]>('/api/swarms', signal),\n};\n\nexport function swarmDetailQuery(id: string): QueryDef<SwarmRun> {\n return {\n key: `swarm-detail-${id}`,\n fetch: (signal) => apiGet<SwarmRun>(`/api/swarm/${encodeURIComponent(id)}`, signal),\n };\n}\n\n/**\n * Same PublicDaemonObservation the backend embeds at ControlSnapshot.daemonObservation\n * — fetched standalone here because /control/daemon renders a fuller detail page\n * than DaemonPanel's dashboard-compact view and wants the raw observation\n * (including `ticks`) independent of whether the rest of /api/control succeeds.\n * Cache key 'daemon' deliberately matches sse.ts's existing (previously orphaned)\n * `daemon` / `daemon-observation` -> ['daemon'] mapping so this now actually\n * live-updates.\n */\nexport const daemonObservationQuery: QueryDef<PublicDaemonObservation> = {\n key: 'daemon',\n fetch: (signal) => apiGet<PublicDaemonObservation>('/api/daemon-observation', signal),\n};\n\nexport const fleetStatusQuery: QueryDef<FleetStatus> = {\n key: 'fleet',\n fetch: (signal) => apiGet<FleetStatus>('/api/fleet', signal),\n};\n\nexport const fleetActivityQuery: QueryDef<FleetActivitySnapshot> = {\n key: 'fleet-activity',\n fetch: (signal) => apiGet<FleetActivitySnapshot>('/api/fleet-activity', signal),\n};\n\n/**\n * GET /api/goals returns a bare array of ad-hoc per-goal projections built\n * inline in src/core/web/api.ts (`path === '/api/goals'`, ~line 1654) — there\n * is no exported backend type for this shape (unlike every other resource\n * here), so this interface is hand-written to match that handler exactly\n * rather than re-exported. Keep it in sync with api.ts if that handler's\n * shape changes.\n */\nexport interface GoalMilestoneSummary {\n title: string;\n status: string;\n order: number;\n}\n\nexport interface GoalSummary {\n id: string;\n objective: string;\n status: string;\n milestones: GoalMilestoneSummary[];\n progress: {\n fractionDone: number;\n counts: Record<string, number>;\n nextActionableId: string | null;\n };\n}\n\nexport const goalsQuery: QueryDef<GoalSummary[]> = {\n key: 'goals',\n fetch: (signal) => apiGet<GoalSummary[]>('/api/goals', signal),\n};\n\n/**\n * GET /api/vision/mission — buildVisionMissionSnapshot() in src/core/web/api.ts\n * is itself typed `Promise<Record<string, unknown>>` on the backend (no\n * exported interface to import), so this is a narrow hand-written contract\n * covering only the fields the Goals view actually reads: top-level `state`\n * (mirrors the SourceQuality.sourceState vocabulary) and the per-source\n * epistemic breakdown under `sources`. `briefing`/`preview`/`shadow` are\n * deliberately left as `unknown` — the view does not render briefing prose.\n */\nexport interface VisionMissionSourceStatus {\n sourceState: 'healthy' | 'degraded' | 'missing' | 'unknown';\n sourcePresent?: boolean;\n complete: boolean;\n reason?: string;\n}\n\nexport interface VisionMissionSnapshot {\n schemaVersion: number;\n state: string;\n authority: string;\n briefing: unknown;\n sources: {\n briefing: VisionMissionSourceStatus | null;\n goals: VisionMissionSourceStatus | null;\n enrollment: VisionMissionSourceStatus | null;\n proposals: VisionMissionSourceStatus | null;\n };\n}\n\nexport const visionMissionQuery: QueryDef<VisionMissionSnapshot> = {\n key: 'vision-mission',\n fetch: (signal) => apiGet<VisionMissionSnapshot>('/api/vision/mission', signal),\n};\n\nexport interface InboxProposal {\n id: string;\n title: string;\n kind: string;\n repo: string;\n origin: string;\n createdAt: string;\n}\n\nexport const inboxQuery: QueryDef<{ pending: number; proposals: InboxProposal[] }> = {\n key: 'inbox',\n fetch: (signal) => apiGet('/api/inbox', signal),\n};\n\n/**\n * `?status=&since=&limit=` view over GET /api/inbox (src/core/web/api.ts\n * INBOX_STATUS_QUERY_VALUES) — the history surface `inboxQuery` above never\n * had: it's hardcoded pending-only. `status: undefined` = server default\n * (pending); `'all'` = every status. Keyed per filter combo so switching\n * status/since/limit doesn't fight over one cache entry, and so sse.ts can\n * sweep every live combo (`invalidatePrefix('inbox-list:')`) on the 'inbox'\n * SSE event without knowing which combos are mounted.\n */\nexport type InboxHistoryStatus = ProposalStatus | 'all';\n\nexport interface InboxListFilters {\n status?: InboxHistoryStatus;\n /** ISO timestamp or YYYY-MM-DD, passed straight through to the server. */\n since?: string;\n limit?: number;\n}\n\nexport interface InboxListResponse {\n pending: number;\n total: number;\n /**\n * Full `Proposal[]`, not the narrow `InboxProposal` shape above — the\n * server has always sent full sanitized Proposal records here\n * (`listProposals()`); `InboxProposal` just under-typed it for the\n * original pending-badge use case. The history view needs `status` and\n * `decisionReason` per row (triage: \"why was this rejected\") without an\n * N+1 decisions-ledger read per row, and both live directly on Proposal.\n */\n proposals: Proposal[];\n /** True when more rows matched than were returned (hit the server cap). */\n truncated: boolean;\n filters: { status: InboxHistoryStatus; since: string | null; limit: number | null };\n}\n\nexport function inboxListQuery(filters: InboxListFilters = {}): QueryDef<InboxListResponse> {\n const params = new URLSearchParams();\n if (filters.status !== undefined) params.set('status', filters.status);\n if (filters.since !== undefined) params.set('since', filters.since);\n if (filters.limit !== undefined) params.set('limit', String(filters.limit));\n const qs = params.toString();\n return {\n key: `inbox-list:${filters.status ?? 'pending'}:${filters.since ?? ''}:${filters.limit ?? ''}`,\n fetch: (signal) => apiGet<InboxListResponse>(`/api/inbox${qs ? `?${qs}` : ''}`, signal),\n };\n}\n\n/**\n * Evidence behind a judge's verdict for one proposal — joined server-side\n * (src/core/web/api.ts GET /api/inbox/:id) from the decisions ledger, which\n * is the ONLY place `judgeReasonCode` lives (Proposal itself never carries\n * it). `judge-parse-failure`/`judge-network-failure` are infra failures,\n * never a considered `judge-review` — render them distinctly, never folded\n * together. When the ledger read is degraded or no row matches this\n * proposal, `sourceQuality` says so; render \"unknown\" via <Epistemic/>\n * rather than treating an empty `decisions` array as \"never judged\".\n */\nexport interface InboxDecisionEvidence {\n sourceQuality: SourceQuality;\n decisions: DecisionEntry[];\n}\n\n/**\n * GET /api/inbox/:id — full proposal detail including diff/taste/verdict\n * (src/core/web/api.ts:1240-1255, `loadProposal(id)`) plus `decisionEvidence`\n * (readDecisionsDetailed({proposalId}), added alongside the inbox history\n * view). Used by the work journal's \"why did it do that\" drill-in (M416) —\n * `/api/inbox` only ever returns pending proposals, but a decided\n * proposal's id (surfaced via fleet-activity's recentMerges/recentActions,\n * or via inboxListQuery's history view) still resolves here as long as its\n * on-disk record hasn't been pruned.\n */\nexport function proposalDetailQuery(id: string): QueryDef<Proposal & { decisionEvidence: InboxDecisionEvidence }> {\n return {\n key: `proposal-detail-${id}`,\n fetch: (signal) =>\n apiGet<Proposal & { decisionEvidence: InboxDecisionEvidence }>(`/api/inbox/${encodeURIComponent(id)}`, signal),\n };\n}\n\n/**\n * GET /api/logs-observation?tail=200 — the provenance-bearing sibling of\n * GET /api/logs (src/core/web/api.ts:~1543-1560): same daemon tick/merge\n * log, plus `sourceQuality` so a degraded/incomplete log can be rendered\n * honestly via <Epistemic/> instead of silently showing a truncated or\n * stale list as complete. `entries` is `null` when the source itself is not\n * healthy+complete (server-side contract, not a client guess). Used by the\n * work journal (M416) as one of several merged history sources — NOT a\n * substitute for full on-disk history: this is capped at 200 entries\n * server-side and only carries daemon tick/merge/info/dispatch events, not\n * raw run stdout or the ~672 historical proposals living only on disk.\n */\nexport interface ControlLogsObservation {\n entries: ControlLogEntry[] | null;\n sourceQuality: SourceQuality;\n}\n\nexport const controlLogsObservationQuery: QueryDef<ControlLogsObservation> = {\n key: 'control-logs-observation',\n fetch: (signal) => apiGet<ControlLogsObservation>('/api/logs-observation?tail=200', signal),\n};\n\n// ---------------------------------------------------------------------------\n// Intelligence + Portfolio (charts/dataviz work)\n// ---------------------------------------------------------------------------\n\nexport type PulseWindow = '1d' | '7d' | '30d';\n\n/** GET /api/pulse?window= — buildRollup() (src/core/observability/rollup.ts).\n * Parameterized like runDetailQuery/swarmDetailQuery above: a distinct cache\n * key per window so switching windows doesn't fight over one cache entry. */\nexport function pulseQuery(window: PulseWindow): QueryDef<ActivityRollup> {\n return {\n key: `pulse-${window}`,\n fetch: (signal) => apiGet<ActivityRollup>(`/api/pulse?window=${window}`, signal),\n };\n}\n\n/**\n * GET /api/genome[?q=] — src/core/web/api.ts:1189-1201. Two response shapes\n * depending on `q`: empty/absent `q` returns `GenomeEntry[]` (list mode,\n * `loadGenome()`); a non-empty `q` returns `RecallHit[]` (query mode,\n * `recall()`, `{entry, score, method}`). Use `isRecallHit` to discriminate —\n * the view can't know which shape it got from the type alone since both are\n * arrays. Keyed per query string so a debounced search doesn't stomp the\n * browse-mode cache entry.\n */\nexport type GenomeResult = GenomeEntry[] | RecallHit[];\n\nexport function isRecallHit(item: GenomeEntry | RecallHit): item is RecallHit {\n return typeof item === 'object' && item !== null && 'entry' in item && 'score' in item;\n}\n\nexport function genomeQuery(q: string): QueryDef<GenomeResult> {\n const trimmed = q.trim();\n return {\n key: trimmed ? `genome-search-${trimmed}` : 'genome-list',\n fetch: (signal) =>\n apiGet<GenomeResult>(trimmed ? `/api/genome?q=${encodeURIComponent(trimmed)}` : '/api/genome', signal),\n };\n}\n\nexport type ModelStatsWindow = '7d' | '30d' | 'all';\n\n/** GET /api/models?window= — computeModelStatsDetailed() joined per-model\n * economics (src/core/fleet/model-stats.ts). NOTE: a second, dead\n * `/api/models` handler later in api.ts's if-chain (local-model provider\n * probe off ControlSnapshot.models) is unreachable — this one always wins\n * since route matching is first-match. */\nexport function modelsQuery(window: ModelStatsWindow): QueryDef<ModelStatsReadResult & { window: ModelStatsWindow }> {\n return {\n key: `models-${window}`,\n fetch: (signal) => apiGet(`/api/models?window=${window}`, signal),\n };\n}\n\n/**\n * GET /api/portfolio — src/core/web/api.ts:1105-1109. Returns\n * `DashboardSnapshot.portfolio ?? null`: `null` on an older producer or\n * empty enrollment, not an error — render that as an honest \"not available\"\n * state, never as a zeroed chart.\n */\nexport const portfolioQuery: QueryDef<PortfolioSummary | null> = {\n key: 'portfolio',\n fetch: (signal) => apiGet<PortfolioSummary | null>('/api/portfolio', signal),\n};\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{a as e,s as t}from"./index-BVxOlseC.js";import{D as n,F as r,M as i,O as a,U as o,l as s,o as c}from"./global-oEl9Qj8u.js";import{c as l,h as u,m as d,p as f,u as p}from"./workbench-types-DzN-5trg.js";import{n as m,r as h}from"./VerseConsoleApp-DTjDpGCd.js";async function g(e){try{let t=await e.json();return{detail:typeof t.error==`string`&&t.error?t.error:null,code:typeof t.code==`string`&&t.code?t.code:null}}catch{return{detail:null,code:null}}}async function _(e,t){let n=await fetch(e,{method:`GET`,credentials:`same-origin`,headers:{"x-ashlr-read-client":a()},...t?{signal:t}:{}});if(n.status===401)throw i(),new c(`Read session expired.`,401,e);if(!n.ok){let{detail:t,code:r}=await g(n);throw new c(`GET ${e} failed (HTTP ${n.status})${t?`: ${t}`:``}.`,n.status,e,t,r)}return await n.json()}async function v(e,t,i){let a=n();if(!a)throw new h;let o=await s(e,t,a,i);return r(),o}var y=encodeURIComponent;function b(e,t){return _(`${d}/${y(e)}`,t)}async function x(e,t){let n=await v(`${d}/${y(e)}`,t);return t.model!==void 0&&o(m),n}function S(e){return _(u,e)}function C(e){return v(u,e)}function w(e,t,n){return v(`${l}/${y(e)}`,t,n)}function T(e,t){return v(`${l}/${y(e)}/${y(t)}/delete`,{})}function E(e,t){return _(`${f}/${y(e)}`,t)}function D(e,t,n={}){return v(`${f}/${y(e)}`,n.sendNow?{text:t,sendNow:!0}:{text:t})}function O(e,t){return v(`${f}/${y(e)}/${y(t)}/delete`,{})}function k(e,t){return v(`${f}/${y(e)}/${y(t)}/send`,{})}function A(e,t,n){let r=new URLSearchParams({sessionId:e,q:t});return _(`${p}?${r.toString()}`,n)}var j=t(e(),1),M=[`collapsed`,`expanded`,`hidden`],N={collapsed:`Collapsed`,expanded:`Expanded`,hidden:`Hidden`},P=`ashlr.verse.reasoning.v1`,F=`collapsed`;function I(e){return typeof e==`string`&&M.includes(e)}function L(){try{let e=localStorage.getItem(P);return I(e)?e:F}catch{return F}}var R=null,z=new Set;function B(){return R===null&&(R=L()),R}function V(e){if(I(e)&&e!==B()){R=e;try{localStorage.setItem(P,e)}catch{}for(let e of[...z])e()}}function H(e){return z.add(e),()=>z.delete(e)}function U(){return(0,j.useSyncExternalStore)(H,B,B)}export{T as a,E as c,A as d,k as f,w as h,U as i,b as l,x as m,N as n,D as o,C as p,V as r,S as s,M as t,O as u};
2
+ //# sourceMappingURL=reasoning-pref-BUcFF_5G.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reasoning-pref-B9-DJUXP.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/composer/composer-queries.ts","../../../../../../src/web-ui/routes/verse/chat/reasoning-pref.ts"],"sourcesContent":["/**\n * routes/verse/composer/composer-queries.ts — every call the 3.10 composer\n * makes (unit C3; core/verse/session-controls-api.ts is the server side).\n *\n * Same conventions as ../context/context-queries.ts:\n * - WRITES pull the held mutation token, throw `VerseMutationLockedError`\n * when none is held (so the caller's token gate can ask for it), and touch\n * the hold on success;\n * - READS carry the read-client proof and keep the server's refusal sentence\n * (`ApiError.detail`), because these routes answer a refused read with a\n * sentence written for a person.\n *\n * Spend: none of these start a model call EXCEPT the two that exist to send —\n * a queued follow-up when nothing is running, and [Send now]. Both go through\n * the engine's one `sendTurn` chokepoint, like POST /sessions/:id/turns.\n */\nimport { getMutationToken, getReadClientProof, reportSessionExpired, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport { ApiError, apiPost } from '../../../data/client.js';\nimport type {\n VerseAttachment,\n VerseAttachmentUpload,\n VerseFilesResponse,\n VerseQueueResponse,\n VerseSessionControlDefaults,\n VerseSessionControlDefaultsUpdate,\n VerseSessionControlsResponse,\n VerseSessionControlsUpdate,\n} from '../../../../core/verse/workbench-types.js';\nimport {\n VERSE_ATTACHMENTS_PATH,\n VERSE_FILES_PATH,\n VERSE_QUEUE_PATH,\n VERSE_SESSION_CONTROL_DEFAULTS_PATH,\n VERSE_SESSION_CONTROLS_PATH,\n} from '../../../../core/verse/workbench-types.js';\nimport { VERSE_SESSIONS_KEY, VerseMutationLockedError } from '../verse-queries.js';\n\n/** POST /queue and /send answer with the queue plus the turn they started, if any. */\nexport type VerseQueueSendResult = VerseQueueResponse & { sentTurnId: string | null };\n\nasync function refusalDetail(res: Response): Promise<{ detail: string | null; code: string | null }> {\n try {\n const body = (await res.json()) as { error?: unknown; code?: unknown };\n return {\n detail: typeof body.error === 'string' && body.error ? body.error : null,\n code: typeof body.code === 'string' && body.code ? body.code : null,\n };\n } catch {\n return { detail: null, code: null };\n }\n}\n\nasync function getJson<T>(path: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(path, {\n method: 'GET',\n credentials: 'same-origin',\n headers: { 'x-ashlr-read-client': getReadClientProof() },\n ...(signal ? { signal } : {}),\n });\n if (res.status === 401) {\n reportSessionExpired();\n throw new ApiError('Read session expired.', 401, path);\n }\n if (!res.ok) {\n const { detail, code } = await refusalDetail(res);\n throw new ApiError(`GET ${path} failed (HTTP ${res.status})${detail ? `: ${detail}` : ''}.`, res.status, path, detail, code);\n }\n return (await res.json()) as T;\n}\n\nasync function post<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new VerseMutationLockedError();\n const result = await apiPost<T>(path, body, token, signal);\n touchMutationHold();\n return result;\n}\n\nconst enc = encodeURIComponent;\n\n// ---------------------------------------------------------------------------\n// Controls\n// ---------------------------------------------------------------------------\n\nexport function fetchSessionControls(sessionId: string, signal?: AbortSignal): Promise<VerseSessionControlsResponse> {\n return getJson(`${VERSE_SESSION_CONTROLS_PATH}/${enc(sessionId)}`, signal);\n}\n\nexport async function updateSessionControls(sessionId: string, update: VerseSessionControlsUpdate): Promise<VerseSessionControlsResponse> {\n const result = await post<VerseSessionControlsResponse>(`${VERSE_SESSION_CONTROLS_PATH}/${enc(sessionId)}`, update);\n // A model switch changes the record the header and the chat list draw.\n if (update.model !== undefined) invalidate(VERSE_SESSIONS_KEY);\n return result;\n}\n\nexport function fetchControlDefaults(signal?: AbortSignal): Promise<VerseSessionControlDefaults> {\n return getJson(VERSE_SESSION_CONTROL_DEFAULTS_PATH, signal);\n}\n\nexport function updateControlDefaults(update: VerseSessionControlDefaultsUpdate): Promise<VerseSessionControlDefaults> {\n return post(VERSE_SESSION_CONTROL_DEFAULTS_PATH, update);\n}\n\n// ---------------------------------------------------------------------------\n// Attachments\n// ---------------------------------------------------------------------------\n\nexport function uploadAttachment(sessionId: string, upload: VerseAttachmentUpload, signal?: AbortSignal): Promise<VerseAttachment> {\n return post(`${VERSE_ATTACHMENTS_PATH}/${enc(sessionId)}`, upload, signal);\n}\n\nexport function deleteAttachment(sessionId: string, attachmentId: string): Promise<{ sessionId: string; items: VerseAttachment[] }> {\n return post(`${VERSE_ATTACHMENTS_PATH}/${enc(sessionId)}/${enc(attachmentId)}/delete`, {});\n}\n\n// ---------------------------------------------------------------------------\n// Queue\n// ---------------------------------------------------------------------------\n\nexport function fetchQueue(sessionId: string, signal?: AbortSignal): Promise<VerseQueueResponse> {\n return getJson(`${VERSE_QUEUE_PATH}/${enc(sessionId)}`, signal);\n}\n\nexport function enqueueFollowUp(sessionId: string, text: string, opts: { sendNow?: boolean } = {}): Promise<VerseQueueSendResult> {\n return post(`${VERSE_QUEUE_PATH}/${enc(sessionId)}`, opts.sendNow ? { text, sendNow: true } : { text });\n}\n\nexport function removeFollowUp(sessionId: string, queueId: string): Promise<VerseQueueResponse> {\n return post(`${VERSE_QUEUE_PATH}/${enc(sessionId)}/${enc(queueId)}/delete`, {});\n}\n\nexport function sendFollowUpNow(sessionId: string, queueId: string): Promise<VerseQueueSendResult> {\n return post(`${VERSE_QUEUE_PATH}/${enc(sessionId)}/${enc(queueId)}/send`, {});\n}\n\n// ---------------------------------------------------------------------------\n// Files (`@`)\n// ---------------------------------------------------------------------------\n\n/** The chat's cwd root rides along (additive), so a match there is written relative. */\nexport type VerseFilesResult = VerseFilesResponse & { primaryRoot?: string | null };\n\nexport function searchSessionFiles(sessionId: string, query: string, signal?: AbortSignal): Promise<VerseFilesResult> {\n const params = new URLSearchParams({ sessionId, q: query });\n return getJson(`${VERSE_FILES_PATH}?${params.toString()}`, signal);\n}\n","/**\n * routes/verse/chat/reasoning-pref.ts — how the transcript shows the model's\n * reasoning (SPEC-310C §2 \"Settings offers Expanded, Collapsed or Hidden\";\n * unit C2 owns the behaviour, C1's Settings ▸ Chat panel renders the control).\n *\n * collapsed (default) a block streams in a three-line window while the\n * model thinks, then folds to \"Thought 12s · ~1.8k tok ▸\";\n * expanded the same stream, but a finished block stays open;\n * hidden no reasoning in the transcript at all — the live status line\n * still says \"Thinking · 12s\", so a long think is never mistaken\n * for a hang.\n *\n * A per-device preference, so it lives in this browser's storage under its\n * own key (the precedent of resources-collapse / chat-panel-sizing: display\n * preferences that are not part of the shell's v3 blob). Framework-free store\n * + one hook; every read and write is try/catch'd because storage can be\n * absent (private window, blocked site data).\n */\nimport { useSyncExternalStore } from 'react';\n\nexport const REASONING_DISPLAYS = ['collapsed', 'expanded', 'hidden'] as const;\nexport type ReasoningDisplay = (typeof REASONING_DISPLAYS)[number];\n\nexport const REASONING_DISPLAY_LABEL: Readonly<Record<ReasoningDisplay, string>> = {\n collapsed: 'Collapsed',\n expanded: 'Expanded',\n hidden: 'Hidden',\n};\n\nexport const REASONING_DISPLAY_KEY = 'ashlr.verse.reasoning.v1';\nexport const DEFAULT_REASONING_DISPLAY: ReasoningDisplay = 'collapsed';\n\nexport function isReasoningDisplay(value: unknown): value is ReasoningDisplay {\n return typeof value === 'string' && (REASONING_DISPLAYS as readonly string[]).includes(value);\n}\n\nfunction read(): ReasoningDisplay {\n try {\n const raw = localStorage.getItem(REASONING_DISPLAY_KEY);\n return isReasoningDisplay(raw) ? raw : DEFAULT_REASONING_DISPLAY;\n } catch {\n return DEFAULT_REASONING_DISPLAY;\n }\n}\n\nlet current: ReasoningDisplay | null = null;\nconst listeners = new Set<() => void>();\n\nexport function getReasoningDisplay(): ReasoningDisplay {\n if (current === null) current = read();\n return current;\n}\n\nexport function setReasoningDisplay(next: ReasoningDisplay): void {\n if (!isReasoningDisplay(next) || next === getReasoningDisplay()) return;\n current = next;\n try {\n localStorage.setItem(REASONING_DISPLAY_KEY, next);\n } catch {\n /* the choice still holds for this page */\n }\n for (const listener of [...listeners]) listener();\n}\n\nexport function subscribeReasoningDisplay(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\n/** Test seam: forget the in-memory value so the next read re-reads storage. */\nexport function resetReasoningDisplay(): void {\n current = null;\n for (const listener of [...listeners]) listener();\n}\n\nexport function useReasoningDisplay(): ReasoningDisplay {\n return useSyncExternalStore(subscribeReasoningDisplay, getReasoningDisplay, getReasoningDisplay);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reasoning-pref-BUcFF_5G.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/composer/composer-queries.ts","../../../../../../src/web-ui/routes/verse/chat/reasoning-pref.ts"],"sourcesContent":["/**\n * routes/verse/composer/composer-queries.ts — every call the 3.10 composer\n * makes (unit C3; core/verse/session-controls-api.ts is the server side).\n *\n * Same conventions as ../context/context-queries.ts:\n * - WRITES pull the held mutation token, throw `VerseMutationLockedError`\n * when none is held (so the caller's token gate can ask for it), and touch\n * the hold on success;\n * - READS carry the read-client proof and keep the server's refusal sentence\n * (`ApiError.detail`), because these routes answer a refused read with a\n * sentence written for a person.\n *\n * Spend: none of these start a model call EXCEPT the two that exist to send —\n * a queued follow-up when nothing is running, and [Send now]. Both go through\n * the engine's one `sendTurn` chokepoint, like POST /sessions/:id/turns.\n */\nimport { getMutationToken, getReadClientProof, reportSessionExpired, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport { ApiError, apiPost } from '../../../data/client.js';\nimport type {\n VerseAttachment,\n VerseAttachmentUpload,\n VerseFilesResponse,\n VerseQueueResponse,\n VerseSessionControlDefaults,\n VerseSessionControlDefaultsUpdate,\n VerseSessionControlsResponse,\n VerseSessionControlsUpdate,\n} from '../../../../core/verse/workbench-types.js';\nimport {\n VERSE_ATTACHMENTS_PATH,\n VERSE_FILES_PATH,\n VERSE_QUEUE_PATH,\n VERSE_SESSION_CONTROL_DEFAULTS_PATH,\n VERSE_SESSION_CONTROLS_PATH,\n} from '../../../../core/verse/workbench-types.js';\nimport { VERSE_SESSIONS_KEY, VerseMutationLockedError } from '../verse-queries.js';\n\n/** POST /queue and /send answer with the queue plus the turn they started, if any. */\nexport type VerseQueueSendResult = VerseQueueResponse & { sentTurnId: string | null };\n\nasync function refusalDetail(res: Response): Promise<{ detail: string | null; code: string | null }> {\n try {\n const body = (await res.json()) as { error?: unknown; code?: unknown };\n return {\n detail: typeof body.error === 'string' && body.error ? body.error : null,\n code: typeof body.code === 'string' && body.code ? body.code : null,\n };\n } catch {\n return { detail: null, code: null };\n }\n}\n\nasync function getJson<T>(path: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(path, {\n method: 'GET',\n credentials: 'same-origin',\n headers: { 'x-ashlr-read-client': getReadClientProof() },\n ...(signal ? { signal } : {}),\n });\n if (res.status === 401) {\n reportSessionExpired();\n throw new ApiError('Read session expired.', 401, path);\n }\n if (!res.ok) {\n const { detail, code } = await refusalDetail(res);\n throw new ApiError(`GET ${path} failed (HTTP ${res.status})${detail ? `: ${detail}` : ''}.`, res.status, path, detail, code);\n }\n return (await res.json()) as T;\n}\n\nasync function post<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new VerseMutationLockedError();\n const result = await apiPost<T>(path, body, token, signal);\n touchMutationHold();\n return result;\n}\n\nconst enc = encodeURIComponent;\n\n// ---------------------------------------------------------------------------\n// Controls\n// ---------------------------------------------------------------------------\n\nexport function fetchSessionControls(sessionId: string, signal?: AbortSignal): Promise<VerseSessionControlsResponse> {\n return getJson(`${VERSE_SESSION_CONTROLS_PATH}/${enc(sessionId)}`, signal);\n}\n\nexport async function updateSessionControls(sessionId: string, update: VerseSessionControlsUpdate): Promise<VerseSessionControlsResponse> {\n const result = await post<VerseSessionControlsResponse>(`${VERSE_SESSION_CONTROLS_PATH}/${enc(sessionId)}`, update);\n // A model switch changes the record the header and the chat list draw.\n if (update.model !== undefined) invalidate(VERSE_SESSIONS_KEY);\n return result;\n}\n\nexport function fetchControlDefaults(signal?: AbortSignal): Promise<VerseSessionControlDefaults> {\n return getJson(VERSE_SESSION_CONTROL_DEFAULTS_PATH, signal);\n}\n\nexport function updateControlDefaults(update: VerseSessionControlDefaultsUpdate): Promise<VerseSessionControlDefaults> {\n return post(VERSE_SESSION_CONTROL_DEFAULTS_PATH, update);\n}\n\n// ---------------------------------------------------------------------------\n// Attachments\n// ---------------------------------------------------------------------------\n\nexport function uploadAttachment(sessionId: string, upload: VerseAttachmentUpload, signal?: AbortSignal): Promise<VerseAttachment> {\n return post(`${VERSE_ATTACHMENTS_PATH}/${enc(sessionId)}`, upload, signal);\n}\n\nexport function deleteAttachment(sessionId: string, attachmentId: string): Promise<{ sessionId: string; items: VerseAttachment[] }> {\n return post(`${VERSE_ATTACHMENTS_PATH}/${enc(sessionId)}/${enc(attachmentId)}/delete`, {});\n}\n\n// ---------------------------------------------------------------------------\n// Queue\n// ---------------------------------------------------------------------------\n\nexport function fetchQueue(sessionId: string, signal?: AbortSignal): Promise<VerseQueueResponse> {\n return getJson(`${VERSE_QUEUE_PATH}/${enc(sessionId)}`, signal);\n}\n\nexport function enqueueFollowUp(sessionId: string, text: string, opts: { sendNow?: boolean } = {}): Promise<VerseQueueSendResult> {\n return post(`${VERSE_QUEUE_PATH}/${enc(sessionId)}`, opts.sendNow ? { text, sendNow: true } : { text });\n}\n\nexport function removeFollowUp(sessionId: string, queueId: string): Promise<VerseQueueResponse> {\n return post(`${VERSE_QUEUE_PATH}/${enc(sessionId)}/${enc(queueId)}/delete`, {});\n}\n\nexport function sendFollowUpNow(sessionId: string, queueId: string): Promise<VerseQueueSendResult> {\n return post(`${VERSE_QUEUE_PATH}/${enc(sessionId)}/${enc(queueId)}/send`, {});\n}\n\n// ---------------------------------------------------------------------------\n// Files (`@`)\n// ---------------------------------------------------------------------------\n\n/** The chat's cwd root rides along (additive), so a match there is written relative. */\nexport type VerseFilesResult = VerseFilesResponse & { primaryRoot?: string | null };\n\nexport function searchSessionFiles(sessionId: string, query: string, signal?: AbortSignal): Promise<VerseFilesResult> {\n const params = new URLSearchParams({ sessionId, q: query });\n return getJson(`${VERSE_FILES_PATH}?${params.toString()}`, signal);\n}\n","/**\n * routes/verse/chat/reasoning-pref.ts — how the transcript shows the model's\n * reasoning (SPEC-310C §2 \"Settings offers Expanded, Collapsed or Hidden\";\n * unit C2 owns the behaviour, C1's Settings ▸ Chat panel renders the control).\n *\n * collapsed (default) a block streams in a three-line window while the\n * model thinks, then folds to \"Thought 12s · ~1.8k tok ▸\";\n * expanded the same stream, but a finished block stays open;\n * hidden no reasoning in the transcript at all — the live status line\n * still says \"Thinking · 12s\", so a long think is never mistaken\n * for a hang.\n *\n * A per-device preference, so it lives in this browser's storage under its\n * own key (the precedent of resources-collapse / chat-panel-sizing: display\n * preferences that are not part of the shell's v3 blob). Framework-free store\n * + one hook; every read and write is try/catch'd because storage can be\n * absent (private window, blocked site data).\n */\nimport { useSyncExternalStore } from 'react';\n\nexport const REASONING_DISPLAYS = ['collapsed', 'expanded', 'hidden'] as const;\nexport type ReasoningDisplay = (typeof REASONING_DISPLAYS)[number];\n\nexport const REASONING_DISPLAY_LABEL: Readonly<Record<ReasoningDisplay, string>> = {\n collapsed: 'Collapsed',\n expanded: 'Expanded',\n hidden: 'Hidden',\n};\n\nexport const REASONING_DISPLAY_KEY = 'ashlr.verse.reasoning.v1';\nexport const DEFAULT_REASONING_DISPLAY: ReasoningDisplay = 'collapsed';\n\nexport function isReasoningDisplay(value: unknown): value is ReasoningDisplay {\n return typeof value === 'string' && (REASONING_DISPLAYS as readonly string[]).includes(value);\n}\n\nfunction read(): ReasoningDisplay {\n try {\n const raw = localStorage.getItem(REASONING_DISPLAY_KEY);\n return isReasoningDisplay(raw) ? raw : DEFAULT_REASONING_DISPLAY;\n } catch {\n return DEFAULT_REASONING_DISPLAY;\n }\n}\n\nlet current: ReasoningDisplay | null = null;\nconst listeners = new Set<() => void>();\n\nexport function getReasoningDisplay(): ReasoningDisplay {\n if (current === null) current = read();\n return current;\n}\n\nexport function setReasoningDisplay(next: ReasoningDisplay): void {\n if (!isReasoningDisplay(next) || next === getReasoningDisplay()) return;\n current = next;\n try {\n localStorage.setItem(REASONING_DISPLAY_KEY, next);\n } catch {\n /* the choice still holds for this page */\n }\n for (const listener of [...listeners]) listener();\n}\n\nexport function subscribeReasoningDisplay(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\n/** Test seam: forget the in-memory value so the next read re-reads storage. */\nexport function resetReasoningDisplay(): void {\n current = null;\n for (const listener of [...listeners]) listener();\n}\n\nexport function useReasoningDisplay(): ReasoningDisplay {\n return useSyncExternalStore(subscribeReasoningDisplay, getReasoningDisplay, getReasoningDisplay);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./nav-NVtnuG7A.js";var t=3e3;function n(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/["\\]/g,`\\$&`)}function r(t,r,i=document){let a=i.querySelector(`[data-surface="${n(t)}"]`);return a?a.querySelector(`#${n(e(r))}`)??a.querySelector(`[data-verse-anchor="${n(r)}"]`):null}function i(e){typeof e.scrollIntoView==`function`&&e.scrollIntoView({block:`center`,behavior:`smooth`}),(e.tabIndex>=0||e.matches(`a[href],button,input,select,textarea,[tabindex]`))&&e.focus({preventScroll:!0})}function a({section:e,anchor:n},a=t){if(typeof document>`u`)return Promise.resolve(!1);let o=r(e,n);return o&&!o.closest(`[hidden]`)?(i(o),Promise.resolve(!0)):new Promise(t=>{let o=!1,s=e=>{o||(o=!0,l.disconnect(),window.clearTimeout(u),e&&i(e),t(e!==null))},c=()=>{let t=r(e,n);t&&!t.closest(`[hidden]`)&&s(t)},l=new MutationObserver(c);l.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`hidden`,`id`,`data-verse-anchor`]});let u=window.setTimeout(()=>s(null),a);c()})}export{t as ANCHOR_WAIT_MS,r as findAnchorElement,a as revealAnchor};
2
+ //# sourceMappingURL=reveal-anchor-D-tV8xau.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reveal-anchor-D-tV8xau.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/reveal-anchor.ts"],"sourcesContent":["/**\n * routes/verse/shell/reveal-anchor.ts — \"go to that card\" for any surface\n * (unit C1; C7 cross-unit request 3).\n *\n * Needs-you items, Leader memos and Command cards point INTO a surface\n * (\"memo:m-7\" on Mind, \"seat:claude-a\" on Apps). Whoever raises it calls\n * `setVerseSection(section, anchor)` (or dispatches VERSE_ANCHOR_EVENT); the\n * shell listens once (VerseApp, through shell/anchor-requests.ts) and reveals\n * the element here.\n *\n * WHY WAIT, AND FOR HOW LONG. The target surface may be a lazy chunk that has\n * not loaded, a keep-alive surface un-hiding, or a list still fetching its\n * rows. Two animation frames (C7's own fallback in command/nav.ts) cover the\n * keep-alive case only; a cold chunk takes longer. So this watches the\n * surface's subtree until the element appears, and gives up quietly after\n * ANCHOR_WAIT_MS — a missing anchor is never an error, the operator is\n * already on the right surface.\n *\n * WHICH ELEMENT. C7 stamps `id=\"c7-anchor-<key>\"` (command/nav.ts anchorId);\n * any other owner may stamp `data-verse-anchor=\"<key>\"` instead of adopting\n * C7's id scheme. The search is scoped to `[data-surface=<section>]` so an\n * identically-keyed card on a HIDDEN keep-alive surface never wins.\n */\nimport { anchorId } from '../command/nav.js';\nimport type { VerseAnchorRequest } from '../verse-ui-store.js';\n\nexport const ANCHOR_WAIT_MS = 3_000;\n\nfunction cssEscape(value: string): string {\n return typeof CSS !== 'undefined' && typeof CSS.escape === 'function' ? CSS.escape(value) : value.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\n/** The anchor's element on `section`'s surface, or null when it is not in the DOM (yet). */\nexport function findAnchorElement(section: string, anchor: string, root: ParentNode = document): HTMLElement | null {\n const host = root.querySelector<HTMLElement>(`[data-surface=\"${cssEscape(section)}\"]`);\n if (!host) return null;\n return (\n host.querySelector<HTMLElement>(`#${cssEscape(anchorId(anchor))}`) ??\n host.querySelector<HTMLElement>(`[data-verse-anchor=\"${cssEscape(anchor)}\"]`)\n );\n}\n\nfunction reveal(el: HTMLElement): void {\n if (typeof el.scrollIntoView === 'function') el.scrollIntoView({ block: 'center', behavior: 'smooth' });\n // Keyboard users land ON the card, not just near it. Only an element that\n // can already take focus is focused — the shell never rewrites another\n // unit's tab order to make a div focusable.\n if (el.tabIndex >= 0 || el.matches('a[href],button,input,select,textarea,[tabindex]')) {\n el.focus({ preventScroll: true });\n }\n}\n\n/**\n * Reveal `anchor` on `section`. Resolves true when it was found and scrolled\n * to, false when it did not appear within `waitMs`. Never throws.\n */\nexport function revealAnchor({ section, anchor }: VerseAnchorRequest, waitMs: number = ANCHOR_WAIT_MS): Promise<boolean> {\n if (typeof document === 'undefined') return Promise.resolve(false);\n const now = findAnchorElement(section, anchor);\n if (now && !now.closest('[hidden]')) {\n reveal(now);\n return Promise.resolve(true);\n }\n return new Promise((resolve) => {\n let done = false;\n const finish = (el: HTMLElement | null) => {\n if (done) return;\n done = true;\n observer.disconnect();\n window.clearTimeout(timer);\n if (el) reveal(el);\n resolve(el !== null);\n };\n const check = () => {\n const el = findAnchorElement(section, anchor);\n // Wait for the surface to be un-hidden too: scrolling a hidden element is a no-op.\n if (el && !el.closest('[hidden]')) finish(el);\n };\n const observer = new MutationObserver(check);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['hidden', 'id', 'data-verse-anchor'] });\n const timer = window.setTimeout(() => finish(null), waitMs);\n check();\n });\n}\n"],"mappings":"sCA0BA,IAAa,EAAiB,IAE9B,SAAS,EAAU,EAAuB,CACxC,OAAO,OAAO,IAAQ,KAAe,OAAO,IAAI,QAAW,WAAa,IAAI,OAAO,CAAK,EAAI,EAAM,QAAQ,SAAU,MAAM,CAC5H,CAGA,SAAgB,EAAkB,EAAiB,EAAgB,EAAmB,SAA8B,CAClH,IAAM,EAAO,EAAK,cAA2B,kBAAkB,EAAU,CAAO,EAAE,GAAG,EAErF,OADK,EAEH,EAAK,cAA2B,IAAI,EAAU,EAAS,CAAM,CAAC,GAAG,GACjE,EAAK,cAA2B,uBAAuB,EAAU,CAAM,EAAE,GAAG,EAH5D,IAKpB,CAEA,SAAS,EAAO,EAAuB,CACjC,OAAO,EAAG,gBAAmB,YAAY,EAAG,eAAe,CAAE,MAAO,SAAU,SAAU,QAAS,CAAC,GAIlG,EAAG,UAAY,GAAK,EAAG,QAAQ,iDAAiD,IAClF,EAAG,MAAM,CAAE,cAAe,EAAK,CAAC,CAEpC,CAMA,SAAgB,EAAa,CAAE,UAAS,UAA8B,EAAiB,EAAkC,CACvH,GAAI,OAAO,SAAa,IAAa,OAAO,QAAQ,QAAQ,EAAK,EACjE,IAAM,EAAM,EAAkB,EAAS,CAAM,EAK7C,OAJI,GAAO,CAAC,EAAI,QAAQ,UAAU,GAChC,EAAO,CAAG,EACH,QAAQ,QAAQ,EAAI,GAEtB,IAAI,QAAS,GAAY,CAC9B,IAAI,EAAO,GACL,EAAU,GAA2B,CACrC,IACJ,EAAO,GACP,EAAS,WAAW,EACpB,OAAO,aAAa,CAAK,EACrB,GAAI,EAAO,CAAE,EACjB,EAAQ,IAAO,IAAI,EACrB,EACM,MAAc,CAClB,IAAM,EAAK,EAAkB,EAAS,CAAM,EAExC,GAAM,CAAC,EAAG,QAAQ,UAAU,GAAG,EAAO,CAAE,CAC9C,EACM,EAAW,IAAI,iBAAiB,CAAK,EAC3C,EAAS,QAAQ,SAAS,KAAM,CAAE,UAAW,GAAM,QAAS,GAAM,WAAY,GAAM,gBAAiB,CAAC,SAAU,KAAM,mBAAmB,CAAE,CAAC,EAC5I,IAAM,EAAQ,OAAO,eAAiB,EAAO,IAAI,EAAG,CAAM,EAC1D,EAAM,CACR,CAAC,CACH"}
@@ -0,0 +1,2 @@
1
+ import{b as e,r as t,v as n,x as r}from"./verse-model-DyCwZF-M.js";import{g as i,y as a}from"./format-BkWdOQDL.js";var o={ready:`ready`,tight:`tight`,exhausted:`blocked`,"signed-out":`blocked`,unknown:`unread`};function s(e,t=Date.now()){let n=a(e,t);return n===null?null:`resets ${n}`}function c(e,t){let n=`${e}_`,i=t;for(;i.startsWith(n)&&i.length>n.length;)i=i.slice(n.length);return r(i)}function l(e,t,n){let r=t.resetDescription??(t.resetsAt===null?null:s(t.resetsAt,n));return{id:t.id,label:c(e,t.id),usedPercent:t.limitReached||!t.measured?null:t.usedPercent,limitReached:t.limitReached,resetText:r,resetsAt:t.resetsAt}}function u(e,t){let n=typeof e.usedPercent==`number`&&Number.isFinite(e.usedPercent)?Math.max(0,Math.min(100,e.usedPercent)):null,i=n!==null&&n>=100;return{id:e.id,label:r(e.id),usedPercent:i?null:n,limitReached:i,resetText:e.resetsAt===null?null:s(e.resetsAt,t),resetsAt:e.resetsAt}}function d(e){if(e===null)return{phrase:null,title:null};if(e.unlimited)return{phrase:`credits unlimited`,title:null};if(!e.hasCredits)return{phrase:null,title:null};if(e.balance===null)return{phrase:`credits available`,title:null};let t=Number.parseFloat(e.balance);return Number.isFinite(t)?{phrase:`${t.toFixed(2)} credits left`,title:e.balance}:{phrase:`credits available`,title:e.balance}}function f(n){let r=e(n),i=n.health.state===`ready`,a=r===null?i?`ready`:`unread`:`blocked`;return{kind:`local`,plan:null,cls:a,word:t[a],summary:r??(i?`runs on this machine`:`readiness not reported`),binding:null,others:[],credits:null,creditsTitle:null,notes:n.health.summary===null?[]:[n.health.summary],evidenceSource:null,observedAt:n.health.observedAt,extended:!1}}function p(r,i){let a=n(r),o=r.health.windows.map(e=>u(e,i)),s=o.find(e=>e.limitReached)??null,c=o.filter(e=>e.usedPercent!==null),l=c.length===0?null:c.reduce((e,t)=>(t.usedPercent??0)>(e.usedPercent??0)?t:e),d=s??l,f=e(r),p=f===null?a.cls:`blocked`;return{kind:`subscription`,plan:null,cls:p,word:t[p],summary:f??a.text,binding:d,others:d===null?o:o.filter(e=>e.id!==d.id),credits:null,creditsTitle:null,notes:[],evidenceSource:null,observedAt:r.health.observedAt,extended:!1}}function m(n,r=Date.now()){if(n.engine===`local`)return f(n);let a=n.capacity;if(a===void 0)return p(n,r);let s=a.windows.map(e=>l(n.engine,e,r)),c=a.binding,u=c===null?null:s.find(e=>e.id===c.id)??l(n.engine,c,r),m=u===null?s:s.filter(e=>e.id!==u.id),h=d(a.credits),g=e(n),_=g===null?o[a.usability]:`blocked`,v;return v=a.usability===`signed-out`?`signed out — reconnect this account`:g===null?u===null?`no capacity reading`:u.limitReached?a.usability===`tight`&&h.phrase!==null?`${u.label} limit reached · credits still spendable`:`${u.label} limit reached`:u.usedPercent===null?`no capacity reading`:`${i(u.usedPercent)} of ${u.label} used`:g,{kind:`subscription`,plan:a.planType,cls:_,word:t[_],summary:v,binding:u,others:m,credits:h.phrase,creditsTitle:h.title,notes:[...a.notes],evidenceSource:a.evidenceSource,observedAt:a.observedAt??n.health.observedAt,extended:!0}}function h(e){return typeof e==`string`&&Number.isFinite(Date.parse(e))}function g(e){return e.limitReached||e.usedPercent!==null&&e.usedPercent>=100}function _(e,t){let n=h(t)?t:null,r=(e.binding===null?e.others:[e.binding,...e.others]).filter(g);if(r.length>0){if(r.some(e=>!h(e.resetsAt)))return null;let e=r.map(e=>e.resetsAt);return n!==null&&e.push(n),e.reduce((e,t)=>Date.parse(t)>Date.parse(e)?t:e)}return n===null?e.binding!==null&&h(e.binding.resetsAt)?e.binding.resetsAt:null:n}function v(e,t){let n=[e.label];return t.plan!==null&&n.push(t.plan),n.push(t.word),n.push(t.summary),t.credits!==null&&n.push(t.credits),t.binding?.resetText!=null&&n.push(t.binding.resetText),n.join(` · `)}function y(e){return e===`tight`||e===`blocked`}export{y as i,m as n,v as r,_ as t};
2
+ //# sourceMappingURL=seat-subscription-B-f9ViU2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"seat-subscription-CH6fW4fJ.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/seat-subscription.ts"],"sourcesContent":["/**\n * routes/verse/seat-subscription.ts — \"what subscription is behind this seat,\n * and how much of it is left\", projected ONCE for every chat surface that\n * shows a seat: the resources panel, the seat selector, the new-chat dialog,\n * the sidebar and the chat header.\n *\n * WHY THIS MODULE EXISTS\n *\n * The resources panel showed a bare \"unknown\" pill for every account — the\n * most-looked-at surface in the app. Measured against a live server,\n * `GET /api/verse/accounts` had all four accounts observed, Claude with three\n * windows and Grok with one, while `bootstrap.seats[].health` was `unknown`\n * forever for Claude and Grok: seat health came from a shared-evidence file\n * scoped `codex-native-metadata`, which structurally cannot carry those two\n * providers. The live data existed and was discarded at the last hop.\n *\n * Owner S's V2.1 contract fixes the supply side — `VerseSeat.capacity`\n * (core/verse/types.ts) now carries the plan, the binding window, the full\n * window list, credits, a usability verdict, the evidence source and the\n * provider's own notes. This module is the single place the chat surfaces\n * read it, so they cannot describe one seat four different ways.\n *\n * TWO SOURCES, ONE VIEW. `capacity` is `undefined` for local seats (an Ollama\n * tag has no subscription) and for a native seat the server could not build a\n * record for. The V1 `health.windows` shape is still read as the fallback, and\n * on that path the verdict is delegated WHOLESALE to\n * `verse-model.seatCapacity` — the seat selector and the composer's seat menu\n * already render those exact words, and two implementations of one rule is\n * how the picker and the panel would drift apart again.\n *\n * HONESTY RULES (docs/VERSE-TELEMETRY-V2.md, closing section) — every one is a\n * place where the obvious projection would lie:\n *\n * 1. THE BINDING WINDOW LEADS. An account is blocked by its WORST window, not\n * its first and not its average. Claude's weekly per-model window sat at\n * 100% while all-models read 58%; leading with 58% is a lie of emphasis.\n * 2. NO SIGNAL IS NOT ZERO. An unread window gets no meter at all. An empty\n * bar reads \"plenty left\" and a full one reads \"exhausted\"; both are false\n * when nothing was measured.\n * 3. THE SENTINEL 100 IS A FLAG. Codex writes `rateLimitReachedType` upstream\n * as the value 100. `measured: false` marks it; it renders as \"limit\n * reached\", never as \"100% used\", and carries no percentage.\n * 4. CREDITS ARE NOT THE WINDOW. A Codex account at 100% of its weekly window\n * with a spendable balance is still usable. The server already encodes\n * that in `usability: 'tight'`; this module reports both facts and never\n * collapses them into one.\n * 5. ONLY A MACHINE-READABLE INSTANT MAY BE FORMATTED. Claude's `resetsAt` is\n * structurally always null and its reset is provider prose; prose is\n * rendered VERBATIM, never parsed and never turned into a countdown.\n *\n * Pure: no React, no I/O.\n */\nimport { describeResetAt } from '../../../core/verse/seat-readiness.js';\nimport type { VerseEngine, VerseSeat } from '../../data/api-types.js';\nimport { percentText } from './autonomy/format.js';\nimport {\n SEAT_CAPACITY_WORD,\n seatCapacity,\n seatUnavailableReason,\n seatWindowLabel,\n type SeatCapacityClass,\n} from './verse-model.js';\n\nexport type { SeatCapacityClass };\n\n/**\n * Owner S's shapes, reached through `VerseSeat` rather than imported by name.\n *\n * This is the drift guard, and it costs nothing: rename or reshape\n * `VerseSeatCapacity` in core/verse/types.ts and every read below stops\n * compiling HERE, at the seam, instead of silently degrading the panel back to\n * the \"unknown\" it was built to fix.\n */\ntype SeatCapacityRecord = NonNullable<VerseSeat['capacity']>;\ntype SeatCapacityWindow = SeatCapacityRecord['windows'][number];\n\n/** One quota window, in the shape every chat surface renders. */\nexport interface SeatWindowView {\n id: string;\n /** Human label, derived from the provider's id. */\n label: string;\n /**\n * 0–100, or null. Null means \"no reading\" OR \"the figure is a sentinel\".\n * NEVER a substituted zero, and a view must not draw a meter for it.\n */\n usedPercent: number | null;\n /** The provider flagged the limit instead of measuring it. */\n limitReached: boolean;\n /**\n * Reset text, ready to render: provider prose VERBATIM when that is what the\n * provider published, a formatted local time when — and only when — it gave\n * a machine-readable instant, null when it gave neither.\n */\n resetText: string | null;\n /**\n * The machine-readable reset instant (ISO), when there was one. The only\n * field a countdown could ever legitimately be derived from; nothing in the\n * chat surfaces derives one today.\n */\n resetsAt: string | null;\n}\n\nexport interface SeatSubscriptionView {\n /**\n * Local seats have no subscription, no quota and no bill — only a readiness\n * — and must not be dressed in meters that imply otherwise.\n */\n kind: 'subscription' | 'local';\n /** Plan tier as the provider names it: \"max\", \"pro\", \"SuperGrok\". */\n plan: string | null;\n cls: SeatCapacityClass;\n /** The class as a word. State is never communicated by colour alone. */\n word: string;\n /**\n * One short phrase for an option label, a pill title or a menu row —\n * \"92% of weekly fable window used\", \"primary window limit reached\".\n */\n summary: string;\n /** The window that actually constrains work. Null when nothing was read. */\n binding: SeatWindowView | null;\n /** Every other window, in provider order. */\n others: SeatWindowView[];\n /** Codex credits as a rendered phrase. Independent of the window. */\n credits: string | null;\n /** The provider's raw balance string, kept verbatim for a title attribute. */\n creditsTitle: string | null;\n /**\n * The provider's own plain-language facts. Owner S's contract says these\n * must be SHOWN rather than left to imply a fault, so they travel here and\n * the panel prints them.\n */\n notes: string[];\n /**\n * Where the windows came from, so nothing implies freshness it lacks.\n * Null on the V1 fallback path, which has no provenance to report.\n */\n evidenceSource: SeatCapacityRecord['evidenceSource'] | null;\n /** When the reading was taken, ISO. Null when nothing was observed. */\n observedAt: string | null;\n /** True when the seat carried owner S's `capacity` record. */\n extended: boolean;\n}\n\n/**\n * S's five-state verdict, mapped onto the four words this app already uses.\n *\n * `signed-out` is BLOCKED-by-action, not unread: we know exactly why there is\n * no capacity and what to do about it. `unknown` is the only genuinely unread\n * state — nobody told us anything, and calling that blocked would assert an\n * outage we never observed.\n *\n * Typed as a total map over S's union, so adding a verdict upstream fails the\n * build here rather than falling through to a silent default.\n */\nconst USABILITY_CLASS: Record<SeatCapacityRecord['usability'], SeatCapacityClass> = {\n ready: 'ready',\n tight: 'tight',\n exhausted: 'blocked',\n 'signed-out': 'blocked',\n unknown: 'unread',\n};\n\n/**\n * A machine-readable instant, formatted — in the app's ONE reset wording,\n * `describeResetAt`'s: \"resets today 11:46 PM\", \"resets Fri 11:46 PM\",\n * \"resets Sep 26, 11:46 PM\". (A second wording here — \"resets Sep 25 at\n * 11:46 PM\" — once reached the chat header and the seat meters while Accounts\n * said \"resets Fri 11:46 PM\" for the same instant.) Prose resets never reach\n * this — they are passed through untouched, because the provider's sentence\n * already carries the timezone it means and reformatting it would be a guess.\n */\nexport function formatResetInstant(iso: string, now: number = Date.now()): string | null {\n const when = describeResetAt(iso, now);\n return when === null ? null : `resets ${when}`;\n}\n\n/**\n * Window ids arrive provider-prefixed and sometimes doubly so\n * (`codex_codex_primary`, `grok_unified_weekly`). Strip the engine's own name\n * off the front before handing the rest to the shared labeller, so the panel\n * reads \"primary window\" rather than \"codex codex primary window\".\n *\n * Only the `capacity` path uses this. The V1 fallback goes through\n * `seatWindowLabel` unaltered, because the seat selector's wording is pinned\n * to it by test.\n */\nexport function seatCapacityWindowLabel(engine: VerseEngine, id: string): string {\n const prefix = `${engine}_`;\n let rest = id;\n while (rest.startsWith(prefix) && rest.length > prefix.length) rest = rest.slice(prefix.length);\n return seatWindowLabel(rest);\n}\n\nfunction toWindowView(engine: VerseEngine, w: SeatCapacityWindow, now: number): SeatWindowView {\n const reset = w.resetDescription ?? (w.resetsAt === null ? null : formatResetInstant(w.resetsAt, now));\n return {\n id: w.id,\n label: seatCapacityWindowLabel(engine, w.id),\n // A flagged limit, and any unmeasured figure, is a sentinel — not a reading.\n usedPercent: w.limitReached || !w.measured ? null : w.usedPercent,\n limitReached: w.limitReached,\n resetText: reset,\n resetsAt: w.resetsAt,\n };\n}\n\n/** The V1 `health.windows` shape, for a seat with no `capacity` record. */\nfunction legacyWindowView(w: VerseSeat['health']['windows'][number], now: number): SeatWindowView {\n const used = typeof w.usedPercent === 'number' && Number.isFinite(w.usedPercent)\n ? Math.max(0, Math.min(100, w.usedPercent))\n : null;\n // Upstream writes a denial as 100, so on this shape a bare 100 cannot be\n // told apart from a measured 100 — and the safe reading of both is \"spent\".\n const flagged = used !== null && used >= 100;\n return {\n id: w.id,\n label: seatWindowLabel(w.id),\n usedPercent: flagged ? null : used,\n limitReached: flagged,\n resetText: w.resetsAt === null ? null : formatResetInstant(w.resetsAt, now),\n resetsAt: w.resetsAt,\n };\n}\n\nfunction creditsPhrase(credits: SeatCapacityRecord['credits']): { phrase: string | null; title: string | null } {\n if (credits === null) return { phrase: null, title: null };\n if (credits.unlimited) return { phrase: 'credits unlimited', title: null };\n if (!credits.hasCredits) return { phrase: null, title: null };\n if (credits.balance === null) return { phrase: 'credits available', title: null };\n const parsed = Number.parseFloat(credits.balance);\n if (!Number.isFinite(parsed)) return { phrase: 'credits available', title: credits.balance };\n // The provider publishes a long decimal (\"2048.4196250000\"). Two places is a\n // rendering choice, not a new number — the raw string rides in the title.\n return { phrase: `${parsed.toFixed(2)} credits left`, title: credits.balance };\n}\n\nfunction localView(seat: VerseSeat): SeatSubscriptionView {\n const unavailable = seatUnavailableReason(seat);\n const ready = seat.health.state === 'ready';\n const cls: SeatCapacityClass = unavailable !== null ? 'blocked' : ready ? 'ready' : 'unread';\n return {\n kind: 'local',\n plan: null,\n cls,\n word: SEAT_CAPACITY_WORD[cls],\n summary: unavailable ?? (ready ? 'runs on this machine' : 'readiness not reported'),\n binding: null,\n others: [],\n credits: null,\n creditsTitle: null,\n notes: seat.health.summary === null ? [] : [seat.health.summary],\n evidenceSource: null,\n observedAt: seat.health.observedAt,\n extended: false,\n };\n}\n\n/** The V1 path: no `capacity` record, so the V1 rule decides, unchanged. */\nfunction fallbackView(seat: VerseSeat, now: number): SeatSubscriptionView {\n const legacy = seatCapacity(seat);\n const windows = seat.health.windows.map((w) => legacyWindowView(w, now));\n const flagged = windows.find((w) => w.limitReached) ?? null;\n const measured = windows.filter((w) => w.usedPercent !== null);\n const worst = measured.length === 0\n ? null\n : measured.reduce((a, b) => ((b.usedPercent ?? 0) > (a.usedPercent ?? 0) ? b : a));\n const binding = flagged ?? worst;\n const unavailable = seatUnavailableReason(seat);\n const cls = unavailable !== null ? 'blocked' : legacy.cls;\n return {\n kind: 'subscription',\n plan: null,\n cls,\n word: SEAT_CAPACITY_WORD[cls],\n summary: unavailable ?? legacy.text,\n binding,\n others: binding === null ? windows : windows.filter((w) => w.id !== binding.id),\n credits: null,\n creditsTitle: null,\n notes: [],\n evidenceSource: null,\n observedAt: seat.health.observedAt,\n extended: false,\n };\n}\n\n/**\n * The whole projection. Local seats short-circuit: they have no subscription,\n * so everything below the readiness line is deliberately empty rather than\n * zeroed. `now` is the clock the reset wording is relative to (\"today\" vs a\n * weekday); it defaults to the real one.\n */\nexport function seatSubscription(seat: VerseSeat, now: number = Date.now()): SeatSubscriptionView {\n if (seat.engine === 'local') return localView(seat);\n const capacity = seat.capacity;\n if (capacity === undefined) return fallbackView(seat, now);\n\n const windows = capacity.windows.map((w) => toWindowView(seat.engine, w, now));\n // Resolve the server's CHOICE of binding window back to this seat's own\n // view of it, so the two can never disagree about a reset or a label.\n const served = capacity.binding;\n const binding = served === null\n ? null\n : (windows.find((w) => w.id === served.id) ?? toWindowView(seat.engine, served, now));\n const others = binding === null ? windows : windows.filter((w) => w.id !== binding.id);\n const credits = creditsPhrase(capacity.credits);\n\n const unavailable = seatUnavailableReason(seat);\n const cls: SeatCapacityClass = unavailable !== null ? 'blocked' : USABILITY_CLASS[capacity.usability];\n\n let summary: string;\n // Signed out is checked FIRST, before the generic unavailable reason: owner\n // S maps `signed-out` onto `health.state: 'unavailable'` (it is the one\n // account state worth a red dot, because it has a remedy), and the generic\n // reason there is a bare \"seat unavailable\" that says nothing about the fix.\n // A signed-out account has no measurement at all — it is an action, never a\n // 0% meter and never a bare \"unknown\".\n if (capacity.usability === 'signed-out') {\n summary = 'signed out — reconnect this account';\n } else if (unavailable !== null) {\n summary = unavailable;\n } else if (binding === null) {\n summary = 'no capacity reading';\n } else if (binding.limitReached) {\n // The server already weighed credits against the spent window; `tight`\n // there means \"spent, but there is still something to spend\".\n summary = capacity.usability === 'tight' && credits.phrase !== null\n ? `${binding.label} limit reached · credits still spendable`\n : `${binding.label} limit reached`;\n } else if (binding.usedPercent === null) {\n summary = 'no capacity reading';\n } else {\n // The one percent rule: \"99%\", never a rounded \"100%\" beside a \"99%\" bar.\n summary = `${percentText(binding.usedPercent)} of ${binding.label} used`;\n }\n\n return {\n kind: 'subscription',\n plan: capacity.planType,\n cls,\n word: SEAT_CAPACITY_WORD[cls],\n summary,\n binding,\n others,\n credits: credits.phrase,\n creditsTitle: credits.title,\n notes: [...capacity.notes],\n evidenceSource: capacity.evidenceSource,\n observedAt: capacity.observedAt ?? seat.health.observedAt,\n extended: true,\n };\n}\n\n/**\n * How much of a freshness claim the evidence supports. A reading served from\n * another process's shared file, or from an operator-seeded baseline, is not\n * a live probe and must not be presented as one.\n */\nexport function evidenceNote(source: SeatSubscriptionView['evidenceSource']): string | null {\n switch (source) {\n case 'collector':\n return null;\n case 'shared-evidence':\n return 'read from another collector’s shared evidence, not probed here';\n case 'baseline':\n return 'operator-seeded baseline, not a live reading';\n case 'none':\n return 'no evidence source — nothing here was measured';\n default:\n return null;\n }\n}\n\nfunction parseableInstant(iso: string | null | undefined): iso is string {\n return typeof iso === 'string' && Number.isFinite(Date.parse(iso));\n}\n\nfunction isSpentWindow(w: Pick<SeatWindowView, 'limitReached' | 'usedPercent'>): boolean {\n return w.limitReached || (w.usedPercent !== null && w.usedPercent >= 100);\n}\n\n/**\n * When a spent seat is usable again. A seat reopens only once EVERY spent\n * window has reset — a Codex seat whose 5-hour window resets in 2h but whose\n * weekly window is also spent until Wednesday is still spent in 2h — so this\n * is the LATEST reset among the spent windows, matching Fleet's \"eligible\n * again\" (fleet/why-seat-model `eligibleAgain`) and the router's headroom\n * `lastReset`. Every surface that says \"resets …\" / \"usable again in …\" for\n * a spent seat (Accounts, the health banner) asks here.\n *\n * - a spent window with only provider prose (Claude) → null: when \"all of\n * them\" have reset is unknown, and prose is never parsed;\n * - `reported` (the health sweep's instant, which is its binding window's)\n * is folded in — it may know of a window this seat record does not — but\n * it can never make the answer EARLIER than a spent window's reset;\n * - nothing spent → `reported`, else the binding window's own instant.\n */\nexport function seatReopensAt(view: Pick<SeatSubscriptionView, 'binding' | 'others'>, reported?: string | null): string | null {\n const known = parseableInstant(reported) ? reported : null;\n const windows = view.binding === null ? view.others : [view.binding, ...view.others];\n const spent = windows.filter(isSpentWindow);\n if (spent.length > 0) {\n if (spent.some((w) => !parseableInstant(w.resetsAt))) return null;\n const instants = spent.map((w) => w.resetsAt as string);\n if (known !== null) instants.push(known);\n return instants.reduce((a, b) => (Date.parse(b) > Date.parse(a) ? b : a));\n }\n if (known !== null) return known;\n return view.binding !== null && parseableInstant(view.binding.resetsAt) ? view.binding.resetsAt : null;\n}\n\n/**\n * One sentence for a `title` attribute or a screen-reader name: the seat, its\n * plan, its verdict and the evidence behind it.\n */\nexport function seatSubscriptionSentence(seat: VerseSeat, view: SeatSubscriptionView): string {\n const parts = [seat.label];\n if (view.plan !== null) parts.push(view.plan);\n parts.push(view.word);\n parts.push(view.summary);\n if (view.credits !== null) parts.push(view.credits);\n if (view.binding?.resetText != null) parts.push(view.binding.resetText);\n return parts.join(' · ');\n}\n\n/** Tight and blocked are worth surfacing outside the panel; the rest is noise. */\nexport function worthFlagging(cls: SeatCapacityClass): boolean {\n return cls === 'tight' || cls === 'blocked';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"seat-subscription-B-f9ViU2.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/seat-subscription.ts"],"sourcesContent":["/**\n * routes/verse/seat-subscription.ts — \"what subscription is behind this seat,\n * and how much of it is left\", projected ONCE for every chat surface that\n * shows a seat: the resources panel, the seat selector, the new-chat dialog,\n * the sidebar and the chat header.\n *\n * WHY THIS MODULE EXISTS\n *\n * The resources panel showed a bare \"unknown\" pill for every account — the\n * most-looked-at surface in the app. Measured against a live server,\n * `GET /api/verse/accounts` had all four accounts observed, Claude with three\n * windows and Grok with one, while `bootstrap.seats[].health` was `unknown`\n * forever for Claude and Grok: seat health came from a shared-evidence file\n * scoped `codex-native-metadata`, which structurally cannot carry those two\n * providers. The live data existed and was discarded at the last hop.\n *\n * Owner S's V2.1 contract fixes the supply side — `VerseSeat.capacity`\n * (core/verse/types.ts) now carries the plan, the binding window, the full\n * window list, credits, a usability verdict, the evidence source and the\n * provider's own notes. This module is the single place the chat surfaces\n * read it, so they cannot describe one seat four different ways.\n *\n * TWO SOURCES, ONE VIEW. `capacity` is `undefined` for local seats (an Ollama\n * tag has no subscription) and for a native seat the server could not build a\n * record for. The V1 `health.windows` shape is still read as the fallback, and\n * on that path the verdict is delegated WHOLESALE to\n * `verse-model.seatCapacity` — the seat selector and the composer's seat menu\n * already render those exact words, and two implementations of one rule is\n * how the picker and the panel would drift apart again.\n *\n * HONESTY RULES (docs/VERSE-TELEMETRY-V2.md, closing section) — every one is a\n * place where the obvious projection would lie:\n *\n * 1. THE BINDING WINDOW LEADS. An account is blocked by its WORST window, not\n * its first and not its average. Claude's weekly per-model window sat at\n * 100% while all-models read 58%; leading with 58% is a lie of emphasis.\n * 2. NO SIGNAL IS NOT ZERO. An unread window gets no meter at all. An empty\n * bar reads \"plenty left\" and a full one reads \"exhausted\"; both are false\n * when nothing was measured.\n * 3. THE SENTINEL 100 IS A FLAG. Codex writes `rateLimitReachedType` upstream\n * as the value 100. `measured: false` marks it; it renders as \"limit\n * reached\", never as \"100% used\", and carries no percentage.\n * 4. CREDITS ARE NOT THE WINDOW. A Codex account at 100% of its weekly window\n * with a spendable balance is still usable. The server already encodes\n * that in `usability: 'tight'`; this module reports both facts and never\n * collapses them into one.\n * 5. ONLY A MACHINE-READABLE INSTANT MAY BE FORMATTED. Claude's `resetsAt` is\n * structurally always null and its reset is provider prose; prose is\n * rendered VERBATIM, never parsed and never turned into a countdown.\n *\n * Pure: no React, no I/O.\n */\nimport { describeResetAt } from '../../../core/verse/seat-readiness.js';\nimport type { VerseEngine, VerseSeat } from '../../data/api-types.js';\nimport { percentText } from './autonomy/format.js';\nimport {\n SEAT_CAPACITY_WORD,\n seatCapacity,\n seatUnavailableReason,\n seatWindowLabel,\n type SeatCapacityClass,\n} from './verse-model.js';\n\nexport type { SeatCapacityClass };\n\n/**\n * Owner S's shapes, reached through `VerseSeat` rather than imported by name.\n *\n * This is the drift guard, and it costs nothing: rename or reshape\n * `VerseSeatCapacity` in core/verse/types.ts and every read below stops\n * compiling HERE, at the seam, instead of silently degrading the panel back to\n * the \"unknown\" it was built to fix.\n */\ntype SeatCapacityRecord = NonNullable<VerseSeat['capacity']>;\ntype SeatCapacityWindow = SeatCapacityRecord['windows'][number];\n\n/** One quota window, in the shape every chat surface renders. */\nexport interface SeatWindowView {\n id: string;\n /** Human label, derived from the provider's id. */\n label: string;\n /**\n * 0–100, or null. Null means \"no reading\" OR \"the figure is a sentinel\".\n * NEVER a substituted zero, and a view must not draw a meter for it.\n */\n usedPercent: number | null;\n /** The provider flagged the limit instead of measuring it. */\n limitReached: boolean;\n /**\n * Reset text, ready to render: provider prose VERBATIM when that is what the\n * provider published, a formatted local time when — and only when — it gave\n * a machine-readable instant, null when it gave neither.\n */\n resetText: string | null;\n /**\n * The machine-readable reset instant (ISO), when there was one. The only\n * field a countdown could ever legitimately be derived from; nothing in the\n * chat surfaces derives one today.\n */\n resetsAt: string | null;\n}\n\nexport interface SeatSubscriptionView {\n /**\n * Local seats have no subscription, no quota and no bill — only a readiness\n * — and must not be dressed in meters that imply otherwise.\n */\n kind: 'subscription' | 'local';\n /** Plan tier as the provider names it: \"max\", \"pro\", \"SuperGrok\". */\n plan: string | null;\n cls: SeatCapacityClass;\n /** The class as a word. State is never communicated by colour alone. */\n word: string;\n /**\n * One short phrase for an option label, a pill title or a menu row —\n * \"92% of weekly fable window used\", \"primary window limit reached\".\n */\n summary: string;\n /** The window that actually constrains work. Null when nothing was read. */\n binding: SeatWindowView | null;\n /** Every other window, in provider order. */\n others: SeatWindowView[];\n /** Codex credits as a rendered phrase. Independent of the window. */\n credits: string | null;\n /** The provider's raw balance string, kept verbatim for a title attribute. */\n creditsTitle: string | null;\n /**\n * The provider's own plain-language facts. Owner S's contract says these\n * must be SHOWN rather than left to imply a fault, so they travel here and\n * the panel prints them.\n */\n notes: string[];\n /**\n * Where the windows came from, so nothing implies freshness it lacks.\n * Null on the V1 fallback path, which has no provenance to report.\n */\n evidenceSource: SeatCapacityRecord['evidenceSource'] | null;\n /** When the reading was taken, ISO. Null when nothing was observed. */\n observedAt: string | null;\n /** True when the seat carried owner S's `capacity` record. */\n extended: boolean;\n}\n\n/**\n * S's five-state verdict, mapped onto the four words this app already uses.\n *\n * `signed-out` is BLOCKED-by-action, not unread: we know exactly why there is\n * no capacity and what to do about it. `unknown` is the only genuinely unread\n * state — nobody told us anything, and calling that blocked would assert an\n * outage we never observed.\n *\n * Typed as a total map over S's union, so adding a verdict upstream fails the\n * build here rather than falling through to a silent default.\n */\nconst USABILITY_CLASS: Record<SeatCapacityRecord['usability'], SeatCapacityClass> = {\n ready: 'ready',\n tight: 'tight',\n exhausted: 'blocked',\n 'signed-out': 'blocked',\n unknown: 'unread',\n};\n\n/**\n * A machine-readable instant, formatted — in the app's ONE reset wording,\n * `describeResetAt`'s: \"resets today 11:46 PM\", \"resets Fri 11:46 PM\",\n * \"resets Sep 26, 11:46 PM\". (A second wording here — \"resets Sep 25 at\n * 11:46 PM\" — once reached the chat header and the seat meters while Accounts\n * said \"resets Fri 11:46 PM\" for the same instant.) Prose resets never reach\n * this — they are passed through untouched, because the provider's sentence\n * already carries the timezone it means and reformatting it would be a guess.\n */\nexport function formatResetInstant(iso: string, now: number = Date.now()): string | null {\n const when = describeResetAt(iso, now);\n return when === null ? null : `resets ${when}`;\n}\n\n/**\n * Window ids arrive provider-prefixed and sometimes doubly so\n * (`codex_codex_primary`, `grok_unified_weekly`). Strip the engine's own name\n * off the front before handing the rest to the shared labeller, so the panel\n * reads \"primary window\" rather than \"codex codex primary window\".\n *\n * Only the `capacity` path uses this. The V1 fallback goes through\n * `seatWindowLabel` unaltered, because the seat selector's wording is pinned\n * to it by test.\n */\nexport function seatCapacityWindowLabel(engine: VerseEngine, id: string): string {\n const prefix = `${engine}_`;\n let rest = id;\n while (rest.startsWith(prefix) && rest.length > prefix.length) rest = rest.slice(prefix.length);\n return seatWindowLabel(rest);\n}\n\nfunction toWindowView(engine: VerseEngine, w: SeatCapacityWindow, now: number): SeatWindowView {\n const reset = w.resetDescription ?? (w.resetsAt === null ? null : formatResetInstant(w.resetsAt, now));\n return {\n id: w.id,\n label: seatCapacityWindowLabel(engine, w.id),\n // A flagged limit, and any unmeasured figure, is a sentinel — not a reading.\n usedPercent: w.limitReached || !w.measured ? null : w.usedPercent,\n limitReached: w.limitReached,\n resetText: reset,\n resetsAt: w.resetsAt,\n };\n}\n\n/** The V1 `health.windows` shape, for a seat with no `capacity` record. */\nfunction legacyWindowView(w: VerseSeat['health']['windows'][number], now: number): SeatWindowView {\n const used = typeof w.usedPercent === 'number' && Number.isFinite(w.usedPercent)\n ? Math.max(0, Math.min(100, w.usedPercent))\n : null;\n // Upstream writes a denial as 100, so on this shape a bare 100 cannot be\n // told apart from a measured 100 — and the safe reading of both is \"spent\".\n const flagged = used !== null && used >= 100;\n return {\n id: w.id,\n label: seatWindowLabel(w.id),\n usedPercent: flagged ? null : used,\n limitReached: flagged,\n resetText: w.resetsAt === null ? null : formatResetInstant(w.resetsAt, now),\n resetsAt: w.resetsAt,\n };\n}\n\nfunction creditsPhrase(credits: SeatCapacityRecord['credits']): { phrase: string | null; title: string | null } {\n if (credits === null) return { phrase: null, title: null };\n if (credits.unlimited) return { phrase: 'credits unlimited', title: null };\n if (!credits.hasCredits) return { phrase: null, title: null };\n if (credits.balance === null) return { phrase: 'credits available', title: null };\n const parsed = Number.parseFloat(credits.balance);\n if (!Number.isFinite(parsed)) return { phrase: 'credits available', title: credits.balance };\n // The provider publishes a long decimal (\"2048.4196250000\"). Two places is a\n // rendering choice, not a new number — the raw string rides in the title.\n return { phrase: `${parsed.toFixed(2)} credits left`, title: credits.balance };\n}\n\nfunction localView(seat: VerseSeat): SeatSubscriptionView {\n const unavailable = seatUnavailableReason(seat);\n const ready = seat.health.state === 'ready';\n const cls: SeatCapacityClass = unavailable !== null ? 'blocked' : ready ? 'ready' : 'unread';\n return {\n kind: 'local',\n plan: null,\n cls,\n word: SEAT_CAPACITY_WORD[cls],\n summary: unavailable ?? (ready ? 'runs on this machine' : 'readiness not reported'),\n binding: null,\n others: [],\n credits: null,\n creditsTitle: null,\n notes: seat.health.summary === null ? [] : [seat.health.summary],\n evidenceSource: null,\n observedAt: seat.health.observedAt,\n extended: false,\n };\n}\n\n/** The V1 path: no `capacity` record, so the V1 rule decides, unchanged. */\nfunction fallbackView(seat: VerseSeat, now: number): SeatSubscriptionView {\n const legacy = seatCapacity(seat);\n const windows = seat.health.windows.map((w) => legacyWindowView(w, now));\n const flagged = windows.find((w) => w.limitReached) ?? null;\n const measured = windows.filter((w) => w.usedPercent !== null);\n const worst = measured.length === 0\n ? null\n : measured.reduce((a, b) => ((b.usedPercent ?? 0) > (a.usedPercent ?? 0) ? b : a));\n const binding = flagged ?? worst;\n const unavailable = seatUnavailableReason(seat);\n const cls = unavailable !== null ? 'blocked' : legacy.cls;\n return {\n kind: 'subscription',\n plan: null,\n cls,\n word: SEAT_CAPACITY_WORD[cls],\n summary: unavailable ?? legacy.text,\n binding,\n others: binding === null ? windows : windows.filter((w) => w.id !== binding.id),\n credits: null,\n creditsTitle: null,\n notes: [],\n evidenceSource: null,\n observedAt: seat.health.observedAt,\n extended: false,\n };\n}\n\n/**\n * The whole projection. Local seats short-circuit: they have no subscription,\n * so everything below the readiness line is deliberately empty rather than\n * zeroed. `now` is the clock the reset wording is relative to (\"today\" vs a\n * weekday); it defaults to the real one.\n */\nexport function seatSubscription(seat: VerseSeat, now: number = Date.now()): SeatSubscriptionView {\n if (seat.engine === 'local') return localView(seat);\n const capacity = seat.capacity;\n if (capacity === undefined) return fallbackView(seat, now);\n\n const windows = capacity.windows.map((w) => toWindowView(seat.engine, w, now));\n // Resolve the server's CHOICE of binding window back to this seat's own\n // view of it, so the two can never disagree about a reset or a label.\n const served = capacity.binding;\n const binding = served === null\n ? null\n : (windows.find((w) => w.id === served.id) ?? toWindowView(seat.engine, served, now));\n const others = binding === null ? windows : windows.filter((w) => w.id !== binding.id);\n const credits = creditsPhrase(capacity.credits);\n\n const unavailable = seatUnavailableReason(seat);\n const cls: SeatCapacityClass = unavailable !== null ? 'blocked' : USABILITY_CLASS[capacity.usability];\n\n let summary: string;\n // Signed out is checked FIRST, before the generic unavailable reason: owner\n // S maps `signed-out` onto `health.state: 'unavailable'` (it is the one\n // account state worth a red dot, because it has a remedy), and the generic\n // reason there is a bare \"seat unavailable\" that says nothing about the fix.\n // A signed-out account has no measurement at all — it is an action, never a\n // 0% meter and never a bare \"unknown\".\n if (capacity.usability === 'signed-out') {\n summary = 'signed out — reconnect this account';\n } else if (unavailable !== null) {\n summary = unavailable;\n } else if (binding === null) {\n summary = 'no capacity reading';\n } else if (binding.limitReached) {\n // The server already weighed credits against the spent window; `tight`\n // there means \"spent, but there is still something to spend\".\n summary = capacity.usability === 'tight' && credits.phrase !== null\n ? `${binding.label} limit reached · credits still spendable`\n : `${binding.label} limit reached`;\n } else if (binding.usedPercent === null) {\n summary = 'no capacity reading';\n } else {\n // The one percent rule: \"99%\", never a rounded \"100%\" beside a \"99%\" bar.\n summary = `${percentText(binding.usedPercent)} of ${binding.label} used`;\n }\n\n return {\n kind: 'subscription',\n plan: capacity.planType,\n cls,\n word: SEAT_CAPACITY_WORD[cls],\n summary,\n binding,\n others,\n credits: credits.phrase,\n creditsTitle: credits.title,\n notes: [...capacity.notes],\n evidenceSource: capacity.evidenceSource,\n observedAt: capacity.observedAt ?? seat.health.observedAt,\n extended: true,\n };\n}\n\n/**\n * How much of a freshness claim the evidence supports. A reading served from\n * another process's shared file, or from an operator-seeded baseline, is not\n * a live probe and must not be presented as one.\n */\nexport function evidenceNote(source: SeatSubscriptionView['evidenceSource']): string | null {\n switch (source) {\n case 'collector':\n return null;\n case 'shared-evidence':\n return 'read from another collector’s shared evidence, not probed here';\n case 'baseline':\n return 'operator-seeded baseline, not a live reading';\n case 'none':\n return 'no evidence source — nothing here was measured';\n default:\n return null;\n }\n}\n\nfunction parseableInstant(iso: string | null | undefined): iso is string {\n return typeof iso === 'string' && Number.isFinite(Date.parse(iso));\n}\n\nfunction isSpentWindow(w: Pick<SeatWindowView, 'limitReached' | 'usedPercent'>): boolean {\n return w.limitReached || (w.usedPercent !== null && w.usedPercent >= 100);\n}\n\n/**\n * When a spent seat is usable again. A seat reopens only once EVERY spent\n * window has reset — a Codex seat whose 5-hour window resets in 2h but whose\n * weekly window is also spent until Wednesday is still spent in 2h — so this\n * is the LATEST reset among the spent windows, matching Fleet's \"eligible\n * again\" (fleet/why-seat-model `eligibleAgain`) and the router's headroom\n * `lastReset`. Every surface that says \"resets …\" / \"usable again in …\" for\n * a spent seat (Accounts, the health banner) asks here.\n *\n * - a spent window with only provider prose (Claude) → null: when \"all of\n * them\" have reset is unknown, and prose is never parsed;\n * - `reported` (the health sweep's instant, which is its binding window's)\n * is folded in — it may know of a window this seat record does not — but\n * it can never make the answer EARLIER than a spent window's reset;\n * - nothing spent → `reported`, else the binding window's own instant.\n */\nexport function seatReopensAt(view: Pick<SeatSubscriptionView, 'binding' | 'others'>, reported?: string | null): string | null {\n const known = parseableInstant(reported) ? reported : null;\n const windows = view.binding === null ? view.others : [view.binding, ...view.others];\n const spent = windows.filter(isSpentWindow);\n if (spent.length > 0) {\n if (spent.some((w) => !parseableInstant(w.resetsAt))) return null;\n const instants = spent.map((w) => w.resetsAt as string);\n if (known !== null) instants.push(known);\n return instants.reduce((a, b) => (Date.parse(b) > Date.parse(a) ? b : a));\n }\n if (known !== null) return known;\n return view.binding !== null && parseableInstant(view.binding.resetsAt) ? view.binding.resetsAt : null;\n}\n\n/**\n * One sentence for a `title` attribute or a screen-reader name: the seat, its\n * plan, its verdict and the evidence behind it.\n */\nexport function seatSubscriptionSentence(seat: VerseSeat, view: SeatSubscriptionView): string {\n const parts = [seat.label];\n if (view.plan !== null) parts.push(view.plan);\n parts.push(view.word);\n parts.push(view.summary);\n if (view.credits !== null) parts.push(view.credits);\n if (view.binding?.resetText != null) parts.push(view.binding.resetText);\n return parts.join(' · ');\n}\n\n/** Tight and blocked are worth surfacing outside the panel; the rest is noise. */\nexport function worthFlagging(cls: SeatCapacityClass): boolean {\n return cls === 'tight' || cls === 'blocked';\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{a as e,n as t,s as n}from"./index-oAAq8XJa.js";var r=n(e(),1),i=t(),a=(0,r.createContext)(!0);function o({visible:e,children:t}){let n=(0,r.useContext)(a);return(0,i.jsx)(a.Provider,{value:n&&e,children:t})}function s(){return(0,r.useContext)(a)}function c(){return typeof document>`u`||document.visibilityState!==`hidden`}function l(e){return typeof document>`u`?()=>{}:(document.addEventListener(`visibilitychange`,e),()=>document.removeEventListener(`visibilitychange`,e))}function u(){return(0,r.useSyncExternalStore)(l,c,()=>!0)}var d=2e3;function f(e,t,n={}){let i=s(),a=u(),o=i&&a,c=(n.enabled??!0)&&o,l=n.refreshOnShow??!0,f=Math.max(d,Number.isFinite(t)?t:d),p=(0,r.useRef)(e);p.current=e;let m=(0,r.useRef)(null);(0,r.useEffect)(()=>{let e=m.current;if(m.current=o,!c)return;e===!1&&l&&p.current();let t=setInterval(()=>p.current(),f);return()=>clearInterval(t)},[c,o,f,l])}export{s as i,o as n,f as r,d as t};
2
- //# sourceMappingURL=section-visibility-qFqc_d4Z.js.map
1
+ import{a as e,n as t,s as n}from"./index-BVxOlseC.js";var r=n(e(),1),i=t(),a=(0,r.createContext)(!0);function o({visible:e,children:t}){let n=(0,r.useContext)(a);return(0,i.jsx)(a.Provider,{value:n&&e,children:t})}function s(){return(0,r.useContext)(a)}function c(){return typeof document>`u`||document.visibilityState!==`hidden`}function l(e){return typeof document>`u`?()=>{}:(document.addEventListener(`visibilitychange`,e),()=>document.removeEventListener(`visibilitychange`,e))}function u(){return(0,r.useSyncExternalStore)(l,c,()=>!0)}var d=2e3;function f(e,t,n={}){let i=s(),a=u(),o=i&&a,c=(n.enabled??!0)&&o,l=n.refreshOnShow??!0,f=Math.max(d,Number.isFinite(t)?t:d),p=(0,r.useRef)(e);p.current=e;let m=(0,r.useRef)(null);(0,r.useEffect)(()=>{let e=m.current;if(m.current=o,!c)return;e===!1&&l&&p.current();let t=setInterval(()=>p.current(),f);return()=>clearInterval(t)},[c,o,f,l])}export{s as i,o as n,f as r,d as t};
2
+ //# sourceMappingURL=section-visibility-Yctxu-if.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"section-visibility-qFqc_d4Z.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/section-visibility.tsx"],"sourcesContent":["/**\n * routes/verse/shell/section-visibility.tsx — \"is this surface on screen?\"\n * for the keep-alive shell (unit C0; SPEC-310C §1).\n *\n * 3.10 keeps the last three surfaces MOUNTED (hidden with `hidden` + `inert`)\n * so switching back is instant and keeps scroll, selection and filters. The\n * price is that a hidden surface's polls keep running: Fleet's live swimlane\n * polling every few seconds behind a chat the operator is reading. The shell\n * (C1) wraps each mounted surface in <SectionVisibilityProvider visible>, and\n * every surface polls through usePollWhileVisible — so a hidden surface, or a\n * hidden window, costs nothing.\n *\n * Outside any provider a section counts as visible: components rendered on\n * their own (tests, the legacy sections before C1 lands) behave as today.\n */\nimport { createContext, useContext, useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react';\n\nconst SectionVisibleContext = createContext<boolean>(true);\n\nexport function SectionVisibilityProvider({ visible, children }: { visible: boolean; children: ReactNode }) {\n // A hidden surface nested in a visible one is hidden; a visible one nested\n // in a hidden one is hidden too.\n const parent = useContext(SectionVisibleContext);\n return <SectionVisibleContext.Provider value={parent && visible}>{children}</SectionVisibleContext.Provider>;\n}\n\n/** True while the surface this component lives in is the one on screen. */\nexport function useSectionVisible(): boolean {\n return useContext(SectionVisibleContext);\n}\n\nfunction documentVisible(): boolean {\n return typeof document === 'undefined' || document.visibilityState !== 'hidden';\n}\n\nfunction subscribeDocument(onChange: () => void): () => void {\n if (typeof document === 'undefined') return () => {};\n document.addEventListener('visibilitychange', onChange);\n return () => document.removeEventListener('visibilitychange', onChange);\n}\n\n/** True while the window itself is visible (not minimised, not a background tab). */\nexport function useDocumentVisible(): boolean {\n return useSyncExternalStore(subscribeDocument, documentVisible, () => true);\n}\n\n/**\n * The performance budget's floor (SPEC-310A §0: \"no new polling faster than\n * 2 s\"). Enforced here, in the one place surfaces poll from, rather than by\n * review.\n */\nexport const MIN_POLL_INTERVAL_MS = 2_000;\n\nexport interface PollOptions {\n /** Poll at all (e.g. only while a panel is expanded). Default true. */\n enabled?: boolean;\n /**\n * Tick once as soon as the surface becomes visible again, so data that went\n * stale while it was hidden is refreshed on sight instead of one interval\n * later. Default true. (The first mount is the caller's own fetch.)\n */\n refreshOnShow?: boolean;\n}\n\n/**\n * Run `tick` every `intervalMs` (at least MIN_POLL_INTERVAL_MS) while the\n * section AND the document are visible. The latest `tick` is always the one\n * called, so passing an inline closure does not restart the interval.\n */\nexport function usePollWhileVisible(tick: () => void, intervalMs: number, options: PollOptions = {}): void {\n const sectionVisible = useSectionVisible();\n const docVisible = useDocumentVisible();\n const visible = sectionVisible && docVisible;\n const active = (options.enabled ?? true) && visible;\n const refreshOnShow = options.refreshOnShow ?? true;\n const every = Math.max(MIN_POLL_INTERVAL_MS, Number.isFinite(intervalMs) ? intervalMs : MIN_POLL_INTERVAL_MS);\n\n const tickRef = useRef(tick);\n tickRef.current = tick;\n // VISIBILITY history, not activity: turning `enabled` on is the caller's\n // own decision (and it fetches for itself); coming back into view is not.\n const wasVisible = useRef<boolean | null>(null);\n\n useEffect(() => {\n const previouslyVisible = wasVisible.current;\n wasVisible.current = visible;\n if (!active) return;\n // Back in view after being hidden: catch up now rather than one interval late.\n if (previouslyVisible === false && refreshOnShow) tickRef.current();\n const id = setInterval(() => tickRef.current(), every);\n return () => clearInterval(id);\n }, [active, visible, every, refreshOnShow]);\n}\n"],"mappings":"2EAiBM,GAAA,EAAwB,EAAA,cAAA,CAAuB,EAAI,EAEzD,SAAgB,EAA0B,CAAE,UAAS,YAAuD,CAG1G,IAAM,GAAA,EAAS,EAAA,WAAA,CAAW,CAAqB,EAC/C,OAAO,EAAA,EAAA,IAAA,CAAC,EAAsB,SAAvB,CAAgC,MAAO,GAAU,EAAU,UAAyC,CAAA,CAC7G,CAGA,SAAgB,GAA6B,CAC3C,OAAA,EAAO,EAAA,WAAA,CAAW,CAAqB,CACzC,CAEA,SAAS,GAA2B,CAClC,OAAO,OAAO,SAAa,KAAe,SAAS,kBAAoB,QACzE,CAEA,SAAS,EAAkB,EAAkC,CAG3D,OAFI,OAAO,SAAa,QAA0B,CAAC,GACnD,SAAS,iBAAiB,mBAAoB,CAAQ,MACzC,SAAS,oBAAoB,mBAAoB,CAAQ,EACxE,CAGA,SAAgB,GAA8B,CAC5C,OAAA,EAAO,EAAA,qBAAA,CAAqB,EAAmB,MAAuB,EAAI,CAC5E,CAOA,IAAa,EAAuB,IAkBpC,SAAgB,EAAoB,EAAkB,EAAoB,EAAuB,CAAC,EAAS,CACzG,IAAM,EAAiB,EAAkB,EACnC,EAAa,EAAmB,EAChC,EAAU,GAAkB,EAC5B,GAAU,EAAQ,SAAW,KAAS,EACtC,EAAgB,EAAQ,eAAiB,GACzC,EAAQ,KAAK,IAAI,EAAsB,OAAO,SAAS,CAAU,EAAI,EAAa,CAAoB,EAEtG,GAAA,EAAU,EAAA,OAAA,CAAO,CAAI,EAC3B,EAAQ,QAAU,EAGlB,IAAM,GAAA,EAAa,EAAA,OAAA,CAAuB,IAAI,GAE9C,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,EAAoB,EAAW,QAErC,GADA,EAAW,QAAU,EACjB,CAAC,EAAQ,OAET,IAAsB,IAAS,GAAe,EAAQ,QAAQ,EAClE,IAAM,EAAK,gBAAkB,EAAQ,QAAQ,EAAG,CAAK,EACrD,UAAa,cAAc,CAAE,CAC/B,EAAG,CAAC,EAAQ,EAAS,EAAO,CAAa,CAAC,CAC5C"}
1
+ {"version":3,"file":"section-visibility-Yctxu-if.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/section-visibility.tsx"],"sourcesContent":["/**\n * routes/verse/shell/section-visibility.tsx — \"is this surface on screen?\"\n * for the keep-alive shell (unit C0; SPEC-310C §1).\n *\n * 3.10 keeps the last three surfaces MOUNTED (hidden with `hidden` + `inert`)\n * so switching back is instant and keeps scroll, selection and filters. The\n * price is that a hidden surface's polls keep running: Fleet's live swimlane\n * polling every few seconds behind a chat the operator is reading. The shell\n * (C1) wraps each mounted surface in <SectionVisibilityProvider visible>, and\n * every surface polls through usePollWhileVisible — so a hidden surface, or a\n * hidden window, costs nothing.\n *\n * Outside any provider a section counts as visible: components rendered on\n * their own (tests, the legacy sections before C1 lands) behave as today.\n */\nimport { createContext, useContext, useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react';\n\nconst SectionVisibleContext = createContext<boolean>(true);\n\nexport function SectionVisibilityProvider({ visible, children }: { visible: boolean; children: ReactNode }) {\n // A hidden surface nested in a visible one is hidden; a visible one nested\n // in a hidden one is hidden too.\n const parent = useContext(SectionVisibleContext);\n return <SectionVisibleContext.Provider value={parent && visible}>{children}</SectionVisibleContext.Provider>;\n}\n\n/** True while the surface this component lives in is the one on screen. */\nexport function useSectionVisible(): boolean {\n return useContext(SectionVisibleContext);\n}\n\nfunction documentVisible(): boolean {\n return typeof document === 'undefined' || document.visibilityState !== 'hidden';\n}\n\nfunction subscribeDocument(onChange: () => void): () => void {\n if (typeof document === 'undefined') return () => {};\n document.addEventListener('visibilitychange', onChange);\n return () => document.removeEventListener('visibilitychange', onChange);\n}\n\n/** True while the window itself is visible (not minimised, not a background tab). */\nexport function useDocumentVisible(): boolean {\n return useSyncExternalStore(subscribeDocument, documentVisible, () => true);\n}\n\n/**\n * The performance budget's floor (SPEC-310A §0: \"no new polling faster than\n * 2 s\"). Enforced here, in the one place surfaces poll from, rather than by\n * review.\n */\nexport const MIN_POLL_INTERVAL_MS = 2_000;\n\nexport interface PollOptions {\n /** Poll at all (e.g. only while a panel is expanded). Default true. */\n enabled?: boolean;\n /**\n * Tick once as soon as the surface becomes visible again, so data that went\n * stale while it was hidden is refreshed on sight instead of one interval\n * later. Default true. (The first mount is the caller's own fetch.)\n */\n refreshOnShow?: boolean;\n}\n\n/**\n * Run `tick` every `intervalMs` (at least MIN_POLL_INTERVAL_MS) while the\n * section AND the document are visible. The latest `tick` is always the one\n * called, so passing an inline closure does not restart the interval.\n */\nexport function usePollWhileVisible(tick: () => void, intervalMs: number, options: PollOptions = {}): void {\n const sectionVisible = useSectionVisible();\n const docVisible = useDocumentVisible();\n const visible = sectionVisible && docVisible;\n const active = (options.enabled ?? true) && visible;\n const refreshOnShow = options.refreshOnShow ?? true;\n const every = Math.max(MIN_POLL_INTERVAL_MS, Number.isFinite(intervalMs) ? intervalMs : MIN_POLL_INTERVAL_MS);\n\n const tickRef = useRef(tick);\n tickRef.current = tick;\n // VISIBILITY history, not activity: turning `enabled` on is the caller's\n // own decision (and it fetches for itself); coming back into view is not.\n const wasVisible = useRef<boolean | null>(null);\n\n useEffect(() => {\n const previouslyVisible = wasVisible.current;\n wasVisible.current = visible;\n if (!active) return;\n // Back in view after being hidden: catch up now rather than one interval late.\n if (previouslyVisible === false && refreshOnShow) tickRef.current();\n const id = setInterval(() => tickRef.current(), every);\n return () => clearInterval(id);\n }, [active, visible, every, refreshOnShow]);\n}\n"],"mappings":"2EAiBM,GAAA,EAAwB,EAAA,cAAA,CAAuB,EAAI,EAEzD,SAAgB,EAA0B,CAAE,UAAS,YAAuD,CAG1G,IAAM,GAAA,EAAS,EAAA,WAAA,CAAW,CAAqB,EAC/C,OAAO,EAAA,EAAA,IAAA,CAAC,EAAsB,SAAvB,CAAgC,MAAO,GAAU,EAAU,UAAyC,CAAA,CAC7G,CAGA,SAAgB,GAA6B,CAC3C,OAAA,EAAO,EAAA,WAAA,CAAW,CAAqB,CACzC,CAEA,SAAS,GAA2B,CAClC,OAAO,OAAO,SAAa,KAAe,SAAS,kBAAoB,QACzE,CAEA,SAAS,EAAkB,EAAkC,CAG3D,OAFI,OAAO,SAAa,QAA0B,CAAC,GACnD,SAAS,iBAAiB,mBAAoB,CAAQ,MACzC,SAAS,oBAAoB,mBAAoB,CAAQ,EACxE,CAGA,SAAgB,GAA8B,CAC5C,OAAA,EAAO,EAAA,qBAAA,CAAqB,EAAmB,MAAuB,EAAI,CAC5E,CAOA,IAAa,EAAuB,IAkBpC,SAAgB,EAAoB,EAAkB,EAAoB,EAAuB,CAAC,EAAS,CACzG,IAAM,EAAiB,EAAkB,EACnC,EAAa,EAAmB,EAChC,EAAU,GAAkB,EAC5B,GAAU,EAAQ,SAAW,KAAS,EACtC,EAAgB,EAAQ,eAAiB,GACzC,EAAQ,KAAK,IAAI,EAAsB,OAAO,SAAS,CAAU,EAAI,EAAa,CAAoB,EAEtG,GAAA,EAAU,EAAA,OAAA,CAAO,CAAI,EAC3B,EAAQ,QAAU,EAGlB,IAAM,GAAA,EAAa,EAAA,OAAA,CAAuB,IAAI,GAE9C,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,EAAoB,EAAW,QAErC,GADA,EAAW,QAAU,EACjB,CAAC,EAAQ,OAET,IAAsB,IAAS,GAAe,EAAQ,QAAQ,EAClE,IAAM,EAAK,gBAAkB,EAAQ,QAAQ,EAAG,CAAK,EACrD,UAAa,cAAc,CAAE,CAC/B,EAAG,CAAC,EAAQ,EAAS,EAAO,CAAa,CAAC,CAC5C"}
@@ -1,2 +1,2 @@
1
- import{u as e}from"./verse-model-D7NZQFAR.js";var t=[`all`,`running`,`needs-you`,`pinned`],n={all:`All`,running:`Running`,"needs-you":`Needs you`,pinned:`Pinned`};function r(e){let t=new Set;for(let n of e)n.subject.sessionId&&t.add(n.subject.sessionId),n.target.kind===`session`&&t.add(n.target.sessionId);return t}var i=72;function a(e){if(!e)return null;if(e.phase===`tool`&&e.tool)return e.tool;if(e.thinkingTail&&e.thinkingTail.trim()){let t=e.thinkingTail.replace(/\s+/g,` `).trim();return t.length>i?`…${t.slice(t.length-i+1).trimStart()}`:t}switch(e.phase){case`thinking`:return`Thinking`;case`writing`:return`Writing`;case`waiting`:return`Waiting for the model`;case`tool`:return`Running a tool`;default:return null}}function o(e,t){return e?.sessions[t]??null}function s(t){let{sessions:n,projects:i,query:s,filter:c,activity:l,meta:u,localSeen:d,selectedId:f}=t,p=new Map((l?.running??[]).map(e=>[e.sessionId,e])),m=r(l?.needsYou??[]),h=u!==null,g=new Map;for(let e of n){let t=o(u,e.id),n=p.get(e.id),r=e.status===`running`||n!==void 0,i=t?.seenTurnCount??null,s=Math.max(i??-1,d.get(e.id)??-1),c=h?Math.max(s,0):s,_=e.id!==f&&c>=0&&e.turnCount>c?e.turnCount-c:0,v=l?m.has(e.id):e.status===`error`,y=r?{kind:`running`,startedAt:n?.startedAt??null}:e.status===`error`?{kind:`failed`}:_>0?{kind:`unread`,newTurns:_}:{kind:`time`,at:e.updatedAt},b=r?a(n?.live??null):null;g.set(e.id,{session:e,status:y,live:b===null?null:{text:b,startedAt:n?.startedAt??null},pinned:t?.pinned===!0,archived:t?.archived===!0,needsYou:v})}let _=[...g.values()],v={all:_.filter(e=>!e.archived).length,running:_.filter(e=>e.status.kind===`running`).length,"needs-you":_.filter(e=>e.needsYou).length,pinned:_.filter(e=>e.pinned&&!e.archived).length},y=e(_.filter(e=>{switch(c){case`running`:return e.status.kind===`running`;case`needs-you`:return e.needsYou;case`pinned`:return e.pinned;default:return!0}}).map(e=>e.session),i,s),b=[],x=[],S=[],C=[];for(let e of y){let t=[];for(let n of e.sessions){let e=g.get(n.id);e.archived?S.push(e):e.pinned?x.push(e):t.push(e)}t.length>0&&C.push({id:`project:${e.projectPath}`,kind:`project`,label:e.name,projectPath:e.projectPath,enrolled:e.enrolled,rows:t})}let w=(e,t)=>t.session.updatedAt.localeCompare(e.session.updatedAt);return x.length>0&&b.push({id:`pinned`,kind:`pinned`,label:`Pinned`,projectPath:null,enrolled:!1,rows:x.sort(w)}),b.push(...C),S.length>0&&b.push({id:`archived`,kind:`archived`,label:`Archived`,projectPath:null,enrolled:!1,rows:S.sort(w)}),{groups:b,counts:v,metaAvailable:h}}export{a as i,n,s as r,t};
2
- //# sourceMappingURL=sidebar-model-M1GaQU5l.js.map
1
+ import{u as e}from"./verse-model-DyCwZF-M.js";var t=[`all`,`running`,`needs-you`,`pinned`],n={all:`All`,running:`Running`,"needs-you":`Needs you`,pinned:`Pinned`};function r(e){let t=new Set;for(let n of e)n.subject.sessionId&&t.add(n.subject.sessionId),n.target.kind===`session`&&t.add(n.target.sessionId);return t}var i=72;function a(e){if(!e)return null;if(e.phase===`tool`&&e.tool)return e.tool;if(e.thinkingTail&&e.thinkingTail.trim()){let t=e.thinkingTail.replace(/\s+/g,` `).trim();return t.length>i?`…${t.slice(t.length-i+1).trimStart()}`:t}switch(e.phase){case`thinking`:return`Thinking`;case`writing`:return`Writing`;case`waiting`:return`Waiting for the model`;case`tool`:return`Running a tool`;default:return null}}function o(e,t){return e?.sessions[t]??null}function s(t){let{sessions:n,projects:i,query:s,filter:c,activity:l,meta:u,localSeen:d,selectedId:f}=t,p=new Map((l?.running??[]).map(e=>[e.sessionId,e])),m=r(l?.needsYou??[]),h=u!==null,g=new Map;for(let e of n){let t=o(u,e.id),n=p.get(e.id),r=e.status===`running`||n!==void 0,i=t?.seenTurnCount??null,s=Math.max(i??-1,d.get(e.id)??-1),c=h?Math.max(s,0):s,_=e.id!==f&&c>=0&&e.turnCount>c?e.turnCount-c:0,v=l?m.has(e.id):e.status===`error`,y=r?{kind:`running`,startedAt:n?.startedAt??null}:e.status===`error`?{kind:`failed`}:_>0?{kind:`unread`,newTurns:_}:{kind:`time`,at:e.updatedAt},b=r?a(n?.live??null):null;g.set(e.id,{session:e,status:y,live:b===null?null:{text:b,startedAt:n?.startedAt??null},pinned:t?.pinned===!0,archived:t?.archived===!0,needsYou:v})}let _=[...g.values()],v={all:_.filter(e=>!e.archived).length,running:_.filter(e=>e.status.kind===`running`).length,"needs-you":_.filter(e=>e.needsYou).length,pinned:_.filter(e=>e.pinned&&!e.archived).length},y=e(_.filter(e=>{switch(c){case`running`:return e.status.kind===`running`;case`needs-you`:return e.needsYou;case`pinned`:return e.pinned;default:return!0}}).map(e=>e.session),i,s),b=[],x=[],S=[],C=[];for(let e of y){let t=[];for(let n of e.sessions){let e=g.get(n.id);e.archived?S.push(e):e.pinned?x.push(e):t.push(e)}t.length>0&&C.push({id:`project:${e.projectPath}`,kind:`project`,label:e.name,projectPath:e.projectPath,enrolled:e.enrolled,rows:t})}let w=(e,t)=>t.session.updatedAt.localeCompare(e.session.updatedAt);return x.length>0&&b.push({id:`pinned`,kind:`pinned`,label:`Pinned`,projectPath:null,enrolled:!1,rows:x.sort(w)}),b.push(...C),S.length>0&&b.push({id:`archived`,kind:`archived`,label:`Archived`,projectPath:null,enrolled:!1,rows:S.sort(w)}),{groups:b,counts:v,metaAvailable:h}}export{a as i,n,s as r,t};
2
+ //# sourceMappingURL=sidebar-model-DQ-FyBHW.js.map