@ashlr/hub 3.11.5 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +153 -0
- package/README.md +29 -16
- package/dist/authority-surface.json +91 -45
- package/dist/build-identity.json +2 -2
- package/dist/cli/authority.d.ts +145 -33
- package/dist/cli/authority.js +1196 -313
- package/dist/cli/authority.js.map +1 -1
- package/dist/cli/cloud.js +12 -3
- package/dist/cli/cloud.js.map +1 -1
- package/dist/core/authority/protected-paths.js +11 -0
- package/dist/core/authority/protected-paths.js.map +1 -1
- package/dist/core/authority/resident.d.ts +101 -0
- package/dist/core/authority/resident.js +246 -0
- package/dist/core/authority/resident.js.map +1 -0
- package/dist/core/authority/trust-roots.js +7 -1
- package/dist/core/authority/trust-roots.js.map +1 -1
- package/dist/core/authority/types.d.ts +43 -0
- package/dist/core/authority/types.js +6 -0
- package/dist/core/authority/types.js.map +1 -1
- package/dist/core/cloud/budget.js +11 -1
- package/dist/core/cloud/budget.js.map +1 -1
- package/dist/core/cloud/cloud-api.d.ts +25 -2
- package/dist/core/cloud/cloud-api.js +139 -5
- package/dist/core/cloud/cloud-api.js.map +1 -1
- package/dist/core/cloud/pr-actions.d.ts +93 -0
- package/dist/core/cloud/pr-actions.js +436 -0
- package/dist/core/cloud/pr-actions.js.map +1 -0
- package/dist/core/cloud/pr-preview.d.ts +128 -0
- package/dist/core/cloud/pr-preview.js +228 -0
- package/dist/core/cloud/pr-preview.js.map +1 -0
- package/dist/core/cloud/store.d.ts +2 -0
- package/dist/core/cloud/store.js +28 -0
- package/dist/core/cloud/store.js.map +1 -1
- package/dist/core/cloud/timeline-api.d.ts +24 -0
- package/dist/core/cloud/timeline-api.js +46 -0
- package/dist/core/cloud/timeline-api.js.map +1 -0
- package/dist/core/cloud/timeline-types.d.ts +62 -0
- package/dist/core/cloud/timeline-types.js +36 -0
- package/dist/core/cloud/timeline-types.js.map +1 -0
- package/dist/core/cloud/timeline.d.ts +102 -0
- package/dist/core/cloud/timeline.js +919 -0
- package/dist/core/cloud/timeline.js.map +1 -0
- package/dist/core/cloud/tracker.js +67 -0
- package/dist/core/cloud/tracker.js.map +1 -1
- package/dist/core/cloud/types.d.ts +38 -0
- package/dist/core/cloud/types.js +1 -1
- package/dist/core/cloud/types.js.map +1 -1
- package/dist/core/daemon/service-install-authority.d.ts +10 -2
- package/dist/core/daemon/service-install-authority.js +14 -5
- package/dist/core/daemon/service-install-authority.js.map +1 -1
- package/dist/core/daemon/service.d.ts +15 -0
- package/dist/core/daemon/service.js +26 -0
- package/dist/core/daemon/service.js.map +1 -1
- package/dist/core/fleet/cloud-intake.d.ts +95 -0
- package/dist/core/fleet/cloud-intake.js +557 -0
- package/dist/core/fleet/cloud-intake.js.map +1 -0
- package/dist/core/fleet/dispatch-router.d.ts +13 -0
- package/dist/core/fleet/dispatch-router.js +32 -8
- package/dist/core/fleet/dispatch-router.js.map +1 -1
- package/dist/core/fleet/fleet-merge-state.d.ts +10 -0
- package/dist/core/fleet/fleet-merge-state.js.map +1 -1
- package/dist/core/fleet/host-merge.d.ts +5 -0
- package/dist/core/fleet/host-merge.js +11 -0
- package/dist/core/fleet/host-merge.js.map +1 -1
- package/dist/core/fleet/merge-gates.d.ts +8 -0
- package/dist/core/fleet/merge-gates.js +25 -5
- package/dist/core/fleet/merge-gates.js.map +1 -1
- package/dist/core/fleet/standing-merge-pass.js +30 -0
- package/dist/core/fleet/standing-merge-pass.js.map +1 -1
- package/dist/core/fleet/tick-hooks-live.d.ts +9 -0
- package/dist/core/fleet/tick-hooks-live.js +44 -5
- package/dist/core/fleet/tick-hooks-live.js.map +1 -1
- package/dist/core/fleet/verify-check-run.d.ts +90 -0
- package/dist/core/fleet/verify-check-run.js +274 -0
- package/dist/core/fleet/verify-check-run.js.map +1 -0
- package/dist/core/routing/budget-api.d.ts +11 -0
- package/dist/core/routing/budget-api.js +64 -8
- package/dist/core/routing/budget-api.js.map +1 -1
- package/dist/core/routing/budget-store.d.ts +22 -1
- package/dist/core/routing/budget-store.js +22 -8
- package/dist/core/routing/budget-store.js.map +1 -1
- package/dist/core/universe/builtins/preparation/manifest.json +1 -1
- package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +8 -2
- package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +7 -1
- package/dist/core/verse/account-health.js +9 -3
- package/dist/core/verse/account-health.js.map +1 -1
- package/dist/core/verse/activity-api.d.ts +5 -1
- package/dist/core/verse/activity-api.js +62 -14
- package/dist/core/verse/activity-api.js.map +1 -1
- package/dist/core/verse/authority-api.d.ts +9 -1
- package/dist/core/verse/authority-api.js +82 -2
- package/dist/core/verse/authority-api.js.map +1 -1
- package/dist/core/verse/seat-readiness.d.ts +25 -0
- package/dist/core/verse/seat-readiness.js +43 -6
- package/dist/core/verse/seat-readiness.js.map +1 -1
- package/dist/core/verse/verse-api.d.ts +3 -1
- package/dist/core/verse/verse-api.js +6 -0
- package/dist/core/verse/verse-api.js.map +1 -1
- package/dist/core/web/public/next/assets/{ActionMenu-DffwPklw.js → ActionMenu-Dwjk6rUQ.js} +2 -2
- package/dist/core/web/public/next/assets/{ActionMenu-DffwPklw.js.map → ActionMenu-Dwjk6rUQ.js.map} +1 -1
- package/dist/core/web/public/next/assets/Advanced-cVYTAsfB.js +2 -0
- package/dist/core/web/public/next/assets/{Advanced-CxzE6ekM.js.map → Advanced-cVYTAsfB.js.map} +1 -1
- package/dist/core/web/public/next/assets/App-IsaWDsdF.js +7 -0
- package/dist/core/web/public/next/assets/{App-DFlp-vdv.js.map → App-IsaWDsdF.js.map} +1 -1
- package/dist/core/web/public/next/assets/AppsSection-DDqeSrty.js +2 -0
- package/dist/core/web/public/next/assets/{AppsSection-Ct2O605-.js.map → AppsSection-DDqeSrty.js.map} +1 -1
- package/dist/core/web/public/next/assets/{AreaTrend-o-GLl2Is.js → AreaTrend-C32rUaxm.js} +2 -2
- package/dist/core/web/public/next/assets/{AreaTrend-o-GLl2Is.js.map → AreaTrend-C32rUaxm.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomyOffState-CmSSTHVe.css +1 -0
- package/dist/core/web/public/next/assets/AutonomyOffState-CpFYS3tQ.js +3 -0
- package/dist/core/web/public/next/assets/AutonomyOffState-CpFYS3tQ.js.map +1 -0
- package/dist/core/web/public/next/assets/AutonomySection-Dwn8UtOy.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-BTg7yVa1.js.map → AutonomySection-Dwn8UtOy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{BranchBar-BjPFtGsj.js → BranchBar-ISZ7xUCj.js} +2 -2
- package/dist/core/web/public/next/assets/{BranchBar-BjPFtGsj.js.map → BranchBar-ISZ7xUCj.js.map} +1 -1
- package/dist/core/web/public/next/assets/BudgetControl-BARVp-En.js +2 -0
- package/dist/core/web/public/next/assets/BudgetControl-BARVp-En.js.map +1 -0
- package/dist/core/web/public/next/assets/{Button-OjVJSrFq.js → Button-DtE0rlWD.js} +2 -2
- package/dist/core/web/public/next/assets/{Button-OjVJSrFq.js.map → Button-DtE0rlWD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{CapacityStrip-Bx4c3dxD.js → CapacityStrip-DKbKA_Aa.js} +2 -2
- package/dist/core/web/public/next/assets/{CapacityStrip-Bx4c3dxD.js.map → CapacityStrip-DKbKA_Aa.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ChartFrame-B_w2Siqg.js → ChartFrame-DK_BDXCt.js} +2 -2
- package/dist/core/web/public/next/assets/{ChartFrame-B_w2Siqg.js.map → ChartFrame-DK_BDXCt.js.map} +1 -1
- package/dist/core/web/public/next/assets/ChatSection-C9myY4ei.js +3 -0
- package/dist/core/web/public/next/assets/ChatSection-C9myY4ei.js.map +1 -0
- package/dist/core/web/public/next/assets/{CloudBudgetForm-5Gf4iAok.js → CloudBudgetForm-CvjK4Oh6.js} +2 -2
- package/dist/core/web/public/next/assets/{CloudBudgetForm-5Gf4iAok.js.map → CloudBudgetForm-CvjK4Oh6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{CommandPalette-iTpk6I8r.js → CommandPalette-DzPsth0u.js} +2 -2
- package/dist/core/web/public/next/assets/{CommandPalette-iTpk6I8r.js.map → CommandPalette-DzPsth0u.js.map} +1 -1
- package/dist/core/web/public/next/assets/CommandSection-Brp73iox.js +3 -0
- package/dist/core/web/public/next/assets/CommandSection-Brp73iox.js.map +1 -0
- package/dist/core/web/public/next/assets/{ConfirmDialog-_S9iE-cJ.js → ConfirmDialog-CLUhzaBj.js} +2 -2
- package/dist/core/web/public/next/assets/{ConfirmDialog-_S9iE-cJ.js.map → ConfirmDialog-CLUhzaBj.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DeleteChatDialog-KUYUygiA.js → DeleteChatDialog-C_eDhV8b.js} +2 -2
- package/dist/core/web/public/next/assets/{DeleteChatDialog-KUYUygiA.js.map → DeleteChatDialog-C_eDhV8b.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Dialog-BuIaG6HF.js → Dialog-BRd_B9OE.js} +2 -2
- package/dist/core/web/public/next/assets/{Dialog-BuIaG6HF.js.map → Dialog-BRd_B9OE.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffPane-De4IQt82.js → DiffPane-Cpr9soB2.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffPane-De4IQt82.js.map → DiffPane-Cpr9soB2.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-B75t6Ile.js → DiffViewer-CNwd0W3Q.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-B75t6Ile.js.map → DiffViewer-CNwd0W3Q.js.map} +1 -1
- package/dist/core/web/public/next/assets/DockHost-BcEq2qaS.js +2 -0
- package/dist/core/web/public/next/assets/{DockHost-BPqyhtCP.js.map → DockHost-BcEq2qaS.js.map} +1 -1
- package/dist/core/web/public/next/assets/{EmptyState-DXkT4vhW.js → EmptyState-DsN1H6MC.js} +2 -2
- package/dist/core/web/public/next/assets/{EmptyState-DXkT4vhW.js.map → EmptyState-DsN1H6MC.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-CfqFe_aP.js → Epistemic-fycQTBRO.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-CfqFe_aP.js.map → Epistemic-fycQTBRO.js.map} +1 -1
- package/dist/core/web/public/next/assets/EvidenceTimeline-PBDAiSuG.css +1 -0
- package/dist/core/web/public/next/assets/EvidenceTimeline-sCCAdnBK.js +2 -0
- package/dist/core/web/public/next/assets/EvidenceTimeline-sCCAdnBK.js.map +1 -0
- package/dist/core/web/public/next/assets/{FleetSection-B7f-XBiX.js → FleetSection-BG8iGraG.js} +3 -3
- package/dist/core/web/public/next/assets/{FleetSection-B7f-XBiX.js.map → FleetSection-BG8iGraG.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Funnel-pXYySwXQ.js → Funnel-B88_wpA4.js} +2 -2
- package/dist/core/web/public/next/assets/{Funnel-pXYySwXQ.js.map → Funnel-B88_wpA4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{GearTray-gj6lT7ba.js → GearTray-CpDt0xwj.js} +2 -2
- package/dist/core/web/public/next/assets/{GearTray-gj6lT7ba.js.map → GearTray-CpDt0xwj.js.map} +1 -1
- package/dist/core/web/public/next/assets/{GrowthSection-D8e9m9Wy.js → GrowthSection-BvZezOsX.js} +2 -2
- package/dist/core/web/public/next/assets/{GrowthSection-D8e9m9Wy.js.map → GrowthSection-BvZezOsX.js.map} +1 -1
- package/dist/core/web/public/next/assets/{HandoffDialog-VfKxKhxF.js → HandoffDialog-B1G6gQyk.js} +2 -2
- package/dist/core/web/public/next/assets/{HandoffDialog-VfKxKhxF.js.map → HandoffDialog-B1G6gQyk.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Input-u1YR_Ogl.js → Input-CI0yBHmn.js} +2 -2
- package/dist/core/web/public/next/assets/{Input-u1YR_Ogl.js.map → Input-CI0yBHmn.js.map} +1 -1
- package/dist/core/web/public/next/assets/{LeaderCard-BDELBrnx.js → LeaderCard-BxxiEg4e.js} +2 -2
- package/dist/core/web/public/next/assets/{LeaderCard-BDELBrnx.js.map → LeaderCard-BxxiEg4e.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MatrixHeatmap-NKLF2srR.js → MatrixHeatmap-iPFEU2Tv.js} +2 -2
- package/dist/core/web/public/next/assets/{MatrixHeatmap-NKLF2srR.js.map → MatrixHeatmap-iPFEU2Tv.js.map} +1 -1
- package/dist/core/web/public/next/assets/McpSection-nuXer4K-.js +2 -0
- package/dist/core/web/public/next/assets/{McpSection-D1tDvlRa.js.map → McpSection-nuXer4K-.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdown-DlfLEJxu.js → MessageMarkdown-BCe4TsXk.js} +3 -3
- package/dist/core/web/public/next/assets/{MessageMarkdown-DlfLEJxu.js.map → MessageMarkdown-BCe4TsXk.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-BYZzI115.js → MessageMarkdownRenderer-DL3LU5kL.js} +2 -2
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-BYZzI115.js.map → MessageMarkdownRenderer-DL3LU5kL.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-BSVhU43Q.js → Meter-b69LM0mU.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-BSVhU43Q.js.map → Meter-b69LM0mU.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MindSection-lqV2e_Sq.js → MindSection-Dc5v5rH3.js} +2 -2
- package/dist/core/web/public/next/assets/{MindSection-lqV2e_Sq.js.map → MindSection-Dc5v5rH3.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MutationTokenDialog-CLGP72g6.js → MutationTokenDialog-Bm34BNVV.js} +2 -2
- package/dist/core/web/public/next/assets/{MutationTokenDialog-CLGP72g6.js.map → MutationTokenDialog-Bm34BNVV.js.map} +1 -1
- package/dist/core/web/public/next/assets/NeedsYouDrawer-N1cWJjpz.js +3 -0
- package/dist/core/web/public/next/assets/NeedsYouDrawer-N1cWJjpz.js.map +1 -0
- package/dist/core/web/public/next/assets/NeedsYouList-CH6Mq-E_.css +1 -0
- package/dist/core/web/public/next/assets/NeedsYouList-D5Kk549g.js +2 -0
- package/dist/core/web/public/next/assets/{NeedsYouList-eBOlJr0b.js.map → NeedsYouList-D5Kk549g.js.map} +1 -1
- package/dist/core/web/public/next/assets/{NewChatDialog-DoYTH26M.js → NewChatDialog-BqFTWgHU.js} +2 -2
- package/dist/core/web/public/next/assets/{NewChatDialog-DoYTH26M.js.map → NewChatDialog-BqFTWgHU.js.map} +1 -1
- package/dist/core/web/public/next/assets/OnboardingFlow-DcDsguAM.js +3 -0
- package/dist/core/web/public/next/assets/OnboardingFlow-DcDsguAM.js.map +1 -0
- package/dist/core/web/public/next/assets/OnboardingGate-BZafM1Vf.js +3 -0
- package/dist/core/web/public/next/assets/{OnboardingGate-DrDEVBqw.js.map → OnboardingGate-BZafM1Vf.js.map} +1 -1
- package/dist/core/web/public/next/assets/{PreviewPane-ULN673zm.js → PreviewPane-zT3gK9TD.js} +2 -2
- package/dist/core/web/public/next/assets/{PreviewPane-ULN673zm.js.map → PreviewPane-zT3gK9TD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ProviderLogo-8WaUjTlN.js → ProviderLogo-AwHn2sKm.js} +2 -2
- package/dist/core/web/public/next/assets/{ProviderLogo-8WaUjTlN.js.map → ProviderLogo-AwHn2sKm.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RailStatus-Ckw9nvYU.js → RailStatus-xHrovWvI.js} +2 -2
- package/dist/core/web/public/next/assets/{RailStatus-Ckw9nvYU.js.map → RailStatus-xHrovWvI.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RefreshIndicator-DGubF4vR.js → RefreshIndicator-SEFZlMB2.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-DGubF4vR.js.map → RefreshIndicator-SEFZlMB2.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BahEaY4d.js → ResourcePoolConsoleApp-CAxPQ4sZ.js} +3 -3
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BahEaY4d.js.map → ResourcePoolConsoleApp-CAxPQ4sZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/ResourcesChrome-BnhnEdJe.js +3 -0
- package/dist/core/web/public/next/assets/{ResourcesChrome-C2ZrA7la.js.map → ResourcesChrome-BnhnEdJe.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcesDrawer-BC93fzwQ.js → ResourcesDrawer-BAFGodny.js} +2 -2
- package/dist/core/web/public/next/assets/{ResourcesDrawer-BC93fzwQ.js.map → ResourcesDrawer-BAFGodny.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-BvhHNpE_.js → RouteErrorBoundary-C8WGiixc.js} +2 -2
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-BvhHNpE_.js.map → RouteErrorBoundary-C8WGiixc.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RunInCloudAction-Dywfnw0C.js → RunInCloudAction-1-IPLYhf.js} +2 -2
- package/dist/core/web/public/next/assets/{RunInCloudAction-Dywfnw0C.js.map → RunInCloudAction-1-IPLYhf.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SeatSelector-CH7hTgwy.js → SeatSelector-DpwNiX8F.js} +2 -2
- package/dist/core/web/public/next/assets/{SeatSelector-CH7hTgwy.js.map → SeatSelector-DpwNiX8F.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Segmented-Wynbg0F5.js → Segmented-Wt1ATod9.js} +2 -2
- package/dist/core/web/public/next/assets/{Segmented-Wynbg0F5.js.map → Segmented-Wt1ATod9.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Select-DMyLCkId.js → Select-By-V9lMc.js} +2 -2
- package/dist/core/web/public/next/assets/{Select-DMyLCkId.js.map → Select-By-V9lMc.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SessionGate-8RtQFWeZ.js → SessionGate-WROS02tK.js} +2 -2
- package/dist/core/web/public/next/assets/{SessionGate-8RtQFWeZ.js.map → SessionGate-WROS02tK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SessionInsightChip-zBJbKv2L.js → SessionInsightChip-DNUcruwp.js} +2 -2
- package/dist/core/web/public/next/assets/{SessionInsightChip-zBJbKv2L.js.map → SessionInsightChip-DNUcruwp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SessionSearch-DfJIcxxM.js → SessionSearch-UHtt7a8f.js} +2 -2
- package/dist/core/web/public/next/assets/{SessionSearch-DfJIcxxM.js.map → SessionSearch-UHtt7a8f.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SettingsSection-Da9RYyH5.js → SettingsSection-Bf74l0Gj.js} +2 -2
- package/dist/core/web/public/next/assets/{SettingsSection-Da9RYyH5.js.map → SettingsSection-Bf74l0Gj.js.map} +1 -1
- package/dist/core/web/public/next/assets/SetupChecklist-BhZpFLsj.css +1 -0
- package/dist/core/web/public/next/assets/SetupChecklist-CzpEcfOl.js +2 -0
- package/dist/core/web/public/next/assets/SetupChecklist-CzpEcfOl.js.map +1 -0
- package/dist/core/web/public/next/assets/{Sheet-DhXyIDmq.js → Sheet-HkBOBMTT.js} +2 -2
- package/dist/core/web/public/next/assets/{Sheet-DhXyIDmq.js.map → Sheet-HkBOBMTT.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-Bi5f-wUw.js → ShortcutsOverlay-DiigiT8H.js} +2 -2
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-Bi5f-wUw.js.map → ShortcutsOverlay-DiigiT8H.js.map} +1 -1
- package/dist/core/web/public/next/assets/Sidebar-C9YtBYVZ.js +3 -0
- package/dist/core/web/public/next/assets/Sidebar-C9YtBYVZ.js.map +1 -0
- package/dist/core/web/public/next/assets/{Slider-Bd9YFWEV.js → Slider-DaEDws-b.js} +2 -2
- package/dist/core/web/public/next/assets/{Slider-Bd9YFWEV.js.map → Slider-DaEDws-b.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatTile-DScTd3c-.js → StatTile-CTX41O6y.js} +2 -2
- package/dist/core/web/public/next/assets/{StatTile-DScTd3c-.js.map → StatTile-CTX41O6y.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatusBadge-DQ1UtQAJ.js → StatusBadge-CXG3iNL4.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-DQ1UtQAJ.js.map → StatusBadge-CXG3iNL4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StepBand-BB3IJxhr.js → StepBand-jJTqYIdX.js} +2 -2
- package/dist/core/web/public/next/assets/{StepBand-BB3IJxhr.js.map → StepBand-jJTqYIdX.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Surface-DWMtQe3Q.js → Surface-5bNA08tK.js} +2 -2
- package/dist/core/web/public/next/assets/{Surface-DWMtQe3Q.js.map → Surface-5bNA08tK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Swimlane-DcENqtdp.js → Swimlane-LrslsGxw.js} +2 -2
- package/dist/core/web/public/next/assets/{Swimlane-DcENqtdp.js.map → Swimlane-LrslsGxw.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Switch-CenChEYf.js → Switch-BmCzfFnu.js} +2 -2
- package/dist/core/web/public/next/assets/{Switch-CenChEYf.js.map → Switch-BmCzfFnu.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Tag-D4k5E3u0.js → Tag-WSUIaMVy.js} +2 -2
- package/dist/core/web/public/next/assets/{Tag-D4k5E3u0.js.map → Tag-WSUIaMVy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{TerminalPane-DCQTukcE.js → TerminalPane-BVFgYX6K.js} +5 -5
- package/dist/core/web/public/next/assets/{TerminalPane-DCQTukcE.js.map → TerminalPane-BVFgYX6K.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DsuWT0pi.js → UniverseConsoleApp-BpphLqoz.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DsuWT0pi.js.map → UniverseConsoleApp-BpphLqoz.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-DyI5ApsA.js → UniverseView-DbpbSMwO.js} +4 -4
- package/dist/core/web/public/next/assets/{UniverseView-DyI5ApsA.js.map → UniverseView-DbpbSMwO.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UsageSection-Imr28nwK.js → UsageSection-CUlaKtws.js} +3 -3
- package/dist/core/web/public/next/assets/{UsageSection-Imr28nwK.js.map → UsageSection-CUlaKtws.js.map} +1 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-DXDv1aOi.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-DXDv1aOi.js.map +1 -0
- package/dist/core/web/public/next/assets/{Workspace-CpxBO98E.js → Workspace-RXy0pqp6.js} +5 -5
- package/dist/core/web/public/next/assets/{Workspace-CpxBO98E.js.map → Workspace-RXy0pqp6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{actions-DE8NoWV_.js → actions-BZTidCXZ.js} +2 -2
- package/dist/core/web/public/next/assets/{actions-DE8NoWV_.js.map → actions-BZTidCXZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{apps-model-CFGCUu9p.js → apps-model-CHO133qt.js} +2 -2
- package/dist/core/web/public/next/assets/{apps-model-CFGCUu9p.js.map → apps-model-CHO133qt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{authority-model-CmnfJzmM.js → authority-model-8NeAEwAF.js} +2 -2
- package/dist/core/web/public/next/assets/{authority-model-CmnfJzmM.js.map → authority-model-8NeAEwAF.js.map} +1 -1
- package/dist/core/web/public/next/assets/{budget-queries-DjrPRFwl.js → budget-queries-DFF-3ICg.js} +2 -2
- package/dist/core/web/public/next/assets/{budget-queries-DjrPRFwl.js.map → budget-queries-DFF-3ICg.js.map} +1 -1
- package/dist/core/web/public/next/assets/{capacity-strip-model-C7cdQnuK.js → capacity-strip-model-5ViUgUlP.js} +2 -2
- package/dist/core/web/public/next/assets/{capacity-strip-model-C7cdQnuK.js.map → capacity-strip-model-5ViUgUlP.js.map} +1 -1
- package/dist/core/web/public/next/assets/{charts-DUD_jprE.js → charts-CzxWrZn7.js} +2 -2
- package/dist/core/web/public/next/assets/{charts-DUD_jprE.js.map → charts-CzxWrZn7.js.map} +1 -1
- package/dist/core/web/public/next/assets/cloud-model-CfRydq8N.js +2 -0
- package/dist/core/web/public/next/assets/{cloud-model-DoxBMDvx.js.map → cloud-model-CfRydq8N.js.map} +1 -1
- package/dist/core/web/public/next/assets/{cloud-queries-CTGvO-k-.js → cloud-queries-DmptbjS8.js} +2 -2
- package/dist/core/web/public/next/assets/{cloud-queries-CTGvO-k-.js.map → cloud-queries-DmptbjS8.js.map} +1 -1
- package/dist/core/web/public/next/assets/{command-catalog-CmrHNFzh.js → command-catalog-ffCAijVY.js} +2 -2
- package/dist/core/web/public/next/assets/{command-catalog-CmrHNFzh.js.map → command-catalog-ffCAijVY.js.map} +1 -1
- package/dist/core/web/public/next/assets/{context-model-2Owp2jYF.js → context-model-Bb15chFt.js} +2 -2
- package/dist/core/web/public/next/assets/{context-model-2Owp2jYF.js.map → context-model-Bb15chFt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{context-model-9xywbXxX.js → context-model-CDeyP0cA.js} +2 -2
- package/dist/core/web/public/next/assets/{context-model-9xywbXxX.js.map → context-model-CDeyP0cA.js.map} +1 -1
- package/dist/core/web/public/next/assets/{context-queries-wvcRnV-I.js → context-queries-_9bFWb9v.js} +2 -2
- package/dist/core/web/public/next/assets/{context-queries-wvcRnV-I.js.map → context-queries-_9bFWb9v.js.map} +1 -1
- package/dist/core/web/public/next/assets/{control-queries-DB6Kh7UN.js → control-queries-CXtmZZH6.js} +2 -2
- package/dist/core/web/public/next/assets/{control-queries-DB6Kh7UN.js.map → control-queries-CXtmZZH6.js.map} +1 -1
- package/dist/core/web/public/next/assets/copy-setup-BIp0iaiZ.js +2 -0
- package/dist/core/web/public/next/assets/copy-setup-BIp0iaiZ.js.map +1 -0
- package/dist/core/web/public/next/assets/{dock-icons-BBUGl4tE.js → dock-icons-Cc43W7r4.js} +2 -2
- package/dist/core/web/public/next/assets/{dock-icons-BBUGl4tE.js.map → dock-icons-Cc43W7r4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{fleet-model-BRfRWkZl.js → fleet-model-DOK8ZtEt.js} +2 -2
- package/dist/core/web/public/next/assets/{fleet-model-BRfRWkZl.js.map → fleet-model-DOK8ZtEt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{fleet-queries-tU6UEXN2.js → fleet-queries-BeEAqD6y.js} +2 -2
- package/dist/core/web/public/next/assets/{fleet-queries-tU6UEXN2.js.map → fleet-queries-BeEAqD6y.js.map} +1 -1
- package/dist/core/web/public/next/assets/{focus-trap-BGOceFvj.js → focus-trap-ChZ5EFfM.js} +2 -2
- package/dist/core/web/public/next/assets/{focus-trap-BGOceFvj.js.map → focus-trap-ChZ5EFfM.js.map} +1 -1
- package/dist/core/web/public/next/assets/format-BmH2yWL6.js +2 -0
- package/dist/core/web/public/next/assets/format-BmH2yWL6.js.map +1 -0
- package/dist/core/web/public/next/assets/{git-model-53iRLInY.js → git-model-BFwAVbxF.js} +2 -2
- package/dist/core/web/public/next/assets/{git-model-53iRLInY.js.map → git-model-BFwAVbxF.js.map} +1 -1
- package/dist/core/web/public/next/assets/global-y_exIyAP.js +2 -0
- package/dist/core/web/public/next/assets/{global-CXHHMz9o.js.map → global-y_exIyAP.js.map} +1 -1
- package/dist/core/web/public/next/assets/{guarded-runners-B8zFVBVZ.js → guarded-runners-Cd4POBtI.js} +2 -2
- package/dist/core/web/public/next/assets/{guarded-runners-B8zFVBVZ.js.map → guarded-runners-Cd4POBtI.js.map} +1 -1
- package/dist/core/web/public/next/assets/{icons-D4s-jlxX.js → icons-CygCEFWs.js} +2 -2
- package/dist/core/web/public/next/assets/{icons-D4s-jlxX.js.map → icons-CygCEFWs.js.map} +1 -1
- package/dist/core/web/public/next/assets/{index-BG177cPN.js → index-CyK31YRm.js} +3 -3
- package/dist/core/web/public/next/assets/{index-BG177cPN.js.map → index-CyK31YRm.js.map} +1 -1
- package/dist/core/web/public/next/assets/{live-model-CEf01wrW.js → live-model-D7hilYFk.js} +2 -2
- package/dist/core/web/public/next/assets/live-model-D7hilYFk.js.map +1 -0
- package/dist/core/web/public/next/assets/{mind.module-D_xk6HIQ.js → mind.module-B15k0Y9q.js} +2 -2
- package/dist/core/web/public/next/assets/{mind.module-D_xk6HIQ.js.map → mind.module-B15k0Y9q.js.map} +1 -1
- package/dist/core/web/public/next/assets/mutations-CWlxEMEm.js +2 -0
- package/dist/core/web/public/next/assets/{mutations-CbTSofVH.js.map → mutations-CWlxEMEm.js.map} +1 -1
- package/dist/core/web/public/next/assets/{nav-CmZcbPFJ.js → nav-mimleI0R.js} +2 -2
- package/dist/core/web/public/next/assets/{nav-CmZcbPFJ.js.map → nav-mimleI0R.js.map} +1 -1
- package/dist/core/web/public/next/assets/{overnight-queries-CR0b_j-V.js → overnight-queries-DJ4mZKSj.js} +2 -2
- package/dist/core/web/public/next/assets/{overnight-queries-CR0b_j-V.js.map → overnight-queries-DJ4mZKSj.js.map} +1 -1
- package/dist/core/web/public/next/assets/primitives-DMijqcu0.js +1 -0
- package/dist/core/web/public/next/assets/{queries-rvRsAGW2.js → queries-Bw1McHTj.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-rvRsAGW2.js.map → queries-Bw1McHTj.js.map} +1 -1
- package/dist/core/web/public/next/assets/{reasoning-pref-DMPQkujg.js → reasoning-pref-KkhI_5-j.js} +2 -2
- package/dist/core/web/public/next/assets/{reasoning-pref-DMPQkujg.js.map → reasoning-pref-KkhI_5-j.js.map} +1 -1
- package/dist/core/web/public/next/assets/{reveal-anchor-BKk2wZZZ.js → reveal-anchor-CCCx_kfT.js} +2 -2
- package/dist/core/web/public/next/assets/{reveal-anchor-BKk2wZZZ.js.map → reveal-anchor-CCCx_kfT.js.map} +1 -1
- package/dist/core/web/public/next/assets/{seat-subscription-CLn2ReSZ.js → seat-subscription-BhUUz7VL.js} +2 -2
- package/dist/core/web/public/next/assets/{seat-subscription-CLn2ReSZ.js.map → seat-subscription-BhUUz7VL.js.map} +1 -1
- package/dist/core/web/public/next/assets/{sidebar-model-CjQLvdag.js → sidebar-model-Dcd437cf.js} +2 -2
- package/dist/core/web/public/next/assets/{sidebar-model-CjQLvdag.js.map → sidebar-model-Dcd437cf.js.map} +1 -1
- package/dist/core/web/public/next/assets/{slots-DRxNosZg.js → slots-BL_J5mmV.js} +3 -3
- package/dist/core/web/public/next/assets/{slots-DRxNosZg.js.map → slots-BL_J5mmV.js.map} +1 -1
- package/dist/core/web/public/next/assets/{surface-data-DshUC_TU.js → surface-data-C3UAQ4QX.js} +2 -2
- package/dist/core/web/public/next/assets/{surface-data-DshUC_TU.js.map → surface-data-C3UAQ4QX.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-prefetch-BXKjPsV5.js +2 -0
- package/dist/core/web/public/next/assets/{surface-prefetch-DO8r4ttQ.js.map → surface-prefetch-BXKjPsV5.js.map} +1 -1
- package/dist/core/web/public/next/assets/{tasks-model-BKPFZAmZ.js → tasks-model-CzHvclVm.js} +2 -2
- package/dist/core/web/public/next/assets/{tasks-model-BKPFZAmZ.js.map → tasks-model-CzHvclVm.js.map} +1 -1
- package/dist/core/web/public/next/assets/{terminal-icons-DoeQkP3y.js → terminal-icons-BwgQTQP0.js} +2 -2
- package/dist/core/web/public/next/assets/{terminal-icons-DoeQkP3y.js.map → terminal-icons-BwgQTQP0.js.map} +1 -1
- package/dist/core/web/public/next/assets/types-IYKrYLku.js +2 -0
- package/dist/core/web/public/next/assets/types-IYKrYLku.js.map +1 -0
- package/dist/core/web/public/next/assets/{usage-queries-MnUeYzwp.js → usage-queries-pXxvy54x.js} +2 -2
- package/dist/core/web/public/next/assets/{usage-queries-MnUeYzwp.js.map → usage-queries-pXxvy54x.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-guarded-action-P12MfNwU.js → use-guarded-action-EgVWjM5c.js} +2 -2
- package/dist/core/web/public/next/assets/{use-guarded-action-P12MfNwU.js.map → use-guarded-action-EgVWjM5c.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-ticker-DIvAZ4Ey.js → use-ticker-BoqFxRFG.js} +2 -2
- package/dist/core/web/public/next/assets/{use-ticker-DIvAZ4Ey.js.map → use-ticker-BoqFxRFG.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-token-gate-DfEja7Wr.js → use-token-gate-DSNIIpI6.js} +2 -2
- package/dist/core/web/public/next/assets/{use-token-gate-DfEja7Wr.js.map → use-token-gate-DSNIIpI6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{useOnboarding-CkOHadqC.js → useOnboarding-Bk0VnkxQ.js} +2 -2
- package/dist/core/web/public/next/assets/{useOnboarding-CkOHadqC.js.map → useOnboarding-Bk0VnkxQ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{useSeatsRefresh-BDlbDlIU.js → useSeatsRefresh-q7Id9RcW.js} +2 -2
- package/dist/core/web/public/next/assets/{useSeatsRefresh-BDlbDlIU.js.map → useSeatsRefresh-q7Id9RcW.js.map} +1 -1
- package/dist/core/web/public/next/assets/{useVerseTranscript-BGtA55k2.js → useVerseTranscript-CqH51rt9.js} +2 -2
- package/dist/core/web/public/next/assets/{useVerseTranscript-BGtA55k2.js.map → useVerseTranscript-CqH51rt9.js.map} +1 -1
- package/dist/core/web/public/next/assets/{verse-icons-wOet9JZy.js → verse-icons-DsG1dOQx.js} +2 -2
- package/dist/core/web/public/next/assets/{verse-icons-wOet9JZy.js.map → verse-icons-DsG1dOQx.js.map} +1 -1
- package/dist/core/web/public/next/assets/{verse-model-CSzzdyu2.js → verse-model-JTuvRBVK.js} +2 -2
- package/dist/core/web/public/next/assets/{verse-model-CSzzdyu2.js.map → verse-model-JTuvRBVK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{warmup-CecO_Nk0.js → warmup-Bv1RumVq.js} +3 -3
- package/dist/core/web/public/next/assets/{warmup-CecO_Nk0.js.map → warmup-Bv1RumVq.js.map} +1 -1
- package/dist/core/web/public/next/assets/why-seat-model-Cau8qG6O.js +2 -0
- package/dist/core/web/public/next/assets/why-seat-model-Cau8qG6O.js.map +1 -0
- package/dist/core/web/public/next/assets/{workspace-model-BdIVybKr.js → workspace-model-D-SLfads.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-BdIVybKr.js.map → workspace-model-D-SLfads.js.map} +1 -1
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/docs/AUTONOMY-GAP.md +1 -1
- package/package.json +1 -1
- package/dist/core/web/public/next/assets/Advanced-CxzE6ekM.js +0 -2
- package/dist/core/web/public/next/assets/App-DFlp-vdv.js +0 -7
- package/dist/core/web/public/next/assets/AppsSection-Ct2O605-.js +0 -2
- package/dist/core/web/public/next/assets/AutonomyOffState-CXhXpsim.js +0 -2
- package/dist/core/web/public/next/assets/AutonomyOffState-CXhXpsim.js.map +0 -1
- package/dist/core/web/public/next/assets/AutonomyOffState-DmbE71vI.css +0 -1
- package/dist/core/web/public/next/assets/AutonomySection-BTg7yVa1.js +0 -2
- package/dist/core/web/public/next/assets/BudgetControl-ng6swoZp.js +0 -2
- package/dist/core/web/public/next/assets/BudgetControl-ng6swoZp.js.map +0 -1
- package/dist/core/web/public/next/assets/ChatSection-Bisj9R6E.js +0 -3
- package/dist/core/web/public/next/assets/ChatSection-Bisj9R6E.js.map +0 -1
- package/dist/core/web/public/next/assets/CommandSection-D8ZihzvE.js +0 -2
- package/dist/core/web/public/next/assets/CommandSection-D8ZihzvE.js.map +0 -1
- package/dist/core/web/public/next/assets/DockHost-BPqyhtCP.js +0 -2
- package/dist/core/web/public/next/assets/McpSection-D1tDvlRa.js +0 -2
- package/dist/core/web/public/next/assets/NeedsYouDrawer-D992XTOn.js +0 -2
- package/dist/core/web/public/next/assets/NeedsYouDrawer-D992XTOn.js.map +0 -1
- package/dist/core/web/public/next/assets/NeedsYouList-CruOt4JT.css +0 -1
- package/dist/core/web/public/next/assets/NeedsYouList-eBOlJr0b.js +0 -2
- package/dist/core/web/public/next/assets/OnboardingFlow-D3yfITK1.js +0 -2
- package/dist/core/web/public/next/assets/OnboardingFlow-D3yfITK1.js.map +0 -1
- package/dist/core/web/public/next/assets/OnboardingGate-DrDEVBqw.js +0 -3
- package/dist/core/web/public/next/assets/ResourcesChrome-C2ZrA7la.js +0 -3
- package/dist/core/web/public/next/assets/Sidebar-BuO3sViG.js +0 -3
- package/dist/core/web/public/next/assets/Sidebar-BuO3sViG.js.map +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-DeuBEvAO.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-DeuBEvAO.js.map +0 -1
- package/dist/core/web/public/next/assets/cloud-model-DoxBMDvx.js +0 -2
- package/dist/core/web/public/next/assets/copy-setup-CJQhFl-4.js +0 -2
- package/dist/core/web/public/next/assets/copy-setup-CJQhFl-4.js.map +0 -1
- package/dist/core/web/public/next/assets/format-DEAaVF6X.js +0 -2
- package/dist/core/web/public/next/assets/format-DEAaVF6X.js.map +0 -1
- package/dist/core/web/public/next/assets/global-CXHHMz9o.js +0 -2
- package/dist/core/web/public/next/assets/live-model-CEf01wrW.js.map +0 -1
- package/dist/core/web/public/next/assets/mutations-CbTSofVH.js +0 -2
- package/dist/core/web/public/next/assets/primitives-DNI-uJBd.js +0 -1
- package/dist/core/web/public/next/assets/surface-prefetch-DO8r4ttQ.js +0 -2
- package/dist/core/web/public/next/assets/types-D5XOdyw8.js +0 -2
- package/dist/core/web/public/next/assets/types-D5XOdyw8.js.map +0 -1
- package/dist/core/web/public/next/assets/why-seat-model-D68ZA7G8.js +0 -2
- package/dist/core/web/public/next/assets/why-seat-model-D68ZA7G8.js.map +0 -1
package/dist/core/web/public/next/assets/{cloud-model-DoxBMDvx.js.map → cloud-model-CfRydq8N.js.map}
RENAMED
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"cloud-model-CfRydq8N.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 /** Self-improvement waits while this many of its PRs await review (3.13). */\n selfImproveMaxOpen: 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: 'selfImproveMaxOpen', label: 'Open self-improvement PRs', hint: 'Self-improvement waits while this many await review.', money: false, integer: true, min: 1, max: 50 },\n { field: 'reserve', label: 'Reserve', hint: 'Self-improvement stops under this estimated balance.', money: true, integer: false, min: 0, max: 100_000 },\n];\n\n/** A pre-3.13 server sends no maxOpenPrs: the form shows the default and never sends it unchanged. */\nfunction maxOpenPrsOf(b: CloudBudgetV1): number {\n const n = (b.selfImprove as Partial<CloudBudgetV1['selfImprove']>).maxOpenPrs;\n return typeof n === 'number' && Number.isFinite(n) ? n : DEFAULT_CLOUD_BUDGET.selfImprove.maxOpenPrs;\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 selfImproveMaxOpen: numText(maxOpenPrsOf(b)),\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.selfImproveMaxOpen!, maxOpenPrsOf(current))) selfImprove.maxOpenPrs = values.selfImproveMaxOpen!;\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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"}
|
package/dist/core/web/public/next/assets/{cloud-queries-CTGvO-k-.js → cloud-queries-DmptbjS8.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=cloud-queries-
|
|
1
|
+
import{D as e,F as t,U as n,c as r,l as i,o as a}from"./global-y_exIyAP.js";import{F as o}from"./VerseConsoleApp-DXDv1aOi.js";import{c as s,d as c,l,o as u,r as d,s as f,u as p}from"./types-IYKrYLku.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(n,r){let a=e();if(!a)throw new g;let o=await i(n,r,a);return t(),o}function w(){n(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 t=await C(u,e);return n(m),t}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-DmptbjS8.js.map
|
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"cloud-queries-DmptbjS8.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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"}
|
package/dist/core/web/public/next/assets/{command-catalog-CmrHNFzh.js → command-catalog-ffCAijVY.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{s as e}from"./index-
|
|
2
|
-
//# sourceMappingURL=command-catalog-
|
|
1
|
+
import{s as e}from"./index-CyK31YRm.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-DXDv1aOi.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.select`),title:`Select item`,group:null,section:`Needs you`,note:`A / R / E then act on every selected item`},{...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-ffCAijVY.js.map
|
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"command-catalog-ffCAijVY.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.select'), title: 'Select item', group: null, section: 'Needs you', note: 'A / R / E then act on every selected item' },\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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"}
|
package/dist/core/web/public/next/assets/{context-model-2Owp2jYF.js → context-model-Bb15chFt.js}
RENAMED
|
@@ -1,3 +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-
|
|
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-JTuvRBVK.js";import"./transient-events-nTLhMBND.js";import{_ as l,t as u,y as d}from"./format-BmH2yWL6.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
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-
|
|
3
|
+
//# sourceMappingURL=context-model-Bb15chFt.js.map
|