chainlesschain 0.165.5 → 0.165.8
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/assets/{AIOps-CEeYTeDB.js → AIOps-B3gWmXjo.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-G9pdT-0e.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-Ca30J7m2.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BtVTm8PU.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-BhViNa6u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DhKq-FnY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-BL0SVbnE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-BWkblnFi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-ClaRNrF8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-sZE2Z77o.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BAfKOnRe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-Cvhay5WM.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-BpejL-4N.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-g2Ki6g_1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-D2xKbNo9.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DbL1Pds3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-DGtZCV4c.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-4kQpoNsI.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-BxTYXXAN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-k0J0LT2N.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-BaqhoPgN.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-T4RVPIM1.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-DszOToWq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9KJU7HF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-BI-_2xZX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-DsnDEPsl.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-BLncAKKK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BPQhGXDn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-CyElbUQW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-CcSntBQD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Bseca1_I.js → Logs-CNEtr-wz.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-d3kM4DDL.js → Marketplace-Dd2notAJ.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-BBsN2CSW.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-Dg2uTvwx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-r1YMRiJV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-C-89kSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-CrPZcVgf.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-sxi7hqJb.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-CAgOMfF1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-DafaPts3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CJM-xUW_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-L0ZjfAh_.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-DeLtgAD8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-CKC2b10z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-eyf2rzY1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-B8p3W6JK.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-BnRMxTq3.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-DjCNI1li.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-u6gXLbWT.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BZAw4tkM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-B1Tr72q7.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-DO7HFuBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BjCztyhj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-ZWyCUbR1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-DhDjeW4G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-R_CN2MuU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-uxFcujeu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-DX8D7rks.js} +1 -1
- package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-BYahQILD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CUSQ2tm-.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-CnN_S8Cs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-DcZlh-O4.js} +4 -4
- package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-ZhBOjzIX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-D5Qnq_lI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-BC9cZ3Be.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-yM5y9yvK.js → Sla-DauJ3E8_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-CFVhEyyr.js → SyncSettings-DUvJbSct.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-BXa-5noQ.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-JxaiUt_3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BW4ERQN8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-DltUbdtv.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-D_VEV60K.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-CCXTCs0R.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-lkGRIPcG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BfGeQ9HX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-BEC7pYvc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-BZdG24o0.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-yoEddedp.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-BXZEprOW.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-gg0bzZal.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DjRVRuin.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-su-dieAc.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-Ci3S8oKF.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-D8F10heH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-11wtYQiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-1IgxgALJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-1agckWSq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-5JqhujnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-6Tniqk1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-8Z-8BIlN.js} +1 -1
- package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-BCI5v0yB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BDJntYmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-BGzixOGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BMMOVoLD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-BTFpdBtt.js} +1 -1
- package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-Ba_HyPnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-BbF1IHof.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-BgT9su_e.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-BzPB6kGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-C6ygzplr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-CC3ZDjzY.js} +1 -1
- package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-CEmXRhAE.js} +1 -1
- package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
- package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-CMPqd5Cv.js} +1 -1
- package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-COWd8uGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CPIYSp4H.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-CR0h6seT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-CUmDhC4j.js} +1 -1
- package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
- package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-CusgZPtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-DBAETXft.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-DIJ2qFOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-Dbq4NMET.js} +1 -1
- package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-DkMEF5VT.js} +1 -1
- package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-DkfQR3hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-DnLucOn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-DugB_aKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-DyLUarhZ.js} +3 -3
- package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-T6_6ygJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-Tu8eWqh-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-W7XUgdVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-dHZinfko.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-jyNgp5FI.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-Cv4p8e4w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-B8EwmMOw.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-B8R9F8TK.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-DoFYz5Bp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CHWYLBjI.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-CXuPDO77.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-BaAtUUon.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-DCc4zcDD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-B_lx89oZ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-DE8DPmR0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-1ktXOsL_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-xH89-wjh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-DscvfW5p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-RdMKQta7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-Z3wTni2T.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B5g7TN7r.js → zoom-B0iChCsm.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 +105 -62
- package/src/commands/background-session.js +100 -10
- package/src/commands/git.js +39 -0
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +160 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +22 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +41 -2
- 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 +832 -91
- package/src/harness/session-index.js +15 -4
- 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/background-session-transport.js +153 -27
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- 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/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/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/llm-config-defaults.js +43 -0
- package/src/lib/mcp-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +156 -26
- package/src/lib/mcp-tls.js +330 -0
- package/src/lib/output-styles.js +12 -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 +730 -71
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +366 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +63 -6
- 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-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +188 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- 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 +583 -58
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- 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/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- 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 +1252 -0
- package/src/lib/session-projection.js +286 -3
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/settings-loader.cjs +29 -0
- 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/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +175 -19
- package/src/repl/permission-prompt.js +29 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +362 -20
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +281 -102
- package/src/runtime/headless-stream.js +199 -30
- package/src/runtime/mcp-config.js +298 -6
- package/src/runtime/policies/agent-policy.js +10 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DPy1XMYh.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
- package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
- package/src/assets/web-panel/assets/index-z1kFadaX.js +0 -1
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
* already exists under src/gateways + src/harness; this module only composes
|
|
7
7
|
* it into one entry point:
|
|
8
8
|
*
|
|
9
|
-
* - resolveRemoteControlOptions()
|
|
9
|
+
* - resolveRemoteControlOptions() explicit authority > safe defaults
|
|
10
10
|
* - buildDirectPairingUri() LAN pairing descriptor (no relay needed)
|
|
11
11
|
* - parseDirectPairingUri() inverse, used by clients + tests
|
|
12
12
|
* - pickLanAddress() best-effort non-internal IPv4
|
|
13
13
|
* - state file read/write ~/.chainlesschain/remote-control/<port>.json
|
|
14
14
|
* so `cc remote-control status/stop` can discover a running host process.
|
|
15
|
-
* The file is 0600 and
|
|
16
|
-
* as ~/.chainlesschain/ide/<port>.json (readable only by the OS user).
|
|
15
|
+
* The file is 0600 and deliberately contains no bearer/pairing token.
|
|
17
16
|
* - renderQrCode() lazy OPTIONAL `qrcode` import; returns
|
|
18
17
|
* null when the package is not installed (URI printing is the contract,
|
|
19
18
|
* the QR is progressive enhancement — no hard dependency, trap #6/#27).
|
|
@@ -23,19 +22,123 @@ import fs from "fs";
|
|
|
23
22
|
import os from "os";
|
|
24
23
|
import path from "path";
|
|
25
24
|
import { randomBytes } from "crypto";
|
|
25
|
+
import { isIP } from "net";
|
|
26
26
|
import { REMOTE_SESSION_SCOPES } from "../harness/remote-session-registry.js";
|
|
27
27
|
import { extractHost, isPrivateHost } from "./sandbox-network-policy.js";
|
|
28
28
|
|
|
29
29
|
export const REMOTE_CONTROL_DEFAULT_PORT = 18800;
|
|
30
|
+
export const REMOTE_CONTROL_DEFAULT_HOST = "127.0.0.1";
|
|
30
31
|
export const REMOTE_CONTROL_PAIRING_SCHEME =
|
|
31
32
|
"chainlesschain://remote-control/pair#";
|
|
32
33
|
export const REMOTE_CONTROL_DEFAULT_SCOPES = Object.freeze([
|
|
33
34
|
"observe",
|
|
34
35
|
"prompt",
|
|
35
|
-
"approve",
|
|
36
|
-
"interrupt",
|
|
37
36
|
]);
|
|
38
37
|
|
|
38
|
+
function cleanHost(value) {
|
|
39
|
+
if (value == null) return null;
|
|
40
|
+
const host = String(value).trim();
|
|
41
|
+
return host || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeBindHost(value, label) {
|
|
45
|
+
let host = cleanHost(value);
|
|
46
|
+
if (!host) return null;
|
|
47
|
+
if (host.startsWith("[") && host.endsWith("]")) {
|
|
48
|
+
host = host.slice(1, -1);
|
|
49
|
+
}
|
|
50
|
+
host = host.replace(/\.+$/, "").toLowerCase();
|
|
51
|
+
if (host.includes("%")) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid ${label} "${host}": scoped IPv6 bind hosts are not supported.`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (host === "localhost" || isIP(host) !== 0) return host;
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Invalid ${label} "${host}": use localhost or an IPv4/IPv6 literal without a port.`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function mappedIpv4FirstOctet(host) {
|
|
63
|
+
const dotted = host.match(/^::ffff:(\d{1,3})\.\d{1,3}\.\d{1,3}\.\d{1,3}$/i);
|
|
64
|
+
if (dotted) return Number(dotted[1]);
|
|
65
|
+
const hex = host.match(/^::ffff:([0-9a-f]{1,4}):[0-9a-f]{1,4}$/i);
|
|
66
|
+
if (!hex) return null;
|
|
67
|
+
return (parseInt(hex[1], 16) >> 8) & 0xff;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Whether a bind host is restricted to this machine. */
|
|
71
|
+
export function isLoopbackBindHost(value) {
|
|
72
|
+
let host = cleanHost(value)?.toLowerCase();
|
|
73
|
+
if (!host) return false;
|
|
74
|
+
if (host.startsWith("[") && host.endsWith("]")) {
|
|
75
|
+
host = host.slice(1, -1);
|
|
76
|
+
}
|
|
77
|
+
host = host.replace(/\.+$/, "");
|
|
78
|
+
return (
|
|
79
|
+
host === "localhost" ||
|
|
80
|
+
(isIP(host) === 6 && host === "::1") ||
|
|
81
|
+
(isIP(host) === 4 && host.split(".")[0] === "127") ||
|
|
82
|
+
mappedIpv4FirstOctet(host) === 127
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isWildcardBindHost(value) {
|
|
87
|
+
let host = cleanHost(value)?.toLowerCase();
|
|
88
|
+
if (host?.startsWith("[") && host.endsWith("]")) {
|
|
89
|
+
host = host.slice(1, -1);
|
|
90
|
+
}
|
|
91
|
+
return host === "0.0.0.0" || host === "::";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isLanBindHost(value) {
|
|
95
|
+
let host = cleanHost(value)?.toLowerCase();
|
|
96
|
+
if (host?.startsWith("[") && host.endsWith("]")) {
|
|
97
|
+
host = host.slice(1, -1);
|
|
98
|
+
}
|
|
99
|
+
return (
|
|
100
|
+
isWildcardBindHost(host) ||
|
|
101
|
+
isLoopbackBindHost(host) ||
|
|
102
|
+
(isIP(host) !== 0 && isPrivateHost(host))
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isAdvertisableLanAddress(value) {
|
|
107
|
+
const host = cleanHost(value)?.toLowerCase();
|
|
108
|
+
return (
|
|
109
|
+
isIP(host) !== 0 &&
|
|
110
|
+
isPrivateHost(host) &&
|
|
111
|
+
!isLoopbackBindHost(host) &&
|
|
112
|
+
!isWildcardBindHost(host) &&
|
|
113
|
+
host !== "169.254.169.254"
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function wsHost(value) {
|
|
118
|
+
const host = cleanHost(value) || REMOTE_CONTROL_DEFAULT_HOST;
|
|
119
|
+
if (host.startsWith("[") && host.endsWith("]")) return host;
|
|
120
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Resolve the direct endpoint that is safe to advertise to pairing clients. */
|
|
124
|
+
export function resolveRemoteControlWsUrl(
|
|
125
|
+
{ host = REMOTE_CONTROL_DEFAULT_HOST, port, allowLan = false } = {},
|
|
126
|
+
{ lanAddress = null } = {},
|
|
127
|
+
) {
|
|
128
|
+
let endpointHost;
|
|
129
|
+
if (allowLan && !isLoopbackBindHost(host)) {
|
|
130
|
+
endpointHost = isWildcardBindHost(host)
|
|
131
|
+
? isAdvertisableLanAddress(lanAddress)
|
|
132
|
+
? cleanHost(lanAddress)
|
|
133
|
+
: REMOTE_CONTROL_DEFAULT_HOST
|
|
134
|
+
: cleanHost(host);
|
|
135
|
+
} else if (isLoopbackBindHost(host)) {
|
|
136
|
+
endpointHost = cleanHost(host);
|
|
137
|
+
} else {
|
|
138
|
+
endpointHost = REMOTE_CONTROL_DEFAULT_HOST;
|
|
139
|
+
}
|
|
140
|
+
return `ws://${wsHost(endpointHost)}:${port}`;
|
|
141
|
+
}
|
|
39
142
|
function b64url(value) {
|
|
40
143
|
return Buffer.from(value, "utf8").toString("base64url");
|
|
41
144
|
}
|
|
@@ -63,22 +166,70 @@ export function parseScopes(raw) {
|
|
|
63
166
|
}
|
|
64
167
|
|
|
65
168
|
/**
|
|
66
|
-
* Resolve effective start options.
|
|
67
|
-
*
|
|
169
|
+
* Resolve effective start options. Remote Control is always command/flag
|
|
170
|
+
* activated by its caller; repository/project configuration is only allowed
|
|
171
|
+
* to disable it. In particular, checked-in config cannot widen the bind host,
|
|
172
|
+
* grant approve/interrupt, select a relay, or install a long-lived token.
|
|
173
|
+
*
|
|
174
|
+
* Direct LAN and privileged scopes are distinct command-line authorities. A
|
|
68
175
|
* missing token is AUTO-GENERATED (32 hex chars) — the WS server must never
|
|
69
|
-
* come up unauthenticated
|
|
70
|
-
* pairing URI embeds the token and pairing is the whole point.
|
|
176
|
+
* come up unauthenticated, even on loopback.
|
|
71
177
|
*/
|
|
72
178
|
export function resolveRemoteControlOptions({
|
|
73
179
|
flags = {},
|
|
74
180
|
env = process.env,
|
|
75
181
|
config = {},
|
|
76
182
|
} = {}) {
|
|
77
|
-
const rc =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
183
|
+
const rc =
|
|
184
|
+
config.remoteControl && typeof config.remoteControl === "object"
|
|
185
|
+
? config.remoteControl
|
|
186
|
+
: {};
|
|
187
|
+
const legacy =
|
|
188
|
+
config.remoteSession && typeof config.remoteSession === "object"
|
|
189
|
+
? config.remoteSession
|
|
190
|
+
: {};
|
|
191
|
+
if (
|
|
192
|
+
config.remoteControl === false ||
|
|
193
|
+
rc.enabled === false ||
|
|
194
|
+
legacy.remoteControlEnabled === false
|
|
195
|
+
) {
|
|
196
|
+
throw new Error("Remote Control is disabled by configuration");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const warnings = [];
|
|
200
|
+
if (
|
|
201
|
+
Object.prototype.hasOwnProperty.call(rc, "host") ||
|
|
202
|
+
Object.prototype.hasOwnProperty.call(rc, "allowLan")
|
|
203
|
+
) {
|
|
204
|
+
warnings.push(
|
|
205
|
+
"Ignoring project Remote Control exposure settings; use explicit --host/--allow-lan for this invocation.",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const flagHost = normalizeBindHost(flags.host, "--host");
|
|
210
|
+
const allowLan = flags.allowLan === true;
|
|
211
|
+
if (flagHost && !isLoopbackBindHost(flagHost) && !allowLan) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Refusing non-loopback --host "${flagHost}" without --allow-lan. ` +
|
|
214
|
+
"Add --allow-lan on a trusted network, or remove --host and configure --relay-url.",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const host = flagHost || (allowLan ? "0.0.0.0" : REMOTE_CONTROL_DEFAULT_HOST);
|
|
218
|
+
if (allowLan && host === "::") {
|
|
219
|
+
throw new Error(
|
|
220
|
+
'Refusing IPv6 wildcard host "::": use an explicit private IPv6 address ' +
|
|
221
|
+
"or 0.0.0.0 so the advertised endpoint matches the listener.",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (allowLan && !isLanBindHost(host)) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Refusing --allow-lan with non-private host "${host}". ` +
|
|
227
|
+
"Use a loopback, wildcard, or private LAN address; use a relay for public networks.",
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const lanAccessible = allowLan && !isLoopbackBindHost(host);
|
|
231
|
+
|
|
232
|
+
const port = Number(flags.port ?? env.CC_REMOTE_CONTROL_PORT ?? undefined);
|
|
82
233
|
const resolvedPort =
|
|
83
234
|
Number.isFinite(port) && port >= 1 && port <= 65535
|
|
84
235
|
? Math.floor(port)
|
|
@@ -86,40 +237,59 @@ export function resolveRemoteControlOptions({
|
|
|
86
237
|
const token =
|
|
87
238
|
flags.token ||
|
|
88
239
|
env.CC_REMOTE_CONTROL_TOKEN ||
|
|
89
|
-
rc.token ||
|
|
90
240
|
randomBytes(16).toString("hex");
|
|
91
|
-
const relayUrl =
|
|
92
|
-
flags.relayUrl ||
|
|
93
|
-
env.CC_REMOTE_SESSION_RELAY_URL ||
|
|
94
|
-
rc.relayUrl ||
|
|
95
|
-
legacy.relayUrl ||
|
|
96
|
-
null;
|
|
241
|
+
const relayUrl = flags.relayUrl || env.CC_REMOTE_SESSION_RELAY_URL || null;
|
|
97
242
|
const peerId =
|
|
98
243
|
flags.peerId ||
|
|
99
244
|
env.CC_REMOTE_SESSION_PEER_ID ||
|
|
100
|
-
rc.peerId ||
|
|
101
|
-
legacy.peerId ||
|
|
102
245
|
(relayUrl
|
|
103
246
|
? `cc-host-${os.hostname()}-${randomBytes(4).toString("hex")}`
|
|
104
247
|
: null);
|
|
248
|
+
|
|
249
|
+
const requestedScopes = parseScopes(flags.scopes ?? null);
|
|
250
|
+
if (requestedScopes.includes("approve") && flags.allowApprove !== true) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
'Remote Control scope "approve" requires explicit --allow-approve',
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (requestedScopes.includes("interrupt") && flags.allowInterrupt !== true) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
'Remote Control scope "interrupt" requires explicit --allow-interrupt',
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
const scopes = [...requestedScopes];
|
|
261
|
+
if (flags.allowApprove === true && !scopes.includes("approve")) {
|
|
262
|
+
scopes.push("approve");
|
|
263
|
+
}
|
|
264
|
+
if (flags.allowInterrupt === true && !scopes.includes("interrupt")) {
|
|
265
|
+
scopes.push("interrupt");
|
|
266
|
+
}
|
|
267
|
+
|
|
105
268
|
return {
|
|
106
269
|
port: resolvedPort,
|
|
107
|
-
host
|
|
270
|
+
host,
|
|
271
|
+
allowLan,
|
|
272
|
+
exposesLan: lanAccessible,
|
|
273
|
+
lanAccessible,
|
|
274
|
+
warnings,
|
|
108
275
|
token,
|
|
109
276
|
relayUrl,
|
|
110
277
|
peerId,
|
|
111
|
-
scopes
|
|
112
|
-
name: flags.name ||
|
|
278
|
+
scopes,
|
|
279
|
+
name: flags.name || `remote-control @ ${os.hostname()}`,
|
|
113
280
|
};
|
|
114
281
|
}
|
|
115
|
-
|
|
116
282
|
/** First non-internal IPv4 address, or null. Pure over an interfaces map. */
|
|
117
283
|
export function pickLanAddress(interfaces = os.networkInterfaces()) {
|
|
118
284
|
for (const entries of Object.values(interfaces || {})) {
|
|
119
285
|
for (const entry of entries || []) {
|
|
120
286
|
const family =
|
|
121
287
|
entry.family === "IPv4" || entry.family === 4 ? "IPv4" : entry.family;
|
|
122
|
-
if (
|
|
288
|
+
if (
|
|
289
|
+
family === "IPv4" &&
|
|
290
|
+
!entry.internal &&
|
|
291
|
+
isAdvertisableLanAddress(entry.address)
|
|
292
|
+
) {
|
|
123
293
|
return entry.address;
|
|
124
294
|
}
|
|
125
295
|
}
|
|
@@ -226,14 +396,22 @@ function stateFilePath(dir, port) {
|
|
|
226
396
|
}
|
|
227
397
|
|
|
228
398
|
/**
|
|
229
|
-
* Persist a running host's discovery record (0600 file, 0700 dir).
|
|
230
|
-
*
|
|
399
|
+
* Persist a running host's non-secret discovery record (0600 file, 0700 dir).
|
|
400
|
+
* Pairing/server tokens are intentionally rejected so status, crash recovery,
|
|
401
|
+
* diagnostics, and uploaded state artifacts cannot disclose them.
|
|
231
402
|
*/
|
|
232
403
|
export function writeRemoteControlState(state, { dir } = {}) {
|
|
233
404
|
const stateDir = dir || remoteControlStateDir();
|
|
234
405
|
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
235
406
|
const file = stateFilePath(stateDir, state.port);
|
|
236
|
-
|
|
407
|
+
const {
|
|
408
|
+
token: _token,
|
|
409
|
+
serverToken: _serverToken,
|
|
410
|
+
pairingToken: _pairingToken,
|
|
411
|
+
pairingUri: _pairingUri,
|
|
412
|
+
...safeState
|
|
413
|
+
} = state;
|
|
414
|
+
fs.writeFileSync(file, JSON.stringify(safeState, null, 2), {
|
|
237
415
|
encoding: "utf-8",
|
|
238
416
|
mode: 0o600,
|
|
239
417
|
});
|
|
@@ -279,8 +457,16 @@ export function readRemoteControlStates({ dir } = {}) {
|
|
|
279
457
|
const file = path.join(stateDir, name);
|
|
280
458
|
try {
|
|
281
459
|
const state = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
460
|
+
const legacyExposure = state.exposure == null;
|
|
461
|
+
const exposure = legacyExposure
|
|
462
|
+
? state.host && !isLoopbackBindHost(state.host)
|
|
463
|
+
? "legacy-unknown-lan"
|
|
464
|
+
: "legacy-unknown"
|
|
465
|
+
: state.exposure;
|
|
282
466
|
states.push({
|
|
283
467
|
...state,
|
|
468
|
+
exposure,
|
|
469
|
+
legacyExposure,
|
|
284
470
|
stateFile: file,
|
|
285
471
|
alive: isPidAlive(state.pid),
|
|
286
472
|
});
|
|
@@ -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
|
}
|