@ashlr/hub 3.11.0 → 3.11.2
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.
- package/CHANGELOG.md +96 -0
- package/dist/authority-surface.json +12 -7
- package/dist/build-identity.json +2 -2
- package/dist/core/universe/builtins/preparation/manifest.json +1 -1
- package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +5 -4
- package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +4 -3
- package/dist/core/universe/campaign.js +9 -1
- package/dist/core/universe/campaign.js.map +1 -1
- package/dist/core/universe/runner.d.ts +3 -0
- package/dist/core/universe/runner.js +7 -3
- package/dist/core/universe/runner.js.map +1 -1
- package/dist/core/verse/compaction-point.d.ts +55 -0
- package/dist/core/verse/compaction-point.js +75 -0
- package/dist/core/verse/compaction-point.js.map +1 -0
- package/dist/core/verse/context-math.d.ts +1 -38
- package/dist/core/verse/context-math.js +5 -59
- package/dist/core/verse/context-math.js.map +1 -1
- package/dist/core/web/public/next/assets/{ActionMenu-CQvNJvHY.js → ActionMenu-_DhtX2gO.js} +2 -2
- package/dist/core/web/public/next/assets/{ActionMenu-CQvNJvHY.js.map → ActionMenu-_DhtX2gO.js.map} +1 -1
- package/dist/core/web/public/next/assets/Advanced-CAAfaBke.js +2 -0
- package/dist/core/web/public/next/assets/{Advanced-DEM93sKI.js.map → Advanced-CAAfaBke.js.map} +1 -1
- package/dist/core/web/public/next/assets/App-BAywyjE8.js +7 -0
- package/dist/core/web/public/next/assets/{App-CimXmK0L.js.map → App-BAywyjE8.js.map} +1 -1
- package/dist/core/web/public/next/assets/AppsSection-CUypKeog.js +2 -0
- package/dist/core/web/public/next/assets/{AppsSection-D6U-dSj6.js.map → AppsSection-CUypKeog.js.map} +1 -1
- package/dist/core/web/public/next/assets/{AreaTrend-LD9lCPkT.js → AreaTrend-CblDqC8Q.js} +2 -2
- package/dist/core/web/public/next/assets/{AreaTrend-LD9lCPkT.js.map → AreaTrend-CblDqC8Q.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-D3d8lYO3.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-3V9pUyxm.js.map → AutonomySection-D3d8lYO3.js.map} +1 -1
- package/dist/core/web/public/next/assets/BranchBar-DU0jAV1E.js +2 -0
- package/dist/core/web/public/next/assets/{BranchBar-U8UdpfEB.js.map → BranchBar-DU0jAV1E.js.map} +1 -1
- package/dist/core/web/public/next/assets/{BudgetControl-DOTlMSmW.js → BudgetControl-Mm1ZL4u6.js} +2 -2
- package/dist/core/web/public/next/assets/{BudgetControl-DOTlMSmW.js.map → BudgetControl-Mm1ZL4u6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{BurnDown-x4PWH9JF.js → BurnDown-C1fgcFqo.js} +2 -2
- package/dist/core/web/public/next/assets/{BurnDown-x4PWH9JF.js.map → BurnDown-C1fgcFqo.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Button-DYv_KKxr.js → Button-DqgLUZvd.js} +2 -2
- package/dist/core/web/public/next/assets/{Button-DYv_KKxr.js.map → Button-DqgLUZvd.js.map} +1 -1
- package/dist/core/web/public/next/assets/CapacityStrip-nWFHvqFB.js +2 -0
- package/dist/core/web/public/next/assets/CapacityStrip-nWFHvqFB.js.map +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DUZNpiVr.js +3 -0
- package/dist/core/web/public/next/assets/ChatSection-DUZNpiVr.js.map +1 -0
- package/dist/core/web/public/next/assets/{CloudBudgetForm-DJeJvBEu.js → CloudBudgetForm-2OW38o_C.js} +2 -2
- package/dist/core/web/public/next/assets/{CloudBudgetForm-DJeJvBEu.js.map → CloudBudgetForm-2OW38o_C.js.map} +1 -1
- package/dist/core/web/public/next/assets/CommandPalette-C5w2o1-r.js +2 -0
- package/dist/core/web/public/next/assets/{CommandPalette-FiD4N8XD.js.map → CommandPalette-C5w2o1-r.js.map} +1 -1
- package/dist/core/web/public/next/assets/CommandSection-Bx2knNqY.js +2 -0
- package/dist/core/web/public/next/assets/{CommandSection-DN8KwH-X.js.map → CommandSection-Bx2knNqY.js.map} +1 -1
- package/dist/core/web/public/next/assets/ConfirmDialog-B7ZwCQOX.js +2 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-B7ZwCQOX.js.map +1 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-Dx15x0Ho.css +1 -0
- package/dist/core/web/public/next/assets/Dialog-BpgcEW6b.js +2 -0
- package/dist/core/web/public/next/assets/Dialog-BpgcEW6b.js.map +1 -0
- package/dist/core/web/public/next/assets/Dialog-tQ8kFulD.css +1 -0
- package/dist/core/web/public/next/assets/{DiffPane-DLJ3gGGK.js → DiffPane-DpqRUtCx.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffPane-DLJ3gGGK.js.map → DiffPane-DpqRUtCx.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-B7NWPjIO.js → DiffViewer-CoV5-nUf.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-B7NWPjIO.js.map → DiffViewer-CoV5-nUf.js.map} +1 -1
- package/dist/core/web/public/next/assets/DockHost-C0Pq7Jak.js +2 -0
- package/dist/core/web/public/next/assets/{DockHost-DvR-hPhZ.js.map → DockHost-C0Pq7Jak.js.map} +1 -1
- package/dist/core/web/public/next/assets/{EmptyState-DhNgDDrF.js → EmptyState-DYW29ncw.js} +2 -2
- package/dist/core/web/public/next/assets/{EmptyState-DhNgDDrF.js.map → EmptyState-DYW29ncw.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-uARPnx9x.js → Epistemic-DVx6oHzp.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-uARPnx9x.js.map → Epistemic-DVx6oHzp.js.map} +1 -1
- package/dist/core/web/public/next/assets/FleetSection-DSETviPK.js +3 -0
- package/dist/core/web/public/next/assets/{FleetSection-D_zRBCub.js.map → FleetSection-DSETviPK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Funnel-CL8uHONq.js → Funnel-DxiGqJrt.js} +2 -2
- package/dist/core/web/public/next/assets/{Funnel-CL8uHONq.js.map → Funnel-DxiGqJrt.js.map} +1 -1
- package/dist/core/web/public/next/assets/GearTray-C3I89sIt.js +2 -0
- package/dist/core/web/public/next/assets/{GearTray-C7k4l_BW.js.map → GearTray-C3I89sIt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{GrowthSection-DwdNSGj8.js → GrowthSection-C3m2bPEz.js} +2 -2
- package/dist/core/web/public/next/assets/{GrowthSection-DwdNSGj8.js.map → GrowthSection-C3m2bPEz.js.map} +1 -1
- package/dist/core/web/public/next/assets/HandoffDialog-Bjb96a0N.js +2 -0
- package/dist/core/web/public/next/assets/{HandoffDialog-FPVOqOMN.js.map → HandoffDialog-Bjb96a0N.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Input-B9-VLcHS.js → Input-ekTehVaH.js} +2 -2
- package/dist/core/web/public/next/assets/{Input-B9-VLcHS.js.map → Input-ekTehVaH.js.map} +1 -1
- package/dist/core/web/public/next/assets/LeaderCard-85VBbbaZ.js +2 -0
- package/dist/core/web/public/next/assets/{LeaderCard-4R0BhDzT.js.map → LeaderCard-85VBbbaZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MatrixHeatmap-DiuushcS.js → MatrixHeatmap-Bo8j0QPC.js} +2 -2
- package/dist/core/web/public/next/assets/{MatrixHeatmap-DiuushcS.js.map → MatrixHeatmap-Bo8j0QPC.js.map} +1 -1
- package/dist/core/web/public/next/assets/McpSection-DvHFaVyp.js +2 -0
- package/dist/core/web/public/next/assets/{McpSection-CJ__OJKl.js.map → McpSection-DvHFaVyp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdown-qWKyJvAQ.js → MessageMarkdown-fsY_ltVa.js} +3 -3
- package/dist/core/web/public/next/assets/{MessageMarkdown-qWKyJvAQ.js.map → MessageMarkdown-fsY_ltVa.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-x05TrsrW.js → MessageMarkdownRenderer-DDdtu82l.js} +2 -2
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-x05TrsrW.js.map → MessageMarkdownRenderer-DDdtu82l.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-D-hncpkO.js → Meter-CeGDXdDM.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-D-hncpkO.js.map → Meter-CeGDXdDM.js.map} +1 -1
- package/dist/core/web/public/next/assets/MindSection-CDVhHvXl.js +2 -0
- package/dist/core/web/public/next/assets/{MindSection-BjXHkQPS.js.map → MindSection-CDVhHvXl.js.map} +1 -1
- package/dist/core/web/public/next/assets/MutationTokenDialog-CrfJ0PdD.css +1 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-DTQzEOHR.js +2 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-DTQzEOHR.js.map +1 -0
- package/dist/core/web/public/next/assets/NeedsYouDrawer-6AuN0BF3.js +2 -0
- package/dist/core/web/public/next/assets/{NeedsYouDrawer-rSd1ZHAR.js.map → NeedsYouDrawer-6AuN0BF3.js.map} +1 -1
- package/dist/core/web/public/next/assets/{NeedsYouList-CG-nB1tr.js → NeedsYouList-ChnRmbSk.js} +2 -2
- package/dist/core/web/public/next/assets/{NeedsYouList-CG-nB1tr.js.map → NeedsYouList-ChnRmbSk.js.map} +1 -1
- package/dist/core/web/public/next/assets/NewChatDialog-B06E7sqg.js +3 -0
- package/dist/core/web/public/next/assets/{NewChatDialog-BeDQ1eW3.js.map → NewChatDialog-B06E7sqg.js.map} +1 -1
- package/dist/core/web/public/next/assets/OnboardingFlow-C2M3zov9.js +2 -0
- package/dist/core/web/public/next/assets/{OnboardingFlow-CkTZtt-m.js.map → OnboardingFlow-C2M3zov9.js.map} +1 -1
- package/dist/core/web/public/next/assets/PreviewPane-Bd6HsX1k.js +2 -0
- package/dist/core/web/public/next/assets/{PreviewPane-kzbbSK4u.js.map → PreviewPane-Bd6HsX1k.js.map} +1 -1
- package/dist/core/web/public/next/assets/ProviderLogo-Bz7KR5j3.js +2 -0
- package/dist/core/web/public/next/assets/ProviderLogo-Bz7KR5j3.js.map +1 -0
- package/dist/core/web/public/next/assets/{RailStatus-D-t2gPae.js → RailStatus-CctCuuLy.js} +2 -2
- package/dist/core/web/public/next/assets/{RailStatus-D-t2gPae.js.map → RailStatus-CctCuuLy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RefreshIndicator-BLnr9IOM.js → RefreshIndicator-CKY5BGdD.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-BLnr9IOM.js.map → RefreshIndicator-CKY5BGdD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-CM_8pJs6.css → ResourcePoolConsoleApp-BX9JLkT4.css} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BeG0gBif.js → ResourcePoolConsoleApp-CspIsW_G.js} +5 -5
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BeG0gBif.js.map → ResourcePoolConsoleApp-CspIsW_G.js.map} +1 -1
- package/dist/core/web/public/next/assets/ResourcesChrome-B7fHZ6zX.js +3 -0
- package/dist/core/web/public/next/assets/ResourcesChrome-B7fHZ6zX.js.map +1 -0
- package/dist/core/web/public/next/assets/ResourcesChrome-uZEqC2EM.css +1 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-B7-N9n7-.js +2 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-B7-N9n7-.js.map +1 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C_B11J0s.js +2 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C_B11J0s.js.map +1 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-DdxhTdZ6.css +1 -0
- package/dist/core/web/public/next/assets/{RunInCloudAction-C3LBp7C4.js → RunInCloudAction-_juw4EXd.js} +2 -2
- package/dist/core/web/public/next/assets/{RunInCloudAction-C3LBp7C4.js.map → RunInCloudAction-_juw4EXd.js.map} +1 -1
- package/dist/core/web/public/next/assets/SeatSelector-D_YXvkPy.js +3 -0
- package/dist/core/web/public/next/assets/{SeatSelector-DpvvbjNm.js.map → SeatSelector-D_YXvkPy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Segmented-BL40udoO.js → Segmented-DnKP6rr7.js} +2 -2
- package/dist/core/web/public/next/assets/{Segmented-BL40udoO.js.map → Segmented-DnKP6rr7.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Select-FWLnAw_j.js → Select-DlbUupTX.js} +2 -2
- package/dist/core/web/public/next/assets/{Select-FWLnAw_j.js.map → Select-DlbUupTX.js.map} +1 -1
- package/dist/core/web/public/next/assets/SessionGate-DnuTZACL.js +2 -0
- package/dist/core/web/public/next/assets/SessionGate-DnuTZACL.js.map +1 -0
- package/dist/core/web/public/next/assets/SessionGate-DvMpXNpJ.css +1 -0
- package/dist/core/web/public/next/assets/SessionInsightChip-C33-k-Q5.js +2 -0
- package/dist/core/web/public/next/assets/{SessionInsightChip-CWye87Ju.js.map → SessionInsightChip-C33-k-Q5.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SessionSearch-YvCHFUuN.js → SessionSearch-DhhR9Csg.js} +2 -2
- package/dist/core/web/public/next/assets/{SessionSearch-YvCHFUuN.js.map → SessionSearch-DhhR9Csg.js.map} +1 -1
- package/dist/core/web/public/next/assets/SettingsSection-C0xQvCz3.js +2 -0
- package/dist/core/web/public/next/assets/SettingsSection-C0xQvCz3.js.map +1 -0
- package/dist/core/web/public/next/assets/Sheet-BvNYOYv5.js +2 -0
- package/dist/core/web/public/next/assets/{Sheet-DZKcQXQg.js.map → Sheet-BvNYOYv5.js.map} +1 -1
- package/dist/core/web/public/next/assets/ShortcutsOverlay-1nUBVO-Z.js +2 -0
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-BpDimdPq.js.map → ShortcutsOverlay-1nUBVO-Z.js.map} +1 -1
- package/dist/core/web/public/next/assets/Sidebar-YrVvvBNM.js +3 -0
- package/dist/core/web/public/next/assets/{Sidebar-BoBzBEFX.js.map → Sidebar-YrVvvBNM.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Slider-DUMo9HDD.js → Slider-B2c-dbQb.js} +2 -2
- package/dist/core/web/public/next/assets/{Slider-DUMo9HDD.js.map → Slider-B2c-dbQb.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatusBadge-BwnBNFQr.js → StatusBadge-Q9YYkijx.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-BwnBNFQr.js.map → StatusBadge-Q9YYkijx.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StepBand-PDXMIlRk.js → StepBand-Ci-C0GUG.js} +2 -2
- package/dist/core/web/public/next/assets/{StepBand-PDXMIlRk.js.map → StepBand-Ci-C0GUG.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Surface-B3TZVoER.js → Surface-DBovMq4P.js} +2 -2
- package/dist/core/web/public/next/assets/{Surface-B3TZVoER.js.map → Surface-DBovMq4P.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Swimlane-DR4OgON_.js → Swimlane-Cdc4TqDe.js} +2 -2
- package/dist/core/web/public/next/assets/{Swimlane-DR4OgON_.js.map → Swimlane-Cdc4TqDe.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Switch-CcR5Llk5.js → Switch-CUrvzO2k.js} +2 -2
- package/dist/core/web/public/next/assets/{Switch-CcR5Llk5.js.map → Switch-CUrvzO2k.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Tag-_-K1h7Hk.js → Tag-Bv8u6APt.js} +2 -2
- package/dist/core/web/public/next/assets/{Tag-_-K1h7Hk.js.map → Tag-Bv8u6APt.js.map} +1 -1
- package/dist/core/web/public/next/assets/TerminalPane-BE4Xlqi5.js +7 -0
- package/dist/core/web/public/next/assets/{TerminalPane-_1aH4ndN.js.map → TerminalPane-BE4Xlqi5.js.map} +1 -1
- package/dist/core/web/public/next/assets/UniverseConsoleApp-BGwejf0m.js +2 -0
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-BUsq-inZ.js.map → UniverseConsoleApp-BGwejf0m.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-Dh7A-hLD.css → UniverseConsoleApp-DfuT-wFw.css} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-Dogy_dsB.js → UniverseView-CgKa-WXC.js} +4 -4
- package/dist/core/web/public/next/assets/{UniverseView-Dogy_dsB.js.map → UniverseView-CgKa-WXC.js.map} +1 -1
- package/dist/core/web/public/next/assets/UsageSection-CazsHm1f.js +2 -0
- package/dist/core/web/public/next/assets/{UsageSection-DiX_dTyo.js.map → UsageSection-CazsHm1f.js.map} +1 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-CTF3Shs0.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-CTF3Shs0.js.map +1 -0
- package/dist/core/web/public/next/assets/Workspace-CgdyXiEV.js +10 -0
- package/dist/core/web/public/next/assets/Workspace-CgdyXiEV.js.map +1 -0
- package/dist/core/web/public/next/assets/Workspace-Dwri2jFJ.css +1 -0
- package/dist/core/web/public/next/assets/actions-MtxBGS5B.js +2 -0
- package/dist/core/web/public/next/assets/{actions-s2lfm_5k.js.map → actions-MtxBGS5B.js.map} +1 -1
- package/dist/core/web/public/next/assets/budget-queries-CP7hgR-p.js +2 -0
- package/dist/core/web/public/next/assets/{budget-queries-wUshcovN.js.map → budget-queries-CP7hgR-p.js.map} +1 -1
- package/dist/core/web/public/next/assets/{capacity-strip-model-Bj_9ztzs.js → capacity-strip-model-fHfu5cph.js} +2 -2
- package/dist/core/web/public/next/assets/{capacity-strip-model-Bj_9ztzs.js.map → capacity-strip-model-fHfu5cph.js.map} +1 -1
- package/dist/core/web/public/next/assets/{charts-CMSgAUKn.js → charts-Bis9WSiS.js} +2 -2
- package/dist/core/web/public/next/assets/{charts-CMSgAUKn.js.map → charts-Bis9WSiS.js.map} +1 -1
- package/dist/core/web/public/next/assets/cloud-queries-BsOepFz6.js +2 -0
- package/dist/core/web/public/next/assets/{cloud-queries-CnV1yaNG.js.map → cloud-queries-BsOepFz6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js → cloud.module-DoFnr8ZH.js} +2 -2
- package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js.map → cloud.module-DoFnr8ZH.js.map} +1 -1
- package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js → command-bus-CMvbYP5K.js} +2 -2
- package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js.map → command-bus-CMvbYP5K.js.map} +1 -1
- package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js +2 -0
- package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js.map +1 -0
- package/dist/core/web/public/next/assets/compaction-point-VL9oegWS.js +2 -0
- package/dist/core/web/public/next/assets/compaction-point-VL9oegWS.js.map +1 -0
- package/dist/core/web/public/next/assets/{composer-keys-DBRymxQn.js → composer-keys-su0g1h9o.js} +2 -2
- package/dist/core/web/public/next/assets/{composer-keys-DBRymxQn.js.map → composer-keys-su0g1h9o.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-Ci3Ch9t5.js +2 -0
- package/dist/core/web/public/next/assets/{context-model-Dwd49-dp.js.map → context-model-Ci3Ch9t5.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-CjiJTnf1.js +2 -0
- package/dist/core/web/public/next/assets/{context-model-MT17V86_.js.map → context-model-CjiJTnf1.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-queries-BDXT6LQ3.js +2 -0
- package/dist/core/web/public/next/assets/{context-queries-xVKIeynP.js.map → context-queries-BDXT6LQ3.js.map} +1 -1
- package/dist/core/web/public/next/assets/control-queries-Dv-h1Clu.js +2 -0
- package/dist/core/web/public/next/assets/{control-queries-DOZR-D8G.js.map → control-queries-Dv-h1Clu.js.map} +1 -1
- package/dist/core/web/public/next/assets/{dock-icons-8EtARwUm.js → dock-icons-t--Eugrd.js} +2 -2
- package/dist/core/web/public/next/assets/{dock-icons-8EtARwUm.js.map → dock-icons-t--Eugrd.js.map} +1 -1
- package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js → dock-store-qIu7kSQE.js} +2 -2
- package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js.map → dock-store-qIu7kSQE.js.map} +1 -1
- package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js → fleet-model-IRbwfhkR.js} +2 -2
- package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js.map → fleet-model-IRbwfhkR.js.map} +1 -1
- package/dist/core/web/public/next/assets/fleet-queries-7FVNByX1.js +2 -0
- package/dist/core/web/public/next/assets/{fleet-queries-BSdRlUwJ.js.map → fleet-queries-7FVNByX1.js.map} +1 -1
- package/dist/core/web/public/next/assets/focus-trap-QqQxQdvH.js +2 -0
- package/dist/core/web/public/next/assets/focus-trap-QqQxQdvH.js.map +1 -0
- package/dist/core/web/public/next/assets/{format-B8DAVcn2.js → format-CaMmVQCn.js} +2 -2
- package/dist/core/web/public/next/assets/{format-B8DAVcn2.js.map → format-CaMmVQCn.js.map} +1 -1
- package/dist/core/web/public/next/assets/git-model-B4S_GUNA.js +5 -0
- package/dist/core/web/public/next/assets/{git-model-CivuBY_D.js.map → git-model-B4S_GUNA.js.map} +1 -1
- package/dist/core/web/public/next/assets/global-BkNl4e6E.js +2 -0
- package/dist/core/web/public/next/assets/global-BkNl4e6E.js.map +1 -0
- package/dist/core/web/public/next/assets/global-BlI2BYa1.css +1 -0
- package/dist/core/web/public/next/assets/guarded-action-DHiL8tY5.js +3 -0
- package/dist/core/web/public/next/assets/guarded-action-DHiL8tY5.js.map +1 -0
- package/dist/core/web/public/next/assets/guarded-runners-DmGEkM2P.js +2 -0
- package/dist/core/web/public/next/assets/guarded-runners-DmGEkM2P.js.map +1 -0
- package/dist/core/web/public/next/assets/icon-base-NtFmP2yB.js +2 -0
- package/dist/core/web/public/next/assets/icon-base-NtFmP2yB.js.map +1 -0
- package/dist/core/web/public/next/assets/icons-C5EFt_Ts.js +2 -0
- package/dist/core/web/public/next/assets/icons-C5EFt_Ts.js.map +1 -0
- package/dist/core/web/public/next/assets/{index-oAAq8XJa.js → index-DbDw9UAs.js} +3 -3
- package/dist/core/web/public/next/assets/{index-oAAq8XJa.js.map → index-DbDw9UAs.js.map} +1 -1
- package/dist/core/web/public/next/assets/{live-model-DCw_IFRI.js → live-model-DEwrl7_A.js} +2 -2
- package/dist/core/web/public/next/assets/{live-model-DCw_IFRI.js.map → live-model-DEwrl7_A.js.map} +1 -1
- package/dist/core/web/public/next/assets/{mind.module-vnuJcwvD.js → mind.module-CLhqMQak.js} +2 -2
- package/dist/core/web/public/next/assets/{mind.module-vnuJcwvD.js.map → mind.module-CLhqMQak.js.map} +1 -1
- package/dist/core/web/public/next/assets/mutations-Kvzsg-iK.js +2 -0
- package/dist/core/web/public/next/assets/{mutations-Dpsql5t7.js.map → mutations-Kvzsg-iK.js.map} +1 -1
- package/dist/core/web/public/next/assets/nav-C0IunEYF.js +2 -0
- package/dist/core/web/public/next/assets/nav-C0IunEYF.js.map +1 -0
- package/dist/core/web/public/next/assets/overnight-queries-C_AfcL7q.js +2 -0
- package/dist/core/web/public/next/assets/{overnight-queries-X-44Gsw5.js.map → overnight-queries-C_AfcL7q.js.map} +1 -1
- package/dist/core/web/public/next/assets/preloaded-CctOitLu.js +2 -0
- package/dist/core/web/public/next/assets/preloaded-CctOitLu.js.map +1 -0
- package/dist/core/web/public/next/assets/primitives-CbAGCNKt.js +1 -0
- package/dist/core/web/public/next/assets/{queries-BA4rNGD-.js → queries-THD05gHP.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-BA4rNGD-.js.map → queries-THD05gHP.js.map} +1 -1
- package/dist/core/web/public/next/assets/reasoning-pref-BVPu0SME.js +2 -0
- package/dist/core/web/public/next/assets/{reasoning-pref-B9-DJUXP.js.map → reasoning-pref-BVPu0SME.js.map} +1 -1
- package/dist/core/web/public/next/assets/reveal-anchor-wCT5b4m3.js +2 -0
- package/dist/core/web/public/next/assets/reveal-anchor-wCT5b4m3.js.map +1 -0
- package/dist/core/web/public/next/assets/seat-subscription-CR_VylsK.js +2 -0
- package/dist/core/web/public/next/assets/{seat-subscription-CH6fW4fJ.js.map → seat-subscription-CR_VylsK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{section-visibility-qFqc_d4Z.js → section-visibility-B0xXUQJ0.js} +2 -2
- package/dist/core/web/public/next/assets/{section-visibility-qFqc_d4Z.js.map → section-visibility-B0xXUQJ0.js.map} +1 -1
- package/dist/core/web/public/next/assets/{sidebar-model-M1GaQU5l.js → sidebar-model-C5wVXGpb.js} +2 -2
- package/dist/core/web/public/next/assets/{sidebar-model-M1GaQU5l.js.map → sidebar-model-C5wVXGpb.js.map} +1 -1
- package/dist/core/web/public/next/assets/slots-DDd8cEoh.js +3 -0
- package/dist/core/web/public/next/assets/{slots-Ds9ZIqUL.js.map → slots-DDd8cEoh.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-data-BFxAlefI.js +2 -0
- package/dist/core/web/public/next/assets/{surface-data-J52iIGMD.js.map → surface-data-BFxAlefI.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-prefetch-CHusyO9q.js +2 -0
- package/dist/core/web/public/next/assets/{surface-prefetch-CgZ-LEJK.js.map → surface-prefetch-CHusyO9q.js.map} +1 -1
- package/dist/core/web/public/next/assets/{tasks-model-ent8f0jZ.js → tasks-model-DvkWveXs.js} +2 -2
- package/dist/core/web/public/next/assets/{tasks-model-ent8f0jZ.js.map → tasks-model-DvkWveXs.js.map} +1 -1
- package/dist/core/web/public/next/assets/{terminal-icons-DJTK7QpR.js → terminal-icons-Dp6pJgV2.js} +2 -2
- package/dist/core/web/public/next/assets/{terminal-icons-DJTK7QpR.js.map → terminal-icons-Dp6pJgV2.js.map} +1 -1
- package/dist/core/web/public/next/assets/{usage-queries-Bn8ccqyN.js → usage-queries-CUTmnegA.js} +2 -2
- package/dist/core/web/public/next/assets/{usage-queries-Bn8ccqyN.js.map → usage-queries-CUTmnegA.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-guarded-action-UCX2u1t1.js +2 -0
- package/dist/core/web/public/next/assets/{use-guarded-action-C2HlSzkz.js.map → use-guarded-action-UCX2u1t1.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-ticker-BNc1H4ZC.js → use-ticker-OXAjob3y.js} +2 -2
- package/dist/core/web/public/next/assets/{use-ticker-BNc1H4ZC.js.map → use-ticker-OXAjob3y.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-token-gate-CDDRAoKH.js +2 -0
- package/dist/core/web/public/next/assets/{use-token-gate-B3IJL6bf.js.map → use-token-gate-CDDRAoKH.js.map} +1 -1
- package/dist/core/web/public/next/assets/useChartWidth-BW6SwYIK.css +1 -0
- package/dist/core/web/public/next/assets/useChartWidth-D9M7kUw7.js +2 -0
- package/dist/core/web/public/next/assets/{useChartWidth-lZ9PrQn0.js.map → useChartWidth-D9M7kUw7.js.map} +1 -1
- package/dist/core/web/public/next/assets/{useSeatsRefresh-CG4jczeL.js → useSeatsRefresh-B2dCVcj9.js} +2 -2
- package/dist/core/web/public/next/assets/{useSeatsRefresh-CG4jczeL.js.map → useSeatsRefresh-B2dCVcj9.js.map} +1 -1
- package/dist/core/web/public/next/assets/useVerseTranscript-BL3CljSI.js +2 -0
- package/dist/core/web/public/next/assets/useVerseTranscript-BL3CljSI.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-icons-DASWdYuE.js +2 -0
- package/dist/core/web/public/next/assets/verse-icons-DASWdYuE.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-model-BQ0Y2CSr.js +2 -0
- package/dist/core/web/public/next/assets/verse-model-BQ0Y2CSr.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js +3 -0
- package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-ui-store-D7YXi63q.js +3 -0
- package/dist/core/web/public/next/assets/{verse-ui-store-zafyzXKD.js.map → verse-ui-store-D7YXi63q.js.map} +1 -1
- package/dist/core/web/public/next/assets/{viewport-BEqGUoTA.js → viewport-BYGH5EgA.js} +2 -2
- package/dist/core/web/public/next/assets/{viewport-BEqGUoTA.js.map → viewport-BYGH5EgA.js.map} +1 -1
- package/dist/core/web/public/next/assets/warmup-AsqwWCUi.js +3 -0
- package/dist/core/web/public/next/assets/{warmup-BR6pxAKs.js.map → warmup-AsqwWCUi.js.map} +1 -1
- package/dist/core/web/public/next/assets/{workspace-model-D_tWD5Wt.js → workspace-model-CXorJr0G.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-D_tWD5Wt.js.map → workspace-model-CXorJr0G.js.map} +1 -1
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/package.json +5 -2
- package/dist/cli/activation.d.ts +0 -41
- package/dist/cli/activation.js +0 -485
- package/dist/cli/activation.js.map +0 -1
- package/dist/core/fleet/generated-repair-resolution.d.ts +0 -36
- package/dist/core/fleet/generated-repair-resolution.js +0 -245
- package/dist/core/fleet/generated-repair-resolution.js.map +0 -1
- package/dist/core/local-runtime/llama/port.d.ts +0 -17
- package/dist/core/local-runtime/llama/port.js +0 -47
- package/dist/core/local-runtime/llama/port.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-host.d.ts +0 -54
- package/dist/core/local-runtime/llama/proxy-host.js +0 -159
- package/dist/core/local-runtime/llama/proxy-host.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-process.d.ts +0 -148
- package/dist/core/local-runtime/llama/proxy-process.js +0 -250
- package/dist/core/local-runtime/llama/proxy-process.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-record.d.ts +0 -40
- package/dist/core/local-runtime/llama/proxy-record.js +0 -133
- package/dist/core/local-runtime/llama/proxy-record.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +0 -118
- package/dist/core/local-runtime/llama/proxy-supervisor.js +0 -511
- package/dist/core/local-runtime/llama/proxy-supervisor.js.map +0 -1
- package/dist/core/web/public/next/assets/Advanced-DEM93sKI.js +0 -2
- package/dist/core/web/public/next/assets/App-CimXmK0L.js +0 -7
- package/dist/core/web/public/next/assets/AppsSection-D6U-dSj6.js +0 -2
- package/dist/core/web/public/next/assets/AutonomySection-3V9pUyxm.js +0 -2
- package/dist/core/web/public/next/assets/BranchBar-U8UdpfEB.js +0 -2
- package/dist/core/web/public/next/assets/CapacityStrip-C667yQe8.js +0 -2
- package/dist/core/web/public/next/assets/CapacityStrip-C667yQe8.js.map +0 -1
- package/dist/core/web/public/next/assets/ChatSection-CCcIPpOq.js +0 -3
- package/dist/core/web/public/next/assets/ChatSection-CCcIPpOq.js.map +0 -1
- package/dist/core/web/public/next/assets/CommandPalette-FiD4N8XD.js +0 -2
- package/dist/core/web/public/next/assets/CommandSection-DN8KwH-X.js +0 -2
- package/dist/core/web/public/next/assets/DockHost-DvR-hPhZ.js +0 -2
- package/dist/core/web/public/next/assets/FleetSection-D_zRBCub.js +0 -3
- package/dist/core/web/public/next/assets/GearTray-C7k4l_BW.js +0 -2
- package/dist/core/web/public/next/assets/HandoffDialog-FPVOqOMN.js +0 -2
- package/dist/core/web/public/next/assets/LeaderCard-4R0BhDzT.js +0 -2
- package/dist/core/web/public/next/assets/McpSection-CJ__OJKl.js +0 -2
- package/dist/core/web/public/next/assets/MindSection-BjXHkQPS.js +0 -2
- package/dist/core/web/public/next/assets/MutationTokenDialog-Cum_DqQZ.css +0 -1
- package/dist/core/web/public/next/assets/MutationTokenDialog-SkRxqV_I.js +0 -2
- package/dist/core/web/public/next/assets/MutationTokenDialog-SkRxqV_I.js.map +0 -1
- package/dist/core/web/public/next/assets/NeedsYouDrawer-rSd1ZHAR.js +0 -2
- package/dist/core/web/public/next/assets/NewChatDialog-BeDQ1eW3.js +0 -3
- package/dist/core/web/public/next/assets/OnboardingFlow-CkTZtt-m.js +0 -2
- package/dist/core/web/public/next/assets/PreviewPane-kzbbSK4u.js +0 -2
- package/dist/core/web/public/next/assets/ResourcesChrome-B3Z3O98t.css +0 -1
- package/dist/core/web/public/next/assets/ResourcesChrome-BzV6bkIa.js +0 -3
- package/dist/core/web/public/next/assets/ResourcesChrome-BzV6bkIa.js.map +0 -1
- package/dist/core/web/public/next/assets/ResourcesDrawer-o4ILHdep.js +0 -2
- package/dist/core/web/public/next/assets/ResourcesDrawer-o4ILHdep.js.map +0 -1
- package/dist/core/web/public/next/assets/RouteErrorBoundary-6RMd_aZg.css +0 -1
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C5SxA2Dg.js +0 -2
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C5SxA2Dg.js.map +0 -1
- package/dist/core/web/public/next/assets/SeatSelector-DpvvbjNm.js +0 -3
- package/dist/core/web/public/next/assets/SessionInsightChip-CWye87Ju.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-Dr18upeE.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-Dr18upeE.js.map +0 -1
- package/dist/core/web/public/next/assets/Sheet-DZKcQXQg.js +0 -2
- package/dist/core/web/public/next/assets/ShortcutsOverlay-BpDimdPq.js +0 -2
- package/dist/core/web/public/next/assets/Sidebar-BoBzBEFX.js +0 -3
- package/dist/core/web/public/next/assets/TerminalPane-_1aH4ndN.js +0 -7
- package/dist/core/web/public/next/assets/UniverseConsoleApp-BUsq-inZ.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-DiX_dTyo.js +0 -2
- package/dist/core/web/public/next/assets/VerseConsoleApp-WVeyJnW7.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-WVeyJnW7.js.map +0 -1
- package/dist/core/web/public/next/assets/Workspace-BYW9dm90.css +0 -1
- package/dist/core/web/public/next/assets/Workspace-BbxZHwQd.js +0 -10
- package/dist/core/web/public/next/assets/Workspace-BbxZHwQd.js.map +0 -1
- package/dist/core/web/public/next/assets/actions-s2lfm_5k.js +0 -2
- package/dist/core/web/public/next/assets/budget-queries-wUshcovN.js +0 -2
- package/dist/core/web/public/next/assets/cloud-queries-CnV1yaNG.js +0 -2
- package/dist/core/web/public/next/assets/command-catalog-BdtDH93o.js +0 -2
- package/dist/core/web/public/next/assets/command-catalog-BdtDH93o.js.map +0 -1
- package/dist/core/web/public/next/assets/context-model-Dwd49-dp.js +0 -2
- package/dist/core/web/public/next/assets/context-model-MT17V86_.js +0 -2
- package/dist/core/web/public/next/assets/context-queries-xVKIeynP.js +0 -2
- package/dist/core/web/public/next/assets/control-queries-DOZR-D8G.js +0 -2
- package/dist/core/web/public/next/assets/dock-panes-UKS-mXhr.js +0 -2
- package/dist/core/web/public/next/assets/dock-panes-UKS-mXhr.js.map +0 -1
- package/dist/core/web/public/next/assets/fleet-queries-BSdRlUwJ.js +0 -2
- package/dist/core/web/public/next/assets/git-model-CivuBY_D.js +0 -5
- package/dist/core/web/public/next/assets/global-Di2PUgj4.js +0 -2
- package/dist/core/web/public/next/assets/global-Di2PUgj4.js.map +0 -1
- package/dist/core/web/public/next/assets/global-_tOCFt9y.css +0 -1
- package/dist/core/web/public/next/assets/guarded-action-CvOHcqyH.js +0 -2
- package/dist/core/web/public/next/assets/guarded-action-CvOHcqyH.js.map +0 -1
- package/dist/core/web/public/next/assets/icons-B_yuAgdN.js +0 -2
- package/dist/core/web/public/next/assets/icons-B_yuAgdN.js.map +0 -1
- package/dist/core/web/public/next/assets/mutations-Dpsql5t7.js +0 -2
- package/dist/core/web/public/next/assets/overnight-queries-X-44Gsw5.js +0 -2
- package/dist/core/web/public/next/assets/primitives-BDIcUvob.js +0 -1
- package/dist/core/web/public/next/assets/reasoning-pref-B9-DJUXP.js +0 -2
- package/dist/core/web/public/next/assets/seat-subscription-CH6fW4fJ.js +0 -2
- package/dist/core/web/public/next/assets/slots-Ds9ZIqUL.js +0 -3
- package/dist/core/web/public/next/assets/surface-data-J52iIGMD.js +0 -2
- package/dist/core/web/public/next/assets/surface-prefetch-CgZ-LEJK.js +0 -2
- package/dist/core/web/public/next/assets/use-guarded-action-C2HlSzkz.js +0 -2
- package/dist/core/web/public/next/assets/use-token-gate-B3IJL6bf.js +0 -2
- package/dist/core/web/public/next/assets/useActivity-Bk3XgXjL.js +0 -2
- package/dist/core/web/public/next/assets/useActivity-Bk3XgXjL.js.map +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-BAtVgn2n.css +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-lZ9PrQn0.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-D7NZQFAR.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-D7NZQFAR.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-queries-DvIJcn6Z.js +0 -2
- package/dist/core/web/public/next/assets/verse-queries-DvIJcn6Z.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-store-DjBWj04q.js +0 -3
- package/dist/core/web/public/next/assets/verse-store-DjBWj04q.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-ui-store-zafyzXKD.js +0 -3
- package/dist/core/web/public/next/assets/warmup-BR6pxAKs.js +0 -3
package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js.map → dock-store-qIu7kSQE.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dock-store-BA1cHOYA.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/dock-catalog.ts","../../../../../../src/web-ui/routes/verse/dock/dock-store.ts"],"sourcesContent":["/**\n * routes/verse/shell/dock-catalog.ts — the chat dock's panes, layout rules\n * and persisted state (unit C0; SPEC-310C §3).\n *\n * The dock is C2's container; its panes belong to four units (C2 Tasks and\n * Context, C4 Terminal and Preview, C5 Review). This file is what they agree\n * on: which panes exist, which command opens each, which render through a\n * slot (slots.tsx, so the container never imports another unit's code), how\n * wide the dock may be, when it becomes a sheet, and the shape it persists.\n *\n * Pure data and pure functions — no React.\n */\nimport type { CommandId } from './command-catalog.js';\nimport { VIEWPORT_BREAKPOINTS } from './viewport.js';\n\nexport const DOCK_PANES = [\n { id: 'terminal', label: 'Terminal', owner: 'C4', commandId: 'dock.terminal', slot: 'terminal-pane' },\n { id: 'preview', label: 'Preview', owner: 'C4', commandId: 'dock.preview', slot: 'preview-pane' },\n { id: 'diff', label: 'Review', owner: 'C5', commandId: 'dock.diff', slot: 'diff-pane' },\n { id: 'tasks', label: 'Tasks', owner: 'C2', commandId: null, slot: null },\n { id: 'context', label: 'Context', owner: 'C2', commandId: null, slot: null },\n] as const satisfies readonly {\n id: string;\n label: string;\n owner: string;\n /** The catalog command that opens (and focuses) this pane; null = opened from its own affordance. */\n commandId: CommandId | null;\n /** Rendered through this slot when the pane is another unit's; null = C2's own component. */\n slot: 'terminal-pane' | 'preview-pane' | 'diff-pane' | null;\n}[];\n\nexport type DockPaneId = (typeof DOCK_PANES)[number]['id'];\nexport const DOCK_PANE_IDS: readonly DockPaneId[] = DOCK_PANES.map((p) => p.id);\n\nexport function isDockPaneId(value: unknown): value is DockPaneId {\n return typeof value === 'string' && (DOCK_PANE_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Layout rules. Widths in CSS px, measured against the WINDOW, not the chat\n * column: \"resizable from 320px to 60% of the window\".\n */\nexport const DOCK_LAYOUT = Object.freeze({\n minWidth: 320,\n maxWidthFraction: 0.6,\n /** A new dock opens this wide (the 440 of the SPEC-310C shell sketch). */\n defaultWidth: 440,\n /** Below this window width the dock is a sheet over the chat, not a column. */\n sheetBelow: VIEWPORT_BREAKPOINTS.wide,\n /** Below this window width it is a bottom sheet… */\n bottomSheetBelow: VIEWPORT_BREAKPOINTS.compact,\n /** …this tall, in vh. */\n bottomSheetHeightVh: 75,\n /** The top pane's share of the height in a vertical split. */\n splitRatio: Object.freeze({ min: 0.2, max: 0.8, default: 0.5 }),\n});\n\nexport type DockPresentation = 'column' | 'sheet' | 'bottom-sheet';\n\n/** How the dock is drawn at a given window width. */\nexport function dockPresentation(windowWidth: number): DockPresentation {\n if (windowWidth < DOCK_LAYOUT.bottomSheetBelow) return 'bottom-sheet';\n if (windowWidth < DOCK_LAYOUT.sheetBelow) return 'sheet';\n return 'column';\n}\n\n/**\n * A persisted or dragged width clamped for this window. The floor wins over\n * the 60% cap on a window too narrow to honour both (it is a sheet there\n * anyway), so the result is never below minWidth.\n */\nexport function clampDockWidth(width: unknown, windowWidth: number): number {\n const n = typeof width === 'number' && Number.isFinite(width) ? Math.round(width) : DOCK_LAYOUT.defaultWidth;\n const max = Math.max(DOCK_LAYOUT.minWidth, Math.floor(windowWidth * DOCK_LAYOUT.maxWidthFraction));\n return Math.min(max, Math.max(DOCK_LAYOUT.minWidth, n));\n}\n\n// ---------------------------------------------------------------------------\n// Persisted state\n// ---------------------------------------------------------------------------\n\n/** The v2 key C1 migrates FROM (verse-ui-store.ts). */\nexport const VERSE_UI_STORAGE_KEY_V2 = 'ashlr.verse.ui.v2';\n/**\n * The v3 key (SPEC-310C §3 \"State persists in ashlr.verse.ui.v3\"). ONE blob,\n * written by C1's verse-ui-store; the dock's state is its `dock` field, read\n * back through sanitizeDockState — so two writers never race for the key.\n */\nexport const VERSE_UI_STORAGE_KEY = 'ashlr.verse.ui.v3';\n\nexport interface DockState {\n open: boolean;\n /** Last dragged width; clamp with clampDockWidth at render (it depends on the window). */\n width: number;\n /** Open panes, in tab order. */\n tabs: DockPaneId[];\n /** The pane shown (on top, when split); null only when there are no tabs. */\n active: DockPaneId | null;\n /** A second pane shown BELOW `active` (\"Preview over Terminal\"); never equal to it. */\n splitWith: DockPaneId | null;\n /** `active`'s share of the height when split. */\n splitRatio: number;\n}\n\nexport const DEFAULT_DOCK_STATE: Readonly<DockState> = Object.freeze({\n open: false,\n width: DOCK_LAYOUT.defaultWidth,\n tabs: [],\n active: null,\n splitWith: null,\n splitRatio: DOCK_LAYOUT.splitRatio.default,\n});\n\n/**\n * Read a persisted `dock` field without trusting it: unknown panes dropped,\n * duplicates collapsed, `active`/`splitWith` forced to open tabs, numbers\n * clamped. Anything unreadable falls back to the default, never throws — a\n * hand-edited or future-version blob must not take the chat down.\n */\nexport function sanitizeDockState(value: unknown): DockState {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return { ...DEFAULT_DOCK_STATE, tabs: [] };\n const raw = value as Record<string, unknown>;\n const tabs: DockPaneId[] = [];\n if (Array.isArray(raw['tabs'])) {\n for (const tab of raw['tabs']) if (isDockPaneId(tab) && !tabs.includes(tab)) tabs.push(tab);\n }\n const active: DockPaneId | null = isDockPaneId(raw['active']) && tabs.includes(raw['active']) ? raw['active'] : (tabs[0] ?? null);\n const splitWith: DockPaneId | null =\n isDockPaneId(raw['splitWith']) && tabs.includes(raw['splitWith']) && raw['splitWith'] !== active ? raw['splitWith'] : null;\n const width = typeof raw['width'] === 'number' && Number.isFinite(raw['width']) && raw['width'] >= DOCK_LAYOUT.minWidth\n ? Math.round(raw['width'])\n : DOCK_LAYOUT.defaultWidth;\n const ratio = typeof raw['splitRatio'] === 'number' && Number.isFinite(raw['splitRatio']) ? raw['splitRatio'] : DOCK_LAYOUT.splitRatio.default;\n return {\n open: raw['open'] === true && tabs.length > 0,\n width,\n tabs,\n active,\n splitWith,\n splitRatio: Math.min(DOCK_LAYOUT.splitRatio.max, Math.max(DOCK_LAYOUT.splitRatio.min, ratio)),\n };\n}\n","/**\n * routes/verse/dock/dock-store.ts — the chat dock's state and the one-shot\n * requests aimed at its panes (unit C2; contract: shell/dock-catalog.ts).\n *\n * PERSISTENCE. The dock's state belongs in `ashlr.verse.ui.v3`'s `dock`\n * field, and that blob has ONE writer — C1's verse-ui-store — so two stores\n * never race for the key (dock-catalog.ts). This store therefore never\n * writes the key. It:\n * - hydrates itself, read-only, from the v3 blob's `dock` field on first\n * read (through sanitizeDockState — a hand-edited blob cannot break the\n * chat);\n * - exposes getDockState / subscribeDockState, which C1's store folds into\n * the blob it writes (`dock: getDockState()`, re-written on change);\n * - accepts hydrateDockState(raw) when C1 prefers to push the field in.\n *\n * REQUESTS. \"Run in terminal\", a ± count opening the branch diff, Preview's\n * dev-server Start: each is a one-shot request with a nonce (slots.tsx\n * contracts) — the same request twice is two opens. They live here, not in\n * React state, because they are raised from deep inside the transcript\n * (CommandOutput) and consumed by a pane in another subtree.\n *\n * Framework-free; the React glue is useDock() at the bottom.\n */\nimport { useSyncExternalStore } from 'react';\nimport {\n DOCK_LAYOUT,\n VERSE_UI_STORAGE_KEY,\n sanitizeDockState,\n type DockPaneId,\n type DockState,\n} from '../shell/dock-catalog.js';\nimport type { DiffPaneRequest, PreviewOpenRequest, TerminalOpenRequest } from '../shell/slots.js';\n\n/**\n * A terminal request as the dock carries it. C0's TerminalOpenRequest now\n * names the Apps [Launch ▸] fields (`via`, `model`) itself, so this is a\n * plain alias — kept so TerminalPane and the Apps launcher keep one name for\n * \"what the dock hands the Terminal pane\". The server resolves the actual\n * command either way, never the page.\n */\nexport type TerminalRequest = TerminalOpenRequest;\n\nexport interface DockRequests {\n terminal: TerminalRequest | null;\n preview: PreviewOpenRequest | null;\n diff: (DiffPaneRequest & { nonce: number }) | null;\n}\n\nexport interface DockSnapshot {\n state: DockState;\n requests: DockRequests;\n}\n\nconst NO_REQUESTS: DockRequests = { terminal: null, preview: null, diff: null };\n\nlet snapshot: DockSnapshot | null = null;\nlet nonce = 0;\nconst listeners = new Set<() => void>();\n\nfunction readPersisted(): DockState {\n try {\n const raw = localStorage.getItem(VERSE_UI_STORAGE_KEY);\n if (!raw) return sanitizeDockState(null);\n const parsed = JSON.parse(raw) as { dock?: unknown } | null;\n return sanitizeDockState(parsed && typeof parsed === 'object' ? parsed.dock : null);\n } catch {\n return sanitizeDockState(null);\n }\n}\n\nfunction current(): DockSnapshot {\n if (snapshot === null) snapshot = { state: readPersisted(), requests: NO_REQUESTS };\n return snapshot;\n}\n\nfunction emit(next: DockSnapshot): void {\n snapshot = next;\n for (const listener of [...listeners]) listener();\n}\n\nfunction setState(next: DockState): void {\n emit({ ...current(), state: next });\n}\n\nexport function subscribeDockState(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getDockSnapshot(): DockSnapshot {\n return current();\n}\n\n/** The persistable part — what C1's store writes as the v3 blob's `dock` field. */\nexport function getDockState(): DockState {\n return current().state;\n}\n\n/** C1 may push the persisted field in (on load, or when another tab wrote it). */\nexport function hydrateDockState(raw: unknown): void {\n setState(sanitizeDockState(raw));\n}\n\n/** Test seam: back to defaults with no requests; next read re-hydrates from storage. */\nexport function resetDockStore(): void {\n snapshot = null;\n nonce = 0;\n for (const listener of [...listeners]) listener();\n}\n\n// ---------------------------------------------------------------------------\n// Transitions (pure over DockState, exported for tests)\n// ---------------------------------------------------------------------------\n\n/** Open `pane` (adding its tab) and make it the visible one. */\nexport function withPaneOpen(state: DockState, pane: DockPaneId): DockState {\n const tabs = state.tabs.includes(pane) ? state.tabs : [...state.tabs, pane];\n // Opening the pane that is the split's lower half swaps it to the top.\n const splitWith = state.splitWith === pane ? (state.active !== pane ? state.active : null) : state.splitWith;\n return { ...state, open: true, tabs, active: pane, splitWith: splitWith === pane ? null : splitWith };\n}\n\n/** The pane's toggle: open & focus it, or — when it is already the visible pane — close the dock. */\nexport function withPaneToggled(state: DockState, pane: DockPaneId): DockState {\n if (state.open && (state.active === pane || state.splitWith === pane)) return { ...state, open: false };\n return withPaneOpen(state, pane);\n}\n\nexport function withTabClosed(state: DockState, pane: DockPaneId): DockState {\n const tabs = state.tabs.filter((t) => t !== pane);\n const splitWith = state.splitWith === pane ? null : state.splitWith;\n let active = state.active;\n if (active === pane) {\n // The split's lower pane moves up; otherwise the neighbour tab takes over.\n const index = state.tabs.indexOf(pane);\n active = splitWith ?? tabs[Math.min(index, tabs.length - 1)] ?? null;\n }\n return {\n ...state,\n tabs,\n active,\n splitWith: splitWith === active ? null : splitWith,\n open: tabs.length > 0 && state.open,\n };\n}\n\n/** Split `pane` BELOW the active one (\"Preview over Terminal\"); null un-splits. */\nexport function withSplit(state: DockState, pane: DockPaneId | null): DockState {\n if (pane === null) return { ...state, splitWith: null };\n if (state.active === null || pane === state.active) return state;\n const tabs = state.tabs.includes(pane) ? state.tabs : [...state.tabs, pane];\n return { ...state, open: true, tabs, splitWith: pane };\n}\n\nexport function withWidth(state: DockState, width: number): DockState {\n if (!Number.isFinite(width)) return state;\n return { ...state, width: Math.max(DOCK_LAYOUT.minWidth, Math.round(width)) };\n}\n\nexport function withSplitRatio(state: DockState, ratio: number): DockState {\n if (!Number.isFinite(ratio)) return state;\n const { min, max } = DOCK_LAYOUT.splitRatio;\n return { ...state, splitRatio: Math.min(max, Math.max(min, ratio)) };\n}\n\n// ---------------------------------------------------------------------------\n// Actions\n// ---------------------------------------------------------------------------\n\nexport function openDockPane(pane: DockPaneId): void {\n setState(withPaneOpen(current().state, pane));\n}\n\nexport function toggleDockPane(pane: DockPaneId): void {\n setState(withPaneToggled(current().state, pane));\n}\n\n/** ⌘\\ — show or hide the dock as it was; an empty dock opens on Tasks. */\nexport function toggleDock(): void {\n const state = current().state;\n if (state.open) setState({ ...state, open: false });\n else if (state.tabs.length === 0) setState(withPaneOpen(state, 'tasks'));\n else setState({ ...state, open: true, active: state.active ?? state.tabs[0]! });\n}\n\nexport function closeDock(): void {\n const state = current().state;\n if (state.open) setState({ ...state, open: false });\n}\n\nexport function closeDockTab(pane: DockPaneId): void {\n setState(withTabClosed(current().state, pane));\n}\n\nexport function splitDock(pane: DockPaneId | null): void {\n setState(withSplit(current().state, pane));\n}\n\nexport function setDockWidth(width: number): void {\n setState(withWidth(current().state, width));\n}\n\nexport function setDockSplitRatio(ratio: number): void {\n setState(withSplitRatio(current().state, ratio));\n}\n\n/** Open the Terminal pane with a request (a tab at a root, a pasted command, an app launch). */\nexport function requestTerminal(request: Omit<TerminalRequest, 'nonce'>): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'terminal'), requests: { ...snap.requests, terminal: { ...request, nonce } } });\n}\n\n/**\n * The same request, but with Terminal as the LOWER half of a split under the\n * pane that is showing (Preview's dev-server Start: the page stays on top and\n * appears when its port answers, the server's output runs underneath — the\n * \"Preview over Terminal\" layout). With nothing showing, or when Terminal is\n * the pane on top, it is a plain open. In the bottom sheet the split's lower\n * pane is mounted but hidden, so the request still runs.\n */\nexport function requestTerminalBelow(request: Omit<TerminalRequest, 'nonce'>): void {\n const snap = current();\n const top = snap.state.open ? snap.state.active : null;\n if (top === null || top === 'terminal') {\n requestTerminal(request);\n return;\n }\n nonce += 1;\n emit({ state: withSplit(snap.state, 'terminal'), requests: { ...snap.requests, terminal: { ...request, nonce } } });\n}\n\nexport function requestPreview(request: Omit<PreviewOpenRequest, 'nonce'>): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'preview'), requests: { ...snap.requests, preview: { ...request, nonce } } });\n}\n\nexport function requestDiff(request: DiffPaneRequest): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'diff'), requests: { ...snap.requests, diff: { ...request, nonce } } });\n}\n\n/**\n * Requests are per chat: switching chats must not replay the last chat's\n * terminal paste. Callers clear on a SWITCH only — never on the Chat\n * section's first mount, which is exactly when an Apps [Launch ▸] (raised on\n * another surface, then `setVerseSection('chat')`) is waiting to be served.\n */\nexport function clearDockRequests(): void {\n const snap = current();\n if (snap.requests === NO_REQUESTS) return;\n emit({ ...snap, requests: NO_REQUESTS });\n}\n\nexport function useDock(): DockSnapshot {\n return useSyncExternalStore(subscribeDockState, getDockSnapshot, getDockSnapshot);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dock-store-qIu7kSQE.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/dock-catalog.ts","../../../../../../src/web-ui/routes/verse/dock/dock-store.ts"],"sourcesContent":["/**\n * routes/verse/shell/dock-catalog.ts — the chat dock's panes, layout rules\n * and persisted state (unit C0; SPEC-310C §3).\n *\n * The dock is C2's container; its panes belong to four units (C2 Tasks and\n * Context, C4 Terminal and Preview, C5 Review). This file is what they agree\n * on: which panes exist, which command opens each, which render through a\n * slot (slots.tsx, so the container never imports another unit's code), how\n * wide the dock may be, when it becomes a sheet, and the shape it persists.\n *\n * Pure data and pure functions — no React.\n */\nimport type { CommandId } from './command-catalog.js';\nimport { VIEWPORT_BREAKPOINTS } from './viewport.js';\n\nexport const DOCK_PANES = [\n { id: 'terminal', label: 'Terminal', owner: 'C4', commandId: 'dock.terminal', slot: 'terminal-pane' },\n { id: 'preview', label: 'Preview', owner: 'C4', commandId: 'dock.preview', slot: 'preview-pane' },\n { id: 'diff', label: 'Review', owner: 'C5', commandId: 'dock.diff', slot: 'diff-pane' },\n { id: 'tasks', label: 'Tasks', owner: 'C2', commandId: null, slot: null },\n { id: 'context', label: 'Context', owner: 'C2', commandId: null, slot: null },\n] as const satisfies readonly {\n id: string;\n label: string;\n owner: string;\n /** The catalog command that opens (and focuses) this pane; null = opened from its own affordance. */\n commandId: CommandId | null;\n /** Rendered through this slot when the pane is another unit's; null = C2's own component. */\n slot: 'terminal-pane' | 'preview-pane' | 'diff-pane' | null;\n}[];\n\nexport type DockPaneId = (typeof DOCK_PANES)[number]['id'];\nexport const DOCK_PANE_IDS: readonly DockPaneId[] = DOCK_PANES.map((p) => p.id);\n\nexport function isDockPaneId(value: unknown): value is DockPaneId {\n return typeof value === 'string' && (DOCK_PANE_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Layout rules. Widths in CSS px, measured against the WINDOW, not the chat\n * column: \"resizable from 320px to 60% of the window\".\n */\nexport const DOCK_LAYOUT = Object.freeze({\n minWidth: 320,\n maxWidthFraction: 0.6,\n /** A new dock opens this wide (the 440 of the SPEC-310C shell sketch). */\n defaultWidth: 440,\n /** Below this window width the dock is a sheet over the chat, not a column. */\n sheetBelow: VIEWPORT_BREAKPOINTS.wide,\n /** Below this window width it is a bottom sheet… */\n bottomSheetBelow: VIEWPORT_BREAKPOINTS.compact,\n /** …this tall, in vh. */\n bottomSheetHeightVh: 75,\n /** The top pane's share of the height in a vertical split. */\n splitRatio: Object.freeze({ min: 0.2, max: 0.8, default: 0.5 }),\n});\n\nexport type DockPresentation = 'column' | 'sheet' | 'bottom-sheet';\n\n/** How the dock is drawn at a given window width. */\nexport function dockPresentation(windowWidth: number): DockPresentation {\n if (windowWidth < DOCK_LAYOUT.bottomSheetBelow) return 'bottom-sheet';\n if (windowWidth < DOCK_LAYOUT.sheetBelow) return 'sheet';\n return 'column';\n}\n\n/**\n * A persisted or dragged width clamped for this window. The floor wins over\n * the 60% cap on a window too narrow to honour both (it is a sheet there\n * anyway), so the result is never below minWidth.\n */\nexport function clampDockWidth(width: unknown, windowWidth: number): number {\n const n = typeof width === 'number' && Number.isFinite(width) ? Math.round(width) : DOCK_LAYOUT.defaultWidth;\n const max = Math.max(DOCK_LAYOUT.minWidth, Math.floor(windowWidth * DOCK_LAYOUT.maxWidthFraction));\n return Math.min(max, Math.max(DOCK_LAYOUT.minWidth, n));\n}\n\n// ---------------------------------------------------------------------------\n// Persisted state\n// ---------------------------------------------------------------------------\n\n/** The v2 key C1 migrates FROM (verse-ui-store.ts). */\nexport const VERSE_UI_STORAGE_KEY_V2 = 'ashlr.verse.ui.v2';\n/**\n * The v3 key (SPEC-310C §3 \"State persists in ashlr.verse.ui.v3\"). ONE blob,\n * written by C1's verse-ui-store; the dock's state is its `dock` field, read\n * back through sanitizeDockState — so two writers never race for the key.\n */\nexport const VERSE_UI_STORAGE_KEY = 'ashlr.verse.ui.v3';\n\nexport interface DockState {\n open: boolean;\n /** Last dragged width; clamp with clampDockWidth at render (it depends on the window). */\n width: number;\n /** Open panes, in tab order. */\n tabs: DockPaneId[];\n /** The pane shown (on top, when split); null only when there are no tabs. */\n active: DockPaneId | null;\n /** A second pane shown BELOW `active` (\"Preview over Terminal\"); never equal to it. */\n splitWith: DockPaneId | null;\n /** `active`'s share of the height when split. */\n splitRatio: number;\n}\n\nexport const DEFAULT_DOCK_STATE: Readonly<DockState> = Object.freeze({\n open: false,\n width: DOCK_LAYOUT.defaultWidth,\n tabs: [],\n active: null,\n splitWith: null,\n splitRatio: DOCK_LAYOUT.splitRatio.default,\n});\n\n/**\n * Read a persisted `dock` field without trusting it: unknown panes dropped,\n * duplicates collapsed, `active`/`splitWith` forced to open tabs, numbers\n * clamped. Anything unreadable falls back to the default, never throws — a\n * hand-edited or future-version blob must not take the chat down.\n */\nexport function sanitizeDockState(value: unknown): DockState {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return { ...DEFAULT_DOCK_STATE, tabs: [] };\n const raw = value as Record<string, unknown>;\n const tabs: DockPaneId[] = [];\n if (Array.isArray(raw['tabs'])) {\n for (const tab of raw['tabs']) if (isDockPaneId(tab) && !tabs.includes(tab)) tabs.push(tab);\n }\n const active: DockPaneId | null = isDockPaneId(raw['active']) && tabs.includes(raw['active']) ? raw['active'] : (tabs[0] ?? null);\n const splitWith: DockPaneId | null =\n isDockPaneId(raw['splitWith']) && tabs.includes(raw['splitWith']) && raw['splitWith'] !== active ? raw['splitWith'] : null;\n const width = typeof raw['width'] === 'number' && Number.isFinite(raw['width']) && raw['width'] >= DOCK_LAYOUT.minWidth\n ? Math.round(raw['width'])\n : DOCK_LAYOUT.defaultWidth;\n const ratio = typeof raw['splitRatio'] === 'number' && Number.isFinite(raw['splitRatio']) ? raw['splitRatio'] : DOCK_LAYOUT.splitRatio.default;\n return {\n open: raw['open'] === true && tabs.length > 0,\n width,\n tabs,\n active,\n splitWith,\n splitRatio: Math.min(DOCK_LAYOUT.splitRatio.max, Math.max(DOCK_LAYOUT.splitRatio.min, ratio)),\n };\n}\n","/**\n * routes/verse/dock/dock-store.ts — the chat dock's state and the one-shot\n * requests aimed at its panes (unit C2; contract: shell/dock-catalog.ts).\n *\n * PERSISTENCE. The dock's state belongs in `ashlr.verse.ui.v3`'s `dock`\n * field, and that blob has ONE writer — C1's verse-ui-store — so two stores\n * never race for the key (dock-catalog.ts). This store therefore never\n * writes the key. It:\n * - hydrates itself, read-only, from the v3 blob's `dock` field on first\n * read (through sanitizeDockState — a hand-edited blob cannot break the\n * chat);\n * - exposes getDockState / subscribeDockState, which C1's store folds into\n * the blob it writes (`dock: getDockState()`, re-written on change);\n * - accepts hydrateDockState(raw) when C1 prefers to push the field in.\n *\n * REQUESTS. \"Run in terminal\", a ± count opening the branch diff, Preview's\n * dev-server Start: each is a one-shot request with a nonce (slots.tsx\n * contracts) — the same request twice is two opens. They live here, not in\n * React state, because they are raised from deep inside the transcript\n * (CommandOutput) and consumed by a pane in another subtree.\n *\n * Framework-free; the React glue is useDock() at the bottom.\n */\nimport { useSyncExternalStore } from 'react';\nimport {\n DOCK_LAYOUT,\n VERSE_UI_STORAGE_KEY,\n sanitizeDockState,\n type DockPaneId,\n type DockState,\n} from '../shell/dock-catalog.js';\nimport type { DiffPaneRequest, PreviewOpenRequest, TerminalOpenRequest } from '../shell/slots.js';\n\n/**\n * A terminal request as the dock carries it. C0's TerminalOpenRequest now\n * names the Apps [Launch ▸] fields (`via`, `model`) itself, so this is a\n * plain alias — kept so TerminalPane and the Apps launcher keep one name for\n * \"what the dock hands the Terminal pane\". The server resolves the actual\n * command either way, never the page.\n */\nexport type TerminalRequest = TerminalOpenRequest;\n\nexport interface DockRequests {\n terminal: TerminalRequest | null;\n preview: PreviewOpenRequest | null;\n diff: (DiffPaneRequest & { nonce: number }) | null;\n}\n\nexport interface DockSnapshot {\n state: DockState;\n requests: DockRequests;\n}\n\nconst NO_REQUESTS: DockRequests = { terminal: null, preview: null, diff: null };\n\nlet snapshot: DockSnapshot | null = null;\nlet nonce = 0;\nconst listeners = new Set<() => void>();\n\nfunction readPersisted(): DockState {\n try {\n const raw = localStorage.getItem(VERSE_UI_STORAGE_KEY);\n if (!raw) return sanitizeDockState(null);\n const parsed = JSON.parse(raw) as { dock?: unknown } | null;\n return sanitizeDockState(parsed && typeof parsed === 'object' ? parsed.dock : null);\n } catch {\n return sanitizeDockState(null);\n }\n}\n\nfunction current(): DockSnapshot {\n if (snapshot === null) snapshot = { state: readPersisted(), requests: NO_REQUESTS };\n return snapshot;\n}\n\nfunction emit(next: DockSnapshot): void {\n snapshot = next;\n for (const listener of [...listeners]) listener();\n}\n\nfunction setState(next: DockState): void {\n emit({ ...current(), state: next });\n}\n\nexport function subscribeDockState(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getDockSnapshot(): DockSnapshot {\n return current();\n}\n\n/** The persistable part — what C1's store writes as the v3 blob's `dock` field. */\nexport function getDockState(): DockState {\n return current().state;\n}\n\n/** C1 may push the persisted field in (on load, or when another tab wrote it). */\nexport function hydrateDockState(raw: unknown): void {\n setState(sanitizeDockState(raw));\n}\n\n/** Test seam: back to defaults with no requests; next read re-hydrates from storage. */\nexport function resetDockStore(): void {\n snapshot = null;\n nonce = 0;\n for (const listener of [...listeners]) listener();\n}\n\n// ---------------------------------------------------------------------------\n// Transitions (pure over DockState, exported for tests)\n// ---------------------------------------------------------------------------\n\n/** Open `pane` (adding its tab) and make it the visible one. */\nexport function withPaneOpen(state: DockState, pane: DockPaneId): DockState {\n const tabs = state.tabs.includes(pane) ? state.tabs : [...state.tabs, pane];\n // Opening the pane that is the split's lower half swaps it to the top.\n const splitWith = state.splitWith === pane ? (state.active !== pane ? state.active : null) : state.splitWith;\n return { ...state, open: true, tabs, active: pane, splitWith: splitWith === pane ? null : splitWith };\n}\n\n/** The pane's toggle: open & focus it, or — when it is already the visible pane — close the dock. */\nexport function withPaneToggled(state: DockState, pane: DockPaneId): DockState {\n if (state.open && (state.active === pane || state.splitWith === pane)) return { ...state, open: false };\n return withPaneOpen(state, pane);\n}\n\nexport function withTabClosed(state: DockState, pane: DockPaneId): DockState {\n const tabs = state.tabs.filter((t) => t !== pane);\n const splitWith = state.splitWith === pane ? null : state.splitWith;\n let active = state.active;\n if (active === pane) {\n // The split's lower pane moves up; otherwise the neighbour tab takes over.\n const index = state.tabs.indexOf(pane);\n active = splitWith ?? tabs[Math.min(index, tabs.length - 1)] ?? null;\n }\n return {\n ...state,\n tabs,\n active,\n splitWith: splitWith === active ? null : splitWith,\n open: tabs.length > 0 && state.open,\n };\n}\n\n/** Split `pane` BELOW the active one (\"Preview over Terminal\"); null un-splits. */\nexport function withSplit(state: DockState, pane: DockPaneId | null): DockState {\n if (pane === null) return { ...state, splitWith: null };\n if (state.active === null || pane === state.active) return state;\n const tabs = state.tabs.includes(pane) ? state.tabs : [...state.tabs, pane];\n return { ...state, open: true, tabs, splitWith: pane };\n}\n\nexport function withWidth(state: DockState, width: number): DockState {\n if (!Number.isFinite(width)) return state;\n return { ...state, width: Math.max(DOCK_LAYOUT.minWidth, Math.round(width)) };\n}\n\nexport function withSplitRatio(state: DockState, ratio: number): DockState {\n if (!Number.isFinite(ratio)) return state;\n const { min, max } = DOCK_LAYOUT.splitRatio;\n return { ...state, splitRatio: Math.min(max, Math.max(min, ratio)) };\n}\n\n// ---------------------------------------------------------------------------\n// Actions\n// ---------------------------------------------------------------------------\n\nexport function openDockPane(pane: DockPaneId): void {\n setState(withPaneOpen(current().state, pane));\n}\n\nexport function toggleDockPane(pane: DockPaneId): void {\n setState(withPaneToggled(current().state, pane));\n}\n\n/** ⌘\\ — show or hide the dock as it was; an empty dock opens on Tasks. */\nexport function toggleDock(): void {\n const state = current().state;\n if (state.open) setState({ ...state, open: false });\n else if (state.tabs.length === 0) setState(withPaneOpen(state, 'tasks'));\n else setState({ ...state, open: true, active: state.active ?? state.tabs[0]! });\n}\n\nexport function closeDock(): void {\n const state = current().state;\n if (state.open) setState({ ...state, open: false });\n}\n\nexport function closeDockTab(pane: DockPaneId): void {\n setState(withTabClosed(current().state, pane));\n}\n\nexport function splitDock(pane: DockPaneId | null): void {\n setState(withSplit(current().state, pane));\n}\n\nexport function setDockWidth(width: number): void {\n setState(withWidth(current().state, width));\n}\n\nexport function setDockSplitRatio(ratio: number): void {\n setState(withSplitRatio(current().state, ratio));\n}\n\n/** Open the Terminal pane with a request (a tab at a root, a pasted command, an app launch). */\nexport function requestTerminal(request: Omit<TerminalRequest, 'nonce'>): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'terminal'), requests: { ...snap.requests, terminal: { ...request, nonce } } });\n}\n\n/**\n * The same request, but with Terminal as the LOWER half of a split under the\n * pane that is showing (Preview's dev-server Start: the page stays on top and\n * appears when its port answers, the server's output runs underneath — the\n * \"Preview over Terminal\" layout). With nothing showing, or when Terminal is\n * the pane on top, it is a plain open. In the bottom sheet the split's lower\n * pane is mounted but hidden, so the request still runs.\n */\nexport function requestTerminalBelow(request: Omit<TerminalRequest, 'nonce'>): void {\n const snap = current();\n const top = snap.state.open ? snap.state.active : null;\n if (top === null || top === 'terminal') {\n requestTerminal(request);\n return;\n }\n nonce += 1;\n emit({ state: withSplit(snap.state, 'terminal'), requests: { ...snap.requests, terminal: { ...request, nonce } } });\n}\n\nexport function requestPreview(request: Omit<PreviewOpenRequest, 'nonce'>): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'preview'), requests: { ...snap.requests, preview: { ...request, nonce } } });\n}\n\nexport function requestDiff(request: DiffPaneRequest): void {\n nonce += 1;\n const snap = current();\n emit({ state: withPaneOpen(snap.state, 'diff'), requests: { ...snap.requests, diff: { ...request, nonce } } });\n}\n\n/**\n * Requests are per chat: switching chats must not replay the last chat's\n * terminal paste. Callers clear on a SWITCH only — never on the Chat\n * section's first mount, which is exactly when an Apps [Launch ▸] (raised on\n * another surface, then `setVerseSection('chat')`) is waiting to be served.\n */\nexport function clearDockRequests(): void {\n const snap = current();\n if (snap.requests === NO_REQUESTS) return;\n emit({ ...snap, requests: NO_REQUESTS });\n}\n\nexport function useDock(): DockSnapshot {\n return useSyncExternalStore(subscribeDockState, getDockSnapshot, getDockSnapshot);\n}\n"],"mappings":"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"}
|
package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js → fleet-model-IRbwfhkR.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{g as e,s as t}from"./format-
|
|
2
|
-
//# sourceMappingURL=fleet-model-
|
|
1
|
+
import{g as e,s as t}from"./format-CaMmVQCn.js";function n(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:null}function r(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:null}function i(e,t){let n=e[t];return typeof n==`number`&&Number.isFinite(n)?n:null}function a(e,t){let n=i(e,t);return n===null?null:Math.max(0,Math.round(n))}function o(e,t){let n=e[t];return typeof n==`boolean`?n:null}function s(e,t){let n=e[t];return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]}var c=[`llama-server`,`ollama`,`vllm`,`unknown`],l=[`running`,`starting`,`stopping`,`stopped`,`unknown`],u=[`queued`,`running`,`finishing`],d=[`off`,`config`,`env`,`config+env`,`latch`,`unknown`];function f(e,t,n,r){let i=e[t];return typeof i==`string`&&n.includes(i)?i:r}function p(e){let t=n(e);return t?{capable:o(t,`capable`),refusal:r(t,`refusal`),slots:a(t,`slots`)}:{capable:null,refusal:null,slots:null}}var m={up:`running`,down:`stopped`,loading:`starting`,unknown:`unknown`};function h(e){let t=e.state;if(typeof t!=`string`||!(t in m))return null;let i=n(e.slots),s=r(e,`host`),c=a(e,`port`),l=i?a(i,`configured`):null,u=i?a(i,`busy`):null,d=i?r(i,`source`):null,f=d!==null&&d!==`unknown`,p=f?l:null,h=r(e,`parallelRefusal`),_=r(e,`runtimeKind`),v=a(e,`contextPerSlot`),y=a(e,`contextTotal`);return{kind:_===`ollama`?`ollama`:_===`unknown`?`unknown`:`llama-server`,state:m[t]??`unknown`,endpoint:s!==null&&c!==null?`${s}:${c}`:r(e,`origin`)??null,model:g(r(e,`modelName`))??g(r(e,`model`)),slotsTotal:p,slotsBusy:f?u:null,contextTokens:v??(p!==null&&p>0&&y!==null&&y>0?Math.floor(y/p):null),startedAt:r(e,`startedAt`),parallel:{capable:h===null?f&&p!==null?!0:null:!1,refusal:h,slots:p},reason:r(e,`lastError`),supervised:o(e,`managed`)??!1,sampledAt:r(e,`checkedAt`)}}function g(e){if(e===null||e.length===0)return null;let t=e.split(`/`).pop()??e;return t.length===0||/^sha256[-:][0-9a-f]{16,}$/i.test(t)?null:t}function _(e){let t=n(e);if(!t)return null;if(!(`kind`in t)&&`slots`in t&&`state`in t){let e=h(t);if(e!==null)return e}return{kind:f(t,`kind`,c,`unknown`),state:f(t,`state`,l,`unknown`),endpoint:r(t,`endpoint`),model:r(t,`model`),slotsTotal:a(t,`slotsTotal`),slotsBusy:a(t,`slotsBusy`),contextTokens:a(t,`contextTokens`),startedAt:r(t,`startedAt`),parallel:p(t.parallel),reason:r(t,`reason`),supervised:o(t,`supervised`)??!1,sampledAt:r(t,`sampledAt`)}}function v(e){let t=n(e);if(!t)return null;let i=r(t,`id`);return i===null?null:{id:i,task:r(t,`task`),repo:r(t,`repo`),engine:r(t,`engine`),model:r(t,`model`),state:f(t,`state`,u,`running`),startedAt:r(t,`startedAt`),slot:a(t,`slot`)}}function y(e){let t=n(e);return t?{agents:(Array.isArray(t.agents)?t.agents:[]).map(v).filter(e=>e!==null),queueDepth:a(t,`queueDepth`),slotsTotal:a(t,`slotsTotal`),slotsBusy:a(t,`slotsBusy`),notes:s(t,`notes`),sampledAt:r(t,`sampledAt`)}:null}function b(e){let t=n(e);if(!t)return null;let i=r(t,`engine`);return i===null?null:{engine:i,reason:r(t,`reason`)??`The dispatcher refuses this engine while local-only is on.`}}var x=new Set([`env`,`config+env`,`latch`,`unknown`]);function S(e){let t=n(e);if(!t)return null;let i=Array.isArray(t.refuses)?t.refuses:[],a=f(t,`source`,d,`unknown`);return{enabled:o(t,`enabled`)??!1,source:a,refuses:i.map(b).filter(e=>e!==null),mutable:x.has(a)?!1:o(t,`mutable`)??!1,detail:r(t,`detail`),sampledAt:r(t,`sampledAt`)??r(t,`checkedAt`)}}function C(e){switch(e){case`llama-server`:return`llama-server`;case`ollama`:return`Ollama`;case`vllm`:return`vLLM`;default:return`the serving runtime`}}function w(e){if(e===null)return{verdict:`unknown`,effectiveConcurrency:null,configuredSlots:null,overstated:!1,headline:`No serving runtime reported`,detail:`Nothing answered for the serving runtime, so how many agents can run at once is unknown. That is a missing reading, not an idle machine.`,tone:`unknown`};let t=e.slotsTotal??e.parallel.slots,n=C(e.kind);if(e.state!==`running`)return{verdict:`offline`,effectiveConcurrency:null,configuredSlots:t,overstated:!1,headline:e.state===`stopped`?`Not serving`:`Serving runtime is ${e.state}`,detail:e.state===`stopped`?`${n} is not running, so no local agent can take a turn. Start it before dispatching a fleet.`:`${n} is ${e.state}; capacity is not settled until it reports running.`,tone:e.state===`stopped`?`neutral`:`warning`};if(e.parallel.capable===!1){let e=t!==null&&t>1;return{verdict:`serialized`,effectiveConcurrency:1,configuredSlots:t,overstated:e,headline:`${n} serializes requests`,detail:e?`${n} has ${t} slots configured but runs one request at a time for this model, so the real capacity is one agent. The other ${t-1} would queue, and a queue looks like slowness rather than a wait.`:`${n} runs one request at a time for this model. A second agent waits for the first to finish.`,tone:`danger`}}return e.parallel.capable===null?{verdict:`unknown`,effectiveConcurrency:null,configuredSlots:t,overstated:!1,headline:`Batching not confirmed`,detail:`${n} is running${t===null?``:` with ${t} slots configured`}, but it has not reported whether it batches concurrent requests for this model. Until it does, this panel will not quote a concurrency it cannot stand behind.`,tone:`unknown`}:t===null?{verdict:`batched`,effectiveConcurrency:null,configuredSlots:null,overstated:!1,headline:`${n} batches concurrent requests`,detail:`${n} batches, but reported no slot count, so the ceiling on concurrent agents is unknown.`,tone:`unknown`}:{verdict:`batched`,effectiveConcurrency:t,configuredSlots:t,overstated:!1,headline:`${t} agent${t===1?``:`s`} in parallel`,detail:`${n} batches across ${t} slot${t===1?``:`s`} sharing one copy of the weights, so ${t} agent${t===1?``:`s`} can be mid-turn at once.`,tone:`success`}}var T=[{runtime:`Ollama`,perRequestSeconds:[3.7,7.6,11.4,15.2],wallSeconds:15.2,verdict:`staggered — the requests queued`},{runtime:`llama-server`,perRequestSeconds:[8.6,8.9,9,9],wallSeconds:9.1,verdict:`together — the requests batched`}];function E(t,n){if(t===null||n===null||n<=0)return{busy:t,total:n,percent:null,percentText:null,saturated:!1,label:t===null?`Slot occupancy is not reported.`:`${t} in flight; the number of slots is not known, so occupancy cannot be drawn.`};let r=Math.min(t,n)/n*100,i=Math.round(r),a=t>=n;return{busy:t,total:n,percent:i,percentText:e(r),saturated:a,label:a?`All ${n} slot${n===1?``:`s`} busy — new turns wait for one to free up.`:`${t} of ${n} slot${n===1?``:`s`} busy.`}}function D(e,t,n){if(e===null)return{state:`unknown`,headline:`Fleet not reported`,detail:`No fleet snapshot answered, so what is in flight cannot be listed. That is a missing reading, not an empty fleet.`,tone:`unknown`};let r=e.agents.filter(e=>e.state!==`queued`).length,i=e.queueDepth??e.agents.filter(e=>e.state===`queued`).length;return i>0?{state:`queued`,headline:`${i} waiting for a slot`,detail:n.verdict===`serialized`?`${r} turn${r===1?``:`s`} in flight and ${i} queued. This runtime serializes, so the queue drains one at a time — the wait is a wait, not slow inference.`:`${r} turn${r===1?``:`s`} in flight and ${i} queued behind ${t.total===null?`the available slots`:`${t.total} slots`}. Queued turns have not started; their wall time is queueing, not work.`,tone:`warning`}:r===0?{state:`idle`,headline:`Nothing in flight`,detail:`No agent is mid-turn. The fleet is idle, not stuck.`,tone:`neutral`}:t.saturated?{state:`saturated`,headline:`All slots busy · ${r} in flight`,detail:`Every slot is working and nothing is queued behind them. The next turn will wait, but none is waiting yet.`,tone:`running`}:{state:`working`,headline:`${r} in flight`,detail:t.label,tone:`running`}}var O=`Refused: local-only is on and this engine is not local.`;function k(e,t){let n=t.filter(e=>e.engine!==`local`),r=t.filter(e=>e.engine===`local`),i=new Map;for(let t of e?.refuses??[])i.set(t.engine,t.reason);return{blocked:n,allowed:r,refusalFor:e=>i.get(e)??O,summary:t.length===0?`No seats are configured, so there is nothing for this policy to block yet.`:n.length===0?`Every one of your ${t.length} seat${t.length===1?``:`s`} is local, so this policy would block nothing today.`:`${n.length} of ${t.length} seat${t.length===1?``:`s`} would become unreachable; ${r.length} local seat${r.length===1?``:`s`} would keep working.`}}function A(e){if(e===null)return null;if(e.detail!==null)return e.detail;switch(e.source){case`env`:case`config+env`:return`This policy is pinned by an environment variable on the server process, so it cannot be changed from here — restart the server without it to take the switch back.`;case`latch`:return`This process already observed a persisted local-only and has pinned it for its own lifetime, so turning it off takes effect for new dispatches but not for the seams that latched. Restart the server to clear it.`;case`config`:return`Stored in the hub config and applied on the next dispatch.`;case`off`:return`Not configured; cloud engines are reachable.`;default:return`The server did not say where this setting came from.`}}function j(e,n){if(!e)return`—`;let r=Date.parse(e);return Number.isNaN(r)?`—`:t(Math.max(0,n-r))}function M(e,n){if(!e)return`—`;let r=Date.parse(e);return Number.isNaN(r)?`—`:`up ${t(Math.max(0,n-r))}`}function N(e){return e===null||!Number.isFinite(e)?`—`:e>=1024?`${Math.round(e/1024)}k`:String(e)}function P(e){let t={running:0,finishing:1,queued:2};return[...e].sort((e,n)=>{let r=t[e.state]-t[n.state];if(r!==0)return r;let i=e.startedAt?Date.parse(e.startedAt):NaN,a=n.startedAt?Date.parse(n.startedAt):NaN;return Number.isNaN(i)&&Number.isNaN(a)?e.id.localeCompare(n.id):Number.isNaN(i)?1:Number.isNaN(a)?-1:i-a})}export{M as a,P as c,_ as d,w as f,N as i,y as l,E as m,j as n,k as o,C as p,D as r,A as s,T as t,S as u};
|
|
2
|
+
//# sourceMappingURL=fleet-model-IRbwfhkR.js.map
|
package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js.map → fleet-model-IRbwfhkR.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-model-CNe8Wh-s.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/autonomy/fleet-model.ts"],"sourcesContent":["/**\n * routes/verse/autonomy/fleet-model.ts — every derivation the local-fleet\n * surfaces make, as pure functions. No React, no I/O, so the rules that decide\n * what the operator is told are unit-testable without a DOM.\n *\n * The one rule this file exists to enforce:\n *\n * **A concurrency number the runtime will not honour is a lie, and the UI\n * must print the truth instead of the configuration.**\n *\n * That is not a hypothetical. Measured on this machine (docs/LOCAL-FLEET.md),\n * four concurrent Qwen3.8 requests to Ollama returned at 3.7 / 7.6 / 11.4 /\n * 15.2s — a four-second stagger, which is a queue. The same weights on\n * llama-server returned at 8.6 / 8.9 / 9.0 / 9.0s: continuous batching across\n * four slots sharing one 27 GB copy. Ollama's log calls it a refusal\n * (`model architecture does not currently support parallel requests`) and\n * `OLLAMA_NUM_PARALLEL` does not move it.\n *\n * So `runtimeCapacity()` never reports `slotsTotal` as capacity unless the\n * runtime says it batches. When a runtime that serializes is configured with\n * four slots, the capacity is ONE and `overstated` is true, and the panel says\n * so in words. A queue that looks like slowness is the exact failure this\n * surface is here to prevent.\n *\n * The bodies these functions narrow come from routes that are landing in\n * parallel (see fleet-contract.ts), so everything is projected STRUCTURALLY\n * rather than cast: a field-name drift degrades to an honest \"unknown\" at\n * render time instead of throwing.\n */\nimport { UNKNOWN, formatDuration, percentText } from './format.js';\nimport type {\n FleetAgent,\n FleetAgentState,\n FleetSnapshot,\n LocalOnlyPolicy,\n LocalOnlyRefusal,\n LocalOnlySource,\n ServingRuntimeKind,\n ServingRuntimeParallelism,\n ServingRuntimeSnapshot,\n ServingRuntimeState,\n} from './fleet-contract.js';\n\n// ---------------------------------------------------------------------------\n// Structural narrowing\n// ---------------------------------------------------------------------------\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction str(source: Record<string, unknown>, key: string): string | null {\n const v = source[key];\n return typeof v === 'string' && v.length > 0 ? v : null;\n}\n\nfunction num(source: Record<string, unknown>, key: string): number | null {\n const v = source[key];\n return typeof v === 'number' && Number.isFinite(v) ? v : null;\n}\n\n/** A count that is meaningful at zero — so 0 stays 0 and absent stays null. */\nfunction count(source: Record<string, unknown>, key: string): number | null {\n const v = num(source, key);\n return v === null ? null : Math.max(0, Math.round(v));\n}\n\nfunction bool(source: Record<string, unknown>, key: string): boolean | null {\n const v = source[key];\n return typeof v === 'boolean' ? v : null;\n}\n\nfunction strings(source: Record<string, unknown>, key: string): string[] {\n const v = source[key];\n return Array.isArray(v) ? v.filter((entry): entry is string => typeof entry === 'string') : [];\n}\n\nconst RUNTIME_KINDS: readonly ServingRuntimeKind[] = ['llama-server', 'ollama', 'vllm', 'unknown'];\nconst RUNTIME_STATES: readonly ServingRuntimeState[] = [\n 'running',\n 'starting',\n 'stopping',\n 'stopped',\n 'unknown',\n];\nconst AGENT_STATES: readonly FleetAgentState[] = ['queued', 'running', 'finishing'];\nconst LOCAL_ONLY_SOURCES: readonly LocalOnlySource[] = [\n 'off',\n 'config',\n 'env',\n 'config+env',\n 'latch',\n 'unknown',\n];\n\nfunction oneOf<T extends string>(\n source: Record<string, unknown>,\n key: string,\n allowed: readonly T[],\n fallback: T,\n): T {\n const v = source[key];\n return typeof v === 'string' && (allowed as readonly string[]).includes(v) ? (v as T) : fallback;\n}\n\nfunction projectParallelism(raw: unknown): ServingRuntimeParallelism {\n const r = record(raw);\n if (!r) return { capable: null, refusal: null, slots: null };\n return { capable: bool(r, 'capable'), refusal: str(r, 'refusal'), slots: count(r, 'slots') };\n}\n\n/**\n * Owner R's `LlamaRuntimeSnapshot` (`src/core/local-runtime/llama/types.ts`)\n * spells the same facts differently, and the route that serves it is not\n * written yet — so rather than betting on one envelope, this reads both.\n *\n * The mapping, with the two judgement calls stated:\n *\n * - `state: 'up' | 'down' | 'loading' | 'unknown'` → running / stopped /\n * starting / unknown.\n * - `contextPerSlot` wins over `contextTotal`. `-c 65536 --parallel 4` gives\n * each slot 16k, and the number an agent's turn actually runs in is the\n * per-slot one. Quoting the total would overstate every agent's context\n * fourfold — the same class of error as quoting slots as concurrency.\n * - `managed` → `supervised`. R is explicit that an unmanaged server may be\n * adopted but is \"never killed on a guess\", which is exactly the condition\n * under which this panel must not offer a stop button.\n */\nconst LLAMA_STATE: Readonly<Record<string, ServingRuntimeState>> = {\n up: 'running',\n down: 'stopped',\n loading: 'starting',\n unknown: 'unknown',\n};\n\nfunction projectLlamaShape(r: Record<string, unknown>): ServingRuntimeSnapshot | null {\n const rawState = r['state'];\n if (typeof rawState !== 'string' || !(rawState in LLAMA_STATE)) return null;\n const slots = record(r['slots']);\n const host = str(r, 'host');\n const port = count(r, 'port');\n const configured = slots ? count(slots, 'configured') : null;\n const busy = slots ? count(slots, 'busy') : null;\n // `slots.source` is R's own trust marker: 'unknown' means \"do not trust a\n // number\", so a slot count carrying it is dropped rather than quoted.\n const slotSource = slots ? str(slots, 'source') : null;\n const trusted = slotSource !== null && slotSource !== 'unknown';\n const total = trusted ? configured : null;\n\n const refusal = str(r, 'parallelRefusal');\n const identity = str(r, 'runtimeKind');\n const contextPerSlot = count(r, 'contextPerSlot');\n const contextTotal = count(r, 'contextTotal');\n\n return {\n // IDENTIFIED where the producer says so. A base URL pointed at Ollama's\n // /v1 answers /v1/models — enough to keep the engine routable — while\n // 404ing /health and /props, and asserting 'llama-server' for it would\n // hide the one fact that matters: that runtime serialises this model.\n kind: identity === 'ollama' ? 'ollama' : identity === 'unknown' ? 'unknown' : 'llama-server',\n state: LLAMA_STATE[rawState] ?? 'unknown',\n endpoint: host !== null && port !== null ? `${host}:${port}` : (str(r, 'origin') ?? null),\n // A digest is not a name. `modelName` falls back to `model_path`'s basename\n // at the producer, which for a blob out of Ollama's content-addressed store\n // is `sha256-…`: it identifies the machine's storage layout and names\n // nothing. Null renders as \"unknown\", which is the honest answer.\n model: humaneModelName(str(r, 'modelName')) ?? humaneModelName(str(r, 'model')),\n slotsTotal: total,\n slotsBusy: trusted ? busy : null,\n // PER-SLOT, or nothing. `-c 65536 --parallel 4` gives each agent 16k, so\n // the `-c` total is usable only divided by a slot count we trust.\n contextTokens:\n contextPerSlot ??\n (total !== null && total > 0 && contextTotal !== null && contextTotal > 0\n ? Math.floor(contextTotal / total)\n : null),\n startedAt: str(r, 'startedAt'),\n parallel: {\n // llama-server's slots ARE its continuous-batching slots: a count read\n // back off the live server (`/props.total_slots`, `/slots`) is a count\n // of requests it will run at the same time, which is what the\n // measurement in docs/LOCAL-FLEET.md showed. An untrusted or absent\n // reading claims nothing — `null`, not `true`.\n capable: refusal !== null ? false : trusted && total !== null ? true : null,\n // The runtime's own words when it refuses, carried verbatim as evidence.\n refusal,\n slots: total,\n },\n reason: str(r, 'lastError'),\n supervised: bool(r, 'managed') ?? false,\n sampledAt: str(r, 'checkedAt'),\n };\n}\n\n/** A displayable model name, or null for an absent or digest-shaped one. */\nfunction humaneModelName(raw: string | null): string | null {\n if (raw === null || raw.length === 0) return null;\n const base = raw.split('/').pop() ?? raw;\n if (base.length === 0) return null;\n if (/^sha256[-:][0-9a-f]{16,}$/i.test(base)) return null;\n return base;\n}\n\nexport function projectServingRuntime(raw: unknown): ServingRuntimeSnapshot | null {\n const r = record(raw);\n if (!r) return null;\n // A body already in this surface's own shape wins; otherwise try owner R's.\n if (!('kind' in r) && 'slots' in r && 'state' in r) {\n const llama = projectLlamaShape(r);\n if (llama !== null) return llama;\n }\n return {\n kind: oneOf(r, 'kind', RUNTIME_KINDS, 'unknown'),\n state: oneOf(r, 'state', RUNTIME_STATES, 'unknown'),\n endpoint: str(r, 'endpoint'),\n model: str(r, 'model'),\n slotsTotal: count(r, 'slotsTotal'),\n slotsBusy: count(r, 'slotsBusy'),\n contextTokens: count(r, 'contextTokens'),\n startedAt: str(r, 'startedAt'),\n parallel: projectParallelism(r['parallel']),\n reason: str(r, 'reason'),\n // A runtime this server cannot supervise must not be offered buttons, so\n // an unreported flag reads as NOT supervised. The conservative default is\n // \"no controls\" rather than controls that 404 on the first click.\n supervised: bool(r, 'supervised') ?? false,\n sampledAt: str(r, 'sampledAt'),\n };\n}\n\nfunction projectAgent(raw: unknown): FleetAgent | null {\n const r = record(raw);\n if (!r) return null;\n const id = str(r, 'id');\n if (id === null) return null;\n return {\n id,\n task: str(r, 'task'),\n repo: str(r, 'repo'),\n engine: str(r, 'engine'),\n model: str(r, 'model'),\n state: oneOf(r, 'state', AGENT_STATES, 'running'),\n startedAt: str(r, 'startedAt'),\n slot: count(r, 'slot'),\n };\n}\n\nexport function projectFleet(raw: unknown): FleetSnapshot | null {\n const r = record(raw);\n if (!r) return null;\n const rawAgents = Array.isArray(r['agents']) ? (r['agents'] as unknown[]) : [];\n return {\n agents: rawAgents.map(projectAgent).filter((a): a is FleetAgent => a !== null),\n queueDepth: count(r, 'queueDepth'),\n slotsTotal: count(r, 'slotsTotal'),\n slotsBusy: count(r, 'slotsBusy'),\n notes: strings(r, 'notes'),\n sampledAt: str(r, 'sampledAt'),\n };\n}\n\nfunction projectRefusal(raw: unknown): LocalOnlyRefusal | null {\n const r = record(raw);\n if (!r) return null;\n const engine = str(r, 'engine');\n if (engine === null) return null;\n return { engine, reason: str(r, 'reason') ?? 'The dispatcher refuses this engine while local-only is on.' };\n}\n\n/**\n * `source` is carried through as the resolver's own value — it is no longer\n * flattened onto a smaller union here.\n *\n * The flattening this replaced mapped `'latch'` onto `'config'`, which tells\n * an operator the mode is stored in the config and editable when in fact the\n * process has pinned it for its own lifetime. Only the separate `mutable`\n * flag kept that from reaching the screen as a working switch that silently\n * does nothing. An unrecognised value lands on `'unknown'`, which makes no\n * claim at all.\n */\n/** Sources that pin the mode to this server process, whatever the config says. */\nconst PINNED_SOURCES: ReadonlySet<LocalOnlySource> = new Set<LocalOnlySource>([\n 'env',\n 'config+env',\n 'latch',\n 'unknown',\n]);\n\nexport function projectLocalOnly(raw: unknown): LocalOnlyPolicy | null {\n const r = record(raw);\n if (!r) return null;\n const rawRefusals = Array.isArray(r['refuses']) ? (r['refuses'] as unknown[]) : [];\n const source = oneOf(r, 'source', LOCAL_ONLY_SOURCES, 'unknown');\n return {\n // An unreported `enabled` is NOT \"on\": claiming cloud is unreachable when\n // the server never said so is the one error this policy cannot afford.\n enabled: bool(r, 'enabled') ?? false,\n source,\n refuses: rawRefusals.map(projectRefusal).filter((x): x is LocalOnlyRefusal => x !== null),\n // A policy pinned by the server's own process — an env var, or a latch\n // this process already took — is never editable from here, whatever the\n // body claims: a write would be silently overridden on the next read.\n // `'unknown'` is treated as pinned too, because offering a switch for a\n // policy this client cannot identify is the more expensive mistake.\n mutable: PINNED_SOURCES.has(source) ? false : (bool(r, 'mutable') ?? false),\n detail: str(r, 'detail'),\n sampledAt: str(r, 'sampledAt') ?? str(r, 'checkedAt'),\n };\n}\n\n// ---------------------------------------------------------------------------\n// The capacity verdict — the point of this whole file\n// ---------------------------------------------------------------------------\n\nexport type CapacityVerdict = 'batched' | 'serialized' | 'unknown' | 'offline';\n\nexport interface RuntimeCapacity {\n verdict: CapacityVerdict;\n /**\n * How many agents can genuinely be in flight at once. `null` means it\n * cannot be known from this reading — which is NOT the same as 0 and must\n * never render as a number.\n */\n effectiveConcurrency: number | null;\n /** What the runtime was configured with, honoured or not. */\n configuredSlots: number | null;\n /**\n * True when the configured slot count is larger than what the runtime will\n * actually run in parallel. While this is true, the configured number must\n * not be presented as capacity anywhere on the screen.\n */\n overstated: boolean;\n headline: string;\n detail: string;\n tone: 'success' | 'warning' | 'danger' | 'neutral' | 'unknown';\n}\n\n/** Human name for a runtime, used in sentences. */\nexport function runtimeLabel(kind: ServingRuntimeKind): string {\n switch (kind) {\n case 'llama-server':\n return 'llama-server';\n case 'ollama':\n return 'Ollama';\n case 'vllm':\n return 'vLLM';\n default:\n return 'the serving runtime';\n }\n}\n\nexport function runtimeCapacity(runtime: ServingRuntimeSnapshot | null): RuntimeCapacity {\n if (runtime === null) {\n return {\n verdict: 'unknown',\n effectiveConcurrency: null,\n configuredSlots: null,\n overstated: false,\n headline: 'No serving runtime reported',\n detail:\n 'Nothing answered for the serving runtime, so how many agents can run at once is unknown. That is a missing reading, not an idle machine.',\n tone: 'unknown',\n };\n }\n\n const configured = runtime.slotsTotal ?? runtime.parallel.slots;\n const name = runtimeLabel(runtime.kind);\n\n if (runtime.state !== 'running') {\n return {\n verdict: 'offline',\n effectiveConcurrency: null,\n configuredSlots: configured,\n overstated: false,\n headline: runtime.state === 'stopped' ? 'Not serving' : `Serving runtime is ${runtime.state}`,\n detail:\n runtime.state === 'stopped'\n ? `${name} is not running, so no local agent can take a turn. Start it before dispatching a fleet.`\n : `${name} is ${runtime.state}; capacity is not settled until it reports running.`,\n tone: runtime.state === 'stopped' ? 'neutral' : 'warning',\n };\n }\n\n if (runtime.parallel.capable === false) {\n // The measured case. The configured number is real — it is simply not a\n // concurrency, and printing it as one is the failure mode this exists for.\n const overstated = configured !== null && configured > 1;\n return {\n verdict: 'serialized',\n effectiveConcurrency: 1,\n configuredSlots: configured,\n overstated,\n headline: `${name} serializes requests`,\n detail: overstated\n ? `${name} has ${configured} slots configured but runs one request at a time for this model, so the real capacity is one agent. The other ${configured - 1} would queue, and a queue looks like slowness rather than a wait.`\n : `${name} runs one request at a time for this model. A second agent waits for the first to finish.`,\n tone: 'danger',\n };\n }\n\n if (runtime.parallel.capable === null) {\n return {\n verdict: 'unknown',\n effectiveConcurrency: null,\n configuredSlots: configured,\n overstated: false,\n headline: 'Batching not confirmed',\n detail: `${name} is running${\n configured === null ? '' : ` with ${configured} slots configured`\n }, but it has not reported whether it batches concurrent requests for this model. Until it does, this panel will not quote a concurrency it cannot stand behind.`,\n tone: 'unknown',\n };\n }\n\n if (configured === null) {\n return {\n verdict: 'batched',\n effectiveConcurrency: null,\n configuredSlots: null,\n overstated: false,\n headline: `${name} batches concurrent requests`,\n detail: `${name} batches, but reported no slot count, so the ceiling on concurrent agents is unknown.`,\n tone: 'unknown',\n };\n }\n\n return {\n verdict: 'batched',\n effectiveConcurrency: configured,\n configuredSlots: configured,\n overstated: false,\n headline: `${configured} agent${configured === 1 ? '' : 's'} in parallel`,\n detail: `${name} batches across ${configured} slot${\n configured === 1 ? '' : 's'\n } sharing one copy of the weights, so ${configured} agent${\n configured === 1 ? '' : 's'\n } can be mid-turn at once.`,\n tone: 'success',\n };\n}\n\n/**\n * The measured comparison, kept as data so the panel renders it as evidence\n * rather than as an assertion in prose. Source: docs/LOCAL-FLEET.md, four\n * concurrent requests against the same Qwen3.8 GGUF on this machine.\n */\nexport interface ConcurrencyEvidenceRow {\n runtime: string;\n /** Per-request completion times, in seconds, in the order they returned. */\n perRequestSeconds: readonly number[];\n wallSeconds: number;\n verdict: string;\n}\n\nexport const CONCURRENCY_EVIDENCE: readonly ConcurrencyEvidenceRow[] = [\n {\n runtime: 'Ollama',\n perRequestSeconds: [3.7, 7.6, 11.4, 15.2],\n wallSeconds: 15.2,\n verdict: 'staggered — the requests queued',\n },\n {\n runtime: 'llama-server',\n perRequestSeconds: [8.6, 8.9, 9.0, 9.0],\n wallSeconds: 9.1,\n verdict: 'together — the requests batched',\n },\n] as const;\n\n// ---------------------------------------------------------------------------\n// Utilisation\n// ---------------------------------------------------------------------------\n\nexport interface SlotUtilisation {\n busy: number | null;\n total: number | null;\n /** 0–100, or null when either half is unknown. */\n percent: number | null;\n /** The share in words, by the one percent rule (\"<1%\", \"99%\"); null when unknown. */\n percentText: string | null;\n /** Every slot is taken: the next turn waits. */\n saturated: boolean;\n /** One line, safe to render on its own. */\n label: string;\n}\n\n/**\n * Slot occupancy.\n *\n * `total` is the EFFECTIVE concurrency, not the configured slot count — on a\n * runtime that serializes, one busy slot out of four configured is full, and\n * drawing it as 25% would tell the operator there is room when there is none.\n */\nexport function slotUtilisation(busy: number | null, effectiveTotal: number | null): SlotUtilisation {\n if (busy === null || effectiveTotal === null || effectiveTotal <= 0) {\n return {\n busy,\n total: effectiveTotal,\n percent: null,\n percentText: null,\n saturated: false,\n label:\n busy === null\n ? 'Slot occupancy is not reported.'\n : `${busy} in flight; the number of slots is not known, so occupancy cannot be drawn.`,\n };\n }\n const clamped = Math.min(busy, effectiveTotal);\n const share = (clamped / effectiveTotal) * 100;\n const percent = Math.round(share);\n const saturated = busy >= effectiveTotal;\n return {\n busy,\n total: effectiveTotal,\n percent,\n percentText: percentText(share),\n saturated,\n label: saturated\n ? `All ${effectiveTotal} slot${effectiveTotal === 1 ? '' : 's'} busy — new turns wait for one to free up.`\n : `${busy} of ${effectiveTotal} slot${effectiveTotal === 1 ? '' : 's'} busy.`,\n };\n}\n\n/**\n * What the operator is actually waiting on.\n *\n * Saturation alone is not bad news — a fleet at full occupancy is a fleet\n * working. Saturation WITH a queue is the thing to say plainly, because from\n * the outside it is indistinguishable from the machine being slow.\n */\nexport interface FleetPressure {\n state: 'idle' | 'working' | 'saturated' | 'queued' | 'unknown';\n headline: string;\n detail: string;\n tone: 'success' | 'running' | 'warning' | 'neutral' | 'unknown';\n}\n\nexport function fleetPressure(\n fleet: FleetSnapshot | null,\n utilisation: SlotUtilisation,\n capacity: RuntimeCapacity,\n): FleetPressure {\n if (fleet === null) {\n return {\n state: 'unknown',\n headline: 'Fleet not reported',\n detail:\n 'No fleet snapshot answered, so what is in flight cannot be listed. That is a missing reading, not an empty fleet.',\n tone: 'unknown',\n };\n }\n\n const running = fleet.agents.filter((a) => a.state !== 'queued').length;\n const queued = fleet.queueDepth ?? fleet.agents.filter((a) => a.state === 'queued').length;\n\n if (queued > 0) {\n return {\n state: 'queued',\n headline: `${queued} waiting for a slot`,\n detail:\n capacity.verdict === 'serialized'\n ? `${running} turn${running === 1 ? '' : 's'} in flight and ${queued} queued. This runtime serializes, so the queue drains one at a time — the wait is a wait, not slow inference.`\n : `${running} turn${running === 1 ? '' : 's'} in flight and ${queued} queued behind ${\n utilisation.total === null ? 'the available slots' : `${utilisation.total} slots`\n }. Queued turns have not started; their wall time is queueing, not work.`,\n tone: 'warning',\n };\n }\n\n if (running === 0) {\n return {\n state: 'idle',\n headline: 'Nothing in flight',\n detail: 'No agent is mid-turn. The fleet is idle, not stuck.',\n tone: 'neutral',\n };\n }\n\n if (utilisation.saturated) {\n return {\n state: 'saturated',\n headline: `All slots busy · ${running} in flight`,\n detail: `Every slot is working and nothing is queued behind them. The next turn will wait, but none is waiting yet.`,\n tone: 'running',\n };\n }\n\n return {\n state: 'working',\n headline: `${running} in flight`,\n detail: utilisation.label,\n tone: 'running',\n };\n}\n\n// ---------------------------------------------------------------------------\n// Local-only impact\n// ---------------------------------------------------------------------------\n\nexport interface SeatLike {\n id: string;\n engine: string;\n label: string;\n}\n\nexport interface LocalOnlyImpact {\n /** Seats that become unreachable while the policy is on. */\n blocked: SeatLike[];\n /** Seats unaffected — the local ones. */\n allowed: SeatLike[];\n /** The named refusal per blocked engine, from the server when it sent one. */\n refusalFor: (engine: string) => string;\n /** One line summarising the blast radius against the CURRENT seat roster. */\n summary: string;\n}\n\nconst DEFAULT_REFUSAL = 'Refused: local-only is on and this engine is not local.';\n\n/**\n * What local-only would block, measured against the seats that actually exist\n * on this machine right now.\n *\n * Deliberately computed from the live roster rather than from a fixed list of\n * cloud engines: the sentence \"this would block 3 of your 5 seats\" is only\n * trustworthy if the 5 is the operator's own number.\n */\nexport function localOnlyImpact(\n policy: LocalOnlyPolicy | null,\n seats: readonly SeatLike[],\n): LocalOnlyImpact {\n const blocked = seats.filter((s) => s.engine !== 'local');\n const allowed = seats.filter((s) => s.engine === 'local');\n const byEngine = new Map<string, string>();\n for (const refusal of policy?.refuses ?? []) byEngine.set(refusal.engine, refusal.reason);\n\n const summary =\n seats.length === 0\n ? 'No seats are configured, so there is nothing for this policy to block yet.'\n : blocked.length === 0\n ? `Every one of your ${seats.length} seat${seats.length === 1 ? '' : 's'} is local, so this policy would block nothing today.`\n : `${blocked.length} of ${seats.length} seat${seats.length === 1 ? '' : 's'} would become unreachable; ${allowed.length} local seat${allowed.length === 1 ? '' : 's'} would keep working.`;\n\n return {\n blocked,\n allowed,\n refusalFor: (engine: string) => byEngine.get(engine) ?? DEFAULT_REFUSAL,\n summary,\n };\n}\n\n/** Where the policy came from, as a sentence an operator can act on. */\nexport function localOnlySourceNote(policy: LocalOnlyPolicy | null): string | null {\n if (policy === null) return null;\n // The policy's own sentence, when it wrote one. It knows things this client\n // does not — e.g. that an env var tried and failed to clear a persisted\n // local-only — and a template would erase exactly that.\n if (policy.detail !== null) return policy.detail;\n switch (policy.source) {\n case 'env':\n case 'config+env':\n return 'This policy is pinned by an environment variable on the server process, so it cannot be changed from here — restart the server without it to take the switch back.';\n case 'latch':\n return 'This process already observed a persisted local-only and has pinned it for its own lifetime, so turning it off takes effect for new dispatches but not for the seams that latched. Restart the server to clear it.';\n case 'config':\n return 'Stored in the hub config and applied on the next dispatch.';\n case 'off':\n return 'Not configured; cloud engines are reachable.';\n default:\n return 'The server did not say where this setting came from.';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Small formatters\n// ---------------------------------------------------------------------------\n\n/** \"4m 12s\" since an ISO instant, against a live clock. Absent stays absent. */\nexport function elapsedSince(iso: string | null, now: number): string {\n if (!iso) return UNKNOWN;\n const at = Date.parse(iso);\n if (Number.isNaN(at)) return UNKNOWN;\n return formatDuration(Math.max(0, now - at));\n}\n\n/** Uptime for the runtime header. Same rules, different words. */\nexport function formatUptime(startedAt: string | null, now: number): string {\n if (!startedAt) return UNKNOWN;\n const at = Date.parse(startedAt);\n if (Number.isNaN(at)) return UNKNOWN;\n return `up ${formatDuration(Math.max(0, now - at))}`;\n}\n\n/**\n * Context windows are quoted in powers of two everywhere the operator has\n * seen them (65536 is \"64k\"), so the divisor is 1024. Matching\n * `usage/local-model.ts`'s `formatContext` on purpose: two spellings of the\n * same number across two panels is how a UI starts to feel untrustworthy.\n */\nexport function formatContextTokens(tokens: number | null): string {\n if (tokens === null || !Number.isFinite(tokens)) return UNKNOWN;\n if (tokens >= 1024) return `${Math.round(tokens / 1024)}k`;\n return String(tokens);\n}\n\n/** Agent rows, ordered so the reader's first question is answered first. */\nexport function orderAgents(agents: readonly FleetAgent[]): FleetAgent[] {\n const rank: Record<FleetAgentState, number> = { running: 0, finishing: 1, queued: 2 };\n return [...agents].sort((a, b) => {\n const byState = rank[a.state] - rank[b.state];\n if (byState !== 0) return byState;\n // Longest-running first: the one most likely to be stuck is the one an\n // operator scanning this table is looking for.\n const at = a.startedAt ? Date.parse(a.startedAt) : Number.NaN;\n const bt = b.startedAt ? Date.parse(b.startedAt) : Number.NaN;\n if (Number.isNaN(at) && Number.isNaN(bt)) return a.id.localeCompare(b.id);\n if (Number.isNaN(at)) return 1;\n if (Number.isNaN(bt)) return -1;\n return at - bt;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fleet-model-IRbwfhkR.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/autonomy/fleet-model.ts"],"sourcesContent":["/**\n * routes/verse/autonomy/fleet-model.ts — every derivation the local-fleet\n * surfaces make, as pure functions. No React, no I/O, so the rules that decide\n * what the operator is told are unit-testable without a DOM.\n *\n * The one rule this file exists to enforce:\n *\n * **A concurrency number the runtime will not honour is a lie, and the UI\n * must print the truth instead of the configuration.**\n *\n * That is not a hypothetical. Measured on this machine (docs/LOCAL-FLEET.md),\n * four concurrent Qwen3.8 requests to Ollama returned at 3.7 / 7.6 / 11.4 /\n * 15.2s — a four-second stagger, which is a queue. The same weights on\n * llama-server returned at 8.6 / 8.9 / 9.0 / 9.0s: continuous batching across\n * four slots sharing one 27 GB copy. Ollama's log calls it a refusal\n * (`model architecture does not currently support parallel requests`) and\n * `OLLAMA_NUM_PARALLEL` does not move it.\n *\n * So `runtimeCapacity()` never reports `slotsTotal` as capacity unless the\n * runtime says it batches. When a runtime that serializes is configured with\n * four slots, the capacity is ONE and `overstated` is true, and the panel says\n * so in words. A queue that looks like slowness is the exact failure this\n * surface is here to prevent.\n *\n * The bodies these functions narrow come from routes that are landing in\n * parallel (see fleet-contract.ts), so everything is projected STRUCTURALLY\n * rather than cast: a field-name drift degrades to an honest \"unknown\" at\n * render time instead of throwing.\n */\nimport { UNKNOWN, formatDuration, percentText } from './format.js';\nimport type {\n FleetAgent,\n FleetAgentState,\n FleetSnapshot,\n LocalOnlyPolicy,\n LocalOnlyRefusal,\n LocalOnlySource,\n ServingRuntimeKind,\n ServingRuntimeParallelism,\n ServingRuntimeSnapshot,\n ServingRuntimeState,\n} from './fleet-contract.js';\n\n// ---------------------------------------------------------------------------\n// Structural narrowing\n// ---------------------------------------------------------------------------\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction str(source: Record<string, unknown>, key: string): string | null {\n const v = source[key];\n return typeof v === 'string' && v.length > 0 ? v : null;\n}\n\nfunction num(source: Record<string, unknown>, key: string): number | null {\n const v = source[key];\n return typeof v === 'number' && Number.isFinite(v) ? v : null;\n}\n\n/** A count that is meaningful at zero — so 0 stays 0 and absent stays null. */\nfunction count(source: Record<string, unknown>, key: string): number | null {\n const v = num(source, key);\n return v === null ? null : Math.max(0, Math.round(v));\n}\n\nfunction bool(source: Record<string, unknown>, key: string): boolean | null {\n const v = source[key];\n return typeof v === 'boolean' ? v : null;\n}\n\nfunction strings(source: Record<string, unknown>, key: string): string[] {\n const v = source[key];\n return Array.isArray(v) ? v.filter((entry): entry is string => typeof entry === 'string') : [];\n}\n\nconst RUNTIME_KINDS: readonly ServingRuntimeKind[] = ['llama-server', 'ollama', 'vllm', 'unknown'];\nconst RUNTIME_STATES: readonly ServingRuntimeState[] = [\n 'running',\n 'starting',\n 'stopping',\n 'stopped',\n 'unknown',\n];\nconst AGENT_STATES: readonly FleetAgentState[] = ['queued', 'running', 'finishing'];\nconst LOCAL_ONLY_SOURCES: readonly LocalOnlySource[] = [\n 'off',\n 'config',\n 'env',\n 'config+env',\n 'latch',\n 'unknown',\n];\n\nfunction oneOf<T extends string>(\n source: Record<string, unknown>,\n key: string,\n allowed: readonly T[],\n fallback: T,\n): T {\n const v = source[key];\n return typeof v === 'string' && (allowed as readonly string[]).includes(v) ? (v as T) : fallback;\n}\n\nfunction projectParallelism(raw: unknown): ServingRuntimeParallelism {\n const r = record(raw);\n if (!r) return { capable: null, refusal: null, slots: null };\n return { capable: bool(r, 'capable'), refusal: str(r, 'refusal'), slots: count(r, 'slots') };\n}\n\n/**\n * Owner R's `LlamaRuntimeSnapshot` (`src/core/local-runtime/llama/types.ts`)\n * spells the same facts differently, and the route that serves it is not\n * written yet — so rather than betting on one envelope, this reads both.\n *\n * The mapping, with the two judgement calls stated:\n *\n * - `state: 'up' | 'down' | 'loading' | 'unknown'` → running / stopped /\n * starting / unknown.\n * - `contextPerSlot` wins over `contextTotal`. `-c 65536 --parallel 4` gives\n * each slot 16k, and the number an agent's turn actually runs in is the\n * per-slot one. Quoting the total would overstate every agent's context\n * fourfold — the same class of error as quoting slots as concurrency.\n * - `managed` → `supervised`. R is explicit that an unmanaged server may be\n * adopted but is \"never killed on a guess\", which is exactly the condition\n * under which this panel must not offer a stop button.\n */\nconst LLAMA_STATE: Readonly<Record<string, ServingRuntimeState>> = {\n up: 'running',\n down: 'stopped',\n loading: 'starting',\n unknown: 'unknown',\n};\n\nfunction projectLlamaShape(r: Record<string, unknown>): ServingRuntimeSnapshot | null {\n const rawState = r['state'];\n if (typeof rawState !== 'string' || !(rawState in LLAMA_STATE)) return null;\n const slots = record(r['slots']);\n const host = str(r, 'host');\n const port = count(r, 'port');\n const configured = slots ? count(slots, 'configured') : null;\n const busy = slots ? count(slots, 'busy') : null;\n // `slots.source` is R's own trust marker: 'unknown' means \"do not trust a\n // number\", so a slot count carrying it is dropped rather than quoted.\n const slotSource = slots ? str(slots, 'source') : null;\n const trusted = slotSource !== null && slotSource !== 'unknown';\n const total = trusted ? configured : null;\n\n const refusal = str(r, 'parallelRefusal');\n const identity = str(r, 'runtimeKind');\n const contextPerSlot = count(r, 'contextPerSlot');\n const contextTotal = count(r, 'contextTotal');\n\n return {\n // IDENTIFIED where the producer says so. A base URL pointed at Ollama's\n // /v1 answers /v1/models — enough to keep the engine routable — while\n // 404ing /health and /props, and asserting 'llama-server' for it would\n // hide the one fact that matters: that runtime serialises this model.\n kind: identity === 'ollama' ? 'ollama' : identity === 'unknown' ? 'unknown' : 'llama-server',\n state: LLAMA_STATE[rawState] ?? 'unknown',\n endpoint: host !== null && port !== null ? `${host}:${port}` : (str(r, 'origin') ?? null),\n // A digest is not a name. `modelName` falls back to `model_path`'s basename\n // at the producer, which for a blob out of Ollama's content-addressed store\n // is `sha256-…`: it identifies the machine's storage layout and names\n // nothing. Null renders as \"unknown\", which is the honest answer.\n model: humaneModelName(str(r, 'modelName')) ?? humaneModelName(str(r, 'model')),\n slotsTotal: total,\n slotsBusy: trusted ? busy : null,\n // PER-SLOT, or nothing. `-c 65536 --parallel 4` gives each agent 16k, so\n // the `-c` total is usable only divided by a slot count we trust.\n contextTokens:\n contextPerSlot ??\n (total !== null && total > 0 && contextTotal !== null && contextTotal > 0\n ? Math.floor(contextTotal / total)\n : null),\n startedAt: str(r, 'startedAt'),\n parallel: {\n // llama-server's slots ARE its continuous-batching slots: a count read\n // back off the live server (`/props.total_slots`, `/slots`) is a count\n // of requests it will run at the same time, which is what the\n // measurement in docs/LOCAL-FLEET.md showed. An untrusted or absent\n // reading claims nothing — `null`, not `true`.\n capable: refusal !== null ? false : trusted && total !== null ? true : null,\n // The runtime's own words when it refuses, carried verbatim as evidence.\n refusal,\n slots: total,\n },\n reason: str(r, 'lastError'),\n supervised: bool(r, 'managed') ?? false,\n sampledAt: str(r, 'checkedAt'),\n };\n}\n\n/** A displayable model name, or null for an absent or digest-shaped one. */\nfunction humaneModelName(raw: string | null): string | null {\n if (raw === null || raw.length === 0) return null;\n const base = raw.split('/').pop() ?? raw;\n if (base.length === 0) return null;\n if (/^sha256[-:][0-9a-f]{16,}$/i.test(base)) return null;\n return base;\n}\n\nexport function projectServingRuntime(raw: unknown): ServingRuntimeSnapshot | null {\n const r = record(raw);\n if (!r) return null;\n // A body already in this surface's own shape wins; otherwise try owner R's.\n if (!('kind' in r) && 'slots' in r && 'state' in r) {\n const llama = projectLlamaShape(r);\n if (llama !== null) return llama;\n }\n return {\n kind: oneOf(r, 'kind', RUNTIME_KINDS, 'unknown'),\n state: oneOf(r, 'state', RUNTIME_STATES, 'unknown'),\n endpoint: str(r, 'endpoint'),\n model: str(r, 'model'),\n slotsTotal: count(r, 'slotsTotal'),\n slotsBusy: count(r, 'slotsBusy'),\n contextTokens: count(r, 'contextTokens'),\n startedAt: str(r, 'startedAt'),\n parallel: projectParallelism(r['parallel']),\n reason: str(r, 'reason'),\n // A runtime this server cannot supervise must not be offered buttons, so\n // an unreported flag reads as NOT supervised. The conservative default is\n // \"no controls\" rather than controls that 404 on the first click.\n supervised: bool(r, 'supervised') ?? false,\n sampledAt: str(r, 'sampledAt'),\n };\n}\n\nfunction projectAgent(raw: unknown): FleetAgent | null {\n const r = record(raw);\n if (!r) return null;\n const id = str(r, 'id');\n if (id === null) return null;\n return {\n id,\n task: str(r, 'task'),\n repo: str(r, 'repo'),\n engine: str(r, 'engine'),\n model: str(r, 'model'),\n state: oneOf(r, 'state', AGENT_STATES, 'running'),\n startedAt: str(r, 'startedAt'),\n slot: count(r, 'slot'),\n };\n}\n\nexport function projectFleet(raw: unknown): FleetSnapshot | null {\n const r = record(raw);\n if (!r) return null;\n const rawAgents = Array.isArray(r['agents']) ? (r['agents'] as unknown[]) : [];\n return {\n agents: rawAgents.map(projectAgent).filter((a): a is FleetAgent => a !== null),\n queueDepth: count(r, 'queueDepth'),\n slotsTotal: count(r, 'slotsTotal'),\n slotsBusy: count(r, 'slotsBusy'),\n notes: strings(r, 'notes'),\n sampledAt: str(r, 'sampledAt'),\n };\n}\n\nfunction projectRefusal(raw: unknown): LocalOnlyRefusal | null {\n const r = record(raw);\n if (!r) return null;\n const engine = str(r, 'engine');\n if (engine === null) return null;\n return { engine, reason: str(r, 'reason') ?? 'The dispatcher refuses this engine while local-only is on.' };\n}\n\n/**\n * `source` is carried through as the resolver's own value — it is no longer\n * flattened onto a smaller union here.\n *\n * The flattening this replaced mapped `'latch'` onto `'config'`, which tells\n * an operator the mode is stored in the config and editable when in fact the\n * process has pinned it for its own lifetime. Only the separate `mutable`\n * flag kept that from reaching the screen as a working switch that silently\n * does nothing. An unrecognised value lands on `'unknown'`, which makes no\n * claim at all.\n */\n/** Sources that pin the mode to this server process, whatever the config says. */\nconst PINNED_SOURCES: ReadonlySet<LocalOnlySource> = new Set<LocalOnlySource>([\n 'env',\n 'config+env',\n 'latch',\n 'unknown',\n]);\n\nexport function projectLocalOnly(raw: unknown): LocalOnlyPolicy | null {\n const r = record(raw);\n if (!r) return null;\n const rawRefusals = Array.isArray(r['refuses']) ? (r['refuses'] as unknown[]) : [];\n const source = oneOf(r, 'source', LOCAL_ONLY_SOURCES, 'unknown');\n return {\n // An unreported `enabled` is NOT \"on\": claiming cloud is unreachable when\n // the server never said so is the one error this policy cannot afford.\n enabled: bool(r, 'enabled') ?? false,\n source,\n refuses: rawRefusals.map(projectRefusal).filter((x): x is LocalOnlyRefusal => x !== null),\n // A policy pinned by the server's own process — an env var, or a latch\n // this process already took — is never editable from here, whatever the\n // body claims: a write would be silently overridden on the next read.\n // `'unknown'` is treated as pinned too, because offering a switch for a\n // policy this client cannot identify is the more expensive mistake.\n mutable: PINNED_SOURCES.has(source) ? false : (bool(r, 'mutable') ?? false),\n detail: str(r, 'detail'),\n sampledAt: str(r, 'sampledAt') ?? str(r, 'checkedAt'),\n };\n}\n\n// ---------------------------------------------------------------------------\n// The capacity verdict — the point of this whole file\n// ---------------------------------------------------------------------------\n\nexport type CapacityVerdict = 'batched' | 'serialized' | 'unknown' | 'offline';\n\nexport interface RuntimeCapacity {\n verdict: CapacityVerdict;\n /**\n * How many agents can genuinely be in flight at once. `null` means it\n * cannot be known from this reading — which is NOT the same as 0 and must\n * never render as a number.\n */\n effectiveConcurrency: number | null;\n /** What the runtime was configured with, honoured or not. */\n configuredSlots: number | null;\n /**\n * True when the configured slot count is larger than what the runtime will\n * actually run in parallel. While this is true, the configured number must\n * not be presented as capacity anywhere on the screen.\n */\n overstated: boolean;\n headline: string;\n detail: string;\n tone: 'success' | 'warning' | 'danger' | 'neutral' | 'unknown';\n}\n\n/** Human name for a runtime, used in sentences. */\nexport function runtimeLabel(kind: ServingRuntimeKind): string {\n switch (kind) {\n case 'llama-server':\n return 'llama-server';\n case 'ollama':\n return 'Ollama';\n case 'vllm':\n return 'vLLM';\n default:\n return 'the serving runtime';\n }\n}\n\nexport function runtimeCapacity(runtime: ServingRuntimeSnapshot | null): RuntimeCapacity {\n if (runtime === null) {\n return {\n verdict: 'unknown',\n effectiveConcurrency: null,\n configuredSlots: null,\n overstated: false,\n headline: 'No serving runtime reported',\n detail:\n 'Nothing answered for the serving runtime, so how many agents can run at once is unknown. That is a missing reading, not an idle machine.',\n tone: 'unknown',\n };\n }\n\n const configured = runtime.slotsTotal ?? runtime.parallel.slots;\n const name = runtimeLabel(runtime.kind);\n\n if (runtime.state !== 'running') {\n return {\n verdict: 'offline',\n effectiveConcurrency: null,\n configuredSlots: configured,\n overstated: false,\n headline: runtime.state === 'stopped' ? 'Not serving' : `Serving runtime is ${runtime.state}`,\n detail:\n runtime.state === 'stopped'\n ? `${name} is not running, so no local agent can take a turn. Start it before dispatching a fleet.`\n : `${name} is ${runtime.state}; capacity is not settled until it reports running.`,\n tone: runtime.state === 'stopped' ? 'neutral' : 'warning',\n };\n }\n\n if (runtime.parallel.capable === false) {\n // The measured case. The configured number is real — it is simply not a\n // concurrency, and printing it as one is the failure mode this exists for.\n const overstated = configured !== null && configured > 1;\n return {\n verdict: 'serialized',\n effectiveConcurrency: 1,\n configuredSlots: configured,\n overstated,\n headline: `${name} serializes requests`,\n detail: overstated\n ? `${name} has ${configured} slots configured but runs one request at a time for this model, so the real capacity is one agent. The other ${configured - 1} would queue, and a queue looks like slowness rather than a wait.`\n : `${name} runs one request at a time for this model. A second agent waits for the first to finish.`,\n tone: 'danger',\n };\n }\n\n if (runtime.parallel.capable === null) {\n return {\n verdict: 'unknown',\n effectiveConcurrency: null,\n configuredSlots: configured,\n overstated: false,\n headline: 'Batching not confirmed',\n detail: `${name} is running${\n configured === null ? '' : ` with ${configured} slots configured`\n }, but it has not reported whether it batches concurrent requests for this model. Until it does, this panel will not quote a concurrency it cannot stand behind.`,\n tone: 'unknown',\n };\n }\n\n if (configured === null) {\n return {\n verdict: 'batched',\n effectiveConcurrency: null,\n configuredSlots: null,\n overstated: false,\n headline: `${name} batches concurrent requests`,\n detail: `${name} batches, but reported no slot count, so the ceiling on concurrent agents is unknown.`,\n tone: 'unknown',\n };\n }\n\n return {\n verdict: 'batched',\n effectiveConcurrency: configured,\n configuredSlots: configured,\n overstated: false,\n headline: `${configured} agent${configured === 1 ? '' : 's'} in parallel`,\n detail: `${name} batches across ${configured} slot${\n configured === 1 ? '' : 's'\n } sharing one copy of the weights, so ${configured} agent${\n configured === 1 ? '' : 's'\n } can be mid-turn at once.`,\n tone: 'success',\n };\n}\n\n/**\n * The measured comparison, kept as data so the panel renders it as evidence\n * rather than as an assertion in prose. Source: docs/LOCAL-FLEET.md, four\n * concurrent requests against the same Qwen3.8 GGUF on this machine.\n */\nexport interface ConcurrencyEvidenceRow {\n runtime: string;\n /** Per-request completion times, in seconds, in the order they returned. */\n perRequestSeconds: readonly number[];\n wallSeconds: number;\n verdict: string;\n}\n\nexport const CONCURRENCY_EVIDENCE: readonly ConcurrencyEvidenceRow[] = [\n {\n runtime: 'Ollama',\n perRequestSeconds: [3.7, 7.6, 11.4, 15.2],\n wallSeconds: 15.2,\n verdict: 'staggered — the requests queued',\n },\n {\n runtime: 'llama-server',\n perRequestSeconds: [8.6, 8.9, 9.0, 9.0],\n wallSeconds: 9.1,\n verdict: 'together — the requests batched',\n },\n] as const;\n\n// ---------------------------------------------------------------------------\n// Utilisation\n// ---------------------------------------------------------------------------\n\nexport interface SlotUtilisation {\n busy: number | null;\n total: number | null;\n /** 0–100, or null when either half is unknown. */\n percent: number | null;\n /** The share in words, by the one percent rule (\"<1%\", \"99%\"); null when unknown. */\n percentText: string | null;\n /** Every slot is taken: the next turn waits. */\n saturated: boolean;\n /** One line, safe to render on its own. */\n label: string;\n}\n\n/**\n * Slot occupancy.\n *\n * `total` is the EFFECTIVE concurrency, not the configured slot count — on a\n * runtime that serializes, one busy slot out of four configured is full, and\n * drawing it as 25% would tell the operator there is room when there is none.\n */\nexport function slotUtilisation(busy: number | null, effectiveTotal: number | null): SlotUtilisation {\n if (busy === null || effectiveTotal === null || effectiveTotal <= 0) {\n return {\n busy,\n total: effectiveTotal,\n percent: null,\n percentText: null,\n saturated: false,\n label:\n busy === null\n ? 'Slot occupancy is not reported.'\n : `${busy} in flight; the number of slots is not known, so occupancy cannot be drawn.`,\n };\n }\n const clamped = Math.min(busy, effectiveTotal);\n const share = (clamped / effectiveTotal) * 100;\n const percent = Math.round(share);\n const saturated = busy >= effectiveTotal;\n return {\n busy,\n total: effectiveTotal,\n percent,\n percentText: percentText(share),\n saturated,\n label: saturated\n ? `All ${effectiveTotal} slot${effectiveTotal === 1 ? '' : 's'} busy — new turns wait for one to free up.`\n : `${busy} of ${effectiveTotal} slot${effectiveTotal === 1 ? '' : 's'} busy.`,\n };\n}\n\n/**\n * What the operator is actually waiting on.\n *\n * Saturation alone is not bad news — a fleet at full occupancy is a fleet\n * working. Saturation WITH a queue is the thing to say plainly, because from\n * the outside it is indistinguishable from the machine being slow.\n */\nexport interface FleetPressure {\n state: 'idle' | 'working' | 'saturated' | 'queued' | 'unknown';\n headline: string;\n detail: string;\n tone: 'success' | 'running' | 'warning' | 'neutral' | 'unknown';\n}\n\nexport function fleetPressure(\n fleet: FleetSnapshot | null,\n utilisation: SlotUtilisation,\n capacity: RuntimeCapacity,\n): FleetPressure {\n if (fleet === null) {\n return {\n state: 'unknown',\n headline: 'Fleet not reported',\n detail:\n 'No fleet snapshot answered, so what is in flight cannot be listed. That is a missing reading, not an empty fleet.',\n tone: 'unknown',\n };\n }\n\n const running = fleet.agents.filter((a) => a.state !== 'queued').length;\n const queued = fleet.queueDepth ?? fleet.agents.filter((a) => a.state === 'queued').length;\n\n if (queued > 0) {\n return {\n state: 'queued',\n headline: `${queued} waiting for a slot`,\n detail:\n capacity.verdict === 'serialized'\n ? `${running} turn${running === 1 ? '' : 's'} in flight and ${queued} queued. This runtime serializes, so the queue drains one at a time — the wait is a wait, not slow inference.`\n : `${running} turn${running === 1 ? '' : 's'} in flight and ${queued} queued behind ${\n utilisation.total === null ? 'the available slots' : `${utilisation.total} slots`\n }. Queued turns have not started; their wall time is queueing, not work.`,\n tone: 'warning',\n };\n }\n\n if (running === 0) {\n return {\n state: 'idle',\n headline: 'Nothing in flight',\n detail: 'No agent is mid-turn. The fleet is idle, not stuck.',\n tone: 'neutral',\n };\n }\n\n if (utilisation.saturated) {\n return {\n state: 'saturated',\n headline: `All slots busy · ${running} in flight`,\n detail: `Every slot is working and nothing is queued behind them. The next turn will wait, but none is waiting yet.`,\n tone: 'running',\n };\n }\n\n return {\n state: 'working',\n headline: `${running} in flight`,\n detail: utilisation.label,\n tone: 'running',\n };\n}\n\n// ---------------------------------------------------------------------------\n// Local-only impact\n// ---------------------------------------------------------------------------\n\nexport interface SeatLike {\n id: string;\n engine: string;\n label: string;\n}\n\nexport interface LocalOnlyImpact {\n /** Seats that become unreachable while the policy is on. */\n blocked: SeatLike[];\n /** Seats unaffected — the local ones. */\n allowed: SeatLike[];\n /** The named refusal per blocked engine, from the server when it sent one. */\n refusalFor: (engine: string) => string;\n /** One line summarising the blast radius against the CURRENT seat roster. */\n summary: string;\n}\n\nconst DEFAULT_REFUSAL = 'Refused: local-only is on and this engine is not local.';\n\n/**\n * What local-only would block, measured against the seats that actually exist\n * on this machine right now.\n *\n * Deliberately computed from the live roster rather than from a fixed list of\n * cloud engines: the sentence \"this would block 3 of your 5 seats\" is only\n * trustworthy if the 5 is the operator's own number.\n */\nexport function localOnlyImpact(\n policy: LocalOnlyPolicy | null,\n seats: readonly SeatLike[],\n): LocalOnlyImpact {\n const blocked = seats.filter((s) => s.engine !== 'local');\n const allowed = seats.filter((s) => s.engine === 'local');\n const byEngine = new Map<string, string>();\n for (const refusal of policy?.refuses ?? []) byEngine.set(refusal.engine, refusal.reason);\n\n const summary =\n seats.length === 0\n ? 'No seats are configured, so there is nothing for this policy to block yet.'\n : blocked.length === 0\n ? `Every one of your ${seats.length} seat${seats.length === 1 ? '' : 's'} is local, so this policy would block nothing today.`\n : `${blocked.length} of ${seats.length} seat${seats.length === 1 ? '' : 's'} would become unreachable; ${allowed.length} local seat${allowed.length === 1 ? '' : 's'} would keep working.`;\n\n return {\n blocked,\n allowed,\n refusalFor: (engine: string) => byEngine.get(engine) ?? DEFAULT_REFUSAL,\n summary,\n };\n}\n\n/** Where the policy came from, as a sentence an operator can act on. */\nexport function localOnlySourceNote(policy: LocalOnlyPolicy | null): string | null {\n if (policy === null) return null;\n // The policy's own sentence, when it wrote one. It knows things this client\n // does not — e.g. that an env var tried and failed to clear a persisted\n // local-only — and a template would erase exactly that.\n if (policy.detail !== null) return policy.detail;\n switch (policy.source) {\n case 'env':\n case 'config+env':\n return 'This policy is pinned by an environment variable on the server process, so it cannot be changed from here — restart the server without it to take the switch back.';\n case 'latch':\n return 'This process already observed a persisted local-only and has pinned it for its own lifetime, so turning it off takes effect for new dispatches but not for the seams that latched. Restart the server to clear it.';\n case 'config':\n return 'Stored in the hub config and applied on the next dispatch.';\n case 'off':\n return 'Not configured; cloud engines are reachable.';\n default:\n return 'The server did not say where this setting came from.';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Small formatters\n// ---------------------------------------------------------------------------\n\n/** \"4m 12s\" since an ISO instant, against a live clock. Absent stays absent. */\nexport function elapsedSince(iso: string | null, now: number): string {\n if (!iso) return UNKNOWN;\n const at = Date.parse(iso);\n if (Number.isNaN(at)) return UNKNOWN;\n return formatDuration(Math.max(0, now - at));\n}\n\n/** Uptime for the runtime header. Same rules, different words. */\nexport function formatUptime(startedAt: string | null, now: number): string {\n if (!startedAt) return UNKNOWN;\n const at = Date.parse(startedAt);\n if (Number.isNaN(at)) return UNKNOWN;\n return `up ${formatDuration(Math.max(0, now - at))}`;\n}\n\n/**\n * Context windows are quoted in powers of two everywhere the operator has\n * seen them (65536 is \"64k\"), so the divisor is 1024. Matching\n * `usage/local-model.ts`'s `formatContext` on purpose: two spellings of the\n * same number across two panels is how a UI starts to feel untrustworthy.\n */\nexport function formatContextTokens(tokens: number | null): string {\n if (tokens === null || !Number.isFinite(tokens)) return UNKNOWN;\n if (tokens >= 1024) return `${Math.round(tokens / 1024)}k`;\n return String(tokens);\n}\n\n/** Agent rows, ordered so the reader's first question is answered first. */\nexport function orderAgents(agents: readonly FleetAgent[]): FleetAgent[] {\n const rank: Record<FleetAgentState, number> = { running: 0, finishing: 1, queued: 2 };\n return [...agents].sort((a, b) => {\n const byState = rank[a.state] - rank[b.state];\n if (byState !== 0) return byState;\n // Longest-running first: the one most likely to be stuck is the one an\n // operator scanning this table is looking for.\n const at = a.startedAt ? Date.parse(a.startedAt) : Number.NaN;\n const bt = b.startedAt ? Date.parse(b.startedAt) : Number.NaN;\n if (Number.isNaN(at) && Number.isNaN(bt)) return a.id.localeCompare(b.id);\n if (Number.isNaN(at)) return 1;\n if (Number.isNaN(bt)) return -1;\n return at - bt;\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as e,s as t}from"./index-DbDw9UAs.js";import{D as n,F as r,U as i,c as a,l as o,m as s,o as c}from"./global-BkNl4e6E.js";import{n as l,t as u}from"./control-queries-Dv-h1Clu.js";import{d,l as f,u as p}from"./fleet-model-IRbwfhkR.js";var m=t(e(),1),h=`verse-runtime`,g=`verse-fleet`,_=`verse-local-only`;function v(e,t){return t instanceof c?t.status===404?`This server does not expose ${e}, so this panel has no source. It is a missing route, not an idle machine.`:`${e} answered HTTP ${t.status}.`:`${e} could not be reached.`}function y(e,t){return async n=>{try{let r=t(await a(e,n));return r===null?{value:null,available:!0,reason:`${e} answered in a shape this client does not recognise, so nothing is shown rather than guessed.`}:{value:r,available:!0,reason:null}}catch(t){if(t instanceof c&&t.status===401||t instanceof DOMException&&t.name===`AbortError`)throw t;return{value:null,available:!1,reason:v(e,t)}}}}var b={key:h,fetch:y(`/api/verse/runtime`,d)},x={key:g,fetch:y(`/api/verse/fleet`,f)},S={key:_,fetch:y(`/api/verse/local-only`,p)};async function C(e,t){let i=n();if(!i)throw new l;let a=await o(e,t,i);return r(),a}async function w(e){let t=await C(`/api/verse/runtime`,{action:e});return i(h),i(g),i(u),t}async function T(e){let t=await C(`/api/verse/local-only`,{enabled:e});return i(_),i(u),t}var E=4e3;function D(e=E){let t=s(b),n=s(x);(0,m.useEffect)(()=>{let r=null,i=()=>{t(),n()},a=()=>{r===null&&(r=setInterval(i,e))},o=()=>{r!==null&&(clearInterval(r),r=null)},s=()=>{if(document.visibilityState===`hidden`){o();return}i(),a()};return document.visibilityState!==`hidden`&&a(),document.addEventListener(`visibilitychange`,s),()=>{o(),document.removeEventListener(`visibilitychange`,s)}},[e,t,n])}export{b as a,w as i,x as n,T as o,S as r,D as s,g as t};
|
|
2
|
+
//# sourceMappingURL=fleet-queries-7FVNByX1.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-queries-
|
|
1
|
+
{"version":3,"file":"fleet-queries-7FVNByX1.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/autonomy/fleet-queries.ts"],"sourcesContent":["/**\n * routes/verse/autonomy/fleet-queries.ts — the reads and writes behind the\n * local-fleet surfaces: `GET/POST /api/verse/runtime`,\n * `GET/POST /api/verse/local-only`, and `GET /api/verse/fleet`.\n *\n * Same contract as control-queries.ts — reads are QueryDefs consumed through\n * useQuery, writes go through the mutation-token gate and invalidate exactly\n * the keys they affect — with two differences that both come from these three\n * routes landing in parallel with this surface (owners R, L, F):\n *\n * 1. **The reads do not throw on absence.** A server without these routes\n * answers 404, and a 404 means \"this panel has no source yet\", not\n * \"Autonomy is broken\". The panels render a designed degraded state from\n * `OptionalFleetRead.reason`. 401 still propagates: an expired read\n * session is an unauthorized state for the whole surface.\n * 2. **The bodies are narrowed structurally, not cast.** `fleet-model.ts`\n * projects every field, so a name drift between the route and this client\n * degrades to \"unknown\" rather than throwing at render time.\n *\n * Polling: the fleet and the runtime are the two things on this screen that\n * change while nobody clicks anything, and neither has an SSE event yet, so\n * `useFleetPolling` re-reads them on an interval — pausing while the tab is\n * hidden, because a background tab polling a runtime probe is pure waste.\n */\nimport { useEffect } from 'react';\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 { VERSE_CONTROL_KEY, VerseControlLockedError } from './control-queries.js';\nimport type {\n FleetSnapshot,\n LocalOnlyPolicy,\n LocalOnlyUpdateResult,\n OptionalFleetRead,\n RuntimeAction,\n RuntimeActionResult,\n ServingRuntimeSnapshot,\n} from './fleet-contract.js';\nimport { projectFleet, projectLocalOnly, projectServingRuntime } from './fleet-model.js';\n\nexport const VERSE_RUNTIME_KEY = 'verse-runtime';\nexport const VERSE_FLEET_KEY = 'verse-fleet';\nexport const VERSE_LOCAL_ONLY_KEY = 'verse-local-only';\n\nfunction describeAbsence(path: string, err: unknown): string {\n if (err instanceof ApiError) {\n if (err.status === 404) {\n return `This server does not expose ${path}, so this panel has no source. It is a missing route, not an idle machine.`;\n }\n return `${path} answered HTTP ${err.status}.`;\n }\n return `${path} could not be reached.`;\n}\n\n/**\n * One optional read, projected. `value` is null both when the route was absent\n * and when it answered something this client could not narrow — `available`\n * and `reason` are what tell the two apart.\n */\nfunction optionalRead<T>(\n path: string,\n project: (raw: unknown) => T | null,\n): (signal?: AbortSignal) => Promise<OptionalFleetRead<T>> {\n return async (signal) => {\n try {\n const raw = await apiGet<unknown>(path, signal);\n const value = project(raw);\n if (value === null) {\n return {\n value: null,\n available: true,\n reason: `${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(path, err) };\n }\n };\n}\n\n/** GET /api/verse/runtime — is the serving runtime up, and what can it do. */\nexport const servingRuntimeQuery: QueryDef<OptionalFleetRead<ServingRuntimeSnapshot>> = {\n key: VERSE_RUNTIME_KEY,\n fetch: optionalRead('/api/verse/runtime', projectServingRuntime),\n};\n\n/** GET /api/verse/fleet — the agents in flight right now. */\nexport const fleetQuery: QueryDef<OptionalFleetRead<FleetSnapshot>> = {\n key: VERSE_FLEET_KEY,\n fetch: optionalRead('/api/verse/fleet', projectFleet),\n};\n\n/** GET /api/verse/local-only — the refusal policy and what it refuses. */\nexport const localOnlyQuery: QueryDef<OptionalFleetRead<LocalOnlyPolicy>> = {\n key: VERSE_LOCAL_ONLY_KEY,\n fetch: optionalRead('/api/verse/local-only', projectLocalOnly),\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\n/**\n * Start / stop / restart the serving runtime.\n *\n * Every one of these changes what the fleet can do, so all three fleet keys\n * and the control snapshot are invalidated together — a restarted runtime\n * with a stale slot count beside it is exactly the disagreement this surface\n * exists to prevent.\n */\nexport async function runRuntimeAction(action: RuntimeAction): Promise<RuntimeActionResult> {\n const result = await post<RuntimeActionResult>('/api/verse/runtime', { action });\n invalidate(VERSE_RUNTIME_KEY);\n invalidate(VERSE_FLEET_KEY);\n invalidate(VERSE_CONTROL_KEY);\n return result;\n}\n\n/** Turn the local-only refusal on or off. */\nexport async function setLocalOnly(enabled: boolean): Promise<LocalOnlyUpdateResult> {\n const result = await post<LocalOnlyUpdateResult>('/api/verse/local-only', { enabled });\n invalidate(VERSE_LOCAL_ONLY_KEY);\n invalidate(VERSE_CONTROL_KEY);\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Polling\n// ---------------------------------------------------------------------------\n\n/** Fast enough that \"is it working\" is answered live; slow enough to be free. */\nexport const FLEET_POLL_MS = 4000;\n\n/**\n * Keep the runtime and fleet reads live while this section is mounted and the\n * tab is visible.\n *\n * Both routes probe a local process, so this is cheap — but only while someone\n * is looking. `visibilitychange` stops the timer when the tab is hidden and\n * takes one immediate reading when it comes back, so a fleet view returned to\n * after lunch is current on the first frame rather than up to an interval old.\n */\nexport function useFleetPolling(intervalMs: number = FLEET_POLL_MS): void {\n const refetchRuntime = useRefetch(servingRuntimeQuery);\n const refetchFleet = useRefetch(fleetQuery);\n\n useEffect(() => {\n let timer: ReturnType<typeof setInterval> | null = null;\n\n const tick = (): void => {\n refetchRuntime();\n refetchFleet();\n };\n\n const start = (): void => {\n if (timer !== null) return;\n timer = setInterval(tick, intervalMs);\n };\n\n const stop = (): void => {\n if (timer === null) return;\n clearInterval(timer);\n timer = null;\n };\n\n const onVisibility = (): void => {\n if (document.visibilityState === 'hidden') {\n stop();\n return;\n }\n tick();\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 }, [intervalMs, refetchRuntime, refetchFleet]);\n}\n"],"mappings":"iQA0Ca,EAAoB,gBACpB,EAAkB,cAClB,EAAuB,mBAEpC,SAAS,EAAgB,EAAc,EAAsB,CAO3D,OANI,aAAe,EACb,EAAI,SAAW,IACV,+BAA+B,EAAK,4EAEtC,GAAG,EAAK,iBAAiB,EAAI,OAAO,GAEtC,GAAG,EAAK,uBACjB,CAOA,SAAS,EACP,EACA,EACyD,CACzD,OAAO,KAAO,IAAW,CACvB,GAAI,CAEF,IAAM,EAAQ,EAAQ,MADJ,EAAgB,EAAM,CAAM,CACrB,EAQzB,OAPI,IAAU,KACL,CACL,MAAO,KACP,UAAW,GACX,OAAQ,GAAG,EAAK,8FAClB,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,EAAM,CAAG,CAAE,CAC7E,CACF,CACF,CAGA,IAAa,EAA2E,CACtF,IAAK,EACL,MAAO,EAAa,qBAAsB,CAAqB,CACjE,EAGa,EAAyD,CACpE,IAAK,EACL,MAAO,EAAa,mBAAoB,CAAY,CACtD,EAGa,EAA+D,CAC1E,IAAK,EACL,MAAO,EAAa,wBAAyB,CAAgB,CAC/D,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,CAUA,eAAsB,EAAiB,EAAqD,CAC1F,IAAM,EAAS,MAAM,EAA0B,qBAAsB,CAAE,QAAO,CAAC,EAI/E,OAHA,EAAW,CAAiB,EAC5B,EAAW,CAAe,EAC1B,EAAW,CAAiB,EACrB,CACT,CAGA,eAAsB,EAAa,EAAkD,CACnF,IAAM,EAAS,MAAM,EAA4B,wBAAyB,CAAE,SAAQ,CAAC,EAGrF,OAFA,EAAW,CAAoB,EAC/B,EAAW,CAAiB,EACrB,CACT,CAOA,IAAa,EAAgB,IAW7B,SAAgB,EAAgB,EAAqB,EAAqB,CACxE,IAAM,EAAiB,EAAW,CAAmB,EAC/C,EAAe,EAAW,CAAU,GAE1C,EAAA,EAAA,UAAA,KAAgB,CACd,IAAI,EAA+C,KAE7C,MAAmB,CACvB,EAAe,EACf,EAAa,CACf,EAEM,MAAoB,CACpB,IAAU,OACd,EAAQ,YAAY,EAAM,CAAU,EACtC,EAEM,MAAmB,CACnB,IAAU,OACd,cAAc,CAAK,EACnB,EAAQ,KACV,EAEM,MAA2B,CAC/B,GAAI,SAAS,kBAAoB,SAAU,CACzC,EAAK,EACL,MACF,CACA,EAAK,EACL,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,EAAY,EAAgB,CAAY,CAAC,CAC/C"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as e,s as t}from"./index-DbDw9UAs.js";var n=t(e(),1),r=[`a[href]`,`button:not([disabled])`,`input:not([disabled])`,`select:not([disabled])`,`textarea:not([disabled])`,`[tabindex]:not([tabindex="-1"])`].join(`,`);function i(e){return Array.from(e.querySelectorAll(r)).filter(e=>e.offsetParent!==null)}function a({open:e,containerRef:t,onClose:r,initialFocusRef:a}){(0,n.useEffect)(()=>{if(!e)return;let n=document.activeElement;return(a?.current??t.current)?.focus({preventScroll:!0}),()=>{n?.focus?.({preventScroll:!0})}},[e]),(0,n.useEffect)(()=>{if(!e)return;function n(e){if(e.key===`Escape`){e.stopPropagation(),r();return}if(e.key!==`Tab`)return;let n=t.current;if(!n)return;let a=i(n);if(a.length===0)return;let o=a[0],s=a[a.length-1];e.shiftKey&&document.activeElement===o?(e.preventDefault(),s.focus()):!e.shiftKey&&document.activeElement===s&&(e.preventDefault(),o.focus())}return document.addEventListener(`keydown`,n,!0),()=>document.removeEventListener(`keydown`,n,!0)},[e,r])}export{a as t};
|
|
2
|
+
//# sourceMappingURL=focus-trap-QqQxQdvH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus-trap-QqQxQdvH.js","names":[],"sources":["../../../../../../src/web-ui/components/primitives/focus-trap.ts"],"sourcesContent":["/**\n * components/primitives/focus-trap.ts — the modal focus contract, extracted\n * from Dialog.tsx so Sheet.tsx (the slide-over overlay) gets exactly the same\n * behavior instead of a second, subtly different implementation:\n *\n * - focus moves into the overlay (or `initialFocusRef`) on open\n * - focus is trapped inside while open (Tab/Shift+Tab wrap)\n * - focus returns to the element that opened it on close\n * - Escape closes\n *\n * Dialog.tsx is unchanged in behavior — it now calls this instead of\n * inlining it. Any future overlay MUST use this rather than hand-rolling a\n * trap (DESIGN.md §3, \"primitives own their accessibility\").\n */\nimport { useEffect, type RefObject } from 'react';\n\nexport const FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/** Focusable descendants that are actually rendered (offsetParent is null for hidden nodes). */\nexport function focusableWithin(node: HTMLElement): HTMLElement[] {\n return Array.from(node.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter((el) => el.offsetParent !== null);\n}\n\nexport interface FocusTrapOptions {\n open: boolean;\n containerRef: RefObject<HTMLElement | null>;\n onClose: () => void;\n initialFocusRef?: RefObject<HTMLElement | null>;\n}\n\nexport function useFocusTrap({ open, containerRef, onClose, initialFocusRef }: FocusTrapOptions): void {\n useEffect(() => {\n if (!open) return;\n const previouslyFocused = document.activeElement as HTMLElement | null;\n const target = initialFocusRef?.current ?? containerRef.current;\n target?.focus({ preventScroll: true });\n\n return () => {\n previouslyFocused?.focus?.({ preventScroll: true });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the refs are refs (stable identity is not the point); only `open` should re-run this.\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n e.stopPropagation();\n onClose();\n return;\n }\n if (e.key !== 'Tab') return;\n const node = containerRef.current;\n if (!node) return;\n const focusable = focusableWithin(node);\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n document.addEventListener('keydown', onKeyDown, true);\n return () => document.removeEventListener('keydown', onKeyDown, true);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- containerRef is a ref; re-binding on every render would drop in-flight key handling.\n }, [open, onClose]);\n}\n"],"mappings":"8DAgBa,EAAqB,CAChC,UACA,yBACA,wBACA,yBACA,2BACA,iCACF,CAAC,CAAC,KAAK,GAAG,EAGV,SAAgB,EAAgB,EAAkC,CAChE,OAAO,MAAM,KAAK,EAAK,iBAA8B,CAAkB,CAAC,CAAC,CAAC,OAAQ,GAAO,EAAG,eAAiB,IAAI,CACnH,CASA,SAAgB,EAAa,CAAE,OAAM,eAAc,UAAS,mBAA2C,EACrG,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,CAAC,EAAM,OACX,IAAM,EAAoB,SAAS,cAInC,OAHe,GAAiB,SAAW,EAAa,QAAA,EAChD,MAAM,CAAE,cAAe,EAAK,CAAC,MAExB,CACX,GAAmB,QAAQ,CAAE,cAAe,EAAK,CAAC,CACpD,CAEF,EAAG,CAAC,CAAI,CAAC,GAET,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,CAAC,EAAM,OACX,SAAS,EAAU,EAAkB,CACnC,GAAI,EAAE,MAAQ,SAAU,CACtB,EAAE,gBAAgB,EAClB,EAAQ,EACR,MACF,CACA,GAAI,EAAE,MAAQ,MAAO,OACrB,IAAM,EAAO,EAAa,QAC1B,GAAI,CAAC,EAAM,OACX,IAAM,EAAY,EAAgB,CAAI,EACtC,GAAI,EAAU,SAAW,EAAG,OAC5B,IAAM,EAAQ,EAAU,GAClB,EAAO,EAAU,EAAU,OAAS,GACtC,EAAE,UAAY,SAAS,gBAAkB,GAC3C,EAAE,eAAe,EACjB,EAAK,MAAM,GACF,CAAC,EAAE,UAAY,SAAS,gBAAkB,IACnD,EAAE,eAAe,EACjB,EAAM,MAAM,EAEhB,CAEA,OADA,SAAS,iBAAiB,UAAW,EAAW,EAAI,MACvC,SAAS,oBAAoB,UAAW,EAAW,EAAI,CAEtE,EAAG,CAAC,EAAM,CAAO,CAAC,CACpB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{n as e,r as t}from"./format-DKrNjdwD.js";import{
|
|
2
|
-
//# sourceMappingURL=format-
|
|
1
|
+
import{n as e,r as t}from"./format-DKrNjdwD.js";import{E as n}from"./VerseConsoleApp-CTF3Shs0.js";import{d as r,g as i}from"./verse-model-BQ0Y2CSr.js";function a(e,t){if(!e)return null;for(let n of e)if(n.seatId===t)return n;return null}function o(e,t=Date.now()){if(typeof e!=`string`||e.length===0)return null;let n=Date.parse(e);if(!Number.isFinite(n))return null;let r=new Date(n),i=r.toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`});return new Date(t).toDateString()===r.toDateString()?`today ${i}`:n>t&&n-t<5184e5?`${r.toLocaleDateString([],{weekday:`short`})} ${i}`:`${r.toLocaleDateString([],{month:`short`,day:`numeric`})}, ${i}`}function s(e){return(e.capacity?.binding??null)?.resetDescription??null}function c(e,t,n=Date.now()){let r=null,i=null;if(t!==null)(t.connection===`signed-out`||t.connection===`exhausted`)&&(r=t.connection,i=t.resetAt);else if(e.engine!==`local`){let t=e.capacity?.usability;(t===`signed-out`||t===`exhausted`)&&(r=t,i=t===`exhausted`?e.capacity?.binding?.resetsAt??null:null)}if(r===null)return null;if(r===`signed-out`)return{connection:r,resetAt:null,reason:`${e.label} is signed out — reconnect it to use this seat.`};let a=o(i,n),c=a===null?s(e):null,l=a===null?c===null?``:` — ${c}`:` — resets ${a}`;return{connection:r,resetAt:i,reason:`${e.label} is out of usage${l}.`}}function l(e){return e.models.some(e=>!e.unavailableReason)}function u(e){return e.engine===`local`?2:e.capacity?.usability===`ready`?0:1}function d(e){let t=e.capacity?.binding?.usedPercent;return typeof t==`number`&&Number.isFinite(t)?t:101}function f(e,t,n){return!(!l(e)||e.health.state===`unavailable`||c(e,t,n)!==null||e.engine===`local`&&t!==null&&t.connection===`unknown`&&t.reasons.length>0)}function p(e,t,n,r=Date.now()){let i=t.find(t=>t.id===e)??null,o=t.map((e,t)=>({seat:e,index:t})).filter(({seat:t})=>t.id!==e&&f(t,a(n,t.id),r));return o.sort((e,t)=>{let n=u(e.seat)-u(t.seat);if(n!==0)return n;if(i!==null){let n=e.seat.engine===i.engine?0:1,r=t.seat.engine===i.engine?0:1;if(n!==r)return n-r}let r=d(e.seat)-d(t.seat);return r===0?e.index-t.index:r}),o.slice(0,5).map(({seat:e})=>e.id)}function m(e){return typeof e!=`number`||!Number.isFinite(e)?`—`:e>0&&e<.01?`<1%`:t(e)}function h(e){return n(e)}var g=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?(?![\w:])/g,_=String.raw`[\p{L}\p{N}%…)\]"'’”\x60]`,v=RegExp(`(${_})\\.{1,2}\\s*;`,`gu`),y=RegExp(`(${_})\\.\\.(?=\\s|$)`,`gu`);function b(e,t=Date.now()){return e.replace(g,e=>o(e,t)??e).replace(v,`$1;`).replace(y,`$1.`)}function x(e){let t=e.trim();return t.endsWith(`...`)||t.endsWith(`…`)?t:t.replace(/\.+$/,``)}function S(e){return r(e)?i(e):e}function C(e){return typeof e!=`number`||!Number.isFinite(e)?`—`:`$${e.toFixed(2)}`}function w(e){return typeof e!=`number`||!Number.isFinite(e)?`—`:String(e)}function T(e){let t=Math.max(0,Math.round(e/1e3));if(t<60)return`${t}s`;let n=Math.floor(t/60),r=t%60;return n<60?`${n}m ${String(r).padStart(2,`0`)}s`:`${Math.floor(n/60)}h ${String(n%60).padStart(2,`0`)}m`}function E(e){if(!e)return`—`;let t=Date.parse(e);if(Number.isNaN(t))return`—`;let n=Math.max(0,Math.round((Date.now()-t)/1e3));if(n<60)return`${n}s`;let r=Math.round(n/60);if(r<60)return`${r}m`;let i=Math.round(r/60);return i<24?`${i}h`:`${Math.round(i/24)}d`}function D(e){let t=E(e);return t===`—`?`—`:`${t} ago`}function O(e=new Date){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}function k(e,t=new Date){return e===O(t)||e===t.toISOString().slice(0,10)}function A(e,t=new Date){if(!e)return`—`;let n=Date.parse(e);if(Number.isNaN(n))return`—`;let r=new Date(n),i=r.toLocaleTimeString(void 0,{hour:`2-digit`,minute:`2-digit`,second:`2-digit`});return O(r)===O(t)?i:`${r.toLocaleDateString(void 0,{month:`short`,day:`numeric`})} ${i}`}function j(e){return typeof e!=`number`||!Number.isFinite(e)||e<=0?`—`:`every ${T(e).replace(/ 00[ms]$/,``)}`}var M=70;function N(t,n,r,i=new Date){let a=typeof t==`number`&&Number.isFinite(t)?Math.max(0,t):null,o=typeof n==`number`&&Number.isFinite(n)?n:null,s=typeof r==`string`&&!k(r,i);if(o===0)return{state:`stopped`,percent:100,label:`${C(a)} spent today · budget $0.00`,note:`Daily budget is 0 — the loop is stopped, not unlimited.`};if(s)return{state:`unknown`,percent:null,label:`— spent today · budget ${C(o)}`,note:`Nothing has been recorded today — the last ledger day is ${e(r)}.`};if(o===null||a===null)return{state:`unknown`,percent:null,label:o===null?`${C(a)} spent today · budget —`:`— spent today · budget ${C(o)}`,note:o===null?`Daily budget is not configured.`:`Today's spend is not observable right now.`};let c=a/o*100,l=Math.min(100,Math.round(c)),u=c>=100?`over`:c>=M?`warn`:`ok`;return{state:u,percent:l,label:`${C(a)} of ${C(o)} today · ${h(a/o*100)}`,note:u===`over`?`Daily budget reached — the loop idles until the date rolls over.`:u===`warn`?`${C(o-a)} left before the loop idles today.`:`${C(o-a)} left today.`}}function P(e,t){if(!e)return null;let n=Date.parse(e);return Number.isNaN(n)||typeof t!=`number`||!Number.isFinite(t)||t<=0?null:n+t}function F(e,t){if(e===null)return`—`;let n=e-t;return n>1e3?`in ${T(n)}`:n>-1e3?`due now`:`overdue by ${T(-n)}`}function I(e){if(!e)return{label:`no tick recorded`,tone:`unknown`};switch(e){case`ok`:return{label:`ok`,tone:`success`};case`kill-switch`:return{label:`refused — emergency stop engaged`,tone:`danger`};case`budget-exhausted`:return{label:`stopped — daily budget spent`,tone:`warning`};case`no-enrolled-repos`:return{label:`nothing to do — no repos enrolled`,tone:`warning`};case`no-backlog`:return{label:`nothing to do — backlog empty`,tone:`neutral`};case`dry-run`:return{label:`dry run`,tone:`neutral`};default:return{label:e,tone:`neutral`}}}export{S as _,E as a,p as b,j as c,C as d,m as f,h as g,P as h,I as i,D as l,O as m,N as n,w as o,k as p,F as r,T as s,x as t,A as u,b as v,c as x,o as y};
|
|
2
|
+
//# sourceMappingURL=format-CaMmVQCn.js.map
|