chainlesschain 0.165.6 → 0.165.9
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/package.json +2 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-DxwjrnEA.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-JdbYowzn.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-C06GCahv.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BUVEEyi1.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-D_ORzkJd.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-8o_Y5ejG.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-DrkHd5VU.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-CKBZCX11.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-C8HkI399.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-zb2H2Wux.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BDcNpU_F.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-MwPtQmNz.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-3p5QCkFK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-BPLlhn52.js} +1 -1
- package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-C-3dBNOM.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-BUcy-F_b.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-CI8NxT8m.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-DaDEBS-Q.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BSv9HXu5.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-yuiKlJmv.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-D6LfldB0.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-CBhLqG61.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-BG8pIl3O.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-CY92VIE6.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-1Q_ehauM.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-D3XirJIU.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DfI9UuGZ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-IeCZk2m7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-DzK4IJAZ.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-BHf5KfmQ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-BmaxSALS.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-BgePFLA9.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-nqgxpOc4.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-xkX6izBt.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Bu8to-86.js} +1 -1
- package/src/assets/web-panel/assets/McpTools-BFWTzEk0.js +4 -0
- package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-B026G2GA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-CM2Eeg6I.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-B79Ob3hy.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CxevSZuV.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-X396cPEX.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-HWIB_aZ1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-gnYD1Xwy.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-Dn2Gf_J1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-BRKBQ5rq.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-Dy___Cgo.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-cqfccxnt.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CSX4FHN3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-Bwa4aghp.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-Dhszyjig.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-CXzTyk6e.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-CFCAAJbb.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-BPJbx6xt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-BbiXvEHz.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-B-s7-wSD.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-Dyug206y.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-BtAI5Fwo.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-JpIPmz6N.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-CZdoN64A.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-BwvnoRIT.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-ASMlMQrD.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-BiJzKFKi.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-C80TWLIO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-C1VNmjmX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-T2MyGUlG.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-DXM6O83L.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-nv69kU7W.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-AyHWe7Qj.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D6PWLxFI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BqVeOmNQ.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-BegIPl9M.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BHUI7cox.js → SpeechSettings-CKJn3xk-.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-Th4cnAMl.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BuKfBroh.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-CMuWN8Ac.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-C4je8E8Q.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-agJESOcO.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-D2qDuVZ_.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-Cvmn99rH.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-D_QYIHH8.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-BmzvQLnX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-D_LopHwH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-DoWl3PhQ.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-5NYcAkxe.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-B-_Q3ewp.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-CHET-gi2.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-C6nvb-n6.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-CTDE9LTX.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-KnemdF8D.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-DUJR3Uek.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-1DqLH1KX.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-CFNTaby8.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-65HO56Bv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-B8RtbSkV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-BHqSqnW7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-BTetEUWd.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-BXWg1crv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-BdiC4LOs.js} +1 -1
- package/src/assets/web-panel/assets/index-BhJtcPWo.js +1 -0
- package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-BuizJo2E.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BwZM1rod.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-C96N9xeL.js} +1 -1
- package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-CHfUdnJH.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-CKvBF3Ov.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-CSD9_I2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-Cc7dbl_p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-CcF0D7UZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-Ceu92a8D.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-CnLpuTAU.js} +1 -1
- package/src/assets/web-panel/assets/index-Cq8MWPDT.js +1 -0
- package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-CquKnw5r.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CyEtHvEE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-DF-fcTvQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-DF1Y0Vuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bissefox.js → index-DJyPeBBk.js} +3 -3
- package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-DP-PbO11.js} +1 -1
- package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-DZgCRMxk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-DfJzS3Qa.js} +1 -1
- package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-DqPpJkte.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-DzReoLyh.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-JGTiDeOW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-NUR0If7O.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-NtAcnJYW.js} +1 -1
- package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-OmuhFYKE.js} +1 -1
- package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-Wlc7o_C8.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-bxTPfdri.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-k6uorYmf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-ox6mIwE2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-rVDD7AAR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-u0h6QqVV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-v-mtxmWH.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-qEt8dI-t.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-DetKPWy-.js} +1 -1
- package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-BeNYeDV2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-C0q0MK_T.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-DLKkrKpE.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-Bw1tWsY8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-DeesBLbd.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-bqLhxHhn.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-DGCXRoQm.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-H-re3Egl.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-BXg4qGX_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-CKLb2KA6.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-CTAhHeyw.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-BQ4pDh5g.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-jrSK6j4j.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-fOQE24pw.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +91 -60
- package/src/commands/git.js +39 -0
- package/src/commands/plugin.js +17 -2
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session.js +11 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/mcp-topic-handlers.js +167 -0
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +701 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-network.js +166 -29
- package/src/lib/agent-router.js +62 -6
- package/src/lib/agent-sandbox.js +7 -1
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-code-bridge.js +159 -54
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/cowork-workflow.js +69 -6
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/external-agent-adapters.js +269 -0
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/ipfs-storage.js +127 -25
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +101 -20
- package/src/lib/mcp-tls.js +103 -3
- package/src/lib/orchestrator.js +47 -0
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +117 -1
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
- package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +151 -6
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +168 -21
- package/src/lib/session-projection.js +244 -4
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/shell-approval.js +7 -5
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workflow-engine.js +42 -11
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +124 -5
- package/src/repl/permission-prompt.js +10 -12
- package/src/runtime/agent-core.js +311 -18
- package/src/runtime/agent-runtime.js +12 -1
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +234 -36
- package/src/runtime/headless-stream.js +181 -6
- package/src/runtime/mcp-config.js +298 -8
- package/src/runtime/policies/agent-policy.js +8 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
- package/src/assets/web-panel/assets/McpTools-DFaIhOfu.js +0 -7
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
|
@@ -22,8 +22,20 @@ import {
|
|
|
22
22
|
mutateSecurityStore,
|
|
23
23
|
readSecurityStore,
|
|
24
24
|
} from "./durable-security-store.js";
|
|
25
|
+
import {
|
|
26
|
+
checkRecordedWorkspaceTrust,
|
|
27
|
+
evaluateWorkspaceTrustDecision,
|
|
28
|
+
projectWorkspaceTrustAudit,
|
|
29
|
+
recordWorkspaceTrustConsent,
|
|
30
|
+
resolveCanonicalWorkspaceRepoIdentity,
|
|
31
|
+
workspaceTrustPathSubject,
|
|
32
|
+
} from "./workspace-trust.js";
|
|
25
33
|
|
|
26
|
-
export const _deps = {
|
|
34
|
+
export const _deps = {
|
|
35
|
+
withFileLock,
|
|
36
|
+
// Test-only seam. Production uses workspace-trust.js's one shared ledger.
|
|
37
|
+
workspaceTrustStorePath: () => null,
|
|
38
|
+
};
|
|
27
39
|
const workspaceAuthorities = new WeakMap();
|
|
28
40
|
|
|
29
41
|
function storePath(opts = {}) {
|
|
@@ -38,6 +50,62 @@ export function projectMcpFingerprint(content) {
|
|
|
38
50
|
return createHash("sha256").update(String(content), "utf-8").digest("hex");
|
|
39
51
|
}
|
|
40
52
|
|
|
53
|
+
const PROJECT_MCP_WORKSPACE_TRUST_SOURCE = "project-mcp";
|
|
54
|
+
|
|
55
|
+
function workspaceTrustStoreOptions(opts = {}) {
|
|
56
|
+
const store =
|
|
57
|
+
opts.workspaceTrustStorePath ||
|
|
58
|
+
process.env.CC_WORKSPACE_TRUST_STORE ||
|
|
59
|
+
_deps.workspaceTrustStorePath() ||
|
|
60
|
+
// Custom source-store callers (notably isolated tests) get an adjacent
|
|
61
|
+
// shared ledger. Normal CLI calls omit storePath and therefore use the
|
|
62
|
+
// single workspace-trust.js default shared by all entry points.
|
|
63
|
+
(opts.storePath
|
|
64
|
+
? path.join(path.dirname(opts.storePath), "workspace-trust-v1.json")
|
|
65
|
+
: null);
|
|
66
|
+
return store ? { storePath: store } : {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function projectMcpWorkspaceTrustContext(file, content, opts = {}) {
|
|
70
|
+
const identity = resolveCanonicalWorkspaceRepoIdentity(
|
|
71
|
+
opts.workspaceRoot || path.dirname(file),
|
|
72
|
+
);
|
|
73
|
+
const subject = workspaceTrustPathSubject(identity, file);
|
|
74
|
+
const fingerprint = projectMcpFingerprint(content);
|
|
75
|
+
return { identity, subject, fingerprint };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function projectMcpWorkspaceRecordKey(context) {
|
|
79
|
+
const subjectDigest = createHash("sha256")
|
|
80
|
+
.update(context.subject, "utf8")
|
|
81
|
+
.digest("hex");
|
|
82
|
+
return `workspace-v1:${context.identity.workspaceId}:${subjectDigest}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function projectMcpDecision(status, context) {
|
|
86
|
+
const decision = evaluateWorkspaceTrustDecision({
|
|
87
|
+
identity: context.identity,
|
|
88
|
+
evidence: [
|
|
89
|
+
{
|
|
90
|
+
source: PROJECT_MCP_WORKSPACE_TRUST_SOURCE,
|
|
91
|
+
consent: status === "trusted" ? "explicit" : "missing",
|
|
92
|
+
fingerprint: context.fingerprint,
|
|
93
|
+
decision:
|
|
94
|
+
status === "trusted"
|
|
95
|
+
? "allow"
|
|
96
|
+
: status === "first-use"
|
|
97
|
+
? "ask"
|
|
98
|
+
: "deny",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
status,
|
|
104
|
+
fingerprint: context.fingerprint,
|
|
105
|
+
workspaceTrust: projectWorkspaceTrustAudit(decision),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
41
109
|
/**
|
|
42
110
|
* Issue an in-process authority only after the caller has verified the exact
|
|
43
111
|
* project file fingerprint. Plain config fields cannot forge this token.
|
|
@@ -50,7 +118,23 @@ export function issueProjectMcpWorkspaceAuthority({
|
|
|
50
118
|
config = {},
|
|
51
119
|
}) {
|
|
52
120
|
const canonicalFile = fs.realpathSync.native(path.resolve(file));
|
|
53
|
-
const
|
|
121
|
+
const workspaceIdentity =
|
|
122
|
+
resolveCanonicalWorkspaceRepoIdentity(workspaceRoot);
|
|
123
|
+
const canonicalRoot = workspaceIdentity.canonicalWorkspaceRoot;
|
|
124
|
+
const trustDecision = evaluateWorkspaceTrustDecision({
|
|
125
|
+
identity: workspaceIdentity,
|
|
126
|
+
evidence: [
|
|
127
|
+
{
|
|
128
|
+
source: PROJECT_MCP_WORKSPACE_TRUST_SOURCE,
|
|
129
|
+
consent: "host-bound",
|
|
130
|
+
fingerprint: projectMcpFingerprint(content),
|
|
131
|
+
decision: "allow",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
});
|
|
135
|
+
if (trustDecision.decision !== "allow") {
|
|
136
|
+
throw new Error("project MCP workspace identity is unavailable");
|
|
137
|
+
}
|
|
54
138
|
const token = Object.freeze({});
|
|
55
139
|
workspaceAuthorities.set(
|
|
56
140
|
token,
|
|
@@ -58,6 +142,9 @@ export function issueProjectMcpWorkspaceAuthority({
|
|
|
58
142
|
file: canonicalFile,
|
|
59
143
|
fingerprint: projectMcpFingerprint(content),
|
|
60
144
|
workspaceRoot: canonicalRoot,
|
|
145
|
+
workspaceId: workspaceIdentity.workspaceId,
|
|
146
|
+
repositoryId: workspaceIdentity.repositoryId,
|
|
147
|
+
workspaceTrust: projectWorkspaceTrustAudit(trustDecision),
|
|
61
148
|
serverName: String(serverName || ""),
|
|
62
149
|
url: typeof config.url === "string" ? config.url : "",
|
|
63
150
|
transport: typeof config.transport === "string" ? config.transport : "",
|
|
@@ -100,6 +187,19 @@ export function resolveProjectMcpWorkspaceAuthority(token, expected = {}) {
|
|
|
100
187
|
) {
|
|
101
188
|
return null;
|
|
102
189
|
}
|
|
190
|
+
try {
|
|
191
|
+
const currentIdentity = resolveCanonicalWorkspaceRepoIdentity(
|
|
192
|
+
record.workspaceRoot,
|
|
193
|
+
);
|
|
194
|
+
if (
|
|
195
|
+
currentIdentity.workspaceId !== record.workspaceId ||
|
|
196
|
+
currentIdentity.repositoryId !== record.repositoryId
|
|
197
|
+
) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
103
203
|
return record.workspaceRoot;
|
|
104
204
|
}
|
|
105
205
|
|
|
@@ -112,29 +212,84 @@ function readStore(opts) {
|
|
|
112
212
|
* @returns {{status:"first-use"|"trusted"|"changed", fingerprint:string}}
|
|
113
213
|
*/
|
|
114
214
|
export function checkProjectMcpTrust(file, content, opts = {}) {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
215
|
+
const context = projectMcpWorkspaceTrustContext(file, content, opts);
|
|
216
|
+
const store = readStore(opts);
|
|
217
|
+
const record = store[projectMcpWorkspaceRecordKey(context)];
|
|
218
|
+
const legacyRecord = store[path.resolve(file)];
|
|
219
|
+
if (!record || typeof record !== "object" || Array.isArray(record)) {
|
|
220
|
+
// A pre-v1 path-keyed grant cannot be shown to bind the current canonical
|
|
221
|
+
// workspace identity. Treat it as changed rather than silently widening
|
|
222
|
+
// it; the existing explicit re-trust path records the v1 intersection.
|
|
223
|
+
const shared = checkRecordedWorkspaceTrust({
|
|
224
|
+
identity: context.identity,
|
|
225
|
+
source: PROJECT_MCP_WORKSPACE_TRUST_SOURCE,
|
|
226
|
+
subject: context.subject,
|
|
227
|
+
evidenceFingerprint: context.fingerprint,
|
|
228
|
+
...workspaceTrustStoreOptions(opts),
|
|
229
|
+
});
|
|
230
|
+
return {
|
|
231
|
+
status:
|
|
232
|
+
legacyRecord?.fingerprint || shared.status === "changed"
|
|
233
|
+
? "changed"
|
|
234
|
+
: "first-use",
|
|
235
|
+
fingerprint: context.fingerprint,
|
|
236
|
+
workspaceTrust: shared.audit,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const binding = record.workspaceTrust;
|
|
240
|
+
if (
|
|
241
|
+
record.fingerprint !== context.fingerprint ||
|
|
242
|
+
!binding ||
|
|
243
|
+
binding.workspaceId !== context.identity.workspaceId ||
|
|
244
|
+
binding.repositoryId !== context.identity.repositoryId ||
|
|
245
|
+
binding.subject !== context.subject
|
|
246
|
+
) {
|
|
247
|
+
return projectMcpDecision("changed", context);
|
|
119
248
|
}
|
|
249
|
+
const shared = checkRecordedWorkspaceTrust({
|
|
250
|
+
identity: context.identity,
|
|
251
|
+
source: PROJECT_MCP_WORKSPACE_TRUST_SOURCE,
|
|
252
|
+
subject: context.subject,
|
|
253
|
+
evidenceFingerprint: context.fingerprint,
|
|
254
|
+
...workspaceTrustStoreOptions(opts),
|
|
255
|
+
});
|
|
120
256
|
return {
|
|
121
|
-
status:
|
|
122
|
-
fingerprint,
|
|
257
|
+
status: shared.status === "trusted" ? "trusted" : "changed",
|
|
258
|
+
fingerprint: context.fingerprint,
|
|
259
|
+
workspaceTrust: shared.audit,
|
|
123
260
|
};
|
|
124
261
|
}
|
|
125
262
|
|
|
126
263
|
/** Record (or re-record) the trusted fingerprint for a file. */
|
|
127
264
|
export function recordProjectMcpTrust(file, content, opts = {}) {
|
|
128
265
|
const target = storePath(opts);
|
|
266
|
+
const context = projectMcpWorkspaceTrustContext(file, content, opts);
|
|
267
|
+
// Persist the shared record first. If the legacy/source-specific store
|
|
268
|
+
// fails afterwards, the checker still requires both records and therefore
|
|
269
|
+
// cannot accidentally authorize from the shared ledger alone.
|
|
270
|
+
recordWorkspaceTrustConsent({
|
|
271
|
+
identity: context.identity,
|
|
272
|
+
source: PROJECT_MCP_WORKSPACE_TRUST_SOURCE,
|
|
273
|
+
subject: context.subject,
|
|
274
|
+
evidenceFingerprint: context.fingerprint,
|
|
275
|
+
consent: "explicit",
|
|
276
|
+
...workspaceTrustStoreOptions(opts),
|
|
277
|
+
});
|
|
129
278
|
return mutateSecurityStore(
|
|
130
279
|
target,
|
|
131
280
|
"project MCP trust",
|
|
132
281
|
(store) => {
|
|
133
|
-
store[
|
|
134
|
-
fingerprint:
|
|
282
|
+
store[projectMcpWorkspaceRecordKey(context)] = {
|
|
283
|
+
fingerprint: context.fingerprint,
|
|
135
284
|
trustedAt: new Date(
|
|
136
285
|
typeof opts.now === "number" ? opts.now : Date.now(),
|
|
137
286
|
).toISOString(),
|
|
287
|
+
workspaceTrust: {
|
|
288
|
+
schemaVersion: context.identity.schemaVersion,
|
|
289
|
+
workspaceId: context.identity.workspaceId,
|
|
290
|
+
repositoryId: context.identity.repositoryId,
|
|
291
|
+
subject: context.subject,
|
|
292
|
+
},
|
|
138
293
|
};
|
|
139
294
|
return true;
|
|
140
295
|
},
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import path from "path";
|
|
9
|
-
import os from "os";
|
|
10
9
|
import { logger } from "./logger.js";
|
|
10
|
+
import { getSessionStoreDir } from "./paths.js";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
function sessionDir() {
|
|
13
|
+
return getSessionStoreDir();
|
|
14
|
+
}
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* List recent chat sessions
|
|
@@ -18,15 +20,16 @@ const SESSION_DIR = path.join(os.homedir(), ".chainlesschain", "sessions");
|
|
|
18
20
|
*/
|
|
19
21
|
export function listSessions(limit = 10) {
|
|
20
22
|
try {
|
|
21
|
-
|
|
23
|
+
const directory = sessionDir();
|
|
24
|
+
if (!fs.existsSync(directory)) {
|
|
22
25
|
return [];
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
const files = fs
|
|
26
|
-
.readdirSync(
|
|
29
|
+
.readdirSync(directory)
|
|
27
30
|
.filter((f) => f.endsWith(".json"))
|
|
28
31
|
.map((f) => {
|
|
29
|
-
const filePath = path.join(
|
|
32
|
+
const filePath = path.join(directory, f);
|
|
30
33
|
const stat = fs.statSync(filePath);
|
|
31
34
|
let summary = "";
|
|
32
35
|
try {
|
|
@@ -59,7 +62,7 @@ export function listSessions(limit = 10) {
|
|
|
59
62
|
*/
|
|
60
63
|
export function loadSession(sessionId) {
|
|
61
64
|
try {
|
|
62
|
-
const filePath = path.join(
|
|
65
|
+
const filePath = path.join(sessionDir(), `${sessionId}.json`);
|
|
63
66
|
if (!fs.existsSync(filePath)) {
|
|
64
67
|
return null;
|
|
65
68
|
}
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { promises as fsp, existsSync, mkdirSync } from "node:fs";
|
|
12
12
|
import { getHomeDir } from "./paths.js";
|
|
13
|
+
import {
|
|
14
|
+
ensureTrustedSessionLifecycleScope,
|
|
15
|
+
resolveTrustedSessionLifecycleScope,
|
|
16
|
+
} from "./session-lifecycle-scope.js";
|
|
13
17
|
import {
|
|
14
18
|
MemoryStore,
|
|
15
19
|
BetaFlags,
|
|
@@ -26,7 +30,10 @@ import {
|
|
|
26
30
|
let _memoryStore = null;
|
|
27
31
|
let _betaFlags = null;
|
|
28
32
|
let _approvalGate = null;
|
|
29
|
-
|
|
33
|
+
// SessionManager owns live handles as well as its parked-session sidecar. A
|
|
34
|
+
// Claude project bucket must therefore receive a separate in-process manager
|
|
35
|
+
// even when another project happens to reuse the same session id.
|
|
36
|
+
const _sessionManagersByScope = new Map();
|
|
30
37
|
|
|
31
38
|
export function getMemoryStorePath() {
|
|
32
39
|
return join(getHomeDir(), "memory-store.json");
|
|
@@ -67,12 +74,14 @@ export async function getApprovalGate() {
|
|
|
67
74
|
return _approvalGate;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
|
-
export function getParkedSessionsPath() {
|
|
71
|
-
return
|
|
77
|
+
export function getParkedSessionsPath(options = {}) {
|
|
78
|
+
return resolveTrustedSessionLifecycleScope(options).parkedSessionsPath;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
|
-
function createParkedSessionsStore(filePath) {
|
|
81
|
+
function createParkedSessionsStore(filePath, options = {}) {
|
|
82
|
+
const ensureDirectory = options.ensureDirectory || null;
|
|
75
83
|
async function readAll() {
|
|
84
|
+
ensureDirectory?.();
|
|
76
85
|
try {
|
|
77
86
|
const raw = await fsp.readFile(filePath, "utf8");
|
|
78
87
|
return JSON.parse(raw || "{}");
|
|
@@ -81,6 +90,7 @@ function createParkedSessionsStore(filePath) {
|
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
async function writeAll(map) {
|
|
93
|
+
ensureDirectory?.();
|
|
84
94
|
const dir = join(filePath, "..");
|
|
85
95
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
86
96
|
await fsp.writeFile(filePath, JSON.stringify(map, null, 2), "utf8");
|
|
@@ -106,12 +116,17 @@ function createParkedSessionsStore(filePath) {
|
|
|
106
116
|
};
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
export function getSessionManager() {
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
export function getSessionManager(options = {}) {
|
|
120
|
+
const scope = resolveTrustedSessionLifecycleScope(options);
|
|
121
|
+
const existing = _sessionManagersByScope.get(scope.key);
|
|
122
|
+
if (existing) return existing;
|
|
123
|
+
const store = createParkedSessionsStore(scope.parkedSessionsPath, {
|
|
124
|
+
ensureDirectory: () => ensureTrustedSessionLifecycleScope(scope),
|
|
125
|
+
});
|
|
126
|
+
const manager = new SessionManager({ store });
|
|
127
|
+
manager._parkedStore = store;
|
|
128
|
+
_sessionManagersByScope.set(scope.key, manager);
|
|
129
|
+
return manager;
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
export function createStreamRouter() {
|
|
@@ -122,7 +137,7 @@ export function resetSessionCoreSingletonsForTests() {
|
|
|
122
137
|
_memoryStore = null;
|
|
123
138
|
_betaFlags = null;
|
|
124
139
|
_approvalGate = null;
|
|
125
|
-
|
|
140
|
+
_sessionManagersByScope.clear();
|
|
126
141
|
}
|
|
127
142
|
|
|
128
143
|
// =====================================================================
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import fs from "node:fs";
|
|
13
13
|
import path from "node:path";
|
|
14
14
|
import crypto from "node:crypto";
|
|
15
|
-
import
|
|
15
|
+
import * as cliPaths from "./paths.js";
|
|
16
16
|
import {
|
|
17
17
|
mutateSecurityStore,
|
|
18
18
|
readSecurityStore,
|
|
@@ -291,7 +291,28 @@ function publicRevocation(record, replayed) {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
export function getSessionHostLeaseRoot() {
|
|
294
|
-
|
|
294
|
+
const projectDir =
|
|
295
|
+
typeof cliPaths.getClaudeProjectStorageDir === "function"
|
|
296
|
+
? cliPaths.getClaudeProjectStorageDir()
|
|
297
|
+
: null;
|
|
298
|
+
if (projectDir) {
|
|
299
|
+
const stateRoot = path.join(projectDir, "session-host-leases");
|
|
300
|
+
// The caller did not supply this location; it is derived exclusively from
|
|
301
|
+
// the validated launch environment. Establish the same private project
|
|
302
|
+
// tree for host leases that JSONL uses for transcripts.
|
|
303
|
+
if (
|
|
304
|
+
typeof cliPaths.getHomeDir === "function" &&
|
|
305
|
+
typeof cliPaths.ensureClaudeProjectStorageTree === "function"
|
|
306
|
+
) {
|
|
307
|
+
cliPaths.ensureClaudeProjectStorageTree(
|
|
308
|
+
cliPaths.getHomeDir(),
|
|
309
|
+
projectDir,
|
|
310
|
+
{ extraDirectories: [stateRoot] },
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return stateRoot;
|
|
314
|
+
}
|
|
315
|
+
return path.join(cliPaths.getStatePath(), "session-host-leases");
|
|
295
316
|
}
|
|
296
317
|
|
|
297
318
|
export class SessionHostLeaseAuthority {
|
|
@@ -970,14 +991,27 @@ export class SessionHostLeaseAuthority {
|
|
|
970
991
|
}
|
|
971
992
|
}
|
|
972
993
|
|
|
973
|
-
const
|
|
994
|
+
const scopedAuthorities = new Map();
|
|
995
|
+
|
|
996
|
+
function authorityFor(options = {}) {
|
|
997
|
+
const stateRoot = path.resolve(
|
|
998
|
+
options?.stateRoot || getSessionHostLeaseRoot(),
|
|
999
|
+
);
|
|
1000
|
+
let authority = scopedAuthorities.get(stateRoot);
|
|
1001
|
+
if (!authority) {
|
|
1002
|
+
authority = new SessionHostLeaseAuthority({ stateRoot });
|
|
1003
|
+
scopedAuthorities.set(stateRoot, authority);
|
|
1004
|
+
}
|
|
1005
|
+
return authority;
|
|
1006
|
+
}
|
|
974
1007
|
|
|
975
1008
|
export function acquireSessionHostLease(sessionId, options) {
|
|
976
|
-
|
|
1009
|
+
const { stateRoot = null, ...leaseOptions } = options || {};
|
|
1010
|
+
return authorityFor({ stateRoot }).acquire(sessionId, leaseOptions);
|
|
977
1011
|
}
|
|
978
1012
|
|
|
979
|
-
export function withSessionHostWriteAuthority(sessionId, task) {
|
|
980
|
-
return
|
|
1013
|
+
export function withSessionHostWriteAuthority(sessionId, task, options = {}) {
|
|
1014
|
+
return authorityFor(options).withWriteAuthority(sessionId, task);
|
|
981
1015
|
}
|
|
982
1016
|
|
|
983
1017
|
export function createSessionHostWriteDelegation(lease) {
|
|
@@ -990,23 +1024,24 @@ export function withSessionHostDelegatedWriteAuthority(
|
|
|
990
1024
|
task,
|
|
991
1025
|
options,
|
|
992
1026
|
) {
|
|
993
|
-
|
|
1027
|
+
const { stateRoot = null, ...delegationOptions } = options || {};
|
|
1028
|
+
return authorityFor({ stateRoot }).withDelegatedWriteAuthority(
|
|
994
1029
|
sessionId,
|
|
995
1030
|
delegation,
|
|
996
1031
|
task,
|
|
997
|
-
|
|
1032
|
+
delegationOptions,
|
|
998
1033
|
);
|
|
999
1034
|
}
|
|
1000
1035
|
|
|
1001
1036
|
export function withSessionHostRecoveryLease(
|
|
1002
1037
|
sessionId,
|
|
1003
1038
|
task,
|
|
1004
|
-
{ hostKind = "background-recovery" } = {},
|
|
1039
|
+
{ hostKind = "background-recovery", stateRoot = null } = {},
|
|
1005
1040
|
) {
|
|
1006
1041
|
if (typeof task !== "function") {
|
|
1007
1042
|
throw new TypeError("session host recovery callback is required");
|
|
1008
1043
|
}
|
|
1009
|
-
const lease = acquireSessionHostLease(sessionId, { hostKind });
|
|
1044
|
+
const lease = acquireSessionHostLease(sessionId, { hostKind, stateRoot });
|
|
1010
1045
|
try {
|
|
1011
1046
|
const result = task(lease);
|
|
1012
1047
|
if (result && typeof result.then === "function") {
|
|
@@ -1018,17 +1053,18 @@ export function withSessionHostRecoveryLease(
|
|
|
1018
1053
|
}
|
|
1019
1054
|
}
|
|
1020
1055
|
|
|
1021
|
-
export function assertSessionHostWriteAuthority(sessionId) {
|
|
1022
|
-
return
|
|
1056
|
+
export function assertSessionHostWriteAuthority(sessionId, options = {}) {
|
|
1057
|
+
return authorityFor(options).withWriteAuthority(
|
|
1023
1058
|
sessionId,
|
|
1024
1059
|
(authority) => authority,
|
|
1025
1060
|
);
|
|
1026
1061
|
}
|
|
1027
1062
|
|
|
1028
|
-
export function readSessionHostAuthority(sessionId) {
|
|
1029
|
-
return
|
|
1063
|
+
export function readSessionHostAuthority(sessionId, options = {}) {
|
|
1064
|
+
return authorityFor(options).readAuthority(sessionId);
|
|
1030
1065
|
}
|
|
1031
1066
|
|
|
1032
|
-
export function revokeSessionHostAuthority(sessionId, options) {
|
|
1033
|
-
|
|
1067
|
+
export function revokeSessionHostAuthority(sessionId, options = {}) {
|
|
1068
|
+
const { stateRoot = null, ...revocationOptions } = options;
|
|
1069
|
+
return authorityFor({ stateRoot }).revoke(sessionId, revocationOptions);
|
|
1034
1070
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trusted storage scope for durable live-session sidecars.
|
|
3
|
+
*
|
|
4
|
+
* The session-core parked store, session message fabric, and workbench
|
|
5
|
+
* authority used to select only `getHomeDir()`. That is correct for native
|
|
6
|
+
* and legacy CLI launches, but it lets two Claude-compatible project buckets
|
|
7
|
+
* under one config root share a same-id sidecar. This module is deliberately
|
|
8
|
+
* narrow: it derives storage only from the validated launcher environment and
|
|
9
|
+
* current working directory. RPC payloads, settings values, and caller
|
|
10
|
+
* supplied paths are never part of the scope decision.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { dirname, join, resolve } from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
ensureClaudeProjectStorageTree,
|
|
17
|
+
getClaudeProjectStorageDir,
|
|
18
|
+
resolveConfigDataRoot,
|
|
19
|
+
} from "./paths.js";
|
|
20
|
+
|
|
21
|
+
const issuedScopes = new WeakSet();
|
|
22
|
+
|
|
23
|
+
function lifecycleScopeError(
|
|
24
|
+
message,
|
|
25
|
+
code = "CC_SESSION_LIFECYCLE_SCOPE_UNSAFE",
|
|
26
|
+
) {
|
|
27
|
+
const error = new Error(message);
|
|
28
|
+
error.code = code;
|
|
29
|
+
return error;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function trustedLaunchEnvironment(options) {
|
|
33
|
+
if (options.launchEnv === undefined || options.launchEnv === null) {
|
|
34
|
+
return process.env;
|
|
35
|
+
}
|
|
36
|
+
if (
|
|
37
|
+
!options.launchEnv ||
|
|
38
|
+
typeof options.launchEnv !== "object" ||
|
|
39
|
+
Array.isArray(options.launchEnv)
|
|
40
|
+
) {
|
|
41
|
+
throw lifecycleScopeError(
|
|
42
|
+
"Lifecycle launch environment must be an object captured at process startup",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return options.launchEnv;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function trustedCwd(options) {
|
|
49
|
+
if (options.cwd === undefined || options.cwd === null) return process.cwd();
|
|
50
|
+
if (typeof options.cwd !== "string" || !options.cwd.trim()) {
|
|
51
|
+
throw lifecycleScopeError(
|
|
52
|
+
"Lifecycle working directory must be a non-empty trusted path",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return options.cwd;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function opaqueProjectScopeKey(projectStorageDir) {
|
|
59
|
+
const normalized =
|
|
60
|
+
process.platform === "win32"
|
|
61
|
+
? resolve(projectStorageDir).toLowerCase()
|
|
62
|
+
: resolve(projectStorageDir);
|
|
63
|
+
return `claude-project:${createHash("sha256")
|
|
64
|
+
.update(normalized, "utf8")
|
|
65
|
+
.digest("hex")}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function issue(scope) {
|
|
69
|
+
const frozen = Object.freeze(scope);
|
|
70
|
+
issuedScopes.add(frozen);
|
|
71
|
+
return frozen;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the only valid lifecycle storage scope for this process boundary.
|
|
76
|
+
*
|
|
77
|
+
* `launchEnv` is an internal launch-time snapshot seam used by the agent REPL
|
|
78
|
+
* and tests. It is intentionally not a general settings/RPC option: callers
|
|
79
|
+
* cannot provide a project path, a scope id, or a durable file path here.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveTrustedSessionLifecycleScope(options = {}) {
|
|
82
|
+
const launchEnv = trustedLaunchEnvironment(options);
|
|
83
|
+
const cwd = trustedCwd(options);
|
|
84
|
+
const config = resolveConfigDataRoot({ env: launchEnv, cwd });
|
|
85
|
+
const projectStorageDir =
|
|
86
|
+
config.source === "claude"
|
|
87
|
+
? getClaudeProjectStorageDir({ env: launchEnv, cwd })
|
|
88
|
+
: null;
|
|
89
|
+
|
|
90
|
+
if (!projectStorageDir) {
|
|
91
|
+
return issue({
|
|
92
|
+
kind: "legacy",
|
|
93
|
+
key: "legacy",
|
|
94
|
+
configRoot: config.path,
|
|
95
|
+
projectStorageDir: null,
|
|
96
|
+
parkedSessionsPath: join(config.path, "parked-sessions.json"),
|
|
97
|
+
messageFabricStatePath: join(
|
|
98
|
+
config.path,
|
|
99
|
+
"session-message-fabric",
|
|
100
|
+
"state.json",
|
|
101
|
+
),
|
|
102
|
+
workbenchStatePath: join(config.path, "session-workbench.json"),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const projectDir = resolve(projectStorageDir);
|
|
107
|
+
return issue({
|
|
108
|
+
kind: "project",
|
|
109
|
+
key: opaqueProjectScopeKey(projectDir),
|
|
110
|
+
configRoot: config.path,
|
|
111
|
+
projectStorageDir: projectDir,
|
|
112
|
+
parkedSessionsPath: join(projectDir, "parked-sessions.json"),
|
|
113
|
+
messageFabricStatePath: join(
|
|
114
|
+
projectDir,
|
|
115
|
+
"session-message-fabric",
|
|
116
|
+
"state.json",
|
|
117
|
+
),
|
|
118
|
+
workbenchStatePath: join(projectDir, "session-workbench.json"),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Establish the owner-only project tree before a default lifecycle sidecar is
|
|
124
|
+
* read or written. Only scope objects issued by the resolver are accepted so
|
|
125
|
+
* an embedding/RPC caller cannot redirect a durable state file with a forged
|
|
126
|
+
* project directory.
|
|
127
|
+
*/
|
|
128
|
+
export function ensureTrustedSessionLifecycleScope(scope, options = {}) {
|
|
129
|
+
if (!issuedScopes.has(scope)) {
|
|
130
|
+
throw lifecycleScopeError(
|
|
131
|
+
"Lifecycle storage scope was not issued by the trusted resolver",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (scope.kind !== "project") return scope;
|
|
135
|
+
|
|
136
|
+
const extraDirectories = Array.isArray(options.extraDirectories)
|
|
137
|
+
? options.extraDirectories.map((entry) => resolve(entry))
|
|
138
|
+
: [];
|
|
139
|
+
ensureClaudeProjectStorageTree(scope.configRoot, scope.projectStorageDir, {
|
|
140
|
+
extraDirectories,
|
|
141
|
+
});
|
|
142
|
+
return scope;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Convenience helper for the default message-fabric state directory. */
|
|
146
|
+
export function sessionLifecycleFabricDirectory(scope) {
|
|
147
|
+
if (!issuedScopes.has(scope)) {
|
|
148
|
+
throw lifecycleScopeError(
|
|
149
|
+
"Lifecycle storage scope was not issued by the trusted resolver",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return dirname(scope.messageFabricStatePath);
|
|
153
|
+
}
|