@ashlr/hub 3.11.3 → 3.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +44 -18
- package/dist/authority-surface.json +72 -57
- package/dist/build-identity.json +2 -2
- package/dist/cli/authority.d.ts +39 -0
- package/dist/cli/authority.js +222 -55
- package/dist/cli/authority.js.map +1 -1
- package/dist/cli/help.js +1 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/verse.d.ts +3 -0
- package/dist/cli/verse.js +10 -0
- package/dist/cli/verse.js.map +1 -1
- package/dist/core/authority/types.d.ts +2 -2
- package/dist/core/cloud/cloud-api.js +1 -1
- package/dist/core/cloud/cloud-api.js.map +1 -1
- package/dist/core/cloud/delivery-contract.d.ts +1 -1
- package/dist/core/cloud/delivery-contract.js +25 -15
- package/dist/core/cloud/delivery-contract.js.map +1 -1
- package/dist/core/cloud/store.js +6 -0
- package/dist/core/cloud/store.js.map +1 -1
- package/dist/core/cloud/tracker.d.ts +2 -0
- package/dist/core/cloud/tracker.js +140 -37
- package/dist/core/cloud/tracker.js.map +1 -1
- package/dist/core/cloud/types.d.ts +9 -1
- package/dist/core/cloud/types.js.map +1 -1
- package/dist/core/comms/dispatch.d.ts +5 -0
- package/dist/core/comms/dispatch.js +12 -1
- package/dist/core/comms/dispatch.js.map +1 -1
- package/dist/core/comms/requests.d.ts +27 -1
- package/dist/core/comms/requests.js +50 -1
- package/dist/core/comms/requests.js.map +1 -1
- package/dist/core/daemon/loop.d.ts +14 -0
- package/dist/core/daemon/loop.js +35 -0
- package/dist/core/daemon/loop.js.map +1 -1
- package/dist/core/fabric/claude-usage.js +23 -1
- package/dist/core/fabric/claude-usage.js.map +1 -1
- package/dist/core/fleet/dispatch-router.d.ts +7 -0
- package/dist/core/fleet/dispatch-router.js +4 -1
- package/dist/core/fleet/dispatch-router.js.map +1 -1
- package/dist/core/fleet/tick-hooks-live.d.ts +5 -1
- package/dist/core/fleet/tick-hooks-live.js +10 -1
- package/dist/core/fleet/tick-hooks-live.js.map +1 -1
- package/dist/core/local-runtime/llama/config.d.ts +14 -0
- package/dist/core/local-runtime/llama/config.js +20 -2
- package/dist/core/local-runtime/llama/config.js.map +1 -1
- package/dist/core/local-runtime/llama/process.d.ts +19 -0
- package/dist/core/local-runtime/llama/process.js +67 -17
- package/dist/core/local-runtime/llama/process.js.map +1 -1
- package/dist/core/local-runtime/llama/supervisor.js +14 -3
- package/dist/core/local-runtime/llama/supervisor.js.map +1 -1
- package/dist/core/observability/rollup.d.ts +12 -0
- package/dist/core/observability/rollup.js +37 -2
- package/dist/core/observability/rollup.js.map +1 -1
- package/dist/core/observability/usage-source.js +49 -1
- package/dist/core/observability/usage-source.js.map +1 -1
- package/dist/core/resources/connection-monitor.js +47 -4
- package/dist/core/resources/connection-monitor.js.map +1 -1
- package/dist/core/resources/engineering-mission-store.d.ts +1 -1
- package/dist/core/routing/headroom.js +7 -2
- package/dist/core/routing/headroom.js.map +1 -1
- package/dist/core/routing/router.d.ts +48 -0
- package/dist/core/routing/router.js +129 -13
- package/dist/core/routing/router.js.map +1 -1
- package/dist/core/run/best-of-n.d.ts +7 -0
- package/dist/core/run/best-of-n.js +3 -0
- package/dist/core/run/best-of-n.js.map +1 -1
- package/dist/core/run/harness-dispatch.d.ts +147 -0
- package/dist/core/run/harness-dispatch.js +234 -0
- package/dist/core/run/harness-dispatch.js.map +1 -0
- package/dist/core/run/orchestrator.js +3 -0
- package/dist/core/run/orchestrator.js.map +1 -1
- package/dist/core/run/provider-client.d.ts +8 -0
- package/dist/core/run/provider-client.js +4 -0
- package/dist/core/run/provider-client.js.map +1 -1
- package/dist/core/run/sandboxed-engine.d.ts +8 -0
- package/dist/core/run/sandboxed-engine.js +60 -7
- package/dist/core/run/sandboxed-engine.js.map +1 -1
- package/dist/core/types.d.ts +14 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/universe/builtins/preparation/manifest.json +1 -1
- package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +181 -5
- package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +180 -4
- package/dist/core/universe/firm-demo.d.ts +1 -1
- package/dist/core/universe/firm-graph.d.ts +2 -2
- package/dist/core/util/scrub.js +16 -3
- package/dist/core/util/scrub.js.map +1 -1
- package/dist/core/verse/accounts.d.ts +9 -1
- package/dist/core/verse/accounts.js +52 -11
- package/dist/core/verse/accounts.js.map +1 -1
- package/dist/core/verse/apps-api.d.ts +12 -0
- package/dist/core/verse/apps-api.js +22 -4
- package/dist/core/verse/apps-api.js.map +1 -1
- package/dist/core/verse/apps.js +22 -12
- package/dist/core/verse/apps.js.map +1 -1
- package/dist/core/verse/local-models.js +3 -7
- package/dist/core/verse/local-models.js.map +1 -1
- package/dist/core/verse/model-display-name.d.ts +34 -0
- package/dist/core/verse/model-display-name.js +125 -0
- package/dist/core/verse/model-display-name.js.map +1 -0
- package/dist/core/verse/seats.d.ts +11 -2
- package/dist/core/verse/seats.js +42 -18
- package/dist/core/verse/seats.js.map +1 -1
- package/dist/core/verse/transient-events.d.ts +21 -0
- package/dist/core/verse/transient-events.js +12 -0
- package/dist/core/verse/transient-events.js.map +1 -0
- package/dist/core/verse/types.d.ts +6 -13
- package/dist/core/verse/types.js +5 -36
- package/dist/core/verse/types.js.map +1 -1
- package/dist/core/web/public/next/assets/ActionMenu-DffwPklw.js +2 -0
- package/dist/core/web/public/next/assets/{ActionMenu-FxXeRbPx.js.map → ActionMenu-DffwPklw.js.map} +1 -1
- package/dist/core/web/public/next/assets/Advanced-CxzE6ekM.js +2 -0
- package/dist/core/web/public/next/assets/Advanced-CxzE6ekM.js.map +1 -0
- package/dist/core/web/public/next/assets/App-DFlp-vdv.js +7 -0
- package/dist/core/web/public/next/assets/App-DFlp-vdv.js.map +1 -0
- package/dist/core/web/public/next/assets/AppsSection-Ct2O605-.js +2 -0
- package/dist/core/web/public/next/assets/AppsSection-Ct2O605-.js.map +1 -0
- package/dist/core/web/public/next/assets/AreaTrend-o-GLl2Is.js +2 -0
- package/dist/core/web/public/next/assets/{AreaTrend-CDxrQU4b.js.map → AreaTrend-o-GLl2Is.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomyOffState-CXhXpsim.js +2 -0
- package/dist/core/web/public/next/assets/AutonomyOffState-CXhXpsim.js.map +1 -0
- package/dist/core/web/public/next/assets/AutonomyOffState-DmbE71vI.css +1 -0
- package/dist/core/web/public/next/assets/AutonomySection-BTg7yVa1.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-DuQkHGD_.js.map → AutonomySection-BTg7yVa1.js.map} +1 -1
- package/dist/core/web/public/next/assets/BranchBar-BjPFtGsj.js +2 -0
- package/dist/core/web/public/next/assets/{BranchBar-CLsvOcyK.js.map → BranchBar-BjPFtGsj.js.map} +1 -1
- package/dist/core/web/public/next/assets/BudgetControl-ng6swoZp.js +2 -0
- package/dist/core/web/public/next/assets/{BudgetControl-TL3FeZ1D.js.map → BudgetControl-ng6swoZp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Button-HHXgoXGr.js → Button-OjVJSrFq.js} +2 -2
- package/dist/core/web/public/next/assets/{Button-HHXgoXGr.js.map → Button-OjVJSrFq.js.map} +1 -1
- package/dist/core/web/public/next/assets/CapacityStrip-Bx4c3dxD.js +2 -0
- package/dist/core/web/public/next/assets/{CapacityStrip-qReu0RaF.js.map → CapacityStrip-Bx4c3dxD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{useChartWidth-BW6SwYIK.css → ChartFrame-BVuBe6RP.css} +1 -1
- package/dist/core/web/public/next/assets/ChartFrame-B_w2Siqg.js +2 -0
- package/dist/core/web/public/next/assets/ChartFrame-B_w2Siqg.js.map +1 -0
- package/dist/core/web/public/next/assets/ChatSection-Bisj9R6E.js +3 -0
- package/dist/core/web/public/next/assets/ChatSection-Bisj9R6E.js.map +1 -0
- package/dist/core/web/public/next/assets/CloudBudgetForm-5Gf4iAok.js +2 -0
- package/dist/core/web/public/next/assets/CloudBudgetForm-5Gf4iAok.js.map +1 -0
- package/dist/core/web/public/next/assets/CommandPalette-iTpk6I8r.js +2 -0
- package/dist/core/web/public/next/assets/CommandPalette-iTpk6I8r.js.map +1 -0
- package/dist/core/web/public/next/assets/CommandSection-CvAVd1w4.css +1 -0
- package/dist/core/web/public/next/assets/CommandSection-D8ZihzvE.js +2 -0
- package/dist/core/web/public/next/assets/CommandSection-D8ZihzvE.js.map +1 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-_S9iE-cJ.js +2 -0
- package/dist/core/web/public/next/assets/{ConfirmDialog-DLrLKM0c.js.map → ConfirmDialog-_S9iE-cJ.js.map} +1 -1
- package/dist/core/web/public/next/assets/DeleteChatDialog-KUYUygiA.js +2 -0
- package/dist/core/web/public/next/assets/DeleteChatDialog-KUYUygiA.js.map +1 -0
- package/dist/core/web/public/next/assets/Dialog-BuIaG6HF.js +2 -0
- package/dist/core/web/public/next/assets/{Dialog-BS9Dxi3G.js.map → Dialog-BuIaG6HF.js.map} +1 -1
- package/dist/core/web/public/next/assets/DiffPane-De4IQt82.js +2 -0
- package/dist/core/web/public/next/assets/{DiffPane-TOmUf3b2.js.map → DiffPane-De4IQt82.js.map} +1 -1
- package/dist/core/web/public/next/assets/DiffViewer-B75t6Ile.js +2 -0
- package/dist/core/web/public/next/assets/{DiffViewer-BpH56302.js.map → DiffViewer-B75t6Ile.js.map} +1 -1
- package/dist/core/web/public/next/assets/DockHost-BPqyhtCP.js +2 -0
- package/dist/core/web/public/next/assets/DockHost-BPqyhtCP.js.map +1 -0
- package/dist/core/web/public/next/assets/{EmptyState-BqR5YAmf.js → EmptyState-DXkT4vhW.js} +2 -2
- package/dist/core/web/public/next/assets/{EmptyState-BqR5YAmf.js.map → EmptyState-DXkT4vhW.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-dhvDTwAZ.js → Epistemic-CfqFe_aP.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-dhvDTwAZ.js.map → Epistemic-CfqFe_aP.js.map} +1 -1
- package/dist/core/web/public/next/assets/FleetSection-B7f-XBiX.js +3 -0
- package/dist/core/web/public/next/assets/FleetSection-B7f-XBiX.js.map +1 -0
- package/dist/core/web/public/next/assets/Funnel-pXYySwXQ.js +2 -0
- package/dist/core/web/public/next/assets/{Funnel-D2u7Wlzc.js.map → Funnel-pXYySwXQ.js.map} +1 -1
- package/dist/core/web/public/next/assets/GearTray-gj6lT7ba.js +2 -0
- package/dist/core/web/public/next/assets/{GearTray-Cb2npoxM.js.map → GearTray-gj6lT7ba.js.map} +1 -1
- package/dist/core/web/public/next/assets/GrowthSection-D8e9m9Wy.js +2 -0
- package/dist/core/web/public/next/assets/{GrowthSection-C5aDPs8B.js.map → GrowthSection-D8e9m9Wy.js.map} +1 -1
- package/dist/core/web/public/next/assets/HandoffDialog-VfKxKhxF.js +2 -0
- package/dist/core/web/public/next/assets/{HandoffDialog-BF3AYg-X.js.map → HandoffDialog-VfKxKhxF.js.map} +1 -1
- package/dist/core/web/public/next/assets/Input-u1YR_Ogl.js +2 -0
- package/dist/core/web/public/next/assets/{Input-B5dmGLZx.js.map → Input-u1YR_Ogl.js.map} +1 -1
- package/dist/core/web/public/next/assets/LeaderCard-BDELBrnx.js +2 -0
- package/dist/core/web/public/next/assets/LeaderCard-BDELBrnx.js.map +1 -0
- package/dist/core/web/public/next/assets/MatrixHeatmap-NKLF2srR.js +2 -0
- package/dist/core/web/public/next/assets/{MatrixHeatmap-BgEUs2Eu.js.map → MatrixHeatmap-NKLF2srR.js.map} +1 -1
- package/dist/core/web/public/next/assets/McpSection-D1tDvlRa.js +2 -0
- package/dist/core/web/public/next/assets/{McpSection-DZkiOuoq.js.map → McpSection-D1tDvlRa.js.map} +1 -1
- package/dist/core/web/public/next/assets/MessageMarkdown-DlfLEJxu.js +3 -0
- package/dist/core/web/public/next/assets/{MessageMarkdown-BauY8ZTY.js.map → MessageMarkdown-DlfLEJxu.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DLBNLfsn.js → MessageMarkdownRenderer-BYZzI115.js} +4 -4
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-DLBNLfsn.js.map → MessageMarkdownRenderer-BYZzI115.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-DNeocJnD.js → Meter-BSVhU43Q.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-DNeocJnD.js.map → Meter-BSVhU43Q.js.map} +1 -1
- package/dist/core/web/public/next/assets/MindSection-lqV2e_Sq.js +2 -0
- package/dist/core/web/public/next/assets/MindSection-lqV2e_Sq.js.map +1 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-CLGP72g6.js +2 -0
- package/dist/core/web/public/next/assets/{MutationTokenDialog-mxrw0Yvy.js.map → MutationTokenDialog-CLGP72g6.js.map} +1 -1
- package/dist/core/web/public/next/assets/NeedsYouDrawer-D992XTOn.js +2 -0
- package/dist/core/web/public/next/assets/{NeedsYouDrawer-CO-Balfp.js.map → NeedsYouDrawer-D992XTOn.js.map} +1 -1
- package/dist/core/web/public/next/assets/NeedsYouList-eBOlJr0b.js +2 -0
- package/dist/core/web/public/next/assets/{NeedsYouList-CzDZbzRD.js.map → NeedsYouList-eBOlJr0b.js.map} +1 -1
- package/dist/core/web/public/next/assets/NewChatDialog-DoYTH26M.js +3 -0
- package/dist/core/web/public/next/assets/NewChatDialog-DoYTH26M.js.map +1 -0
- package/dist/core/web/public/next/assets/OnboardingFlow-D3yfITK1.js +2 -0
- package/dist/core/web/public/next/assets/OnboardingFlow-D3yfITK1.js.map +1 -0
- package/dist/core/web/public/next/assets/OnboardingFlow-HUNQGQuU.css +1 -0
- package/dist/core/web/public/next/assets/OnboardingGate-DrDEVBqw.js +3 -0
- package/dist/core/web/public/next/assets/OnboardingGate-DrDEVBqw.js.map +1 -0
- package/dist/core/web/public/next/assets/PreviewPane-ULN673zm.js +2 -0
- package/dist/core/web/public/next/assets/{PreviewPane-BS-CleCd.js.map → PreviewPane-ULN673zm.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ProviderLogo-C5mBg8i0.js → ProviderLogo-8WaUjTlN.js} +2 -2
- package/dist/core/web/public/next/assets/{ProviderLogo-C5mBg8i0.js.map → ProviderLogo-8WaUjTlN.js.map} +1 -1
- package/dist/core/web/public/next/assets/RailStatus-Ckw9nvYU.js +2 -0
- package/dist/core/web/public/next/assets/{RailStatus-cPRyOmrk.js.map → RailStatus-Ckw9nvYU.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RefreshIndicator-CMwSCiz5.js → RefreshIndicator-DGubF4vR.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-CMwSCiz5.js.map → RefreshIndicator-DGubF4vR.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BFi1opsC.js → ResourcePoolConsoleApp-BahEaY4d.js} +4 -4
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BFi1opsC.js.map → ResourcePoolConsoleApp-BahEaY4d.js.map} +1 -1
- package/dist/core/web/public/next/assets/ResourcesChrome-C2ZrA7la.js +3 -0
- package/dist/core/web/public/next/assets/ResourcesChrome-C2ZrA7la.js.map +1 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-BC93fzwQ.js +2 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-BC93fzwQ.js.map +1 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-BCFJ2Uq8.css +1 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-BvhHNpE_.js +2 -0
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-DYtwc2ja.js.map → RouteErrorBoundary-BvhHNpE_.js.map} +1 -1
- package/dist/core/web/public/next/assets/RunInCloudAction-Dywfnw0C.js +2 -0
- package/dist/core/web/public/next/assets/{RunInCloudAction-BEGEEM8O.js.map → RunInCloudAction-Dywfnw0C.js.map} +1 -1
- package/dist/core/web/public/next/assets/SeatSelector-CH7hTgwy.js +3 -0
- package/dist/core/web/public/next/assets/{SeatSelector-CYHRR7Wp.js.map → SeatSelector-CH7hTgwy.js.map} +1 -1
- package/dist/core/web/public/next/assets/Segmented-Wynbg0F5.js +2 -0
- package/dist/core/web/public/next/assets/{Segmented-BE53BenI.js.map → Segmented-Wynbg0F5.js.map} +1 -1
- package/dist/core/web/public/next/assets/Select-DMyLCkId.js +2 -0
- package/dist/core/web/public/next/assets/{Select-BwSGkHCS.js.map → Select-DMyLCkId.js.map} +1 -1
- package/dist/core/web/public/next/assets/SessionGate-8RtQFWeZ.js +2 -0
- package/dist/core/web/public/next/assets/{SessionGate-e0dFJm9d.js.map → SessionGate-8RtQFWeZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/SessionInsightChip-zBJbKv2L.js +2 -0
- package/dist/core/web/public/next/assets/{SessionInsightChip-CxlSyueJ.js.map → SessionInsightChip-zBJbKv2L.js.map} +1 -1
- package/dist/core/web/public/next/assets/SessionSearch-DfJIcxxM.js +2 -0
- package/dist/core/web/public/next/assets/{SessionSearch-_yp7S2TP.js.map → SessionSearch-DfJIcxxM.js.map} +1 -1
- package/dist/core/web/public/next/assets/SettingsSection-Da9RYyH5.js +2 -0
- package/dist/core/web/public/next/assets/SettingsSection-Da9RYyH5.js.map +1 -0
- package/dist/core/web/public/next/assets/Sheet-DhXyIDmq.js +2 -0
- package/dist/core/web/public/next/assets/{Sheet-DVP2Mgy6.js.map → Sheet-DhXyIDmq.js.map} +1 -1
- package/dist/core/web/public/next/assets/ShortcutsOverlay-Bi5f-wUw.js +2 -0
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-BcIcoClX.js.map → ShortcutsOverlay-Bi5f-wUw.js.map} +1 -1
- package/dist/core/web/public/next/assets/Sidebar-BuO3sViG.js +3 -0
- package/dist/core/web/public/next/assets/Sidebar-BuO3sViG.js.map +1 -0
- package/dist/core/web/public/next/assets/Slider-Bd9YFWEV.js +2 -0
- package/dist/core/web/public/next/assets/{Slider-BypUWjpH.js.map → Slider-Bd9YFWEV.js.map} +1 -1
- package/dist/core/web/public/next/assets/StatTile-DScTd3c-.js +2 -0
- package/dist/core/web/public/next/assets/StatTile-DScTd3c-.js.map +1 -0
- package/dist/core/web/public/next/assets/{StatusBadge-HCZp416_.js → StatusBadge-DQ1UtQAJ.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-HCZp416_.js.map → StatusBadge-DQ1UtQAJ.js.map} +1 -1
- package/dist/core/web/public/next/assets/StepBand-BB3IJxhr.js +2 -0
- package/dist/core/web/public/next/assets/{StepBand-Duz1PUes.js.map → StepBand-BB3IJxhr.js.map} +1 -1
- package/dist/core/web/public/next/assets/Surface-DWMtQe3Q.js +2 -0
- package/dist/core/web/public/next/assets/Surface-DWMtQe3Q.js.map +1 -0
- package/dist/core/web/public/next/assets/Surface-e1z6GdZt.css +1 -0
- package/dist/core/web/public/next/assets/Swimlane-DcENqtdp.js +2 -0
- package/dist/core/web/public/next/assets/{Swimlane-DGcIcnNA.js.map → Swimlane-DcENqtdp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Switch-iveI-8NY.js → Switch-CenChEYf.js} +2 -2
- package/dist/core/web/public/next/assets/{Switch-iveI-8NY.js.map → Switch-CenChEYf.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Tag-DH15CUJp.js → Tag-D4k5E3u0.js} +2 -2
- package/dist/core/web/public/next/assets/{Tag-DH15CUJp.js.map → Tag-D4k5E3u0.js.map} +1 -1
- package/dist/core/web/public/next/assets/TerminalPane-DCQTukcE.js +7 -0
- package/dist/core/web/public/next/assets/TerminalPane-DCQTukcE.js.map +1 -0
- package/dist/core/web/public/next/assets/UniverseConsoleApp-DsuWT0pi.js +2 -0
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-C8yQ0AaR.js.map → UniverseConsoleApp-DsuWT0pi.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-_nPURPqe.js → UniverseView-DyI5ApsA.js} +3 -3
- package/dist/core/web/public/next/assets/{UniverseView-_nPURPqe.js.map → UniverseView-DyI5ApsA.js.map} +1 -1
- package/dist/core/web/public/next/assets/UsageSection-Imr28nwK.js +3 -0
- package/dist/core/web/public/next/assets/UsageSection-Imr28nwK.js.map +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-DeuBEvAO.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-DeuBEvAO.js.map +1 -0
- package/dist/core/web/public/next/assets/Workspace-CpxBO98E.js +10 -0
- package/dist/core/web/public/next/assets/Workspace-CpxBO98E.js.map +1 -0
- package/dist/core/web/public/next/assets/actions-BmIf9avl.css +1 -0
- package/dist/core/web/public/next/assets/actions-DE8NoWV_.js +2 -0
- package/dist/core/web/public/next/assets/actions-DE8NoWV_.js.map +1 -0
- package/dist/core/web/public/next/assets/apps-model-CFGCUu9p.js +2 -0
- package/dist/core/web/public/next/assets/apps-model-CFGCUu9p.js.map +1 -0
- package/dist/core/web/public/next/assets/authority-model-CmnfJzmM.js +2 -0
- package/dist/core/web/public/next/assets/authority-model-CmnfJzmM.js.map +1 -0
- package/dist/core/web/public/next/assets/binding-left-DZpq5sLf.js +2 -0
- package/dist/core/web/public/next/assets/binding-left-DZpq5sLf.js.map +1 -0
- package/dist/core/web/public/next/assets/{budget-queries-V3wcJVK_.js → budget-queries-DjrPRFwl.js} +2 -2
- package/dist/core/web/public/next/assets/{budget-queries-V3wcJVK_.js.map → budget-queries-DjrPRFwl.js.map} +1 -1
- package/dist/core/web/public/next/assets/capacity-strip-model-C7cdQnuK.js +2 -0
- package/dist/core/web/public/next/assets/capacity-strip-model-C7cdQnuK.js.map +1 -0
- package/dist/core/web/public/next/assets/charts-DUD_jprE.js +2 -0
- package/dist/core/web/public/next/assets/charts-DUD_jprE.js.map +1 -0
- package/dist/core/web/public/next/assets/charts-DhKTrQLw.css +1 -0
- package/dist/core/web/public/next/assets/clipboard-DkZdqlxf.js +2 -0
- package/dist/core/web/public/next/assets/clipboard-DkZdqlxf.js.map +1 -0
- package/dist/core/web/public/next/assets/cloud-D-HXms6q.css +1 -0
- package/dist/core/web/public/next/assets/cloud-model-DoxBMDvx.js +2 -0
- package/dist/core/web/public/next/assets/cloud-model-DoxBMDvx.js.map +1 -0
- package/dist/core/web/public/next/assets/cloud-queries-CTGvO-k-.js +2 -0
- package/dist/core/web/public/next/assets/cloud-queries-CTGvO-k-.js.map +1 -0
- package/dist/core/web/public/next/assets/cloud.module-DOP-N4Ch.js +2 -0
- package/dist/core/web/public/next/assets/cloud.module-DOP-N4Ch.js.map +1 -0
- package/dist/core/web/public/next/assets/command-catalog-CmrHNFzh.js +2 -0
- package/dist/core/web/public/next/assets/command-catalog-CmrHNFzh.js.map +1 -0
- package/dist/core/web/public/next/assets/context-model-2Owp2jYF.js +3 -0
- package/dist/core/web/public/next/assets/context-model-2Owp2jYF.js.map +1 -0
- package/dist/core/web/public/next/assets/context-model-9xywbXxX.js +2 -0
- package/dist/core/web/public/next/assets/context-model-9xywbXxX.js.map +1 -0
- package/dist/core/web/public/next/assets/{context-queries-CrPcHWTz.js → context-queries-wvcRnV-I.js} +2 -2
- package/dist/core/web/public/next/assets/{context-queries-CrPcHWTz.js.map → context-queries-wvcRnV-I.js.map} +1 -1
- package/dist/core/web/public/next/assets/{control-queries-CmgBgKV7.js → control-queries-DB6Kh7UN.js} +2 -2
- package/dist/core/web/public/next/assets/{control-queries-CmgBgKV7.js.map → control-queries-DB6Kh7UN.js.map} +1 -1
- package/dist/core/web/public/next/assets/copy-setup-CJQhFl-4.js +2 -0
- package/dist/core/web/public/next/assets/copy-setup-CJQhFl-4.js.map +1 -0
- package/dist/core/web/public/next/assets/dock-icons-BBUGl4tE.js +2 -0
- package/dist/core/web/public/next/assets/{dock-icons-CUh8dOxJ.js.map → dock-icons-BBUGl4tE.js.map} +1 -1
- package/dist/core/web/public/next/assets/fleet-model-BRfRWkZl.js +2 -0
- package/dist/core/web/public/next/assets/fleet-model-BRfRWkZl.js.map +1 -0
- package/dist/core/web/public/next/assets/fleet-queries-tU6UEXN2.js +2 -0
- package/dist/core/web/public/next/assets/fleet-queries-tU6UEXN2.js.map +1 -0
- package/dist/core/web/public/next/assets/{focus-trap-HtcoXeEq.js → focus-trap-BGOceFvj.js} +2 -2
- package/dist/core/web/public/next/assets/{focus-trap-HtcoXeEq.js.map → focus-trap-BGOceFvj.js.map} +1 -1
- package/dist/core/web/public/next/assets/format-DEAaVF6X.js +2 -0
- package/dist/core/web/public/next/assets/format-DEAaVF6X.js.map +1 -0
- package/dist/core/web/public/next/assets/{git-model-bk_RXh3y.js → git-model-53iRLInY.js} +2 -2
- package/dist/core/web/public/next/assets/{git-model-bk_RXh3y.js.map → git-model-53iRLInY.js.map} +1 -1
- package/dist/core/web/public/next/assets/global-CXHHMz9o.js +2 -0
- package/dist/core/web/public/next/assets/{global-oEl9Qj8u.js.map → global-CXHHMz9o.js.map} +1 -1
- package/dist/core/web/public/next/assets/{guarded-runners-LHLXmXIk.js → guarded-runners-B8zFVBVZ.js} +2 -2
- package/dist/core/web/public/next/assets/{guarded-runners-LHLXmXIk.js.map → guarded-runners-B8zFVBVZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/icons-D4s-jlxX.js +2 -0
- package/dist/core/web/public/next/assets/icons-D4s-jlxX.js.map +1 -0
- package/dist/core/web/public/next/assets/{index-BVxOlseC.js → index-BG177cPN.js} +3 -3
- package/dist/core/web/public/next/assets/{index-BVxOlseC.js.map → index-BG177cPN.js.map} +1 -1
- package/dist/core/web/public/next/assets/live-model-CEf01wrW.js +2 -0
- package/dist/core/web/public/next/assets/live-model-CEf01wrW.js.map +1 -0
- package/dist/core/web/public/next/assets/local-model-C9xNi9fN.js +2 -0
- package/dist/core/web/public/next/assets/local-model-C9xNi9fN.js.map +1 -0
- package/dist/core/web/public/next/assets/mind.module-D_xk6HIQ.js +2 -0
- package/dist/core/web/public/next/assets/{mind.module-gTGAuJt1.js.map → mind.module-D_xk6HIQ.js.map} +1 -1
- package/dist/core/web/public/next/assets/mutations-CbTSofVH.js +2 -0
- package/dist/core/web/public/next/assets/{mutations-C5S7okTW.js.map → mutations-CbTSofVH.js.map} +1 -1
- package/dist/core/web/public/next/assets/nav-CmZcbPFJ.js +2 -0
- package/dist/core/web/public/next/assets/{nav-NVtnuG7A.js.map → nav-CmZcbPFJ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{overnight-queries-DGm3qL1b.js → overnight-queries-CR0b_j-V.js} +2 -2
- package/dist/core/web/public/next/assets/{overnight-queries-DGm3qL1b.js.map → overnight-queries-CR0b_j-V.js.map} +1 -1
- package/dist/core/web/public/next/assets/primitives-DNI-uJBd.js +1 -0
- package/dist/core/web/public/next/assets/{queries-CYisSAAB.js → queries-rvRsAGW2.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-CYisSAAB.js.map → queries-rvRsAGW2.js.map} +1 -1
- package/dist/core/web/public/next/assets/reasoning-pref-DMPQkujg.js +2 -0
- package/dist/core/web/public/next/assets/{reasoning-pref-BUcFF_5G.js.map → reasoning-pref-DMPQkujg.js.map} +1 -1
- package/dist/core/web/public/next/assets/{reveal-anchor-D-tV8xau.js → reveal-anchor-BKk2wZZZ.js} +2 -2
- package/dist/core/web/public/next/assets/{reveal-anchor-D-tV8xau.js.map → reveal-anchor-BKk2wZZZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/seat-subscription-CLn2ReSZ.js +2 -0
- package/dist/core/web/public/next/assets/{seat-subscription-B-f9ViU2.js.map → seat-subscription-CLn2ReSZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{sidebar-model-DQ-FyBHW.js → sidebar-model-CjQLvdag.js} +2 -2
- package/dist/core/web/public/next/assets/{sidebar-model-DQ-FyBHW.js.map → sidebar-model-CjQLvdag.js.map} +1 -1
- package/dist/core/web/public/next/assets/slots-DRxNosZg.js +3 -0
- package/dist/core/web/public/next/assets/{slots-BhP8b8tf.js.map → slots-DRxNosZg.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-data-DshUC_TU.js +2 -0
- package/dist/core/web/public/next/assets/surface-data-DshUC_TU.js.map +1 -0
- package/dist/core/web/public/next/assets/surface-prefetch-DO8r4ttQ.js +2 -0
- package/dist/core/web/public/next/assets/surface-prefetch-DO8r4ttQ.js.map +1 -0
- package/dist/core/web/public/next/assets/{tasks-model-BKDNqmmt.js → tasks-model-BKPFZAmZ.js} +2 -2
- package/dist/core/web/public/next/assets/{tasks-model-BKDNqmmt.js.map → tasks-model-BKPFZAmZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{terminal-icons-D2xe-aM1.js → terminal-icons-DoeQkP3y.js} +2 -2
- package/dist/core/web/public/next/assets/{terminal-icons-D2xe-aM1.js.map → terminal-icons-DoeQkP3y.js.map} +1 -1
- package/dist/core/web/public/next/assets/transient-events-nTLhMBND.js +2 -0
- package/dist/core/web/public/next/assets/transient-events-nTLhMBND.js.map +1 -0
- package/dist/core/web/public/next/assets/types-D5XOdyw8.js.map +1 -1
- package/dist/core/web/public/next/assets/{usage-queries-BZuAANlI.js → usage-queries-MnUeYzwp.js} +2 -2
- package/dist/core/web/public/next/assets/{usage-queries-BZuAANlI.js.map → usage-queries-MnUeYzwp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-guarded-action-Cf4MeNBa.js → use-guarded-action-P12MfNwU.js} +2 -2
- package/dist/core/web/public/next/assets/{use-guarded-action-Cf4MeNBa.js.map → use-guarded-action-P12MfNwU.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-ticker-D9yi49my.js → use-ticker-DIvAZ4Ey.js} +2 -2
- package/dist/core/web/public/next/assets/{use-ticker-D9yi49my.js.map → use-ticker-DIvAZ4Ey.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-token-gate-DqrSXdX6.js → use-token-gate-DfEja7Wr.js} +2 -2
- package/dist/core/web/public/next/assets/{use-token-gate-DqrSXdX6.js.map → use-token-gate-DfEja7Wr.js.map} +1 -1
- package/dist/core/web/public/next/assets/useOnboarding-CkOHadqC.js +2 -0
- package/dist/core/web/public/next/assets/useOnboarding-CkOHadqC.js.map +1 -0
- package/dist/core/web/public/next/assets/useSeatsRefresh-BDlbDlIU.js +2 -0
- package/dist/core/web/public/next/assets/useSeatsRefresh-BDlbDlIU.js.map +1 -0
- package/dist/core/web/public/next/assets/useVerseTranscript-BGtA55k2.js +2 -0
- package/dist/core/web/public/next/assets/{useVerseTranscript-CDYrrWOO.js.map → useVerseTranscript-BGtA55k2.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-icons-wOet9JZy.js +2 -0
- package/dist/core/web/public/next/assets/verse-icons-wOet9JZy.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-model-CSzzdyu2.js +2 -0
- package/dist/core/web/public/next/assets/verse-model-CSzzdyu2.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-store-DNVRuiJL.js +2 -0
- package/dist/core/web/public/next/assets/verse-store-DNVRuiJL.js.map +1 -0
- package/dist/core/web/public/next/assets/warmup-CecO_Nk0.js +3 -0
- package/dist/core/web/public/next/assets/{warmup-HG5vYbsN.js.map → warmup-CecO_Nk0.js.map} +1 -1
- package/dist/core/web/public/next/assets/why-seat-model-D68ZA7G8.js +2 -0
- package/dist/core/web/public/next/assets/why-seat-model-D68ZA7G8.js.map +1 -0
- package/dist/core/web/public/next/assets/{workspace-model-CbhdPhei.js → workspace-model-BdIVybKr.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-CbhdPhei.js.map → workspace-model-BdIVybKr.js.map} +1 -1
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/docs/README.md +2 -1
- package/package.json +1 -1
- package/dist/core/web/public/next/assets/ActionMenu-FxXeRbPx.js +0 -2
- package/dist/core/web/public/next/assets/Advanced-D_eS4472.js +0 -2
- package/dist/core/web/public/next/assets/Advanced-D_eS4472.js.map +0 -1
- package/dist/core/web/public/next/assets/App-Cj4zKbG3.js +0 -7
- package/dist/core/web/public/next/assets/App-Cj4zKbG3.js.map +0 -1
- package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js +0 -2
- package/dist/core/web/public/next/assets/AppsSection-Ddu_RCQE.js.map +0 -1
- package/dist/core/web/public/next/assets/AreaTrend-CDxrQU4b.js +0 -2
- package/dist/core/web/public/next/assets/AutonomySection-DuQkHGD_.js +0 -2
- package/dist/core/web/public/next/assets/BranchBar-CLsvOcyK.js +0 -2
- package/dist/core/web/public/next/assets/BudgetControl-TL3FeZ1D.js +0 -2
- package/dist/core/web/public/next/assets/BurnDown-sFXkUGdB.js +0 -2
- package/dist/core/web/public/next/assets/BurnDown-sFXkUGdB.js.map +0 -1
- package/dist/core/web/public/next/assets/CapacityStrip-qReu0RaF.js +0 -2
- package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js +0 -3
- package/dist/core/web/public/next/assets/ChatSection-DZuZfTaC.js.map +0 -1
- package/dist/core/web/public/next/assets/CloudBudgetForm-DJbiT1wi.js +0 -2
- package/dist/core/web/public/next/assets/CloudBudgetForm-DJbiT1wi.js.map +0 -1
- package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js +0 -2
- package/dist/core/web/public/next/assets/CommandPalette-zYLFZRnj.js.map +0 -1
- package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js +0 -2
- package/dist/core/web/public/next/assets/CommandSection-rIeMK5Ch.js.map +0 -1
- package/dist/core/web/public/next/assets/ConfirmDialog-DLrLKM0c.js +0 -2
- package/dist/core/web/public/next/assets/Dialog-BS9Dxi3G.js +0 -2
- package/dist/core/web/public/next/assets/DiffPane-TOmUf3b2.js +0 -2
- package/dist/core/web/public/next/assets/DiffViewer-BpH56302.js +0 -2
- package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js +0 -2
- package/dist/core/web/public/next/assets/DockHost-DzkzR-HD.js.map +0 -1
- package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js +0 -3
- package/dist/core/web/public/next/assets/FleetSection-ViAb549H.js.map +0 -1
- package/dist/core/web/public/next/assets/Funnel-D2u7Wlzc.js +0 -2
- package/dist/core/web/public/next/assets/GearTray-Cb2npoxM.js +0 -2
- package/dist/core/web/public/next/assets/GrowthSection-C5aDPs8B.js +0 -2
- package/dist/core/web/public/next/assets/HandoffDialog-BF3AYg-X.js +0 -2
- package/dist/core/web/public/next/assets/Input-B5dmGLZx.js +0 -2
- package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js +0 -2
- package/dist/core/web/public/next/assets/LeaderCard-DZrmjDbI.js.map +0 -1
- package/dist/core/web/public/next/assets/MatrixHeatmap-BgEUs2Eu.js +0 -2
- package/dist/core/web/public/next/assets/McpSection-DZkiOuoq.js +0 -2
- package/dist/core/web/public/next/assets/MessageMarkdown-BauY8ZTY.js +0 -3
- package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js +0 -2
- package/dist/core/web/public/next/assets/MindSection-BvJXFjyX.js.map +0 -1
- package/dist/core/web/public/next/assets/MutationTokenDialog-mxrw0Yvy.js +0 -2
- package/dist/core/web/public/next/assets/NeedsYouDrawer-CO-Balfp.js +0 -2
- package/dist/core/web/public/next/assets/NeedsYouList-CzDZbzRD.js +0 -2
- package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js +0 -3
- package/dist/core/web/public/next/assets/NewChatDialog-B8eJjlFf.js.map +0 -1
- package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js +0 -2
- package/dist/core/web/public/next/assets/OnboardingFlow-Cij6KWat.js.map +0 -1
- package/dist/core/web/public/next/assets/OnboardingFlow-DSpPj8UE.css +0 -1
- package/dist/core/web/public/next/assets/PreviewPane-BS-CleCd.js +0 -2
- package/dist/core/web/public/next/assets/RailStatus-cPRyOmrk.js +0 -2
- package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js +0 -3
- package/dist/core/web/public/next/assets/ResourcesChrome-BJCauW_t.js.map +0 -1
- package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js +0 -2
- package/dist/core/web/public/next/assets/ResourcesDrawer-BDecg6nO.js.map +0 -1
- package/dist/core/web/public/next/assets/ResourcesDrawer-CSKIO_wZ.css +0 -1
- package/dist/core/web/public/next/assets/RouteErrorBoundary-DYtwc2ja.js +0 -2
- package/dist/core/web/public/next/assets/RunInCloudAction-BEGEEM8O.js +0 -2
- package/dist/core/web/public/next/assets/SeatSelector-CYHRR7Wp.js +0 -3
- package/dist/core/web/public/next/assets/Segmented-BE53BenI.js +0 -2
- package/dist/core/web/public/next/assets/Select-BwSGkHCS.js +0 -2
- package/dist/core/web/public/next/assets/SessionGate-e0dFJm9d.js +0 -2
- package/dist/core/web/public/next/assets/SessionInsightChip-CxlSyueJ.js +0 -2
- package/dist/core/web/public/next/assets/SessionSearch-_yp7S2TP.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-wGnpWeGW.js.map +0 -1
- package/dist/core/web/public/next/assets/Sheet-DVP2Mgy6.js +0 -2
- package/dist/core/web/public/next/assets/ShortcutsOverlay-BcIcoClX.js +0 -2
- package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js +0 -3
- package/dist/core/web/public/next/assets/Sidebar-DjQOqPbm.js.map +0 -1
- package/dist/core/web/public/next/assets/Slider-BypUWjpH.js +0 -2
- package/dist/core/web/public/next/assets/StepBand-Duz1PUes.js +0 -2
- package/dist/core/web/public/next/assets/Surface-B75lVfzm.js +0 -2
- package/dist/core/web/public/next/assets/Surface-B75lVfzm.js.map +0 -1
- package/dist/core/web/public/next/assets/Surface-DMca0gAe.css +0 -1
- package/dist/core/web/public/next/assets/Swimlane-DGcIcnNA.js +0 -2
- package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js +0 -7
- package/dist/core/web/public/next/assets/TerminalPane-MQT-veKZ.js.map +0 -1
- package/dist/core/web/public/next/assets/UniverseConsoleApp-C8yQ0AaR.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-CqxRGJ40.js.map +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-DTjDpGCd.js.map +0 -1
- package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js +0 -10
- package/dist/core/web/public/next/assets/Workspace-DfcFGzME.js.map +0 -1
- package/dist/core/web/public/next/assets/actions-Ddfg2iLl.css +0 -1
- package/dist/core/web/public/next/assets/actions-tWevL9HV.js +0 -2
- package/dist/core/web/public/next/assets/actions-tWevL9HV.js.map +0 -1
- package/dist/core/web/public/next/assets/apps-model-CDFvmsQV.js +0 -2
- package/dist/core/web/public/next/assets/apps-model-CDFvmsQV.js.map +0 -1
- package/dist/core/web/public/next/assets/capacity-strip-model-BDbmQ3e3.js +0 -2
- package/dist/core/web/public/next/assets/capacity-strip-model-BDbmQ3e3.js.map +0 -1
- package/dist/core/web/public/next/assets/charts-CfAjuKMv.js +0 -2
- package/dist/core/web/public/next/assets/charts-CfAjuKMv.js.map +0 -1
- package/dist/core/web/public/next/assets/charts-uSX6xVYN.css +0 -1
- package/dist/core/web/public/next/assets/cloud-C1KPNRHC.css +0 -1
- package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js +0 -2
- package/dist/core/web/public/next/assets/cloud-queries-Br_r0UAz.js.map +0 -1
- package/dist/core/web/public/next/assets/cloud.module-ascz58c3.js +0 -2
- package/dist/core/web/public/next/assets/cloud.module-ascz58c3.js.map +0 -1
- package/dist/core/web/public/next/assets/command-bus-xYZoK9nQ.js +0 -2
- package/dist/core/web/public/next/assets/command-bus-xYZoK9nQ.js.map +0 -1
- package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js +0 -2
- package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js.map +0 -1
- package/dist/core/web/public/next/assets/composer-keys-su0g1h9o.js +0 -2
- package/dist/core/web/public/next/assets/composer-keys-su0g1h9o.js.map +0 -1
- package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js +0 -2
- package/dist/core/web/public/next/assets/context-model-BC-HOmPo.js.map +0 -1
- package/dist/core/web/public/next/assets/context-model-CIBmSugV.js +0 -2
- package/dist/core/web/public/next/assets/context-model-CIBmSugV.js.map +0 -1
- package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js +0 -2
- package/dist/core/web/public/next/assets/dark-since-DOMvtB_G.js.map +0 -1
- package/dist/core/web/public/next/assets/dock-icons-CUh8dOxJ.js +0 -2
- package/dist/core/web/public/next/assets/dock-store-B2vlxqPu.js +0 -2
- package/dist/core/web/public/next/assets/dock-store-B2vlxqPu.js.map +0 -1
- package/dist/core/web/public/next/assets/fleet-model-7i-FbzKI.js +0 -2
- package/dist/core/web/public/next/assets/fleet-model-7i-FbzKI.js.map +0 -1
- package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js +0 -2
- package/dist/core/web/public/next/assets/fleet-queries-DzGoqq8X.js.map +0 -1
- package/dist/core/web/public/next/assets/format-BkWdOQDL.js +0 -2
- package/dist/core/web/public/next/assets/format-BkWdOQDL.js.map +0 -1
- package/dist/core/web/public/next/assets/global-oEl9Qj8u.js +0 -2
- package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js +0 -3
- package/dist/core/web/public/next/assets/guarded-action-OzqcPjmh.js.map +0 -1
- package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js +0 -2
- package/dist/core/web/public/next/assets/icon-base-DvM1eI5V.js.map +0 -1
- package/dist/core/web/public/next/assets/icons-DEQa2rTx.js +0 -2
- package/dist/core/web/public/next/assets/icons-DEQa2rTx.js.map +0 -1
- package/dist/core/web/public/next/assets/live-model-bmsJgnYD.js +0 -2
- package/dist/core/web/public/next/assets/live-model-bmsJgnYD.js.map +0 -1
- package/dist/core/web/public/next/assets/local-model-DFyjKX0b.js +0 -2
- package/dist/core/web/public/next/assets/local-model-DFyjKX0b.js.map +0 -1
- package/dist/core/web/public/next/assets/mind.module-gTGAuJt1.js +0 -2
- package/dist/core/web/public/next/assets/mutations-C5S7okTW.js +0 -2
- package/dist/core/web/public/next/assets/nav-NVtnuG7A.js +0 -2
- package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js +0 -2
- package/dist/core/web/public/next/assets/preloaded-Cwf9rsnZ.js.map +0 -1
- package/dist/core/web/public/next/assets/primitives-DyCKYSBG.js +0 -1
- package/dist/core/web/public/next/assets/reasoning-pref-BUcFF_5G.js +0 -2
- package/dist/core/web/public/next/assets/seat-subscription-B-f9ViU2.js +0 -2
- package/dist/core/web/public/next/assets/section-visibility-Yctxu-if.js +0 -2
- package/dist/core/web/public/next/assets/section-visibility-Yctxu-if.js.map +0 -1
- package/dist/core/web/public/next/assets/slots-BhP8b8tf.js +0 -3
- package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js +0 -2
- package/dist/core/web/public/next/assets/surface-data-DTI7LjvH.js.map +0 -1
- package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js +0 -2
- package/dist/core/web/public/next/assets/surface-prefetch-CdUEBFrl.js.map +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js +0 -2
- package/dist/core/web/public/next/assets/useChartWidth-owwodNVz.js.map +0 -1
- package/dist/core/web/public/next/assets/useSeatsRefresh-DgFy-Ph4.js +0 -2
- package/dist/core/web/public/next/assets/useSeatsRefresh-DgFy-Ph4.js.map +0 -1
- package/dist/core/web/public/next/assets/useVerseTranscript-CDYrrWOO.js +0 -2
- package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js +0 -2
- package/dist/core/web/public/next/assets/verse-icons-F9VwX07_.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-DyCwZF-M.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js +0 -3
- package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js +0 -3
- package/dist/core/web/public/next/assets/verse-ui-store-62DxpVHS.js.map +0 -1
- package/dist/core/web/public/next/assets/viewport-nTubFCwf.js +0 -2
- package/dist/core/web/public/next/assets/viewport-nTubFCwf.js.map +0 -1
- package/dist/core/web/public/next/assets/warmup-HG5vYbsN.js +0 -3
- package/dist/core/web/public/next/assets/workbench-types-DzN-5trg.js +0 -2
- package/dist/core/web/public/next/assets/workbench-types-DzN-5trg.js.map +0 -1
- /package/dist/core/web/public/next/assets/{BurnDown-BuLMsJMx.css → StatTile-BuLMsJMx.css} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-model-DoxBMDvx.js","names":[],"sources":["../../../../../../__vite-browser-external","../../../../../../src/core/util/scrub.ts","../../../../../../src/web-ui/routes/verse/cloud/cloud-model.ts"],"sourcesContent":["module.exports = {}","/**\n * src/core/util/scrub.ts — THE shared secret-scrub utility (single source of truth).\n *\n * Extracted from judge-trace.ts (stripSecrets) and extended to cover all\n * patterns previously missing from handlers.ts (scrubDiffSecrets). Since 3.10\n * the two historical duplicates delegate here instead of drifting:\n * - src/core/generative/invent.ts re-exports `scrubSecrets` verbatim.\n * - src/core/knowledge/index.ts runs this scrub FIRST, then its own\n * stricter corpus patterns (32-hex, `/`-bearing base64, Stripe) that\n * `ashlr verify-safety` pins by array length. Knowledge chunks are never\n * identity-checked, so the stricter pass is safe there and must not leak\n * into this default (see \"IDENTITY CALLERS\" below).\n *\n * Patterns covered by `scrubSecrets`:\n * 0. PEM/private-key blocks\n * 1. sk- API keys (Anthropic, OpenAI, etc.)\n * 2. GitHub tokens: ghp_, gho_, ghu_, ghs_, ghr_, gha_, github_pat_ (classic\n * and fine-grained `github_pat_<22>_<59>`)\n * 3. Bearer / Token / Authorization header values\n * 4. Generic key=value secrets: api_key, secret, token, password, passwd,\n * auth, credential, client_secret, private_key, refresh_token,\n * access_token, connection_string (including ASHLR_* env vars)\n * 5. Slack tokens: xox[baprs]-…\n * 6. AWS access key IDs: AKIA…\n * 7. JWTs: eyJ….<sig>\n * 8. Bare hex-64 (SHA-256 / API key hex form)\n * 9. GitLab/HuggingFace/npm/Google token prefixes\n * 10. URL authority passwords and long base64 blobs\n * 11. (3.10) Telegram bot tokens `<bot id>:<33–35 chars>` — bare, in\n * `api.telegram.org/bot<token>/…` URLs, and under any key name\n * 12. (3.10) xAI API keys `xai-…`\n * 13. (3.10) Suffix-named assignments the generic rule's `\\b` anchor misses:\n * SCREAMING env vars (`GROQ_API_KEY=…`, `DB_PASSWORD=…`), snake_case\n * (`bot_token: …`) and camelCase (`githubToken: …`) names\n * 14. (3.10) Quoted JSON/YAML keys (`\"password\": \"…\"`, `\\\"botToken\\\":\\\"…\\\"`),\n * which rule 4 never matched because the closing quote sits between the\n * name and the colon\n * 15. (3.10) Slack / Discord incoming-webhook URL paths\n *\n * `scrubPrivateText` layers two PRIVACY redactions on top for free text that\n * is persisted or exported (reasoning store, session export, logs):\n * - home-directory paths → `~` (on by default)\n * - email addresses → `[REDACTED]` (opt-in)\n *\n * IDENTITY CALLERS (why home paths/emails are NOT in `scrubSecrets`): many\n * ledgers validate values with `scrubSecrets(v) === v` (dispatch manifests,\n * worked/dispatch-production ledgers, best-of-n, generated-repair) and several\n * of those values are absolute repo paths or git author identities. Rewriting\n * `/Users/<me>/repo` → `~/repo` in the default scrub would make every such\n * record look \"secret-bearing\" and be rejected, and would corrupt functional\n * paths. Privacy redaction is therefore an explicit, separate call.\n *\n * IDEMPOTENCE is load-bearing: records persisted after scrubbing are later\n * re-validated with `scrubSecrets(v) === v`, and `canonicalizeProposalDiff`\n * iterates to a fixed point. Every rule's output is a fixed point of every\n * rule (pinned by test/scrub-310.test.ts).\n *\n * PURITY: no I/O, no side-effects. `scrubPrivateText` reads the home directory\n * from the environment (no filesystem access) unless `homes` is supplied.\n */\n\nimport { homedir } from 'node:os';\n\nconst REDACTED = '[REDACTED]';\n\nfunction scrubLongBase64Like(match: string): string {\n // Preserve ordinary Git SHA-1 commit ids for forensic audit trails. The\n // explicit hex-64 rule above still redacts longer raw-key shapes.\n if (/^[0-9a-fA-F]{40}$/.test(match)) return match;\n // Avoid erasing obvious low-entropy test/log filler such as xxxxx...; real\n // token-like blobs still have mixed character content and are redacted below.\n const repeatedCandidate = match.replace(/^\\+/, '').replace(/=+$/, '');\n if (/^(.)\\1{39,}$/.test(repeatedCandidate)) return match;\n return REDACTED;\n}\n\n// ---------------------------------------------------------------------------\n// 3.10 additions\n// ---------------------------------------------------------------------------\n\n/**\n * Telegram bot token: numeric bot id (8–10 digits today; 6–12 tolerated) +\n * ':' + a 33–35 char url-safe secret (35 for issued tokens; Telegram's own\n * documentation example is 34). The optional `bot` prefix covers the Bot API\n * URL form (`https://api.telegram.org/bot<id>:<secret>/sendMessage`), where a\n * plain `\\b` anchor fails because `t` and the first digit are both word chars.\n * The narrow secret length plus the non-word lookarounds keep\n * `<epoch-seconds>:<uuid|hex32|sha1>` style ids (36 / 32 / 40 chars) intact —\n * several ledgers identity-check such ids.\n */\nconst TELEGRAM_BOT_TOKEN = /\\d{6,12}:[A-Za-z0-9_-]{33,35}(?![A-Za-z0-9_-])/g;\n\n/**\n * Left-boundary check for a Telegram candidate, done in the replacer instead of\n * a leading lookbehind: a regex that starts with `\\d` lets V8 skip ahead to\n * digits, which measured ~3x cheaper on 1 MB of prose than testing a\n * lookbehind at every offset. Accept when the id starts the string, follows a\n * non-word char, or follows a `bot` that itself follows a non-word char.\n */\nfunction redactTelegram(match: string, offset: number, whole: string): string {\n const boundary = (i: number): boolean => i < 0 || !/[A-Za-z0-9_]/.test(whole[i]!);\n if (boundary(offset - 1)) return REDACTED;\n if (whole.slice(offset - 3, offset) === 'bot' && boundary(offset - 4)) return REDACTED;\n return match;\n}\n\n/**\n * Incoming-webhook URLs are bearer credentials in their path (anyone holding\n * the URL can post). Host + fixed path prefix are kept for forensics.\n */\nconst WEBHOOK_URL =\n /(https?:\\/\\/(?:hooks\\.slack\\.com\\/(?:services|workflows|triggers)|(?:ptb\\.|canary\\.)?discord(?:app)?\\.com\\/api\\/webhooks)\\/)[A-Za-z0-9_/-]{8,}/gi;\n\n/** xAI (Grok) API keys: `xai-` + ≥20 alphanumerics (80 in practice). */\nconst XAI_API_KEY = /(?<![A-Za-z0-9_-])xai-[A-Za-z0-9]{20,}/g;\n\n/**\n * Value shape shared by the suffix-name rules: optional quote, ≥8 chars that\n * are not whitespace/separators/quotes, optional closing quote. Mirrors rule 4.\n */\nconst ASSIGNED_VALUE = String.raw`\\s*[=:]\\s*[\"']?[^\\s,;'\"]{8,}[\"']?`;\n\n/**\n * SCREAMING_SNAKE env-var names ending in a secret noun. Rule 4's `\\b(token|…)`\n * never fires inside `GROQ_API_KEY` / `TELEGRAM_BOT_TOKEN` because `_` is a\n * word char, so the whole `.env` idiom slipped through. Case-SENSITIVE on\n * purpose: `sort_key=…` in prose/code must not match.\n */\nconst ENV_SECRET_ASSIGNMENT = new RegExp(\n String.raw`(?<![A-Za-z0-9_$])([A-Z][A-Z0-9_]*_(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|CREDENTIALS?|AUTH|DSN))` +\n ASSIGNED_VALUE,\n 'g',\n);\n\n/** snake_case names with a secret suffix (`bot_token`, `db_password`). */\nconst SNAKE_SECRET_ASSIGNMENT = new RegExp(\n String.raw`(?<![A-Za-z0-9_$])([a-z][a-z0-9_]*_(?:api_key|token|secret|password|passwd|pwd|credentials?|private_key|access_key|secret_key|signing_key))` +\n ASSIGNED_VALUE,\n 'g',\n);\n\n/**\n * Cheap literal-led prefilters for the snake/camel rules. Those two regexes\n * start at every lowercase word, which measured ~3–4.5 ms per MB; the\n * prefilters cost <1 ms and skip them on the common text that has no\n * `…Token =` / `…_password:` assignment at all.\n */\nconst SNAKE_PREFILTER = /_(?:api_key|token|secret|password|passwd|pwd|credentials?|private_key|access_key|secret_key|signing_key)\\s*[=:]/;\nconst CAMEL_PREFILTER = /(?:Token|Secret|Password|Passwd|ApiKey|PrivateKey|AccessKey|SecretKey|SigningKey|Credentials?)\\s*[=:]/;\n\n/** camelCase names with a secret suffix (`githubToken`, `clientSecret`). */\nconst CAMEL_SECRET_ASSIGNMENT = new RegExp(\n String.raw`(?<![A-Za-z0-9_$])([a-z][A-Za-z0-9]*(?:Token|Secret|Password|Passwd|ApiKey|PrivateKey|AccessKey|SecretKey|SigningKey|Credentials?))` +\n ASSIGNED_VALUE,\n 'g',\n);\n\n/**\n * Quoted keys (JSON, YAML, JS object literals), including JSON-escaped quotes\n * inside a serialized string (`\\\"password\\\":\\\"…\\\"`). The name must END in a\n * secret noun (`max_tokens`, `tokenizer`, `author` do not). Structure is kept —\n * only the value becomes `[REDACTED]` — so a scrubbed JSON line stays parseable.\n */\nconst QUOTED_SECRET_KEY =\n /(\\\\?[\"'])([A-Za-z0-9_.-]*(?:api[_-]?key|token|secret|password|passwd|pwd|credentials?|private[_-]?key|access[_-]?key|secret[_-]?key|signing[_-]?key|auth|connection[_-]?string))\\1(\\s*:\\s*)(\\\\?[\"'])([^\"'\\s\\\\]{8,})\\4/gi;\n\n/**\n * Values in CODE that merely reference a secret (a member chain, call, template\n * or env interpolation) rather than contain one. Scrubbed diffs are persisted\n * and applied as proposals, so turning `const GITHUB_TOKEN = process.env.GITHUB_TOKEN`\n * into `GITHUB_TOKEN=[REDACTED]` would corrupt a legitimate change. Applies to\n * the 3.10 suffix rules only — rule 4's historical behaviour is unchanged.\n */\nfunction isCodeReference(value: string): boolean {\n const v = value.replace(/^[\"']|[\"']$/g, '');\n return (\n /^[A-Za-z_$][\\w$]*(?:\\??\\.[A-Za-z_$][\\w$]*)+[)\\]]*$/.test(v) || // process.env.X, opts.apiToken\n /^[A-Za-z_$][\\w$.]*\\(/.test(v) || // getToken(), cfg.read(\n /^(?:\\$\\{|\\$[A-Za-z_]|`|<)/.test(v) // ${VAR}, $VAR, `template`, <placeholder>\n );\n}\n\n/**\n * Type annotations and identifier-only right-hand sides (`refreshToken: RefreshTokenRecord`,\n * `botToken: undefined`) carry no digits; real tokens essentially always do.\n * Only the camel/snake rules use this skip (they fire inside ordinary TS code);\n * the SCREAMING env rule still redacts a letters-only `.env` password.\n */\nfunction isIdentifierOnly(value: string): boolean {\n const v = value.replace(/^[\"']|[\"']$/g, '');\n return !/\\d/.test(v) && /^[A-Za-z_$][A-Za-z_$.<>[\\]|&?!()]*$/.test(v);\n}\n\n/** A value an earlier rule (or an earlier pass) already replaced. */\nfunction isAlreadyRedacted(value: string): boolean {\n return value.replace(/^[\"']|[\"']$/g, '') === REDACTED;\n}\n\nfunction redactAssignment(name: string, match: string, value: string, allowIdentifiers: boolean): string {\n // Idempotence: `NAME = \"[REDACTED]\"` must stay byte-identical on a re-scrub.\n if (isAlreadyRedacted(value)) return match;\n if (isCodeReference(value)) return match;\n if (allowIdentifiers && isIdentifierOnly(value)) return match;\n return `${name}=${REDACTED}`;\n}\n\nfunction valueOf(match: string, name: string): string {\n return match.slice(name.length).replace(/^\\s*[=:]\\s*/, '');\n}\n\n/**\n * Scrub recognised secret patterns from `text`.\n * Returns the scrubbed string with secrets replaced by `[REDACTED]`.\n * Never throws. Idempotent: `scrubSecrets(scrubSecrets(x)) === scrubSecrets(x)`.\n */\nexport function scrubSecrets(text: string): string {\n try {\n let out = text\n // 0. PEM/private-key blocks. Run before generic/base64 redaction so\n // BEGIN/END markers do not survive with only the body removed.\n .replace(/-----BEGIN[ A-Z]*PRIVATE KEY-----[\\s\\S]*?-----END[ A-Z]*PRIVATE KEY-----/g, REDACTED)\n .replace(/-----BEGIN[ A-Z]*PRIVATE KEY-----[^\\n]*/g, REDACTED)\n // 11. Telegram bot tokens — before rule 4 so a `botToken: <id>:<secret>`\n // value is removed whole even when its key name is not recognised.\n .replace(TELEGRAM_BOT_TOKEN, redactTelegram)\n // 15. Slack / Discord incoming-webhook URLs (credential in the path).\n .replace(WEBHOOK_URL, '$1[REDACTED]')\n // 12. xAI keys — before rule 10 so the `xai-` prefix does not survive\n // next to a base64-redacted tail.\n .replace(XAI_API_KEY, REDACTED)\n // 1. sk- API keys (Anthropic, OpenAI, etc.)\n .replace(/\\bsk-[A-Za-z0-9_-]{16,}/g, REDACTED)\n // 2. GitHub tokens (classic prefixes + fine-grained github_pat_<22>_<59>)\n .replace(/\\bgh[poursa]_[A-Za-z0-9]{16,}/g, REDACTED)\n .replace(/\\bgithub_pat_[A-Za-z0-9_]{22,}/g, REDACTED)\n // 3. Bearer / Token / Authorization header values\n .replace(/\\b(Bearer|Token|Authorization)\\s+[A-Za-z0-9\\-._~+/]+=*/gi, '$1 [REDACTED]')\n // 4. Generic key=value secret patterns. ASHLR_* env vars are only\n // redacted when they look like assignments, not plain audit metadata\n // such as \"keys=query\".\n .replace(\n /\\b(api[_-]?key|api[_-]?token|secret|secret[_-]?key|token|password|passwd|pwd|auth|credential|client[_-]?secret|private[_-]?key|access[_-]?token|auth[_-]?token|refresh[_-]?token|id[_-]?token|session[_-]?token|connection[_-]?string|conn[_-]?str|_?auth[_-]?token|ASHLR_[A-Z_]+)\\s*[=:]\\s*[\"']?[^\\s,;'\"]{8,}[\"']?/gi,\n '$1=[REDACTED]',\n )\n // 5. Slack tokens\n .replace(/\\bxox[baprs]-[A-Za-z0-9-]{10,}/gi, REDACTED)\n // 6. AWS access key IDs\n .replace(/\\bAKIA[0-9A-Z]{16}\\b/g, REDACTED)\n // 7. JWTs (eyJ header.payload.sig)\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+/g, REDACTED)\n // 8. Bare hex-64 strings (SHA-256 / hex API keys)\n .replace(/\\b[0-9a-fA-F]{64,}\\b/g, REDACTED)\n // 9. Other common provider token prefixes.\n .replace(/\\bglpat-[A-Za-z0-9_-]{16,}/g, REDACTED)\n .replace(/\\bhf_[A-Za-z0-9]{16,}\\b/g, REDACTED)\n .replace(/\\bnpm_[A-Za-z0-9]{16,}\\b/g, REDACTED)\n .replace(/\\bAIza[0-9A-Za-z_-]{35}\\b/g, REDACTED)\n // 10. URL passwords and long base64-ish blobs.\n .replace(/(:\\/\\/[^:\\s/@]*:)[^@\\s]+(@)/g, '$1[REDACTED]$2')\n .replace(/(?<![/\\w])[A-Za-z0-9+]{40,}={0,2}(?![/\\w])/g, scrubLongBase64Like)\n // 13. Suffix-named assignments rule 4's `\\b` anchor cannot see. LAST, so\n // every token-shape rule above has already turned a JWT/AKIA/hex value\n // into [REDACTED]; that marker is left untouched below. Running these\n // earlier broke idempotence: a dotted JWT reads like a member chain\n // (skipped as a code reference), then pass 2 rewrote `NAME = \"[REDACTED]\"`.\n .replace(ENV_SECRET_ASSIGNMENT, (m: string, name: string) => redactAssignment(name, m, valueOf(m, name), false));\n if (SNAKE_PREFILTER.test(out)) {\n out = out.replace(SNAKE_SECRET_ASSIGNMENT, (m: string, name: string) => redactAssignment(name, m, valueOf(m, name), true));\n }\n if (CAMEL_PREFILTER.test(out)) {\n out = out.replace(CAMEL_SECRET_ASSIGNMENT, (m: string, name: string) => redactAssignment(name, m, valueOf(m, name), true));\n }\n // 14. Quoted keys: keep the JSON/YAML structure, replace the value only.\n return out.replace(\n QUOTED_SECRET_KEY,\n (m: string, q: string, name: string, sep: string, vq: string, value: string) =>\n isAlreadyRedacted(value) || isCodeReference(value) ? m : `${q}${name}${q}${sep}${vq}${REDACTED}${vq}`,\n );\n } catch {\n // Never throws — return original text on unexpected error.\n return text;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Privacy redaction for persisted / exported free text\n// ---------------------------------------------------------------------------\n\nexport interface PrivateScrubOptions {\n /** Rewrite home-directory paths to `~`. Default: true. */\n readonly homePaths?: boolean;\n /** Replace email addresses with `[REDACTED]`. Default: false (opt-in). */\n readonly emails?: boolean;\n /**\n * Home directories to collapse. Default: `os.homedir()`, `$HOME`,\n * `$USERPROFILE`. Supplied explicitly by callers that already resolved them\n * (and by tests).\n */\n readonly homes?: readonly string[];\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction defaultHomes(): string[] {\n const out: string[] = [];\n try {\n out.push(homedir());\n } catch {\n // homedir() can throw when no home is resolvable; env fallbacks below.\n }\n if (typeof process === 'undefined') return out;\n for (const key of ['HOME', 'USERPROFILE'] as const) {\n const v = process.env[key];\n if (v) out.push(v);\n }\n return out;\n}\n\n/**\n * Case-insensitive filesystems (macOS APFS default, Windows) name one home many\n * ways. A function, not a module-level constant: the Verse web UI imports\n * `scrubSecrets` from this module (routes/verse/cloud/cloud-model.ts), and a\n * top-level `process.platform` read throws `ReferenceError: process is not\n * defined` in the browser — which took down every lazy section that pulls the\n * cloud model in (Command, Fleet). Nothing here may touch `process` at load.\n */\nfunction caseInsensitiveFs(): boolean {\n if (typeof process === 'undefined') return false;\n return process.platform === 'darwin' || process.platform === 'win32';\n}\n\n/**\n * Other users' home roots (`/Users/<name>`, `/home/<name>`, `C:\\Users\\<name>`):\n * the username is the PII, so the whole root collapses to `~` too. macOS's\n * `/Users/Shared` is a system directory, not a person.\n */\nconst GENERIC_HOME_ROOT =\n /(?<![\\w.-])(?:\\/(?:Users|home)\\/(?!Shared(?![\\w.-]))[A-Za-z0-9._-]+|[A-Za-z]:\\\\Users\\\\[A-Za-z0-9._-]+)(?![\\w.-])/g;\n\n/**\n * Collapse home-directory paths to `~`. A home only matches as a whole path\n * prefix on segment boundaries: `/Users/me/x` → `~/x`; `/opt/Users/me` is left\n * alone, and `/Users/meagan` is never split into `~agan` (it is another\n * person's home, collapsed whole by the generic rule). Never throws.\n */\nexport function redactHomePaths(text: string, homes: readonly string[] = defaultHomes()): string {\n try {\n let out = text;\n const unique = Array.from(\n new Set(\n homes\n .map((h) => h.replace(/[\\\\/]+$/, ''))\n // A bare root (\"/\", \"C:\") would collapse every absolute path.\n .filter((h) => h.length > 1 && !/^[A-Za-z]:$/.test(h)),\n ),\n ).sort((a, b) => b.length - a.length);\n for (const home of unique) {\n const re = new RegExp(`(?<![\\\\w.-])${escapeRegExp(home)}(?![\\\\w.-])`, caseInsensitiveFs() ? 'gi' : 'g');\n out = out.replace(re, '~');\n }\n return out.replace(GENERIC_HOME_ROOT, '~');\n } catch {\n return text;\n }\n}\n\n/**\n * Email addresses in free text. `git@host:org/repo` SSH remotes are not\n * personal addresses and stay intact; `pkg@1.2.3` version pins never match\n * because the final label must be alphabetic.\n */\nconst EMAIL = /(?<![\\w.%+-])([A-Za-z0-9._%+-]+)@[A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)*\\.[A-Za-z]{2,}(?![\\w-])/g;\n\n/** Replace email addresses with `[REDACTED]`. Never throws. */\nexport function redactEmails(text: string): string {\n try {\n return text.replace(EMAIL, (m: string, local: string) => (local === 'git' ? m : REDACTED));\n } catch {\n return text;\n }\n}\n\n/**\n * Secrets + privacy scrub for free text that is persisted or exported\n * (reasoning steps, session exports, crash logs). Runs `scrubSecrets`, then\n * collapses home paths (default on) and, when asked, emails. Home paths are\n * collapsed on both sides of the secret scrub so a secret rule can never split\n * a home prefix into an unrecognisable remainder (same idiom as\n * `sanitizePublicJson`). Never throws. Idempotent.\n */\nexport function scrubPrivateText(text: string, options: PrivateScrubOptions = {}): string {\n const homePaths = options.homePaths !== false;\n const homes = homePaths ? (options.homes ?? defaultHomes()) : [];\n let out = homePaths ? redactHomePaths(text, homes) : text;\n out = scrubSecrets(out);\n if (homePaths) out = redactHomePaths(out, homes);\n if (options.emails === true) out = redactEmails(out);\n return out;\n}\n\n/** Stable bytes for proposal persistence, hashing, and provenance signing. */\nexport function canonicalizeProposalDiff(text: string): string {\n let current = text;\n for (let pass = 0; pass < 8; pass += 1) {\n const next = scrubSecrets(current)\n .replace(/\\bsk_(?:live|test)_[A-Za-z0-9_]{16,}\\b/g, REDACTED)\n .replace(/\\b(?:AKIA|ASIA|AROA)[0-9A-Z]{16}\\b/g, REDACTED)\n .replace(/\\b[0-9a-fA-F]{32,}\\b/g, REDACTED);\n if (next === current) return current;\n current = next;\n }\n throw new Error('proposal diff canonicalization did not converge');\n}\n","/**\n * routes/verse/cloud/cloud-model.ts — the cloud lane's words and decisions as\n * pure functions (3.11 unit C3), so the card, the Usage panel, the Fleet chip\n * and the composer action say the same thing and the copy is tested as a\n * table. No React here.\n *\n * Copy rules (DESIGN §13.8, and the cloud contract's own):\n * - spend is ALWAYS an estimate and says so — Claude does not expose the\n * credit balance, so every dollar figure carries \"estimate\" beside it;\n * - server prose (gate reasons, state reasons, report summaries) is scrubbed\n * for secrets before `tidyProse` makes ISO instants readable in local time;\n * - times are local and relative (\"started 5m ago\"), never ISO;\n * - a state is always a WORD beside any colour.\n */\nimport {\n CLOUD_PROMPT_MAX_CHARS,\n CLOUD_TERMINAL_STATES,\n DEFAULT_CLOUD_BUDGET,\n type CloudBudgetUpdate,\n type CloudBudgetV1,\n type CloudBudgetView,\n type CloudSeatStatus,\n type CloudTaskState,\n type CloudTaskV1,\n} from '../../../../core/cloud/types.js';\nimport { describeResetAt } from '../../../../core/verse/seat-readiness.js';\nimport { scrubSecrets } from '../../../../core/util/scrub.js';\nimport type { Tone } from '../../../components/primitives/StatusBadge.js';\nimport type { MeterTone } from '../../../components/primitives/Meter.js';\nimport { tidyProse } from '../autonomy/format.js';\nimport { relativePhrase } from '../context/context-model.js';\nimport { usedPercentText } from '../percent-text.js';\n\n/** Cloud text can originate in a PR body; keep every card reason safe even outside the HTTP sanitizer. */\nfunction safeCloudProse(text: string, now?: number): string {\n return tidyProse(scrubSecrets(text), now);\n}\n\n/** The repo a new task targets unless the operator names another (the self-improvement default). */\nexport const CLOUD_DEFAULT_REPO = DEFAULT_CLOUD_BUDGET.selfImprove.repo;\n\n/** Same rule the service enforces (core/cloud/service.ts), checked here so the dialog can say so first. */\nexport const CLOUD_REPO_PATTERN = /^[A-Za-z0-9-]+\\/[A-Za-z0-9._-]+$/;\n\n/**\n * A branch name git would accept, conservatively: no spaces or control\n * characters, no `..`, no leading `-` or `/`, no trailing `/` or `.lock`.\n * The server re-checks it against origin; this only catches typing slips.\n */\nconst BRANCH_CHARS = /^[A-Za-z0-9._/+-]{1,200}$/;\nexport function isBranchName(value: string): boolean {\n if (!BRANCH_CHARS.test(value)) return false;\n if (value.startsWith('-') || value.startsWith('/') || value.endsWith('/') || value.endsWith('.') || value.endsWith('.lock')) return false;\n return !value.includes('..') && !value.includes('//');\n}\n\n/** Only these hosts become links: the ids came from a CLI's stdout and gh's JSON. */\nexport function safeHref(url: string | null | undefined, host: 'claude.ai' | 'github.com'): string | null {\n if (typeof url !== 'string' || url.length > 2_048) return null;\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'https:' && parsed.hostname === host ? parsed.toString() : null;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Money and the credits meter\n// ---------------------------------------------------------------------------\n\n/** \"$250\", \"$12.50\" — whole dollars stay whole; never \"-$0\". */\nexport function formatDollars(value: number): string {\n if (!Number.isFinite(value)) return '—';\n const v = Math.abs(value) < 0.005 ? 0 : value;\n const text = Number.isInteger(v) ? String(Math.abs(v)) : Math.abs(v).toFixed(2);\n return `${v < 0 ? '−' : ''}$${text}`;\n}\n\nexport interface CreditsMeterView {\n /** Remaining, clamped at 0 for the bar (the text says when it went below). */\n value: number;\n max: number | null;\n /** \"$212 of $250 · estimate\" */\n text: string;\n /** \"15% used\" — the one percent rule. */\n usedText: string;\n tone: MeterTone;\n /** A plain sentence when the estimate is at or under the self-improvement reserve. */\n warning: string | null;\n}\n\nexport function creditsMeter(view: CloudBudgetView): CreditsMeterView {\n const total = view.creditsTotalUsd;\n const remaining = view.estimatedRemainingUsd;\n const reserve = view.budget.selfImprove.reserveUsd;\n const max = total > 0 ? total : null;\n const used = max === null ? null : ((total - Math.max(0, remaining)) / total) * 100;\n const tone: MeterTone = remaining <= 0 ? 'danger' : remaining < reserve ? 'warning' : 'accent';\n const warning = remaining <= 0\n ? 'The estimate says the credits are spent. Check the real balance on claude.ai and adjust the budget.'\n : remaining < reserve\n ? `Under the ${formatDollars(reserve)} reserve, so Verse stops launching self-improvement tasks.`\n : null;\n return {\n value: Math.max(0, remaining),\n max,\n text: `${formatDollars(Math.max(0, remaining))} of ${formatDollars(total)} · estimate`,\n usedText: used === null ? 'no credit total set' : `${usedPercentText(used)} used`,\n tone,\n warning,\n };\n}\n\n/** The next local midnight — when the per-day caps start counting again. */\nexport function nextLocalMidnight(now: number): number {\n const d = new Date(now);\n d.setHours(24, 0, 0, 0);\n return d.getTime();\n}\n\n/** \"3 of 20 sessions today · 1 running\" plus when the daily count resets. */\nexport function sessionsLine(view: CloudBudgetView, now: number = Date.now()): string {\n const reset = describeResetAt(new Date(nextLocalMidnight(now)).toISOString(), now);\n const running = view.running === 1 ? '1 running' : `${view.running} running`;\n return `${view.sessionsToday} of ${view.budget.maxSessionsPerDay} sessions today · ${running}${reset ? ` · resets ${reset}` : ''}`;\n}\n\nexport function selfImproveLine(view: CloudBudgetView): string {\n const si = view.budget.selfImprove;\n if (!si.enabled) return 'Self-improvement is off. Verse launches cloud tasks only when you ask.';\n return `${view.selfImproveToday} of ${si.maxPerDay} self-improvement launches today, on ${si.repo}. Stops under a ${formatDollars(si.reserveUsd)} estimated balance.`;\n}\n\n/** A gate's reason, fit to print (null when the gate is open). */\nexport function gateText(gate: { ok: boolean; reason: string | null }, fallback: string): string | null {\n if (gate.ok) return null;\n return gate.reason ? safeCloudProse(gate.reason) : fallback;\n}\n\nexport const SEAT_NOT_READY = \"The Claude seat isn't set up on this Mac.\";\n\nexport function seatText(seat: CloudSeatStatus): string | null {\n if (seat.ready) return null;\n return seat.reason ? safeCloudProse(seat.reason) : SEAT_NOT_READY;\n}\n\n// ---------------------------------------------------------------------------\n// Standing: what the credit figure MEANS while the lane cannot launch\n// ---------------------------------------------------------------------------\n//\n// The credit figure is always an estimate (a flat per-session cost counted\n// against the total the operator typed in; the real balance is not\n// readable). While the lane can launch, that estimate is headroom and a meter\n// is the honest picture. While the Claude seat is NOT set up nothing can\n// launch, so \"$250 of $250 left\" beside \"the seat isn't set up\" read as live\n// headroom the operator could not use. Then the setup blocker is the STATE,\n// and the credits a secondary, visibly approximate figure:\n//\n// Cloud: not set up · ~$250 credits\n//\n// Shared by Command's Cloud card, Usage's Cloud credits panel and the\n// Resources drawer's cloud card.\n\n/** The state word shown in place of the meter while the seat is missing. */\nexport const CLOUD_NOT_SET_UP_WORD = 'Not set up';\n\n/**\n * \"~$250 credits\" — whole dollars, because an estimate that cannot be spent\n * yet has no business showing cents. Never negative.\n */\nexport function approxCredits(remainingUsd: number): string {\n const v = Number.isFinite(remainingUsd) ? Math.max(0, Math.round(remainingUsd)) : 0;\n return `~$${v} credits`;\n}\n\n/** \"Cloud: not set up · ~$250 credits\" — the one-line summary of a lane that cannot launch. */\nexport function notSetUpLine(remainingUsd: number): string {\n return `Cloud: not set up · ${approxCredits(remainingUsd)}`;\n}\n\n/**\n * Why a launch button is disabled right now, or null when it may be pressed.\n * Seat first (nothing launches without it), then the budget gate.\n */\nexport function launchBlock(seat: CloudSeatStatus, budget: CloudBudgetView): string | null {\n return seatText(seat) ?? gateText(budget.canLaunch, 'The cloud budget does not allow another launch right now.');\n}\n\n// ---------------------------------------------------------------------------\n// Tasks\n// ---------------------------------------------------------------------------\n\nexport const STATE_WORD: Record<CloudTaskState, string> = {\n queued: 'Queued',\n launching: 'Launching',\n running: 'Running',\n 'pr-open': 'PR open',\n merged: 'Merged',\n closed: 'Closed',\n failed: 'Failed',\n expired: 'Expired',\n};\n\nexport const STATE_TONE: Record<CloudTaskState, Tone> = {\n queued: 'info',\n launching: 'info',\n running: 'running',\n 'pr-open': 'warning',\n merged: 'success',\n closed: 'neutral',\n failed: 'danger',\n expired: 'unknown',\n};\n\n/** A draft PR reads \"Draft PR\", not \"PR open\": the session may still be pushing. */\nexport function stateWord(task: Pick<CloudTaskV1, 'state' | 'pr'>): string {\n if (task.state === 'pr-open' && !task.pr) return 'PR unverified';\n if (task.state === 'pr-open' && task.pr?.draft) return 'Draft PR';\n return STATE_WORD[task.state] ?? task.state;\n}\n\nexport function isTerminal(state: CloudTaskState): boolean {\n return CLOUD_TERMINAL_STATES.includes(state);\n}\n\n/** In flight: counted by the Fleet chip and the \"running\" line. */\nexport function isInFlight(state: CloudTaskState): boolean {\n return state === 'queued' || state === 'launching' || state === 'running';\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n/** How many tasks the card lists before \"N more on GitHub\". */\nexport const CARD_TASK_LIMIT = 6;\n\n/**\n * What the card lists: everything still moving or waiting on review, then\n * anything that ended in the last 24 h (a failure you have not seen yet),\n * newest first. Older finished tasks live in `ashlr cloud list --all`.\n */\nexport function cardTasks(tasks: readonly CloudTaskV1[], now: number = Date.now()): { shown: CloudTaskV1[]; hidden: number } {\n const recent = (t: CloudTaskV1) => {\n const at = Date.parse(t.updatedAt);\n return Number.isFinite(at) && now - at < DAY_MS;\n };\n const listed = tasks\n .filter((t) => !isTerminal(t.state) || recent(t))\n .sort((a, b) => (Date.parse(b.createdAt) || 0) - (Date.parse(a.createdAt) || 0));\n return { shown: listed.slice(0, CARD_TASK_LIMIT), hidden: Math.max(0, listed.length - CARD_TASK_LIMIT) };\n}\n\nexport function inFlightCount(tasks: readonly CloudTaskV1[]): number {\n return tasks.filter((t) => isInFlight(t.state)).length;\n}\n\n/** \"ashlrai/ashlr-hub from master · started 5m ago\" */\nexport function taskMeta(task: CloudTaskV1, now: number = Date.now()): string {\n const when = task.state === 'queued' || task.state === 'launching' ? task.createdAt : task.launchedAt ?? task.createdAt;\n const phrase = relativePhrase(when, now);\n const verb = task.state === 'queued' ? 'queued' : task.state === 'failed' ? 'tried' : 'started';\n return `${task.repo} from ${task.baseBranch}${phrase ? ` · ${verb} ${phrase}` : ''}`;\n}\n\n/** The one line under a task's title: the report for a PR, else the state's reason. */\nexport function taskDetail(task: CloudTaskV1, now: number = Date.now()): string | null {\n if (task.report?.summary && (task.state === 'pr-open' || task.state === 'merged')) return `Cloud session reports (unverified): ${safeCloudProse(task.report.summary, now)}`;\n return task.stateReason ? safeCloudProse(task.stateReason, now) : null;\n}\n\n/** Dismiss is offered for anything Verse is still tracking; merged and closed tasks are already done. */\nexport function canDismiss(task: Pick<CloudTaskV1, 'state'>): boolean {\n return task.state !== 'merged' && task.state !== 'closed';\n}\n\n// ---------------------------------------------------------------------------\n// The launch form\n// ---------------------------------------------------------------------------\n\nexport interface LaunchDraft {\n repo: string;\n baseBranch: string;\n prompt: string;\n}\n\nexport type LaunchErrors = Partial<Record<keyof LaunchDraft, string>>;\n\nexport type LaunchValidation =\n | { ok: true; request: { repo: string; baseBranch?: string; prompt: string } }\n | { ok: false; errors: LaunchErrors };\n\nconst COUNT = new Intl.NumberFormat('en-US');\n\nexport function validateLaunch(draft: LaunchDraft): LaunchValidation {\n const errors: LaunchErrors = {};\n const repo = draft.repo.trim();\n const base = draft.baseBranch.trim();\n const prompt = draft.prompt.trim();\n if (!repo) errors.repo = 'Enter the GitHub repository as owner/name.';\n else if (!CLOUD_REPO_PATTERN.test(repo)) errors.repo = 'Use the GitHub owner/name form, like ashlrai/ashlr-hub.';\n if (base && !isBranchName(base)) errors.baseBranch = \"That isn't a branch name git accepts.\";\n if (!prompt) errors.prompt = 'Describe the task for the cloud session.';\n else if (prompt.length > CLOUD_PROMPT_MAX_CHARS) {\n errors.prompt = `The task is ${COUNT.format(prompt.length)} characters; the limit is ${COUNT.format(CLOUD_PROMPT_MAX_CHARS)}.`;\n }\n if (Object.keys(errors).length > 0) return { ok: false, errors };\n return { ok: true, request: { repo, ...(base ? { baseBranch: base } : {}), prompt } };\n}\n\n// ---------------------------------------------------------------------------\n// The budget form (Edit budget popover and the Usage panel share it)\n// ---------------------------------------------------------------------------\n\nexport interface BudgetForm {\n total: string;\n spent: string;\n perSession: string;\n maxPerDay: string;\n maxConcurrent: string;\n selfImprove: boolean;\n selfImproveMax: string;\n reserve: string;\n}\n\nexport type BudgetFormField = Exclude<keyof BudgetForm, 'selfImprove'>;\nexport type BudgetErrors = Partial<Record<BudgetFormField, string>>;\n\n/** Field → label and bounds. Bounds are the form's sanity check; the server clamps as well. */\nexport const BUDGET_FIELDS: ReadonlyArray<{\n field: BudgetFormField;\n label: string;\n hint: string;\n money: boolean;\n integer: boolean;\n min: number;\n max: number;\n}> = [\n { field: 'total', label: 'Credits on the account', hint: 'What claude.ai says you have.', money: true, integer: false, min: 0, max: 100_000 },\n { field: 'spent', label: 'Already spent', hint: 'Spent before Verse tracked it, or a correction after checking.', money: true, integer: false, min: -100_000, max: 100_000 },\n { field: 'perSession', label: 'Estimate per session', hint: 'Flat cost Verse counts for each launch.', money: true, integer: false, min: 0.01, max: 1_000 },\n { field: 'maxPerDay', label: 'Sessions per day', hint: 'From every origin, counted per local day.', money: false, integer: true, min: 0, max: 500 },\n { field: 'maxConcurrent', label: 'Running at once', hint: 'Launching or running together.', money: false, integer: true, min: 1, max: 50 },\n { field: 'selfImproveMax', label: 'Self-improvement per day', hint: 'Launches Verse may make on its own.', money: false, integer: true, min: 0, max: 100 },\n { field: 'reserve', label: 'Reserve', hint: 'Self-improvement stops under this estimated balance.', money: true, integer: false, min: 0, max: 100_000 },\n];\n\nfunction numText(n: number): string {\n return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100);\n}\n\nexport function budgetFormFrom(b: CloudBudgetV1): BudgetForm {\n return {\n total: numText(b.creditsTotalUsd),\n spent: numText(b.creditsSpentAdjustmentUsd),\n perSession: numText(b.estimatedCostPerSessionUsd),\n maxPerDay: numText(b.maxSessionsPerDay),\n maxConcurrent: numText(b.maxConcurrent),\n selfImprove: b.selfImprove.enabled,\n selfImproveMax: numText(b.selfImprove.maxPerDay),\n reserve: numText(b.selfImprove.reserveUsd),\n };\n}\n\nexport type BudgetPatch = { ok: true; update: CloudBudgetUpdate; changed: boolean } | { ok: false; errors: BudgetErrors };\n\n/**\n * The form as ONE update carrying only what changed — so two people editing\n * different fields never overwrite each other's numbers with stale ones.\n */\nexport function budgetPatch(form: BudgetForm, current: CloudBudgetV1): BudgetPatch {\n const errors: BudgetErrors = {};\n const values: Partial<Record<BudgetFormField, number>> = {};\n for (const spec of BUDGET_FIELDS) {\n const raw = form[spec.field].trim().replace(/^\\$/, '');\n const n = raw === '' ? Number.NaN : Number(raw);\n if (!Number.isFinite(n)) errors[spec.field] = 'Enter a number.';\n else if (spec.integer && !Number.isInteger(n)) errors[spec.field] = 'Enter a whole number.';\n else if (n < spec.min || n > spec.max) {\n errors[spec.field] = spec.money\n ? `Between ${formatDollars(spec.min)} and ${formatDollars(spec.max)}.`\n : `Between ${COUNT.format(spec.min)} and ${COUNT.format(spec.max)}.`;\n } else values[spec.field] = n;\n }\n if (Object.keys(errors).length > 0) return { ok: false, errors };\n const update: CloudBudgetUpdate = {};\n const same = (a: number, b: number) => Math.abs(a - b) < 0.005;\n if (!same(values.total!, current.creditsTotalUsd)) update.creditsTotalUsd = values.total!;\n if (!same(values.spent!, current.creditsSpentAdjustmentUsd)) update.creditsSpentAdjustmentUsd = values.spent!;\n if (!same(values.perSession!, current.estimatedCostPerSessionUsd)) update.estimatedCostPerSessionUsd = values.perSession!;\n if (!same(values.maxPerDay!, current.maxSessionsPerDay)) update.maxSessionsPerDay = values.maxPerDay!;\n if (!same(values.maxConcurrent!, current.maxConcurrent)) update.maxConcurrent = values.maxConcurrent!;\n const selfImprove: NonNullable<CloudBudgetUpdate['selfImprove']> = {};\n if (form.selfImprove !== current.selfImprove.enabled) selfImprove.enabled = form.selfImprove;\n if (!same(values.selfImproveMax!, current.selfImprove.maxPerDay)) selfImprove.maxPerDay = values.selfImproveMax!;\n if (!same(values.reserve!, current.selfImprove.reserveUsd)) selfImprove.reserveUsd = values.reserve!;\n if (Object.keys(selfImprove).length > 0) update.selfImprove = selfImprove;\n return { ok: true, update, changed: Object.keys(update).length > 0 };\n}\n\n// ---------------------------------------------------------------------------\n// The composer's \"Run in cloud\"\n// ---------------------------------------------------------------------------\n\nexport interface RunInCloudInputs {\n /** The cloud overview, or null when the route did not answer. */\n overview: { seat: CloudSeatStatus; budget: CloudBudgetView } | null;\n /** Why the overview is missing, when it is. */\n overviewReason: string | null;\n /** The chat project's GitHub `owner/name`, or null when it has none. */\n repo: string | null;\n /** True while the chat's roots have not been read yet. */\n rootsLoading: boolean;\n prompt: string;\n}\n\n/** Null when \"Run in cloud\" may be pressed; otherwise the tooltip saying why not. */\nexport function runInCloudBlock(inputs: RunInCloudInputs): string | null {\n if (!inputs.overview) return inputs.overviewReason ?? 'The cloud lane did not answer.';\n const seat = seatText(inputs.overview.seat);\n if (seat) return seat;\n if (!inputs.repo) {\n return inputs.rootsLoading\n ? \"Reading this chat's project…\"\n : \"This chat's project has no GitHub origin, so a cloud session has nothing to clone.\";\n }\n const gate = gateText(inputs.overview.budget.canLaunch, 'The cloud budget does not allow another launch right now.');\n if (gate) return gate;\n if (!inputs.prompt.trim()) return 'Type the task in the message box first.';\n if (inputs.prompt.trim().length > CLOUD_PROMPT_MAX_CHARS) {\n return `The message is over ${COUNT.format(CLOUD_PROMPT_MAX_CHARS)} characters — trim it to run it in the cloud.`;\n }\n return null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{E as e,H as t,P as n,c as r,l as i,o as a}from"./global-CXHHMz9o.js";import{F as o}from"./VerseConsoleApp-DeuBEvAO.js";import{c as s,d as c,l,o as u,r as d,s as f,u as p}from"./types-D5XOdyw8.js";var m=`verse-cloud`,h=3e4,g=class extends Error{constructor(){super(`Unlock actions with the mutation token first.`),this.name=`CloudLockedError`}},_=class extends Error{failure;constructor(e,t){super(e),this.failure=t,this.name=`CloudLaunchRefusedError`}};function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e){if(!v(e))return null;let{seat:t,budget:n,tasks:r,backlog:i}=e;if(!v(t)||typeof t.ready!=`boolean`||!v(n)||!v(n.budget)||!v(n.canLaunch)||!v(n.canSelfImprove))return null;for(let e of[`creditsTotalUsd`,`estimatedSpentUsd`,`estimatedRemainingUsd`,`sessionsToday`,`selfImproveToday`,`running`])if(typeof n[e]!=`number`||!Number.isFinite(n[e]))return null;return!Array.isArray(r)||!r.every(e=>v(e)&&typeof e.id==`string`&&typeof e.state==`string`)||!v(i)||!Array.isArray(i.items)?null:e}var b=`The cloud lane is not in this build yet.`;function x(e){return e instanceof a?e.status===404?b:e.status===503?`The cloud lane failed to load on the server.`:`The cloud lane answered HTTP ${e.status}.`:`The cloud lane could not be reached.`}var S={key:m,fetch:async e=>{try{let t=y(await r(l,e));return t===null?{value:null,available:!0,reason:`Unrecognized response — update Ashlr.`}:{value:t,available:!0,reason:null}}catch(e){if(e instanceof a&&e.status===401||e instanceof DOMException&&e.name===`AbortError`)throw e;return{value:null,available:!1,reason:x(e)}}}};async function C(t,r){let a=e();if(!a)throw new g;let o=await i(t,r,a);return n(),o}function w(){t(m),o()}async function T(e){try{let t=await C(s,e);if(!v(t)||t.ok!==!0||!v(t.task))throw new _(v(t)&&typeof t.error==`string`&&t.error?t.error:`The cloud session was not started, and the server sent no reason.`,v(t)&&typeof t.failure==`string`?t.failure:null);return t}finally{w()}}async function E(e){let n=await C(u,e);return t(m),n}async function D(e={count:1}){try{let t=await C(f,e);return{launched:v(t)&&Array.isArray(t.launched)?t.launched:[],skipped:v(t)&&Array.isArray(t.skipped)?t.skipped:[]}}finally{w()}}async function O(){try{return await C(p,{})}finally{w()}}async function k(e){if(!d.test(e))throw Error(`That task id is not one Verse issued, so nothing was sent.`);try{return await C(`${c}/${e}/dismiss`,{})}finally{w()}}export{T as a,E as c,k as i,h as n,O as o,S as r,D as s,b as t};
|
|
2
|
+
//# sourceMappingURL=cloud-queries-CTGvO-k-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-queries-CTGvO-k-.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/cloud/cloud-queries.ts"],"sourcesContent":["/**\n * routes/verse/cloud/cloud-queries.ts — the one read and the writes behind\n * the cloud lane's UI (3.11 unit C3; server: core/cloud/cloud-api.ts, C2).\n *\n * GET /api/verse/cloud CloudOverviewResponse\n * POST /api/verse/cloud/launch CloudLaunchRequest → CloudLaunchResponse\n * POST /api/verse/cloud/budget CloudBudgetUpdate\n * POST /api/verse/cloud/refresh (re-read tasks from GitHub)\n * POST /api/verse/cloud/improve CloudImproveRequest → CloudImproveResponse\n * POST /api/verse/cloud/tasks/<id>/dismiss (mark closed; never touches GitHub)\n *\n * WHY the read is OPTIONAL (the surface-data.ts rule): the routes land in a\n * parallel unit, and a server without them answers 404. That is \"this build\n * has no cloud lane yet\" — one card's designed state — never a broken\n * Command. 401 and aborts still propagate: an expired read session is the\n * whole app's state.\n *\n * LAZY ONLY. Everything that imports this file (Command's card, the Usage\n * panel, the Fleet chip, the composer's \"Run in cloud\") is itself behind an\n * import(); nothing cloud-related may cost chat first-paint bytes.\n *\n * Writes pull the held mutation token (CloudLockedError without one — the\n * callers route every write through a token gate that opens the unlock\n * dialog first), then invalidate the overview so every cloud view moves\n * together, and nudge the shell's activity poll (Needs-you carries cloud\n * items).\n */\nimport {\n CLOUD_TASK_ID_PATTERN,\n VERSE_CLOUD_BUDGET_PATH,\n VERSE_CLOUD_IMPROVE_PATH,\n VERSE_CLOUD_LAUNCH_PATH,\n VERSE_CLOUD_PATH,\n VERSE_CLOUD_REFRESH_PATH,\n VERSE_CLOUD_TASKS_PATH,\n type CloudBudgetUpdate,\n type CloudImproveRequest,\n type CloudImproveResponse,\n type CloudLaunchFailureCode,\n type CloudLaunchRequest,\n type CloudLaunchResponse,\n type CloudOverviewResponse,\n} from '../../../../core/cloud/types.js';\nimport { ApiError, apiGet, apiPost } from '../../../data/client.js';\nimport { getMutationToken, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { refreshActivity } from '../shell/useActivity.js';\nimport type { OptionalRead } from '../command/surface-data.js';\n\nexport const CLOUD_KEY = 'verse-cloud';\n\n/** The card, the chip and the panel all poll at this pace while visible (SPEC 3.11 C3). */\nexport const CLOUD_POLL_MS = 30_000;\n\nexport class CloudLockedError extends Error {\n constructor() {\n super('Unlock actions with the mutation token first.');\n this.name = 'CloudLockedError';\n }\n}\n\n/**\n * The server answered a launch with `ok: false` — a budget gate, a missing\n * seat, a CLI that printed no session. `message` is the server's own\n * sentence; `failure` the machine code for tests and chips.\n */\nexport class CloudLaunchRefusedError extends Error {\n constructor(message: string, public readonly failure: CloudLaunchFailureCode | null) {\n super(message);\n this.name = 'CloudLaunchRefusedError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Just enough structure that no render can crash on `undefined.map` or read\n * a budget field that is not there. Field-level honesty (null = unknown) is\n * the model's job.\n */\nexport function narrowCloudOverview(raw: unknown): CloudOverviewResponse | null {\n if (!isRecord(raw)) return null;\n const { seat, budget, tasks, backlog } = raw;\n if (!isRecord(seat) || typeof seat['ready'] !== 'boolean') return null;\n if (!isRecord(budget) || !isRecord(budget['budget']) || !isRecord(budget['canLaunch']) || !isRecord(budget['canSelfImprove'])) return null;\n for (const key of ['creditsTotalUsd', 'estimatedSpentUsd', 'estimatedRemainingUsd', 'sessionsToday', 'selfImproveToday', 'running']) {\n if (typeof budget[key] !== 'number' || !Number.isFinite(budget[key])) return null;\n }\n if (!Array.isArray(tasks) || !tasks.every((t) => isRecord(t) && typeof t['id'] === 'string' && typeof t['state'] === 'string')) return null;\n if (!isRecord(backlog) || !Array.isArray(backlog['items'])) return null;\n return raw as unknown as CloudOverviewResponse;\n}\n\n/**\n * The 404 sentence. Exported so Command's CloudCard can recognise a server\n * without the lane and render nothing at all, rather than spending a\n * full-width row to say so; launch buttons elsewhere still use it as their\n * disabled tooltip.\n */\nexport const CLOUD_NOT_IN_BUILD = 'The cloud lane is not in this build yet.';\n\n/** Operator words for a cloud read that did not answer — never a path, never a trace. */\nfunction absence(err: unknown): string {\n if (err instanceof ApiError) {\n if (err.status === 404) return CLOUD_NOT_IN_BUILD;\n if (err.status === 503) return 'The cloud lane failed to load on the server.';\n return `The cloud lane answered HTTP ${err.status}.`;\n }\n return 'The cloud lane could not be reached.';\n}\n\nexport const cloudQuery: QueryDef<OptionalRead<CloudOverviewResponse>> = {\n key: CLOUD_KEY,\n fetch: async (signal) => {\n try {\n const value = narrowCloudOverview(await apiGet<unknown>(VERSE_CLOUD_PATH, signal));\n if (value === null) {\n // Nothing is shown rather than guessed from an unknown shape.\n return { value: null, available: true, reason: 'Unrecognized response — update Ashlr.' };\n }\n return { value, available: true, reason: null };\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) throw err;\n if (err instanceof DOMException && err.name === 'AbortError') throw err;\n return { value: null, available: false, reason: absence(err) };\n }\n },\n};\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new CloudLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\nfunction settled(): void {\n invalidate(CLOUD_KEY);\n void refreshActivity();\n}\n\n/**\n * Launch one cloud session. Resolves only on `ok: true` with a task; a\n * refusal the server words in a 200 body is thrown as CloudLaunchRefusedError\n * so every caller shows it the same way as a 4xx refusal (whose sentence is\n * `ApiError.detail`). Invalidates either way — a failed launch is still a\n * recorded task.\n */\nexport async function launchCloudTask(request: CloudLaunchRequest): Promise<CloudLaunchResponse & { task: NonNullable<CloudLaunchResponse['task']> }> {\n try {\n const response = await post<CloudLaunchResponse>(VERSE_CLOUD_LAUNCH_PATH, request);\n if (!isRecord(response) || response.ok !== true || !isRecord(response.task)) {\n const error = isRecord(response) && typeof response.error === 'string' && response.error ? response.error : 'The cloud session was not started, and the server sent no reason.';\n const failure = isRecord(response) && typeof response.failure === 'string' ? response.failure : null;\n throw new CloudLaunchRefusedError(error, failure);\n }\n return response as CloudLaunchResponse & { task: NonNullable<CloudLaunchResponse['task']> };\n } finally {\n settled();\n }\n}\n\n/** One budget change (any subset; the server validates and clamps). */\nexport async function updateCloudBudget(update: CloudBudgetUpdate): Promise<unknown> {\n const result = await post<unknown>(VERSE_CLOUD_BUDGET_PATH, update);\n invalidate(CLOUD_KEY);\n return result;\n}\n\n/** Launch up to `count` backlog items now (the \"Improve Verse\" button). */\nexport async function runCloudImprove(request: CloudImproveRequest = { count: 1 }): Promise<CloudImproveResponse> {\n try {\n const result = await post<unknown>(VERSE_CLOUD_IMPROVE_PATH, request);\n const launched = isRecord(result) && Array.isArray(result['launched']) ? (result['launched'] as CloudImproveResponse['launched']) : [];\n const skipped = isRecord(result) && Array.isArray(result['skipped']) ? (result['skipped'] as CloudImproveResponse['skipped']) : [];\n return { launched, skipped };\n } finally {\n settled();\n }\n}\n\n/** Ask the server to re-read every open task from GitHub now. */\nexport async function refreshCloudTasks(): Promise<unknown> {\n try {\n return await post<unknown>(VERSE_CLOUD_REFRESH_PATH, {});\n } finally {\n settled();\n }\n}\n\n/**\n * Stop tracking one task (it becomes `closed`, \"Dismissed in Verse.\"). The id\n * is re-checked against the contract's pattern: it came from the server, but\n * it is spliced into a path the mutation token is sent to.\n */\nexport async function dismissCloudTask(taskId: string): Promise<unknown> {\n if (!CLOUD_TASK_ID_PATTERN.test(taskId)) throw new Error('That task id is not one Verse issued, so nothing was sent.');\n try {\n return await post<unknown>(`${VERSE_CLOUD_TASKS_PATH}/${taskId}/dismiss`, {});\n } finally {\n settled();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={body:`_body_1kus1_10`,actionsRow:`_actionsRow_1kus1_17`,headActions:`_headActions_1kus1_24`,meterBlock:`_meterBlock_1kus1_31`,estimateNote:`_estimateNote_1kus1_37`,muted:`_muted_1kus1_38`,facts:`_facts_1kus1_46`,link:`_link_1kus1_54`,linkButton:`_linkButton_1kus1_67`,toggleRow:`_toggleRow_1kus1_94`,toggleText:`_toggleText_1kus1_101`,tasks:`_tasks_1kus1_108`,task:`_task_1kus1_108`,taskChip:`_taskChip_1kus1_128`,taskMain:`_taskMain_1kus1_132`,taskTitle:`_taskTitle_1kus1_139`,taskLinks:`_taskLinks_1kus1_148`,notice:`_notice_1kus1_157`,noticeRow:`_noticeRow_1kus1_187`,popover:`_popover_1kus1_197`,popoverTitle:`_popoverTitle_1kus1_212`,form:`_form_1kus1_222`,fieldGrid:`_fieldGrid_1kus1_228`,textareaField:`_textareaField_1kus1_234`,label:`_label_1kus1_240`,textarea:`_textarea_1kus1_234`,fieldError:`_fieldError_1kus1_269`,formActions:`_formActions_1kus1_276`,dialogWidth:`_dialogWidth_1kus1_283`,laneChip:`_laneChip_1kus1_289`,sheetAction:`_sheetAction_1kus1_295`,standing:`_standing_1kus1_304`};export{e as t};
|
|
2
|
+
//# sourceMappingURL=cloud.module-DOP-N4Ch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.module-DOP-N4Ch.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/cloud/cloud.module.css"],"sourcesContent":["/*\n * routes/verse/cloud/cloud.module.css — the cloud lane's card, dialog,\n * popover, Usage panel, Fleet chip and composer action (3.11 unit C3).\n * Tokens only (style-scan-310): no raw hex colour, no px font size, and\n * every colour follows the theme. Nothing is ellipsized: long titles and\n * reasons wrap at word boundaries (only an unbreakable run, like a URL, may\n * break inside itself).\n */\n\n.body {\n display: flex;\n flex-direction: column;\n gap: var(--space-3);\n min-width: 0;\n}\n\n.actionsRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n}\n\n.headActions {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n}\n\n.meterBlock {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n}\n\n.estimateNote,\n.muted {\n margin: 0;\n color: var(--text-tertiary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.facts {\n margin: 0;\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.link {\n color: var(--text-link);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n text-decoration: none;\n white-space: nowrap;\n}\n\n.link:hover {\n text-decoration: underline;\n}\n\n.link:focus-visible,\n.linkButton:focus-visible {\n outline: 2px solid var(--border-focus);\n outline-offset: 2px;\n}\n\n.linkButton {\n padding: 0;\n border: 0;\n background: none;\n color: var(--text-link);\n font: inherit;\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.linkButton:hover {\n text-decoration: underline;\n}\n\n.linkButton:disabled {\n color: var(--text-disabled);\n cursor: default;\n text-decoration: none;\n}\n\n.toggleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2) var(--space-3);\n}\n\n.toggleText {\n flex: 1 1 16rem;\n min-width: 0;\n}\n\n/* ---- tasks ------------------------------------------------------------ */\n\n.tasks {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.task {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: var(--space-1) var(--space-3);\n padding: var(--space-2) 0;\n border-top: 1px solid var(--border-subtle);\n}\n\n.task:first-child {\n border-top: 0;\n}\n\n.taskChip {\n padding-top: 1px;\n}\n\n.taskMain {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n min-width: 0;\n}\n\n.taskTitle {\n margin: 0;\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n font-weight: var(--font-weight-medium);\n overflow-wrap: break-word;\n}\n\n.taskLinks {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-1) var(--space-3);\n}\n\n/* ---- notices (the NoticeSlot tones, inside a card) --------------------- */\n\n.notice {\n margin: 0;\n padding: var(--space-2) var(--space-3);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-md);\n background: var(--bg-surface-raised);\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.notice[data-tone='warning'] {\n border-color: var(--status-warning-solid);\n background: var(--status-warning-bg);\n color: var(--status-warning-fg);\n}\n\n.notice[data-tone='danger'] {\n border-color: var(--status-danger-solid);\n background: var(--status-danger-bg);\n color: var(--status-danger-fg);\n}\n\n.notice[data-tone='success'] {\n border-color: var(--status-success-solid);\n background: var(--status-success-bg);\n color: var(--status-success-fg);\n}\n\n.noticeRow {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-1) var(--space-3);\n}\n\n/* ---- the Edit budget popover ------------------------------------------ */\n\n.popover {\n position: absolute;\n top: calc(100% + var(--space-2));\n right: 0;\n z-index: var(--z-dropdown);\n width: min(26rem, calc(100vw - var(--space-8)));\n max-height: min(34rem, 70vh);\n overflow-y: auto;\n padding: var(--space-4);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-lg);\n background: var(--bg-surface-raised);\n box-shadow: var(--shadow-menu);\n}\n\n.popoverTitle {\n margin: 0 0 var(--space-3);\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n font-weight: var(--font-weight-semibold);\n}\n\n/* ---- forms (launch dialog, budget) ------------------------------------ */\n\n.form {\n display: flex;\n flex-direction: column;\n gap: var(--space-3);\n}\n\n.fieldGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));\n gap: var(--space-3);\n}\n\n.textareaField {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n}\n\n.label {\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n font-weight: var(--font-weight-medium);\n}\n\n.textarea {\n min-height: 8rem;\n padding: var(--space-2) var(--space-3);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-md);\n background: var(--bg-input);\n color: var(--text-primary);\n font: inherit;\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n resize: vertical;\n}\n\n.textarea:focus-visible {\n outline: 2px solid var(--border-focus);\n outline-offset: 0;\n}\n\n.textarea[aria-invalid='true'] {\n border-color: var(--status-danger-solid);\n}\n\n.fieldError {\n margin: 0;\n color: var(--status-danger-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n\n.formActions {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: var(--space-2);\n}\n\n.dialogWidth {\n width: min(34rem, calc(100vw - var(--space-8)));\n}\n\n/* ---- the Fleet chip ---------------------------------------------------- */\n\n.laneChip[data-active='true'] {\n border-color: var(--status-running-solid);\n}\n\n/* ---- the composer's \"Run in cloud\" ------------------------------------- */\n\n.sheetAction {\n display: flex;\n flex-direction: column;\n gap: var(--space-2);\n padding-top: var(--space-3);\n border-top: 1px solid var(--border-subtle);\n}\n\n/* The not-set-up standing: a state word and the credit estimate beside it, no meter. */\n.standing {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-2);\n margin: 0;\n min-width: 0;\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{s as e}from"./index-BG177cPN.js";import{Cn as t,Sn as n,_n as r,bn as i,cn as a,dn as o,fn as s,gn as c,hn as l,ln as u,mn as d,pn as f,un as p,vn as m,xn as h,yn as g}from"./VerseConsoleApp-DeuBEvAO.js";var _=e({AUTONOMY_SETUP_COMMAND:()=>w,COMMAND_KEYS:()=>a,DESKTOP_COMMANDS:()=>u,OPEN_SESSION_COMMAND_RE:()=>p,PALETTE_GROUPS:()=>v,PALETTE_OPEN_BUDGET_MS:()=>50,PALETTE_PREFIXES:()=>y,PALETTE_RECENT_LIMIT:()=>5,SHORTCUT_SECTIONS:()=>b,WORKBENCH_COMMANDS:()=>k,WORKBENCH_COMMAND_EVENT:()=>o,chordMatches:()=>s,commandCatalogLoaded:()=>f,commandChord:()=>d,detectKeyPlatform:()=>l,eventKeyName:()=>c,findCommand:()=>j,formatChord:()=>r,keyBinding:()=>m,loadedCommand:()=>g,matchCommand:()=>P,matchKey:()=>i,paletteCommands:()=>M,parseChord:()=>h,parseDesktopCommand:()=>n,registerCommandCatalog:()=>t,shortcutSections:()=>N}),v=[{id:`needs-you`,label:`Needs you`},{id:`chats`,label:`Chats`},{id:`actions`,label:`Actions`},{id:`go-to`,label:`Go to`},{id:`seats-apps`,label:`Seats & Apps`},{id:`projects`,label:`Projects`}],y={">":`actions`,"#":`chats`},b=[`Surfaces`,`Navigation`,`Chat`,`Dock`,`Composer`,`Needs you`,`Charts`,`App`];function x(e){let t=a[e];return t.native?{id:e,scope:t.scope,keys:t.keys,native:t.native}:{id:e,scope:t.scope,keys:t.keys}}var S={confirm:{title:`Stop every running chat?`,body:`Each running turn is cancelled. Queued follow-ups are held, not sent.`,confirmLabel:`Stop chats`,destructive:!0},token:!0},C={confirm:{title:`Stop the fleet?`,body:`Autonomous work halts within one tick and stays stopped until you resume it. Chats keep running.`,confirmLabel:`Stop fleet`,destructive:!0},token:!0},w=`ashlr authority setup`,T=(e,t,n,r)=>({id:e,title:t,scope:`global`,keys:[],group:`actions`,section:`App`,keywords:n,...r?{surface:r}:{}}),E=(e,t,n)=>T(`autonomy.${e}`,`Autonomy: ${t}`,[`autonomy`,`switch`,...n],`command`),D=(e,t,n)=>T(`budget.${e}`,`Budget mode: ${t}`,[`budget`,`spend`,...n],`command`),O=(e,t)=>({...x(e),title:`Go to ${t}`,group:`go-to`,section:`Surfaces`,keywords:[t.toLowerCase()]}),k=[O(`surface.command`,`Command`),O(`surface.fleet`,`Fleet`),O(`surface.growth`,`Growth`),O(`surface.mind`,`Mind`),O(`surface.chat`,`Chat`),{...x(`section.settings`),title:`Open Settings`,group:`go-to`,section:`Surfaces`,keywords:[`preferences`,`appearance`]},{id:`section.apps`,title:`Open Apps & Accounts`,scope:`global`,keys:[],group:`go-to`,section:`Surfaces`,keywords:[`mcp`,`accounts`,`seats`,`integrations`,`ollama`]},{id:`section.usage`,title:`Open Usage`,scope:`global`,keys:[],group:`go-to`,section:`Surfaces`,keywords:[`capacity`,`spend`,`limits`]},{...x(`palette.open`),title:`Command palette`,group:null,section:`Navigation`},{...x(`needs-you.open`),title:`Open Needs you`,group:`actions`,section:`Navigation`,keywords:[`approvals`,`inbox`,`triage`]},{...x(`shortcuts.open`),title:`Keyboard shortcuts`,group:`actions`,section:`Navigation`,keywords:[`keys`,`help`]},{...x(`resources.toggle`),title:`Open Resources`,group:`actions`,section:`Navigation`,note:`stops the running turn instead while one runs`,keywords:[`show resources`,`accounts`,`seats`,`capacity`,`credits`,`cloud`,`local models`]},{id:`resources.bar.toggle`,title:`Show or hide the resource bar`,scope:`global`,keys:[],group:`actions`,section:`Navigation`,keywords:[`battery`,`usage bars`,`accounts`,`capacity`,`rail`,`resources`]},{...x(`history.back`),title:`Back`,group:null,section:`Navigation`,note:`through surfaces and chats`},{...x(`history.forward`),title:`Forward`,group:null,section:`Navigation`,note:`through surfaces and chats`},{...x(`chat.recent-next`),title:`Next recent chat`,group:null,section:`Navigation`},{...x(`chat.recent-prev`),title:`Previous recent chat`,group:null,section:`Navigation`},{...x(`rail.toggle-labels`),title:`Show or hide rail labels`,group:`actions`,section:`Navigation`},{...x(`chat.new`),title:`New chat`,group:`actions`,section:`Chat`},{id:`chat.new-on`,title:`New chat on…`,scope:`global`,keys:[],group:`actions`,section:`Chat`,argument:{kind:`seat`,prompt:`Seat`},keywords:[`seat`,`account`,`engine`]},{...x(`chat.sidebar`),title:`Show or hide the chat list`,group:`actions`,section:`Chat`},{...x(`chat.find`),title:`Find in chat`,group:`actions`,section:`Chat`,keywords:[`search`]},{...x(`chat.turn-prev`),title:`Previous turn`,group:null,section:`Chat`},{...x(`chat.turn-next`),title:`Next turn`,group:null,section:`Chat`},{id:`chats.stop-all`,title:`Stop running chats…`,scope:`global`,keys:[],group:`actions`,section:`Chat`,guard:S,keywords:[`cancel`,`halt`]},{id:`composer.cloud`,title:`Run in cloud…`,scope:`chat`,keys:[],group:`actions`,section:`Composer`,keywords:[`cloud`,`remote`,`launch`,`draft pr`]},{id:`git.create-pr`,title:`Create pull request…`,scope:`chat`,keys:[],group:`actions`,section:`Chat`,keywords:[`pr`,`github`,`git`,`open pull request`]},{id:`git.merge-pr`,title:`Merge pull request…`,scope:`chat`,keys:[],group:`actions`,section:`Chat`,keywords:[`pr`,`github`,`git`,`land`,`ship`]},{...x(`dock.toggle`),title:`Show or hide the dock`,group:`actions`,section:`Dock`},{...x(`dock.terminal`),title:`Terminal`,group:`actions`,section:`Dock`,keywords:[`shell`,`console`]},{...x(`dock.terminal-new`),title:`New terminal tab`,group:`actions`,section:`Dock`},{...x(`dock.preview`),title:`Preview`,group:`actions`,section:`Dock`,keywords:[`browser`,`dev server`]},{...x(`dock.diff`),title:`Review changes`,group:`actions`,section:`Dock`,keywords:[`diff`,`git`]},{...x(`composer.permission`),title:`Permission mode…`,group:`actions`,section:`Composer`,keywords:[`plan`,`bypass`,`auto`]},{...x(`composer.model`),title:`Model…`,group:`actions`,section:`Composer`},{...x(`composer.effort`),title:`Effort…`,group:`actions`,section:`Composer`,keywords:[`reasoning`,`thinking`]},{...x(`composer.attach`),title:`Attach files…`,group:`actions`,section:`Composer`,keywords:[`upload`,`image`]},{...x(`composer.send`),title:`Send`,group:null,section:`Composer`,note:`queues while a turn runs (up to 3)`},{...x(`composer.stop-and-send`),title:`Stop and send`,group:null,section:`Composer`},{...x(`composer.stop`),title:`Stop the running turn`,group:null,section:`Composer`,note:`only from an empty composer with no overlay open`},{...x(`drawer.next`),title:`Next item`,group:null,section:`Needs you`},{...x(`drawer.prev`),title:`Previous item`,group:null,section:`Needs you`},{...x(`drawer.open`),title:`Open item`,group:null,section:`Needs you`},{...x(`drawer.approve`),title:`Approve`,group:null,section:`Needs you`,note:`confirms, then asks for the token`},{...x(`drawer.reject`),title:`Reject`,group:null,section:`Needs you`,note:`confirms, then asks for the token`},{...x(`drawer.veto`),title:`Veto`,group:null,section:`Needs you`,note:`confirms, then asks for the token`},{...x(`drawer.done`),title:`Mark done`,group:null,section:`Needs you`},{...x(`drawer.split-prev`),title:`Previous split`,group:null,section:`Needs you`},{...x(`drawer.split-next`),title:`Next split`,group:null,section:`Needs you`},{...x(`chart.table`),title:`Show as a table`,group:null,section:`Charts`,note:`when a chart card has focus`},{...x(`appearance.toggle-theme`),title:`Toggle light / dark`,group:`actions`,section:`App`,keywords:[`theme`,`dark mode`]},{...x(`app.summon`),title:`Show Verse and focus the composer`,group:null,section:`App`,note:`system-wide; turn on in Settings ▸ Desktop`},{id:`fleet.stop`,title:`Stop the fleet…`,scope:`global`,keys:[],group:`actions`,section:`App`,guard:C,keywords:[`kill`,`halt`,`autonomy`]},E(`off`,`Off`,[`disable`,`lower`]),E(`propose`,`Propose`,[`proposals`,`review`]),E(`autonomous`,`Autonomous`,[`auto`,`turn on`,`enable`]),T(`autonomy.grant`,`Approve grant…`,[`touch id`,`re-approve`,`renew`,`authority`,`autonomy`],`command`),D(`all-in`,`All-in`,[`all in`,`max`]),D(`balanced`,`Balanced`,[`default`]),D(`reserve`,`Reserve`,[`save`,`conserve`]),T(`autonomy.copy-setup`,`Copy autonomy setup command`,[w,`autonomy`,`turn on`,`clipboard`])],A=new Map(k.map(e=>[e.id,e]));function j(e){return A.get(e)??null}t(j);function M(e){return k.filter(t=>t.group===e)}function N(){let e=k.filter(e=>e.keys.length>0);return b.map(t=>({section:t,commands:e.filter(e=>e.section===t)})).filter(e=>e.commands.length>0)}function P(e,t,n=l()){let r=i(e,t,n);return r?j(r.id):null}export{j as a,N as c,_ as i,v as n,P as o,y as r,M as s,w as t};
|
|
2
|
+
//# sourceMappingURL=command-catalog-CmrHNFzh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-catalog-CmrHNFzh.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/command-catalog.ts"],"sourcesContent":["/**\n * routes/verse/shell/command-catalog.ts — every workbench command, in ONE\n * table (unit C0; SPEC-310C §1).\n *\n * The ⌘K palette (C1), the shortcuts overlay (⌘/), the drawer and every\n * surface's buttons read this table; the key test reads it too, which is how\n * \"no two commands share a key\" and \"no key the native menu already owns\"\n * stay true as units add commands.\n *\n * KEYS are written in ./command-keys.ts, the half of the catalog the chat\n * first paint needs (a key press, a shortcut hint, the desktop menu bridge);\n * a row with keys takes them from there with `bind(id)`, so each key is\n * still written once. This module — titles, palette groups, keywords,\n * guards, the keyless palette actions — loads with the palette's chunk, not\n * with first paint, and re-exports ./command-keys.ts so its importers keep\n * one import site.\n *\n * Pure data plus pure helpers — no React, no DOM — so the palette can import\n * it without pulling a surface into its chunk.\n *\n * Adding a command: one entry below (a change request to C0 when another\n * unit needs it), plus its keys in command-keys.ts if it has any. The\n * handler lives with the unit that owns the behaviour and dispatches on the\n * command `id`.\n */\nimport {\n COMMAND_KEYS,\n matchKey,\n detectKeyPlatform,\n registerCommandCatalog,\n type CommandScope,\n type KeyChord,\n type KeyedCommandId,\n type KeyEventLike,\n type KeyPlatform,\n type NativeBinding,\n} from './command-keys.js';\n\nexport * from './command-keys.js';\n\n// ===========================================================================\n// The palette\n// ===========================================================================\n\n/** Result groups, in the order the palette shows them (SPEC-310C §1). */\nexport const PALETTE_GROUPS = [\n { id: 'needs-you', label: 'Needs you' },\n { id: 'chats', label: 'Chats' },\n { id: 'actions', label: 'Actions' },\n { id: 'go-to', label: 'Go to' },\n { id: 'seats-apps', label: 'Seats & Apps' },\n { id: 'projects', label: 'Projects' },\n] as const;\nexport type PaletteGroupId = (typeof PALETTE_GROUPS)[number]['id'];\n\n/** A leading `>` limits results to actions; `#` to chats. */\nexport const PALETTE_PREFIXES: Readonly<Record<'>' | '#', PaletteGroupId>> = { '>': 'actions', '#': 'chats' };\n\n/** An empty query shows the last this-many actions run. */\nexport const PALETTE_RECENT_LIMIT = 5;\n\n/** The palette opens in under this (it lives in the shell chunk). */\nexport const PALETTE_OPEN_BUDGET_MS = 50;\n\n// ===========================================================================\n// Commands\n// ===========================================================================\n\nexport type CommandArgumentKind = 'seat' | 'project' | 'section' | 'session';\n\n/**\n * A guarded command asks for confirmation FIRST, then the mutation token\n * (the palette must never be a shortcut around the confirm step its regular\n * UI requires).\n */\nexport interface CommandGuard {\n confirm: { title: string; body: string; confirmLabel: string; destructive: boolean };\n token: boolean;\n}\n\n/** Sections of the shortcuts overlay (⌘/), in display order. */\nexport const SHORTCUT_SECTIONS = ['Surfaces', 'Navigation', 'Chat', 'Dock', 'Composer', 'Needs you', 'Charts', 'App'] as const;\nexport type ShortcutSection = (typeof SHORTCUT_SECTIONS)[number];\n\nexport interface WorkbenchCommand {\n id: string;\n /** Palette / overlay text: a verb phrase, operator language. */\n title: string;\n scope: CommandScope;\n /** Empty = reachable from the palette (or a button) only. */\n keys: readonly KeyChord[];\n /** Where the palette lists it; null = not a palette entry (drawer and chart keys). */\n group: 'actions' | 'go-to' | null;\n section: ShortcutSection;\n keywords?: readonly string[];\n /** Tab fills this argument (\"New chat on…\" → a seat). */\n argument?: { kind: CommandArgumentKind; prompt: string };\n guard?: CommandGuard;\n native?: NativeBinding;\n /** A qualifier the overlay prints under the title. */\n note?: string;\n /**\n * The surface that serves it. The command's behaviour — and the dialogs it\n * may open (the Touch ID sheet, the token prompt) — live on that surface,\n * so the shell brings it forward and parks the command until the surface\n * registers its handler (run-command.ts). The palette says where it runs.\n */\n surface?: 'command';\n}\n\n/** A keyed row's id, scope, keys and native binding — from command-keys.ts, never re-declared here. */\nfunction bind<I extends KeyedCommandId>(id: I): { id: I; scope: CommandScope; keys: readonly KeyChord[]; native?: NativeBinding } {\n const spec: { scope: CommandScope; keys: readonly KeyChord[]; native?: NativeBinding } = COMMAND_KEYS[id];\n return spec.native ? { id, scope: spec.scope, keys: spec.keys, native: spec.native } : { id, scope: spec.scope, keys: spec.keys };\n}\n\nconst STOP_CHATS_GUARD: CommandGuard = {\n confirm: {\n title: 'Stop every running chat?',\n body: 'Each running turn is cancelled. Queued follow-ups are held, not sent.',\n confirmLabel: 'Stop chats',\n destructive: true,\n },\n token: true,\n};\n\nconst STOP_FLEET_GUARD: CommandGuard = {\n confirm: {\n title: 'Stop the fleet?',\n body: 'Autonomous work halts within one tick and stays stopped until you resume it. Chats keep running.',\n confirmLabel: 'Stop fleet',\n destructive: true,\n },\n token: true,\n};\n\n/** Where the copy-setup command points: the one CLI step that turns autonomy on. */\nexport const AUTONOMY_SETUP_COMMAND = 'ashlr authority setup';\n\n/**\n * A palette-only App action (no key) — shared shape of the autonomy, grant,\n * budget and setup entries below.\n */\nconst appAction = (id: string, title: string, keywords: readonly string[], surface?: 'command'): WorkbenchCommand => ({\n id,\n title,\n scope: 'global',\n keys: [],\n group: 'actions',\n section: 'App',\n keywords,\n ...(surface ? { surface } : {}),\n});\n\n/**\n * The Command surface's autonomy switch, one palette entry per position.\n * Served by AutonomyBar with the switch's own rules: lowering is instant,\n * raising past the installed grant opens the Touch ID sheet instead.\n */\nconst autonomySwitch = (to: 'off' | 'propose' | 'autonomous', label: string, keywords: readonly string[]) =>\n appAction(`autonomy.${to}`, `Autonomy: ${label}`, ['autonomy', 'switch', ...keywords], 'command');\n\n/** A9's budget mode, set from the palette — clamped to the grant's ceiling like the Budget pill. */\nconst budgetMode = (mode: 'all-in' | 'balanced' | 'reserve', label: string, keywords: readonly string[]) =>\n appAction(`budget.${mode}`, `Budget mode: ${label}`, ['budget', 'spend', ...keywords], 'command');\n\nconst surface = <I extends KeyedCommandId>(id: I, name: string) => ({\n ...bind(id),\n title: `Go to ${name}`,\n group: 'go-to',\n section: 'Surfaces',\n keywords: [name.toLowerCase()],\n} as const);\n\nexport const WORKBENCH_COMMANDS = [\n // ── Surfaces (⌘1 Command … ⌘5 Chat) and the gear tray ────────────────────\n surface('surface.command', 'Command'),\n surface('surface.fleet', 'Fleet'),\n surface('surface.growth', 'Growth'),\n surface('surface.mind', 'Mind'),\n surface('surface.chat', 'Chat'),\n { ...bind('section.settings'), title: 'Open Settings', group: 'go-to', section: 'Surfaces', keywords: ['preferences', 'appearance'] },\n { id: 'section.apps', title: 'Open Apps & Accounts', scope: 'global', keys: [], group: 'go-to', section: 'Surfaces', keywords: ['mcp', 'accounts', 'seats', 'integrations', 'ollama'] },\n { id: 'section.usage', title: 'Open Usage', scope: 'global', keys: [], group: 'go-to', section: 'Surfaces', keywords: ['capacity', 'spend', 'limits'] },\n\n // ── Navigation ───────────────────────────────────────────────────────────\n { ...bind('palette.open'), title: 'Command palette', group: null, section: 'Navigation' },\n { ...bind('needs-you.open'), title: 'Open Needs you', group: 'actions', section: 'Navigation', keywords: ['approvals', 'inbox', 'triage'] },\n { ...bind('shortcuts.open'), title: 'Keyboard shortcuts', group: 'actions', section: 'Navigation', keywords: ['keys', 'help'] },\n // The Resources drawer (3.11 C6): the key toggles it, the palette opens it.\n {\n ...bind('resources.toggle'),\n title: 'Open Resources',\n group: 'actions',\n section: 'Navigation',\n // The composer claims ⌘. to stop a running turn (Composer.tsx) — say so\n // where the key is listed, so the same chord is never a surprise.\n note: 'stops the running turn instead while one runs',\n keywords: ['show resources', 'accounts', 'seats', 'capacity', 'credits', 'cloud', 'local models'],\n },\n {\n id: 'resources.bar.toggle',\n title: 'Show or hide the resource bar',\n scope: 'global',\n keys: [],\n group: 'actions',\n section: 'Navigation',\n keywords: ['battery', 'usage bars', 'accounts', 'capacity', 'rail', 'resources'],\n },\n { ...bind('history.back'), title: 'Back', group: null, section: 'Navigation', note: 'through surfaces and chats' },\n { ...bind('history.forward'), title: 'Forward', group: null, section: 'Navigation', note: 'through surfaces and chats' },\n { ...bind('chat.recent-next'), title: 'Next recent chat', group: null, section: 'Navigation' },\n { ...bind('chat.recent-prev'), title: 'Previous recent chat', group: null, section: 'Navigation' },\n { ...bind('rail.toggle-labels'), title: 'Show or hide rail labels', group: 'actions', section: 'Navigation' },\n\n // ── Chats ────────────────────────────────────────────────────────────────\n { ...bind('chat.new'), title: 'New chat', group: 'actions', section: 'Chat' },\n {\n id: 'chat.new-on',\n title: 'New chat on…',\n scope: 'global',\n keys: [],\n group: 'actions',\n section: 'Chat',\n argument: { kind: 'seat', prompt: 'Seat' },\n keywords: ['seat', 'account', 'engine'],\n },\n { ...bind('chat.sidebar'), title: 'Show or hide the chat list', group: 'actions', section: 'Chat' },\n { ...bind('chat.find'), title: 'Find in chat', group: 'actions', section: 'Chat', keywords: ['search'] },\n { ...bind('chat.turn-prev'), title: 'Previous turn', group: null, section: 'Chat' },\n { ...bind('chat.turn-next'), title: 'Next turn', group: null, section: 'Chat' },\n { id: 'chats.stop-all', title: 'Stop running chats…', scope: 'global', keys: [], group: 'actions', section: 'Chat', guard: STOP_CHATS_GUARD, keywords: ['cancel', 'halt'] },\n // ── Git (C5's BranchBar serves these) ─────────────────────────────────────\n // Palette-only and unguarded ON PURPOSE: each OPENS BranchBar's own dialog\n // (CreatePrDialog / MergeDialog), which carries the before-click disclosure,\n // and every write inside it still goes through the mutation-token gate —\n // so the palette is never a shortcut around a confirm step. No key: a\n // merge is never one keystroke away. Chat-scoped because the bar lives\n // above the composer; from another surface the shell switches to Chat and\n // parks the command until BranchBar registers (command-bus).\n // ── Cloud (3.11 C3) ──────────────────────────────────────────────────────\n // Opens the composer's Chat settings sheet, where \"Run in cloud\" lives with\n // its own disabled reasons (no GitHub origin, seat not ready, budget gate,\n // empty box) and the token gate — the palette never launches by itself.\n { id: 'composer.cloud', title: 'Run in cloud…', scope: 'chat', keys: [], group: 'actions', section: 'Composer', keywords: ['cloud', 'remote', 'launch', 'draft pr'] },\n { id: 'git.create-pr', title: 'Create pull request…', scope: 'chat', keys: [], group: 'actions', section: 'Chat', keywords: ['pr', 'github', 'git', 'open pull request'] },\n { id: 'git.merge-pr', title: 'Merge pull request…', scope: 'chat', keys: [], group: 'actions', section: 'Chat', keywords: ['pr', 'github', 'git', 'land', 'ship'] },\n\n // ── Dock (C2 container; panes from dock-catalog.ts) ───────────────────────\n { ...bind('dock.toggle'), title: 'Show or hide the dock', group: 'actions', section: 'Dock' },\n { ...bind('dock.terminal'), title: 'Terminal', group: 'actions', section: 'Dock', keywords: ['shell', 'console'] },\n { ...bind('dock.terminal-new'), title: 'New terminal tab', group: 'actions', section: 'Dock' },\n { ...bind('dock.preview'), title: 'Preview', group: 'actions', section: 'Dock', keywords: ['browser', 'dev server'] },\n { ...bind('dock.diff'), title: 'Review changes', group: 'actions', section: 'Dock', keywords: ['diff', 'git'] },\n\n // ── Composer ─────────────────────────────────────────────────────────────\n { ...bind('composer.permission'), title: 'Permission mode…', group: 'actions', section: 'Composer', keywords: ['plan', 'bypass', 'auto'] },\n { ...bind('composer.model'), title: 'Model…', group: 'actions', section: 'Composer' },\n { ...bind('composer.effort'), title: 'Effort…', group: 'actions', section: 'Composer', keywords: ['reasoning', 'thinking'] },\n { ...bind('composer.attach'), title: 'Attach files…', group: 'actions', section: 'Composer', keywords: ['upload', 'image'] },\n { ...bind('composer.send'), title: 'Send', group: null, section: 'Composer', note: 'queues while a turn runs (up to 3)' },\n { ...bind('composer.stop-and-send'), title: 'Stop and send', group: null, section: 'Composer' },\n { ...bind('composer.stop'), title: 'Stop the running turn', group: null, section: 'Composer', note: 'only from an empty composer with no overlay open' },\n\n // ── Needs-you drawer ─────────────────────────────────────────────────────\n { ...bind('drawer.next'), title: 'Next item', group: null, section: 'Needs you' },\n { ...bind('drawer.prev'), title: 'Previous item', group: null, section: 'Needs you' },\n { ...bind('drawer.open'), title: 'Open item', group: null, section: 'Needs you' },\n { ...bind('drawer.approve'), title: 'Approve', group: null, section: 'Needs you', note: 'confirms, then asks for the token' },\n { ...bind('drawer.reject'), title: 'Reject', group: null, section: 'Needs you', note: 'confirms, then asks for the token' },\n { ...bind('drawer.veto'), title: 'Veto', group: null, section: 'Needs you', note: 'confirms, then asks for the token' },\n { ...bind('drawer.done'), title: 'Mark done', group: null, section: 'Needs you' },\n { ...bind('drawer.split-prev'), title: 'Previous split', group: null, section: 'Needs you' },\n { ...bind('drawer.split-next'), title: 'Next split', group: null, section: 'Needs you' },\n\n // ── Charts ───────────────────────────────────────────────────────────────\n { ...bind('chart.table'), title: 'Show as a table', group: null, section: 'Charts', note: 'when a chart card has focus' },\n\n // ── App ──────────────────────────────────────────────────────────────────\n { ...bind('appearance.toggle-theme'), title: 'Toggle light / dark', group: 'actions', section: 'App', keywords: ['theme', 'dark mode'] },\n { ...bind('app.summon'), title: 'Show Verse and focus the composer', group: null, section: 'App', note: 'system-wide; turn on in Settings ▸ Desktop' },\n { id: 'fleet.stop', title: 'Stop the fleet…', scope: 'global', keys: [], group: 'actions', section: 'App', guard: STOP_FLEET_GUARD, keywords: ['kill', 'halt', 'autonomy'] },\n\n // ── Autonomy (Command's AutonomyBar serves these) ────────────────────────\n // No keys on purpose: raising authority is never one keystroke away, and\n // each entry runs the bar's own path (Touch ID sheet, token, read-only).\n autonomySwitch('off', 'Off', ['disable', 'lower']),\n autonomySwitch('propose', 'Propose', ['proposals', 'review']),\n autonomySwitch('autonomous', 'Autonomous', ['auto', 'turn on', 'enable']),\n appAction('autonomy.grant', 'Approve grant…', ['touch id', 're-approve', 'renew', 'authority', 'autonomy'], 'command'),\n budgetMode('all-in', 'All-in', ['all in', 'max']),\n budgetMode('balanced', 'Balanced', ['default']),\n budgetMode('reserve', 'Reserve', ['save', 'conserve']),\n // Served by the shell (run-command.ts → copy-setup.ts), wherever you are.\n appAction('autonomy.copy-setup', 'Copy autonomy setup command', [AUTONOMY_SETUP_COMMAND, 'autonomy', 'turn on', 'clipboard']),\n] as const satisfies readonly WorkbenchCommand[];\n\nexport type CommandId = (typeof WORKBENCH_COMMANDS)[number]['id'];\n\nconst BY_ID: ReadonlyMap<string, WorkbenchCommand> = new Map(WORKBENCH_COMMANDS.map((c) => [c.id, c as WorkbenchCommand]));\n\nexport function findCommand(id: string): WorkbenchCommand | null {\n return BY_ID.get(id) ?? null;\n}\n\n// run-command.ts resolves keyless ids (palette actions, guards, surfaces) through this.\nregisterCommandCatalog(findCommand);\n\n/** Palette entries of one group, in catalog order. */\nexport function paletteCommands(group: 'actions' | 'go-to'): WorkbenchCommand[] {\n return (WORKBENCH_COMMANDS as readonly WorkbenchCommand[]).filter((c) => c.group === group);\n}\n\n/** The shortcuts overlay: every command with keys, grouped by section, in display order. */\nexport function shortcutSections(): Array<{ section: ShortcutSection; commands: WorkbenchCommand[] }> {\n const withKeys = (WORKBENCH_COMMANDS as readonly WorkbenchCommand[]).filter((c) => c.keys.length > 0);\n return SHORTCUT_SECTIONS.map((section) => ({ section, commands: withKeys.filter((c) => c.section === section) })).filter(\n (s) => s.commands.length > 0,\n );\n}\n\n/**\n * The command a key press runs, given the scopes live right now (innermost\n * first, e.g. ['composer', 'chat', 'global']). Null when nothing is bound —\n * the caller must then leave the event alone. The full catalog entry; first\n * paint matches with command-keys.ts matchKey().\n */\nexport function matchCommand(event: KeyEventLike, liveScopes: readonly CommandScope[], platform: KeyPlatform = detectKeyPlatform()): WorkbenchCommand | null {\n const binding = matchKey(event, liveScopes, platform);\n return binding ? findCommand(binding.id) : null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{B as e,D as t,H as n,I as r,M as i,P as a,R as o,l as s,o as c}from"./verse-model-CSzzdyu2.js";import"./transient-events-nTLhMBND.js";import{_ as l,t as u,y as d}from"./format-DEAaVF6X.js";var f=[`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],p=65536,m=12e3,h=[`This conversation is about to continue in a fresh session that has none of its context.`,`Write the handoff note that new session should start from. Do not edit files or run commands for this — only write the note.`,``,`Cover, concretely:`,`- The goal, and what "done" looks like.`,`- Decisions made so far, each with the reason it was made.`,`- Current state: what is finished, what is half-done, what is untested.`,`- Files changed and anything not yet committed.`,`- How to verify the work (exact commands).`,`- Open questions, risks, and approaches already tried that did not work.`,``,`Keep it under 600 words. Prefer file paths and commands over prose.`].join(`
|
|
2
|
+
`),g=65536,_=139264,v=6144,y=/\s·\spart\s(\d+)$/;function b(e){let t=e.replace(/\s+/g,` `).trim(),n=y.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 x(e,t){if(!e)return null;let n=e.models.find(e=>e.id===t);if(n)return n;let r=a(t);return e.models.find(e=>a(e.id)===r)??null}function S(e,t,n=Date.now()){return e?e.health.state===`unavailable`?`${e.label} is unavailable: ${u(d(e.health.summary??`no reason given`,n))}.`:t?t.unavailableReason?`${t.label} cannot run on ${e.label}: ${u(d(t.unavailableReason,n))}.`:null:`Pick a model on this seat.`:`Pick a seat to continue on.`}function C(e,t){let n=e.find(e=>e.id===t.seatId);if(n&&n.health.state!==`unavailable`){let e=x(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 w(e){let{option:t,target:n,source:r,preferences:o}=e,s=e=>e!==void 0&&i(t,e)!==null;if(n!==null&&n.seatId===r.seatId&&a(n.model)===a(r.model)&&s(r.contextMode??`standard`))return r.contextMode??`standard`;let c=n?o?.seats[n.seatId]?.contextMode:void 0;return s(c)?c:`standard`}function T(e,t){return i(e,t)===null?void 0:t}function E(e,n){let r=i(e,n);if(!r)return null;let a=`${c(r.contextWindow)} window`;return r.autoCompactAt===null?a:`${a} · compacts ≈${t(r.autoCompactAt)}`}function D(e,a,s,c){let u=r(e),d=n(c),f=u+d,p=o(u,a,d),m=`~${t(u)} tokens of handoff + an estimated ~${t(d)} of fixed prompt`,h=i(a,`standard`),g=h?h.autoCompactAt??h.contextWindow:null,_={verdict:p,handoffTokens:u,overheadTokens:d,needTokens:f};switch(p){case`fits`:return{..._,tone:`ok`,text:`Fits: ${m} leaves most of the budget before the first compaction.`};case`tight`:return{..._,tone:`warn`,text:`Tight: ${m} is ${l(f/(g??f)*100)} of where this model compacts — the new chat will compact early.`};case`expansive`:return s===`expansive`?{..._,tone:`warn`,text:`Fits only the expansive budget: ${m}.`}:{..._,tone:`danger`,text:`Too big for the standard budget (${m}). Switch to Expansive, or trim the handoff.`};case`split`:return{..._,tone:`danger`,text:`Too big for one context on this model (${m}). Trim the handoff or pick a seat with a larger window.`};default:return{..._,verdict:null,tone:`unknown`,text:`This model’s window is not known, so there is no fit estimate.`}}}function O(t){return e(t)}function k(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 A=typeof TextEncoder==`function`?new TextEncoder:null;function j(e){if(A)return A.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 M=65536,N=_;function P(e){return j(JSON.stringify(e)??``)}function F(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 I(e,t=Date.now()){if(!e)return null;let n=s(e,t);return n?n===`now`?`just now`:/^\d+[mhd]$/.test(n)?`${n} ago`:`on ${n}`:null}var L=[{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,}/}],R=v,z=`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 ${R/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 B(e){for(let{label:t,re:n}of L)if(n.test(e))return t;return null}var V=`[REDACTED]`;function H(e){return e.split(V).length-1}function U(e){let t=[];for(let n of e.toLowerCase().split(/\s+/))n&&!t.includes(n)&&t.push(n);return t}function W(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function G(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(W).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 K(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()]}export{k as C,h as D,m as E,p as O,S,f as T,H as _,V as a,U as b,C as c,D as d,b as f,O as g,x as h,z as i,g as k,F as l,P as m,R as n,E as o,G as p,N as r,w as s,M as t,K as u,I as v,j as w,B as x,T as y};
|
|
3
|
+
//# sourceMappingURL=context-model-2Owp2jYF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-model-2Owp2jYF.js","names":[],"sources":["../../../../../../src/core/verse/types.ts","../../../../../../src/web-ui/routes/verse/context/context-model.ts"],"sourcesContent":["/**\n * Ashlr Verse — interactive multi-turn session contract (V1).\n *\n * A Verse session binds one project directory to one execution seat\n * (engine + account + model). Each user turn spawns ONE vendor CLI process\n * that resumes the vendor-native conversation by id, streams normalized\n * events, and exits. No long-lived stdin is held between turns.\n *\n * Seats:\n * claude -> `claude -p ... --output-format stream-json` via a native-profile launcher (account pinned by CLAUDE_CONFIG_DIR)\n * local -> the plain `claude` binary with ANTHROPIC_BASE_URL pointed at Ollama (Anthropic-compatible API); model = Ollama tag\n * codex -> `codex exec --json` / `codex exec resume <threadId> --json` via a native-profile launcher (CODEX_HOME pinned)\n * grok -> `grok -p ... --output-format streaming-messages-json` via a native-profile launcher (GROK_HOME pinned)\n *\n * Nothing in this module reads secrets. Account identity is pinned by the\n * launcher command recorded in ~/.ashlr/account-connections/connections.json.\n */\nimport type { TRANSIENT_EVENT_TYPE_LIST } from './transient-events.js';\n\nexport type VerseEngine = 'claude' | 'codex' | 'grok' | 'local';\n\nexport type VerseSessionStatus = 'idle' | 'running' | 'error';\n\n/** One selectable execution seat shown in the model/account picker. */\nexport interface VerseSeat {\n /** Stable id, e.g. `claude`, `codex-personal`, `grok`, `local:qwen3-coder-next:ctx64k`. */\n id: string;\n engine: VerseEngine;\n /** Human label, e.g. \"Claude Code\", \"Personal Codex\", \"Grok\", \"Qwen3-Coder-Next (local)\". */\n label: string;\n /** Account id from connections.json for native seats; `local` for Ollama seats. */\n accountId: string;\n /** Models this seat can run. First entry is the default. */\n models: VerseModelOption[];\n /** Best-known context window in tokens for the default model (null when unknown). */\n contextWindow: number | null;\n /** Latest health/usage evidence, if any. Never an admission input in V1. */\n health: VerseSeatHealth;\n /**\n * V2.1 ADDITIVE. The subscription capacity behind this seat, so the seat\n * list can render a meter without a second request.\n *\n * Absent (`undefined`) for LOCAL seats: an Ollama tag has no subscription\n * and no provider window. Its real constraint is machine memory, which is\n * reported by `VerseBootstrap.localRuntime`, not by a quota. Absent on a\n * native seat means the server could not build a record for that account\n * at all — `health.state` says what it does know.\n */\n capacity?: VerseSeatCapacity;\n /**\n * V3.9 ADDITIVE. The version of the CLI binary this seat is PINNED to (its\n * native-profile launcher execs one exact file). Absent when unknown and on\n * local seats. A pinned binary never updates itself, so a model that needs a\n * newer CLI is listed with `unavailableReason` rather than silently failing.\n */\n cliVersion?: string;\n /**\n * V3.9 ADDITIVE. Plain-language facts about this seat's context setup the UI\n * must show rather than imply (binary skew, catalog not yet fetched, …).\n * Absent when there is nothing to say, so ordinary seats keep their shape.\n */\n notes?: string[];\n}\n\n/**\n * Where a context-window figure came from, so nothing on screen implies more\n * certainty than it has (docs/VERSE-CONTEXT.md §1).\n *\n * - `runtime` — the CLI reported it for THIS turn (claude/grok\n * `result.modelUsage.<m>.contextWindow`, codex rollout\n * `token_count.info.model_context_window`, Ollama\n * `/api/ps`). Wins over everything else.\n * - `provider-catalog` — the seat's own served catalog on disk (codex/grok\n * `native-state/models_cache.json`).\n * - `cli-catalog` — read out of the pinned CLI binary's embedded catalog\n * and recorded here with the version it was read from.\n * - `documented` — the provider's published model table.\n * - `fallback` — a named default; the UI marks it as an estimate.\n */\nexport type VerseWindowSource = 'runtime' | 'provider-catalog' | 'cli-catalog' | 'documented' | 'fallback';\n\n/**\n * How much context a session is allowed to accumulate before the CLI compacts.\n *\n * - `standard` — the default. Evidence-based budget per engine: Claude 1M\n * models compact near 400k instead of ~967k; Codex, Grok and\n * local run their native windows.\n * - `expansive` — opt-in. Claude runs its full native window (CLI `auto`),\n * Codex raises `model_context_window` to the model's catalog\n * maximum (872k on GPT-6 / GPT-5.6). Costs more usage per turn,\n * buys simultaneous visibility for coupled, cross-cutting work.\n *\n * A mode that does not exist for a model (Grok, local, 200k Claude models,\n * GPT-5.5) is simply absent from that model's option — never faked.\n */\nexport type VerseContextMode = 'standard' | 'expansive';\n\nexport const VERSE_CONTEXT_MODES: readonly VerseContextMode[] = ['standard', 'expansive'];\n\n/** One mode's budget for a model on a seat. */\nexport interface VerseContextBudget {\n /** The window the CLI measures against in this mode (the meter's denominator). */\n contextWindow: number;\n /** Token count at which the CLI auto-compacts in this mode; null when unknown. */\n autoCompactAt: number | null;\n /**\n * The RAW window value the CLI must be TOLD to reach this budget, when that\n * differs from `contextWindow` — codex measures against 95% of the window it\n * is configured with, so expansive GPT-6 is `providerWindow: 872_000`\n * (`-c model_context_window=872000`) but `contextWindow: 828_400`.\n */\n providerWindow?: number | null;\n}\n\nexport interface VerseModelOption {\n id: string;\n label: string;\n /** Standard-mode window the CLI measures against (codex: the 95% effective window). */\n contextWindow: number | null;\n /**\n * V3.9 ADDITIVE — every field below is optional so records and fixtures\n * written before it keep validating. Absent means \"not known\", never zero.\n */\n /** Standard-mode auto-compaction point. */\n autoCompactAt?: number | null;\n /** Present ONLY when this model on this seat has a real expansive mode. */\n expansive?: VerseContextBudget | null;\n /** The CLI's default max output for this model, when known. */\n maxOutputTokens?: number | null;\n windowSource?: VerseWindowSource;\n /** Minimum CLI version that knows this model id (e.g. '2.1.280'); null when any. */\n minCliVersion?: string | null;\n /**\n * Why this model cannot run on this seat right now, in plain language (e.g.\n * \"needs Claude Code 2.1.280; this seat runs 2.1.257\"). Null/absent = runnable.\n * Listed rather than hidden so the operator learns WHY a model is missing.\n */\n unavailableReason?: string | null;\n}\n\nexport interface VerseSeatHealth {\n state: 'ready' | 'degraded' | 'unavailable' | 'unknown';\n /** e.g. \"5h window 72% used, resets 14:00\" — plain text, already sanitized. */\n summary: string | null;\n windows: Array<{ id: string; usedPercent: number | null; resetsAt: string | null }>;\n observedAt: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Seat capacity (V2.1, additive) — see docs/VERSE-TELEMETRY-V2.md, which is\n// the authority for every honesty rule encoded in these shapes.\n//\n// This file is imported by the BROWSER bundle, so it may not import\n// `core/verse/accounts.ts` (node:fs). The shapes below therefore restate the\n// provider-record shapes structurally. `seats.ts` assigns the account record's\n// values straight into them, so a divergence in `core/verse/accounts.ts` stops\n// `seats.ts` compiling — that assignment is the drift guard.\n// ---------------------------------------------------------------------------\n\n/** One provider-reported quota window, as carried on a seat. */\nexport interface VerseSeatWindow {\n id: string;\n /** Provider-reported percent, or null for NO SIGNAL — which is not zero. */\n usedPercent: number | null;\n /**\n * Machine-readable reset instant. STRUCTURALLY ALWAYS NULL for Claude: that\n * provider publishes only a sentence, which lives in `resetDescription` and\n * is rendered verbatim. The server never synthesizes an instant from it.\n * (3.10.1: the Command burn-down chart may place the reset on its time axis\n * via `resetInstantFromWords` in web-ui command-model, but only for the\n * collector's exact \"Mon D at H:MMam (Area/Zone)\" form and only when the\n * result lands within one window of now; anything else stays words.)\n */\n resetsAt: string | null;\n /** The provider's own reset wording, verbatim (\"resets Sep 25 at 7pm (America/New_York)\"). */\n resetDescription: string | null;\n /**\n * TRUE only when the provider explicitly FLAGGED the limit. Such a window's\n * `usedPercent` is the SENTINEL 100 — a denial, not a measurement — and must\n * be labelled \"limit reached\", never \"100% used\".\n */\n limitReached: boolean;\n /** False when `usedPercent` is a flagged sentinel rather than a reading. */\n measured: boolean;\n}\n\n/**\n * Codex credits, INDEPENDENT of the window: a fully used weekly window with a\n * spendable balance is not blocked. `null` is \"no signal\", never \"no credits\".\n */\nexport interface VerseSeatCredits {\n hasCredits: boolean;\n unlimited: boolean;\n /** Provider-reported decimal string, kept verbatim — never rounded. */\n balance: string | null;\n}\n\n/**\n * The coarse \"can I use this seat right now\" verdict. Deliberately coarse: the\n * exact numbers are in `windows`, and a verdict that pretended to more\n * precision than the providers give would be a lie.\n *\n * - `ready` — a reading exists and the binding window has headroom.\n * - `tight` — the binding window is at/above the warn threshold, OR it is\n * spent while Codex credits remain spendable (not blocked).\n * - `exhausted` — the binding window is spent and nothing else is left.\n * - `signed-out` — the account is not authenticated; reconnect it.\n * - `unknown` — NO window carried a percent. Not zero, not healthy.\n */\nexport type VerseSeatUsability = 'ready' | 'tight' | 'exhausted' | 'signed-out' | 'unknown';\n\n/**\n * Where a seat's windows came from, so nothing implies freshness it lacks.\n * Mirrors `VerseEvidenceSource` in `core/verse/accounts.ts`.\n */\nexport type VerseSeatEvidenceSource = 'collector' | 'shared-evidence' | 'baseline' | 'none';\n\n/** Everything the seat list needs to draw a capacity meter honestly. */\nexport interface VerseSeatCapacity {\n /** e.g. \"max\", \"pro\", \"SuperGrok\". Null when the provider gave none. */\n planType: string | null;\n /**\n * THE CONSTRAINT THAT ACTUALLY BLOCKS WORK: the window with the highest\n * `usedPercent`. Null when no window carried a percent at all.\n */\n binding: VerseSeatWindow | null;\n /** Every window the provider reported, in provider order. */\n windows: VerseSeatWindow[];\n /** Codex only; null for every other provider and when no signal exists. */\n credits: VerseSeatCredits | null;\n usability: VerseSeatUsability;\n observedAt: string | null;\n evidenceSource: VerseSeatEvidenceSource;\n /** Plain-language provider facts the UI must show instead of implying a fault. */\n notes: string[];\n}\n\nexport interface VerseProject {\n path: string;\n name: string;\n /** From ~/.ashlr/enrollment.json when present. */\n enrolled: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Workspaces (V2.2, ADDITIVE) — see docs/VERSE-WORKSPACES.md §1.\n//\n// A session bound to ONE directory cannot express \"this service plus the\n// shared library it depends on\", which is most real work. A workspace is a\n// NAMED set of roots with exactly one marked primary.\n//\n// `VerseSession.projectPath` keeps its meaning — it IS the primary root — so\n// every session record, adapter and test written before workspaces existed\n// still loads and still runs. Nothing rewrites saved chat history.\n//\n// ENROLLMENT IS NOT PART OF THIS SHAPE ON PURPOSE. A workspace says what a\n// session may REACH; `~/.ashlr/enrollment.json` says what the AUTONOMOUS lane\n// may mutate, and the two are deliberately separate registries. Membership in\n// a workspace grants no enrollment: `enrolled` below is READ from the\n// registry per root and is never written by anything in this feature.\n// ---------------------------------------------------------------------------\n\n/** One directory in a workspace. Exactly one root in a workspace is primary. */\nexport interface VerseWorkspaceRoot {\n /** Absolute, physically resolved path (symlinks followed). */\n path: string;\n /** Display name; defaults to the basename. */\n name: string;\n /**\n * The session's cwd and the value `projectPath` carries. Exactly one root\n * per workspace has this set.\n */\n primary: boolean;\n}\n\n/**\n * A named set of roots.\n *\n * ONE TYPE, TWO USES, DELIBERATELY. An interactive session binds to a\n * workspace to decide what it can reach; the autonomous lane reads the same\n * workspace as a SECTION — \"these repositories collaborate, work on them\n * together\" — when `section` is true. They were not split because the thing\n * being named is identical in both: a set of directories that belong to one\n * piece of work. Two registries of named repo groups would drift.\n *\n * What is NOT on this type is priority. A repo may belong to several\n * workspaces, so a per-membership rank would give one repo several\n * contradictory ranks. Priority is therefore an attribute OF THE REPO, held\n * once per path in the same registry — see `VerseRootPriority`.\n */\nexport interface VerseWorkspace {\n id: string;\n name: string;\n /** Primary first, then the extras in the order they were added. */\n roots: VerseWorkspaceRoot[];\n /**\n * Offer this workspace to the autonomous lane as a section.\n *\n * GRANTS NOTHING. The fleet still reaches only what\n * `~/.ashlr/enrollment.json` permits; a section can group and order those\n * repos and nothing else. A member that is not enrolled stays unreachable.\n */\n section: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Relative standing of one repository in the autonomous lane's attention.\n *\n * ORDERING ONLY, NEVER ADMISSION. `critical` on an unenrolled repo is still\n * refused by `assertMayMutate`; `low` on an enrolled one is still permitted.\n * An ordinal rather than a number because the operator's judgement here is\n * \"this matters more than that\", and a 0-100 score would invent a precision\n * nobody holds.\n *\n * Absent means `normal`, which is how every repo already behaves — so a\n * registry with no priorities at all reproduces today's flat list exactly.\n */\nexport type VerseRootPriority = 'critical' | 'high' | 'normal' | 'low';\n\nexport const VERSE_ROOT_PRIORITIES: readonly VerseRootPriority[] =\n ['critical', 'high', 'normal', 'low'];\n\nexport const VERSE_DEFAULT_ROOT_PRIORITY: VerseRootPriority = 'normal';\n\n/** Sort weight; lower sorts first. Never a budget, never a multiplier. */\nexport const VERSE_ROOT_PRIORITY_RANK: Record<VerseRootPriority, number> = {\n critical: 0,\n high: 1,\n normal: 2,\n low: 3,\n};\n\n/** One enrolled repository as the autonomy scope view ranks it. */\nexport interface VerseAutonomyScopeEntry {\n path: string;\n name: string;\n priority: VerseRootPriority;\n /** Sections (workspaces with `section: true`) this repo belongs to. */\n sections: Array<{ id: string; name: string }>;\n /** True when a focus section is set and this repo is outside it. */\n outsideFocus: boolean;\n}\n\n/**\n * The autonomous lane's scope, ORDERED.\n *\n * Built by INTERSECTING section membership with the enrollment registry, so\n * it can only ever be a subset of what enrollment already allows. Sections\n * add ordering and grouping; they never add reach.\n */\nexport interface VerseAutonomyScopeView {\n focusSectionId: string | null;\n focusSectionName: string | null;\n /** Enrolled repos, most important first. */\n entries: VerseAutonomyScopeEntry[];\n /**\n * Paths a section names that are NOT enrolled. Listed so the blast radius\n * is legible in both directions — these are exactly the repos a section\n * does NOT give the fleet.\n */\n unenrolledSectionRoots: string[];\n}\n\n/**\n * Per-root git identity. A turn that edits two repos produces two diffs, so\n * the transcript has to be able to attribute each file to the right one.\n *\n * `remote` is the canonical `owner/repo` for a GitHub origin and null for\n * anything else — deliberately NOT the raw remote URL, which can carry a\n * token in its userinfo and would then reach an API response.\n */\nexport interface VerseRootGit {\n branch: string;\n /** Count of porcelain entries; 0 is clean. */\n dirty: number;\n ahead: number;\n behind: number;\n remote: string | null;\n}\n\n/** A root as the client sees it: identity, reachability and honest scope. */\nexport interface VerseRootStatus {\n path: string;\n name: string;\n primary: boolean;\n exists: boolean;\n /**\n * Read live from the enrollment registry. FALSE means the interactive lane\n * still works on this root while the autonomous lane refuses it — being in\n * a workspace never changes this.\n */\n enrolled: boolean;\n /** Null when the root is not a git repository, or git is unavailable. */\n git: VerseRootGit | null;\n /**\n * False when the session's engine has no way to be granted this root (Grok's\n * CLI exposes `--cwd` and nothing else). The root is still listed, because\n * hiding it would misrepresent what the workspace is.\n */\n reachable: boolean;\n}\n\n/** GET /api/verse/sessions/:id/roots */\nexport interface VerseSessionRootsResponse {\n sessionId: string;\n workspaceId: string | null;\n workspaceName: string | null;\n roots: VerseRootStatus[];\n /**\n * Plain-language facts the UI must show rather than imply: an unreachable\n * root on a Grok seat, or a root the autonomous lane will refuse.\n */\n notes: string[];\n}\n\n/** GET /api/verse/workspaces */\nexport interface VerseWorkspacesResponse {\n workspaces: VerseWorkspace[];\n /** Live per-root facts, keyed by workspace id, so one read draws the list. */\n status: Record<string, VerseRootStatus[]>;\n /** Per-repo priority, keyed by canonical path. Absent key means `normal`. */\n priorities: Record<string, VerseRootPriority>;\n focusSectionId: string | null;\n}\n\n/** POST /api/verse/workspaces */\nexport interface VerseWorkspaceCreateRequest {\n name: string;\n /** Absolute paths. The first is the primary. */\n roots: string[];\n /** Also offer this set to the autonomous lane as a section. Default false. */\n section?: boolean;\n}\n\n/** POST /api/verse/workspaces/:id/update */\nexport interface VerseWorkspaceUpdateRequest {\n name?: string;\n /** When present, replaces the root set wholesale. First entry is primary. */\n roots?: string[];\n section?: boolean;\n}\n\n/** POST /api/verse/workspaces/priority */\nexport interface VerseRootPriorityRequest {\n path: string;\n priority: VerseRootPriority;\n}\n\n/** POST /api/verse/workspaces/focus — `sectionId: null` clears the focus. */\nexport interface VerseFocusSectionRequest {\n sectionId: string | null;\n}\n\n/** Hard cap on roots per workspace — a blast radius, not a UI limit. */\nexport const VERSE_MAX_WORKSPACE_ROOTS = 8;\n\n/**\n * Every root a session can reach, primary first.\n *\n * Pure and total: a record written before workspaces existed has no\n * `extraRoots` and yields exactly `[projectPath]`, which is what every caller\n * assumed before this existed.\n */\nexport function verseSessionRoots(\n session: Pick<VerseSession, 'projectPath' | 'extraRoots'>,\n): string[] {\n const out = [session.projectPath];\n for (const root of session.extraRoots ?? []) {\n if (typeof root === 'string' && root.length > 0 && !out.includes(root)) out.push(root);\n }\n return out;\n}\n\nexport interface VerseUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n /**\n * Prompt size of the most recent assistant call = live context occupancy.\n * Stored UNCLAMPED: a reading above the window is information (the CLI is\n * about to compact or overflow), and only the UI decides how to draw it.\n */\n contextTokens: number;\n contextWindow: number | null;\n /** V3.9 ADDITIVE. Where `contextWindow` came from; absent on old records. */\n contextWindowSource?: VerseWindowSource;\n /** V3.9 ADDITIVE. Auto-compaction point for the session's current mode. */\n autoCompactAt?: number | null;\n /**\n * V3.9 ADDITIVE. False when `contextTokens` is an UPPER BOUND rather than a\n * measurement (codex before its rollout is readable: the turn total sums\n * every model call). Absent means exact.\n */\n contextTokensExact?: boolean;\n}\n\nexport interface VerseSession {\n id: string;\n title: string;\n /**\n * The PRIMARY root: the process cwd and the folder every pre-workspace\n * client already reads. Unchanged in meaning, which is why no session\n * record ever has to be rewritten.\n */\n projectPath: string;\n /**\n * V2.2 ADDITIVE. Roots BEYOND the primary, pinned at creation time.\n *\n * Absent on every record written before workspaces existed, and absent on\n * a single-root session today — so the on-disk shape of an ordinary chat\n * does not change at all.\n *\n * PINNED, not referenced: a session keeps the roots it was created with\n * even if the named workspace is later edited or deleted, for the same\n * reason `launch.anthropicBaseUrl` is pinned — a conversation must not\n * silently gain reach because config moved underneath it.\n */\n extraRoots?: string[];\n /** V2.2 ADDITIVE. Provenance of the pinned roots; absent for an ad-hoc set. */\n workspaceId?: string;\n workspaceName?: string;\n engine: VerseEngine;\n accountId: string;\n seatId: string;\n model: string;\n /** Vendor conversation id used for resume (claude/grok: our uuid; codex: thread id from output). */\n nativeSessionId: string | null;\n createdAt: string;\n updatedAt: string;\n status: VerseSessionStatus;\n turnCount: number;\n usage: VerseUsage;\n lastError: string | null;\n /**\n * V3.9 ADDITIVE. The session's context budget. Every record written by 3.9+\n * carries it explicitly, so ABSENT means a pre-3.9 record: the engine\n * materializes it on first touch — `expansive` for a Claude 1M model (that\n * is the native ~967k compaction those chats always ran with, so upgrading\n * never forces a paid compaction), `standard` otherwise (= native).\n * Changeable mid-session (POST /sessions/:id/context-mode): it changes CLI\n * flags, not prompt content — but switching DOWN below the current\n * occupancy makes the CLI compact on its next turn.\n */\n contextMode?: VerseContextMode;\n /** V3.9 ADDITIVE. Native compactions observed so far (claude/grok compact_boundary, codex rollout `compacted`). */\n compactionCount?: number;\n /** V3.9 ADDITIVE. Set when this session was started as a handoff from another. */\n handoffFrom?: { sessionId: string; title: string };\n /**\n * V3.9 ADDITIVE. Shared project memory was offered to this session's CLI at\n * creation. Pinned, like the roots: a conversation must not silently gain a\n * writable directory because a preference moved underneath it.\n */\n memoryEnabled?: boolean;\n /**\n * V3.10 ADDITIVE (contract: unit C0; written by C3's engine). The operator's\n * per-chat effort and permission choices, applied from the NEXT turn — a\n * running turn keeps the argv it was launched with. Absent (every record\n * before 3.10, and any key never set) = the engine default: permission mode\n * `accept-edits`, effort = the CLI's own default.\n *\n * A MODEL change is deliberately NOT stored here: it rewrites `model` above,\n * which every launch already reads — one field, one truth.\n */\n controls?: VerseSessionControls;\n}\n\n/**\n * V3.10. Permission modes a chat can run in (SPEC-310C §0.6), in picker order.\n * - `plan` — the CLI plans and edits nothing.\n * - `accept-edits` — the default: edits applied without asking.\n * - `auto` — the CLI's own auto mode.\n * - `bypass` — every check skipped. Shown red and confirmed PER CHAT:\n * never a default, never inherited by a new chat.\n * Adapters map these to each CLI's spelling (claude `acceptEdits` /\n * `bypassPermissions`, grok `dontAsk`, …). An engine that cannot honour one\n * reports it unavailable WITH a reason rather than approximating it. The\n * in-page \"Ask\" mode (answering permission prompts in the UI) is 3.11.\n */\nexport const VERSE_PERMISSION_MODES = ['plan', 'accept-edits', 'auto', 'bypass'] as const;\nexport type VersePermissionMode = (typeof VERSE_PERMISSION_MODES)[number];\nexport const VERSE_DEFAULT_PERMISSION_MODE: VersePermissionMode = 'accept-edits';\n\n/**\n * V3.10. Reasoning effort, the union across engines — claude `--effort`\n * (low · medium · high · xhigh · max) and codex `model_reasoning_effort`\n * (minimal · low · medium · high · xhigh). Each seat offers only the subset\n * its CLI accepts; the rest come back unavailable with a reason.\n */\nexport const VERSE_EFFORTS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const;\nexport type VerseEffort = (typeof VERSE_EFFORTS)[number];\n\n/** V3.10. `VerseSession.controls` — absent key = engine default (see there). */\nexport interface VerseSessionControls {\n effort?: VerseEffort;\n permissionMode?: VersePermissionMode;\n}\n\n/** Normalized event stream. `seq` is monotonic per session and is the SSE id. */\nexport type VerseEvent =\n | { seq: number; at: string; type: 'user-message'; turnId: string; text: string }\n | { seq: number; at: string; type: 'turn-started'; turnId: string; pid: number | null }\n | { seq: number; at: string; type: 'text-delta'; turnId: string; text: string }\n | { seq: number; at: string; type: 'assistant-message'; turnId: string; text: string }\n /**\n * Reasoning the CLI chose to show. V3.10 ADDITIVE optional fields (absent on\n * older records = unknown, never \"false\"/\"0\"):\n * - `redacted` the CLI sent a signature-only / redacted block, so `text` is\n * empty but the model DID think (the UI says \"Thought 12s\").\n * - `durationMs` wall time the block took to stream, when measured.\n * - `kind` see VerseThinkingKind.\n */\n | {\n seq: number;\n at: string;\n type: 'thinking';\n turnId: string;\n text: string;\n redacted?: boolean;\n durationMs?: number;\n kind?: VerseThinkingKind;\n }\n | { seq: number; at: string; type: 'tool-use'; turnId: string; toolUseId: string; name: string; input: unknown }\n | { seq: number; at: string; type: 'tool-result'; turnId: string; toolUseId: string; output: string; isError: boolean }\n | { seq: number; at: string; type: 'usage'; turnId: string; usage: VerseUsage }\n | { seq: number; at: string; type: 'turn-done'; turnId: string; ok: boolean; nativeSessionId: string | null; durationMs: number }\n /**\n * `code` (V3.10 ADDITIVE, optional) is a stable machine key for errors the UI\n * or engine acts on — see VERSE_ERROR_CODES. Absent = uncategorised.\n */\n | { seq: number; at: string; type: 'error'; turnId: string | null; message: string; code?: string }\n | { seq: number; at: string; type: 'cancelled'; turnId: string }\n /**\n * V3.9. The CLI compacted its own conversation. Token counts are the CLI's\n * own (`compact_metadata.pre_tokens/post_tokens` on claude/grok). Codex\n * rollouts mark only THAT a compaction happened, so its counts are the last\n * `token_count` reading before the marker and the first one after it —\n * measured, but by Verse, not stated by the CLI; its `durationMs` is null.\n * Any count may be null when no reading brackets the compaction.\n */\n | {\n seq: number;\n at: string;\n type: 'compaction';\n turnId: string | null;\n trigger: 'auto' | 'manual';\n preTokens: number | null;\n postTokens: number | null;\n durationMs: number | null;\n }\n /**\n * V3.9. A context-occupancy READING that is not a usage delta — e.g. codex's\n * rollout `token_count` (exact last-call prompt size and the window the CLI\n * measured it against), polled mid-turn and read again after it. Replaces\n * the session's `contextTokens`; never summed. The engine fills\n * `autoCompactAt` for the window in force.\n */\n | {\n seq: number;\n at: string;\n type: 'context';\n turnId: string | null;\n contextTokens: number;\n contextWindow: number | null;\n exact: boolean;\n autoCompactAt?: number | null;\n /**\n * Where `contextWindow` came from. A CLI reading is `runtime`; the event\n * the engine writes after a MODE SWITCH carries the new budget's catalog\n * source, so a client never relabels a catalog figure as a measurement.\n * Absent on events from before this field existed (treat as `runtime`).\n */\n contextWindowSource?: VerseWindowSource;\n }\n // ── V3.10 TRANSIENT events ────────────────────────────────────────────────\n // Never written to the session log; fanned out to live SSE listeners only\n // (see VERSE_TRANSIENT_EVENT_TYPES for the wire rules). A reload therefore\n // never shows them — everything they convey is either ephemeral (a spinner)\n // or re-stated by a persisted event at turn end (`thinking`, `usage`).\n /** A chunk of streamed reasoning text for the in-flight thinking block. */\n | { seq: number; at: string; type: 'thinking-delta'; turnId: string; text: string }\n /**\n * The CLI reports reasoning volume without text (claude\n * `system/thinking_tokens`). An ESTIMATE, rate-limited by the adapter.\n */\n | { seq: number; at: string; type: 'thinking-progress'; turnId: string; estimatedTokens: number }\n /**\n * What the running turn is doing right now. `tool` names the running tool\n * when phase is `tool`. `outTokens`/`tokPerSec` are absent when unmeasured.\n */\n | {\n seq: number;\n at: string;\n type: 'progress';\n turnId: string;\n phase: VerseProgressPhase;\n tool?: string;\n elapsedMs: number;\n outTokens?: number;\n tokPerSec?: number;\n }\n /** A human-readable notice about the turn's plumbing (API retry, preflight, no-output watchdog). */\n | { seq: number; at: string; type: 'status'; turnId: string | null; kind: VerseStatusKind; message: string }\n // ── V3.10 PERSISTED events ────────────────────────────────────────────────\n /**\n * The engine recovered from a lost native conversation (e.g. the vendor\n * thread vanished) by starting a new native session seeded with the handoff\n * note (`how: 'handoff'`) or a bare new session (`new-native-session`).\n */\n | { seq: number; at: string; type: 'recovered'; turnId: string | null; how: VerseRecoveryHow; message: string }\n /**\n * The log hit its cap and events with `seq < droppedBefore` were dropped at\n * a TURN boundary (never mid-turn). `turnId` is always null — it is present\n * only so code reading `event.turnId` across the union keeps typechecking.\n */\n | { seq: number; at: string; type: 'history-truncated'; turnId: null; droppedBefore: number };\n\nexport type VerseEventType = VerseEvent['type'];\n\n/** V3.10. `summary` = vendor-summarised reasoning, `raw` = verbatim, `progress` = a heading/placeholder only. */\nexport type VerseThinkingKind = 'summary' | 'raw' | 'progress';\n\n/** V3.10. Phase of a running turn, carried by the transient `progress` event. */\nexport type VerseProgressPhase = 'thinking' | 'tool' | 'writing' | 'waiting';\n\n/** V3.10. What a transient `status` notice is about. */\nexport type VerseStatusKind = 'retry' | 'preflight' | 'watchdog';\n\n/** V3.10. How a `recovered` event restored the conversation. */\nexport type VerseRecoveryHow = 'new-native-session' | 'handoff';\n\n/**\n * V3.10. Known `error.code` values. `code` stays `string` on the wire so an\n * older client never rejects a newer code; these are the ones Verse acts on.\n * - `native-thread-missing` — the vendor conversation to resume no longer exists.\n * - `session-in-use` — the CLI refused because the native session id is locked.\n */\nexport const VERSE_ERROR_CODES = ['native-thread-missing', 'session-in-use'] as const;\nexport type VerseErrorCode = (typeof VERSE_ERROR_CODES)[number];\n\n/**\n * V3.10. Event types that are NEVER persisted — emitted to live SSE listeners\n * only. The list and its guard live in ./transient-events.ts (re-exported\n * here): the web chat's first-paint store needs the guard, and importing it\n * from this module put every runtime value exported here — the handoff\n * prompt, the effort tables — into the chat's first-paint chunk.\n */\nexport { VERSE_TRANSIENT_EVENT_TYPES, isTransientVerseEvent } from './transient-events.js';\n\nexport type VerseTransientEventType = (typeof TRANSIENT_EVENT_TYPE_LIST)[number];\nexport type VerseTransientEvent = Extract<VerseEvent, { type: VerseTransientEventType }>;\nexport type VersePersistedEvent = Exclude<VerseEvent, { type: VerseTransientEventType }>;\n\n/**\n * Which endpoint a LOCAL seat sends its turns to.\n *\n * - `ollama` — the historical lane: the plain `claude` binary with\n * ANTHROPIC_BASE_URL pointed straight at Ollama's\n * Anthropic-compatible endpoint on :11434. Ollama\n * serialises this model architecture, so a fleet of local\n * agents on this lane is a queue (docs/LOCAL-FLEET.md).\n * - `llama-server` — the same binary pointed at the local Anthropic\n * normalising proxy that fronts llama-server. That proxy\n * is the ONLY way to reach llama-server's parallel slots\n * and the prompt-cache fix, because Qwen3.8's chat\n * template rejects Claude Code's request shape unless the\n * proxy rewrites it on the way through.\n *\n * DISPATCH ONLY. Model DISCOVERY stays on Ollama in both lanes — llama-server\n * implements neither `/api/tags` nor `/api/show`, which is where every local\n * seat's identity, context window and `tools` capability comes from.\n */\nexport type VerseLocalDispatch = 'ollama' | 'llama-server';\n\n/** What a client is told about this machine's local serving situation. */\nexport interface VerseLocalRuntimeSummary {\n /** Ollama reachable? Drives the local seats and an inline hint when false. */\n ollama: { reachable: boolean; baseUrl: string; models: string[] };\n /**\n * Where local seats DISPATCH turns, reported only when that is NOT the\n * default Ollama lane.\n *\n * Absent means `ollama`, i.e. exactly what every existing client already\n * assumes — so opting in is visible on the wire and NOT opting in changes no\n * payload at all.\n */\n dispatch?: { lane: VerseLocalDispatch; baseUrl: string };\n}\n\n/** GET /api/verse/bootstrap */\nexport interface VerseBootstrap {\n seats: VerseSeat[];\n projects: VerseProject[];\n sessions: VerseSession[];\n /** Whether POST routes are enabled on this server (`ashlr serve --allow-dispatch` / `ashlr verse`). */\n dispatchEnabled: boolean;\n localRuntime: VerseLocalRuntimeSummary;\n}\n\n/**\n * GET /api/verse/seats (V2.1)\n *\n * WHY THIS ROUTE EXISTS: `bootstrap` is a MOUNT-TIME snapshot. A client that\n * opened the app before the collector's first cycle finished would show\n * \"unknown\" forever. This is the same seat list, cheap enough to poll, and it\n * always reflects the collector's CURRENT state rather than a cached one.\n */\nexport interface VerseSeatsResponse {\n /** When this body was built. */\n sampledAt: string;\n seats: VerseSeat[];\n localRuntime: VerseBootstrap['localRuntime'];\n}\n\n/**\n * POST /api/verse/sessions\n *\n * Two spellings, never mixed (the API rejects the mix rather than silently\n * preferring one):\n * - `workspaceId` alone — the named workspace supplies primary and extras.\n * - `projectPath` (+ optional `extraRoots`) — an ad-hoc set, which is also\n * exactly the pre-workspace request shape.\n */\nexport interface VerseCreateSessionRequest {\n projectPath: string;\n seatId: string;\n model?: string;\n title?: string;\n /** V2.2 ADDITIVE. Absolute paths beyond the primary. */\n extraRoots?: string[];\n /** V2.2 ADDITIVE. Bind to a named workspace instead of naming paths. */\n workspaceId?: string;\n /**\n * SERVER-FILLED. The API resolves this from the registry once `workspaceId`\n * is validated, and never reads it off the request body — so a session can\n * not be labelled with a workspace name that is not the one it was built\n * from.\n */\n workspaceName?: string;\n /**\n * V3.9 ADDITIVE. Absent → the seat's preferred mode (preferences.json), else\n * `standard`. `expansive` on a model with no expansive budget is rejected.\n */\n contextMode?: VerseContextMode;\n /**\n * V3.9 ADDITIVE. Start this session as a handoff from an existing one. The\n * server resolves the source's title itself (never from the body) and pins\n * `handoffFrom`; the handoff TEXT is sent by the client as turn 1, after the\n * operator has read it — nothing is spent on their behalf.\n */\n handoffFromSessionId?: string;\n}\n\n/** POST /api/verse/sessions/:id/turns */\nexport interface VerseTurnRequest {\n text: string;\n}\nexport interface VerseTurnResponse {\n turnId: string;\n session: VerseSession;\n}\n\n/** GET /api/verse/sessions/:id */\nexport interface VerseSessionDetail {\n session: VerseSession;\n events: VerseEvent[];\n}\n\n/** Adapter input: how to launch one turn. Pure data, produced by the engine, consumed by the spawner. */\nexport interface VerseTurnLaunch {\n argv: string[];\n cwd: string;\n /** Extra env layered over a minimal base (PATH/HOME/TMPDIR/LANG). Never contains secrets. */\n env: Record<string, string>;\n /** Text written to stdin (codex); null when the prompt is in argv. */\n stdin: string | null;\n}\n\nexport const VERSE_MAX_TURN_TEXT_BYTES = 64 * 1024;\nexport const VERSE_MAX_EVENTS_PER_SESSION = 20_000;\nexport const VERSE_TURN_TIMEOUT_MS = 30 * 60 * 1000;\n/**\n * LAST-RESORT fallbacks, used only when neither the runtime, the seat's own\n * catalog, nor the verified per-model table (core/verse/model-windows.ts) knows\n * the model. Each mirrors what the CLI ITSELF assumes for an unknown id, so the\n * meter never claims more than the CLI will allow:\n *\n * - claude 200_000 — Claude Code's `Mg()` default for an unrecognized model.\n * Every KNOWN Claude id carries its real window (1M for the 5.x family,\n * Opus 4.8 and Sonnet 5) in the per-model table instead.\n * - codex 258_400 — 272_000 × the 95% `effective_context_window_percent`\n * every codex catalog entry carries; the figure codex's own rollouts report\n * as `model_context_window` (65,440 of 65,440 observed events).\n * - grok 500_000 — `context_window` for every grok-4.x id in the seat catalog.\n * - local 65_536 — the num_ctx the default local tag pins.\n */\nexport const VERSE_DEFAULT_CONTEXT_WINDOWS: Record<string, number> = {\n claude: 200_000,\n codex: 258_400,\n grok: 500_000,\n local: 65_536,\n};\n\n// ---------------------------------------------------------------------------\n// V3.9 context orchestration — request/response shapes (docs/VERSE-CONTEXT.md)\n// ---------------------------------------------------------------------------\n\n/** GET /api/verse/preferences — the operator's standing context choices. */\nexport interface VersePreferences {\n version: 1;\n /** Per-seat default context mode for NEW sessions. Absent = `standard`. */\n seats: Record<string, { contextMode?: VerseContextMode }>;\n /** Shared project memory. On by default; listed canonical paths opt out. */\n memory: { enabled: boolean; disabledProjects: string[] };\n /**\n * V3.10 ADDITIVE. Stream vendor-summarised reasoning on non-local seats\n * (claude `--thinking-display summarized`). Absent = on (Mason's default).\n */\n thinkingDisplay?: boolean;\n}\n\n/**\n * POST /api/verse/preferences — exactly ONE of the three forms per request, so\n * a body can never change more than the operator clicked.\n */\nexport type VersePreferencesUpdate =\n | { seatId: string; contextMode: VerseContextMode }\n | { memoryEnabled: boolean }\n | { projectPath: string; memoryEnabled: boolean };\n\n/** POST /api/verse/sessions/:id/context-mode */\nexport interface VerseContextModeRequest {\n mode: VerseContextMode;\n}\n\n/** POST /api/verse/sessions/:id/handoff-preview */\nexport interface VerseHandoffPreviewRequest {\n /**\n * Include the latest assistant message verbatim (capped) as the agent's own\n * summary — used after \"Ask this seat to summarize\" produced one.\n */\n includeLastAssistant?: boolean;\n /** Optional operator focus line, e.g. \"finish the migration of X\". ≤ 500 chars. */\n focus?: string;\n}\n\n/** Deterministic, zero-spend handoff note built from the session's event log. */\nexport interface VerseHandoffPreview {\n sourceSessionId: string;\n sourceTitle: string;\n /** The exact text to send as turn 1 of the new session. ≤ VERSE_HANDOFF_MAX_CHARS. */\n text: string;\n stats: {\n chars: number;\n /** chars / 4, rounded up — the same estimator the fit badges use. */\n estTokens: number;\n turnsCovered: number;\n filesTouched: number;\n /** Sections dropped to respect the cap, by name; empty when nothing was cut. */\n truncated: string[];\n };\n}\n\nexport const VERSE_HANDOFF_MAX_CHARS = 12_000;\n\n/**\n * The canned turn behind \"Ask this seat to summarize first\". Shared by the UI\n * (which sends it as an ordinary turn through the normal spend chokepoint) and\n * the handoff builder (which must NOT carry it into the new session as the\n * operator's \"latest ask\" — the new chat would otherwise be told to write a\n * handoff note instead of continuing the work).\n *\n * Written so the reply is useful as turn 1 of a session that has NONE of this\n * context. It asks for no tool use on purpose: a summary that starts editing\n * files is not a summary, and a read-only turn is also the cheapest one.\n */\nexport const VERSE_HANDOFF_SUMMARY_REQUEST = [\n 'This conversation is about to continue in a fresh session that has none of its context.',\n 'Write the handoff note that new session should start from. Do not edit files or run commands for this — only write the note.',\n '',\n 'Cover, concretely:',\n '- The goal, and what \"done\" looks like.',\n '- Decisions made so far, each with the reason it was made.',\n '- Current state: what is finished, what is half-done, what is untested.',\n '- Files changed and anything not yet committed.',\n '- How to verify the work (exact commands).',\n '- Open questions, risks, and approaches already tried that did not work.',\n '',\n 'Keep it under 600 words. Prefer file paths and commands over prose.',\n].join('\\n');\n\n/** GET /api/verse/context-fit — how big the reachable code is, in tokens. */\nexport interface VerseContextFitRoot {\n path: string;\n /** Tracked text files counted (git ls-files, binaries and >1 MB files skipped). */\n files: number;\n bytes: number;\n estTokens: number;\n /** True when the scan hit its file or time cap; estTokens is then a floor. */\n truncated: boolean;\n}\n\nexport interface VerseContextFit {\n roots: VerseContextFitRoot[];\n totalEstTokens: number;\n /** Always 'bytes/4' today; named so a better estimator can be introduced honestly. */\n estimator: 'bytes/4';\n sampledAt: string;\n}\n\n/**\n * Per-model verdict for a working set (see context-math.ts `fitVerdict`):\n * fits in standard, tight in standard, fits only in expansive, or too big for\n * any single context (split the work).\n */\nexport type VerseFitVerdict = 'fits' | 'tight' | 'expansive' | 'split';\n\n/** GET /api/verse/search?q=&limit= — keyword search over past sessions. Zero spend. */\nexport interface VerseSearchHit {\n sessionId: string;\n title: string;\n projectPath: string;\n engine: VerseEngine;\n seq: number;\n at: string;\n kind: 'user' | 'assistant';\n /** ≤ 240 chars around the first match, whitespace-collapsed. */\n snippet: string;\n score: number;\n}\n\nexport interface VerseSearchResponse {\n query: string;\n hits: VerseSearchHit[];\n /** Sessions actually scanned (the scan is bounded; see session-search.ts). */\n scannedSessions: number;\n truncated: boolean;\n}\n\n/** GET/POST /api/verse/memory — shared project memory for one project. */\nexport interface VerseProjectMemory {\n projectPath: string;\n enabled: boolean;\n /** Contents of MEMORY.md ('' when absent). */\n content: string;\n bytes: number;\n updatedAt: string | null;\n /** Other files the agents created in the memory directory (names only). */\n files: string[];\n /**\n * Present (true) only when `content` as SENT differs from the bytes on disk:\n * the public-JSON sanitizer rewrote the home path to `~` or replaced a\n * secret-looking value with `[REDACTED]`. The file itself is untouched; a\n * save that would write placeholders over real values is refused (409\n * VERSE_MEMORY_REDACTED), so the editor must say so before the operator types.\n */\n contentSanitized?: boolean;\n}\n\n/** POST /api/verse/memory */\nexport interface VerseProjectMemoryWrite {\n projectPath: string;\n content: string;\n}\n\nexport const VERSE_MEMORY_MAX_BYTES = 64 * 1024;\n\n/**\n * POST /api/verse/memory's own JSON body cap: JSON escaping can double the\n * content (every newline becomes `\\\\n`), plus room for the other fields. Every\n * other route keeps the 64 KiB default. Shared so the browser's pre-check can\n * never refuse a save the server would accept.\n */\nexport const VERSE_MEMORY_BODY_MAX_BYTES = 2 * VERSE_MEMORY_MAX_BYTES + 8 * 1024;\n\n/** Cap on the memory block appended to each session's system prompt. */\nexport const VERSE_MEMORY_BLOCK_MAX_BYTES = 6 * 1024;\n","/**\n * routes/verse/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 { formatContextWindow, formatRelative } from '../verse-model.js';\nimport { asClause, percentText, tidyProse } from '../autonomy/format.js';\nimport { formatTokens } from '../verse-readouts.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/**\n * Why a seat+model cannot take a new session right now; null when it can.\n *\n * The seat's own reason is embedded as a clause: its closing period is\n * dropped (no \"exhausted..\") and any ISO instant in it — a health summary\n * saying when usage resets — is read as the viewer's local time.\n */\nexport function targetUnavailableReason(\n seat: VerseSeat | null | undefined,\n option: VerseModelOption | null,\n now: number = Date.now(),\n): string | null {\n if (!seat) return 'Pick a seat to continue on.';\n if (seat.health.state === 'unavailable') {\n return `${seat.label} is unavailable: ${asClause(tidyProse(seat.health.summary ?? 'no reason given', now))}.`;\n }\n if (!option) return 'Pick a model on this seat.';\n if (option.unavailableReason) {\n return `${option.label} cannot run on ${seat.label}: ${asClause(tidyProse(option.unavailableReason, now))}.`;\n }\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 = `${formatContextWindow(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 ${percentText((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 // Only the relative tokens (\"5m\", \"3h\", \"2d\") take \"ago\". Anything else is a\n // DATE, whatever the locale prints: \"Sep 1\" (en-US) but \"1 Sept\" (en-GB)\n // and \"1. Sept.\" (de) start with a digit — testing for a leading digit\n // printed \"Last completed 1 Sept ago.\" outside the US.\n return /^\\d+[mhd]$/.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"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{B as e,C as t,D as n,H as r,M as i,N as a,R as o,h as s,i as c,o as l,t as u,x as d}from"./verse-model-CSzzdyu2.js";import{p as f}from"./format-DEAaVF6X.js";function p(e,t){return e?s([e],{seatId:e.id,model:t}):null}function m(e,t){return t!==`standard`&&i(e,t)!==null?t:`standard`}function h(e,t){return i(e,m(e,t))}function g(e,t){let n=d(e);if(n!==null)return n;let r=t.unavailableReason;return typeof r==`string`&&r.trim().length>0?r.trim():null}var _={standard:`Standard`,expansive:`Expansive`},v={claude:`Claude Code`,codex:`Codex CLI`,grok:`Grok CLI`,local:`Claude Code`};function y(e){let t=typeof e.cliVersion==`string`?e.cliVersion.trim():``;return t.length>0?`${v[e.engine]} ${t}`:null}function b(e){return(e.notes??[]).filter(e=>typeof e==`string`&&e.trim().length>0)}function x(e){return e===`fallback`}function S(e,t=`standard`){let r=m(e,t),a=i(e,r);return a?`${`${l(a.contextWindow)} ctx${x(e.windowSource)?` (est.)`:``}`}${a.autoCompactAt===null?``:` · compacts ≈${n(a.autoCompactAt)}`}${r===`expansive`?` (expansive)`:``}`:`window unknown`}function C(t){let r=i(t,`standard`),a=e(t)?i(t,`expansive`):null,o=[];if(!r)o.push(`Context window unknown for this model.`);else{let e=r.autoCompactAt===null?``:`; compacts at about ${n(r.autoCompactAt)}${a?` in Standard`:``}`;o.push(`${l(r.contextWindow)}-token window${e}.`)}return a&&o.push(`Expansive runs to about ${n(a.autoCompactAt??a.contextWindow)} before compacting.`),t.windowSource&&o.push(`Window ${c[t.windowSource]}.`),o.join(` `)}function w(e){return e===`codex`?u:null}function T(e){let t=i(e,`standard`),r=i(e,`expansive`),a=t?t.autoCompactAt??t.contextWindow:null,o=r?r.autoCompactAt??r.contextWindow:null;return a===null||o===null||a<=0?null:`A turn at ${n(o)} re-sends ${(o/a).toFixed(1)}× the tokens of one at ${n(a)}.`}function E(e,t,r){let a=i(t,`standard`),o=i(t,`expansive`),s=a?a.autoCompactAt??a.contextWindow:null,c=o?o.autoCompactAt??o.contextWindow:null;if(r===`standard`||c===null)return s===null?`The CLI compacts at its own default point for this model.`:e===`claude`?`Compacts at about ${n(s)}. Every turn re-sends the whole context, so holding it under this point keeps each turn cheaper and recall sharper.`:`Compacts at about ${n(s)} — the CLI's own default budget for this model.`;let l=T(t),u=w(e);return`Runs to about ${n(c)} before compacting — for tightly coupled, cross-cutting work that needs everything in view at once. Each turn re-sends everything held, so a long expansive chat uses noticeably more of your plan per turn.${l?` ${l}`:``}${u?` ${u}`:``}`}function D(e,t){return i(t,`standard`)?e===`grok`||e===`local`?`One budget: this CLI compacts at its own fixed point, and Verse cannot raise it per chat.`:`One budget: this model has no larger window to expand into.`:`This model’s window is unknown, so Verse cannot offer a larger budget for it.`}var O={fits:`code fits`,tight:`code fits, tight`,expansive:`code needs expansive`,split:`code too big — split`},k={fits:`Fits`,tight:`Tight fit`,expansive:`Needs expansive`,split:`Split the work`};function A(e){return e.roots.some(e=>e.truncated)}function j(e,t,n,a){if(typeof e!=`number`||!Number.isFinite(e)||e<0)return null;let s=r(a);if(m(t,n)===`expansive`&&t){let n=i(t,`expansive`);return o(e,{...t,contextWindow:n.contextWindow,autoCompactAt:n.autoCompactAt,expansive:null},s)}return o(e,t,s)}function M(t){let r=`${t.floor?`at least `:``}~${n(t.tokens)} tokens of tracked code`,a=m(t.option,t.mode)===`expansive`,o=i(t.option,`standard`),s=i(t.option,`expansive`),c=o?n(o.autoCompactAt??o.contextWindow):null,l=s?n(s.autoCompactAt??s.contextWindow):null,u=a?l:c,d=a?`Expansive budget`:`budget`;switch(t.verdict){case`fits`:return`All ${r} fits well inside this model's ${d}${u?` (compacts ≈${u})`:``}, with room left for the conversation.`;case`tight`:{let n=!a&&l!==null&&e(t.option)?` Expansive (≈${l}) would hold it with room to spare.`:``;return`${P(r)} fits under the ≈${u??`?`} compaction point${a?` of the Expansive budget`:``}, with little room for the conversation — expect it to compact if the agent reads widely.${n}`}case`expansive`:return`${P(r)} is more than the Standard budget (≈${c??`?`}) holds; only Expansive (≈${l??`?`}) takes it all. Switch to Expansive, or narrow the folders to the part you are changing.`;case`split`:return`${P(r)} is more than any single context on this model holds${l?`, even Expansive (≈${l})`:``}. Split the work: fan it out across several chats, each scoped to one folder or subsystem, or narrow this chat to the part you are changing.`}}function N(e){let t=e?` for ${v[e]}`:``;return`Estimated from the size of tracked text files (bytes ÷ 4), plus an estimated ~${n(r(e))} of fixed prompt${t} that every chat starts with. An upper bound — agents rarely read everything.`}function P(e){return e.length===0?e:e.charAt(0).toUpperCase()+e.slice(1)}function F(n,r){let i=t(r,n);return{tokens:Math.floor(i.contextTokens),exact:i.exact,window:i.contextWindow,autoCompactAt:i.autoCompactAt,source:i.source,mode:i.mode,hasModes:e(i.option)}}function I(e){return e.cacheReadTokens<=0&&e.cacheCreationTokens<=0?null:a(e)}function L(e){return e===null||!Number.isFinite(e)?`—`:f(e)}function R(e){let t=new Map,n=0,r=null,i=(e,n,r)=>{if(!Number.isFinite(n)||n<0)return;let i=t.get(e)??{exact:null,bound:null};r?i.exact=Math.max(i.exact??0,n):i.bound=Math.max(i.bound??0,n),t.set(e,i)};for(let t of e)switch(t.type){case`usage`:r=t.usage,i(t.turnId,t.usage.contextTokens,t.usage.contextTokensExact!==!1);break;case`context`:t.turnId!==null&&i(t.turnId,t.contextTokens,t.exact);break;case`compaction`:n+=1}let a=0,o=null,s=!0,c=0;for(let e of t.values()){let t=e.exact??e.bound;t!==null&&(e.exact===null&&(s=!1),c+=1,a+=t,o=o===null?t:Math.max(o,t))}return{turns:c,average:c===0?null:Math.round(a/c),peak:o,exact:s,compactions:n,lastTurnCacheHit:r===null?null:I(r)}}function z(e,t,n,i){if(e.status===`running`||i===null)return null;let a=Date.parse(i);if(!Number.isFinite(a))return null;let o=n-a;return o<36e5||t<r(e.engine)?null:{idleMs:o,tokens:t,local:e.engine===`local`}}function B(e,t){return e.window!==null&&t.window!==null?e.tokens/e.window>t.tokens/t.window:e.window!==null||t.window===null&&e.tokens>t.tokens}function V(e,t){let n=new Map;for(let t of e){let e=n.get(t.seatId)??[];e.push(t),n.set(t.seatId,e)}let r=[];for(let[e,i]of n){let n=t.find(t=>t.id===e)??null,a=0,o=0,s=0,c=0,l=0,u=0,d=0,f=null;for(let e of i){a+=e.usage.inputTokens,o+=e.usage.cacheReadTokens,s+=e.usage.cacheCreationTokens,c+=e.usage.outputTokens,l+=e.turnCount,u+=e.compactionCount??0;let n=F(e,t);if(n.mode===`expansive`&&(d+=1),n.tokens<=0)continue;let r={sessionId:e.id,title:e.title,tokens:n.tokens,window:n.window,exact:n.exact};(f===null||B(r,f))&&(f=r)}r.push({seatId:e,label:n?.label??e,engine:n?.engine??i[0].engine,retired:n===null,sessions:i.length,turns:l,promptTokens:a+o+s,outputTokens:c,cacheHitRatio:I({inputTokens:a,cacheReadTokens:o,cacheCreationTokens:s}),compactions:u,expansiveSessions:d,fullest:f})}return r.sort((e,t)=>t.promptTokens-e.promptTokens||e.label.localeCompare(t.label))}export{p as C,V as S,R as T,D as _,h as a,y as b,M as c,L as d,z as f,g,j as h,E as i,A as l,C as m,k as n,w as o,S as p,O as r,T as s,_ as t,N as u,I as v,F as w,b as x,m as y};
|
|
2
|
+
//# sourceMappingURL=context-model-9xywbXxX.js.map
|