chainlesschain 0.163.8 → 0.165.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -4
- package/scripts/macos-mcp-launcher-build.mjs +317 -0
- package/scripts/macos-mcp-launcher-postinstall.sh +54 -0
- package/scripts/macos-mcp-launcher-preinstall.sh +61 -0
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-CavM7W3k.js → AIOps-CYxFDrcm.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CUvZGabQ.js → ActionButton-CPBtdiU2.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DeBnWnbe.js → Analytics-Dayq7Dp7.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DbI5kxvd.js → AppLayout-6U7bPb2s.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-D93bigKm.js → Artifacts-D2Iqg_3D.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CBNvfUZa.js → Audit-CdoOB7WV.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CJLyh_YU.js → BackgroundAgents-DrnAVB8w.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D_0yoehD.js → Backup-CesMi5cy.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-TJxmbObW.js → BaseInput-DaX9ptax.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CE0dto1q.js → Chat-OrwOPxDS.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZ5-yYiD.js → ChatBubbleRenderer-CvPZFRxM.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-Ck1RVIVo.js → Checkbox-DiHbcysC.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-qpoyf6DK.js → Codegen-CbgCkaBJ.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DUyPWjIE.js → Col-Du5hgrh4.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D_S1BemS.js → Community-DsPOBW-p.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BjxeK-Gc.js → Compact-BwHIFkNC.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Dd3CEJsu.js → Compliance-BvrDQSgd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-B4QG7-ad.js → Cowork-gwAE6arx.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-BtZnids5.js → Cron-DW-0l4uF.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CHPfCBFD.js → Crosschain-j-NsLesB.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DT8DCEOK.js → DID-CibVEBOI.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-COQab5_U.js → Dashboard-W4z9vSfe.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BY1PsQuU.js → Dropdown-DAcrkKxG.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CsuAuaq3.js → EmailListRenderer-6GXah-89.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BqpinD-j.js → FamilyGuardDashboard-TKDCq8Ku.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D0gkfQFv.js → Federation-BXBLlcNm.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BqgE-axK.js → FormItemContext-BoJJBkG3.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-D0hJ2KvC.js → GenericCardRenderer-okjsl46v.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CmMB0uPY.js → Git-Rvs7i6nn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BnVEI0Dk.js → Governance-CHSUxXXS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-YTAMihRP.js → Inference-V3BlfxsB.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-0ax7VHop.js → KnowledgeGraph-C_NlD4aR.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B9TRcokL.js → Logs-Nn4LNxhA.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-SVAgNnkt.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-B6CSuD_H.js → Marketplace-DEmjFcg-.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CjMXvOVU.js → McpTools-DlopXipI.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CXRF71U3.js → Memory-BG3ynX60.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DQgNp_oS.js → MobileBridge-DeRzWOfT.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DlPwcCbq.js → MobileProjects-Tyd3o2A0.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-6hDTRPe1.js → Mtc-BoPbOv1C.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-BlVHU1DS.js → MtcAudit-un-rTHp7.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DP9pzEbV.js → Multisig-Ds0oveEB.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Djorh8EV.js → NLProgramming-D09JI-yY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-oLZ-JkUg.js → Notes-DTRhiKJn.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Cg288VH3.js → NotificationSettings-BAnnNCim.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Yr-1fSYi.js +1 -0
- package/src/assets/web-panel/assets/{Organization-CBdo-bHs.js → Organization-S9EAolpj.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-qZdnVumx.js → Overflow-CqcLKqJ8.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-D4kNfGNQ.js → P2P-Dw0F9EcU.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DnlMemM7.js → PdhVaultBrowser-BDRwUevN.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DHucHFBJ.js → Permissions-D3_c3nKB.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BovlOIPR.js → PersonalDataHub-CxdMV98x.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-Deru3CQY.js → Pipeline-CgjNQFLI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Bq5oTTbU.js → Privacy-BHghIIht.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BfzxVWAz.js → ProjectInit-De6pqaKM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CKC6XBtH.js → ProjectSettings-Bg4ZkNYQ.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CP8vPvzs.js +1 -0
- package/src/assets/web-panel/assets/{Providers-C_jx4TWX.js → Providers-CUnHbNf-.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BdIHeVGC.js → QrScannerModal-DA2f3Wts.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BwZwAcPD.js → QuickAsk-C1FeHDqp.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BNTt0L-m.js → Recommend-B5guaUMN.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-PDADSds3.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-D6VpG32B.js → Reputation-eTlRhgvK.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D_74OvQQ.js → Row-D7AqSGY0.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BTr0a-BQ.js → RssFeed-JiuLm4Ja.js} +3 -3
- package/src/assets/web-panel/assets/{Search-C3MCFoNU.js → Search-CwruahW3.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CqW9aJ6s.js → Security-CdhqaR0b.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Cw5GNaHF.js → Services-CU0ZIlcT.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DZps5qeY.js → Skeleton-CtduPfAb.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C6NQ1znT.js → Skills-CROXm6AY.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DFP8G2Sh.js → Sla-DiZiABFD.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-ESt6MB6v.js → SpeechSettings-B5pLcXUw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BHnN98aM.js → SyncSettings-DBX6J1Oe.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-NiSCaOf-.js → Tasks-BLyRJd3-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DsKFhW2C.js → Templates-BKa5qzmk.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CQ0E0ze5.js → Tenant-DnD3a4M6.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D_rBu_by.js → Terminal-DEitQeTb.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D3Vm4Po4.js → TimelineRenderer-CjHggTDq.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DNTxRH6q.js → Tokens-CsGT7sfl.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dgprz1ms.js → Trigger-yr4rFDrW.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-B2rUnVBD.js → Trust-CLvu1Sjp.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DEGQNc6_.js → UkeySign-B0m7yUP8.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-jlsAx3Eh.js → VideoEditing-C0VvP4lc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-8jQih1ij.js → Wallet-kllBAG1M.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-XLeO6xIB.js → WebAuthn-C54UQNLx.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-De_dgDAG.js → WorkflowEditor-B7y8qSvU.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CI19R_Sk.js → chat-DRzTh8CJ.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DsYTOYN3.js → colors-BSMwSnCx.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CM3-t_Qr.js → compact-item-BTg602H5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BjxlBRZv.js → createContext-B6EGsdvp.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Dol8jLEP.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-RDaWX_4i.js → hasIn-DDKqJ8RD.js} +1 -1
- package/src/assets/web-panel/assets/{index-nAH87mQP.js → index-323Hhxxt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIUnNOjG.js → index-BICn3Qh1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyIWssPC.js → index-BMuGd2--.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX7tuWVJ.js → index-BRqt4fO-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd3euP_4.js → index-BVTAl5N2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cpqm1qfU.js → index-BsR7Y2NG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcPFB8qJ.js → index-Bx044qDj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd9OJhHG.js → index-C1GFfuqN.js} +1 -1
- package/src/assets/web-panel/assets/index-C3QX5tOl.js +1 -0
- package/src/assets/web-panel/assets/{index-B-Z8eFUJ.js → index-C66NpWy5.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlT2VAEK.js → index-CDTvsX8t.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dsij_KsN.js → index-CGNklbP6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4gkzxkw.js → index-CHfLYwcW.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6sm5-Xq.js → index-CNxg61YP.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0yx-jtQ.js → index-CQlHFN_g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjTrRt13.js → index-CUEU48o1.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7WBt_4T.js → index-CWGax2QW.js} +1 -1
- package/src/assets/web-panel/assets/{index-IeiL9RKc.js → index-C_JzIXUq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnHH7bnD.js → index-Cf23wVMs.js} +1 -1
- package/src/assets/web-panel/assets/{index-BKJr-TZp.js → index-CmAInUkX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiECJi-Q.js → index-CmAubjDi.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCnZyIMA.js → index-Co7oM1qr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Drlnwr8F.js → index-CwPUZbMI.js} +1 -1
- package/src/assets/web-panel/assets/{index-9R6BxAr0.js → index-D1naNj4U.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGqqJfL0.js → index-D5CsBShd.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi46Xu4u.js → index-DBT3YeQ9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXXtGMUJ.js → index-DLdrHhML.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0PMlbMs.js → index-Dc77VR3H.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0zcy54x.js → index-Dfv2lKu3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN6UPD2E.js → index-DiW0sDkz.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dxge-xWl.js → index-DpTHIRfZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyOGN6a5.js → index-DqFXGzzC.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9O9YyWE.js → index-XFe-1twF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHNYPO0b.js → index-p2HYn89m.js} +3 -3
- package/src/assets/web-panel/assets/{index-CVQMeiwH.js → index-pqfux8vZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-pPL66J85.js → index-rjzk9z1Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoQA0-52.js → index-th2ihDc0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5REnKt8.js → index-u2YVld9W.js} +1 -1
- package/src/assets/web-panel/assets/index-xYybvEic.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-BTL88of4.js → initDefaultProps-C3Djh9fa.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BUB2pXwB.js → motion-BzqFmbSe.js} +1 -1
- package/src/assets/web-panel/assets/{move-egCkkOMm.js → move-DJo_FFaW.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DAWhZC8c.js → mtc-parser-_tlTiBvk.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CvOxe_6d.js → omit-CB2ZyxUg.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DZUSLZ5Z.js → pickAttrs-DZwIFD4u.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BERxGNMY.js → placementArrow-DxZY0dgk.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DZuDfZ9n.js → responsiveObserve-CzzGt7GL.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DO756sgI.js → slide-BaIK52yz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BRQlIz28.js → statusUtils-3-FHu9_M.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DCSfdybl.js → styleChecker-BkRT68fK.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-LD8dWVve.js → useFlexGapSupport-DHYwKS-s.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BX9uW2LK.js → useFs-DVVUhV8p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BPSlPuHU.js → usePersonalDataHub-CkrFKAzQ.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BmOILC9V.js → vnode-BuBLFz0c.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-1iKowKGg.js → zoom-kpm6LtXU.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/cowork.js +418 -41
- package/src/commands/permissions.js +240 -16
- package/src/commands/plugin.js +1293 -56
- package/src/commands/session-location.js +276 -0
- package/src/commands/session.js +2 -0
- package/src/data/changelog.json +28 -2
- package/src/gateways/ws/action-protocol.js +316 -17
- package/src/gateways/ws/message-dispatcher.js +30 -0
- package/src/gateways/ws/remote-session-protocol.js +565 -35
- package/src/gateways/ws/ws-agent-handler.js +12 -0
- package/src/gateways/ws/ws-approval-gate.js +5 -0
- package/src/gateways/ws/ws-server.js +510 -50
- package/src/harness/jsonl-session-store.js +341 -24
- package/src/harness/mcp-client.js +8 -1
- package/src/harness/remote-session-audit.js +2 -0
- package/src/harness/remote-session-crypto.js +929 -21
- package/src/harness/remote-session-registry.js +945 -24
- package/src/harness/session-list-index.js +233 -14
- package/src/lib/agent-authority.js +6 -0
- package/src/lib/auto-mode-config.js +98 -3
- package/src/lib/background-agent-keeper-client.js +248 -0
- package/src/lib/background-agent-keeper-protocol.js +291 -0
- package/src/lib/background-agent-supervisor.js +1357 -85
- package/src/lib/background-interaction-resolver.js +12 -0
- package/src/lib/background-session-transport.js +23 -6
- package/src/lib/background-turn-bootstrap-protocol.js +190 -0
- package/src/lib/cowork-workflow.js +1188 -76
- package/src/lib/delivery-production-adapter.js +38 -2
- package/src/lib/dynamic-workflow-facade.js +1736 -0
- package/src/lib/execution-location-contract.js +551 -0
- package/src/lib/execution-location-runtime.js +130 -0
- package/src/lib/ide-context.js +94 -6
- package/src/lib/mcp-stdio-package-materialization.js +166 -14
- package/src/lib/packer/pkg-config-generator.js +2 -0
- package/src/lib/permission-authority.js +77 -0
- package/src/lib/permission-side-effect-center.js +495 -0
- package/src/lib/plugin-runtime/install.js +514 -13
- package/src/lib/plugin-runtime/marketplace-artifact-readback.js +900 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +1181 -0
- package/src/lib/plugin-runtime/marketplace-impact.js +408 -0
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1567 -0
- package/src/lib/plugin-security.js +60 -3
- package/src/lib/process-execution-broker/index.js +1095 -57
- package/src/lib/process-execution-broker/linux-bwrap-descriptor-launch.js +375 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +90 -16
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +159 -18
- package/src/lib/process-execution-broker/macos-mcp-launcher-contract.js +278 -0
- package/src/lib/process-execution-broker/macos-mcp-launcher-protocol.json +23 -0
- package/src/lib/process-execution-broker/macos-mcp-launcher.c +1497 -0
- package/src/lib/process-execution-broker/mcp-fd-entry-bootstrap.js +37 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +2330 -279
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +50 -4
- package/src/lib/public-ide-capability-manifest.js +3 -0
- package/src/lib/remote-approval-bridge.js +777 -34
- package/src/lib/remote-control.js +2 -0
- package/src/lib/remote-membership-authority.js +632 -0
- package/src/lib/remote-membership-coordinator.js +2973 -0
- package/src/lib/remote-membership-host-store.js +1799 -0
- package/src/lib/runtime-provenance-ledger.js +171 -46
- package/src/lib/scoped-permission-store.js +369 -0
- package/src/lib/session-anti-rollback-anchor.js +262 -5
- package/src/lib/shell-approval.js +169 -6
- package/src/lib/sub-agent-context.js +4 -0
- package/src/lib/with-file-lock.js +73 -38
- package/src/lib/workflow-definition-contract.js +149 -0
- package/src/repl/agent-repl.js +111 -36
- package/src/repl/chat-repl.js +2 -0
- package/src/repl/clipboard-image.js +735 -16
- package/src/repl/ide-status.js +1 -1
- package/src/repl/prompt-interactions.js +59 -6
- package/src/repl/remote-approval.js +29 -7
- package/src/runtime/agent-core.js +1182 -155
- package/src/runtime/headless-runner.js +52 -30
- package/src/runtime/headless-stream.js +51 -28
- package/src/workers/background-agent-keeper.js +689 -0
- package/src/workers/background-agent-worker.js +365 -13
- package/src/workers/background-turn-bootstrap.js +170 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-yySN5YNM.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Bb1O3Ofr.js +0 -1
- package/src/assets/web-panel/assets/Projects-CMolGhae.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-Biyn2-Ro.js +0 -5
- package/src/assets/web-panel/assets/devWarning-S1ld2LwU.js +0 -1
- package/src/assets/web-panel/assets/index-Drz6yq0m.js +0 -1
- package/src/assets/web-panel/assets/index-uWiUs7S_.js +0 -1
|
@@ -35,6 +35,17 @@ export const SESSION_ANTI_ROLLBACK_DETECTED_CODE =
|
|
|
35
35
|
export const SESSION_ANTI_ROLLBACK_UNAVAILABLE_CODE =
|
|
36
36
|
"CC_SESSION_ANTI_ROLLBACK_UNAVAILABLE";
|
|
37
37
|
|
|
38
|
+
// Test-only seams for deterministic durability failures. Production callers
|
|
39
|
+
// never assign these hooks; keeping them immediately before the two fsync
|
|
40
|
+
// boundaries lets tests exercise the real append and rollback path without
|
|
41
|
+
// replacing node:fs for the namespace or lock stores.
|
|
42
|
+
export const _sessionAntiRollbackFaultHooks = Object.seal({
|
|
43
|
+
afterRecordOpen: null,
|
|
44
|
+
beforeRecordFsync: null,
|
|
45
|
+
beforeRecordDirectoryFsync: null,
|
|
46
|
+
beforeRecordRollback: null,
|
|
47
|
+
});
|
|
48
|
+
|
|
38
49
|
const NAMESPACE_VERSION = 1;
|
|
39
50
|
const ANCHOR_SCHEMA = "chainlesschain.session-anti-rollback-anchor/v1";
|
|
40
51
|
const GENERATION_SCHEMA = "chainlesschain.session-generation-authority/v1";
|
|
@@ -59,12 +70,19 @@ function anchorError(code, message, cause = null, details = {}) {
|
|
|
59
70
|
|
|
60
71
|
function unavailable(cause, filePath = null) {
|
|
61
72
|
if (cause?.code === SESSION_ANTI_ROLLBACK_DETECTED_CODE) return cause;
|
|
62
|
-
|
|
73
|
+
const error = anchorError(
|
|
63
74
|
SESSION_ANTI_ROLLBACK_UNAVAILABLE_CODE,
|
|
64
75
|
"Session anti-rollback witness is unavailable; canonical session access is denied",
|
|
65
76
|
cause,
|
|
66
77
|
filePath ? { filePath } : {},
|
|
67
78
|
);
|
|
79
|
+
for (const key of ["commitState", "rollbackState", "persistenceTarget"]) {
|
|
80
|
+
if (cause?.[key] != null) error[key] = cause[key];
|
|
81
|
+
}
|
|
82
|
+
if (cause?.rollbackCause) {
|
|
83
|
+
error.rollbackErrorCode = cause.rollbackCause?.code || null;
|
|
84
|
+
}
|
|
85
|
+
return error;
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
function rollback(sessionId, message, current = null, candidate = null) {
|
|
@@ -628,11 +646,173 @@ function appendRecord(location, record) {
|
|
|
628
646
|
return appendRecordAtPath(location.recordPath, record, null);
|
|
629
647
|
}
|
|
630
648
|
|
|
649
|
+
function settleExistingRecord(location, record) {
|
|
650
|
+
if (isAffectedWindowsZeroDeviceStatRuntime()) {
|
|
651
|
+
return withTrustedFileParentSync(
|
|
652
|
+
fs,
|
|
653
|
+
location.recordPath,
|
|
654
|
+
({ canonicalPath, parentDevice }) =>
|
|
655
|
+
settleExistingRecordAtPath(canonicalPath, record, parentDevice),
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
return settleExistingRecordAtPath(location.recordPath, record, null);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function runFaultHook(name, payload) {
|
|
662
|
+
if (process.env.CC_SESSION_ANTI_ROLLBACK_FAULT_INJECTION !== "1") return;
|
|
663
|
+
const hook = _sessionAntiRollbackFaultHooks[name];
|
|
664
|
+
if (typeof hook === "function") hook(payload);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function fsyncDirectory(directory) {
|
|
668
|
+
const descriptor = fs.openSync(directory, "r");
|
|
669
|
+
try {
|
|
670
|
+
fs.fsyncSync(descriptor);
|
|
671
|
+
} finally {
|
|
672
|
+
fs.closeSync(descriptor);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function settleExistingRecordAtPath(recordPath, record, parentDevice) {
|
|
677
|
+
let descriptor = null;
|
|
678
|
+
try {
|
|
679
|
+
let flags = fs.constants.O_RDWR;
|
|
680
|
+
if (typeof fs.constants.O_NOFOLLOW === "number") {
|
|
681
|
+
flags |= fs.constants.O_NOFOLLOW;
|
|
682
|
+
}
|
|
683
|
+
descriptor = fs.openSync(recordPath, flags);
|
|
684
|
+
const opened = fs.fstatSync(descriptor, { bigint: true });
|
|
685
|
+
const published = fs.lstatSync(recordPath, { bigint: true });
|
|
686
|
+
const identityMatches =
|
|
687
|
+
parentDevice === null
|
|
688
|
+
? String(opened.dev) === String(published.dev) &&
|
|
689
|
+
String(opened.ino) === String(published.ino)
|
|
690
|
+
: samePathHandleStableFileIdentity(published, opened, parentDevice);
|
|
691
|
+
if (
|
|
692
|
+
!opened.isFile() ||
|
|
693
|
+
published.isSymbolicLink() ||
|
|
694
|
+
!published.isFile() ||
|
|
695
|
+
!identityMatches
|
|
696
|
+
) {
|
|
697
|
+
throw new Error("session anti-rollback record identity changed");
|
|
698
|
+
}
|
|
699
|
+
runFaultHook("beforeRecordFsync", {
|
|
700
|
+
recordPath,
|
|
701
|
+
record,
|
|
702
|
+
createsRecord: false,
|
|
703
|
+
unchanged: true,
|
|
704
|
+
});
|
|
705
|
+
fs.fsyncSync(descriptor);
|
|
706
|
+
const after = fs.lstatSync(recordPath, { bigint: true });
|
|
707
|
+
const afterIdentityMatches =
|
|
708
|
+
parentDevice === null
|
|
709
|
+
? String(opened.dev) === String(after.dev) &&
|
|
710
|
+
String(opened.ino) === String(after.ino)
|
|
711
|
+
: samePathHandleStableFileIdentity(after, opened, parentDevice);
|
|
712
|
+
if (after.isSymbolicLink() || !after.isFile() || !afterIdentityMatches) {
|
|
713
|
+
throw new Error("session anti-rollback record identity changed");
|
|
714
|
+
}
|
|
715
|
+
// A complete revision-1 line can survive a process death before either
|
|
716
|
+
// the file fsync or the first directory-entry barrier. Replaying the exact
|
|
717
|
+
// candidate must settle both boundaries before reporting success.
|
|
718
|
+
if (record.revision === "1" && process.platform !== "win32") {
|
|
719
|
+
runFaultHook("beforeRecordDirectoryFsync", {
|
|
720
|
+
recordPath,
|
|
721
|
+
record,
|
|
722
|
+
createsRecord: false,
|
|
723
|
+
recoveredCompleteRecord: true,
|
|
724
|
+
});
|
|
725
|
+
fsyncDirectory(path.dirname(recordPath));
|
|
726
|
+
}
|
|
727
|
+
} catch (cause) {
|
|
728
|
+
if (cause && typeof cause === "object" && !cause.commitState) {
|
|
729
|
+
cause.commitState = "unknown";
|
|
730
|
+
cause.rollbackState = "not-required";
|
|
731
|
+
cause.persistenceTarget = recordPath;
|
|
732
|
+
}
|
|
733
|
+
throw cause;
|
|
734
|
+
} finally {
|
|
735
|
+
if (descriptor !== null) fs.closeSync(descriptor);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function rollbackRecordAppend({
|
|
740
|
+
recordPath,
|
|
741
|
+
opened,
|
|
742
|
+
initialSize,
|
|
743
|
+
createsRecord,
|
|
744
|
+
parentDevice,
|
|
745
|
+
}) {
|
|
746
|
+
runFaultHook("beforeRecordRollback", {
|
|
747
|
+
recordPath,
|
|
748
|
+
initialSize,
|
|
749
|
+
createsRecord,
|
|
750
|
+
});
|
|
751
|
+
let descriptor = null;
|
|
752
|
+
try {
|
|
753
|
+
let flags = fs.constants.O_RDWR;
|
|
754
|
+
if (typeof fs.constants.O_NOFOLLOW === "number") {
|
|
755
|
+
flags |= fs.constants.O_NOFOLLOW;
|
|
756
|
+
}
|
|
757
|
+
descriptor = fs.openSync(recordPath, flags);
|
|
758
|
+
const rollbackOpened = fs.fstatSync(descriptor, { bigint: true });
|
|
759
|
+
const published = fs.lstatSync(recordPath, { bigint: true });
|
|
760
|
+
const identityMatches =
|
|
761
|
+
!published.isSymbolicLink() &&
|
|
762
|
+
published.isFile() &&
|
|
763
|
+
String(opened.dev) === String(rollbackOpened.dev) &&
|
|
764
|
+
String(opened.ino) === String(rollbackOpened.ino) &&
|
|
765
|
+
(parentDevice === null
|
|
766
|
+
? String(rollbackOpened.dev) === String(published.dev) &&
|
|
767
|
+
String(rollbackOpened.ino) === String(published.ino)
|
|
768
|
+
: samePathHandleStableFileIdentity(
|
|
769
|
+
published,
|
|
770
|
+
rollbackOpened,
|
|
771
|
+
parentDevice,
|
|
772
|
+
));
|
|
773
|
+
if (!identityMatches) {
|
|
774
|
+
throw new Error("session anti-rollback rollback record identity changed");
|
|
775
|
+
}
|
|
776
|
+
fs.ftruncateSync(descriptor, initialSize);
|
|
777
|
+
fs.fsyncSync(descriptor);
|
|
778
|
+
} finally {
|
|
779
|
+
if (descriptor !== null) fs.closeSync(descriptor);
|
|
780
|
+
}
|
|
781
|
+
if (createsRecord) {
|
|
782
|
+
fs.unlinkSync(recordPath);
|
|
783
|
+
if (process.platform !== "win32") {
|
|
784
|
+
fsyncDirectory(path.dirname(recordPath));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function rollbackEmptyCreatedRecord(recordPath) {
|
|
790
|
+
const published = fs.lstatSync(recordPath, { bigint: true });
|
|
791
|
+
if (
|
|
792
|
+
published.isSymbolicLink() ||
|
|
793
|
+
!published.isFile() ||
|
|
794
|
+
published.size !== 0n
|
|
795
|
+
) {
|
|
796
|
+
throw new Error(
|
|
797
|
+
"session anti-rollback unverified first record cannot be removed",
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
fs.unlinkSync(recordPath);
|
|
801
|
+
if (process.platform !== "win32") {
|
|
802
|
+
fsyncDirectory(path.dirname(recordPath));
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
631
806
|
function appendRecordAtPath(recordPath, record, parentDevice) {
|
|
632
807
|
let descriptor = null;
|
|
808
|
+
let recordOpened = false;
|
|
809
|
+
let createsRecord = false;
|
|
810
|
+
let requiresDirectoryPublication = false;
|
|
811
|
+
let initialSize = 0;
|
|
812
|
+
let opened = null;
|
|
633
813
|
try {
|
|
634
814
|
let flags = fs.constants.O_WRONLY | fs.constants.O_APPEND;
|
|
635
|
-
|
|
815
|
+
createsRecord = !fs.existsSync(recordPath);
|
|
636
816
|
if (createsRecord) {
|
|
637
817
|
flags |= fs.constants.O_CREAT | fs.constants.O_EXCL;
|
|
638
818
|
}
|
|
@@ -640,7 +820,18 @@ function appendRecordAtPath(recordPath, record, parentDevice) {
|
|
|
640
820
|
flags |= fs.constants.O_NOFOLLOW;
|
|
641
821
|
}
|
|
642
822
|
descriptor = fs.openSync(recordPath, flags, 0o600);
|
|
643
|
-
|
|
823
|
+
recordOpened = true;
|
|
824
|
+
runFaultHook("afterRecordOpen", { recordPath, record, createsRecord });
|
|
825
|
+
opened = fs.fstatSync(descriptor, { bigint: true });
|
|
826
|
+
if (!opened.isFile()) {
|
|
827
|
+
throw new Error("session anti-rollback append target is not a file");
|
|
828
|
+
}
|
|
829
|
+
initialSize = Number(opened.size);
|
|
830
|
+
// A previous process can die immediately after O_CREAT|O_EXCL and leave a
|
|
831
|
+
// visible zero-byte entry before its parent-directory fsync. Treat that
|
|
832
|
+
// exact crash residue like a first publication: the retry must flush the
|
|
833
|
+
// directory entry after it writes and fsyncs revision 1.
|
|
834
|
+
requiresDirectoryPublication = createsRecord || opened.size === 0n;
|
|
644
835
|
const payload = Buffer.from(`${JSON.stringify(record)}\n`, "utf8");
|
|
645
836
|
let offset = 0;
|
|
646
837
|
while (offset < payload.length) {
|
|
@@ -657,6 +848,11 @@ function appendRecordAtPath(recordPath, record, parentDevice) {
|
|
|
657
848
|
}
|
|
658
849
|
offset += written;
|
|
659
850
|
}
|
|
851
|
+
runFaultHook("beforeRecordFsync", {
|
|
852
|
+
recordPath,
|
|
853
|
+
record,
|
|
854
|
+
createsRecord,
|
|
855
|
+
});
|
|
660
856
|
fs.fsyncSync(descriptor);
|
|
661
857
|
const published = fs.lstatSync(recordPath, { bigint: true });
|
|
662
858
|
const identityMatches =
|
|
@@ -667,14 +863,64 @@ function appendRecordAtPath(recordPath, record, parentDevice) {
|
|
|
667
863
|
if (published.isSymbolicLink() || !published.isFile() || !identityMatches) {
|
|
668
864
|
throw new Error("session anti-rollback record identity changed");
|
|
669
865
|
}
|
|
670
|
-
if (
|
|
866
|
+
if (requiresDirectoryPublication && process.platform !== "win32") {
|
|
671
867
|
const directoryDescriptor = fs.openSync(path.dirname(recordPath), "r");
|
|
672
868
|
try {
|
|
869
|
+
runFaultHook("beforeRecordDirectoryFsync", {
|
|
870
|
+
recordPath,
|
|
871
|
+
record,
|
|
872
|
+
createsRecord,
|
|
873
|
+
recoveredEmptyRecord: !createsRecord,
|
|
874
|
+
});
|
|
673
875
|
fs.fsyncSync(directoryDescriptor);
|
|
674
876
|
} finally {
|
|
675
877
|
fs.closeSync(directoryDescriptor);
|
|
676
878
|
}
|
|
677
879
|
}
|
|
880
|
+
} catch (cause) {
|
|
881
|
+
// A completed JSON line is immediately readable even when its fsync (or a
|
|
882
|
+
// first-record directory fsync) reports failure. Best-effort rollback to
|
|
883
|
+
// the pre-append byte boundary prevents that unacknowledged record from
|
|
884
|
+
// becoming the next readback authority in the still-running process. If
|
|
885
|
+
// this was the first record, remove the empty entry and durably publish
|
|
886
|
+
// that removal on POSIX so a retry recreates and flushes the entry again.
|
|
887
|
+
try {
|
|
888
|
+
if (descriptor !== null) {
|
|
889
|
+
fs.closeSync(descriptor);
|
|
890
|
+
descriptor = null;
|
|
891
|
+
}
|
|
892
|
+
if (opened !== null) {
|
|
893
|
+
rollbackRecordAppend({
|
|
894
|
+
recordPath,
|
|
895
|
+
opened,
|
|
896
|
+
initialSize,
|
|
897
|
+
createsRecord,
|
|
898
|
+
parentDevice,
|
|
899
|
+
});
|
|
900
|
+
} else if (recordOpened && createsRecord) {
|
|
901
|
+
rollbackEmptyCreatedRecord(recordPath);
|
|
902
|
+
}
|
|
903
|
+
if (cause && typeof cause === "object") {
|
|
904
|
+
cause.commitState = "not-committed";
|
|
905
|
+
cause.rollbackState = "restored";
|
|
906
|
+
cause.persistenceTarget = recordPath;
|
|
907
|
+
}
|
|
908
|
+
} catch (rollbackCause) {
|
|
909
|
+
if (cause && typeof cause === "object") {
|
|
910
|
+
cause.rollbackCause = rollbackCause;
|
|
911
|
+
cause.commitState = "unknown";
|
|
912
|
+
cause.rollbackState = "failed";
|
|
913
|
+
cause.persistenceTarget = recordPath;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (cause && typeof cause === "object" && !cause.commitState) {
|
|
917
|
+
// open/fstat failures happen before any payload write. A successfully
|
|
918
|
+
// opened first entry is removed above before reporting this state.
|
|
919
|
+
cause.commitState = "not-committed";
|
|
920
|
+
cause.rollbackState = "not-required";
|
|
921
|
+
cause.persistenceTarget = recordPath;
|
|
922
|
+
}
|
|
923
|
+
throw cause;
|
|
678
924
|
} finally {
|
|
679
925
|
if (descriptor !== null) fs.closeSync(descriptor);
|
|
680
926
|
}
|
|
@@ -708,7 +954,18 @@ export function publishSessionAntiRollbackAnchor(
|
|
|
708
954
|
current.headHash === candidate.headHash &&
|
|
709
955
|
current.eventCount === candidate.eventCount &&
|
|
710
956
|
current.deletedAtMs === candidate.deletedAtMs;
|
|
711
|
-
|
|
957
|
+
// Revision 1 is the only record that can carry an unsettled first
|
|
958
|
+
// directory entry. Settle it before either acknowledging an exact
|
|
959
|
+
// replay or advancing directly to a proven successor.
|
|
960
|
+
if (current?.revision === "1") {
|
|
961
|
+
settleExistingRecord(location, current);
|
|
962
|
+
}
|
|
963
|
+
if (unchanged) {
|
|
964
|
+
if (current.revision !== "1") {
|
|
965
|
+
settleExistingRecord(location, current);
|
|
966
|
+
}
|
|
967
|
+
return current;
|
|
968
|
+
}
|
|
712
969
|
if (current === null) ensureNamespaceEntry(location);
|
|
713
970
|
const core = recordCore(
|
|
714
971
|
location,
|
|
@@ -32,6 +32,129 @@ import {
|
|
|
32
32
|
resolveInstallPolicy,
|
|
33
33
|
} from "./install-command-policy.js";
|
|
34
34
|
|
|
35
|
+
const SHELL_EXECUTION_DESCRIPTOR_VERSION =
|
|
36
|
+
"chainlesschain.shell-execution-descriptor/v1";
|
|
37
|
+
|
|
38
|
+
function deepFreezeData(value, seen = new WeakSet()) {
|
|
39
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value))
|
|
40
|
+
return value;
|
|
41
|
+
if (seen.has(value)) return value;
|
|
42
|
+
seen.add(value);
|
|
43
|
+
for (const child of Object.values(value)) deepFreezeData(child, seen);
|
|
44
|
+
return Object.freeze(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function cloneData(value, label) {
|
|
48
|
+
try {
|
|
49
|
+
return structuredClone(value);
|
|
50
|
+
} catch (cause) {
|
|
51
|
+
throw new Error(`${label} must be structured-cloneable`, { cause });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Capture only the declared run_shell data contract and freeze it. */
|
|
56
|
+
export function snapshotShellExecutionArgs(args = {}, fallbackCommand = null) {
|
|
57
|
+
const source = args && typeof args === "object" ? args : {};
|
|
58
|
+
const snapshot = {
|
|
59
|
+
command:
|
|
60
|
+
source.command !== undefined
|
|
61
|
+
? cloneData(source.command, "command")
|
|
62
|
+
: cloneData(fallbackCommand ?? null, "command"),
|
|
63
|
+
};
|
|
64
|
+
for (const key of ["cwd", "shell", "run_in_background", "timeout"]) {
|
|
65
|
+
if (source[key] !== undefined) snapshot[key] = cloneData(source[key], key);
|
|
66
|
+
}
|
|
67
|
+
return deepFreezeData(snapshot);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build the bounded, secret-free data tuple shown to ApprovalGate and later
|
|
72
|
+
* consumed at dispatch. Runtime objects/callbacks never enter this descriptor.
|
|
73
|
+
*/
|
|
74
|
+
export function createShellExecutionDescriptor({
|
|
75
|
+
args,
|
|
76
|
+
workspace = null,
|
|
77
|
+
shellInvocation = null,
|
|
78
|
+
pluginInvocation = null,
|
|
79
|
+
pluginSandboxPolicy = null,
|
|
80
|
+
pluginSandboxExecutionContract = null,
|
|
81
|
+
sandbox = null,
|
|
82
|
+
environmentRef = null,
|
|
83
|
+
} = {}) {
|
|
84
|
+
const shellArgs = snapshotShellExecutionArgs(args);
|
|
85
|
+
const descriptor = {
|
|
86
|
+
...shellArgs,
|
|
87
|
+
execution: {
|
|
88
|
+
version: SHELL_EXECUTION_DESCRIPTOR_VERSION,
|
|
89
|
+
mode: shellArgs.run_in_background === true ? "background" : "foreground",
|
|
90
|
+
workspace: workspace || null,
|
|
91
|
+
shell: shellInvocation
|
|
92
|
+
? {
|
|
93
|
+
kind: shellInvocation.kind || null,
|
|
94
|
+
useDefaultShell: shellInvocation.useDefaultShell === true,
|
|
95
|
+
file: shellInvocation.file || null,
|
|
96
|
+
argv: Array.isArray(shellInvocation.argv)
|
|
97
|
+
? [...shellInvocation.argv]
|
|
98
|
+
: [],
|
|
99
|
+
}
|
|
100
|
+
: null,
|
|
101
|
+
plugin: pluginInvocation
|
|
102
|
+
? {
|
|
103
|
+
command: pluginInvocation.command || null,
|
|
104
|
+
id: pluginInvocation.pluginId || null,
|
|
105
|
+
version: pluginInvocation.pluginVersion || null,
|
|
106
|
+
source: pluginInvocation.pluginSource || null,
|
|
107
|
+
bin: pluginInvocation.binName || null,
|
|
108
|
+
path: pluginInvocation.binPath || null,
|
|
109
|
+
runtime: pluginInvocation.runtime || null,
|
|
110
|
+
argv: Array.isArray(pluginInvocation.args)
|
|
111
|
+
? [...pluginInvocation.args]
|
|
112
|
+
: [],
|
|
113
|
+
executableSha256:
|
|
114
|
+
pluginInvocation.executableIdentity?.sha256 || null,
|
|
115
|
+
sandboxPolicy: pluginSandboxPolicy
|
|
116
|
+
? cloneData(pluginSandboxPolicy, "pluginSandboxPolicy")
|
|
117
|
+
: null,
|
|
118
|
+
sandboxExecution: pluginSandboxExecutionContract
|
|
119
|
+
? {
|
|
120
|
+
version:
|
|
121
|
+
pluginSandboxExecutionContract.contractVersion || null,
|
|
122
|
+
kind: pluginSandboxExecutionContract.kind || null,
|
|
123
|
+
pluginRoot: pluginSandboxExecutionContract.pluginRoot || null,
|
|
124
|
+
workingDirectory:
|
|
125
|
+
pluginSandboxExecutionContract.workingDirectory || null,
|
|
126
|
+
runtimePath:
|
|
127
|
+
pluginSandboxExecutionContract.runtimePath || null,
|
|
128
|
+
entrySha256:
|
|
129
|
+
pluginSandboxExecutionContract.entryIdentity?.sha256 ||
|
|
130
|
+
null,
|
|
131
|
+
runtimeSha256:
|
|
132
|
+
pluginSandboxExecutionContract.runtimeIdentity?.sha256 ||
|
|
133
|
+
null,
|
|
134
|
+
}
|
|
135
|
+
: null,
|
|
136
|
+
}
|
|
137
|
+
: null,
|
|
138
|
+
sandbox: sandbox ? cloneData(sandbox, "sandbox") : null,
|
|
139
|
+
environment: environmentRef
|
|
140
|
+
? cloneData(environmentRef, "environmentRef")
|
|
141
|
+
: null,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
return deepFreezeData(descriptor);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function shellApprovalAction(riskLevel) {
|
|
148
|
+
return riskLevel ? `${riskLevel}-risk` : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function commitShellApprovalSideEffects(result) {
|
|
152
|
+
const audit = result?.installAudit || null;
|
|
153
|
+
if (!audit) return false;
|
|
154
|
+
recordInstallCommandAudit(audit.entry, audit.options || {});
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
35
158
|
export const SHELL_TO_RISK = {
|
|
36
159
|
allow: RISK.LOW,
|
|
37
160
|
warn: RISK.MEDIUM,
|
|
@@ -41,10 +164,16 @@ export const SHELL_TO_RISK = {
|
|
|
41
164
|
|
|
42
165
|
export async function evaluateShellCommandWithApproval({
|
|
43
166
|
command,
|
|
167
|
+
args = null,
|
|
44
168
|
sessionId = null,
|
|
45
169
|
approvalGate = null,
|
|
46
170
|
shellPolicyOptions = {},
|
|
47
171
|
installPolicy = null,
|
|
172
|
+
workspace = null,
|
|
173
|
+
targetEnv = null,
|
|
174
|
+
policyEnv = process.env,
|
|
175
|
+
policyVersion = null,
|
|
176
|
+
deferSideEffects = false,
|
|
48
177
|
} = {}) {
|
|
49
178
|
const shellPolicy = sharedShellPolicy.evaluateShellCommandPolicy(
|
|
50
179
|
command,
|
|
@@ -62,8 +191,9 @@ export async function evaluateShellCommandWithApproval({
|
|
|
62
191
|
// CC_INSTALL_RISK_FLOOR) so the real run_shell path picks it up with no extra
|
|
63
192
|
// wiring. Both unset → disabled → the block below is skipped, byte-unchanged.
|
|
64
193
|
const effectiveInstallPolicy =
|
|
65
|
-
installPolicy || resolveInstallPolicy({ env:
|
|
194
|
+
installPolicy || resolveInstallPolicy({ env: policyEnv });
|
|
66
195
|
let install = null;
|
|
196
|
+
let installAudit = null;
|
|
67
197
|
if (effectiveInstallPolicy && effectiveInstallPolicy.enabled) {
|
|
68
198
|
const cls = classifyCodeAcquisition(command);
|
|
69
199
|
if (cls.flagged) {
|
|
@@ -72,8 +202,8 @@ export async function evaluateShellCommandWithApproval({
|
|
|
72
202
|
riskLevel = applyRiskFloor(riskLevel, effectiveInstallPolicy.riskFloor);
|
|
73
203
|
}
|
|
74
204
|
if (effectiveInstallPolicy.audit) {
|
|
75
|
-
|
|
76
|
-
{
|
|
205
|
+
installAudit = {
|
|
206
|
+
entry: {
|
|
77
207
|
command,
|
|
78
208
|
shellDecision: shellPolicy.decision,
|
|
79
209
|
riskLevel,
|
|
@@ -82,8 +212,12 @@ export async function evaluateShellCommandWithApproval({
|
|
|
82
212
|
global: hasGlobalInstall(cls),
|
|
83
213
|
sessionId,
|
|
84
214
|
},
|
|
85
|
-
effectiveInstallPolicy.auditOpts || {},
|
|
86
|
-
|
|
215
|
+
options: effectiveInstallPolicy.auditOpts || {},
|
|
216
|
+
};
|
|
217
|
+
if (!deferSideEffects) {
|
|
218
|
+
recordInstallCommandAudit(installAudit.entry, installAudit.options);
|
|
219
|
+
installAudit = null;
|
|
220
|
+
}
|
|
87
221
|
}
|
|
88
222
|
}
|
|
89
223
|
}
|
|
@@ -100,6 +234,7 @@ export async function evaluateShellCommandWithApproval({
|
|
|
100
234
|
riskLevel,
|
|
101
235
|
policy: null,
|
|
102
236
|
install,
|
|
237
|
+
installAudit,
|
|
103
238
|
};
|
|
104
239
|
}
|
|
105
240
|
|
|
@@ -114,14 +249,29 @@ export async function evaluateShellCommandWithApproval({
|
|
|
114
249
|
riskLevel,
|
|
115
250
|
policy: null,
|
|
116
251
|
install,
|
|
252
|
+
installAudit,
|
|
117
253
|
};
|
|
118
254
|
}
|
|
119
255
|
|
|
256
|
+
const operationArgs = args
|
|
257
|
+
? Object.isFrozen(args)
|
|
258
|
+
? args
|
|
259
|
+
: deepFreezeData(cloneData(args, "run_shell approval args"))
|
|
260
|
+
: snapshotShellExecutionArgs({ command });
|
|
261
|
+
const effectivePolicyVersion =
|
|
262
|
+
typeof policyVersion === "string" && policyVersion
|
|
263
|
+
? policyVersion
|
|
264
|
+
: `shell-policy:${shellPolicy.decision}:${shellPolicy.ruleId || "none"}`;
|
|
265
|
+
const action = shellApprovalAction(riskLevel);
|
|
120
266
|
const gateResult = await approvalGate.decide({
|
|
121
267
|
sessionId,
|
|
122
268
|
riskLevel,
|
|
123
269
|
tool: "run_shell",
|
|
124
|
-
args:
|
|
270
|
+
args: operationArgs,
|
|
271
|
+
cwd: workspace,
|
|
272
|
+
targetEnv,
|
|
273
|
+
policyVersion: effectivePolicyVersion,
|
|
274
|
+
action,
|
|
125
275
|
});
|
|
126
276
|
|
|
127
277
|
return {
|
|
@@ -133,6 +283,19 @@ export async function evaluateShellCommandWithApproval({
|
|
|
133
283
|
riskLevel,
|
|
134
284
|
policy: gateResult.policy,
|
|
135
285
|
install,
|
|
286
|
+
installAudit,
|
|
287
|
+
authorization: gateResult.authorization || null,
|
|
288
|
+
authorizationContext: gateResult.authorization
|
|
289
|
+
? {
|
|
290
|
+
tool: "run_shell",
|
|
291
|
+
action,
|
|
292
|
+
args: operationArgs,
|
|
293
|
+
workspace,
|
|
294
|
+
session: sessionId,
|
|
295
|
+
targetEnv,
|
|
296
|
+
policyVersion: effectivePolicyVersion,
|
|
297
|
+
}
|
|
298
|
+
: null,
|
|
136
299
|
// Explainability passthrough (autoMode.decisions wrapper attaches these):
|
|
137
300
|
// which gate rule fired and why — surfaced in the permission chain.
|
|
138
301
|
gateReason: gateResult.reason ?? null,
|
|
@@ -323,6 +323,7 @@ export class SubAgentContext {
|
|
|
323
323
|
// parent's policy, Plan lock, workspace sandbox, or unattended boundary.
|
|
324
324
|
this._parentAuthority = Object.freeze({
|
|
325
325
|
permissionRules: options.permissionRules || null,
|
|
326
|
+
permissionRulesProvider: options.permissionRulesProvider || null,
|
|
326
327
|
hostManagedToolPolicy: options.hostManagedToolPolicy || null,
|
|
327
328
|
planManager: options.planManager || null,
|
|
328
329
|
sandbox: options.sandbox || null,
|
|
@@ -618,6 +619,9 @@ export class SubAgentContext {
|
|
|
618
619
|
if (authority.permissionRules) {
|
|
619
620
|
options.permissionRules = authority.permissionRules;
|
|
620
621
|
}
|
|
622
|
+
if (authority.permissionRulesProvider) {
|
|
623
|
+
options.permissionRulesProvider = authority.permissionRulesProvider;
|
|
624
|
+
}
|
|
621
625
|
if (authority.hostManagedToolPolicy) {
|
|
622
626
|
options.hostManagedToolPolicy = authority.hostManagedToolPolicy;
|
|
623
627
|
}
|