@ashlr/hub 3.10.0 → 3.10.1
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 +101 -0
- package/dist/authority-surface.json +43 -23
- package/dist/build-identity.json +2 -2
- package/dist/core/daemon/capacity-publisher.d.ts +19 -0
- package/dist/core/daemon/capacity-publisher.js +49 -2
- package/dist/core/daemon/capacity-publisher.js.map +1 -1
- package/dist/core/daemon/overnight-status.d.ts +13 -0
- package/dist/core/daemon/overnight-status.js +93 -8
- package/dist/core/daemon/overnight-status.js.map +1 -1
- package/dist/core/fleet/dispatch-router.js +26 -9
- package/dist/core/fleet/dispatch-router.js.map +1 -1
- package/dist/core/fleet/fleet-runtime-journal.js +12 -5
- package/dist/core/fleet/fleet-runtime-journal.js.map +1 -1
- package/dist/core/fleet/fleet-types.d.ts +13 -0
- package/dist/core/routing/budget-store.js +11 -5
- package/dist/core/routing/budget-store.js.map +1 -1
- package/dist/core/routing/capacity-history-api.d.ts +30 -0
- package/dist/core/routing/capacity-history-api.js +147 -0
- package/dist/core/routing/capacity-history-api.js.map +1 -0
- package/dist/core/routing/capacity-history-types.d.ts +63 -0
- package/dist/core/routing/capacity-history-types.js +18 -0
- package/dist/core/routing/capacity-history-types.js.map +1 -0
- package/dist/core/routing/capacity-history.d.ts +130 -0
- package/dist/core/routing/capacity-history.js +654 -0
- package/dist/core/routing/capacity-history.js.map +1 -0
- package/dist/core/routing/headroom.d.ts +5 -1
- package/dist/core/routing/headroom.js +82 -41
- package/dist/core/routing/headroom.js.map +1 -1
- package/dist/core/routing/router.d.ts +8 -1
- package/dist/core/routing/router.js +49 -16
- package/dist/core/routing/router.js.map +1 -1
- package/dist/core/routing/seat-reasons.d.ts +35 -0
- package/dist/core/routing/seat-reasons.js +48 -0
- package/dist/core/routing/seat-reasons.js.map +1 -0
- package/dist/core/routing/types.d.ts +37 -0
- package/dist/core/routing/types.js.map +1 -1
- package/dist/core/verse/fleet-history-types.d.ts +12 -4
- package/dist/core/verse/fleet-history-types.js +4 -2
- package/dist/core/verse/fleet-history-types.js.map +1 -1
- package/dist/core/verse/fleet-live-api.d.ts +14 -1
- package/dist/core/verse/fleet-live-api.js +31 -11
- package/dist/core/verse/fleet-live-api.js.map +1 -1
- package/dist/core/verse/preferences.d.ts +3 -1
- package/dist/core/verse/preferences.js +5 -2
- package/dist/core/verse/preferences.js.map +1 -1
- package/dist/core/verse/types.d.ts +5 -1
- package/dist/core/verse/types.js.map +1 -1
- package/dist/core/verse/verse-api.js +2 -1
- package/dist/core/verse/verse-api.js.map +1 -1
- package/dist/core/web/public/next/assets/{ActionMenu-DhbNne-M.js → ActionMenu-BBB7VYjz.js} +2 -2
- package/dist/core/web/public/next/assets/{ActionMenu-DhbNne-M.js.map → ActionMenu-BBB7VYjz.js.map} +1 -1
- package/dist/core/web/public/next/assets/Advanced-Bywv_iYR.css +1 -0
- package/dist/core/web/public/next/assets/Advanced-xVYFAeA8.js +2 -0
- package/dist/core/web/public/next/assets/Advanced-xVYFAeA8.js.map +1 -0
- package/dist/core/web/public/next/assets/{App-DyBg1v_q.js → App-CbDCLAxn.js} +5 -5
- package/dist/core/web/public/next/assets/{App-DyBg1v_q.js.map → App-CbDCLAxn.js.map} +1 -1
- package/dist/core/web/public/next/assets/AppsSection-B5HWJi9I.js +2 -0
- package/dist/core/web/public/next/assets/AppsSection-B5HWJi9I.js.map +1 -0
- package/dist/core/web/public/next/assets/AreaTrend-BgnK0wAy.js +2 -0
- package/dist/core/web/public/next/assets/AreaTrend-BgnK0wAy.js.map +1 -0
- package/dist/core/web/public/next/assets/AutonomySection-BF5UCsMv.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-CoMngwNB.js.map → AutonomySection-BF5UCsMv.js.map} +1 -1
- package/dist/core/web/public/next/assets/BranchBar-BZNQ50YE.css +1 -0
- package/dist/core/web/public/next/assets/BranchBar-nfSb59Kv.js +2 -0
- package/dist/core/web/public/next/assets/BranchBar-nfSb59Kv.js.map +1 -0
- package/dist/core/web/public/next/assets/BudgetControl-BESM12dO.js +2 -0
- package/dist/core/web/public/next/assets/BudgetControl-BESM12dO.js.map +1 -0
- package/dist/core/web/public/next/assets/BurnDown-D1AfskPT.js +2 -0
- package/dist/core/web/public/next/assets/BurnDown-D1AfskPT.js.map +1 -0
- package/dist/core/web/public/next/assets/{Button-0-nNbv1h.js → Button-Cr_qkQEc.js} +2 -2
- package/dist/core/web/public/next/assets/{Button-0-nNbv1h.js.map → Button-Cr_qkQEc.js.map} +1 -1
- package/dist/core/web/public/next/assets/CapacityStrip-Cc28CyWk.css +1 -0
- package/dist/core/web/public/next/assets/CapacityStrip-DqqYnLek.js +2 -0
- package/dist/core/web/public/next/assets/CapacityStrip-DqqYnLek.js.map +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DoqjyVoj.js +3 -0
- package/dist/core/web/public/next/assets/{ChatSection-B9cdRKUh.js.map → ChatSection-DoqjyVoj.js.map} +1 -1
- package/dist/core/web/public/next/assets/{CommandPalette-GpeCSrPy.js → CommandPalette-lLV-OR63.js} +2 -2
- package/dist/core/web/public/next/assets/{CommandPalette-GpeCSrPy.js.map → CommandPalette-lLV-OR63.js.map} +1 -1
- package/dist/core/web/public/next/assets/CommandSection-fiZ4DJpM.js +2 -0
- package/dist/core/web/public/next/assets/CommandSection-fiZ4DJpM.js.map +1 -0
- package/dist/core/web/public/next/assets/Composer-ByzxOJK6.css +1 -0
- package/dist/core/web/public/next/assets/Composer.module-C8U73KdQ.js +2 -0
- package/dist/core/web/public/next/assets/{Composer.module-DJMdrqNt.js.map → Composer.module-C8U73KdQ.js.map} +1 -1
- package/dist/core/web/public/next/assets/DiffPane-BAvvbBsf.css +1 -0
- package/dist/core/web/public/next/assets/DiffPane-BVxhK-cy.js +2 -0
- package/dist/core/web/public/next/assets/DiffPane-BVxhK-cy.js.map +1 -0
- package/dist/core/web/public/next/assets/{DiffViewer-tOGWk0CA.js → DiffViewer-yCdw9J-Y.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-tOGWk0CA.js.map → DiffViewer-yCdw9J-Y.js.map} +1 -1
- package/dist/core/web/public/next/assets/DockHost-B4RaYOET.css +1 -0
- package/dist/core/web/public/next/assets/DockHost-BZReKwos.js +2 -0
- package/dist/core/web/public/next/assets/DockHost-BZReKwos.js.map +1 -0
- package/dist/core/web/public/next/assets/{EmptyState-DdJ4CxtO.js → EmptyState-D1GDxVZy.js} +2 -2
- package/dist/core/web/public/next/assets/{EmptyState-DdJ4CxtO.js.map → EmptyState-D1GDxVZy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-BLjM5y_y.js → Epistemic-DRp4dBfB.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-BLjM5y_y.js.map → Epistemic-DRp4dBfB.js.map} +1 -1
- package/dist/core/web/public/next/assets/FleetSection-CFiWeOEs.css +1 -0
- package/dist/core/web/public/next/assets/FleetSection-rPLy_NPo.js +3 -0
- package/dist/core/web/public/next/assets/FleetSection-rPLy_NPo.js.map +1 -0
- package/dist/core/web/public/next/assets/Funnel-zvDj3He4.js +2 -0
- package/dist/core/web/public/next/assets/Funnel-zvDj3He4.js.map +1 -0
- package/dist/core/web/public/next/assets/GearTray-CkviqjYr.js +2 -0
- package/dist/core/web/public/next/assets/GearTray-CkviqjYr.js.map +1 -0
- package/dist/core/web/public/next/assets/GrowthSection-Bg5sW0jI.js +2 -0
- package/dist/core/web/public/next/assets/GrowthSection-Bg5sW0jI.js.map +1 -0
- package/dist/core/web/public/next/assets/HandoffDialog-DcPj1ri9.js +2 -0
- package/dist/core/web/public/next/assets/HandoffDialog-DcPj1ri9.js.map +1 -0
- package/dist/core/web/public/next/assets/{Input-CTu0CycC.js → Input-Ci7TD96x.js} +2 -2
- package/dist/core/web/public/next/assets/{Input-CTu0CycC.js.map → Input-Ci7TD96x.js.map} +1 -1
- package/dist/core/web/public/next/assets/{LeaderCard-BuSDeATv.js → LeaderCard-BSFTlMRF.js} +2 -2
- package/dist/core/web/public/next/assets/{LeaderCard-BuSDeATv.js.map → LeaderCard-BSFTlMRF.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MatrixHeatmap-9dKgJ_pT.css → MatrixHeatmap-Bmnvo1l1.css} +1 -1
- package/dist/core/web/public/next/assets/MatrixHeatmap-DojKZe9L.js +2 -0
- package/dist/core/web/public/next/assets/MatrixHeatmap-DojKZe9L.js.map +1 -0
- package/dist/core/web/public/next/assets/McpSection-CvcYu1X_.js +2 -0
- package/dist/core/web/public/next/assets/{McpSection-1UncWBpW.js.map → McpSection-CvcYu1X_.js.map} +1 -1
- package/dist/core/web/public/next/assets/MessageMarkdown-BrLCrb73.js +3 -0
- package/dist/core/web/public/next/assets/{MessageMarkdown-C4pl9LI5.js.map → MessageMarkdown-BrLCrb73.js.map} +1 -1
- package/dist/core/web/public/next/assets/MessageMarkdown-Cgh5f1-Q.css +1 -0
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-CZsF77ZM.js → MessageMarkdownRenderer-CjmdBw2p.js} +2 -2
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-CZsF77ZM.js.map → MessageMarkdownRenderer-CjmdBw2p.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-C18ciKz4.js → Meter-DTHbmhQg.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-C18ciKz4.js.map → Meter-DTHbmhQg.js.map} +1 -1
- package/dist/core/web/public/next/assets/MindSection-DcO9fmYe.js +2 -0
- package/dist/core/web/public/next/assets/MindSection-DcO9fmYe.js.map +1 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-Cqn2uPV6.js +2 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-Cqn2uPV6.js.map +1 -0
- package/dist/core/web/public/next/assets/NeedsYouDrawer-BpvidnM2.css +1 -0
- package/dist/core/web/public/next/assets/NeedsYouDrawer-hiTINupk.js +2 -0
- package/dist/core/web/public/next/assets/NeedsYouDrawer-hiTINupk.js.map +1 -0
- package/dist/core/web/public/next/assets/NeedsYouList-B4Z37Phx.js +2 -0
- package/dist/core/web/public/next/assets/NeedsYouList-B4Z37Phx.js.map +1 -0
- package/dist/core/web/public/next/assets/NeedsYouList-CruOt4JT.css +1 -0
- package/dist/core/web/public/next/assets/NewChatDialog-utSckh7C.js +3 -0
- package/dist/core/web/public/next/assets/{NewChatDialog-B_BsS9pr.js.map → NewChatDialog-utSckh7C.js.map} +1 -1
- package/dist/core/web/public/next/assets/OnboardingFlow-7N3YSUHN.js +2 -0
- package/dist/core/web/public/next/assets/{OnboardingFlow-CtCPpgoX.js.map → OnboardingFlow-7N3YSUHN.js.map} +1 -1
- package/dist/core/web/public/next/assets/PreviewPane-CqzkaAjG.js +2 -0
- package/dist/core/web/public/next/assets/PreviewPane-CqzkaAjG.js.map +1 -0
- package/dist/core/web/public/next/assets/PreviewPane-DoMsJSG3.css +1 -0
- package/dist/core/web/public/next/assets/RailStatus-sZhApA8k.js +2 -0
- package/dist/core/web/public/next/assets/RailStatus-sZhApA8k.js.map +1 -0
- package/dist/core/web/public/next/assets/{RefreshIndicator-CnhEovMZ.js → RefreshIndicator-C1uQIegT.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-CnhEovMZ.js.map → RefreshIndicator-C1uQIegT.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-28gLwmwV.js → ResourcePoolConsoleApp-DXjjV6Sh.js} +4 -4
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-28gLwmwV.js.map → ResourcePoolConsoleApp-DXjjV6Sh.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-AyUPCIb3.js → RouteErrorBoundary-D1LZxQjN.js} +2 -2
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-AyUPCIb3.js.map → RouteErrorBoundary-D1LZxQjN.js.map} +1 -1
- package/dist/core/web/public/next/assets/SeatSelector-CT92PqM_.js +3 -0
- package/dist/core/web/public/next/assets/{SeatSelector-DaUO-zoK.js.map → SeatSelector-CT92PqM_.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Segmented-B_8c_83S.js → Segmented-BMvq-FfD.js} +2 -2
- package/dist/core/web/public/next/assets/{Segmented-B_8c_83S.js.map → Segmented-BMvq-FfD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Select-Bl5g5Dt_.js → Select-DkdeoSQn.js} +2 -2
- package/dist/core/web/public/next/assets/{Select-Bl5g5Dt_.js.map → Select-DkdeoSQn.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SessionInsightChip-w0I21q0c.js → SessionInsightChip-CwlakUYU.js} +2 -2
- package/dist/core/web/public/next/assets/{SessionInsightChip-w0I21q0c.js.map → SessionInsightChip-CwlakUYU.js.map} +1 -1
- package/dist/core/web/public/next/assets/SessionSearch-DFByh2NZ.js +2 -0
- package/dist/core/web/public/next/assets/{SessionSearch-B5aQ0BCM.js.map → SessionSearch-DFByh2NZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/SettingsSection-DgSaHFK4.js +2 -0
- package/dist/core/web/public/next/assets/SettingsSection-DgSaHFK4.js.map +1 -0
- package/dist/core/web/public/next/assets/{Sheet-CVjk-cLI.js → Sheet-C7APkGPt.js} +2 -2
- package/dist/core/web/public/next/assets/{Sheet-CVjk-cLI.js.map → Sheet-C7APkGPt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-BiBjnBcc.js → ShortcutsOverlay-ZPyXUS_I.js} +2 -2
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-BiBjnBcc.js.map → ShortcutsOverlay-ZPyXUS_I.js.map} +1 -1
- package/dist/core/web/public/next/assets/Sidebar-BRc1vGDF.css +1 -0
- package/dist/core/web/public/next/assets/Sidebar-Chsgazmk.js +3 -0
- package/dist/core/web/public/next/assets/Sidebar-Chsgazmk.js.map +1 -0
- package/dist/core/web/public/next/assets/{Slider-BS9MU5ZV.js → Slider-B0kjW7cd.js} +2 -2
- package/dist/core/web/public/next/assets/{Slider-BS9MU5ZV.js.map → Slider-B0kjW7cd.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatusBadge-DAf-ArKE.js → StatusBadge-wv4e6f8b.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-DAf-ArKE.js.map → StatusBadge-wv4e6f8b.js.map} +1 -1
- package/dist/core/web/public/next/assets/StepBand-Db8WPw8J.js +2 -0
- package/dist/core/web/public/next/assets/StepBand-Db8WPw8J.js.map +1 -0
- package/dist/core/web/public/next/assets/{Surface-CiYOdNKQ.js → Surface-BkDrQ4t3.js} +2 -2
- package/dist/core/web/public/next/assets/{Surface-CiYOdNKQ.js.map → Surface-BkDrQ4t3.js.map} +1 -1
- package/dist/core/web/public/next/assets/Swimlane-Dcve56cP.js +2 -0
- package/dist/core/web/public/next/assets/Swimlane-Dcve56cP.js.map +1 -0
- package/dist/core/web/public/next/assets/{Switch-BzRXtNPt.js → Switch-DamlpSMY.js} +2 -2
- package/dist/core/web/public/next/assets/{Switch-BzRXtNPt.js.map → Switch-DamlpSMY.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Tag-DEEbDz7T.js → Tag-Cs4tudBN.js} +2 -2
- package/dist/core/web/public/next/assets/{Tag-DEEbDz7T.js.map → Tag-Cs4tudBN.js.map} +1 -1
- package/dist/core/web/public/next/assets/TerminalPane-BDkKFych.js +7 -0
- package/dist/core/web/public/next/assets/TerminalPane-BDkKFych.js.map +1 -0
- package/dist/core/web/public/next/assets/TerminalPane-BwfMbnJz.css +1 -0
- package/dist/core/web/public/next/assets/UniverseConsoleApp-BE72VyBL.js +2 -0
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DKqAf-eD.js.map → UniverseConsoleApp-BE72VyBL.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-D6081LQL.js → UniverseView-BbmYY2P4.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseView-D6081LQL.js.map → UniverseView-BbmYY2P4.js.map} +1 -1
- package/dist/core/web/public/next/assets/UsageSection-DsB-Jqyl.css +1 -0
- package/dist/core/web/public/next/assets/UsageSection-DsC5WmLk.js +2 -0
- package/dist/core/web/public/next/assets/UsageSection-DsC5WmLk.js.map +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-CXJWDiEM.css +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-Dq_hREXs.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-Dq_hREXs.js.map +1 -0
- package/dist/core/web/public/next/assets/Workspace-BYW9dm90.css +1 -0
- package/dist/core/web/public/next/assets/Workspace-CKvVEPFq.js +10 -0
- package/dist/core/web/public/next/assets/Workspace-CKvVEPFq.js.map +1 -0
- package/dist/core/web/public/next/assets/actions-BnjZy8a-.js +2 -0
- package/dist/core/web/public/next/assets/actions-BnjZy8a-.js.map +1 -0
- package/dist/core/web/public/next/assets/actions-Ddfg2iLl.css +1 -0
- package/dist/core/web/public/next/assets/{budget-queries-Dacv1VYP.js → budget-queries-Cp6vO7FH.js} +2 -2
- package/dist/core/web/public/next/assets/{budget-queries-Dacv1VYP.js.map → budget-queries-Cp6vO7FH.js.map} +1 -1
- package/dist/core/web/public/next/assets/calendar-day-BxBYHfX1.js +2 -0
- package/dist/core/web/public/next/assets/calendar-day-BxBYHfX1.js.map +1 -0
- package/dist/core/web/public/next/assets/capacity-strip-model-9-Yb211u.js +2 -0
- package/dist/core/web/public/next/assets/capacity-strip-model-9-Yb211u.js.map +1 -0
- package/dist/core/web/public/next/assets/charts-BmbKhjqw.js +2 -0
- package/dist/core/web/public/next/assets/charts-BmbKhjqw.js.map +1 -0
- package/dist/core/web/public/next/assets/colors-BZ3a9wFf.js +2 -0
- package/dist/core/web/public/next/assets/colors-BZ3a9wFf.js.map +1 -0
- package/dist/core/web/public/next/assets/{command-bus-CpYuam1F.js → command-bus-DHJJLWh8.js} +2 -2
- package/dist/core/web/public/next/assets/{command-bus-CpYuam1F.js.map → command-bus-DHJJLWh8.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-BGznyHNQ.js +2 -0
- package/dist/core/web/public/next/assets/context-model-BGznyHNQ.js.map +1 -0
- package/dist/core/web/public/next/assets/context-model-DeJ28uSZ.js +2 -0
- package/dist/core/web/public/next/assets/context-model-DeJ28uSZ.js.map +1 -0
- package/dist/core/web/public/next/assets/context-queries-_mEgy4eg.js +2 -0
- package/dist/core/web/public/next/assets/context-queries-_mEgy4eg.js.map +1 -0
- package/dist/core/web/public/next/assets/context.module-BijAfoJw.js +2 -0
- package/dist/core/web/public/next/assets/context.module-BijAfoJw.js.map +1 -0
- package/dist/core/web/public/next/assets/{control-queries-BGjarMm9.js → control-queries-C4TG8blN.js} +2 -2
- package/dist/core/web/public/next/assets/{control-queries-BGjarMm9.js.map → control-queries-C4TG8blN.js.map} +1 -1
- package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js +2 -0
- package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js.map +1 -0
- package/dist/core/web/public/next/assets/dock-icons-BB7Fi6hT.js +2 -0
- package/dist/core/web/public/next/assets/dock-icons-BB7Fi6hT.js.map +1 -0
- package/dist/core/web/public/next/assets/dock-panes-mnIwSpRp.js +2 -0
- package/dist/core/web/public/next/assets/{dock-panes-BNTLvX7G.js.map → dock-panes-mnIwSpRp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{dock-store-_E5h6TEq.js → dock-store-CdHQRP8s.js} +2 -2
- package/dist/core/web/public/next/assets/{dock-store-_E5h6TEq.js.map → dock-store-CdHQRP8s.js.map} +1 -1
- package/dist/core/web/public/next/assets/fleet-model-B4FKC7fH.js +2 -0
- package/dist/core/web/public/next/assets/fleet-model-B4FKC7fH.js.map +1 -0
- package/dist/core/web/public/next/assets/format-DKrNjdwD.js +2 -0
- package/dist/core/web/public/next/assets/format-DKrNjdwD.js.map +1 -0
- package/dist/core/web/public/next/assets/format-lW58rL2y.js +2 -0
- package/dist/core/web/public/next/assets/format-lW58rL2y.js.map +1 -0
- package/dist/core/web/public/next/assets/{git-model-5v5xGobf.js → git-model-DaGw1SzS.js} +2 -2
- package/dist/core/web/public/next/assets/{git-model-5v5xGobf.js.map → git-model-DaGw1SzS.js.map} +1 -1
- package/dist/core/web/public/next/assets/global-DG1fmn4y.js +2 -0
- package/dist/core/web/public/next/assets/global-DG1fmn4y.js.map +1 -0
- package/dist/core/web/public/next/assets/{guarded-action-vOPc8v75.js → guarded-action--lguLXfz.js} +2 -2
- package/dist/core/web/public/next/assets/{guarded-action-vOPc8v75.js.map → guarded-action--lguLXfz.js.map} +1 -1
- package/dist/core/web/public/next/assets/{icons-DWb24OA6.js → icons-YPgbel-I.js} +2 -2
- package/dist/core/web/public/next/assets/{icons-DWb24OA6.js.map → icons-YPgbel-I.js.map} +1 -1
- package/dist/core/web/public/next/assets/{index-afDA8cwW.js → index-Dk8R-3a7.js} +3 -3
- package/dist/core/web/public/next/assets/{index-afDA8cwW.js.map → index-Dk8R-3a7.js.map} +1 -1
- package/dist/core/web/public/next/assets/live-model-44wEU6I8.js +2 -0
- package/dist/core/web/public/next/assets/live-model-44wEU6I8.js.map +1 -0
- package/dist/core/web/public/next/assets/mind-B9PzLcl0.css +1 -0
- package/dist/core/web/public/next/assets/mind.module-BOABBdi_.js +2 -0
- package/dist/core/web/public/next/assets/mind.module-BOABBdi_.js.map +1 -0
- package/dist/core/web/public/next/assets/mutations-DjXM3IDg.js +2 -0
- package/dist/core/web/public/next/assets/{mutations-C6E10SPX.js.map → mutations-DjXM3IDg.js.map} +1 -1
- package/dist/core/web/public/next/assets/{overnight-queries-CCGfiS-g.js → overnight-queries-Be_eossf.js} +2 -2
- package/dist/core/web/public/next/assets/{overnight-queries-CCGfiS-g.js.map → overnight-queries-Be_eossf.js.map} +1 -1
- package/dist/core/web/public/next/assets/pane-chrome-8JmEhHVD.css +1 -0
- package/dist/core/web/public/next/assets/pane-chrome.module-D7o-fVd0.js +2 -0
- package/dist/core/web/public/next/assets/pane-chrome.module-D7o-fVd0.js.map +1 -0
- package/dist/core/web/public/next/assets/primitives-Dl3SEQvh.js +1 -0
- package/dist/core/web/public/next/assets/{queries-BuYIEHst.js → queries-CYEVCq5r.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-BuYIEHst.js.map → queries-CYEVCq5r.js.map} +1 -1
- package/dist/core/web/public/next/assets/{reasoning-pref-BVzGczV7.js → reasoning-pref-a_ZP6GzU.js} +2 -2
- package/dist/core/web/public/next/assets/{reasoning-pref-BVzGczV7.js.map → reasoning-pref-a_ZP6GzU.js.map} +1 -1
- package/dist/core/web/public/next/assets/seat-subscription-CRigL-GE.js +2 -0
- package/dist/core/web/public/next/assets/seat-subscription-CRigL-GE.js.map +1 -0
- package/dist/core/web/public/next/assets/{section-visibility-CSrsTcwK.js → section-visibility-u7q-Q9AA.js} +2 -2
- package/dist/core/web/public/next/assets/{section-visibility-CSrsTcwK.js.map → section-visibility-u7q-Q9AA.js.map} +1 -1
- package/dist/core/web/public/next/assets/{sidebar-model-C8fyqSQY.js → sidebar-model-i4WRuQ6j.js} +2 -2
- package/dist/core/web/public/next/assets/{sidebar-model-C8fyqSQY.js.map → sidebar-model-i4WRuQ6j.js.map} +1 -1
- package/dist/core/web/public/next/assets/slots-CV9ZuLQU.js +3 -0
- package/dist/core/web/public/next/assets/{slots-W8gkmdfE.js.map → slots-CV9ZuLQU.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-data-DLvmUX9z.js +2 -0
- package/dist/core/web/public/next/assets/surface-data-DLvmUX9z.js.map +1 -0
- package/dist/core/web/public/next/assets/surface-prefetch-Ct2JwXvL.js +2 -0
- package/dist/core/web/public/next/assets/surface-prefetch-Ct2JwXvL.js.map +1 -0
- package/dist/core/web/public/next/assets/tasks-model-DWs5u9RO.js +2 -0
- package/dist/core/web/public/next/assets/{tasks-model-CnYQzgxk.js.map → tasks-model-DWs5u9RO.js.map} +1 -1
- package/dist/core/web/public/next/assets/{terminal-icons-CxUYaWX9.js → terminal-icons-Ct9cZsEK.js} +2 -2
- package/dist/core/web/public/next/assets/{terminal-icons-CxUYaWX9.js.map → terminal-icons-Ct9cZsEK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{tool-semantics-CTRuLF5j.js → tool-semantics-GX8ScuSH.js} +2 -2
- package/dist/core/web/public/next/assets/{tool-semantics-CTRuLF5j.js.map → tool-semantics-GX8ScuSH.js.map} +1 -1
- package/dist/core/web/public/next/assets/{turn-files-BvB5LLNw.js → turn-files-BPHQzYuH.js} +2 -2
- package/dist/core/web/public/next/assets/{turn-files-BvB5LLNw.js.map → turn-files-BPHQzYuH.js.map} +1 -1
- package/dist/core/web/public/next/assets/{usage-queries-CQOpeE1H.js → usage-queries-CI_y9oWZ.js} +2 -2
- package/dist/core/web/public/next/assets/{usage-queries-CQOpeE1H.js.map → usage-queries-CI_y9oWZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-guarded-action-DVCtLzSo.js +2 -0
- package/dist/core/web/public/next/assets/use-guarded-action-DVCtLzSo.js.map +1 -0
- package/dist/core/web/public/next/assets/{use-ticker-hryPXnyp.js → use-ticker-C3LQ2IYs.js} +2 -2
- package/dist/core/web/public/next/assets/{use-ticker-hryPXnyp.js.map → use-ticker-C3LQ2IYs.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-token-gate-B5gOFkeH.js +2 -0
- package/dist/core/web/public/next/assets/use-token-gate-B5gOFkeH.js.map +1 -0
- package/dist/core/web/public/next/assets/{useActivity-Cg_9-7nt.js → useActivity-C3R1Sudh.js} +2 -2
- package/dist/core/web/public/next/assets/{useActivity-Cg_9-7nt.js.map → useActivity-C3R1Sudh.js.map} +1 -1
- package/dist/core/web/public/next/assets/useChartWidth-0APW0BR3.js +2 -0
- package/dist/core/web/public/next/assets/useChartWidth-0APW0BR3.js.map +1 -0
- package/dist/core/web/public/next/assets/useChartWidth-BAtVgn2n.css +1 -0
- package/dist/core/web/public/next/assets/{useSeatsRefresh-4Ov9eT1F.js → useSeatsRefresh-BN8bso-n.js} +2 -2
- package/dist/core/web/public/next/assets/{useSeatsRefresh-4Ov9eT1F.js.map → useSeatsRefresh-BN8bso-n.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-model-yHPwjf-7.js +2 -0
- package/dist/core/web/public/next/assets/verse-model-yHPwjf-7.js.map +1 -0
- package/dist/core/web/public/next/assets/{verse-queries-BjQoOTwv.js → verse-queries-2PxoBISm.js} +2 -2
- package/dist/core/web/public/next/assets/{verse-queries-BjQoOTwv.js.map → verse-queries-2PxoBISm.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-store-DHTuZpYa.js +3 -0
- package/dist/core/web/public/next/assets/verse-store-DHTuZpYa.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-ui-store-Dy4HuDBV.js +3 -0
- package/dist/core/web/public/next/assets/{verse-ui-store-KTTKfv-T.js.map → verse-ui-store-Dy4HuDBV.js.map} +1 -1
- package/dist/core/web/public/next/assets/{viewport-ZB86jdES.js → viewport-DDoJ_xVq.js} +2 -2
- package/dist/core/web/public/next/assets/{viewport-ZB86jdES.js.map → viewport-DDoJ_xVq.js.map} +1 -1
- package/dist/core/web/public/next/assets/warmup-BsL2LdR-.js +3 -0
- package/dist/core/web/public/next/assets/warmup-BsL2LdR-.js.map +1 -0
- package/dist/core/web/public/next/assets/{workspace-model-4hq_Mv3N.js → workspace-model-Dd3LmIym.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-4hq_Mv3N.js.map → workspace-model-Dd3LmIym.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 +1 -1
- package/dist/core/web/public/next/assets/Advanced-CHjxJt2z.css +0 -1
- package/dist/core/web/public/next/assets/Advanced-DM0DYibH.js +0 -2
- package/dist/core/web/public/next/assets/Advanced-DM0DYibH.js.map +0 -1
- package/dist/core/web/public/next/assets/AppsSection-CXxbfPJQ.js +0 -2
- package/dist/core/web/public/next/assets/AppsSection-CXxbfPJQ.js.map +0 -1
- package/dist/core/web/public/next/assets/AreaTrend-gUVXkfhf.js +0 -2
- package/dist/core/web/public/next/assets/AreaTrend-gUVXkfhf.js.map +0 -1
- package/dist/core/web/public/next/assets/AutonomySection-CoMngwNB.js +0 -2
- package/dist/core/web/public/next/assets/BranchBar-C-NovaXr.js +0 -2
- package/dist/core/web/public/next/assets/BranchBar-C-NovaXr.js.map +0 -1
- package/dist/core/web/public/next/assets/BranchBar-C9BpPVLr.css +0 -1
- package/dist/core/web/public/next/assets/BudgetControl-P5lMxHvQ.js +0 -2
- package/dist/core/web/public/next/assets/BudgetControl-P5lMxHvQ.js.map +0 -1
- package/dist/core/web/public/next/assets/BurnDown-4P1LDoCL.js +0 -2
- package/dist/core/web/public/next/assets/BurnDown-4P1LDoCL.js.map +0 -1
- package/dist/core/web/public/next/assets/CapacityStrip-D6Yyg4a1.js +0 -2
- package/dist/core/web/public/next/assets/CapacityStrip-D6Yyg4a1.js.map +0 -1
- package/dist/core/web/public/next/assets/CapacityStrip-wgFP9MUA.css +0 -1
- package/dist/core/web/public/next/assets/ChatSection-B9cdRKUh.js +0 -3
- package/dist/core/web/public/next/assets/CommandSection-BC0uXim_.js +0 -2
- package/dist/core/web/public/next/assets/CommandSection-BC0uXim_.js.map +0 -1
- package/dist/core/web/public/next/assets/Composer-CkcwbQhG.css +0 -1
- package/dist/core/web/public/next/assets/Composer.module-DJMdrqNt.js +0 -2
- package/dist/core/web/public/next/assets/DiffPane-5uLKa0au.js +0 -2
- package/dist/core/web/public/next/assets/DiffPane-5uLKa0au.js.map +0 -1
- package/dist/core/web/public/next/assets/DiffPane-Csboxnn0.css +0 -1
- package/dist/core/web/public/next/assets/DockHost-BGnTgdIb.css +0 -1
- package/dist/core/web/public/next/assets/DockHost-CBV_6Ehi.js +0 -2
- package/dist/core/web/public/next/assets/DockHost-CBV_6Ehi.js.map +0 -1
- package/dist/core/web/public/next/assets/FleetSection-DuJcQCGg.js +0 -3
- package/dist/core/web/public/next/assets/FleetSection-DuJcQCGg.js.map +0 -1
- package/dist/core/web/public/next/assets/FleetSection-w9AjqfTR.css +0 -1
- package/dist/core/web/public/next/assets/Funnel-qX3Hlbce.js +0 -2
- package/dist/core/web/public/next/assets/Funnel-qX3Hlbce.js.map +0 -1
- package/dist/core/web/public/next/assets/GearTray-kVGP1coG.js +0 -2
- package/dist/core/web/public/next/assets/GearTray-kVGP1coG.js.map +0 -1
- package/dist/core/web/public/next/assets/GrowthSection-CYxJ9MH5.js +0 -2
- package/dist/core/web/public/next/assets/GrowthSection-CYxJ9MH5.js.map +0 -1
- package/dist/core/web/public/next/assets/HandoffDialog-DHZo3Ev0.js +0 -2
- package/dist/core/web/public/next/assets/HandoffDialog-DHZo3Ev0.js.map +0 -1
- package/dist/core/web/public/next/assets/MatrixHeatmap-B_JWCjZx.js +0 -2
- package/dist/core/web/public/next/assets/MatrixHeatmap-B_JWCjZx.js.map +0 -1
- package/dist/core/web/public/next/assets/McpSection-1UncWBpW.js +0 -2
- package/dist/core/web/public/next/assets/MessageMarkdown-C4pl9LI5.js +0 -3
- package/dist/core/web/public/next/assets/MessageMarkdown-Czcl3L_Q.css +0 -1
- package/dist/core/web/public/next/assets/MindSection-C3fCw_fD.js +0 -2
- package/dist/core/web/public/next/assets/MindSection-C3fCw_fD.js.map +0 -1
- package/dist/core/web/public/next/assets/MutationTokenDialog-Dn0tzCrh.js +0 -2
- package/dist/core/web/public/next/assets/MutationTokenDialog-Dn0tzCrh.js.map +0 -1
- package/dist/core/web/public/next/assets/NeedsYouDrawer-CuNYJCJ0.js +0 -2
- package/dist/core/web/public/next/assets/NeedsYouDrawer-CuNYJCJ0.js.map +0 -1
- package/dist/core/web/public/next/assets/NeedsYouDrawer-SkhvvdLb.css +0 -1
- package/dist/core/web/public/next/assets/NewChatDialog-B_BsS9pr.js +0 -3
- package/dist/core/web/public/next/assets/OnboardingFlow-CtCPpgoX.js +0 -2
- package/dist/core/web/public/next/assets/PreviewPane-Cx6RRwzM.js +0 -2
- package/dist/core/web/public/next/assets/PreviewPane-Cx6RRwzM.js.map +0 -1
- package/dist/core/web/public/next/assets/PreviewPane-WQJFjSXe.css +0 -1
- package/dist/core/web/public/next/assets/RailStatus-BWs6xevj.js +0 -2
- package/dist/core/web/public/next/assets/RailStatus-BWs6xevj.js.map +0 -1
- package/dist/core/web/public/next/assets/SeatSelector-DaUO-zoK.js +0 -3
- package/dist/core/web/public/next/assets/SessionSearch-B5aQ0BCM.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-CuESA30a.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-CuESA30a.js.map +0 -1
- package/dist/core/web/public/next/assets/Sidebar-DfD8MNZt.css +0 -1
- package/dist/core/web/public/next/assets/Sidebar-q11dtYZf.js +0 -3
- package/dist/core/web/public/next/assets/Sidebar-q11dtYZf.js.map +0 -1
- package/dist/core/web/public/next/assets/StepBand-B1W7rU70.js +0 -2
- package/dist/core/web/public/next/assets/StepBand-B1W7rU70.js.map +0 -1
- package/dist/core/web/public/next/assets/Swimlane-BR40WVcj.js +0 -2
- package/dist/core/web/public/next/assets/Swimlane-BR40WVcj.js.map +0 -1
- package/dist/core/web/public/next/assets/TerminalPane-CPKUTD9K.js +0 -7
- package/dist/core/web/public/next/assets/TerminalPane-CPKUTD9K.js.map +0 -1
- package/dist/core/web/public/next/assets/TerminalPane-CmE2QL-Q.css +0 -1
- package/dist/core/web/public/next/assets/UniverseConsoleApp-DKqAf-eD.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-B6fR3cru.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-B6fR3cru.js.map +0 -1
- package/dist/core/web/public/next/assets/UsageSection-DwgNOYvi.css +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-C0V221WL.css +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-bjARLPwN.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-bjARLPwN.js.map +0 -1
- package/dist/core/web/public/next/assets/Workspace-DQXMVDf7.css +0 -1
- package/dist/core/web/public/next/assets/Workspace-DtJb5tiM.js +0 -10
- package/dist/core/web/public/next/assets/Workspace-DtJb5tiM.js.map +0 -1
- package/dist/core/web/public/next/assets/actions-Bszsdpms.js +0 -2
- package/dist/core/web/public/next/assets/actions-Bszsdpms.js.map +0 -1
- package/dist/core/web/public/next/assets/actions-Dug8YIf0.css +0 -1
- package/dist/core/web/public/next/assets/capacity-strip-model-BR539Ci8.js +0 -2
- package/dist/core/web/public/next/assets/capacity-strip-model-BR539Ci8.js.map +0 -1
- package/dist/core/web/public/next/assets/charts-D4NaLaXA.js +0 -2
- package/dist/core/web/public/next/assets/charts-D4NaLaXA.js.map +0 -1
- package/dist/core/web/public/next/assets/colors-B-vHsX1S.js +0 -2
- package/dist/core/web/public/next/assets/colors-B-vHsX1S.js.map +0 -1
- package/dist/core/web/public/next/assets/context-model-Bto3LZn2.js +0 -2
- package/dist/core/web/public/next/assets/context-model-Bto3LZn2.js.map +0 -1
- package/dist/core/web/public/next/assets/context-queries-DVdSLrYy.js +0 -2
- package/dist/core/web/public/next/assets/context-queries-DVdSLrYy.js.map +0 -1
- package/dist/core/web/public/next/assets/context.module-B_k02HNU.js +0 -2
- package/dist/core/web/public/next/assets/context.module-B_k02HNU.js.map +0 -1
- package/dist/core/web/public/next/assets/dock-icons-oG8d7mNZ.js +0 -2
- package/dist/core/web/public/next/assets/dock-icons-oG8d7mNZ.js.map +0 -1
- package/dist/core/web/public/next/assets/dock-panes-BNTLvX7G.js +0 -2
- package/dist/core/web/public/next/assets/fleet-model-ClBbQ0Uj.js +0 -2
- package/dist/core/web/public/next/assets/fleet-model-ClBbQ0Uj.js.map +0 -1
- package/dist/core/web/public/next/assets/format-BNYWGJat.js +0 -2
- package/dist/core/web/public/next/assets/format-BNYWGJat.js.map +0 -1
- package/dist/core/web/public/next/assets/global-DVgMguLr.js +0 -2
- package/dist/core/web/public/next/assets/global-DVgMguLr.js.map +0 -1
- package/dist/core/web/public/next/assets/live-model-Dm6ITHvX.js +0 -2
- package/dist/core/web/public/next/assets/live-model-Dm6ITHvX.js.map +0 -1
- package/dist/core/web/public/next/assets/mind-B-lYfSUJ.css +0 -1
- package/dist/core/web/public/next/assets/mind.module-BzhAlg3e.js +0 -2
- package/dist/core/web/public/next/assets/mind.module-BzhAlg3e.js.map +0 -1
- package/dist/core/web/public/next/assets/mutations-C6E10SPX.js +0 -2
- package/dist/core/web/public/next/assets/primitives-DQvYikjm.js +0 -1
- package/dist/core/web/public/next/assets/seat-subscription-CrWOV4Qc.js +0 -2
- package/dist/core/web/public/next/assets/seat-subscription-CrWOV4Qc.js.map +0 -1
- package/dist/core/web/public/next/assets/slots-W8gkmdfE.js +0 -3
- package/dist/core/web/public/next/assets/surface-data-ZtCegYMQ.js +0 -2
- package/dist/core/web/public/next/assets/surface-data-ZtCegYMQ.js.map +0 -1
- package/dist/core/web/public/next/assets/tasks-model-CnYQzgxk.js +0 -2
- package/dist/core/web/public/next/assets/types-Dypu22dp.js +0 -3
- package/dist/core/web/public/next/assets/types-Dypu22dp.js.map +0 -1
- package/dist/core/web/public/next/assets/use-guarded-action-CO_aAsed.js +0 -2
- package/dist/core/web/public/next/assets/use-guarded-action-CO_aAsed.js.map +0 -1
- package/dist/core/web/public/next/assets/use-token-gate-DYA2-ALC.js +0 -2
- package/dist/core/web/public/next/assets/use-token-gate-DYA2-ALC.js.map +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-B8E5mJgM.css +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-MEXvDxDL.js +0 -2
- package/dist/core/web/public/next/assets/useChartWidth-MEXvDxDL.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-model-E98kXngO.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-E98kXngO.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-store-BgKx_YGs.js +0 -2
- package/dist/core/web/public/next/assets/verse-store-BgKx_YGs.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-ui-store-KTTKfv-T.js +0 -3
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{D as e,M as t,O as n,P as r,at as i,it as a,s as o,u as s}from"./global-DVgMguLr.js";import{C as c,d as l,n as u,r as d}from"./verse-queries-BjQoOTwv.js";import{f}from"./verse-store-BgKx_YGs.js";var p=`verse-preferences`,m=`verse-memory:`;async function h(e){try{let t=await e.json();if(typeof t.error==`string`&&t.error)return t.error;if(typeof t.note==`string`&&t.note)return t.note}catch{}return null}async function g(e,r){let i=await fetch(e,{method:`GET`,credentials:`same-origin`,headers:{"x-ashlr-read-client":n()},signal:r});if(i.status===401)throw t(),new o(`Read session expired.`,401,e);if(!i.ok){let t=await h(i);throw new o(`GET ${e} failed (HTTP ${i.status})${t?`: ${t}`:``}.`,i.status,e,t)}return await i.json()}async function _(t,n){let i=e();if(!i)throw new d;let a=await s(t,n,i);return r(),a}function v(e,t){let n=t.toString();return n?`${e}?${n}`:e}async function y(e,t){let n={mode:t},r=await _(c(e,`/context-mode`),n);return f(e,r),a(u),r}async function b(e,t={}){let n={};t.includeLastAssistant===!0&&(n.includeLastAssistant=!0);let r=typeof t.focus==`string`?t.focus.trim().slice(0,500):``;return r&&(n.focus=r),_(c(e,`/handoff-preview`),n)}async function x(e){let{source:t}=e,n={projectPath:t.projectPath,seatId:e.seatId,handoffFromSessionId:t.id},r=(t.extraRoots??[]).filter(e=>typeof e==`string`&&e.length>0&&e!==t.projectPath);r.length>0&&(n.extraRoots=r),e.model&&(n.model=e.model),e.contextMode&&(n.contextMode=e.contextMode);let i=e.title?.trim();i&&(n.title=i);let a=await _(`/api/verse/sessions`,n);return l(),a}function S(e){return g(`/api/verse/preferences`,e)}var C={key:p,fetch:e=>S(e)};async function w(e){let t=await _(`/api/verse/preferences`,e);return a(p),i(m),t}function T(e){let t=new URLSearchParams,n=e.workspaceId?.trim()??``,r=e.projectPath?.trim()??``,i=(e.extraRoots??[]).map(e=>e.trim()).filter(e=>e.length>0);if(n){if(r||i.length>0)throw Error(`Estimate a workspace or a folder set, not both.`);t.set(`workspaceId`,n)}else if(r){t.set(`projectPath`,r);for(let e of i)t.append(`extraRoots`,e)}else throw Error(`Pick a project or a workspace to estimate.`);return v(`/api/verse/context-fit`,t)}function E(e,t){return g(T(e),t)}function D(e,t,n){let r=new URLSearchParams({q:e});return typeof t==`number`&&Number.isFinite(t)&&r.set(`limit`,String(Math.max(1,Math.min(50,Math.floor(t))))),g(v(`/api/verse/search`,r),n)}function O(e){return`${m}${e}`}function k(e,t){return g(v(`/api/verse/memory`,new URLSearchParams({projectPath:e})),t)}function A(e){return{key:O(e),fetch:t=>k(e,t)}}async function j(e,t){let n=await _(`/api/verse/memory`,{projectPath:e,content:t});return a(O(e)),n}export{k as a,w as c,j as d,S as i,C as l,E as n,D as o,b as r,y as s,x as t,A as u};
|
|
2
|
-
//# sourceMappingURL=context-queries-DVdSLrYy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-queries-DVdSLrYy.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/context/context-queries.ts"],"sourcesContent":["/**\n * routes/verse/context/context-queries.ts — every V3.9 context-orchestration\n * call the Verse console makes (docs/VERSE-CONTEXT.md): context mode, handoff\n * preview + handoff creation, preferences, context fit, session search and\n * shared project memory.\n *\n * Same conventions as ../verse-queries.ts, deliberately:\n *\n * - WRITES pull the held mutation token from auth-store, throw\n * `VerseMutationLockedError` (the SAME class, so ChatSection's `fail`\n * recognises it) when none is held, touch the hold on success, and\n * invalidate exactly the cache keys they obviously change.\n * - READS carry the per-tab read-client proof and report an expired read\n * session, exactly like `apiGet`. They are written out here rather than\n * routed through `apiGet` for one reason: `apiGet` discards the response\n * body on a non-2xx, and the routes below answer a refused read (\"path is\n * not a directory\", \"q must be at least 2 characters\") with a sentence\n * written for a person. Those sentences travel as `ApiError.detail`, the\n * field `describeControlError` already prefers.\n *\n * NOTHING HERE SPENDS. Every route below is deterministic server work\n * (preferences file, git ls-files, events.jsonl scans, a memory file). The\n * one model call a context feature can cause — \"ask this seat to summarize\"\n * — goes through the ordinary `sendVerseTurn`, i.e. through the engine's\n * `startTurn` chokepoint and the local-only policy, never through this file.\n *\n * `handoff-preview` is a POST although it only reads: it spawns\n * `git diff --stat` per root, and the API keeps every spawning route behind\n * the dispatch flag and the mutation token. It therefore needs the token like\n * any write, and the dialog says so.\n */\nimport type { VerseCreateSessionRequest, VerseSession } from '../../../data/api-types.js';\nimport type {\n VerseContextFit,\n VerseContextMode,\n VerseContextModeRequest,\n VerseHandoffPreview,\n VerseHandoffPreviewRequest,\n VersePreferences,\n VersePreferencesUpdate,\n VerseProjectMemory,\n VerseProjectMemoryWrite,\n VerseSearchResponse,\n} from '../../../../core/verse/types.js';\nimport { getMutationToken, reportSessionExpired, getReadClientProof, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate, invalidatePrefix } from '../../../data/cache.js';\nimport { ApiError, apiPost } from '../../../data/client.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { invalidateVerseLists, verseSessionPath, VERSE_SESSIONS_KEY, VerseMutationLockedError } from '../verse-queries.js';\nimport { setVerseSession } from '../verse-store.js';\n\nexport const VERSE_PREFERENCES_KEY = 'verse-preferences';\n/** Prefix of every per-project memory key; `invalidatePrefix` reaches all of them. */\nexport const VERSE_MEMORY_KEY_PREFIX = 'verse-memory:';\n/** Prefix of every context-fit key (one per root set). */\nexport const VERSE_CONTEXT_FIT_KEY_PREFIX = 'verse-context-fit:';\n\n// ---------------------------------------------------------------------------\n// Transport\n// ---------------------------------------------------------------------------\n\n/** The server's own refusal sentence, when the body carried one. */\nasync function refusalDetail(res: Response): Promise<string | null> {\n try {\n const body = (await res.json()) as { error?: unknown; note?: unknown };\n if (typeof body.error === 'string' && body.error) return body.error;\n if (typeof body.note === 'string' && body.note) return body.note;\n } catch {\n /* not JSON — the status alone has to do */\n }\n return null;\n}\n\n/**\n * GET with the read-client proof. Behaves like `apiGet` (401 reports the\n * expired read session) but keeps the server's refusal sentence.\n */\nasync function getJson<T>(path: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(path, {\n method: 'GET',\n credentials: 'same-origin',\n headers: { 'x-ashlr-read-client': getReadClientProof() },\n signal,\n });\n if (res.status === 401) {\n reportSessionExpired();\n throw new ApiError('Read session expired.', 401, path);\n }\n if (!res.ok) {\n const detail = await refusalDetail(res);\n throw new ApiError(`GET ${path} failed (HTTP ${res.status})${detail ? `: ${detail}` : ''}.`, res.status, path, detail);\n }\n return (await res.json()) as T;\n}\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new VerseMutationLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\nfunction withQuery(path: string, params: URLSearchParams): string {\n const query = params.toString();\n return query ? `${path}?${query}` : path;\n}\n\n// ---------------------------------------------------------------------------\n// Context mode\n// ---------------------------------------------------------------------------\n\n/**\n * Switch a session between `standard` and `expansive`. Applies from the next\n * turn (it only changes CLI flags, never prompt content, so the provider's\n * prompt cache survives the switch).\n *\n * The returned record is also written into verse-store, so the meter, the\n * mode chip and the composer hint all move on this response rather than on\n * the next list refresh — whichever component made the call.\n */\nexport async function setSessionContextMode(sessionId: string, mode: VerseContextMode): Promise<VerseSession> {\n const body: VerseContextModeRequest = { mode };\n const session = await post<VerseSession>(verseSessionPath(sessionId, '/context-mode'), body);\n setVerseSession(sessionId, session);\n invalidate(VERSE_SESSIONS_KEY);\n return session;\n}\n\n// ---------------------------------------------------------------------------\n// Handoff\n// ---------------------------------------------------------------------------\n\n/** Longest focus line the API accepts (types.ts VerseHandoffPreviewRequest). */\nexport const HANDOFF_FOCUS_MAX_CHARS = 500;\n\n/**\n * Build the deterministic handoff note for a session. Zero spend: the server\n * reads events.jsonl and a bounded `git diff --stat` per root.\n *\n * Only keys that carry a value are sent — the route rejects unknown keys, and\n * `includeLastAssistant: false` is the default anyway, so omitting it keeps\n * the body minimal and the server's validation the single authority.\n */\nexport async function fetchHandoffPreview(sessionId: string, req: VerseHandoffPreviewRequest = {}): Promise<VerseHandoffPreview> {\n const body: VerseHandoffPreviewRequest = {};\n if (req.includeLastAssistant === true) body.includeLastAssistant = true;\n const focus = typeof req.focus === 'string' ? req.focus.trim().slice(0, HANDOFF_FOCUS_MAX_CHARS) : '';\n if (focus) body.focus = focus;\n return post<VerseHandoffPreview>(verseSessionPath(sessionId, '/handoff-preview'), body);\n}\n\nexport interface HandoffSessionInput {\n /** The session being handed off. Its PINNED roots are reused, see below. */\n source: VerseSession;\n seatId: string;\n model?: string;\n contextMode?: VerseContextMode;\n /** Optional title; absent → the server titles it from the first message. */\n title?: string;\n}\n\n/**\n * Create the session a handoff continues in. FREE: creating a session spawns\n * nothing. The handoff text is NOT sent here — the caller pre-fills the new\n * chat's composer and the operator presses send, so the first (spent) turn is\n * always one the operator read.\n *\n * Roots: the source's PINNED roots (`projectPath` + `extraRoots`), never its\n * `workspaceId`. A session keeps the roots it was created with even if the\n * named workspace was later edited or deleted (types.ts VerseSession\n * `extraRoots`); re-resolving the workspace here would let the continuation\n * silently gain or lose a folder the conversation was about — or fail\n * outright on a deleted workspace. The API also refuses the two spellings\n * mixed, so it is one or the other, and the pinned set is the honest one.\n *\n * The server resolves `handoffFrom.title` from `handoffFromSessionId` itself;\n * nothing about provenance is taken from this body.\n */\nexport async function createHandoffSession(input: HandoffSessionInput): Promise<VerseSession> {\n const { source } = input;\n const req: VerseCreateSessionRequest = {\n projectPath: source.projectPath,\n seatId: input.seatId,\n handoffFromSessionId: source.id,\n };\n const extras = (source.extraRoots ?? []).filter((root) => typeof root === 'string' && root.length > 0 && root !== source.projectPath);\n if (extras.length > 0) req.extraRoots = extras;\n if (input.model) req.model = input.model;\n if (input.contextMode) req.contextMode = input.contextMode;\n const title = input.title?.trim();\n if (title) req.title = title;\n const session = await post<VerseSession>('/api/verse/sessions', req);\n invalidateVerseLists();\n return session;\n}\n\n// ---------------------------------------------------------------------------\n// Preferences\n// ---------------------------------------------------------------------------\n\nexport function fetchPreferences(signal?: AbortSignal): Promise<VersePreferences> {\n return getJson<VersePreferences>('/api/verse/preferences', signal);\n}\n\nexport const versePreferencesQuery: QueryDef<VersePreferences> = {\n key: VERSE_PREFERENCES_KEY,\n fetch: (signal) => fetchPreferences(signal),\n};\n\n/**\n * Change ONE standing preference (the three request forms are exclusive, so a\n * body never changes more than the operator clicked). Memory records carry an\n * `enabled` flag derived from these preferences, so every cached memory read\n * is refreshed too.\n */\nexport async function updatePreferences(update: VersePreferencesUpdate): Promise<VersePreferences> {\n const prefs = await post<VersePreferences>('/api/verse/preferences', update);\n invalidate(VERSE_PREFERENCES_KEY);\n invalidatePrefix(VERSE_MEMORY_KEY_PREFIX);\n return prefs;\n}\n\n// ---------------------------------------------------------------------------\n// Context fit\n// ---------------------------------------------------------------------------\n\nexport interface ContextFitQuery {\n workspaceId?: string;\n projectPath?: string;\n extraRoots?: string[];\n}\n\n/**\n * The query string for a fit request: a workspace id ALONE, or a project path\n * with each extra root as its own repeated `extraRoots` parameter (a path may\n * contain a comma, so a joined list could not be split back honestly). The\n * API refuses the two spellings mixed — refused here first, with the reason.\n */\nexport function contextFitPath(q: ContextFitQuery): string {\n const params = new URLSearchParams();\n const workspaceId = q.workspaceId?.trim() ?? '';\n const projectPath = q.projectPath?.trim() ?? '';\n const extras = (q.extraRoots ?? []).map((r) => r.trim()).filter((r) => r.length > 0);\n if (workspaceId) {\n if (projectPath || extras.length > 0) throw new Error('Estimate a workspace or a folder set, not both.');\n params.set('workspaceId', workspaceId);\n } else if (projectPath) {\n params.set('projectPath', projectPath);\n for (const root of extras) params.append('extraRoots', root);\n } else {\n throw new Error('Pick a project or a workspace to estimate.');\n }\n return withQuery('/api/verse/context-fit', params);\n}\n\n/** How big the reachable code is, in estimated tokens. Zero spend (git ls-files + stat). */\nexport function fetchContextFit(q: ContextFitQuery, signal?: AbortSignal): Promise<VerseContextFit> {\n return getJson<VerseContextFit>(contextFitPath(q), signal);\n}\n\n/** Cache-backed form, keyed by the exact root set, for pickers that re-render often. */\nexport function verseContextFitQuery(q: ContextFitQuery): QueryDef<VerseContextFit> {\n const path = contextFitPath(q);\n return { key: `${VERSE_CONTEXT_FIT_KEY_PREFIX}${path}`, fetch: (signal) => getJson<VerseContextFit>(path, signal) };\n}\n\n// ---------------------------------------------------------------------------\n// Session search\n// ---------------------------------------------------------------------------\n\n/** The API caps `limit` at 50 (session-search.ts); asking for more is refused, so clamp. */\nexport const SEARCH_LIMIT_MAX = 50;\n\n/** Keyword search over past sessions' messages. Zero spend — a bounded scan of events.jsonl. */\nexport function searchSessions(q: string, limit?: number, signal?: AbortSignal): Promise<VerseSearchResponse> {\n const params = new URLSearchParams({ q });\n if (typeof limit === 'number' && Number.isFinite(limit)) {\n params.set('limit', String(Math.max(1, Math.min(SEARCH_LIMIT_MAX, Math.floor(limit)))));\n }\n return getJson<VerseSearchResponse>(withQuery('/api/verse/search', params), signal);\n}\n\n// ---------------------------------------------------------------------------\n// Shared project memory\n// ---------------------------------------------------------------------------\n\n/**\n * The memory record as the browser receives it. The server's public-JSON\n * sanitizer rewrites the home folder as `~` and secret-shaped text as\n * `[REDACTED]` on the way out, so `content` can differ from the file on disk;\n * the route then adds `contentSanitized: true` (absent otherwise). Declared\n * here, not in types.ts: that contract is frozen and the flag is additive.\n */\nexport type VerseProjectMemoryView = VerseProjectMemory & { contentSanitized?: boolean };\n\nexport function verseProjectMemoryKey(projectPath: string): string {\n return `${VERSE_MEMORY_KEY_PREFIX}${projectPath}`;\n}\n\nexport function fetchProjectMemory(projectPath: string, signal?: AbortSignal): Promise<VerseProjectMemoryView> {\n return getJson<VerseProjectMemoryView>(withQuery('/api/verse/memory', new URLSearchParams({ projectPath })), signal);\n}\n\nexport function verseProjectMemoryQuery(projectPath: string): QueryDef<VerseProjectMemoryView> {\n return { key: verseProjectMemoryKey(projectPath), fetch: (signal) => fetchProjectMemory(projectPath, signal) };\n}\n\n/**\n * Replace MEMORY.md for a project (atomic, 0600 on the server, capped at\n * VERSE_MEMORY_MAX_BYTES). `''` clears it. Chats already open keep the block\n * pinned in their instructions at creation; they see the change when they\n * next read the file.\n */\nexport async function writeProjectMemory(projectPath: string, content: string): Promise<VerseProjectMemoryView> {\n const body: VerseProjectMemoryWrite = { projectPath, content };\n const memory = await post<VerseProjectMemoryView>('/api/verse/memory', body);\n invalidate(verseProjectMemoryKey(projectPath));\n return memory;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{$ as e,K as t,W as n,Y as r,Z as i,nt as a}from"./global-DVgMguLr.js";import{a as o,o as s}from"./types-Dypu22dp.js";import{r as c}from"./verse-store-BgKx_YGs.js";import{c as l}from"./verse-model-E98kXngO.js";var u=/\s·\spart\s(\d+)$/;function d(e){let t=e.replace(/\s+/g,` `).trim(),n=u.exec(t),r=(n?t.slice(0,n.index):t)||`Untitled chat`,i=` · part ${n?Number.parseInt(n[1],10)+1:2}`,a=120-i.length;return`${r.length>a?`${r.slice(0,a-1).trimEnd()}…`:r}${i}`}function f(e,n){if(!e)return null;let r=e.models.find(e=>e.id===n);if(r)return r;let i=t(n);return e.models.find(e=>t(e.id)===i)??null}function p(e,t){return e?e.health.state===`unavailable`?`${e.label} is unavailable: ${e.health.summary??`no reason given`}.`:t?t.unavailableReason?`${t.label} cannot run on ${e.label}: ${t.unavailableReason}.`:null:`Pick a model on this seat.`:`Pick a seat to continue on.`}function m(e,t){let n=e.find(e=>e.id===t.seatId);if(n&&n.health.state!==`unavailable`){let e=f(n,t.model);if(e&&!e.unavailableReason)return{seatId:n.id,model:e.id};let r=n.models.find(e=>!e.unavailableReason);if(r)return{seatId:n.id,model:r.id}}for(let t of e){if(t.health.state===`unavailable`)continue;let e=t.models.find(e=>!e.unavailableReason);if(e)return{seatId:t.id,model:e.id}}return null}function h(e){let{option:r,target:i,source:a,preferences:o}=e,s=e=>e!==void 0&&n(r,e)!==null;if(i!==null&&i.seatId===a.seatId&&t(i.model)===t(a.model)&&s(a.contextMode??`standard`))return a.contextMode??`standard`;let c=i?o?.seats[i.seatId]?.contextMode:void 0;return s(c)?c:`standard`}function g(e,t){return n(e,t)===null?void 0:t}function _(e,t){let r=n(e,t);if(!r)return null;let i=`${c(r.contextWindow)} window`;return r.autoCompactAt===null?i:`${i} · compacts ≈${c(r.autoCompactAt)}`}function v(e,t,o,s){let l=r(e),u=a(s),d=l+u,f=i(l,t,u),p=`~${c(l)} tokens of handoff + an estimated ~${c(u)} of fixed prompt`,m=n(t,`standard`),h=m?m.autoCompactAt??m.contextWindow:null,g={verdict:f,handoffTokens:l,overheadTokens:u,needTokens:d};switch(f){case`fits`:return{...g,tone:`ok`,text:`Fits: ${p} leaves most of the budget before the first compaction.`};case`tight`:return{...g,tone:`warn`,text:`Tight: ${p} is ${Math.round(d/(h??d)*100)}% of where this model compacts — the new chat will compact early.`};case`expansive`:return o===`expansive`?{...g,tone:`warn`,text:`Fits only the expansive budget: ${p}.`}:{...g,tone:`danger`,text:`Too big for the standard budget (${p}). Switch to Expansive, or trim the handoff.`};case`split`:return{...g,tone:`danger`,text:`Too big for one context on this model (${p}). Trim the handoff or pick a seat with a larger window.`};default:return{...g,verdict:null,tone:`unknown`,text:`This model’s window is not known, so there is no fit estimate.`}}}function y(t){return e(t)}function b(e,t){return e===`local`?`Runs on this machine with ${t}; it spends no subscription usage.`:`Sends one turn to ${t} and spends from its usage. The automatic preview costs nothing.`}var x=typeof TextEncoder==`function`?new TextEncoder:null;function S(e){if(x)return x.encode(e).length;let t=0;for(let n of e){let e=n.codePointAt(0)??0;t+=e<128?1:e<2048?2:e<65536?3:4}return t}var C=65536,w=s;function T(e){return S(JSON.stringify(e)??``)}function E(e){if(!Number.isFinite(e)||e<0)return`—`;if(e<1024)return`${e} B`;let t=e/1024;return`${t<10?t.toFixed(1).replace(/\.0$/,``):Math.round(t)} KB`}function D(e,t=Date.now()){if(!e)return null;let n=l(e,t);return n?n===`now`?`just now`:/^\d/.test(n)?`${n} ago`:`on ${n}`:null}var O=[{label:`a private key`,re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:`an Anthropic API key`,re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:`an OpenAI-style API key`,re:/\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/},{label:`an xAI API key`,re:/\bxai-[A-Za-z0-9]{32,}/},{label:`a GitHub token`,re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{30,}|\bgithub_pat_[A-Za-z0-9_]{40,}/},{label:`an AWS access key`,re:/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/},{label:`a Slack token`,re:/\bxox[abprs]-[A-Za-z0-9-]{10,}/},{label:`a Google API key`,re:/\bAIza[0-9A-Za-z_-]{35}\b/},{label:`a Stripe secret key`,re:/\b(?:sk|rk)_live_[0-9A-Za-z]{20,}/}],k=o,A=`Reading and editing memory here spends nothing. On a paid seat it is not free to use: each new chat’s system prompt carries a memory block of up to ${k/1024} KB, re-sent every turn (cached after the first), and the agent reads and updates MEMORY.md as it works. Local seats spend nothing.`;function j(e){for(let{label:t,re:n}of O)if(n.test(e))return t;return null}var M=`[REDACTED]`;function N(e){return e.split(M).length-1}function P(e){let t=[];for(let n of e.toLowerCase().split(/\s+/))n&&!t.includes(n)&&t.push(n);return t}function F(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function I(e,t){let n=[...t].filter(e=>e.length>0).sort((e,t)=>t.length-e.length);if(n.length===0||e.length===0)return e?[{text:e,match:!1}]:[];let r=new RegExp(n.map(F).join(`|`),`gi`),i=[],a=0;for(let t of e.matchAll(r)){let n=t.index??0;t[0].length!==0&&(n>a&&i.push({text:e.slice(a,n),match:!1}),i.push({text:t[0],match:!0}),a=n+t[0].length)}return a<e.length&&i.push({text:e.slice(a),match:!1}),i}function L(e){let t=new Map;for(let n of e){let e=t.get(n.sessionId);if(!e){t.set(n.sessionId,{sessionId:n.sessionId,title:n.title,projectPath:n.projectPath,engine:n.engine,top:n,count:1,latestAt:n.at});continue}e.count+=1,n.at>e.latestAt&&(e.latestAt=n.at)}return[...t.values()]}var R={label:`_label_1j3uu_14`,optional:`_optional_1j3uu_23`,field:`_field_1j3uu_25`,row:`_row_1j3uu_26`,input:`_input_1j3uu_27`,textarea:`_textarea_1j3uu_30`,hint:`_hint_1j3uu_59`,locked:`_locked_1j3uu_61`,warn:`_warn_1j3uu_62`,error:`_error_1j3uu_70`,primary:`_primary_1j3uu_80`,secondary:`_secondary_1j3uu_81`,cancel:`_cancel_1j3uu_82`,link:`_link_1j3uu_109`,small:`_small_1j3uu_121`,pending:`_pending_1j3uu_124`,"context-pulse":`_context-pulse_1j3uu_1`,dialogWidth:`_dialogWidth_1j3uu_145`,form:`_form_1j3uu_146`,aside:`_aside_1j3uu_150`,fit:`_fit_1j3uu_172`,"fit-ok":`_fit-ok_1j3uu_180`,"fit-warn":`_fit-warn_1j3uu_181`,"fit-danger":`_fit-danger_1j3uu_182`,"fit-unknown":`_fit-unknown_1j3uu_183`,cost:`_cost_1j3uu_185`,actions:`_actions_1j3uu_192`,blocked:`_blocked_1j3uu_193`,memory:`_memory_1j3uu_207`,memoryHead:`_memoryHead_1j3uu_215`,memoryState:`_memoryState_1j3uu_228`,memoryHeadAction:`_memoryHeadAction_1j3uu_229`,memoryBody:`_memoryBody_1j3uu_242`,memoryMeta:`_memoryMeta_1j3uu_257`,memoryFiles:`_memoryFiles_1j3uu_258`,memoryActions:`_memoryActions_1j3uu_265`,spacer:`_spacer_1j3uu_266`,danger:`_danger_1j3uu_267`,counter:`_counter_1j3uu_269`,counterOver:`_counterOver_1j3uu_270`,search:`_search_1j3uu_276`,searchBox:`_searchBox_1j3uu_277`,searchHead:`_searchHead_1j3uu_284`,searchCount:`_searchCount_1j3uu_298`,searchNote:`_searchNote_1j3uu_304`,searchError:`_searchError_1j3uu_305`,hits:`_hits_1j3uu_315`,hit:`_hit_1j3uu_315`,hitMarker:`_hitMarker_1j3uu_332`,hitTitle:`_hitTitle_1j3uu_339`,hitTime:`_hitTime_1j3uu_348`,hitSnippet:`_hitSnippet_1j3uu_355`,hitWho:`_hitWho_1j3uu_365`,"engine-claude":`_engine-claude_1j3uu_374`,"engine-codex":`_engine-codex_1j3uu_375`,"engine-grok":`_engine-grok_1j3uu_376`,"engine-local":`_engine-local_1j3uu_377`};export{p as C,j as S,S as T,y as _,A as a,g as b,h as c,L as d,v as f,f as g,T as h,w as i,m as l,I as m,C as n,M as o,d as p,k as r,_ as s,R as t,E as u,N as v,b as w,P as x,D as y};
|
|
2
|
-
//# sourceMappingURL=context.module-B_k02HNU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.module-B_k02HNU.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/context/context-model.ts","../../../../../../src/web-ui/routes/verse/context/context.module.css"],"sourcesContent":["/**\n * routes/verse/context/context-model.ts — the pure half of the context\n * components (handoff dialog, memory panel, session search). No React, no\n * I/O, so every rule the components render is pinned by a plain unit test.\n *\n * Every number shown here comes from context-math.ts (the one module the\n * server and the browser share), so \"compacts ≈367k\" in the handoff dialog is\n * the same figure the meter and the seat picker show.\n */\nimport type { VerseEngine, VerseModelOption, VerseSeat, VerseSession } from '../../../data/api-types.js';\nimport type {\n VerseContextMode,\n VerseFitVerdict,\n VersePreferences,\n VerseSearchHit,\n} from '../../../../core/verse/types.js';\nimport { VERSE_MEMORY_BLOCK_MAX_BYTES, VERSE_MEMORY_BODY_MAX_BYTES } from '../../../../core/verse/types.js';\nimport {\n budgetFor,\n canonicalModelId,\n estimateTokensFromChars,\n fitVerdict,\n hasExpansiveMode,\n sessionOverheadTokens,\n} from '../../../../core/verse/context-math.js';\nimport { formatRelative } from '../verse-model.js';\nimport { formatTokens } from '../verse-store.js';\n\n// ---------------------------------------------------------------------------\n// Handoff\n// ---------------------------------------------------------------------------\n\n// The canned \"Ask this seat to summarize first\" turn is\n// VERSE_HANDOFF_SUMMARY_REQUEST in core/verse/types.ts, not a copy here: the\n// handoff builder on the server matches that exact text to keep it OUT of the\n// new session's \"latest requests\" (otherwise the new chat is told to write\n// another handoff note). A local copy that drifted by one character would\n// silently defeat that filter.\n\n/** Mirrors session-engine's TITLE_MAX so a title we send is never cut by the server instead. */\nexport const TITLE_MAX_CHARS = 120;\n\nconst PART_SUFFIX = /\\s·\\spart\\s(\\d+)$/;\n\n/**\n * Title for the session a handoff continues in: \"Fix the login bug · part 2\".\n * A continuation of a continuation counts up rather than stacking suffixes, so\n * the sidebar reads \"part 3\", never \"part 2 · part 2\".\n */\nexport function handoffTitle(sourceTitle: string): string {\n const collapsed = sourceTitle.replace(/\\s+/g, ' ').trim();\n const match = PART_SUFFIX.exec(collapsed);\n const base = (match ? collapsed.slice(0, match.index) : collapsed) || 'Untitled chat';\n const next = match ? Number.parseInt(match[1]!, 10) + 1 : 2;\n const suffix = ` · part ${next}`;\n const room = TITLE_MAX_CHARS - suffix.length;\n const clipped = base.length > room ? `${base.slice(0, room - 1).trimEnd()}…` : base;\n return `${clipped}${suffix}`;\n}\n\n/**\n * A seat's catalog entry for a model id. Exact id first, then canonical — a\n * session created before the alias fix stores `claude-opus-5.5` while the\n * catalog now lists `claude-opus-5-5` (context-math VERSE_MODEL_ID_ALIASES).\n */\nexport function modelOption(seat: VerseSeat | null | undefined, modelId: string): VerseModelOption | null {\n if (!seat) return null;\n const exact = seat.models.find((m) => m.id === modelId);\n if (exact) return exact;\n const canonical = canonicalModelId(modelId);\n return seat.models.find((m) => canonicalModelId(m.id) === canonical) ?? null;\n}\n\n/** Why a seat+model cannot take a new session right now; null when it can. */\nexport function targetUnavailableReason(seat: VerseSeat | null | undefined, option: VerseModelOption | null): string | null {\n if (!seat) return 'Pick a seat to continue on.';\n if (seat.health.state === 'unavailable') return `${seat.label} is unavailable: ${seat.health.summary ?? 'no reason given'}.`;\n if (!option) return 'Pick a model on this seat.';\n if (option.unavailableReason) return `${option.label} cannot run on ${seat.label}: ${option.unavailableReason}.`;\n return null;\n}\n\nexport interface HandoffTarget {\n seatId: string;\n model: string;\n}\n\n/**\n * Where a handoff lands by default: the SAME seat and model (the common case\n * is \"same agent, fresh context\"). When that model can no longer run there,\n * the seat's first runnable model; when the seat itself is gone or\n * unavailable, the first runnable model on any available seat, in the seat\n * list's own order.\n */\nexport function defaultHandoffTarget(seats: readonly VerseSeat[], source: Pick<VerseSession, 'seatId' | 'model'>): HandoffTarget | null {\n const own = seats.find((s) => s.id === source.seatId);\n if (own && own.health.state !== 'unavailable') {\n const same = modelOption(own, source.model);\n if (same && !same.unavailableReason) return { seatId: own.id, model: same.id };\n const runnable = own.models.find((m) => !m.unavailableReason);\n if (runnable) return { seatId: own.id, model: runnable.id };\n }\n for (const seat of seats) {\n if (seat.health.state === 'unavailable') continue;\n const runnable = seat.models.find((m) => !m.unavailableReason);\n if (runnable) return { seatId: seat.id, model: runnable.id };\n }\n return null;\n}\n\n/**\n * The mode a handoff starts in. Only a mode that EXISTS for the target model\n * is ever returned (a mode with no budget would be refused by the server).\n *\n * 1. Same seat and model as the source → keep the source's mode. \"Continue in\n * a fresh chat\" means the same settings with a clean context; an operator\n * who chose expansive for this work chose it for the work.\n * 2. Otherwise the target seat's standing preference (preferences.json).\n * 3. Otherwise standard.\n */\nexport function defaultHandoffMode(input: {\n option: VerseModelOption | null;\n target: HandoffTarget | null;\n source: Pick<VerseSession, 'seatId' | 'model' | 'contextMode'>;\n preferences: VersePreferences | null | undefined;\n}): VerseContextMode {\n const { option, target, source, preferences } = input;\n const valid = (mode: VerseContextMode | undefined): mode is VerseContextMode =>\n mode !== undefined && budgetFor(option, mode) !== null;\n const sameModel = target !== null && target.seatId === source.seatId\n && canonicalModelId(target.model) === canonicalModelId(source.model);\n if (sameModel && valid(source.contextMode ?? 'standard')) return source.contextMode ?? 'standard';\n const preferred = target ? preferences?.seats[target.seatId]?.contextMode : undefined;\n if (valid(preferred)) return preferred;\n return 'standard';\n}\n\n/**\n * The mode to put on the create request, or undefined to leave it to the\n * server. A mode is sent only when the model has a budget for it — which is\n * exactly the server's acceptance rule — so what the dialog shows is what is\n * created, and an unknown-window model is never refused over a mode it could\n * not have.\n */\nexport function requestMode(option: VerseModelOption | null, mode: VerseContextMode): VerseContextMode | undefined {\n return budgetFor(option, mode) !== null ? mode : undefined;\n}\n\n/** \"1M window · compacts ≈367k\" for a model in a mode; null when the budget is unknown. */\nexport function budgetLine(option: VerseModelOption | null, mode: VerseContextMode): string | null {\n const budget = budgetFor(option, mode);\n if (!budget) return null;\n const window = `${formatTokens(budget.contextWindow)} window`;\n return budget.autoCompactAt === null ? window : `${window} · compacts ≈${formatTokens(budget.autoCompactAt)}`;\n}\n\nexport interface HandoffFit {\n verdict: VerseFitVerdict | null;\n /** The handoff text's own estimate (chars / 4). */\n handoffTokens: number;\n /** The target engine's estimated fixed prompt (context-math `sessionOverheadTokens`). */\n overheadTokens: number;\n /** Handoff + that fixed prompt: what the new session holds before its first reply. */\n needTokens: number;\n /** One sentence for the dialog. */\n text: string;\n tone: 'ok' | 'warn' | 'danger' | 'unknown';\n}\n\n/**\n * Will the new session START comfortably? The handoff is small by design\n * (≤ 12k chars ≈ 3k tokens), but every fresh session also carries the CLI's\n * fixed prompt — a large share of a 64k local slot — so this is a real\n * question on small windows, and the sentence shows both halves of the sum\n * rather than a bare verdict.\n *\n * The fixed prompt is the TARGET seat's engine estimate (local ≈15k measured,\n * claude ≈25k, …), not one flat figure: a flat 30k called every handoff to a\n * 64k local seat \"too big for one context\" when the real sum was ~20k. It is\n * still an estimate — no CLI reports its base prompt before the first turn —\n * so the sentence says \"estimated\" rather than presenting it as a reading.\n * `engine` null (target seat not known yet) falls back to the largest estimate.\n */\nexport function handoffFit(\n textChars: number,\n option: VerseModelOption | null,\n mode: VerseContextMode,\n engine: VerseEngine | null,\n): HandoffFit {\n const handoffTokens = estimateTokensFromChars(textChars);\n const overheadTokens = sessionOverheadTokens(engine);\n const needTokens = handoffTokens + overheadTokens;\n const verdict = fitVerdict(handoffTokens, option, overheadTokens);\n const sum = `~${formatTokens(handoffTokens)} tokens of handoff + an estimated ~${formatTokens(overheadTokens)} of fixed prompt`;\n const standard = budgetFor(option, 'standard');\n const standardCap = standard ? standard.autoCompactAt ?? standard.contextWindow : null;\n const base = { verdict, handoffTokens, overheadTokens, needTokens };\n switch (verdict) {\n case 'fits':\n return { ...base, tone: 'ok', text: `Fits: ${sum} leaves most of the budget before the first compaction.` };\n case 'tight':\n return {\n ...base,\n tone: 'warn',\n text: `Tight: ${sum} is ${Math.round((needTokens / (standardCap ?? needTokens)) * 100)}% of where this model compacts — the new chat will compact early.`,\n };\n case 'expansive':\n return mode === 'expansive'\n ? { ...base, tone: 'warn', text: `Fits only the expansive budget: ${sum}.` }\n : { ...base, tone: 'danger', text: `Too big for the standard budget (${sum}). Switch to Expansive, or trim the handoff.` };\n case 'split':\n return { ...base, tone: 'danger', text: `Too big for one context on this model (${sum}). Trim the handoff or pick a seat with a larger window.` };\n default:\n return { ...base, verdict: null, tone: 'unknown', text: 'This model’s window is not known, so there is no fit estimate.' };\n }\n}\n\n/** True when the target model has a real expansive budget (context-math, re-exported for the dialog). */\nexport function offersExpansive(option: VerseModelOption | null): boolean {\n return hasExpansiveMode(option);\n}\n\n/** A local turn runs on this machine; every other engine spends subscription usage. */\nexport function turnCostSentence(engine: VerseEngine, seatLabel: string): string {\n return engine === 'local'\n ? `Runs on this machine with ${seatLabel}; it spends no subscription usage.`\n : `Sends one turn to ${seatLabel} and spends from its usage. The automatic preview costs nothing.`;\n}\n\n// ---------------------------------------------------------------------------\n// Bytes\n// ---------------------------------------------------------------------------\n\nconst encoder = typeof TextEncoder === 'function' ? new TextEncoder() : null;\n\n/** UTF-8 byte length — the unit the server's caps (64 KB memory content, 64 KB turn) are written in. */\nexport function utf8Bytes(text: string): number {\n if (encoder) return encoder.encode(text).length;\n // Fallback for an environment with no TextEncoder: count code points by width.\n let bytes = 0;\n for (const ch of text) {\n const cp = ch.codePointAt(0) ?? 0;\n bytes += cp < 0x80 ? 1 : cp < 0x800 ? 2 : cp < 0x10000 ? 3 : 4;\n }\n return bytes;\n}\n\n/**\n * The API's default request-body cap: `readBody` in src/core/web/api.ts, which\n * every /api/verse POST uses unless its route passes a larger one (only\n * POST /memory does — see MEMORY_BODY_MAX_BYTES). A write travels as JSON, so what must fit is\n * the ENCODED body — every `\\n` and `\"` escaped to two bytes — not the text\n * alone; the editors measure it so a refusal happens on screen instead of as\n * \"request body too large\".\n */\nexport const API_BODY_MAX_BYTES = 65_536;\n\n/**\n * POST /api/verse/memory's own body cap — mirrors `VERSE_MEMORY_BODY_MAX_BYTES`\n * in src/core/verse/verse-api.ts (a server module the browser cannot import):\n * room for 64 KiB of content in which EVERY byte escapes to two, plus the\n * project path and the JSON around it. The server still holds the CONTENT to\n * VERSE_MEMORY_MAX_BYTES after parsing. Guarding the memory editor with the\n * 64 KiB default instead blocked saves the server accepts — a 60–64 KB\n * MEMORY.md with ordinary line breaks could not be edited from the panel,\n * which is the exact case the server's larger cap exists for.\n */\nexport const MEMORY_BODY_MAX_BYTES = VERSE_MEMORY_BODY_MAX_BYTES;\n\n/** UTF-8 size of `body` exactly as the client will send it. */\nexport function jsonBodyBytes(body: unknown): number {\n return utf8Bytes(JSON.stringify(body) ?? '');\n}\n\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '—';\n if (bytes < 1024) return `${bytes} B`;\n const kb = bytes / 1024;\n return `${kb < 10 ? kb.toFixed(1).replace(/\\.0$/, '') : Math.round(kb)} KB`;\n}\n\n/**\n * A relative time that reads as a phrase: \"just now\", \"5m ago\", \"on Sep 19\".\n * `formatRelative` (verse-model) is sized for a 5ch sidebar column and reads\n * as a bare token; prose needs the preposition.\n */\nexport function relativePhrase(iso: string | null | undefined, now: number = Date.now()): string | null {\n if (!iso) return null;\n const short = formatRelative(iso, now);\n if (!short) return null;\n if (short === 'now') return 'just now';\n return /^\\d/.test(short) ? `${short} ago` : `on ${short}`;\n}\n\n// ---------------------------------------------------------------------------\n// Memory\n// ---------------------------------------------------------------------------\n\n/**\n * Shapes that are almost certainly credentials. MEMORY.md is sent to every\n * seat's model on every turn, so a pasted key would travel to every provider\n * — the panel warns BEFORE the save rather than trusting the server's scrub\n * to catch it on the way out. Conservative on purpose: a false alarm on prose\n * would teach the operator to ignore the warning.\n */\nconst SECRET_PATTERNS: ReadonlyArray<{ label: string; re: RegExp }> = [\n { label: 'a private key', re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },\n { label: 'an Anthropic API key', re: /\\bsk-ant-[A-Za-z0-9_-]{20,}/ },\n { label: 'an OpenAI-style API key', re: /\\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/ },\n { label: 'an xAI API key', re: /\\bxai-[A-Za-z0-9]{32,}/ },\n { label: 'a GitHub token', re: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{30,}|\\bgithub_pat_[A-Za-z0-9_]{40,}/ },\n { label: 'an AWS access key', re: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/ },\n { label: 'a Slack token', re: /\\bxox[abprs]-[A-Za-z0-9-]{10,}/ },\n { label: 'a Google API key', re: /\\bAIza[0-9A-Za-z_-]{35}\\b/ },\n { label: 'a Stripe secret key', re: /\\b(?:sk|rk)_live_[0-9A-Za-z]{20,}/ },\n];\n\n/**\n * Mirrors `VERSE_MEMORY_BLOCK_MAX_BYTES` in src/core/verse/project-memory.ts\n * (a node module the browser cannot import): the most memory text a chat's\n * instructions carry, however large MEMORY.md itself is.\n */\nexport const MEMORY_BLOCK_MAX_BYTES = VERSE_MEMORY_BLOCK_MAX_BYTES;\n\n/**\n * What memory costs, in the one wording the memory panel and the resources\n * panel share. Memory is ON by default and snapshotted into every new chat's\n * system prompt (claude `--append-system-prompt`, codex `developer_instructions`,\n * grok `--rules`), which re-rides every turn, and it asks writable seats to\n * read and update MEMORY.md — tool calls and output. On a paid seat that is\n * real (if small, mostly cached) usage, so \"nothing here spends\" would be\n * false for exactly the operator deciding whether to leave it on near a limit.\n * Reading and editing the file from the panel is still free.\n */\nexport const MEMORY_SPEND_NOTE =\n `Reading and editing memory here spends nothing. On a paid seat it is not free to use: each new chat’s system prompt carries a memory block of up to ${MEMORY_BLOCK_MAX_BYTES / 1024} KB, re-sent every turn (cached after the first), and the agent reads and updates MEMORY.md as it works. Local seats spend nothing.`;\n\n/** The first credential shape found, as a phrase (\"a GitHub token\"), or null. */\nexport function secretLike(text: string): string | null {\n for (const { label, re } of SECRET_PATTERNS) if (re.test(text)) return label;\n return null;\n}\n\n/**\n * The placeholder the server's scrubber (src/core/util/scrub.ts) writes over\n * secret-shaped text in every rule. In a memory view flagged\n * `contentSanitized`, each one stands for a REAL value still in MEMORY.md.\n */\nexport const REDACTION_MARKER = '[REDACTED]';\n\n/** How many redaction placeholders a text carries. */\nexport function redactionMarkers(text: string): number {\n return text.split(REDACTION_MARKER).length - 1;\n}\n\n// ---------------------------------------------------------------------------\n// Search\n// ---------------------------------------------------------------------------\n\n/** Below this many characters a keyword search over every message is noise, not recall. */\nexport const SEARCH_MIN_CHARS = 2;\n/** Keystrokes inside this window collapse into one request. */\nexport const SEARCH_DEBOUNCE_MS = 250;\n/** Hits asked for per search — enough to fill the sidebar twice over. */\nexport const SEARCH_LIMIT = 30;\n\n/** Whitespace-separated terms, lowercased and de-duplicated (the server ANDs them). */\nexport function searchTerms(query: string): string[] {\n const out: string[] = [];\n for (const raw of query.toLowerCase().split(/\\s+/)) {\n if (raw && !out.includes(raw)) out.push(raw);\n }\n return out;\n}\n\nexport interface HighlightSegment {\n text: string;\n match: boolean;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Split `text` into plain and matched runs for every term, case-insensitive.\n * Longest terms first so \"migrate\" wins over \"migrat\" at the same position.\n */\nexport function highlightSegments(text: string, terms: readonly string[]): HighlightSegment[] {\n const usable = [...terms].filter((t) => t.length > 0).sort((a, b) => b.length - a.length);\n if (usable.length === 0 || text.length === 0) return text ? [{ text, match: false }] : [];\n const re = new RegExp(usable.map(escapeRegExp).join('|'), 'gi');\n const out: HighlightSegment[] = [];\n let last = 0;\n for (const m of text.matchAll(re)) {\n const start = m.index ?? 0;\n if (m[0].length === 0) continue;\n if (start > last) out.push({ text: text.slice(last, start), match: false });\n out.push({ text: m[0], match: true });\n last = start + m[0].length;\n }\n if (last < text.length) out.push({ text: text.slice(last), match: false });\n return out;\n}\n\nexport interface SessionSearchGroup {\n sessionId: string;\n title: string;\n projectPath: string;\n engine: VerseEngine;\n /** The best-scoring hit in this session (the server returns hits best-first). */\n top: VerseSearchHit;\n /** Every hit this session contributed to the response. */\n count: number;\n /** Newest hit time, for the row's relative time. */\n latestAt: string;\n}\n\n/**\n * One row per session. The server ranks MESSAGES; the sidebar lists CHATS,\n * and five rows for one chat would push every other match off screen. Order\n * is the order each session first appears, i.e. by its best hit.\n */\nexport function groupSearchHits(hits: readonly VerseSearchHit[]): SessionSearchGroup[] {\n const groups = new Map<string, SessionSearchGroup>();\n for (const hit of hits) {\n const existing = groups.get(hit.sessionId);\n if (!existing) {\n groups.set(hit.sessionId, {\n sessionId: hit.sessionId,\n title: hit.title,\n projectPath: hit.projectPath,\n engine: hit.engine,\n top: hit,\n count: 1,\n latestAt: hit.at,\n });\n continue;\n }\n existing.count += 1;\n if (hit.at > existing.latestAt) existing.latestAt = hit.at;\n }\n return [...groups.values()];\n}\n","/*\n * routes/verse/context/context.module.css — the handoff dialog, the project\n * memory panel and the message-search results. One file because the three\n * share one control vocabulary with the new-chat dialog (NewChatDialog.module.css)\n * and the resources panel: micro-labels in the display face, hairline fields,\n * a single accent-filled primary, ghost secondaries. DESIGN-V2 §1: no cards,\n * no shadows outside the dialog itself, colour only where it carries meaning.\n */\n\n/* ======================================================================\n Shared field vocabulary\n ====================================================================== */\n\n.label {\n margin: 0;\n font-family: var(--font-display);\n font-size: var(--text-2xs-size, 11px);\n font-weight: var(--font-weight-semibold);\n letter-spacing: var(--tracking-label, 0.06em);\n text-transform: uppercase;\n color: var(--text-tertiary);\n}\n.optional { font-weight: var(--font-weight-regular); letter-spacing: 0; text-transform: none; opacity: 0.8; }\n\n.field { display: grid; gap: var(--space-1); min-width: 0; }\n.row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-width: 0; }\n.row > .input { flex: 1 1 12rem; min-width: 0; }\n\n.input,\n.textarea {\n width: 100%;\n border: 1px solid var(--border-default);\n border-radius: var(--radius-sm);\n background: var(--bg-input);\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);\n}\n.input { min-height: var(--control-h); padding: 0 var(--space-2); }\n.textarea {\n display: block;\n min-height: 8rem;\n padding: var(--space-2);\n resize: vertical;\n font-family: var(--font-mono);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n white-space: pre-wrap;\n}\n.input::placeholder,\n.textarea::placeholder { color: var(--text-tertiary); }\n.input:hover:not(:disabled),\n.textarea:hover:not(:disabled) { border-color: var(--border-strong); }\n/* Same focus treatment as NewChatDialog and the Input primitive. */\n.input:focus-visible,\n.textarea:focus-visible { border-color: var(--border-focus); box-shadow: var(--shadow-focus-ring); }\n.textarea:disabled { opacity: 0.7; cursor: progress; }\n\n.hint { margin: 0; font-size: var(--text-xs-size); line-height: var(--text-xs-line); color: var(--text-tertiary); }\n.hint code,\n.locked code { font-size: 0.95em; }\n.warn {\n margin: 0;\n padding-left: var(--space-3);\n border-left: 2px solid var(--status-warning-solid);\n color: var(--status-warning-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n.error {\n margin: 0;\n padding-left: var(--space-3);\n border-left: 2px solid var(--status-danger-solid);\n color: var(--status-danger-fg);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n}\n\n/* Buttons: one filled primary per surface; everything else is a ghost. */\n.primary,\n.secondary,\n.cancel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n min-height: var(--control-h);\n padding: 0 var(--space-3);\n border: 1px solid transparent;\n border-radius: var(--radius-sm);\n font-size: var(--text-sm-size);\n font-weight: var(--font-weight-medium);\n white-space: nowrap;\n transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard),\n border-color var(--duration-fast) var(--ease-standard);\n}\n.primary { padding: 0 var(--space-4); background: var(--accent-600); color: var(--accent-contrast); }\n.primary:hover:not(:disabled),\n.primary:active:not(:disabled) { background: var(--accent-700); }\n.secondary { border-color: var(--border-default); color: var(--text-primary); }\n.secondary:hover:not(:disabled) { background: var(--bg-hover); border-color: var(--border-strong); }\n.secondary:active:not(:disabled) { background: var(--bg-active); }\n.cancel { color: var(--text-secondary); }\n.cancel:hover { background: var(--bg-hover); color: var(--text-primary); }\n.cancel:active { background: var(--bg-active); }\n.primary:disabled,\n.secondary:disabled { opacity: 0.45; cursor: not-allowed; }\n\n.link {\n padding: 0;\n border: 0;\n background: none;\n color: var(--accent-500);\n font: inherit;\n text-decoration: underline;\n text-underline-offset: 2px;\n cursor: pointer;\n}\n.link:hover { color: var(--accent-600); }\n\n.small { min-height: var(--control-h-sm); padding: 0 var(--space-2); font-size: var(--text-xs-size); }\n\n/* The quiet running marker: a pulsing dot, never a spinner (DESIGN-V2 §4). */\n.pending {\n flex: none;\n width: 6px;\n height: 6px;\n border-radius: var(--radius-full);\n background: var(--status-running-solid);\n animation: context-pulse 1.8s ease-out infinite;\n}\n@keyframes context-pulse {\n 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-running-solid) 55%, transparent); }\n 100% { box-shadow: 0 0 0 5px transparent; }\n}\n@media (prefers-reduced-motion: reduce) {\n :root:not([data-motion=\"full\"]) .pending { animation: none; }\n}\n:root[data-motion=\"reduce\"] .pending { animation: none; }\n\n/* ======================================================================\n Handoff dialog\n ====================================================================== */\n\n.dialogWidth { width: min(640px, calc(100vw - 32px)); max-height: 86vh; }\n.form { display: grid; gap: var(--space-4); }\n\n/* The optional, spending path sits on a hairline rule so it reads as an\n aside to the free flow around it, not as a step in it. */\n.aside {\n display: grid;\n gap: var(--space-2);\n padding-left: var(--space-3);\n border-left: 1px solid var(--border-default);\n}\n\n.locked {\n display: grid;\n justify-items: start;\n gap: var(--space-2);\n padding: var(--space-3);\n border: 1px dashed var(--border-default);\n border-radius: var(--radius-sm);\n color: var(--text-secondary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n}\n.locked p { margin: 0; }\n\n/* Fit verdict: text first; the rule on its edge is reinforcement, never the\n only signal (DESIGN-V2 §6). */\n.fit {\n margin: 0;\n padding-left: var(--space-3);\n border-left: 2px solid var(--border-default);\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n.fit-ok { border-left-color: var(--status-success-solid); }\n.fit-warn { border-left-color: var(--status-warning-solid); }\n.fit-danger { border-left-color: var(--status-danger-solid); color: var(--status-danger-fg); }\n.fit-unknown { border-left-style: dashed; }\n\n.cost {\n margin: 0;\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n\n.actions { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--space-2); }\n.blocked {\n flex: 1 1 12rem;\n min-width: 0;\n color: var(--text-tertiary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n\n/* ======================================================================\n Project memory (a section of the resources panel)\n Mirrors ResourcesPanel.module.css `.section + .section`: it is mounted as\n a sibling of those sections, never inside one, so it carries its own rule.\n ====================================================================== */\n\n.memory {\n display: grid;\n gap: var(--space-3);\n margin-top: var(--space-4);\n padding: var(--space-4) var(--space-4) 0;\n border-top: 1px solid var(--border-subtle);\n min-width: 0;\n}\n.memoryHead {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-2);\n margin: 0;\n font-family: var(--font-display);\n font-size: var(--text-2xs-size, 11px);\n font-weight: var(--font-weight-semibold);\n letter-spacing: var(--tracking-label, 0.06em);\n text-transform: uppercase;\n color: var(--text-tertiary);\n}\n.memoryState { font-weight: var(--font-weight-regular); letter-spacing: 0; text-transform: none; color: var(--text-secondary); }\n.memoryHeadAction {\n margin-left: auto;\n padding: 0 var(--space-1);\n border-radius: var(--radius-xs);\n color: var(--text-tertiary);\n font-family: var(--font-ui);\n font-size: var(--text-2xs-size, 11px);\n font-weight: var(--font-weight-regular);\n letter-spacing: 0;\n text-transform: none;\n}\n.memoryHeadAction:hover { color: var(--text-primary); background: var(--bg-hover); }\n\n.memoryBody {\n margin: 0;\n max-height: 16rem;\n overflow: auto;\n padding: var(--space-2);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-sm);\n background: var(--bg-code, var(--bg-input));\n color: var(--text-primary);\n font-family: var(--font-mono);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.memoryMeta { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); }\n.memoryFiles { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-1); }\n.memoryFiles code {\n padding: 0 var(--space-1);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-xs);\n font-size: var(--text-2xs-size, 11px);\n}\n.memoryActions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }\n.memoryActions .spacer { flex: 1; }\n.danger { color: var(--status-danger-fg); }\n.danger:hover:not(:disabled) { background: var(--status-danger-bg); border-color: var(--status-danger-solid); }\n.counter { font-variant-numeric: tabular-nums; }\n.counterOver { color: var(--status-danger-fg); }\n\n/* ======================================================================\n Message search (the sidebar's lower half while a query is typed)\n ====================================================================== */\n\n.search { padding-top: var(--space-4); min-width: 0; }\n.searchBox {\n position: relative;\n display: flex;\n align-items: center;\n margin: 0 var(--space-3) var(--space-2);\n}\n.searchBox .input { padding-left: var(--space-2); }\n.searchHead {\n display: flex;\n align-items: baseline;\n gap: var(--space-2);\n padding: 0 var(--space-3) var(--space-1);\n margin: 0;\n font-family: var(--font-display);\n font-size: var(--text-2xs-size, 11px);\n line-height: 1.4;\n font-weight: var(--font-weight-semibold);\n letter-spacing: var(--tracking-label, 0.06em);\n text-transform: uppercase;\n color: var(--text-tertiary);\n}\n.searchCount {\n margin-left: auto;\n font-variant-numeric: tabular-nums;\n letter-spacing: 0;\n font-weight: var(--font-weight-regular);\n}\n.searchNote { padding: var(--space-1) var(--space-3); }\n.searchError {\n display: grid;\n justify-items: start;\n gap: var(--space-1);\n padding: var(--space-1) var(--space-3);\n color: var(--status-danger-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n\n.hits { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }\n.hit {\n display: grid;\n grid-template-columns: 2px minmax(0, 1fr) auto;\n grid-template-areas:\n 'marker title time'\n 'marker snippet snippet';\n column-gap: var(--space-2);\n row-gap: 2px;\n width: 100%;\n padding: var(--space-2) var(--space-3);\n color: var(--text-secondary);\n text-align: left;\n transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);\n}\n.hit:hover { background: var(--bg-hover); color: var(--text-primary); }\n.hit:active { background: var(--bg-active); }\n.hitMarker {\n grid-area: marker;\n align-self: stretch;\n width: 2px;\n border-radius: 1px;\n background: var(--verse-engine, var(--text-tertiary));\n}\n.hitTitle {\n grid-area: title;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n}\n.hitTime {\n grid-area: time;\n font-family: var(--font-display);\n font-size: var(--text-2xs-size, 11px);\n font-variant-numeric: tabular-nums;\n color: var(--text-secondary);\n}\n.hitSnippet {\n grid-area: snippet;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: anywhere;\n}\n.hitWho { color: var(--text-tertiary); }\n/* A match is marked by weight AND tint — never tint alone (DESIGN-V2 §6). */\n.hitSnippet mark {\n padding: 0 1px;\n border-radius: 2px;\n background: color-mix(in srgb, var(--accent-500) 18%, transparent);\n color: var(--text-primary);\n font-weight: var(--font-weight-semibold);\n}\n.engine-claude { --verse-engine: var(--engine-claude, #c96442); }\n.engine-codex { --verse-engine: var(--engine-codex, #10a37f); }\n.engine-grok { --verse-engine: var(--engine-grok, #6b7280); }\n.engine-local { --verse-engine: var(--engine-local, #7c5cff); }\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as e,n as t,s as n}from"./index-afDA8cwW.js";import{x as r}from"./verse-model-E98kXngO.js";var i=n(e(),1),a={live:`_live_ytrey_7`,line:`_line_ytrey_14`,pulse:`_pulse_ytrey_24`,"live-pulse":`_live-pulse_ytrey_1`,text:`_text_ytrey_34`,word:`_word_ytrey_43`,sep:`_sep_ytrey_44`,figure:`_figure_ytrey_45`,detail:`_detail_ytrey_49`,stop:`_stop_ytrey_58`,stopIcon:`_stopIcon_ytrey_75`,notice:`_notice_ytrey_82`,noticeWord:`_noticeWord_ytrey_94`,noticeText:`_noticeText_ytrey_96`},o=t();function s(e,t){for(let t=e.length-1;t>=0;--t){let n=e[t];if(n.kind===`user`)break;if(n.kind===`tool`){if(n.result===null)return{phase:`tool`,detail:r(n.input)||n.name};break}if(n.kind===`assistant`&&n.streaming)return{phase:`writing`,detail:null};if(n.kind===`assistant`||n.kind===`thinking`)break}return t.thinking?{phase:`thinking`,detail:null}:{phase:`waiting`,detail:null}}var c={thinking:`Thinking`,tool:`Running`,writing:`Writing`,waiting:`Waiting`},l={retry:`Retrying`,preflight:`Preflight`,watchdog:`Still running`};function u(e){if(!Number.isFinite(e)||e<0)return`0s`;let t=Math.floor(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m ${t%60}s`:`${Math.floor(n/60)}h ${n%60}m`}function d(e,t,n){let r=e.progress,i=t.phase===`tool`||t.phase===`writing`?t.phase:r?.phase??t.phase,a=r?r.elapsedMs+Math.max(0,n-r.receivedAt):e.startedAt===null?null:Math.max(0,n-e.startedAt),o=i===`tool`?t.detail??r?.tool??null:null,s=r?.tokPerSec??null,l=(i===`writing`||i===`thinking`)&&s!==null&&s>0?`${s>=10?Math.round(s):s.toFixed(1)} tok/s`:null;return{phase:i,word:c[i],elapsedMs:a,detail:o,rate:l}}var f=(0,i.memo)(function({live:e,derived:t,onStop:n,showNotice:r=!0}){let[s,c]=(0,i.useState)(()=>Date.now());(0,i.useEffect)(()=>{c(Date.now());let e=setInterval(()=>c(Date.now()),1e3);return()=>clearInterval(e)},[e.turnId]);let f=d(e,t,s),p=f.detail?`${f.word}: ${f.detail}`:f.word,m=r?e.notice:null;return(0,o.jsxs)(`div`,{className:a.live,"data-phase":f.phase,children:[(0,o.jsxs)(`div`,{className:a.line,children:[(0,o.jsx)(`span`,{className:a.pulse,"aria-hidden":`true`}),(0,o.jsxs)(`span`,{className:a.text,"aria-hidden":`true`,children:[(0,o.jsx)(`span`,{className:a.word,children:f.word}),f.elapsedMs===null?null:(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(`span`,{className:a.sep,children:`·`}),(0,o.jsx)(`span`,{className:a.figure,children:u(f.elapsedMs)})]}),f.detail?(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(`span`,{className:a.sep,children:`·`}),(0,o.jsx)(`span`,{className:a.detail,title:f.detail,children:f.detail})]}):null,f.rate?(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(`span`,{className:a.sep,children:`·`}),(0,o.jsx)(`span`,{className:a.figure,children:f.rate})]}):null]}),(0,o.jsx)(`span`,{className:`visually-hidden`,role:`status`,children:p}),n?(0,o.jsxs)(`button`,{type:`button`,className:a.stop,onClick:n,"aria-label":`Stop this turn`,title:`Stop this turn (⌘.)`,children:[(0,o.jsx)(`span`,{className:a.stopIcon,"aria-hidden":`true`}),`Stop`]}):null]}),m?(0,o.jsxs)(`p`,{className:a.notice,"data-kind":m.kind,role:`status`,children:[(0,o.jsx)(`span`,{className:a.noticeWord,children:l[m.kind]}),(0,o.jsx)(`span`,{className:a.noticeText,children:m.message})]}):null]})}),p=new Set,m=null,h=Date.now();function g(e){return p.add(e),m===null&&(h=Date.now(),m=setInterval(()=>{h=Date.now();for(let e of[...p])e()},1e3)),()=>{p.delete(e),p.size===0&&m!==null&&(clearInterval(m),m=null)}}function _(){return h}var v=()=>()=>{},y=()=>0;function b(e=!0){let t=(0,i.useSyncExternalStore)(e?g:v,e?_:y,e?_:y);return Math.max(t,Date.now())}function x(e){if(typeof e==`number`)return Number.isFinite(e)?e:null;if(typeof e!=`string`)return null;let t=Date.parse(e);return Number.isFinite(t)?t:null}function S({since:e,className:t,label:n}){let r=x(e),i=b(r!==null);if(r===null)return null;let a=u(Math.max(0,i-r));return(0,o.jsx)(`span`,{className:t,"data-live-timer":``,"aria-hidden":!n||void 0,"aria-label":n?`${n} ${a}`:void 0,role:n?`timer`:void 0,children:a})}function C({size:e=16,children:t,...n}){return(0,o.jsx)(`svg`,{width:e,height:e,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,strokeWidth:1.5,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,focusable:`false`,...n,children:t})}function w(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`rect`,{x:`1.75`,y:`2.75`,width:`12.5`,height:`10.5`,rx:`1.5`}),(0,o.jsx)(`path`,{d:`m4.5 6.25 2 1.75-2 1.75M8.25 10h3`})]})}function T(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`rect`,{x:`1.75`,y:`2.75`,width:`12.5`,height:`10.5`,rx:`1.5`}),(0,o.jsx)(`path`,{d:`M1.75 5.75h12.5M4 4.25h.01M5.75 4.25h.01`})]})}function E(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`path`,{d:`M5 2.5v5M2.5 5h5M9 11h4.5`}),(0,o.jsx)(`path`,{d:`M11.5 2.5 4.5 13.5`})]})}function D(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`path`,{d:`M6.5 4h7M6.5 8h7M6.5 12h7`}),(0,o.jsx)(`path`,{d:`m2.25 4 1 1 1.5-2M2.5 8h1.75M2.5 12h1.75`})]})}function O(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`path`,{d:`m8 2 6 3-6 3-6-3 6-3Z`}),(0,o.jsx)(`path`,{d:`m2 8 6 3 6-3M2 11l6 3 6-3`})]})}function k(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`rect`,{x:`2.25`,y:`2.25`,width:`11.5`,height:`11.5`,rx:`1.5`}),(0,o.jsx)(`path`,{d:`M2.25 8h11.5`})]})}function A(e){return(0,o.jsx)(C,{...e,children:(0,o.jsx)(`path`,{d:`m4 4 8 8M12 4l-8 8`})})}function j(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`circle`,{cx:`3.5`,cy:`8`,r:`0.9`,fill:`currentColor`,stroke:`none`}),(0,o.jsx)(`circle`,{cx:`8`,cy:`8`,r:`0.9`,fill:`currentColor`,stroke:`none`}),(0,o.jsx)(`circle`,{cx:`12.5`,cy:`8`,r:`0.9`,fill:`currentColor`,stroke:`none`})]})}function M(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`path`,{d:`M9.75 2.25 13.75 6.25 11 7.5 8.5 10l.25 2.75-5-5L6.5 8 9 5.25Z`}),(0,o.jsx)(`path`,{d:`m5 11-2.75 2.75`})]})}function N(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`rect`,{x:`1.75`,y:`2.75`,width:`12.5`,height:`3`,rx:`1`}),(0,o.jsx)(`path`,{d:`M2.75 5.75v6.5a1 1 0 0 0 1 1h8.5a1 1 0 0 0 1-1v-6.5M6.5 8.5h3`})]})}function P(e){return(0,o.jsx)(C,{...e,children:(0,o.jsx)(`path`,{d:`M10.5 2.75 13.25 5.5 6 12.75H3.25V10Z`})})}function F(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`path`,{d:`M2.5 8h9M8.5 4.5 12 8l-3.5 3.5`}),(0,o.jsx)(`path`,{d:`M13.75 3v10`})]})}function I(e){return(0,o.jsxs)(C,{...e,children:[(0,o.jsx)(`rect`,{x:`5.25`,y:`5.25`,width:`8.5`,height:`8.5`,rx:`1.5`}),(0,o.jsx)(`path`,{d:`M10.75 5.25V3.75a1.5 1.5 0 0 0-1.5-1.5h-5.5a1.5 1.5 0 0 0-1.5 1.5v5.5a1.5 1.5 0 0 0 1.5 1.5h1.5`})]})}function L(e){return(0,o.jsx)(C,{...e,children:(0,o.jsx)(`path`,{d:`M2.75 4.25h10.5M6.25 4.25V2.75h3.5v1.5M4.25 4.25l.6 9h6.3l.6-9`})})}var R={terminal:w,preview:T,diff:E,tasks:D,context:O};export{F as a,P as c,S as d,f,R as i,k as l,A as n,j as o,s as p,I as r,M as s,N as t,L as u};
|
|
2
|
-
//# sourceMappingURL=dock-icons-oG8d7mNZ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dock-icons-oG8d7mNZ.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/chat/LiveStatus.module.css","../../../../../../src/web-ui/routes/verse/chat/LiveStatus.tsx","../../../../../../src/web-ui/routes/verse/chat/LiveTimer.tsx","../../../../../../src/web-ui/routes/verse/dock/dock-icons.tsx"],"sourcesContent":["/**\n * The live line sits where the caret used to: under the running turn, on\n * the reading measure, in tertiary text with tabular figures. Colour means\n * state only — the running pulse — and a notice is a 2px rule in the warning\n * colour with its kind in words (DESIGN §6: never colour alone).\n */\n.live {\n display: flex;\n flex-direction: column;\n gap: var(--space-2);\n min-width: 0;\n}\n\n.line {\n display: flex;\n align-items: center;\n gap: var(--space-2);\n min-height: var(--control-h-sm);\n min-width: 0;\n color: var(--text-tertiary);\n font-size: var(--text-sm-size);\n}\n\n.pulse {\n width: 8px;\n height: 8px;\n flex: none;\n border-radius: var(--radius-full);\n background: var(--status-running-solid);\n animation: live-pulse 1.4s var(--ease-standard) infinite;\n}\n.live[data-phase='waiting'] .pulse { background: var(--border-strong); }\n\n.text {\n display: inline-flex;\n align-items: baseline;\n gap: var(--space-2);\n min-width: 0;\n flex: 1 1 auto;\n overflow: hidden;\n white-space: nowrap;\n}\n.word { color: var(--text-secondary); font-weight: var(--font-weight-medium); }\n.sep { color: var(--text-tertiary); }\n.figure {\n font-family: var(--font-display);\n font-variant-numeric: tabular-nums;\n}\n.detail {\n font-family: var(--font-mono);\n font-size: var(--text-xs-size);\n color: var(--text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.stop {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n min-height: var(--control-h-xs);\n padding: 0 var(--space-2);\n border: 1px solid transparent;\n border-radius: var(--radius-sm);\n background: transparent;\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n cursor: pointer;\n transition: color var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);\n}\n.stop:hover { color: var(--text-primary); background: var(--bg-hover); border-color: var(--border-default); }\n.stop:active { background: var(--bg-active); }\n.stopIcon {\n width: 8px;\n height: 8px;\n border-radius: 1px;\n background: currentColor;\n}\n\n.notice {\n display: flex;\n gap: var(--space-2);\n margin: 0;\n padding-left: var(--space-3);\n border-left: 2px solid var(--status-warning-solid);\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line-relaxed);\n overflow-wrap: anywhere;\n}\n.notice[data-kind='preflight'] { border-left-color: var(--status-info-solid); }\n.noticeWord { color: var(--status-warning-fg); font-weight: var(--font-weight-medium); white-space: nowrap; }\n.notice[data-kind='preflight'] .noticeWord { color: var(--status-info-fg); }\n.noticeText { min-width: 0; }\n\n@keyframes live-pulse { 50% { opacity: 0.3; } }\n\n@media (prefers-reduced-motion: reduce) {\n :root:not([data-motion=\"full\"]) .pulse { animation: none; }\n}\n:root[data-motion=\"reduce\"] .pulse { animation: none; }\n\n@media (max-width: 480px) {\n /* 3.10: one line above the composer, beside the tasks chip — the text\n ellipsises (its detail first) rather than wrapping Stop onto a row of\n its own. */\n .line { flex-wrap: nowrap; }\n}\n","/**\n * routes/verse/chat/LiveStatus.tsx — the line under a running turn (V3.10):\n *\n * ● Running · 14s · npm test Stop\n * ● Writing · 1m 3s · 38 tok/s Stop\n *\n * It replaces the bare caret that used to be the ONLY live signal: a turn\n * that had been silently retrying an overloaded API for four minutes looked\n * exactly like one that was about to answer.\n *\n * Sources, best first:\n * 1. transient `progress` frames (phase, server-measured elapsed, tok/s);\n * 2. the transcript itself — a pending tool call means \"running that\n * tool\", a streaming bubble means \"writing\" — so a server that sends no\n * progress frames still gets an honest line;\n * 3. the turn's own start stamp for the clock.\n * A figure nobody measured (tok/s, the tool) is left out, never guessed.\n *\n * Under it, when the engine says so, one plumbing notice: an API retry, a\n * failed preflight (e.g. Ollama not answering), or the no-output watchdog.\n * It clears itself once output resumes (verse-store livePersisted).\n *\n * Accessibility: the visible line ticks every second, so it is NOT a live\n * region; a visually hidden status announces only phase/tool changes, and a\n * notice is announced once when it appears.\n */\nimport { memo, useEffect, useState } from 'react';\nimport type { VerseProgressPhase, VerseStatusKind } from '../../../../core/verse/types.js';\nimport { summarizeToolInput } from '../verse-model.js';\nimport type { TranscriptItem, VerseLiveState } from '../verse-store.js';\nimport styles from './LiveStatus.module.css';\n\nexport interface LivePhase {\n phase: VerseProgressPhase;\n /** What the phase is about: the running command/path, when known. */\n detail: string | null;\n}\n\n/**\n * What the transcript says the running turn is doing right now — the\n * fallback for a server that sends no `progress` frames, and the source of\n * the tool's human-readable argument even when one does.\n */\nexport function derivePhaseFromTranscript(items: readonly TranscriptItem[], live: Pick<VerseLiveState, 'thinking'>): LivePhase {\n for (let i = items.length - 1; i >= 0; i -= 1) {\n const item = items[i]!;\n if (item.kind === 'user') break;\n if (item.kind === 'tool') {\n if (item.result === null) return { phase: 'tool', detail: summarizeToolInput(item.input) || item.name };\n break;\n }\n if (item.kind === 'assistant' && item.streaming) return { phase: 'writing', detail: null };\n if (item.kind === 'assistant' || item.kind === 'thinking') break;\n }\n if (live.thinking) return { phase: 'thinking', detail: null };\n return { phase: 'waiting', detail: null };\n}\n\nconst PHASE_WORD: Record<VerseProgressPhase, string> = {\n thinking: 'Thinking',\n tool: 'Running',\n writing: 'Writing',\n waiting: 'Waiting',\n};\n\nconst NOTICE_WORD: Record<VerseStatusKind, string> = {\n retry: 'Retrying',\n preflight: 'Preflight',\n watchdog: 'Still running',\n};\n\n/** \"14s\" / \"1m 3s\" / \"1h 2m\". */\nexport function formatLiveElapsed(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '0s';\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${s % 60}s`;\n return `${Math.floor(m / 60)}h ${m % 60}m`;\n}\n\nexport interface LiveStatusLine {\n phase: VerseProgressPhase;\n word: string;\n elapsedMs: number | null;\n detail: string | null;\n rate: string | null;\n}\n\n/** Pure: the parts of the line at clock time `now` (exported for tests). */\nexport function liveStatusLine(live: VerseLiveState, derived: LivePhase, now: number): LiveStatusLine {\n const progress = live.progress;\n // The transcript knows a tool call is pending before (or without) a\n // progress frame saying so, and it knows the call's argument; a progress\n // frame knows about reasoning and waiting, which the transcript cannot see.\n const phase: VerseProgressPhase = derived.phase === 'tool' || derived.phase === 'writing'\n ? derived.phase\n : progress?.phase ?? derived.phase;\n const elapsedMs = progress\n ? progress.elapsedMs + Math.max(0, now - progress.receivedAt)\n : live.startedAt !== null ? Math.max(0, now - live.startedAt) : null;\n const detail = phase === 'tool' ? derived.detail ?? progress?.tool ?? null : null;\n const tps = progress?.tokPerSec ?? null;\n const rate = (phase === 'writing' || phase === 'thinking') && tps !== null && tps > 0 ? `${tps >= 10 ? Math.round(tps) : tps.toFixed(1)} tok/s` : null;\n return { phase, word: PHASE_WORD[phase], elapsedMs, detail, rate };\n}\n\nexport interface LiveStatusProps {\n live: VerseLiveState;\n derived: LivePhase;\n onStop?: () => void;\n /**\n * 3.10: above the composer the engine's notice goes to the notice slot\n * (one notice at a time, in priority order), so the line omits it.\n */\n showNotice?: boolean;\n}\n\nexport const LiveStatus = memo(function LiveStatus({ live, derived, onStop, showNotice = true }: LiveStatusProps) {\n const [now, setNow] = useState(() => Date.now());\n // The clock only; never a request. Re-armed per turn.\n useEffect(() => {\n setNow(Date.now());\n const id = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(id);\n }, [live.turnId]);\n\n const line = liveStatusLine(live, derived, now);\n const spoken = line.detail ? `${line.word}: ${line.detail}` : line.word;\n const notice = showNotice ? live.notice : null;\n\n return (\n <div className={styles.live} data-phase={line.phase}>\n <div className={styles.line}>\n <span className={styles.pulse} aria-hidden=\"true\" />\n <span className={styles.text} aria-hidden=\"true\">\n <span className={styles.word}>{line.word}</span>\n {line.elapsedMs !== null ? <><span className={styles.sep}>·</span><span className={styles.figure}>{formatLiveElapsed(line.elapsedMs)}</span></> : null}\n {line.detail ? <><span className={styles.sep}>·</span><span className={styles.detail} title={line.detail}>{line.detail}</span></> : null}\n {line.rate ? <><span className={styles.sep}>·</span><span className={styles.figure}>{line.rate}</span></> : null}\n </span>\n <span className=\"visually-hidden\" role=\"status\">{spoken}</span>\n {onStop ? (\n <button type=\"button\" className={styles.stop} onClick={onStop} aria-label=\"Stop this turn\" title=\"Stop this turn (⌘.)\">\n <span className={styles.stopIcon} aria-hidden=\"true\" />Stop\n </button>\n ) : null}\n </div>\n {notice ? (\n <p className={styles.notice} data-kind={notice.kind} role=\"status\">\n <span className={styles.noticeWord}>{NOTICE_WORD[notice.kind]}</span>\n <span className={styles.noticeText}>{notice.message}</span>\n </p>\n ) : null}\n </div>\n );\n});\n","/**\n * routes/verse/chat/LiveTimer.tsx — \"12s\", counting, for anything running\n * (unit C2): a pending tool row, an activity group, a running chat in the\n * sidebar, a task in the Tasks pane.\n *\n * ONE shared one-second clock for every timer on the page. A long session\n * can show dozens of running rows at once (a group of parallel tool calls,\n * the sidebar's running chats, the tasks list); a `setInterval` each would\n * be dozens of timers re-rendering out of phase. Subscribers join a single\n * interval that exists only while at least one of them is mounted, and all\n * of them tick on the same beat.\n *\n * It is a clock, never a request (SPEC-310A §0: no polling faster than 2 s —\n * this polls nothing).\n */\nimport { useSyncExternalStore } from 'react';\nimport { formatLiveElapsed } from './LiveStatus.js';\n\nconst listeners = new Set<() => void>();\nlet timer: ReturnType<typeof setInterval> | null = null;\nlet now = Date.now();\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n if (timer === null) {\n now = Date.now();\n timer = setInterval(() => {\n now = Date.now();\n for (const l of [...listeners]) l();\n }, 1000);\n }\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0 && timer !== null) {\n clearInterval(timer);\n timer = null;\n }\n };\n}\n\nfunction snapshot(): number {\n return now;\n}\n\nconst idle = () => () => {};\n// A constant: useSyncExternalStore requires a stable snapshot between\n// renders, and `Date.now` as a snapshot would loop.\nconst idleSnapshot = () => 0;\n\n/**\n * The shared clock (ms). While `active` is false the component does not\n * subscribe at all — a settled row costs nothing — and reads the wall clock\n * once per render instead.\n */\nexport function useSecondClock(active = true): number {\n const tick = useSyncExternalStore(active ? subscribe : idle, active ? snapshot : idleSnapshot, active ? snapshot : idleSnapshot);\n // A freshly mounted subscriber must not show the clock as it stood when the\n // previous beat fired (up to a second stale): take the later of the two.\n return Math.max(tick, Date.now());\n}\n\n/** Parse an ISO stamp or client-clock ms into ms; null when unusable. */\nexport function startMs(since: string | number | null | undefined): number | null {\n if (typeof since === 'number') return Number.isFinite(since) ? since : null;\n if (typeof since !== 'string') return null;\n const ms = Date.parse(since);\n return Number.isFinite(ms) ? ms : null;\n}\n\nexport interface LiveTimerProps {\n /** ISO start stamp or client-clock ms. Unparseable → renders nothing (an unknown start is not \"0s\"). */\n since: string | number | null | undefined;\n className?: string;\n /** Spoken label prefix, e.g. \"running for\". Omit to hide the timer from assistive tech (it ticks every second). */\n label?: string;\n}\n\nexport function LiveTimer({ since, className, label }: LiveTimerProps) {\n const start = startMs(since);\n const clock = useSecondClock(start !== null);\n if (start === null) return null;\n const text = formatLiveElapsed(Math.max(0, clock - start));\n return (\n <span className={className} data-live-timer=\"\" aria-hidden={label ? undefined : true}\n aria-label={label ? `${label} ${text}` : undefined} role={label ? 'timer' : undefined}>\n {text}\n </span>\n );\n}\n","/**\n * routes/verse/dock/dock-icons.tsx — the chat surface's own 16px glyphs\n * (unit C2): the dock's pane tabs and the header's pane toggles, the ⋯\n * menu, pin and archive. Same drawing grammar as components/primitives/icons\n * (16-unit box, 1.5 stroke, round caps, currentColor) so they sit beside the\n * shared set without looking borrowed; kept here because verse-icons.tsx is\n * the shell's file.\n */\nimport type { ReactNode, SVGProps } from 'react';\nimport type { DockPaneId } from '../shell/dock-catalog.js';\n\nexport interface GlyphProps extends Omit<SVGProps<SVGSVGElement>, 'children' | 'viewBox'> {\n size?: number;\n}\n\nfunction Glyph({ size = 16, children, ...rest }: GlyphProps & { children: ReactNode }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.5}\n strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\" focusable=\"false\" {...rest}>\n {children}\n </svg>\n );\n}\n\nexport function TerminalGlyph(p: GlyphProps) {\n return <Glyph {...p}><rect x=\"1.75\" y=\"2.75\" width=\"12.5\" height=\"10.5\" rx=\"1.5\" /><path d=\"m4.5 6.25 2 1.75-2 1.75M8.25 10h3\" /></Glyph>;\n}\n\nexport function PreviewGlyph(p: GlyphProps) {\n return <Glyph {...p}><rect x=\"1.75\" y=\"2.75\" width=\"12.5\" height=\"10.5\" rx=\"1.5\" /><path d=\"M1.75 5.75h12.5M4 4.25h.01M5.75 4.25h.01\" /></Glyph>;\n}\n\nexport function ReviewGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"M5 2.5v5M2.5 5h5M9 11h4.5\" /><path d=\"M11.5 2.5 4.5 13.5\" /></Glyph>;\n}\n\nexport function TasksGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"M6.5 4h7M6.5 8h7M6.5 12h7\" /><path d=\"m2.25 4 1 1 1.5-2M2.5 8h1.75M2.5 12h1.75\" /></Glyph>;\n}\n\nexport function ContextGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"m8 2 6 3-6 3-6-3 6-3Z\" /><path d=\"m2 8 6 3 6-3M2 11l6 3 6-3\" /></Glyph>;\n}\n\nexport function SplitGlyph(p: GlyphProps) {\n return <Glyph {...p}><rect x=\"2.25\" y=\"2.25\" width=\"11.5\" height=\"11.5\" rx=\"1.5\" /><path d=\"M2.25 8h11.5\" /></Glyph>;\n}\n\nexport function CloseGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"m4 4 8 8M12 4l-8 8\" /></Glyph>;\n}\n\nexport function MoreGlyph(p: GlyphProps) {\n return (\n <Glyph {...p}>\n <circle cx=\"3.5\" cy=\"8\" r=\"0.9\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"8\" cy=\"8\" r=\"0.9\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"12.5\" cy=\"8\" r=\"0.9\" fill=\"currentColor\" stroke=\"none\" />\n </Glyph>\n );\n}\n\nexport function PinGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"M9.75 2.25 13.75 6.25 11 7.5 8.5 10l.25 2.75-5-5L6.5 8 9 5.25Z\" /><path d=\"m5 11-2.75 2.75\" /></Glyph>;\n}\n\nexport function ArchiveGlyph(p: GlyphProps) {\n return <Glyph {...p}><rect x=\"1.75\" y=\"2.75\" width=\"12.5\" height=\"3\" rx=\"1\" /><path d=\"M2.75 5.75v6.5a1 1 0 0 0 1 1h8.5a1 1 0 0 0 1-1v-6.5M6.5 8.5h3\" /></Glyph>;\n}\n\nexport function RenameGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"M10.5 2.75 13.25 5.5 6 12.75H3.25V10Z\" /></Glyph>;\n}\n\nexport function HandoffGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"M2.5 8h9M8.5 4.5 12 8l-3.5 3.5\" /><path d=\"M13.75 3v10\" /></Glyph>;\n}\n\nexport function CopyGlyph(p: GlyphProps) {\n return <Glyph {...p}><rect x=\"5.25\" y=\"5.25\" width=\"8.5\" height=\"8.5\" rx=\"1.5\" /><path d=\"M10.75 5.25V3.75a1.5 1.5 0 0 0-1.5-1.5h-5.5a1.5 1.5 0 0 0-1.5 1.5v5.5a1.5 1.5 0 0 0 1.5 1.5h1.5\" /></Glyph>;\n}\n\nexport function TrashGlyph(p: GlyphProps) {\n return <Glyph {...p}><path d=\"M2.75 4.25h10.5M6.25 4.25V2.75h3.5v1.5M4.25 4.25l.6 9h6.3l.6-9\" /></Glyph>;\n}\n\nexport const DOCK_PANE_GLYPH: Readonly<Record<DockPaneId, (p: GlyphProps) => ReactNode>> = {\n terminal: TerminalGlyph,\n preview: PreviewGlyph,\n diff: ReviewGlyph,\n tasks: TasksGlyph,\n context: ContextGlyph,\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{_ as e}from"./dock-store-_E5h6TEq.js";import{o as t}from"./slots-W8gkmdfE.js";var n=Object.fromEntries(e.filter(e=>e.slot!==null).map(e=>[e.id,e.slot]));function r(e){let r=n[e];return r===void 0||t(r)}var i=Object.fromEntries(e.map(e=>[e.id,e.label]));export{r as n,i as t};
|
|
2
|
-
//# sourceMappingURL=dock-panes-BNTLvX7G.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{s as e}from"./format-BNYWGJat.js";function t(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:null}function n(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:null}function r(e,t){let n=e[t];return typeof n==`number`&&Number.isFinite(n)?n:null}function i(e,t){let n=r(e,t);return n===null?null:Math.max(0,Math.round(n))}function a(e,t){let n=e[t];return typeof n==`boolean`?n:null}function o(e,t){let n=e[t];return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]}var s=[`llama-server`,`ollama`,`vllm`,`unknown`],c=[`running`,`starting`,`stopping`,`stopped`,`unknown`],l=[`queued`,`running`,`finishing`],u=[`off`,`config`,`env`,`config+env`,`latch`,`unknown`];function d(e,t,n,r){let i=e[t];return typeof i==`string`&&n.includes(i)?i:r}function f(e){let r=t(e);return r?{capable:a(r,`capable`),refusal:n(r,`refusal`),slots:i(r,`slots`)}:{capable:null,refusal:null,slots:null}}var p={up:`running`,down:`stopped`,loading:`starting`,unknown:`unknown`};function m(e){let r=e.state;if(typeof r!=`string`||!(r in p))return null;let o=t(e.slots),s=n(e,`host`),c=i(e,`port`),l=o?i(o,`configured`):null,u=o?i(o,`busy`):null,d=o?n(o,`source`):null,f=d!==null&&d!==`unknown`,m=f?l:null,g=n(e,`parallelRefusal`),_=n(e,`runtimeKind`),v=i(e,`contextPerSlot`),y=i(e,`contextTotal`);return{kind:_===`ollama`?`ollama`:_===`unknown`?`unknown`:`llama-server`,state:p[r]??`unknown`,endpoint:s!==null&&c!==null?`${s}:${c}`:n(e,`origin`)??null,model:h(n(e,`modelName`))??h(n(e,`model`)),slotsTotal:m,slotsBusy:f?u:null,contextTokens:v??(m!==null&&m>0&&y!==null&&y>0?Math.floor(y/m):null),startedAt:n(e,`startedAt`),parallel:{capable:g===null?f&&m!==null?!0:null:!1,refusal:g,slots:m},reason:n(e,`lastError`),supervised:a(e,`managed`)??!1,sampledAt:n(e,`checkedAt`)}}function h(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 g(e){let r=t(e);if(!r)return null;if(!(`kind`in r)&&`slots`in r&&`state`in r){let e=m(r);if(e!==null)return e}return{kind:d(r,`kind`,s,`unknown`),state:d(r,`state`,c,`unknown`),endpoint:n(r,`endpoint`),model:n(r,`model`),slotsTotal:i(r,`slotsTotal`),slotsBusy:i(r,`slotsBusy`),contextTokens:i(r,`contextTokens`),startedAt:n(r,`startedAt`),parallel:f(r.parallel),reason:n(r,`reason`),supervised:a(r,`supervised`)??!1,sampledAt:n(r,`sampledAt`)}}function _(e){let r=t(e);if(!r)return null;let a=n(r,`id`);return a===null?null:{id:a,task:n(r,`task`),repo:n(r,`repo`),engine:n(r,`engine`),model:n(r,`model`),state:d(r,`state`,l,`running`),startedAt:n(r,`startedAt`),slot:i(r,`slot`)}}function v(e){let r=t(e);return r?{agents:(Array.isArray(r.agents)?r.agents:[]).map(_).filter(e=>e!==null),queueDepth:i(r,`queueDepth`),slotsTotal:i(r,`slotsTotal`),slotsBusy:i(r,`slotsBusy`),notes:o(r,`notes`),sampledAt:n(r,`sampledAt`)}:null}function y(e){let r=t(e);if(!r)return null;let i=n(r,`engine`);return i===null?null:{engine:i,reason:n(r,`reason`)??`The dispatcher refuses this engine while local-only is on.`}}var b=new Set([`env`,`config+env`,`latch`,`unknown`]);function x(e){let r=t(e);if(!r)return null;let i=Array.isArray(r.refuses)?r.refuses:[],o=d(r,`source`,u,`unknown`);return{enabled:a(r,`enabled`)??!1,source:o,refuses:i.map(y).filter(e=>e!==null),mutable:b.has(o)?!1:a(r,`mutable`)??!1,detail:n(r,`detail`),sampledAt:n(r,`sampledAt`)??n(r,`checkedAt`)}}function S(e){switch(e){case`llama-server`:return`llama-server`;case`ollama`:return`Ollama`;case`vllm`:return`vLLM`;default:return`the serving runtime`}}function C(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=S(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 w=[{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 T(e,t){if(e===null||t===null||t<=0)return{busy:e,total:t,percent:null,saturated:!1,label:e===null?`Slot occupancy is not reported.`:`${e} in flight; the number of slots is not known, so occupancy cannot be drawn.`};let n=Math.round(Math.min(e,t)/t*100),r=e>=t;return{busy:e,total:t,percent:n,saturated:r,label:r?`All ${t} slot${t===1?``:`s`} busy — new turns wait for one to free up.`:`${e} of ${t} slot${t===1?``:`s`} busy.`}}function E(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 D=`Refused: local-only is on and this engine is not local.`;function O(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)??D,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 k(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 A(t,n){if(!t)return`—`;let r=Date.parse(t);return Number.isNaN(r)?`—`:e(Math.max(0,n-r))}function j(t,n){if(!t)return`—`;let r=Date.parse(t);return Number.isNaN(r)?`—`:`up ${e(Math.max(0,n-r))}`}function M(e){return e===null||!Number.isFinite(e)?`—`:e>=1024?`${Math.round(e/1024)}k`:String(e)}function N(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{j as a,N as c,g as d,C as f,M as i,v as l,T as m,A as n,O as o,S as p,E as r,k as s,w as t,x as u};
|
|
2
|
-
//# sourceMappingURL=fleet-model-ClBbQ0Uj.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-model-ClBbQ0Uj.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 } 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 /** 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 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 percent = Math.round((clamped / effectiveTotal) * 100);\n const saturated = busy >= effectiveTotal;\n return {\n busy,\n total: effectiveTotal,\n percent,\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":"yCA+CA,SAAS,EAAO,EAAgD,CAC9D,OAAO,OAAO,GAAU,UAAY,GAAkB,CAAC,MAAM,QAAQ,CAAK,EACrE,EACD,IACN,CAEA,SAAS,EAAI,EAAiC,EAA4B,CACxE,IAAM,EAAI,EAAO,GACjB,OAAO,OAAO,GAAM,UAAY,EAAE,OAAS,EAAI,EAAI,IACrD,CAEA,SAAS,EAAI,EAAiC,EAA4B,CACxE,IAAM,EAAI,EAAO,GACjB,OAAO,OAAO,GAAM,UAAY,OAAO,SAAS,CAAC,EAAI,EAAI,IAC3D,CAGA,SAAS,EAAM,EAAiC,EAA4B,CAC1E,IAAM,EAAI,EAAI,EAAQ,CAAG,EACzB,OAAO,IAAM,KAAO,KAAO,KAAK,IAAI,EAAG,KAAK,MAAM,CAAC,CAAC,CACtD,CAEA,SAAS,EAAK,EAAiC,EAA6B,CAC1E,IAAM,EAAI,EAAO,GACjB,OAAO,OAAO,GAAM,UAAY,EAAI,IACtC,CAEA,SAAS,EAAQ,EAAiC,EAAuB,CACvE,IAAM,EAAI,EAAO,GACjB,OAAO,MAAM,QAAQ,CAAC,EAAI,EAAE,OAAQ,GAA2B,OAAO,GAAU,QAAQ,EAAI,CAAC,CAC/F,CAEA,IAAM,EAA+C,CAAC,eAAgB,SAAU,OAAQ,SAAS,EAC3F,EAAiD,CACrD,UACA,WACA,WACA,UACA,SACF,EACM,EAA2C,CAAC,SAAU,UAAW,WAAW,EAC5E,EAAiD,CACrD,MACA,SACA,MACA,aACA,QACA,SACF,EAEA,SAAS,EACP,EACA,EACA,EACA,EACG,CACH,IAAM,EAAI,EAAO,GACjB,OAAO,OAAO,GAAM,UAAa,EAA8B,SAAS,CAAC,EAAK,EAAU,CAC1F,CAEA,SAAS,EAAmB,EAAyC,CACnE,IAAM,EAAI,EAAO,CAAG,EAEpB,OADK,EACE,CAAE,QAAS,EAAK,EAAG,SAAS,EAAG,QAAS,EAAI,EAAG,SAAS,EAAG,MAAO,EAAM,EAAG,OAAO,CAAE,EAD5E,CAAE,QAAS,KAAM,QAAS,KAAM,MAAO,IAAK,CAE7D,CAmBA,IAAM,EAA6D,CACjE,GAAI,UACJ,KAAM,UACN,QAAS,WACT,QAAS,SACX,EAEA,SAAS,EAAkB,EAA2D,CACpF,IAAM,EAAW,EAAE,MACnB,GAAI,OAAO,GAAa,UAAY,EAAE,KAAY,GAAc,OAAO,KACvE,IAAM,EAAQ,EAAO,EAAE,KAAQ,EACzB,EAAO,EAAI,EAAG,MAAM,EACpB,EAAO,EAAM,EAAG,MAAM,EACtB,EAAa,EAAQ,EAAM,EAAO,YAAY,EAAI,KAClD,EAAO,EAAQ,EAAM,EAAO,MAAM,EAAI,KAGtC,EAAa,EAAQ,EAAI,EAAO,QAAQ,EAAI,KAC5C,EAAU,IAAe,MAAQ,IAAe,UAChD,EAAQ,EAAU,EAAa,KAE/B,EAAU,EAAI,EAAG,iBAAiB,EAClC,EAAW,EAAI,EAAG,aAAa,EAC/B,EAAiB,EAAM,EAAG,gBAAgB,EAC1C,EAAe,EAAM,EAAG,cAAc,EAE5C,MAAO,CAKL,KAAM,IAAa,SAAW,SAAW,IAAa,UAAY,UAAY,eAC9E,MAAO,EAAY,IAAa,UAChC,SAAU,IAAS,MAAQ,IAAS,KAAO,GAAG,EAAK,GAAG,IAAU,EAAI,EAAG,QAAQ,GAAK,KAKpF,MAAO,EAAgB,EAAI,EAAG,WAAW,CAAC,GAAK,EAAgB,EAAI,EAAG,OAAO,CAAC,EAC9E,WAAY,EACZ,UAAW,EAAU,EAAO,KAG5B,cACE,IACC,IAAU,MAAQ,EAAQ,GAAK,IAAiB,MAAQ,EAAe,EACpE,KAAK,MAAM,EAAe,CAAK,EAC/B,MACN,UAAW,EAAI,EAAG,WAAW,EAC7B,SAAU,CAMR,QAAS,IAAY,KAAe,GAAW,IAAU,KAAO,GAAO,KAA3C,GAE5B,UACA,MAAO,CACT,EACA,OAAQ,EAAI,EAAG,WAAW,EAC1B,WAAY,EAAK,EAAG,SAAS,GAAK,GAClC,UAAW,EAAI,EAAG,WAAW,CAC/B,CACF,CAGA,SAAS,EAAgB,EAAmC,CAC1D,GAAI,IAAQ,MAAQ,EAAI,SAAW,EAAG,OAAO,KAC7C,IAAM,EAAO,EAAI,MAAM,GAAG,CAAC,CAAC,IAAI,GAAK,EAGrC,OAFI,EAAK,SAAW,GAChB,6BAA6B,KAAK,CAAI,EAAU,KAC7C,CACT,CAEA,SAAgB,EAAsB,EAA6C,CACjF,IAAM,EAAI,EAAO,CAAG,EACpB,GAAI,CAAC,EAAG,OAAO,KAEf,GAAI,EAAE,SAAU,IAAM,UAAW,GAAK,UAAW,EAAG,CAClD,IAAM,EAAQ,EAAkB,CAAC,EACjC,GAAI,IAAU,KAAM,OAAO,CAC7B,CACA,MAAO,CACL,KAAM,EAAM,EAAG,OAAQ,EAAe,SAAS,EAC/C,MAAO,EAAM,EAAG,QAAS,EAAgB,SAAS,EAClD,SAAU,EAAI,EAAG,UAAU,EAC3B,MAAO,EAAI,EAAG,OAAO,EACrB,WAAY,EAAM,EAAG,YAAY,EACjC,UAAW,EAAM,EAAG,WAAW,EAC/B,cAAe,EAAM,EAAG,eAAe,EACvC,UAAW,EAAI,EAAG,WAAW,EAC7B,SAAU,EAAmB,EAAE,QAAW,EAC1C,OAAQ,EAAI,EAAG,QAAQ,EAIvB,WAAY,EAAK,EAAG,YAAY,GAAK,GACrC,UAAW,EAAI,EAAG,WAAW,CAC/B,CACF,CAEA,SAAS,EAAa,EAAiC,CACrD,IAAM,EAAI,EAAO,CAAG,EACpB,GAAI,CAAC,EAAG,OAAO,KACf,IAAM,EAAK,EAAI,EAAG,IAAI,EAEtB,OADI,IAAO,KAAa,KACjB,CACL,KACA,KAAM,EAAI,EAAG,MAAM,EACnB,KAAM,EAAI,EAAG,MAAM,EACnB,OAAQ,EAAI,EAAG,QAAQ,EACvB,MAAO,EAAI,EAAG,OAAO,EACrB,MAAO,EAAM,EAAG,QAAS,EAAc,SAAS,EAChD,UAAW,EAAI,EAAG,WAAW,EAC7B,KAAM,EAAM,EAAG,MAAM,CACvB,CACF,CAEA,SAAgB,EAAa,EAAoC,CAC/D,IAAM,EAAI,EAAO,CAAG,EAGpB,OAFK,EAEE,CACL,QAFgB,MAAM,QAAQ,EAAE,MAAS,EAAK,EAAE,OAA0B,CAAC,EAAA,CAEzD,IAAI,CAAY,CAAC,CAAC,OAAQ,GAAuB,IAAM,IAAI,EAC7E,WAAY,EAAM,EAAG,YAAY,EACjC,WAAY,EAAM,EAAG,YAAY,EACjC,UAAW,EAAM,EAAG,WAAW,EAC/B,MAAO,EAAQ,EAAG,OAAO,EACzB,UAAW,EAAI,EAAG,WAAW,CAC/B,EATe,IAUjB,CAEA,SAAS,EAAe,EAAuC,CAC7D,IAAM,EAAI,EAAO,CAAG,EACpB,GAAI,CAAC,EAAG,OAAO,KACf,IAAM,EAAS,EAAI,EAAG,QAAQ,EAE9B,OADI,IAAW,KAAa,KACrB,CAAE,SAAQ,OAAQ,EAAI,EAAG,QAAQ,GAAK,4DAA6D,CAC5G,CAcA,IAAM,EAA+C,IAAI,IAAqB,CAC5E,MACA,aACA,QACA,SACF,CAAC,EAED,SAAgB,EAAiB,EAAsC,CACrE,IAAM,EAAI,EAAO,CAAG,EACpB,GAAI,CAAC,EAAG,OAAO,KACf,IAAM,EAAc,MAAM,QAAQ,EAAE,OAAU,EAAK,EAAE,QAA2B,CAAC,EAC3E,EAAS,EAAM,EAAG,SAAU,EAAoB,SAAS,EAC/D,MAAO,CAGL,QAAS,EAAK,EAAG,SAAS,GAAK,GAC/B,SACA,QAAS,EAAY,IAAI,CAAc,CAAC,CAAC,OAAQ,GAA6B,IAAM,IAAI,EAMxF,QAAS,EAAe,IAAI,CAAM,EAAI,GAAS,EAAK,EAAG,SAAS,GAAK,GACrE,OAAQ,EAAI,EAAG,QAAQ,EACvB,UAAW,EAAI,EAAG,WAAW,GAAK,EAAI,EAAG,WAAW,CACtD,CACF,CA8BA,SAAgB,EAAa,EAAkC,CAC7D,OAAQ,EAAR,CACE,IAAK,eACH,MAAO,eACT,IAAK,SACH,MAAO,SACT,IAAK,OACH,MAAO,OACT,QACE,MAAO,qBACX,CACF,CAEA,SAAgB,EAAgB,EAAyD,CACvF,GAAI,IAAY,KACd,MAAO,CACL,QAAS,UACT,qBAAsB,KACtB,gBAAiB,KACjB,WAAY,GACZ,SAAU,8BACV,OACE,2IACF,KAAM,SACR,EAGF,IAAM,EAAa,EAAQ,YAAc,EAAQ,SAAS,MACpD,EAAO,EAAa,EAAQ,IAAI,EAEtC,GAAI,EAAQ,QAAU,UACpB,MAAO,CACL,QAAS,UACT,qBAAsB,KACtB,gBAAiB,EACjB,WAAY,GACZ,SAAU,EAAQ,QAAU,UAAY,cAAgB,sBAAsB,EAAQ,QACtF,OACE,EAAQ,QAAU,UACd,GAAG,EAAK,0FACR,GAAG,EAAK,MAAM,EAAQ,MAAM,qDAClC,KAAM,EAAQ,QAAU,UAAY,UAAY,SAClD,EAGF,GAAI,EAAQ,SAAS,UAAY,GAAO,CAGtC,IAAM,EAAa,IAAe,MAAQ,EAAa,EACvD,MAAO,CACL,QAAS,aACT,qBAAsB,EACtB,gBAAiB,EACjB,aACA,SAAU,GAAG,EAAK,sBAClB,OAAQ,EACJ,GAAG,EAAK,OAAO,EAAW,gHAAgH,EAAa,EAAE,mEACzJ,GAAG,EAAK,2FACZ,KAAM,QACR,CACF,CA4BA,OA1BI,EAAQ,SAAS,UAAY,KACxB,CACL,QAAS,UACT,qBAAsB,KACtB,gBAAiB,EACjB,WAAY,GACZ,SAAU,yBACV,OAAQ,GAAG,EAAK,aACd,IAAe,KAAO,GAAK,SAAS,EAAW,mBAChD,iKACD,KAAM,SACR,EAGE,IAAe,KACV,CACL,QAAS,UACT,qBAAsB,KACtB,gBAAiB,KACjB,WAAY,GACZ,SAAU,GAAG,EAAK,8BAClB,OAAQ,GAAG,EAAK,uFAChB,KAAM,SACR,EAGK,CACL,QAAS,UACT,qBAAsB,EACtB,gBAAiB,EACjB,WAAY,GACZ,SAAU,GAAG,EAAW,QAAQ,IAAe,EAAI,GAAK,IAAI,cAC5D,OAAQ,GAAG,EAAK,kBAAkB,EAAW,OAC3C,IAAe,EAAI,GAAK,IACzB,uCAAuC,EAAW,QACjD,IAAe,EAAI,GAAK,IACzB,2BACD,KAAM,SACR,CACF,CAeA,IAAa,EAA0D,CACrE,CACE,QAAS,SACT,kBAAmB,CAAC,IAAK,IAAK,KAAM,IAAI,EACxC,YAAa,KACb,QAAS,iCACX,EACA,CACE,QAAS,eACT,kBAAmB,CAAC,IAAK,IAAK,EAAK,CAAG,EACtC,YAAa,IACb,QAAS,iCACX,CACF,EAwBA,SAAgB,EAAgB,EAAqB,EAAgD,CACnG,GAAI,IAAS,MAAQ,IAAmB,MAAQ,GAAkB,EAChE,MAAO,CACL,OACA,MAAO,EACP,QAAS,KACT,UAAW,GACX,MACE,IAAS,KACL,kCACA,GAAG,EAAK,4EAChB,EAGF,IAAM,EAAU,KAAK,MADL,KAAK,IAAI,EAAM,CACH,EAAU,EAAkB,GAAG,EACrD,EAAY,GAAQ,EAC1B,MAAO,CACL,OACA,MAAO,EACP,UACA,YACA,MAAO,EACH,OAAO,EAAe,OAAO,IAAmB,EAAI,GAAK,IAAI,4CAC7D,GAAG,EAAK,MAAM,EAAe,OAAO,IAAmB,EAAI,GAAK,IAAI,OAC1E,CACF,CAgBA,SAAgB,EACd,EACA,EACA,EACe,CACf,GAAI,IAAU,KACZ,MAAO,CACL,MAAO,UACP,SAAU,qBACV,OACE,oHACF,KAAM,SACR,EAGF,IAAM,EAAU,EAAM,OAAO,OAAQ,GAAM,EAAE,QAAU,QAAQ,CAAC,CAAC,OAC3D,EAAS,EAAM,YAAc,EAAM,OAAO,OAAQ,GAAM,EAAE,QAAU,QAAQ,CAAC,CAAC,OAkCpF,OAhCI,EAAS,EACJ,CACL,MAAO,SACP,SAAU,GAAG,EAAO,qBACpB,OACE,EAAS,UAAY,aACjB,GAAG,EAAQ,OAAO,IAAY,EAAI,GAAK,IAAI,iBAAiB,EAAO,+GACnE,GAAG,EAAQ,OAAO,IAAY,EAAI,GAAK,IAAI,iBAAiB,EAAO,iBACjE,EAAY,QAAU,KAAO,sBAAwB,GAAG,EAAY,MAAM,QAC3E,yEACP,KAAM,SACR,EAGE,IAAY,EACP,CACL,MAAO,OACP,SAAU,oBACV,OAAQ,sDACR,KAAM,SACR,EAGE,EAAY,UACP,CACL,MAAO,YACP,SAAU,oBAAoB,EAAQ,YACtC,OAAQ,6GACR,KAAM,SACR,EAGK,CACL,MAAO,UACP,SAAU,GAAG,EAAQ,YACrB,OAAQ,EAAY,MACpB,KAAM,SACR,CACF,CAuBA,IAAM,EAAkB,0DAUxB,SAAgB,EACd,EACA,EACiB,CACjB,IAAM,EAAU,EAAM,OAAQ,GAAM,EAAE,SAAW,OAAO,EAClD,EAAU,EAAM,OAAQ,GAAM,EAAE,SAAW,OAAO,EAClD,EAAW,IAAI,IACrB,IAAK,IAAM,KAAW,GAAQ,SAAW,CAAC,EAAG,EAAS,IAAI,EAAQ,OAAQ,EAAQ,MAAM,EASxF,MAAO,CACL,UACA,UACA,WAAa,GAAmB,EAAS,IAAI,CAAM,GAAK,EACxD,QAVA,EAAM,SAAW,EACb,6EACA,EAAQ,SAAW,EACjB,qBAAqB,EAAM,OAAO,OAAO,EAAM,SAAW,EAAI,GAAK,IAAI,sDACvE,GAAG,EAAQ,OAAO,MAAM,EAAM,OAAO,OAAO,EAAM,SAAW,EAAI,GAAK,IAAI,6BAA6B,EAAQ,OAAO,aAAa,EAAQ,SAAW,EAAI,GAAK,IAAI,qBAO3K,CACF,CAGA,SAAgB,EAAoB,EAA+C,CACjF,GAAI,IAAW,KAAM,OAAO,KAI5B,GAAI,EAAO,SAAW,KAAM,OAAO,EAAO,OAC1C,OAAQ,EAAO,OAAf,CACE,IAAK,MACL,IAAK,aACH,MAAO,qKACT,IAAK,QACH,MAAO,qNACT,IAAK,SACH,MAAO,6DACT,IAAK,MACH,MAAO,+CACT,QACE,MAAO,sDACX,CACF,CAOA,SAAgB,EAAa,EAAoB,EAAqB,CACpE,GAAI,CAAC,EAAK,MAAA,IACV,IAAM,EAAK,KAAK,MAAM,CAAG,EAEzB,OADI,OAAO,MAAM,CAAE,EAAG,IACf,EAAe,KAAK,IAAI,EAAG,EAAM,CAAE,CAAC,CAC7C,CAGA,SAAgB,EAAa,EAA0B,EAAqB,CAC1E,GAAI,CAAC,EAAW,MAAA,IAChB,IAAM,EAAK,KAAK,MAAM,CAAS,EAE/B,OADI,OAAO,MAAM,CAAE,EAAG,IACf,MAAM,EAAe,KAAK,IAAI,EAAG,EAAM,CAAE,CAAC,GACnD,CAQA,SAAgB,EAAoB,EAA+B,CAGjE,OAFI,IAAW,MAAQ,CAAC,OAAO,SAAS,CAAM,EAAG,IAC7C,GAAU,KAAa,GAAG,KAAK,MAAM,EAAS,IAAI,EAAE,GACjD,OAAO,CAAM,CACtB,CAGA,SAAgB,EAAY,EAA6C,CACvE,IAAM,EAAwC,CAAE,QAAS,EAAG,UAAW,EAAG,OAAQ,CAAE,EACpF,MAAO,CAAC,GAAG,CAAM,CAAC,CAAC,MAAM,EAAG,IAAM,CAChC,IAAM,EAAU,EAAK,EAAE,OAAS,EAAK,EAAE,OACvC,GAAI,IAAY,EAAG,OAAO,EAG1B,IAAM,EAAK,EAAE,UAAY,KAAK,MAAM,EAAE,SAAS,EAAI,IAC7C,EAAK,EAAE,UAAY,KAAK,MAAM,EAAE,SAAS,EAAI,IAInD,OAHI,OAAO,MAAM,CAAE,GAAK,OAAO,MAAM,CAAE,EAAU,EAAE,GAAG,cAAc,EAAE,EAAE,EACpE,OAAO,MAAM,CAAE,EAAU,EACzB,OAAO,MAAM,CAAE,EAAU,GACtB,EAAK,CACd,CAAC,CACH"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function e(e){return typeof e!=`number`||!Number.isFinite(e)?`—`:`$${e.toFixed(2)}`}function t(e){return typeof e!=`number`||!Number.isFinite(e)?`—`:String(e)}function n(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 r(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 i(e){let t=r(e);return t===`—`?`—`:`${t} ago`}function a(e){if(!e)return`—`;let t=Date.parse(e);return Number.isNaN(t)?`—`:new Date(t).toLocaleTimeString(void 0,{hour:`2-digit`,minute:`2-digit`,second:`2-digit`})}function o(e=new Date){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}function s(e,t=new Date){return e===o(t)||e===t.toISOString().slice(0,10)}function c(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 l(e){return typeof e!=`number`||!Number.isFinite(e)||e<=0?`—`:`every ${n(e).replace(/ 00[ms]$/,``)}`}function u(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,c=typeof r==`string`&&!s(r,i);if(o===0)return{state:`stopped`,percent:100,label:`${e(a)} spent today · budget $0.00`,note:`Daily budget is 0 — the loop is stopped, not unlimited.`};if(c)return{state:`unknown`,percent:null,label:`— spent today · budget ${e(o)}`,note:`Nothing has been recorded today — the last ledger day is ${r}.`};if(o===null||a===null)return{state:`unknown`,percent:null,label:o===null?`${e(a)} spent today · budget —`:`— spent today · budget ${e(o)}`,note:o===null?`Daily budget is not configured.`:`Today's spend is not observable right now.`};let l=a/o*100,u=Math.min(100,Math.round(l)),d=l>=100?`over`:l>=70?`warn`:`ok`;return{state:d,percent:u,label:`${e(a)} of ${e(o)} today · ${u}%`,note:d===`over`?`Daily budget reached — the loop idles until the date rolls over.`:d===`warn`?`${e(o-a)} left before the loop idles today.`:`${e(o-a)} left today.`}}function d(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 r=e-t;return r>1e3?`in ${n(r)}`:r>-1e3?`due now`:`overdue by ${n(-r)}`}function p(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{a,l as c,e as d,s as f,r as i,i as l,f as n,t as o,d as p,p as r,n as s,u as t,c as u};
|
|
2
|
-
//# sourceMappingURL=format-BNYWGJat.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format-BNYWGJat.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/autonomy/format.ts"],"sourcesContent":["/**\n * routes/verse/autonomy/format.ts — the small pure functions the cockpit's\n * numerals go through. Kept framework-free so they unit-test without a DOM,\n * and kept in ONE place so \"unknown\" never renders as 0 in one panel and as\n * an em-dash in the next.\n *\n * The rule every function here obeys: absent is not zero. A null spend is\n * \"—\", a null budget is \"not configured\", and a budget of exactly 0 is\n * \"stopped\" — which is the single most misread number in the whole product.\n */\n\n/** What we render when a value genuinely is not known. */\nexport const UNKNOWN = '—';\n\nexport function formatUsd(value: number | null | undefined): string {\n if (typeof value !== 'number' || !Number.isFinite(value)) return UNKNOWN;\n return `$${value.toFixed(2)}`;\n}\n\nexport function formatCount(value: number | null | undefined): string {\n if (typeof value !== 'number' || !Number.isFinite(value)) return UNKNOWN;\n return String(value);\n}\n\n/** \"4m 12s\" / \"1h 06m\" / \"12s\" — for durations and countdowns alike. */\nexport function formatDuration(ms: number): string {\n const total = Math.max(0, Math.round(ms / 1000));\n if (total < 60) return `${total}s`;\n const minutes = Math.floor(total / 60);\n const seconds = total % 60;\n if (minutes < 60) return `${minutes}m ${String(seconds).padStart(2, '0')}s`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${String(minutes % 60).padStart(2, '0')}m`;\n}\n\n/** Compact age for dense table cells: \"42s\", \"7m\", \"3h\", \"5d\". */\nexport function formatAge(iso: string | null | undefined): string {\n if (!iso) return UNKNOWN;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return UNKNOWN;\n const seconds = Math.max(0, Math.round((Date.now() - ms) / 1000));\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${hours}h`;\n return `${Math.round(hours / 24)}d`;\n}\n\nexport function formatRelative(iso: string | null | undefined): string {\n const age = formatAge(iso);\n return age === UNKNOWN ? UNKNOWN : `${age} ago`;\n}\n\n/** Local wall-clock time, for the audit table's second column. */\nexport function formatClock(iso: string | null | undefined): string {\n if (!iso) return UNKNOWN;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return UNKNOWN;\n return new Date(ms).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' });\n}\n\n/** The viewer's local calendar day as `YYYY-MM-DD` — NOT the UTC slice. */\nexport function localDateKey(at: Date = new Date()): string {\n const y = at.getFullYear();\n const m = String(at.getMonth() + 1).padStart(2, '0');\n const d = String(at.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n\n/**\n * Is `todayDate` still the current ledger day?\n *\n * The daemon stamps it in UTC (`resetDayIfNeeded` in core/daemon/state.ts is\n * `new Date().toISOString().slice(0, 10)`) but the operator reads the screen\n * in local time, and the two disagree for part of every day at any offset but\n * zero. Comparing against the local day alone would announce \"nothing has been\n * recorded today\" every evening west of UTC — a false alarm on the meter whose\n * whole purpose is to be trustworthy.\n *\n * So either spelling counts as current. That is deliberately conservative: it\n * can call a day current for a few hours after the UTC rollover, and it will\n * never call a genuinely current day stale. The case this exists for is a\n * ledger day weeks old, which neither spelling matches.\n */\nexport function isCurrentLedgerDay(todayDate: string, now: Date = new Date()): boolean {\n return todayDate === localDateKey(now) || todayDate === now.toISOString().slice(0, 10);\n}\n\n/**\n * Time-of-day for a row from today; date + time for anything older.\n *\n * `formatClock` alone is only honest inside a single-day window, and the\n * tables that use it are not one: the audit trail defaults to 200 rows and\n * offers 500, which on a real machine spans days. Two rows reading \"03:12:44\"\n * could be last night and last week, and the only date was in a hover title\n * that a touch viewport never fires.\n */\nexport function formatStamp(iso: string | null | undefined, now: Date = new Date()): string {\n if (!iso) return UNKNOWN;\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return UNKNOWN;\n const at = new Date(ms);\n const clock = at.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' });\n if (localDateKey(at) === localDateKey(now)) return clock;\n const day = at.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n return `${day} ${clock}`;\n}\n\n/** Human tick interval, e.g. \"every 15m\". */\nexport function formatInterval(intervalMs: number | null | undefined): string {\n if (typeof intervalMs !== 'number' || !Number.isFinite(intervalMs) || intervalMs <= 0) return UNKNOWN;\n // A configured interval is a round number far more often than not; \"every\n // 15m\" reads better than \"every 15m 00s\" and loses nothing when it isn't.\n return `every ${formatDuration(intervalMs).replace(/ 00[ms]$/, '')}`;\n}\n\nexport type BudgetState = 'stopped' | 'unknown' | 'ok' | 'warn' | 'over';\n\nexport interface BudgetMeter {\n state: BudgetState;\n /** 0-100, clamped. Null when there is nothing honest to draw. */\n percent: number | null;\n /** One line: \"$4.50 of $25.00 today · 18%\". */\n label: string;\n /** The plain-language verdict shown next to the meter. */\n note: string;\n}\n\nconst BUDGET_WARN_PERCENT = 70;\n\n/**\n * Today's spend against the configured daily cap, as ONE line.\n *\n * A cap of exactly 0 is the contract's guard rail: it means the loop is\n * stopped, and it must never render as an empty (i.e. \"plenty left\") meter.\n *\n * `todayDate` is the ledger day `spend.todayUsd` was counted for. It is NOT\n * optional decoration: the daemon writes the figure once per day and leaves it\n * there, so a machine that last ticked three weeks ago answers\n * `{todayUsd: 0, todayDate: \"2026-09-01\"}`. Rendering that as \"$0.00 of $50.00\n * today\" is a fabricated statement about today on the one number an operator\n * checks before walking away from an autonomous loop. Pass `null` only when\n * the source genuinely has no date to offer (the legacy daemon observation).\n */\nexport function budgetMeter(\n spentUsd: number | null | undefined,\n capUsd: number | null | undefined,\n todayDate?: string | null,\n now: Date = new Date(),\n): BudgetMeter {\n const spend = typeof spentUsd === 'number' && Number.isFinite(spentUsd) ? Math.max(0, spentUsd) : null;\n const cap = typeof capUsd === 'number' && Number.isFinite(capUsd) ? capUsd : null;\n // A cap of 0 is a statement about configuration, not about the ledger, so\n // it still reads true on a stale day and is checked first.\n const stale = typeof todayDate === 'string' && !isCurrentLedgerDay(todayDate, now);\n\n if (cap === 0) {\n return {\n state: 'stopped',\n percent: 100,\n label: `${formatUsd(spend)} spent today · budget $0.00`,\n note: 'Daily budget is 0 — the loop is stopped, not unlimited.',\n };\n }\n if (stale) {\n return {\n state: 'unknown',\n percent: null,\n label: `${UNKNOWN} spent today · budget ${formatUsd(cap)}`,\n note: `Nothing has been recorded today — the last ledger day is ${todayDate}.`,\n };\n }\n if (cap === null || spend === null) {\n return {\n state: 'unknown',\n percent: null,\n label: cap === null ? `${formatUsd(spend)} spent today · budget ${UNKNOWN}` : `${UNKNOWN} spent today · budget ${formatUsd(cap)}`,\n note: cap === null ? 'Daily budget is not configured.' : \"Today's spend is not observable right now.\",\n };\n }\n\n const raw = (spend / cap) * 100;\n const percent = Math.min(100, Math.round(raw));\n const state: BudgetState = raw >= 100 ? 'over' : raw >= BUDGET_WARN_PERCENT ? 'warn' : 'ok';\n return {\n state,\n percent,\n label: `${formatUsd(spend)} of ${formatUsd(cap)} today · ${percent}%`,\n note:\n state === 'over'\n ? 'Daily budget reached — the loop idles until the date rolls over.'\n : state === 'warn'\n ? `${formatUsd(cap - spend)} left before the loop idles today.`\n : `${formatUsd(cap - spend)} left today.`,\n };\n}\n\n/** Epoch ms of the next expected tick, or null when it cannot be derived. */\nexport function nextTickAt(lastTickAt: string | null | undefined, intervalMs: number | null | undefined): number | null {\n if (!lastTickAt) return null;\n const last = Date.parse(lastTickAt);\n if (Number.isNaN(last)) return null;\n if (typeof intervalMs !== 'number' || !Number.isFinite(intervalMs) || intervalMs <= 0) return null;\n return last + intervalMs;\n}\n\n/** \"in 4m 12s\" / \"due now\" / \"overdue by 2m 03s\". */\nexport function countdownLabel(nextAt: number | null, now: number): string {\n if (nextAt === null) return UNKNOWN;\n const delta = nextAt - now;\n if (delta > 1000) return `in ${formatDuration(delta)}`;\n if (delta > -1000) return 'due now';\n return `overdue by ${formatDuration(-delta)}`;\n}\n\n/** Last tick outcome, phrased the way a person would say it out loud. */\nexport function describeTickOutcome(reason: string | null | undefined): { label: string; tone: 'success' | 'warning' | 'danger' | 'neutral' | 'unknown' } {\n if (!reason) return { label: 'no tick recorded', tone: 'unknown' };\n switch (reason) {\n case 'ok':\n return { label: 'ok', tone: 'success' };\n case 'kill-switch':\n return { label: 'refused — emergency stop engaged', tone: 'danger' };\n case 'budget-exhausted':\n return { label: 'stopped — daily budget spent', tone: 'warning' };\n case 'no-enrolled-repos':\n return { label: 'nothing to do — no repos enrolled', tone: 'warning' };\n case 'no-backlog':\n return { label: 'nothing to do — backlog empty', tone: 'neutral' };\n case 'dry-run':\n return { label: 'dry run', tone: 'neutral' };\n default:\n return { label: reason, tone: 'neutral' };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as e,n as t,r as n,s as r}from"./index-afDA8cwW.js";var i=r(e(),1),a={data:void 0,error:void 0,status:`idle`,updatedAt:null},o=new Map,s=0,c=[],l=0,u=0;function d(){--s;let e=c.shift();e&&e()}function ee(e){let t=()=>{let t=u;s+=1,s>l&&(l=s);let n=()=>{t===u&&d()},r;try{r=e()}catch(e){return n(),Promise.reject(e instanceof Error?e:Error(String(e)))}return r.then(e=>(n(),e),e=>{throw n(),e})};return s<4?t():new Promise((e,n)=>{c.push(()=>{t().then(e,n)})})}function te(){u+=1,s=0,l=0;for(let e of c.splice(0))e()}function f(e){let t=o.get(e);return t||(t={snapshot:a,inFlight:null,fetcher:null,subscribers:new Set,issued:0,issuedAt:0,applied:0},o.set(e,t)),t}function p(e,t){e.snapshot={...e.snapshot,...t}}function m(e){let t=o.get(e);if(t)for(let e of t.subscribers)e()}function h(e,t){if(Object.is(e,t))return!0;if(typeof e!=typeof t||e===null||t===null||typeof e!=`object`||Array.isArray(e)!==Array.isArray(t))return!1;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((e,n)=>h(e,t[n]));let n=e,r=t,i=Object.keys(n);return i.length===Object.keys(r).length&&i.every(e=>Object.prototype.hasOwnProperty.call(r,e)&&h(n[e],r[e]))}function g(e,t){let n=f(e);return n.subscribers.add(t),()=>{n.subscribers.delete(t)}}function _(e){return f(e).snapshot}function ne(e,t){return e.snapshot.updatedAt===null||e.snapshot.status===`error`?!1:Date.now()-e.snapshot.updatedAt<t}function re(e,t){return e.inFlight===null?!1:t.joinWindowMs===void 0||Date.now()-e.issuedAt<t.joinWindowMs}function ie(e,t,n){let r=f(e);if(r.fetcher=t,!n.force){if(re(r,n))return r.inFlight;if(n.maxAgeMs!==void 0&&ne(r,n.maxAgeMs))return Promise.resolve()}let i=r.issued+=1;r.issuedAt=Date.now(),p(r,{status:r.snapshot.data===void 0?`loading`:`refreshing`}),m(e);let a=ee(t).then(e=>{if(i<r.applied)return;r.applied=i;let t=r.snapshot.data!==void 0&&h(r.snapshot.data,e);p(r,{data:t?r.snapshot.data:e,error:void 0,status:`success`,updatedAt:Date.now()})}).catch(e=>{i<r.applied||(r.applied=i,p(r,{error:e instanceof Error?e:Error(String(e)),status:`error`}))}).finally(()=>{r.issued===i&&(r.inFlight=null),m(e)});return r.inFlight=a,a}function ae(e,t){return ie(e,t,{})}function oe(e,t,n){return ie(e,t,{maxAgeMs:n})}function se(e,t,n=!1){return ie(e,t,n?{force:!0}:{joinWindowMs:250})}function ce(e){let t=o.get(e);t&&t.fetcher&&ae(e,t.fetcher)}function v(e){for(let t of o.keys())t.startsWith(e)&&ce(t)}function le(e,t){t.snapshot=a,t.fetcher=null,t.inFlight=null,t.issued+=1,t.applied=t.issued,m(e)}function ue(){te();for(let[e,t]of[...o]){if(t.subscribers.size===0){o.delete(e);continue}le(e,t)}}var de=4e5,fe=2e4,pe=13e3,me=.9,he=36e5,ge={claude:25e3,codex:15e3,grok:2e4,local:15e3},_e=25e3;function ve(e){return e?ge[e]??25e3:_e}ge.local+8e3+fe+pe;function y(e){return typeof e==`number`&&Number.isFinite(e)&&e>0?Math.floor(e):null}function ye(e,t,n=null){let r=Math.min(e,y(n)??e),i=Math.min(y(t)??2e4,fe);return Math.max(0,r-i-pe)}function be(e){return Math.floor(e*me)}var xe={"claude-opus-5.5":`claude-opus-5-5`};function Se(e){return xe[e]??e}function b(e,t){if(!e)return null;if(t===`expansive`)return e.expansive??null;let n=y(e.contextWindow);return n===null?null:{contextWindow:n,autoCompactAt:y(e.autoCompactAt??null)}}function x(e){let t=b(e,`standard`),n=b(e,`expansive`);return n?!t||(n.autoCompactAt??n.contextWindow)>(t.autoCompactAt??t.contextWindow):!1}function Ce(e,t){if(t===`expansive`)return null;let n=y(e?.contextWindow??null);return n!==null&&n>4e5&&x(e)?de:null}function we(e){let t=y(e.runtimeWindow);if(t===null)return e.budget?.autoCompactAt??null;if(e.budget&&e.budget.contextWindow===t)return e.budget.autoCompactAt;switch(e.engine){case`claude`:case`local`:return ye(t,e.maxOutputTokens??null,e.autocompactWindow??null);case`codex`:return be(Math.round(t*100/95));default:{let n=e.budget;return!n||n.autoCompactAt===null||n.contextWindow<=0?null:Math.floor(t*(n.autoCompactAt/n.contextWindow))}}}var Te=.8,Ee=.95;function De(e){let t=Math.max(0,Math.floor(e.contextTokens||0)),n=y(e.contextWindow),r=y(e.autoCompactAt??null),i=n===null?null:t/n,a=r===null?null:t/r,o=a??i,s=e.contextTokensExact!==!1,c=`unknown`;return!s&&o!==null&&o>=1?c=`unknown`:n!==null&&t>n?c=`over`:o!==null&&(c=o>=.95?`danger`:o>=.8?`warn`:`ok`),{tokens:t,window:n,autoCompactAt:r,ofWindow:i,ofCompaction:a,untilCompaction:r===null?null:Math.max(0,r-t),tone:c,exact:s}}function Oe(e){let t=e.inputTokens+e.cacheReadTokens+e.cacheCreationTokens;return t>0?e.cacheReadTokens/t:null}function ke(e){return Math.ceil(Math.max(0,e)/4)}function Ae(e,t,n=_e){let r=b(t,`standard`);if(!r)return null;let i=Math.max(0,e)+Math.max(0,n),a=r.autoCompactAt??r.contextWindow;if(i<=a*.6)return`fits`;if(i<=a)return`tight`;let o=b(t,`expansive`);return o&&i<=(o.autoCompactAt??o.contextWindow)?`expansive`:`split`}function S(e){return e>=1e6?`${(e/1e6).toFixed(e%1e6==0?0:1)}M`:e>=1e3?`${Math.round(e/1e3)}k`:String(e)}function je(e){let t=De(e.usage),n=[],r=`none`,i=e=>{(e===`urge`||e===`suggest`&&r===`none`)&&(r=e)};t.exact&&(t.tone===`over`?(i(`urge`),n.push(`Context (${S(t.tokens)}) is past the ${S(t.window??0)} window.`)):t.ofCompaction!==null&&t.ofCompaction>=.8&&(i(`suggest`),n.push(`About ${S(t.untilCompaction??0)} tokens left before the CLI auto-compacts.`)));let a=e.compactionCount??0;if(a>=2&&(i(`suggest`),n.push(`Compacted ${a} times — early turns now survive only as summaries.`)),e.lastActivityAt){let r=e.now-Date.parse(e.lastActivityAt);t.exact&&Number.isFinite(r)&&r>=36e5&&t.tokens>=1e5&&(i(`suggest`),n.push(`Idle over an hour: the prompt cache has likely expired, so the next turn re-reads ~${S(t.tokens)} tokens at full cost.`))}return{level:r,reasons:n}}function Me(e){if((e.session.contextMode??`standard`)!==`standard`||!x(e.option))return{suggest:!1,reason:null};let t=e.session.compactionCount??0;return t>=2?{suggest:!0,reason:`This session has compacted ${t} times; expansive mode keeps more of it in view.`}:typeof e.workingSetTokens==`number`&&Ae(e.workingSetTokens,e.option)===`expansive`?{suggest:!0,reason:`The reachable code (~${S(e.workingSetTokens)} tokens) only fits the expansive budget.`}:{suggest:!1,reason:null}}function Ne(e){return e===`claude`||e===`codex`}var C=`ashlr.verse.drafts.v1`,w=`ashlr.verse.sent.v1`,Pe=40,T=65536;function E(e,t){let n={};try{let r=localStorage.getItem(e),i=r?JSON.parse(r):null;if(i&&typeof i==`object`&&!Array.isArray(i))for(let[e,r]of Object.entries(i)){let i=t(r);i!==null&&(n[e]=i)}}catch{}return n}function Fe(e,t){let n=Object.keys(t);for(let e of n.slice(0,Math.max(0,n.length-Pe)))delete t[e];try{return localStorage.setItem(e,JSON.stringify(t)),!0}catch{return!1}}var Ie=e=>typeof e==`string`&&e.length>0?e.slice(0,T):null,Le=e=>{if(!Array.isArray(e))return null;let t=e.filter(e=>typeof e==`string`&&e.length>0).slice(-25).map(e=>e.slice(0,T));return t.length>0?t:null},D=new Map;function Re(e){if(!e)return``;let t=D.get(e);return t===void 0?E(C,Ie)[e]??``:t}function ze(e,t){if(!e)return;let n=t.trim().length>0?t.slice(0,T):``,r=E(C,Ie);if(delete r[e],n&&(r[e]=n),D.delete(e),!Fe(C,r)){D.set(e,n);for(let e of[...D.keys()].slice(0,Math.max(0,D.size-Pe)))D.delete(e)}}function Be(e){return e?E(w,Le)[e]??[]:[]}function Ve(e,t){if(!e||t.trim().length===0)return;let n=E(w,Le),r=n[e]??[];delete n[e],n[e]=(r[r.length-1]===t?r:[...r,t.slice(0,T)]).slice(-25),Fe(w,n)}function He(e){if(e){D.delete(e);for(let[t,n]of[[C,Ie],[w,Le]]){let r=E(t,n);e in r&&(delete r[e],Fe(t,r))}}}function Ue(){D.clear();for(let e of[C,w])try{localStorage.removeItem(e)}catch{}}function We(e){let t=e.trim();return t.length===0?0:Math.max(1,Math.ceil(t.length/4))}function Ge(e,t){let n=We(e);if(n===0)return null;let r=typeof t.contextWindow==`number`&&t.contextWindow>0?t.contextWindow:null;if(r===null)return null;let i=De({contextTokens:(typeof t.contextTokens==`number`&&Number.isFinite(t.contextTokens)?Math.max(0,t.contextTokens):0)+n,contextWindow:r,autoCompactAt:t.autoCompactAt??null,contextTokensExact:t.exact});return i.tone===`ok`||i.tone===`unknown`?null:{draftTokens:n,projectedTokens:i.tokens,projectedPercent:Math.min(999,Math.round((i.ofWindow??0)*100)),autoCompactAt:i.autoCompactAt,pastCompaction:i.ofCompaction!==null&&i.ofCompaction>=1,tone:i.tone,exact:i.exact}}var Ke=`ashlr.readClientProof.v1`,qe=/^[a-f0-9]{64}$/,Je=12e5;function Ye(){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,`0`)).join(``)}function Xe(){try{let e=sessionStorage.getItem(Ke);if(e&&qe.test(e))return e}catch{}let e=Ye();try{sessionStorage.setItem(Ke,e)}catch{}return e}var Ze=Xe(),O=null,k=null,Qe=!1,A=null,j=null,M=null,N={phase:`checking`,checked:!1,mutationTokenHeldUntil:null},$e=new Set;function et(){for(let e of $e)e()}function P(e){N={...N,...e},et()}function F(e){return $e.add(e),()=>$e.delete(e)}function I(){return N}function tt(){return Ze}async function L(e){let t=e.trim();if(!/^[a-f0-9]{64}$/.test(t))throw Error(`That does not look like a read token — expected 64 hex characters.`);let n=await fetch(`/api/session`,{method:`POST`,credentials:`same-origin`,headers:{"x-ashlr-token":t,"x-ashlr-read-client":Ze}});if(n.status===204){A=t,P({phase:`authenticated`,checked:!0});return}throw n.status===401?Error(`Server rejected that token. Double-check the read token printed by this server at startup.`):Error(`Unexpected response establishing session (HTTP ${n.status}).`)}async function nt(){try{await fetch(`/api/session`,{method:`DELETE`,credentials:`same-origin`,headers:{"x-ashlr-read-client":Ze}})}catch{}A=null,M=null,R()}function R(){B(),ue(),Ue(),P({phase:`unauthenticated`,checked:!0})}function z(){if(N.phase===`authenticated`){if(A){rt();return}R()}}function rt(){if(j)return j;let e=A;return e?(j=(async()=>{try{return await L(e),v(``),!0}catch{return A=null,M=null,R(),!1}finally{j=null}})(),j):Promise.resolve(!1)}function it(e){P({phase:e?`authenticated`:`unauthenticated`,checked:!0})}function at(){let e=window.__ASHLR_TOKENS__;if(e&&typeof e==`object`){try{delete window.__ASHLR_TOKENS__}catch{window.__ASHLR_TOKENS__=void 0}typeof e.readToken==`string`&&(M={readToken:e.readToken,...typeof e.token==`string`?{token:e.token}:{}})}return M?{...M}:null}async function ot(){let e=at();if(!e)return!1;try{await L(e.readToken)}catch{return M=null,!1}return e.token&&/^[a-f0-9]{64}$/.test(e.token.trim())&&ut(e.token.trim(),{host:!0}),!0}function st(){return O}function ct(){return O!==null}function lt(){k&&clearTimeout(k),k=setTimeout(()=>{Qe&&O!==null?dt():B()},Je)}function ut(e,t={}){O=e,Qe=t.host===!0,P({mutationTokenHeldUntil:Date.now()+Je}),lt()}function dt(){O!==null&&(P({mutationTokenHeldUntil:Date.now()+Je}),lt())}function B(){O=null,Qe=!1,k&&=(clearTimeout(k),null),P({mutationTokenHeldUntil:null})}var ft=[`runs`,`swarms`,`inbox`,`daemon`,`daemon-observation`,`fleet-activity-ping`,`fleet-activity-observation`,`snapshot`,`session-expired`],pt={runs:[`runs`],swarms:[`swarms`],inbox:[`inbox`],daemon:[`daemon`,`control-snapshot`,`verse-control`,`verse-usage-control`,`verse-caps`],"daemon-observation":[`daemon`,`control-snapshot`,`verse-control`,`verse-usage-control`,`verse-caps`],"fleet-activity-ping":[`fleet-activity`,`fleet`],"fleet-activity-observation":[`fleet-activity`,`fleet`],snapshot:[`dashboard-snapshot`,`agent-os-snapshot`],"session-expired":[]},mt=new Map,V=null,H=null,U=0;function ht(){return Math.min(1e3*2**U,15e3)}function gt(){V&&=(V.close(),null),H&&=(clearTimeout(H),null)}function _t(){H||=setTimeout(()=>{H=null,U+=1,vt()},ht())}function vt(){if(n()){gt();return}if(I().phase!==`authenticated`)return;gt();let e=`/api/events?client=${encodeURIComponent(tt())}`,t=new EventSource(e,{withCredentials:!0});V=t,t.onopen=()=>{U=0};for(let e of ft)t.addEventListener(e,t=>{let n=t,r;try{r=JSON.parse(n.data)}catch{r=void 0}for(let t of pt[e])ce(t);e===`inbox`&&(v(`inbox-list:`),v(`proposal-detail-`)),e===`session-expired`&&z();let i=mt.get(e);if(i)for(let e of i)e(r)});t.onerror=()=>{t.close(),V===t&&(V=null),I().phase===`authenticated`&&_t()}}F(()=>{I().phase===`authenticated`?(U=0,vt()):gt()});var yt=`ashlr.theme.v1`;function bt(e){let t=document.documentElement;e===`system`?t.removeAttribute(`data-theme`):t.setAttribute(`data-theme`,e)}function xt(){try{let e=localStorage.getItem(yt);if(e===`light`||e===`dark`||e===`system`)return e}catch{}return`system`}var W=xt();bt(W);var St=new Set;function Ct(){for(let e of St)e()}function wt(e){return St.add(e),()=>St.delete(e)}function G(){return W}function K(e){W=e,bt(W);try{localStorage.setItem(yt,W)}catch{}Ct()}function Tt(){K(W===`system`?`light`:W===`light`?`dark`:`system`)}var Et=`ashlr.verse.appearance.v1`,Dt=30,Ot=78,q={h:245,s:72,l:58},kt=[{id:`indigo`,label:`Indigo`,h:245,s:72,l:58},{id:`blue`,label:`Blue`,h:212,s:80,l:50},{id:`cyan`,label:`Cyan`,h:190,s:80,l:40},{id:`teal`,label:`Teal`,h:168,s:70,l:38},{id:`green`,label:`Green`,h:145,s:60,l:38},{id:`amber`,label:`Amber`,h:38,s:80,l:42},{id:`rose`,label:`Rose`,h:348,s:72,l:50},{id:`violet`,label:`Violet`,h:278,s:66,l:55}];function At(){try{return typeof window<`u`&&typeof window.matchMedia==`function`&&window.matchMedia(`(prefers-reduced-motion: reduce)`).matches}catch{return!1}}function jt(){return{theme:`system`,accentH:q.h,accentS:q.s,accentL:q.l,density:`comfortable`,uiScale:`default`,displayFont:`grotesk`,radius:`default`,motion:`system`,reduceMotion:At()}}function Mt(e){return e===`system`||e===`reduce`||e===`full`}function Nt(e){return e===`reduce`||e!==`full`&&At()}var Pt=(e,t,n)=>Math.min(n,Math.max(t,e));function Ft(e){return e===`comfortable`||e===`compact`}function It(e){return e==="default"||e===`large`||e===`xlarge`}function Lt(e){return e===`grotesk`||e===`ui`||e===`mono`}function Rt(e){return e===`sharp`||e==="default"||e===`soft`}function zt(e,t){let n={...e,...t},r=t.reduceMotion,i=Mt(t.motion)?t.motion:typeof r==`boolean`?r?`reduce`:`full`:Mt(e.motion)?e.motion:`system`;return{theme:n.theme===`light`||n.theme===`dark`?n.theme:`system`,accentH:Number.isFinite(n.accentH)?(Math.round(n.accentH)%360+360)%360:q.h,accentS:Number.isFinite(n.accentS)?Pt(Math.round(n.accentS),0,100):q.s,accentL:Number.isFinite(n.accentL)?Pt(Math.round(n.accentL),Dt,Ot):q.l,density:Ft(n.density)?n.density:`comfortable`,uiScale:It(n.uiScale)?n.uiScale:`default`,displayFont:Lt(n.displayFont)?n.displayFont:`grotesk`,radius:Rt(n.radius)?n.radius:`default`,motion:i,reduceMotion:Nt(i)}}function Bt(){let e=jt(),t={};try{let e=localStorage.getItem(Et);if(e){let n=JSON.parse(e);n&&typeof n==`object`&&(t=n)}}catch{}let{reduceMotion:n,...r}=t;return zt(e,{...r,theme:G()})}function Vt(e){try{let{theme:t,reduceMotion:n,...r}=e;localStorage.setItem(Et,JSON.stringify(r))}catch{}}var Ht={grotesk:null,ui:`var(--font-ui)`,mono:`var(--font-mono)`};function Ut(e){if(typeof document>`u`)return;let t=document.documentElement;t.style.setProperty(`--accent-h`,String(e.accentH)),t.style.setProperty(`--accent-s`,`${e.accentS}%`),t.style.setProperty(`--accent-l`,`${e.accentL}%`);let n=Ht[e.displayFont];n===null?t.style.removeProperty(`--font-display`):t.style.setProperty(`--font-display`,n),t.setAttribute(`data-density`,e.density),t.setAttribute(`data-ui-scale`,e.uiScale),t.setAttribute(`data-radius`,e.radius),e.motion===`system`?t.removeAttribute(`data-motion`):t.setAttribute(`data-motion`,e.motion)}var J=Bt();Ut(J);var Y=new Set;function Wt(){for(let e of Y)e()}wt(()=>{J.theme!==G()&&(J={...J,theme:G()},Wt())});function Gt(e){return Y.add(e),()=>Y.delete(e)}function Kt(){return J}function qt(e){let t=zt(J,e),n=t.theme!==J.theme;J=t,Ut(J),Vt(J),n&&K(J.theme),Wt()}function Jt(){try{localStorage.removeItem(Et)}catch{}J=jt(),Ut(J),K(`system`),Wt()}function Yt(e=J){let t=jt();return e.theme===t.theme&&e.accentH===t.accentH&&e.accentS===t.accentS&&e.accentL===t.accentL&&e.density===t.density&&e.uiScale===t.uiScale&&e.displayFont===t.displayFont&&e.radius===t.radius&&e.motion===t.motion}function Xt(e=J){return kt.find(t=>t.h===e.accentH&&t.s===e.accentS&&t.l===e.accentL)??null}var Zt={min:Dt,max:Ot};function Qt(e,t){let n=t?.freshMs??1e4,r=(0,i.useSyncExternalStore)((0,i.useCallback)(t=>g(e.key,t),[e.key]),()=>_(e.key),()=>_(e.key));return(0,i.useEffect)(()=>{oe(e.key,()=>e.fetch(),n)},[e.key,n]),r}function $t(e){return(0,i.useCallback)(()=>{se(e.key,()=>e.fetch())},[e])}function en(e){return(0,i.useCallback)(()=>{se(e.key,()=>e.fetch(),!0)},[e])}function tn(){return(0,i.useSyncExternalStore)(F,()=>I().phase,()=>I().phase)}function nn(){let e=(0,i.useSyncExternalStore)(F,()=>I().mutationTokenHeldUntil,()=>I().mutationTokenHeldUntil);return{hasHold:ct(),token:st(),heldUntil:e,setToken:ut,touch:dt,clear:B}}function rn(){return{theme:(0,i.useSyncExternalStore)(wt,G,G),set:K,cycle:Tt}}function an(){return{appearance:(0,i.useSyncExternalStore)(Gt,Kt,Kt),set:qt,reset:Jt}}var X=class extends Error{status;path;detail;code;constructor(e,t,n,r=null,i=null){super(e),this.status=t,this.path=n,this.detail=r,this.code=i,this.name=`ApiError`}},on=class extends X{constructor(e){super(`This server was started without --allow-dispatch, so mutations are disabled.`,404,e),this.name=`DispatchDisabledError`}};async function sn(e,t){let n=await fetch(e,{method:`GET`,credentials:`same-origin`,headers:{"x-ashlr-read-client":tt()},signal:t});if(n.status===401)throw z(),new X(`Read session expired.`,401,e);if(!n.ok)throw new X(`GET ${e} failed (HTTP ${n.status}).`,n.status,e);return await n.json()}async function cn(e,t,n,r){let i=await fetch(e,{method:`POST`,credentials:`same-origin`,headers:{"Content-Type":`application/json`,"x-ashlr-token":n},body:JSON.stringify(t??{}),signal:r});if(i.status===401)throw new X(`Mutation token was rejected.`,401,e);if(!i.ok){let{detail:t,code:n}=await ln(i);throw i.status===404&&n===null?new on(e):new X(`POST ${e} failed (HTTP ${i.status})${t?`: ${t}`:``}.`,i.status,e,t||null,n)}if(i.status!==204)return await i.json()}async function ln(e){try{let t=await e.json();return{detail:typeof t.error==`string`&&t.error?t.error:typeof t.note==`string`?t.note:``,code:typeof t.code==`string`&&t.code?t.code:null}}catch{return{detail:``,code:null}}}var Z={grid:`_grid_x7p2y_1`,card:`_card_x7p2y_10`,title:`_title_x7p2y_19`,stat:`_stat_x7p2y_25`,caption:`_caption_x7p2y_31`,line:`_line_x7p2y_37`,row:`_row_x7p2y_42`,cell:`_cell_x7p2y_51`},Q=t();function un({width:e=`100%`}){return(0,Q.jsx)(`div`,{className:`skeleton ${Z.line}`,style:{width:e}})}function dn(){return(0,Q.jsxs)(`div`,{className:Z.card,children:[(0,Q.jsx)(`div`,{className:`skeleton ${Z.title}`}),(0,Q.jsx)(`div`,{className:`skeleton ${Z.stat}`}),(0,Q.jsx)(`div`,{className:`skeleton ${Z.caption}`})]})}function fn(){return(0,Q.jsxs)(`div`,{className:Z.row,children:[(0,Q.jsx)(`div`,{className:`skeleton ${Z.cell}`,style:{width:`18%`}}),(0,Q.jsx)(`div`,{className:`skeleton ${Z.cell}`,style:{width:`38%`}}),(0,Q.jsx)(`div`,{className:`skeleton ${Z.cell}`,style:{width:`14%`}}),(0,Q.jsx)(`div`,{className:`skeleton ${Z.cell}`,style:{width:`10%`}})]})}function pn({count:e=4}){return(0,Q.jsx)(`div`,{className:Z.grid,children:Array.from({length:e},(e,t)=>(0,Q.jsx)(dn,{},t))})}var mn={skipLink:`_skipLink_1uply_1`};function hn(){return(0,Q.jsx)(`button`,{type:`button`,className:mn.skipLink,onClick:()=>{let e=document.getElementById(`main-content`);e?.focus(),e?.scrollIntoView({block:`start`})},children:`Skip to content`})}var $={wrap:`_wrap_14qw9_1`,card:`_card_14qw9_10`,badge:`_badge_14qw9_19`,heading:`_heading_14qw9_24`,body:`_body_14qw9_31`,code:`_code_14qw9_38`,example:`_example_14qw9_49`,form:`_form_14qw9_56`,label:`_label_14qw9_63`,input:`_input_14qw9_68`,error:`_error_14qw9_82`,submit:`_submit_14qw9_87`,optional:`_optional_14qw9_107`},gn=/^[a-f0-9]{64}$/;function _n({onAuthenticated:e,heading:t=`Connect to ashlr serve`,command:n=`ashlr serve`,subject:r=`This dashboard`,mutationField:a=!1}){let[o,s]=(0,i.useState)(``),[c,l]=(0,i.useState)(``),[u,d]=(0,i.useState)(null),[ee,te]=(0,i.useState)(!1),f=(0,i.useRef)(null),p=(0,i.useId)(),m=(0,i.useId)(),h=(0,i.useId)(),g=(0,i.useRef)(e);g.current=e,(0,i.useEffect)(()=>{let e=!1;return ot().then(t=>{t&&!e&&g.current?.()}),()=>{e=!0}},[]);async function _(t){t.preventDefault(),d(null);let n=c.trim();if(a&&n&&!gn.test(n)){d(`That does not look like a mutation token — expected 64 hex characters (leave it empty to unlock later).`);return}te(!0);try{await L(o),s(``),a&&n&&ut(n),l(``),e?.()}catch(e){d(e instanceof Error?e.message:`Could not establish a session.`),f.current?.select()}finally{te(!1)}}return(0,Q.jsx)(`div`,{className:$.wrap,children:(0,Q.jsxs)(`div`,{className:$.card,children:[(0,Q.jsx)(`div`,{className:$.badge,"aria-hidden":`true`,children:`⛺`}),(0,Q.jsx)(`h1`,{className:$.heading,children:t}),(0,Q.jsxs)(`p`,{className:$.body,children:[r,` only ever talks to the ashlr server running on this machine (`,(0,Q.jsx)(`code`,{children:`127.0.0.1`}),`, never a remote host). To read data, paste the`,` `,(0,Q.jsx)(`strong`,{children:`read token`}),` printed in the terminal where you ran:`]}),(0,Q.jsx)(`pre`,{className:$.code,children:n}),(0,Q.jsxs)(`p`,{className:$.body,children:[`It looks like a 64-character hex string, printed once at startup — something like`,` `,(0,Q.jsx)(`code`,{className:$.example,children:`a1b2c3…`}),`. This dialog never stores it: it is exchanged once for a short-lived session cookie, then discarded.`]}),(0,Q.jsxs)(`form`,{onSubmit:_,className:$.form,noValidate:!0,children:[(0,Q.jsx)(`label`,{htmlFor:p,className:$.label,children:`Read token`}),(0,Q.jsx)(`input`,{ref:f,id:p,name:`read-token`,type:`password`,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,inputMode:`text`,className:$.input,value:o,onChange:e=>s(e.target.value),"aria-invalid":u?!0:void 0,"aria-describedby":u?h:void 0,placeholder:`64 hex characters`,autoFocus:!0}),a?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`label`,{htmlFor:m,className:$.label,children:[`Mutation token `,(0,Q.jsx)(`span`,{className:$.optional,children:`optional — paste it now to enable sending`})]}),(0,Q.jsx)(`input`,{id:m,name:`mutation-token`,type:`password`,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,inputMode:`text`,className:$.input,value:c,onChange:e=>l(e.target.value),placeholder:`64 hex characters, printed under “Dispatch enabled”`})]}):null,u?(0,Q.jsx)(`p`,{id:h,className:$.error,role:`alert`,children:u}):null,(0,Q.jsx)(`button`,{type:`submit`,className:$.submit,disabled:ee||o.trim().length===0,children:ee?`Connecting…`:`Connect`})]})]})})}export{x as $,it as A,ze as B,ot as C,st as D,I as E,Ge as F,Oe as G,Ee as H,He as I,Ne as J,Se as K,Re as L,z as M,F as N,tt as O,dt as P,je as Q,Be as R,Tt as S,nt as T,Te as U,he as V,b as W,Me as X,ke as Y,Ae as Z,rn as _,un as a,v as at,Yt as b,on as c,g as ct,an as d,De as et,tn as f,en as g,$t as h,pn as i,ce as it,rt as j,ct as k,sn as l,Qt as m,hn as n,ve as nt,fn as o,se as ot,nn as p,Ce as q,dn as r,_ as rt,X as s,ae as st,_n as t,we as tt,cn as u,Zt as v,B as w,Xt as x,kt as y,Ve as z};
|
|
2
|
-
//# sourceMappingURL=global-DVgMguLr.js.map
|