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
|
@@ -18,7 +18,7 @@ import fs from "fs";
|
|
|
18
18
|
import path from "path";
|
|
19
19
|
import broker from "../lib/process-execution-broker/index.js";
|
|
20
20
|
import os from "os";
|
|
21
|
-
import { randomUUID } from "node:crypto";
|
|
21
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
22
22
|
import { isProxy } from "node:util/types";
|
|
23
23
|
import sharedCodingAgentPolicy from "./coding-agent-policy.cjs";
|
|
24
24
|
import sharedShellPolicy from "./coding-agent-shell-policy.cjs";
|
|
@@ -28,6 +28,12 @@ import sharedHookRunner from "../lib/hook-runner.js";
|
|
|
28
28
|
import sharedHookEvents from "../lib/settings-hook-events.js";
|
|
29
29
|
import { mergeProviderOptions } from "../lib/provider-options.js";
|
|
30
30
|
import { applyCredentialProxy } from "../lib/credential-proxy.js";
|
|
31
|
+
import {
|
|
32
|
+
commitShellApprovalSideEffects,
|
|
33
|
+
createShellExecutionDescriptor,
|
|
34
|
+
evaluateShellCommandWithApproval,
|
|
35
|
+
snapshotShellExecutionArgs,
|
|
36
|
+
} from "../lib/shell-approval.js";
|
|
31
37
|
import { describeBackgroundCommand } from "../lib/terminal-context.js";
|
|
32
38
|
import { buildTelemetryAttributes } from "../lib/telemetry-ids.js";
|
|
33
39
|
import {
|
|
@@ -230,6 +236,36 @@ function collectWorkspacePluginBinSandboxPolicy(
|
|
|
230
236
|
);
|
|
231
237
|
}
|
|
232
238
|
|
|
239
|
+
function projectPluginBinInvocationAuthority(invocation) {
|
|
240
|
+
if (!invocation) return null;
|
|
241
|
+
const identity = invocation.executableIdentity || {};
|
|
242
|
+
return {
|
|
243
|
+
command: invocation.command || null,
|
|
244
|
+
runtime: invocation.runtime || null,
|
|
245
|
+
shell: invocation.shell === true,
|
|
246
|
+
pluginId: invocation.pluginId || null,
|
|
247
|
+
pluginVersion: invocation.pluginVersion || null,
|
|
248
|
+
pluginSource: invocation.pluginSource || null,
|
|
249
|
+
scope: invocation.scope || null,
|
|
250
|
+
binName: invocation.binName || null,
|
|
251
|
+
binPath: invocation.binPath || null,
|
|
252
|
+
pluginRoot: invocation.pluginRoot || null,
|
|
253
|
+
args: Array.isArray(invocation.args) ? [...invocation.args] : [],
|
|
254
|
+
requiredBoundaries: [
|
|
255
|
+
...(invocation.sandboxPolicy?.requiredBoundaries || []),
|
|
256
|
+
].sort(),
|
|
257
|
+
executableIdentity: {
|
|
258
|
+
realPath: identity.realPath || null,
|
|
259
|
+
sha256: identity.sha256 || null,
|
|
260
|
+
bytes: identity.bytes ?? null,
|
|
261
|
+
dev: identity.dev ?? null,
|
|
262
|
+
ino: identity.ino ?? null,
|
|
263
|
+
mtimeMs: identity.mtimeMs ?? null,
|
|
264
|
+
mode: identity.mode ?? null,
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
233
269
|
function rethrowRunCodeSandboxFailure(error) {
|
|
234
270
|
if (
|
|
235
271
|
error?.sandboxFailClosed === true ||
|
|
@@ -2223,6 +2259,308 @@ function attachDiffReviewAudit(result, audit) {
|
|
|
2223
2259
|
return result;
|
|
2224
2260
|
}
|
|
2225
2261
|
|
|
2262
|
+
const RUN_CODE_ARGUMENT_KEYS = new Set([
|
|
2263
|
+
"language",
|
|
2264
|
+
"code",
|
|
2265
|
+
"timeout",
|
|
2266
|
+
"persist",
|
|
2267
|
+
]);
|
|
2268
|
+
|
|
2269
|
+
function snapshotRunCodeExecutionArgs(args) {
|
|
2270
|
+
if (
|
|
2271
|
+
!args ||
|
|
2272
|
+
typeof args !== "object" ||
|
|
2273
|
+
Array.isArray(args) ||
|
|
2274
|
+
isProxy(args)
|
|
2275
|
+
) {
|
|
2276
|
+
throw new TypeError("run_code arguments must be a plain data object");
|
|
2277
|
+
}
|
|
2278
|
+
const descriptors = Object.getOwnPropertyDescriptors(args);
|
|
2279
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
2280
|
+
if (typeof key !== "string" || !RUN_CODE_ARGUMENT_KEYS.has(key)) {
|
|
2281
|
+
throw new TypeError(`run_code contains unsupported field ${String(key)}`);
|
|
2282
|
+
}
|
|
2283
|
+
if (!Object.hasOwn(descriptors[key], "value")) {
|
|
2284
|
+
throw new TypeError(`run_code field ${key} must be a data property`);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
const language = descriptors.language?.value;
|
|
2288
|
+
const code = descriptors.code?.value;
|
|
2289
|
+
if (typeof language !== "string" || !language) {
|
|
2290
|
+
throw new TypeError("run_code language must be a non-empty string");
|
|
2291
|
+
}
|
|
2292
|
+
if (typeof code !== "string") {
|
|
2293
|
+
throw new TypeError("run_code code must be a string");
|
|
2294
|
+
}
|
|
2295
|
+
const snapshot = { language, code };
|
|
2296
|
+
if (descriptors.timeout) {
|
|
2297
|
+
const timeout = descriptors.timeout.value;
|
|
2298
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout)) {
|
|
2299
|
+
throw new TypeError("run_code timeout must be a finite number");
|
|
2300
|
+
}
|
|
2301
|
+
snapshot.timeout = timeout;
|
|
2302
|
+
}
|
|
2303
|
+
if (descriptors.persist) {
|
|
2304
|
+
const persist = descriptors.persist.value;
|
|
2305
|
+
if (typeof persist !== "boolean") {
|
|
2306
|
+
throw new TypeError("run_code persist must be a boolean");
|
|
2307
|
+
}
|
|
2308
|
+
snapshot.persist = persist;
|
|
2309
|
+
}
|
|
2310
|
+
return Object.freeze(snapshot);
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
function runCodeApprovalDescriptor(args, cwd) {
|
|
2314
|
+
return Object.freeze({
|
|
2315
|
+
language: args.language,
|
|
2316
|
+
codeSha256: createHash("sha256").update(args.code, "utf8").digest("hex"),
|
|
2317
|
+
codeBytes: Buffer.byteLength(args.code, "utf8"),
|
|
2318
|
+
timeout: Math.min(Math.max(args.timeout || 60, 1), 300),
|
|
2319
|
+
persist: args.persist === true,
|
|
2320
|
+
cwd,
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
function canonicalPolicyAuthorityData(value, state = null, depth = 0) {
|
|
2325
|
+
const budget = state || { nodes: 0, seen: new WeakSet() };
|
|
2326
|
+
budget.nodes += 1;
|
|
2327
|
+
if (budget.nodes > 4096 || depth > 12) {
|
|
2328
|
+
throw new TypeError("policy authority exceeds canonical bounds");
|
|
2329
|
+
}
|
|
2330
|
+
if (value === null) return "null";
|
|
2331
|
+
if (value === undefined) return '"$undefined"';
|
|
2332
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
2333
|
+
return JSON.stringify(value);
|
|
2334
|
+
}
|
|
2335
|
+
if (typeof value === "number") {
|
|
2336
|
+
if (!Number.isFinite(value)) {
|
|
2337
|
+
throw new TypeError("policy authority contains a non-finite number");
|
|
2338
|
+
}
|
|
2339
|
+
return JSON.stringify(value);
|
|
2340
|
+
}
|
|
2341
|
+
if (typeof value !== "object" || isProxy(value)) {
|
|
2342
|
+
throw new TypeError("policy authority must contain data objects only");
|
|
2343
|
+
}
|
|
2344
|
+
if (budget.seen.has(value)) {
|
|
2345
|
+
throw new TypeError("policy authority must not contain cycles");
|
|
2346
|
+
}
|
|
2347
|
+
budget.seen.add(value);
|
|
2348
|
+
try {
|
|
2349
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
2350
|
+
const keys = Reflect.ownKeys(descriptors);
|
|
2351
|
+
if (keys.some((key) => typeof key !== "string") || keys.length > 512) {
|
|
2352
|
+
throw new TypeError("policy authority contains invalid keys");
|
|
2353
|
+
}
|
|
2354
|
+
if (Array.isArray(value)) {
|
|
2355
|
+
const length = descriptors.length?.value;
|
|
2356
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > 512) {
|
|
2357
|
+
throw new TypeError("policy authority array exceeds bounds");
|
|
2358
|
+
}
|
|
2359
|
+
const entries = [];
|
|
2360
|
+
for (let index = 0; index < length; index += 1) {
|
|
2361
|
+
const descriptor = descriptors[String(index)];
|
|
2362
|
+
if (!descriptor || !Object.hasOwn(descriptor, "value")) {
|
|
2363
|
+
throw new TypeError("policy authority arrays must be dense data");
|
|
2364
|
+
}
|
|
2365
|
+
entries.push(
|
|
2366
|
+
canonicalPolicyAuthorityData(descriptor.value, budget, depth + 1),
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
const allowedKeys = new Set([
|
|
2370
|
+
"length",
|
|
2371
|
+
...Array.from({ length }, (_, index) => String(index)),
|
|
2372
|
+
]);
|
|
2373
|
+
if (keys.some((key) => !allowedKeys.has(key))) {
|
|
2374
|
+
throw new TypeError("policy authority arrays contain extra fields");
|
|
2375
|
+
}
|
|
2376
|
+
return `[${entries.join(",")}]`;
|
|
2377
|
+
}
|
|
2378
|
+
const prototype = Object.getPrototypeOf(value);
|
|
2379
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
2380
|
+
throw new TypeError("policy authority must contain plain objects");
|
|
2381
|
+
}
|
|
2382
|
+
return `{${keys
|
|
2383
|
+
.sort()
|
|
2384
|
+
.map((key) => {
|
|
2385
|
+
const descriptor = descriptors[key];
|
|
2386
|
+
if (!Object.hasOwn(descriptor, "value")) {
|
|
2387
|
+
throw new TypeError("policy authority accessors are not allowed");
|
|
2388
|
+
}
|
|
2389
|
+
return `${JSON.stringify(key)}:${canonicalPolicyAuthorityData(
|
|
2390
|
+
descriptor.value,
|
|
2391
|
+
budget,
|
|
2392
|
+
depth + 1,
|
|
2393
|
+
)}`;
|
|
2394
|
+
})
|
|
2395
|
+
.join(",")}}`;
|
|
2396
|
+
} finally {
|
|
2397
|
+
budget.seen.delete(value);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
function digestPolicyAuthority(projection) {
|
|
2402
|
+
return createHash("sha256")
|
|
2403
|
+
.update("chainlesschain.shell-policy-authority.v1\0", "utf8")
|
|
2404
|
+
.update(canonicalPolicyAuthorityData(projection), "utf8")
|
|
2405
|
+
.digest("hex");
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
function validatePermissionRuleset(rules) {
|
|
2409
|
+
if (!rules) return null;
|
|
2410
|
+
if (typeof rules !== "object" || Array.isArray(rules)) {
|
|
2411
|
+
throw new TypeError(
|
|
2412
|
+
"permission authority provider returned an invalid ruleset",
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
const normalized = {};
|
|
2416
|
+
for (const key of ["allow", "ask", "deny"]) {
|
|
2417
|
+
const value = rules[key];
|
|
2418
|
+
if (value !== undefined && !Array.isArray(value)) {
|
|
2419
|
+
throw new TypeError(
|
|
2420
|
+
"permission authority provider returned an invalid ruleset",
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
normalized[key] = value ? [...value] : [];
|
|
2424
|
+
}
|
|
2425
|
+
return normalized;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
function projectPermissionAuthority({ authority, rules, tool, args, cwd }) {
|
|
2429
|
+
const verdict = rules
|
|
2430
|
+
? evaluatePermissionRules({ tool, args, cwd, rules })
|
|
2431
|
+
: { decision: null, rule: null };
|
|
2432
|
+
const source = verdict.rule
|
|
2433
|
+
? authority?.sources?.[`${verdict.decision}:${verdict.rule}`] || null
|
|
2434
|
+
: null;
|
|
2435
|
+
const scopedRule = source?.startsWith("scoped:")
|
|
2436
|
+
? authority?.scoped?.rules?.find(
|
|
2437
|
+
(record) =>
|
|
2438
|
+
record.status === "active" && `scoped:${record.id}` === source,
|
|
2439
|
+
) || null
|
|
2440
|
+
: null;
|
|
2441
|
+
return {
|
|
2442
|
+
rules: rules || null,
|
|
2443
|
+
sources: authority?.sources || null,
|
|
2444
|
+
scoped: authority?.scoped || null,
|
|
2445
|
+
version: authority?.version || authority?.policyVersion || null,
|
|
2446
|
+
revision: authority?.revision ?? null,
|
|
2447
|
+
headHash: authority?.headHash || null,
|
|
2448
|
+
verdict: {
|
|
2449
|
+
decision: verdict.decision || null,
|
|
2450
|
+
rule: verdict.rule || null,
|
|
2451
|
+
source,
|
|
2452
|
+
scopedRule: scopedRule
|
|
2453
|
+
? {
|
|
2454
|
+
id: scopedRule.id || null,
|
|
2455
|
+
revision: scopedRule.revision ?? null,
|
|
2456
|
+
expiresAt: scopedRule.expiresAt ?? null,
|
|
2457
|
+
status: scopedRule.status || null,
|
|
2458
|
+
}
|
|
2459
|
+
: null,
|
|
2460
|
+
},
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
function selectedHostToolPolicy(hostAuthority, tool) {
|
|
2465
|
+
const policies = hostAuthority?.tools || hostAuthority?.toolPolicies || null;
|
|
2466
|
+
return policies && typeof policies === "object"
|
|
2467
|
+
? policies[tool] || null
|
|
2468
|
+
: null;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
function snapshotPlanToolAuthority(planManager, tool, args) {
|
|
2472
|
+
const active = planManager?.isActive?.() === true;
|
|
2473
|
+
return {
|
|
2474
|
+
active,
|
|
2475
|
+
executionLockActive: planManager?.executionLock != null,
|
|
2476
|
+
toolAllowed:
|
|
2477
|
+
!active || (tool === "git" && isReadOnlyGitCommand(args?.command))
|
|
2478
|
+
? true
|
|
2479
|
+
: planManager?.isToolAllowed?.(tool) === true,
|
|
2480
|
+
};
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
function snapshotEffectiveToolSetAuthority(effectiveAllowedToolNames, tool) {
|
|
2484
|
+
const enforced = Array.isArray(effectiveAllowedToolNames);
|
|
2485
|
+
return {
|
|
2486
|
+
enforced,
|
|
2487
|
+
allowed: !enforced || effectiveAllowedToolNames.includes(tool),
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
function evaluateSelectedToolAdmissionAuthority({
|
|
2492
|
+
admission,
|
|
2493
|
+
tool,
|
|
2494
|
+
runtimeDescriptor,
|
|
2495
|
+
localToolDescriptor,
|
|
2496
|
+
hostManagedToolPolicy,
|
|
2497
|
+
}) {
|
|
2498
|
+
if (admission?.enforce !== true) {
|
|
2499
|
+
return {
|
|
2500
|
+
override: {},
|
|
2501
|
+
decision: null,
|
|
2502
|
+
projection: { enforce: false },
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
2505
|
+
const override =
|
|
2506
|
+
admission.tools && typeof admission.tools === "object"
|
|
2507
|
+
? admission.tools[tool] || {}
|
|
2508
|
+
: {};
|
|
2509
|
+
const tier =
|
|
2510
|
+
override.tier ||
|
|
2511
|
+
runtimeDescriptor?.tier ||
|
|
2512
|
+
getCodingAgentToolPolicy(tool)?.tier ||
|
|
2513
|
+
(localToolDescriptor ? "extension" : "mvp");
|
|
2514
|
+
const inputs = {
|
|
2515
|
+
capabilityGranted:
|
|
2516
|
+
override.capabilityGranted ?? admission.capabilityGranted,
|
|
2517
|
+
policyAllowed:
|
|
2518
|
+
override.policyAllowed ??
|
|
2519
|
+
(hostManagedToolPolicy
|
|
2520
|
+
? (hostManagedToolPolicy?.tools ||
|
|
2521
|
+
hostManagedToolPolicy?.toolPolicies ||
|
|
2522
|
+
{})[tool]?.allowed !== false
|
|
2523
|
+
: admission.policyAllowed),
|
|
2524
|
+
permissionGranted:
|
|
2525
|
+
override.permissionGranted ?? admission.permissionGranted,
|
|
2526
|
+
budgetOk: override.budgetOk ?? admission.budgetOk,
|
|
2527
|
+
uiSupported: override.uiSupported ?? admission.uiSupported,
|
|
2528
|
+
};
|
|
2529
|
+
const decision = admitTool({ tool, tier, ...inputs });
|
|
2530
|
+
return {
|
|
2531
|
+
override,
|
|
2532
|
+
decision,
|
|
2533
|
+
projection: {
|
|
2534
|
+
enforce: true,
|
|
2535
|
+
tier: decision.tier,
|
|
2536
|
+
capabilityGranted: inputs.capabilityGranted === true,
|
|
2537
|
+
policyAllowed: inputs.policyAllowed === true,
|
|
2538
|
+
permissionGranted: inputs.permissionGranted === true,
|
|
2539
|
+
budgetOk: inputs.budgetOk === true,
|
|
2540
|
+
uiSupported: inputs.uiSupported === true,
|
|
2541
|
+
admitted: decision.admitted === true,
|
|
2542
|
+
unmet: [...decision.unmet],
|
|
2543
|
+
},
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
function snapshotApprovalPolicyAuthority(approvalGate, sessionId) {
|
|
2548
|
+
if (!approvalGate) return null;
|
|
2549
|
+
const snapshot = approvalGate.getAuthorizationPolicySnapshot?.(sessionId);
|
|
2550
|
+
if (snapshot && typeof snapshot.then === "function") {
|
|
2551
|
+
throw new TypeError("approval policy snapshot must be synchronous");
|
|
2552
|
+
}
|
|
2553
|
+
if (snapshot) return snapshot;
|
|
2554
|
+
return {
|
|
2555
|
+
schema: "chainlesschain.approval-policy-authority/v1",
|
|
2556
|
+
kind: "legacy-gate",
|
|
2557
|
+
sessionId: sessionId ? String(sessionId) : null,
|
|
2558
|
+
policy: approvalGate.getSessionPolicy?.(sessionId) || null,
|
|
2559
|
+
hasAuthorizationConsumer:
|
|
2560
|
+
approvalGate.hasAuthorizationConsumer?.() === true,
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2226
2564
|
/**
|
|
2227
2565
|
* Execute a single tool call with plan-mode filtering and hook pipeline.
|
|
2228
2566
|
*
|
|
@@ -2235,9 +2573,99 @@ function attachDiffReviewAudit(result, audit) {
|
|
|
2235
2573
|
* @returns {Promise<object>} tool result
|
|
2236
2574
|
*/
|
|
2237
2575
|
export async function executeTool(name, args, context = {}) {
|
|
2576
|
+
const liveExecutionContext = context;
|
|
2577
|
+
// run_shell can cross several asynchronous permission/hook boundaries before
|
|
2578
|
+
// reaching executeToolInner. Capture its declared tuple and working directory
|
|
2579
|
+
// before any of them so an in-process caller cannot swap the approved command
|
|
2580
|
+
// or reinterpret a relative cwd after process.chdir().
|
|
2581
|
+
let shellAuthorityCwd = null;
|
|
2582
|
+
let shellAuthorityWorkspaceCwd = null;
|
|
2583
|
+
let runCodeAuthorityCwd = null;
|
|
2584
|
+
if (name === "run_shell") {
|
|
2585
|
+
try {
|
|
2586
|
+
const entryProcessCwd = process.cwd();
|
|
2587
|
+
const declared = snapshotShellExecutionArgs(args);
|
|
2588
|
+
const contextCwd =
|
|
2589
|
+
context.cwd === undefined || context.cwd === null
|
|
2590
|
+
? entryProcessCwd
|
|
2591
|
+
: context.cwd;
|
|
2592
|
+
if (typeof contextCwd !== "string" || !contextCwd) {
|
|
2593
|
+
throw new TypeError("run_shell context cwd must be a non-empty string");
|
|
2594
|
+
}
|
|
2595
|
+
if (
|
|
2596
|
+
declared.cwd !== undefined &&
|
|
2597
|
+
(typeof declared.cwd !== "string" || !declared.cwd)
|
|
2598
|
+
) {
|
|
2599
|
+
throw new TypeError("run_shell cwd must be a non-empty string");
|
|
2600
|
+
}
|
|
2601
|
+
shellAuthorityWorkspaceCwd = fs.realpathSync.native(
|
|
2602
|
+
path.resolve(entryProcessCwd, contextCwd),
|
|
2603
|
+
);
|
|
2604
|
+
if (!fs.statSync(shellAuthorityWorkspaceCwd).isDirectory()) {
|
|
2605
|
+
throw new Error("run_shell context cwd is not a directory");
|
|
2606
|
+
}
|
|
2607
|
+
const requestedCwd = path.resolve(
|
|
2608
|
+
shellAuthorityWorkspaceCwd,
|
|
2609
|
+
declared.cwd || ".",
|
|
2610
|
+
);
|
|
2611
|
+
shellAuthorityCwd = fs.realpathSync.native(requestedCwd);
|
|
2612
|
+
if (!fs.statSync(shellAuthorityCwd).isDirectory()) {
|
|
2613
|
+
throw new Error("run_shell cwd is not a directory");
|
|
2614
|
+
}
|
|
2615
|
+
args = snapshotShellExecutionArgs({
|
|
2616
|
+
...declared,
|
|
2617
|
+
cwd: shellAuthorityCwd,
|
|
2618
|
+
});
|
|
2619
|
+
} catch (error) {
|
|
2620
|
+
return {
|
|
2621
|
+
error: `[Shell Dispatch] ${error?.message || "invalid execution arguments"}`,
|
|
2622
|
+
policy: {
|
|
2623
|
+
decision: "blocked",
|
|
2624
|
+
via: "shell-execution-snapshot",
|
|
2625
|
+
code: "CC_SHELL_EXECUTION_SNAPSHOT_INVALID",
|
|
2626
|
+
},
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
if (name === "run_code") {
|
|
2631
|
+
try {
|
|
2632
|
+
const entryProcessCwd = process.cwd();
|
|
2633
|
+
args = snapshotRunCodeExecutionArgs(args);
|
|
2634
|
+
const contextCwd =
|
|
2635
|
+
context.cwd === undefined || context.cwd === null
|
|
2636
|
+
? entryProcessCwd
|
|
2637
|
+
: context.cwd;
|
|
2638
|
+
if (typeof contextCwd !== "string" || !contextCwd) {
|
|
2639
|
+
throw new TypeError("run_code context cwd must be a non-empty string");
|
|
2640
|
+
}
|
|
2641
|
+
runCodeAuthorityCwd = fs.realpathSync.native(
|
|
2642
|
+
path.resolve(entryProcessCwd, contextCwd),
|
|
2643
|
+
);
|
|
2644
|
+
if (!fs.statSync(runCodeAuthorityCwd).isDirectory()) {
|
|
2645
|
+
throw new Error("run_code cwd is not a directory");
|
|
2646
|
+
}
|
|
2647
|
+
} catch (error) {
|
|
2648
|
+
return {
|
|
2649
|
+
error: `[Run Code Dispatch] ${error?.message || "invalid execution arguments"}`,
|
|
2650
|
+
policy: {
|
|
2651
|
+
decision: "blocked",
|
|
2652
|
+
via: "run-code-execution-snapshot",
|
|
2653
|
+
code: "CC_RUN_CODE_EXECUTION_SNAPSHOT_INVALID",
|
|
2654
|
+
},
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2238
2658
|
const hookDb = context.hookDb || null;
|
|
2239
2659
|
const skillLoader = context.skillLoader || _defaultSkillLoader;
|
|
2240
|
-
|
|
2660
|
+
// Keep the authority-bearing workspace root distinct from the command's
|
|
2661
|
+
// requested cwd. A command may run in a nested directory, but Plugin policy
|
|
2662
|
+
// discovery and strong-sandbox issuance must remain anchored to the host
|
|
2663
|
+
// workspace that admitted the tool call.
|
|
2664
|
+
const cwd =
|
|
2665
|
+
shellAuthorityWorkspaceCwd ||
|
|
2666
|
+
runCodeAuthorityCwd ||
|
|
2667
|
+
context.cwd ||
|
|
2668
|
+
process.cwd();
|
|
2241
2669
|
const planManager = context.planManager || getPlanModeManager();
|
|
2242
2670
|
// The provider receives a filtered schema, but an untrusted/buggy model can
|
|
2243
2671
|
// still emit an arbitrary tool_call. Reuse the same pure preflight that the
|
|
@@ -2247,6 +2675,27 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2247
2675
|
cwd,
|
|
2248
2676
|
});
|
|
2249
2677
|
if (authorityDenial) return authorityDenial;
|
|
2678
|
+
let admittedEffectiveToolSetDigest = null;
|
|
2679
|
+
if (name === "run_shell") {
|
|
2680
|
+
try {
|
|
2681
|
+
admittedEffectiveToolSetDigest = digestPolicyAuthority(
|
|
2682
|
+
snapshotEffectiveToolSetAuthority(
|
|
2683
|
+
context.effectiveAllowedToolNames,
|
|
2684
|
+
name,
|
|
2685
|
+
),
|
|
2686
|
+
);
|
|
2687
|
+
} catch (error) {
|
|
2688
|
+
return {
|
|
2689
|
+
error:
|
|
2690
|
+
"[Shell Dispatch] The effective tool capability ceiling could not be bound. No command side effect was started.",
|
|
2691
|
+
policy: {
|
|
2692
|
+
decision: "blocked",
|
|
2693
|
+
via: "shell-policy-authority",
|
|
2694
|
+
code: error?.code || "CC_SHELL_POLICY_AUTHORITY_UNAVAILABLE",
|
|
2695
|
+
},
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2250
2699
|
const localToolDescriptor =
|
|
2251
2700
|
context.externalToolDescriptors &&
|
|
2252
2701
|
typeof context.externalToolDescriptors === "object"
|
|
@@ -2259,41 +2708,41 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2259
2708
|
: null;
|
|
2260
2709
|
const runtimeDescriptor =
|
|
2261
2710
|
getRuntimeToolDescriptor(name) || localToolDescriptor;
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
: {};
|
|
2268
|
-
const tier =
|
|
2269
|
-
override.tier ||
|
|
2270
|
-
runtimeDescriptor?.tier ||
|
|
2271
|
-
getCodingAgentToolPolicy(name)?.tier ||
|
|
2272
|
-
(localToolDescriptor ? "extension" : "mvp");
|
|
2273
|
-
const decision = admitTool({
|
|
2711
|
+
let admittedToolAdmissionDigest = null;
|
|
2712
|
+
let toolAdmissionEvaluation;
|
|
2713
|
+
try {
|
|
2714
|
+
toolAdmissionEvaluation = evaluateSelectedToolAdmissionAuthority({
|
|
2715
|
+
admission: context.toolAdmission,
|
|
2274
2716
|
tool: name,
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
policyAllowed:
|
|
2279
|
-
override.policyAllowed ??
|
|
2280
|
-
(context.hostManagedToolPolicy
|
|
2281
|
-
? (context.hostManagedToolPolicy?.tools ||
|
|
2282
|
-
context.hostManagedToolPolicy?.toolPolicies ||
|
|
2283
|
-
{})[name]?.allowed !== false
|
|
2284
|
-
: admission.policyAllowed),
|
|
2285
|
-
permissionGranted:
|
|
2286
|
-
override.permissionGranted ?? admission.permissionGranted,
|
|
2287
|
-
budgetOk: override.budgetOk ?? admission.budgetOk,
|
|
2288
|
-
uiSupported: override.uiSupported ?? admission.uiSupported,
|
|
2717
|
+
runtimeDescriptor,
|
|
2718
|
+
localToolDescriptor,
|
|
2719
|
+
hostManagedToolPolicy: context.hostManagedToolPolicy,
|
|
2289
2720
|
});
|
|
2721
|
+
if (name === "run_shell") {
|
|
2722
|
+
admittedToolAdmissionDigest = digestPolicyAuthority(
|
|
2723
|
+
toolAdmissionEvaluation.projection,
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
} catch (error) {
|
|
2727
|
+
return {
|
|
2728
|
+
error:
|
|
2729
|
+
"[Tool Admission] The selected tool authority could not be bound. No tool side effect was started.",
|
|
2730
|
+
policy: {
|
|
2731
|
+
decision: "blocked",
|
|
2732
|
+
via: "tool-admission",
|
|
2733
|
+
code: error?.code || "CC_TOOL_ADMISSION_AUTHORITY_UNAVAILABLE",
|
|
2734
|
+
},
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
if (toolAdmissionEvaluation.decision) {
|
|
2738
|
+
const { override, decision } = toolAdmissionEvaluation;
|
|
2290
2739
|
const attribution = buildToolAttribution({
|
|
2291
2740
|
tool: name,
|
|
2292
2741
|
source:
|
|
2293
2742
|
override.source ||
|
|
2294
2743
|
runtimeDescriptor?.source ||
|
|
2295
2744
|
localToolDescriptor?.source ||
|
|
2296
|
-
|
|
2745
|
+
context.toolAdmission?.source ||
|
|
2297
2746
|
null,
|
|
2298
2747
|
version: override.version || runtimeDescriptor?.version || null,
|
|
2299
2748
|
scope: override.scope || localToolDescriptor?.scope || null,
|
|
@@ -2401,20 +2850,112 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2401
2850
|
// shell-policy denylist also still applies.
|
|
2402
2851
|
// No matching rule + no host policy → every existing layer runs unchanged
|
|
2403
2852
|
// (default behaviour is byte-for-byte).
|
|
2404
|
-
|
|
2853
|
+
let permissionAuthority = null;
|
|
2854
|
+
let effectivePermissionRules = context.permissionRules || null;
|
|
2855
|
+
if (typeof context.permissionRulesProvider === "function") {
|
|
2856
|
+
try {
|
|
2857
|
+
permissionAuthority = await context.permissionRulesProvider({
|
|
2858
|
+
cwd,
|
|
2859
|
+
tool: name,
|
|
2860
|
+
args,
|
|
2861
|
+
});
|
|
2862
|
+
effectivePermissionRules =
|
|
2863
|
+
permissionAuthority?.rules || permissionAuthority || null;
|
|
2864
|
+
if (
|
|
2865
|
+
effectivePermissionRules &&
|
|
2866
|
+
(!Array.isArray(effectivePermissionRules.allow) ||
|
|
2867
|
+
!Array.isArray(effectivePermissionRules.ask) ||
|
|
2868
|
+
!Array.isArray(effectivePermissionRules.deny))
|
|
2869
|
+
) {
|
|
2870
|
+
throw new TypeError(
|
|
2871
|
+
"permission authority provider returned an invalid ruleset",
|
|
2872
|
+
);
|
|
2873
|
+
}
|
|
2874
|
+
} catch (error) {
|
|
2875
|
+
return {
|
|
2876
|
+
error:
|
|
2877
|
+
`[Permission Authority] Tool "${name}" was blocked because the current permission authority could not be resolved. ` +
|
|
2878
|
+
"No tool side effect was started.",
|
|
2879
|
+
policy: {
|
|
2880
|
+
decision: "blocked",
|
|
2881
|
+
via: "permission-authority-load",
|
|
2882
|
+
code: error?.code || "CC_PERMISSION_AUTHORITY_UNAVAILABLE",
|
|
2883
|
+
},
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
const evaluatedSettingsVerdict = effectivePermissionRules
|
|
2405
2888
|
? evaluatePermissionRules({
|
|
2406
2889
|
tool: name,
|
|
2407
2890
|
args,
|
|
2408
2891
|
cwd,
|
|
2409
|
-
rules:
|
|
2892
|
+
rules: effectivePermissionRules,
|
|
2410
2893
|
})
|
|
2411
2894
|
: { decision: null, rule: null };
|
|
2895
|
+
const permissionRuleSource = evaluatedSettingsVerdict.rule
|
|
2896
|
+
? permissionAuthority?.sources?.[
|
|
2897
|
+
`${evaluatedSettingsVerdict.decision}:${evaluatedSettingsVerdict.rule}`
|
|
2898
|
+
] || null
|
|
2899
|
+
: null;
|
|
2900
|
+
const matchedScopedRule = permissionRuleSource?.startsWith("scoped:")
|
|
2901
|
+
? permissionAuthority?.scoped?.rules?.find(
|
|
2902
|
+
(record) =>
|
|
2903
|
+
record.status === "active" &&
|
|
2904
|
+
`scoped:${record.id}` === permissionRuleSource,
|
|
2905
|
+
)
|
|
2906
|
+
: null;
|
|
2907
|
+
const settingsVerdict = {
|
|
2908
|
+
...evaluatedSettingsVerdict,
|
|
2909
|
+
source: permissionRuleSource,
|
|
2910
|
+
priority:
|
|
2911
|
+
evaluatedSettingsVerdict.decision === "deny"
|
|
2912
|
+
? 1
|
|
2913
|
+
: evaluatedSettingsVerdict.decision === "ask"
|
|
2914
|
+
? 3
|
|
2915
|
+
: evaluatedSettingsVerdict.decision === "allow"
|
|
2916
|
+
? 4
|
|
2917
|
+
: null,
|
|
2918
|
+
scopedRule: matchedScopedRule || null,
|
|
2919
|
+
};
|
|
2920
|
+
let admittedPermissionPolicyDigest = null;
|
|
2921
|
+
if (name === "run_shell") {
|
|
2922
|
+
try {
|
|
2923
|
+
admittedPermissionPolicyDigest = digestPolicyAuthority(
|
|
2924
|
+
projectPermissionAuthority({
|
|
2925
|
+
authority: permissionAuthority,
|
|
2926
|
+
rules: validatePermissionRuleset(effectivePermissionRules),
|
|
2927
|
+
tool: name,
|
|
2928
|
+
args,
|
|
2929
|
+
cwd,
|
|
2930
|
+
}),
|
|
2931
|
+
);
|
|
2932
|
+
} catch (error) {
|
|
2933
|
+
return {
|
|
2934
|
+
error:
|
|
2935
|
+
"[Shell Dispatch] The evaluated permission authority could not be bound. No command side effect was started.",
|
|
2936
|
+
policy: {
|
|
2937
|
+
decision: "blocked",
|
|
2938
|
+
via: "shell-policy-authority",
|
|
2939
|
+
code: error?.code || "CC_SHELL_POLICY_AUTHORITY_UNAVAILABLE",
|
|
2940
|
+
},
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2412
2944
|
|
|
2413
2945
|
// 1. settings deny
|
|
2414
2946
|
if (settingsVerdict.decision === "deny") {
|
|
2415
2947
|
return {
|
|
2416
2948
|
error: `[Permission] Tool "${name}" denied by settings rule: ${settingsVerdict.rule}. This is a configured policy — retrying won't help; tell the user if the task genuinely needs it.`,
|
|
2417
|
-
policy: {
|
|
2949
|
+
policy: {
|
|
2950
|
+
decision: "deny",
|
|
2951
|
+
rule: settingsVerdict.rule,
|
|
2952
|
+
via: "settings",
|
|
2953
|
+
source: settingsVerdict.source,
|
|
2954
|
+
priority: settingsVerdict.priority,
|
|
2955
|
+
scopedRuleId: settingsVerdict.scopedRule?.id || null,
|
|
2956
|
+
revision: settingsVerdict.scopedRule?.revision || null,
|
|
2957
|
+
expiresAt: settingsVerdict.scopedRule?.expiresAt || null,
|
|
2958
|
+
},
|
|
2418
2959
|
};
|
|
2419
2960
|
}
|
|
2420
2961
|
|
|
@@ -2429,6 +2970,22 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2429
2970
|
toolPolicies && typeof toolPolicies === "object"
|
|
2430
2971
|
? toolPolicies[name]
|
|
2431
2972
|
: null;
|
|
2973
|
+
let admittedHostPolicyDigest = null;
|
|
2974
|
+
if (name === "run_shell") {
|
|
2975
|
+
try {
|
|
2976
|
+
admittedHostPolicyDigest = digestPolicyAuthority(hostToolPolicy);
|
|
2977
|
+
} catch (error) {
|
|
2978
|
+
return {
|
|
2979
|
+
error:
|
|
2980
|
+
"[Shell Dispatch] The evaluated host policy could not be bound. No command side effect was started.",
|
|
2981
|
+
policy: {
|
|
2982
|
+
decision: "blocked",
|
|
2983
|
+
via: "shell-policy-authority",
|
|
2984
|
+
code: error?.code || "CC_SHELL_POLICY_AUTHORITY_UNAVAILABLE",
|
|
2985
|
+
},
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2432
2989
|
const hostPolicyAllowsReadOnlyGit =
|
|
2433
2990
|
name === "git" &&
|
|
2434
2991
|
hostToolPolicy?.planModeBehavior === "readonly-conditional" &&
|
|
@@ -2462,6 +3019,24 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2462
3019
|
planManager.isActive() && !executionLockActive;
|
|
2463
3020
|
const externalToolBlockedDuringPlanning =
|
|
2464
3021
|
planReadOnlyFenceActive && !STATIC_AGENT_TOOL_NAMES.has(name);
|
|
3022
|
+
let admittedPlanPolicyDigest = null;
|
|
3023
|
+
if (name === "run_shell") {
|
|
3024
|
+
try {
|
|
3025
|
+
admittedPlanPolicyDigest = digestPolicyAuthority(
|
|
3026
|
+
snapshotPlanToolAuthority(planManager, name, args),
|
|
3027
|
+
);
|
|
3028
|
+
} catch (error) {
|
|
3029
|
+
return {
|
|
3030
|
+
error:
|
|
3031
|
+
"[Shell Dispatch] The evaluated plan authority could not be bound. No command side effect was started.",
|
|
3032
|
+
policy: {
|
|
3033
|
+
decision: "blocked",
|
|
3034
|
+
via: "shell-policy-authority",
|
|
3035
|
+
code: error?.code || "CC_SHELL_POLICY_AUTHORITY_UNAVAILABLE",
|
|
3036
|
+
},
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
2465
3040
|
context = { ...context, planReadOnlyFenceActive };
|
|
2466
3041
|
if (
|
|
2467
3042
|
planManager.isActive() &&
|
|
@@ -2523,6 +3098,11 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2523
3098
|
decision: "ask",
|
|
2524
3099
|
rule: settingsVerdict.rule,
|
|
2525
3100
|
via: "settings",
|
|
3101
|
+
source: settingsVerdict.source,
|
|
3102
|
+
priority: settingsVerdict.priority,
|
|
3103
|
+
scopedRuleId: settingsVerdict.scopedRule?.id || null,
|
|
3104
|
+
revision: settingsVerdict.scopedRule?.revision || null,
|
|
3105
|
+
expiresAt: settingsVerdict.scopedRule?.expiresAt || null,
|
|
2526
3106
|
},
|
|
2527
3107
|
};
|
|
2528
3108
|
}
|
|
@@ -2755,6 +3335,160 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2755
3335
|
if (instructionPreflight) return instructionPreflight;
|
|
2756
3336
|
}
|
|
2757
3337
|
|
|
3338
|
+
let shellDispatchPolicyAuthority = null;
|
|
3339
|
+
if (name === "run_shell") {
|
|
3340
|
+
try {
|
|
3341
|
+
const permissionRulesProvider = context.permissionRulesProvider || null;
|
|
3342
|
+
const approvalGate = context.approvalGate || null;
|
|
3343
|
+
const initialProjection = {
|
|
3344
|
+
permission: projectPermissionAuthority({
|
|
3345
|
+
authority: permissionAuthority,
|
|
3346
|
+
rules: validatePermissionRuleset(effectivePermissionRules),
|
|
3347
|
+
tool: name,
|
|
3348
|
+
args,
|
|
3349
|
+
cwd,
|
|
3350
|
+
}),
|
|
3351
|
+
host: selectedHostToolPolicy(
|
|
3352
|
+
context.hostManagedToolPolicy || null,
|
|
3353
|
+
name,
|
|
3354
|
+
),
|
|
3355
|
+
plan: snapshotPlanToolAuthority(planManager, name, args),
|
|
3356
|
+
effectiveToolSet: snapshotEffectiveToolSetAuthority(
|
|
3357
|
+
context.effectiveAllowedToolNames,
|
|
3358
|
+
name,
|
|
3359
|
+
),
|
|
3360
|
+
toolAdmission: evaluateSelectedToolAdmissionAuthority({
|
|
3361
|
+
admission: context.toolAdmission,
|
|
3362
|
+
tool: name,
|
|
3363
|
+
runtimeDescriptor,
|
|
3364
|
+
localToolDescriptor,
|
|
3365
|
+
hostManagedToolPolicy: context.hostManagedToolPolicy,
|
|
3366
|
+
}).projection,
|
|
3367
|
+
approval: snapshotApprovalPolicyAuthority(
|
|
3368
|
+
approvalGate,
|
|
3369
|
+
context.sessionId || null,
|
|
3370
|
+
),
|
|
3371
|
+
unattended: context.unattendedActionPolicy || null,
|
|
3372
|
+
shellPolicyOverrides: context.shellPolicyOverrides || null,
|
|
3373
|
+
classifyAllShell: context.classifyAllShell === true,
|
|
3374
|
+
};
|
|
3375
|
+
if (
|
|
3376
|
+
digestPolicyAuthority(initialProjection.permission) !==
|
|
3377
|
+
admittedPermissionPolicyDigest ||
|
|
3378
|
+
digestPolicyAuthority(initialProjection.host) !==
|
|
3379
|
+
admittedHostPolicyDigest ||
|
|
3380
|
+
digestPolicyAuthority(initialProjection.plan) !==
|
|
3381
|
+
admittedPlanPolicyDigest ||
|
|
3382
|
+
digestPolicyAuthority(initialProjection.effectiveToolSet) !==
|
|
3383
|
+
admittedEffectiveToolSetDigest ||
|
|
3384
|
+
digestPolicyAuthority(initialProjection.toolAdmission) !==
|
|
3385
|
+
admittedToolAdmissionDigest
|
|
3386
|
+
) {
|
|
3387
|
+
const error = new Error(
|
|
3388
|
+
"Shell policy authority changed while an earlier confirmation was pending",
|
|
3389
|
+
);
|
|
3390
|
+
error.code = "CC_SHELL_POLICY_AUTHORITY_CHANGED";
|
|
3391
|
+
throw error;
|
|
3392
|
+
}
|
|
3393
|
+
const initialDigest = digestPolicyAuthority(initialProjection);
|
|
3394
|
+
shellDispatchPolicyAuthority = Object.freeze({
|
|
3395
|
+
policyVersion: `cc-shell-policy-authority/v1:${initialDigest}`,
|
|
3396
|
+
async revalidate() {
|
|
3397
|
+
try {
|
|
3398
|
+
if (
|
|
3399
|
+
(liveExecutionContext.permissionRulesProvider || null) !==
|
|
3400
|
+
permissionRulesProvider ||
|
|
3401
|
+
(liveExecutionContext.approvalGate || null) !== approvalGate ||
|
|
3402
|
+
(liveExecutionContext.planManager || getPlanModeManager()) !==
|
|
3403
|
+
planManager
|
|
3404
|
+
) {
|
|
3405
|
+
const error = new Error("policy authority provider changed");
|
|
3406
|
+
error.code = "CC_SHELL_POLICY_AUTHORITY_CHANGED";
|
|
3407
|
+
throw error;
|
|
3408
|
+
}
|
|
3409
|
+
let currentPermissionAuthority = null;
|
|
3410
|
+
let currentPermissionRules =
|
|
3411
|
+
liveExecutionContext.permissionRules || null;
|
|
3412
|
+
if (permissionRulesProvider) {
|
|
3413
|
+
currentPermissionAuthority = await permissionRulesProvider({
|
|
3414
|
+
cwd,
|
|
3415
|
+
tool: name,
|
|
3416
|
+
args,
|
|
3417
|
+
});
|
|
3418
|
+
currentPermissionRules =
|
|
3419
|
+
currentPermissionAuthority?.rules ||
|
|
3420
|
+
currentPermissionAuthority ||
|
|
3421
|
+
null;
|
|
3422
|
+
}
|
|
3423
|
+
const currentProjection = {
|
|
3424
|
+
permission: projectPermissionAuthority({
|
|
3425
|
+
authority: currentPermissionAuthority,
|
|
3426
|
+
rules: validatePermissionRuleset(currentPermissionRules),
|
|
3427
|
+
tool: name,
|
|
3428
|
+
args,
|
|
3429
|
+
cwd,
|
|
3430
|
+
}),
|
|
3431
|
+
host: selectedHostToolPolicy(
|
|
3432
|
+
liveExecutionContext.hostManagedToolPolicy || null,
|
|
3433
|
+
name,
|
|
3434
|
+
),
|
|
3435
|
+
plan: snapshotPlanToolAuthority(planManager, name, args),
|
|
3436
|
+
effectiveToolSet: snapshotEffectiveToolSetAuthority(
|
|
3437
|
+
liveExecutionContext.effectiveAllowedToolNames,
|
|
3438
|
+
name,
|
|
3439
|
+
),
|
|
3440
|
+
toolAdmission: evaluateSelectedToolAdmissionAuthority({
|
|
3441
|
+
admission: liveExecutionContext.toolAdmission,
|
|
3442
|
+
tool: name,
|
|
3443
|
+
runtimeDescriptor,
|
|
3444
|
+
localToolDescriptor,
|
|
3445
|
+
hostManagedToolPolicy:
|
|
3446
|
+
liveExecutionContext.hostManagedToolPolicy,
|
|
3447
|
+
}).projection,
|
|
3448
|
+
approval: snapshotApprovalPolicyAuthority(
|
|
3449
|
+
approvalGate,
|
|
3450
|
+
context.sessionId || null,
|
|
3451
|
+
),
|
|
3452
|
+
unattended: liveExecutionContext.unattendedActionPolicy || null,
|
|
3453
|
+
shellPolicyOverrides:
|
|
3454
|
+
liveExecutionContext.shellPolicyOverrides || null,
|
|
3455
|
+
classifyAllShell: liveExecutionContext.classifyAllShell === true,
|
|
3456
|
+
};
|
|
3457
|
+
if (digestPolicyAuthority(currentProjection) !== initialDigest) {
|
|
3458
|
+
const error = new Error(
|
|
3459
|
+
"Shell permission authority changed after initial admission",
|
|
3460
|
+
);
|
|
3461
|
+
error.code = "CC_SHELL_POLICY_AUTHORITY_CHANGED";
|
|
3462
|
+
throw error;
|
|
3463
|
+
}
|
|
3464
|
+
return true;
|
|
3465
|
+
} catch (error) {
|
|
3466
|
+
if (error?.code === "CC_SHELL_POLICY_AUTHORITY_CHANGED") {
|
|
3467
|
+
throw error;
|
|
3468
|
+
}
|
|
3469
|
+
const wrapped = new Error(
|
|
3470
|
+
"Current shell permission authority could not be revalidated",
|
|
3471
|
+
{ cause: error },
|
|
3472
|
+
);
|
|
3473
|
+
wrapped.code =
|
|
3474
|
+
error?.code || "CC_SHELL_POLICY_AUTHORITY_UNAVAILABLE";
|
|
3475
|
+
throw wrapped;
|
|
3476
|
+
}
|
|
3477
|
+
},
|
|
3478
|
+
});
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
return {
|
|
3481
|
+
error:
|
|
3482
|
+
"[Shell Dispatch] The current permission authority could not be bound. No command side effect was started.",
|
|
3483
|
+
policy: {
|
|
3484
|
+
decision: "blocked",
|
|
3485
|
+
via: "shell-policy-authority",
|
|
3486
|
+
code: error?.code || "CC_SHELL_POLICY_AUTHORITY_UNAVAILABLE",
|
|
3487
|
+
},
|
|
3488
|
+
};
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
|
|
2758
3492
|
// PreToolUse hooks. DB hooks (cc hook add) stay observe-only — a failure
|
|
2759
3493
|
// never blocks. settings.json hooks (context.settingsHooks) are decision-
|
|
2760
3494
|
// capable: a `block` (exit 2 / {decision:block}) stops the tool here, an
|
|
@@ -2865,6 +3599,7 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2865
3599
|
backgroundUsageFailureState: context.backgroundUsageFailureState || null,
|
|
2866
3600
|
planManager,
|
|
2867
3601
|
permissionRules: context.permissionRules || null,
|
|
3602
|
+
permissionRulesProvider: context.permissionRulesProvider || null,
|
|
2868
3603
|
effectiveAllowedToolNames: context.effectiveAllowedToolNames ?? null,
|
|
2869
3604
|
hostManagedToolPolicy: context.hostManagedToolPolicy || null,
|
|
2870
3605
|
externalToolDescriptors: context.externalToolDescriptors || null,
|
|
@@ -2877,12 +3612,14 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2877
3612
|
subtreeInstructionScope:
|
|
2878
3613
|
context.subtreeInstructionScope || context.sessionId || "__legacy__",
|
|
2879
3614
|
shellPolicyOverrides: context.shellPolicyOverrides || null,
|
|
3615
|
+
classifyAllShell: context.classifyAllShell === true,
|
|
2880
3616
|
approvalGate: context.approvalGate || null,
|
|
2881
3617
|
shellConfirm: context.shellConfirm || null,
|
|
2882
3618
|
additionalDirectories: context.additionalDirectories || null,
|
|
2883
3619
|
sandbox: context.sandbox || null,
|
|
2884
3620
|
ruleAllowed,
|
|
2885
3621
|
settingsVerdict,
|
|
3622
|
+
shellDispatchPolicyAuthority,
|
|
2886
3623
|
subAgentDepth: context.subAgentDepth || 0,
|
|
2887
3624
|
subAgentBudget: context.subAgentBudget || null,
|
|
2888
3625
|
sessionBudget: context.sessionBudget || null,
|
|
@@ -4257,6 +4994,7 @@ async function executeToolInner(
|
|
|
4257
4994
|
sandbox,
|
|
4258
4995
|
ruleAllowed = false,
|
|
4259
4996
|
settingsVerdict = null,
|
|
4997
|
+
shellDispatchPolicyAuthority = null,
|
|
4260
4998
|
subAgentDepth = 0,
|
|
4261
4999
|
subAgentBudget = null,
|
|
4262
5000
|
sessionBudget = null,
|
|
@@ -4738,6 +5476,9 @@ async function executeToolInner(
|
|
|
4738
5476
|
}
|
|
4739
5477
|
|
|
4740
5478
|
case "run_shell": {
|
|
5479
|
+
// Bound the declared run_shell data once. Approval and dispatch never
|
|
5480
|
+
// observe later mutation of the caller-owned tool arguments.
|
|
5481
|
+
args = Object.isFrozen(args) ? args : snapshotShellExecutionArgs(args);
|
|
4741
5482
|
if (unattendedActionPolicy?.unattended === true) {
|
|
4742
5483
|
const unattendedVerdict = evaluateUnattendedShellAction(args.command, {
|
|
4743
5484
|
...unattendedActionPolicy,
|
|
@@ -4755,12 +5496,12 @@ async function executeToolInner(
|
|
|
4755
5496
|
}
|
|
4756
5497
|
}
|
|
4757
5498
|
|
|
4758
|
-
const shellPolicyOpts = {
|
|
5499
|
+
const shellPolicyOpts = Object.freeze({
|
|
4759
5500
|
...(shellPolicyOverrides
|
|
4760
|
-
? { overrideRuleIds: shellPolicyOverrides }
|
|
5501
|
+
? { overrideRuleIds: Object.freeze([...shellPolicyOverrides]) }
|
|
4761
5502
|
: {}),
|
|
4762
5503
|
...(classifyAllShell ? { classifyAllShell: true } : {}),
|
|
4763
|
-
};
|
|
5504
|
+
});
|
|
4764
5505
|
// Layer-by-layer explanation chain for a blocked command: which layers
|
|
4765
5506
|
// were consulted and what each said (settings rules → shell policy →
|
|
4766
5507
|
// approval gate). Attached to denial results so `/permissions denials`
|
|
@@ -4796,60 +5537,80 @@ async function executeToolInner(
|
|
|
4796
5537
|
return chain;
|
|
4797
5538
|
};
|
|
4798
5539
|
const override = getRuntimeToolDescriptorByCommand(args.command);
|
|
4799
|
-
let shellPolicy
|
|
5540
|
+
let shellPolicy = evaluateShellCommandPolicy(
|
|
5541
|
+
args.command,
|
|
5542
|
+
shellPolicyOpts,
|
|
5543
|
+
);
|
|
4800
5544
|
let approvalOutcome = null;
|
|
5545
|
+
let approvalAuthorization = null;
|
|
5546
|
+
let approvalAuthorizationContext = null;
|
|
5547
|
+
let approvalGateResult = null;
|
|
4801
5548
|
// A settings `allow` rule (ruleAllowed) pre-authorizes: skip the
|
|
4802
5549
|
// ApprovalGate tier confirm, but still run the hard shell-policy denylist
|
|
4803
5550
|
// below so an allow rule can never re-enable a blocked unsafe command.
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
5551
|
+
const requestShellApproval = async (operationArgs) => {
|
|
5552
|
+
await shellDispatchPolicyAuthority?.revalidate?.();
|
|
5553
|
+
if (approvalGate && !ruleAllowed) {
|
|
5554
|
+
const approvalPolicyVersion = `${shellDispatchPolicyAuthority?.policyVersion || "cc-shell-policy-authority/v1:unobserved"}:shell:${shellPolicy.decision}:${shellPolicy.ruleId || "none"}`;
|
|
5555
|
+
const gated = await evaluateShellCommandWithApproval({
|
|
5556
|
+
command: args.command,
|
|
5557
|
+
args: operationArgs,
|
|
5558
|
+
sessionId,
|
|
5559
|
+
approvalGate,
|
|
5560
|
+
shellPolicyOptions: shellPolicyOpts,
|
|
5561
|
+
workspace: args.cwd || cwd,
|
|
5562
|
+
policyEnv: shellParentEnvironment,
|
|
5563
|
+
policyVersion: approvalPolicyVersion,
|
|
5564
|
+
deferSideEffects: true,
|
|
5565
|
+
});
|
|
5566
|
+
approvalGateResult = gated;
|
|
5567
|
+
shellPolicy = gated.shellPolicy;
|
|
5568
|
+
approvalOutcome = {
|
|
5569
|
+
decision: gated.decision,
|
|
5570
|
+
via: gated.via,
|
|
5571
|
+
riskLevel: gated.riskLevel,
|
|
5572
|
+
policy: gated.policy,
|
|
5573
|
+
};
|
|
5574
|
+
approvalAuthorization = gated.authorization;
|
|
5575
|
+
approvalAuthorizationContext = gated.authorizationContext;
|
|
5576
|
+
if (!gated.allowed) {
|
|
5577
|
+
// Make a policy denial ACTIONABLE for the model (Claude-Code 2.1.193
|
|
5578
|
+
// "denial reasons to transcripts"): tell it this won't change on
|
|
5579
|
+
// retry and to involve the user, so it stops re-issuing the same
|
|
5580
|
+
// blocked command (which otherwise just burns turns + tokens).
|
|
5581
|
+
const tierLabel =
|
|
5582
|
+
typeof gated.policy === "string" ? `"${gated.policy}" ` : "";
|
|
5583
|
+
return attachDescriptor(
|
|
5584
|
+
{
|
|
5585
|
+
error:
|
|
5586
|
+
gated.via === "shell-policy"
|
|
5587
|
+
? `[Shell Policy] ${gated.reason} This command is blocked by policy and will not run — do not retry it; find another approach.`
|
|
5588
|
+
: `[ApprovalGate] command denied by the ${tierLabel}approval policy (via ${gated.via}). Retrying the same command will not help — it needs user approval. Tell the user (they can run it themselves, approve it, or relax the policy) and continue with other work.`,
|
|
5589
|
+
shellCommandPolicy: shellPolicy,
|
|
5590
|
+
approval: approvalOutcome,
|
|
5591
|
+
permissionChain: buildPermissionChain(gated),
|
|
5592
|
+
},
|
|
5593
|
+
override || runtimeDescriptor,
|
|
5594
|
+
);
|
|
5595
|
+
}
|
|
5596
|
+
} else {
|
|
5597
|
+
shellPolicy = evaluateShellCommandPolicy(
|
|
5598
|
+
args.command,
|
|
5599
|
+
shellPolicyOpts,
|
|
4850
5600
|
);
|
|
5601
|
+
if (!shellPolicy.allowed) {
|
|
5602
|
+
return attachDescriptor(
|
|
5603
|
+
{
|
|
5604
|
+
error: `[Shell Policy] ${shellPolicy.reason} This command is blocked by policy and will not run — do not retry it; find another approach.`,
|
|
5605
|
+
shellCommandPolicy: shellPolicy,
|
|
5606
|
+
permissionChain: buildPermissionChain(null),
|
|
5607
|
+
},
|
|
5608
|
+
override || runtimeDescriptor,
|
|
5609
|
+
);
|
|
5610
|
+
}
|
|
4851
5611
|
}
|
|
4852
|
-
|
|
5612
|
+
return null;
|
|
5613
|
+
};
|
|
4853
5614
|
|
|
4854
5615
|
// P1 #8 Windows/PowerShell first-class: per-call args.shell or the
|
|
4855
5616
|
// configured settings `shell.windowsDefault` may route this command
|
|
@@ -4858,7 +5619,7 @@ async function executeToolInner(
|
|
|
4858
5619
|
// the historical default shell byte-identical (useDefaultShell=true).
|
|
4859
5620
|
const { resolveShellInvocation } =
|
|
4860
5621
|
await import("../lib/shell-selector.cjs");
|
|
4861
|
-
|
|
5622
|
+
let shellInv = resolveShellInvocation({
|
|
4862
5623
|
command: args.command,
|
|
4863
5624
|
requested: args.shell,
|
|
4864
5625
|
cwd: args.cwd || cwd,
|
|
@@ -4875,12 +5636,16 @@ async function executeToolInner(
|
|
|
4875
5636
|
// Resolver failures for a matching strict alias are terminal — falling
|
|
4876
5637
|
// back to a shell would bypass target identity and manifest requirements.
|
|
4877
5638
|
let pluginBinInvocation = null;
|
|
4878
|
-
let
|
|
5639
|
+
let pluginBinRuntime = null;
|
|
4879
5640
|
let pluginBinSandboxPolicy = null;
|
|
4880
5641
|
let pluginBinSandboxExecutionContract = null;
|
|
5642
|
+
const backgroundPlatform =
|
|
5643
|
+
typeof _backgroundProcessDeps.platform === "function"
|
|
5644
|
+
? _backgroundProcessDeps.platform()
|
|
5645
|
+
: process.platform;
|
|
4881
5646
|
try {
|
|
4882
5647
|
const pluginBin = await import("../lib/plugin-runtime/bin.js");
|
|
4883
|
-
|
|
5648
|
+
pluginBinRuntime = pluginBin;
|
|
4884
5649
|
pluginBinSandboxPolicy = collectWorkspacePluginBinSandboxPolicy(
|
|
4885
5650
|
pluginBin,
|
|
4886
5651
|
cwd,
|
|
@@ -4904,8 +5669,8 @@ async function executeToolInner(
|
|
|
4904
5669
|
{ cwd },
|
|
4905
5670
|
);
|
|
4906
5671
|
if (
|
|
4907
|
-
|
|
4908
|
-
(
|
|
5672
|
+
backgroundPlatform === "linux" ||
|
|
5673
|
+
(backgroundPlatform === "win32" &&
|
|
4909
5674
|
pluginBinInvocation.runtime === "node")
|
|
4910
5675
|
) {
|
|
4911
5676
|
try {
|
|
@@ -4950,7 +5715,7 @@ async function executeToolInner(
|
|
|
4950
5715
|
const processOrigin = pluginBinInvocation
|
|
4951
5716
|
? "plugin:bin"
|
|
4952
5717
|
: "tool:run_shell";
|
|
4953
|
-
|
|
5718
|
+
let processProvenance = {
|
|
4954
5719
|
...(pluginBinInvocation
|
|
4955
5720
|
? {
|
|
4956
5721
|
pluginId: pluginBinInvocation.pluginId,
|
|
@@ -4984,21 +5749,262 @@ async function executeToolInner(
|
|
|
4984
5749
|
}
|
|
4985
5750
|
: {};
|
|
4986
5751
|
|
|
5752
|
+
// Snapshot process-derived execution inputs before confirmation. The
|
|
5753
|
+
// descriptor carries only an opaque, secret-free environment reference;
|
|
5754
|
+
// the actual child environment remains in this invocation-local closure.
|
|
5755
|
+
const shellParentEnvironment = Object.freeze({ ...process.env });
|
|
5756
|
+
const agentIdentityEnvironment = Object.freeze({
|
|
5757
|
+
CLAUDECODE: "1",
|
|
5758
|
+
...(sessionId
|
|
5759
|
+
? {
|
|
5760
|
+
CC_SESSION_ID: String(sessionId),
|
|
5761
|
+
CLAUDE_CODE_SESSION_ID: String(sessionId),
|
|
5762
|
+
}
|
|
5763
|
+
: {}),
|
|
5764
|
+
});
|
|
5765
|
+
const credentialProxyResult = applyCredentialProxy(
|
|
5766
|
+
{
|
|
5767
|
+
...shellParentEnvironment,
|
|
5768
|
+
...agentIdentityEnvironment,
|
|
5769
|
+
},
|
|
5770
|
+
{ env: shellParentEnvironment },
|
|
5771
|
+
);
|
|
5772
|
+
const shellChildEnvironment = Object.freeze({
|
|
5773
|
+
...credentialProxyResult.env,
|
|
5774
|
+
});
|
|
5775
|
+
const sandboxChildEnvironment = agentIdentityEnvironment;
|
|
5776
|
+
const shellExecutionDescriptor = createShellExecutionDescriptor({
|
|
5777
|
+
args,
|
|
5778
|
+
workspace: args.cwd || cwd,
|
|
5779
|
+
shellInvocation: shellInv,
|
|
5780
|
+
pluginInvocation: pluginBinInvocation,
|
|
5781
|
+
pluginSandboxPolicy: pluginBinSandboxPolicy,
|
|
5782
|
+
pluginSandboxExecutionContract: pluginBinSandboxExecutionContract,
|
|
5783
|
+
sandbox,
|
|
5784
|
+
environmentRef: {
|
|
5785
|
+
id: `run-shell-env:${randomUUID()}`,
|
|
5786
|
+
inheritance: sandbox ? "sandbox-minimal" : "host-snapshot",
|
|
5787
|
+
credentialProxy: credentialProxyResult.enabled === true,
|
|
5788
|
+
maskedNames: Object.freeze([...credentialProxyResult.masked]),
|
|
5789
|
+
},
|
|
5790
|
+
});
|
|
5791
|
+
// From here onward the descriptor itself is the tool-argument snapshot.
|
|
5792
|
+
args = shellExecutionDescriptor;
|
|
5793
|
+
shellInv = shellExecutionDescriptor.execution.shell;
|
|
5794
|
+
const shellSandbox = shellExecutionDescriptor.execution.sandbox;
|
|
5795
|
+
const executionPlugin = shellExecutionDescriptor.execution.plugin;
|
|
5796
|
+
const approvedPluginProjection = executionPlugin
|
|
5797
|
+
? canonicalPolicyAuthorityData(executionPlugin)
|
|
5798
|
+
: null;
|
|
5799
|
+
const approvedWorkspacePluginPolicy = canonicalPolicyAuthorityData(
|
|
5800
|
+
pluginBinSandboxPolicy,
|
|
5801
|
+
);
|
|
5802
|
+
const approvedPluginInvocationAuthority = pluginBinInvocation
|
|
5803
|
+
? canonicalPolicyAuthorityData(
|
|
5804
|
+
projectPluginBinInvocationAuthority(pluginBinInvocation),
|
|
5805
|
+
)
|
|
5806
|
+
: null;
|
|
5807
|
+
const refreshPluginBinExecution = () => {
|
|
5808
|
+
if (!executionPlugin) return;
|
|
5809
|
+
if (!pluginBinRuntime) {
|
|
5810
|
+
const error = new Error(
|
|
5811
|
+
"Plugin bin runtime is unavailable during launch revalidation",
|
|
5812
|
+
);
|
|
5813
|
+
error.code = "ERR_PLUGIN_BIN_DISCOVERY_FAILED";
|
|
5814
|
+
error.pluginBinFailClosed = true;
|
|
5815
|
+
throw error;
|
|
5816
|
+
}
|
|
5817
|
+
const freshInvocation = pluginBinRuntime.resolvePluginBinInvocation(
|
|
5818
|
+
shellExecutionDescriptor.command,
|
|
5819
|
+
{
|
|
5820
|
+
cwd,
|
|
5821
|
+
commandCwd: shellExecutionDescriptor.cwd || cwd,
|
|
5822
|
+
failClosed: true,
|
|
5823
|
+
},
|
|
5824
|
+
);
|
|
5825
|
+
if (!freshInvocation) {
|
|
5826
|
+
const error = new Error(
|
|
5827
|
+
"Approved plugin bin alias is no longer available",
|
|
5828
|
+
);
|
|
5829
|
+
error.code = "ERR_PLUGIN_BIN_LAUNCH_AUTHORITY_CHANGED";
|
|
5830
|
+
error.pluginBinFailClosed = true;
|
|
5831
|
+
throw error;
|
|
5832
|
+
}
|
|
5833
|
+
if (
|
|
5834
|
+
canonicalPolicyAuthorityData(
|
|
5835
|
+
projectPluginBinInvocationAuthority(freshInvocation),
|
|
5836
|
+
) !== approvedPluginInvocationAuthority
|
|
5837
|
+
) {
|
|
5838
|
+
const error = new Error(
|
|
5839
|
+
"Plugin bin manifest, trust, alias, or sandbox authority changed after approval",
|
|
5840
|
+
);
|
|
5841
|
+
error.code = "ERR_PLUGIN_BIN_LAUNCH_AUTHORITY_CHANGED";
|
|
5842
|
+
error.pluginBinFailClosed = true;
|
|
5843
|
+
throw error;
|
|
5844
|
+
}
|
|
5845
|
+
let freshPolicy = collectWorkspacePluginBinSandboxPolicy(
|
|
5846
|
+
pluginBinRuntime,
|
|
5847
|
+
cwd,
|
|
5848
|
+
shellExecutionDescriptor.cwd || cwd,
|
|
5849
|
+
);
|
|
5850
|
+
if (freshInvocation.sandboxPolicy) {
|
|
5851
|
+
freshPolicy = pluginBinRuntime.pinPluginBinSandboxPolicy(
|
|
5852
|
+
{
|
|
5853
|
+
requiredBoundaries: [
|
|
5854
|
+
...(freshPolicy?.requiredBoundaries || []),
|
|
5855
|
+
...(freshInvocation.sandboxPolicy.requiredBoundaries || []),
|
|
5856
|
+
],
|
|
5857
|
+
},
|
|
5858
|
+
{ cwd },
|
|
5859
|
+
);
|
|
5860
|
+
}
|
|
5861
|
+
let freshContract = null;
|
|
5862
|
+
if (
|
|
5863
|
+
freshInvocation.sandboxPolicy &&
|
|
5864
|
+
(backgroundPlatform === "linux" ||
|
|
5865
|
+
(backgroundPlatform === "win32" &&
|
|
5866
|
+
freshInvocation.runtime === "node"))
|
|
5867
|
+
) {
|
|
5868
|
+
freshContract = pluginBinRuntime.createPluginSandboxExecutionContract(
|
|
5869
|
+
freshInvocation,
|
|
5870
|
+
{ sync: shellExecutionDescriptor.run_in_background !== true },
|
|
5871
|
+
);
|
|
5872
|
+
}
|
|
5873
|
+
const freshProjection = createShellExecutionDescriptor({
|
|
5874
|
+
args: shellExecutionDescriptor,
|
|
5875
|
+
workspace: shellExecutionDescriptor.execution.workspace,
|
|
5876
|
+
shellInvocation: shellExecutionDescriptor.execution.shell,
|
|
5877
|
+
pluginInvocation: freshInvocation,
|
|
5878
|
+
pluginSandboxPolicy: freshPolicy,
|
|
5879
|
+
pluginSandboxExecutionContract: freshContract,
|
|
5880
|
+
sandbox: shellExecutionDescriptor.execution.sandbox,
|
|
5881
|
+
environmentRef: shellExecutionDescriptor.execution.environment,
|
|
5882
|
+
}).execution.plugin;
|
|
5883
|
+
if (
|
|
5884
|
+
canonicalPolicyAuthorityData(freshProjection) !==
|
|
5885
|
+
approvedPluginProjection
|
|
5886
|
+
) {
|
|
5887
|
+
const error = new Error(
|
|
5888
|
+
"Plugin bin manifest, trust, alias, or sandbox authority changed after approval",
|
|
5889
|
+
);
|
|
5890
|
+
error.code = "ERR_PLUGIN_BIN_LAUNCH_AUTHORITY_CHANGED";
|
|
5891
|
+
error.pluginBinFailClosed = true;
|
|
5892
|
+
throw error;
|
|
5893
|
+
}
|
|
5894
|
+
pluginBinInvocation = freshInvocation;
|
|
5895
|
+
pluginBinSandboxPolicy = freshPolicy;
|
|
5896
|
+
pluginBinSandboxExecutionContract = freshContract;
|
|
5897
|
+
processProvenance = {
|
|
5898
|
+
pluginId: freshInvocation.pluginId,
|
|
5899
|
+
pluginVersion: freshInvocation.pluginVersion,
|
|
5900
|
+
pluginSource: freshInvocation.pluginSource,
|
|
5901
|
+
pluginExecutableIdentity: freshInvocation.executableIdentity,
|
|
5902
|
+
...(freshPolicy ? { sandboxPolicy: freshPolicy } : {}),
|
|
5903
|
+
...(freshContract ? { sandboxExecutionContract: freshContract } : {}),
|
|
5904
|
+
};
|
|
5905
|
+
};
|
|
5906
|
+
const refreshPluginExecutionAuthority = () => {
|
|
5907
|
+
if (executionPlugin) {
|
|
5908
|
+
refreshPluginBinExecution();
|
|
5909
|
+
return;
|
|
5910
|
+
}
|
|
5911
|
+
if (!pluginBinRuntime) return;
|
|
5912
|
+
const freshPolicy = collectWorkspacePluginBinSandboxPolicy(
|
|
5913
|
+
pluginBinRuntime,
|
|
5914
|
+
cwd,
|
|
5915
|
+
shellExecutionDescriptor.cwd || cwd,
|
|
5916
|
+
);
|
|
5917
|
+
if (
|
|
5918
|
+
canonicalPolicyAuthorityData(freshPolicy) !==
|
|
5919
|
+
approvedWorkspacePluginPolicy
|
|
5920
|
+
) {
|
|
5921
|
+
const error = new Error(
|
|
5922
|
+
"Workspace plugin sandbox authority changed after approval",
|
|
5923
|
+
);
|
|
5924
|
+
error.code = "ERR_PLUGIN_BIN_LAUNCH_AUTHORITY_CHANGED";
|
|
5925
|
+
error.pluginBinFailClosed = true;
|
|
5926
|
+
throw error;
|
|
5927
|
+
}
|
|
5928
|
+
pluginBinSandboxPolicy = freshPolicy;
|
|
5929
|
+
processProvenance = freshPolicy ? { sandboxPolicy: freshPolicy } : {};
|
|
5930
|
+
};
|
|
5931
|
+
|
|
5932
|
+
const approvalDenial = await requestShellApproval(
|
|
5933
|
+
shellExecutionDescriptor,
|
|
5934
|
+
);
|
|
5935
|
+
if (approvalDenial) return approvalDenial;
|
|
5936
|
+
|
|
5937
|
+
let shellDispatchAdmitted = false;
|
|
5938
|
+
const admitShellDispatch = async () => {
|
|
5939
|
+
if (shellDispatchAdmitted) {
|
|
5940
|
+
throw new Error("Shell dispatch authorization was already consumed");
|
|
5941
|
+
}
|
|
5942
|
+
await shellDispatchPolicyAuthority?.revalidate?.();
|
|
5943
|
+
refreshPluginExecutionAuthority();
|
|
5944
|
+
const revalidated = evaluateShellCommandPolicy(
|
|
5945
|
+
shellExecutionDescriptor.command,
|
|
5946
|
+
shellPolicyOpts,
|
|
5947
|
+
);
|
|
5948
|
+
if (
|
|
5949
|
+
!revalidated.allowed ||
|
|
5950
|
+
revalidated.decision !== shellPolicy.decision ||
|
|
5951
|
+
(revalidated.ruleId || null) !== (shellPolicy.ruleId || null)
|
|
5952
|
+
) {
|
|
5953
|
+
const error = new Error(
|
|
5954
|
+
"Shell policy changed after approval; command dispatch was denied",
|
|
5955
|
+
);
|
|
5956
|
+
error.code = "ERR_SHELL_POLICY_REVALIDATION_FAILED";
|
|
5957
|
+
throw error;
|
|
5958
|
+
}
|
|
5959
|
+
if (approvalAuthorization) {
|
|
5960
|
+
if (typeof approvalGate?.consumeAuthorization !== "function") {
|
|
5961
|
+
throw new Error(
|
|
5962
|
+
"Remote shell approval cannot be consumed by this ApprovalGate",
|
|
5963
|
+
);
|
|
5964
|
+
}
|
|
5965
|
+
await approvalGate.consumeAuthorization(
|
|
5966
|
+
approvalAuthorization,
|
|
5967
|
+
approvalAuthorizationContext,
|
|
5968
|
+
);
|
|
5969
|
+
approvalAuthorization = null;
|
|
5970
|
+
}
|
|
5971
|
+
await shellDispatchPolicyAuthority?.revalidate?.();
|
|
5972
|
+
refreshPluginExecutionAuthority();
|
|
5973
|
+
const finalPolicy = evaluateShellCommandPolicy(
|
|
5974
|
+
shellExecutionDescriptor.command,
|
|
5975
|
+
shellPolicyOpts,
|
|
5976
|
+
);
|
|
5977
|
+
if (
|
|
5978
|
+
!finalPolicy.allowed ||
|
|
5979
|
+
finalPolicy.decision !== shellPolicy.decision ||
|
|
5980
|
+
(finalPolicy.ruleId || null) !== (shellPolicy.ruleId || null)
|
|
5981
|
+
) {
|
|
5982
|
+
const error = new Error(
|
|
5983
|
+
"Shell policy changed while authorization was consumed; command dispatch was denied",
|
|
5984
|
+
);
|
|
5985
|
+
error.code = "ERR_SHELL_POLICY_REVALIDATION_FAILED";
|
|
5986
|
+
throw error;
|
|
5987
|
+
}
|
|
5988
|
+
// Auditing is intentionally committed only after durable authorization
|
|
5989
|
+
// consumption and immediately before the first external side effect.
|
|
5990
|
+
commitShellApprovalSideEffects(approvalGateResult);
|
|
5991
|
+
if (pluginBinInvocation) {
|
|
5992
|
+
pluginBinResult.plugin_bin.launch_identity_reattested = true;
|
|
5993
|
+
}
|
|
5994
|
+
shellDispatchAdmitted = true;
|
|
5995
|
+
};
|
|
5996
|
+
|
|
4987
5997
|
// Background: spawn, register, return a task_id immediately. The agent
|
|
4988
5998
|
// polls output + completion via check_shell. No timeout — that's the whole
|
|
4989
5999
|
// point of backgrounding (builds, test suites, dev servers).
|
|
4990
6000
|
if (args.run_in_background === true) {
|
|
4991
|
-
if (
|
|
6001
|
+
if (shellSandbox) {
|
|
4992
6002
|
return attachDescriptor({
|
|
4993
6003
|
error:
|
|
4994
6004
|
"[Sandbox] Background shell tasks are not supported in the ephemeral sandbox. Run in the foreground or explicitly disable --sandbox.",
|
|
4995
6005
|
policy: { decision: "deny", via: "sandbox" },
|
|
4996
6006
|
});
|
|
4997
6007
|
}
|
|
4998
|
-
const backgroundPlatform =
|
|
4999
|
-
typeof _backgroundProcessDeps.platform === "function"
|
|
5000
|
-
? _backgroundProcessDeps.platform()
|
|
5001
|
-
: process.platform;
|
|
5002
6008
|
const pinnedBackgroundBoundaries = [
|
|
5003
6009
|
...(pluginBinSandboxPolicy?.requiredBoundaries || []),
|
|
5004
6010
|
];
|
|
@@ -5050,16 +6056,7 @@ async function executeToolInner(
|
|
|
5050
6056
|
cwd: workingDirectory,
|
|
5051
6057
|
shell: false,
|
|
5052
6058
|
windowsHide: true,
|
|
5053
|
-
env:
|
|
5054
|
-
...process.env,
|
|
5055
|
-
CLAUDECODE: "1",
|
|
5056
|
-
...(sessionId
|
|
5057
|
-
? {
|
|
5058
|
-
CC_SESSION_ID: String(sessionId),
|
|
5059
|
-
CLAUDE_CODE_SESSION_ID: String(sessionId),
|
|
5060
|
-
}
|
|
5061
|
-
: {}),
|
|
5062
|
-
}).env,
|
|
6059
|
+
env: shellChildEnvironment,
|
|
5063
6060
|
detached: false,
|
|
5064
6061
|
origin: "tool:run_shell",
|
|
5065
6062
|
policy: "allow",
|
|
@@ -5114,6 +6111,9 @@ async function executeToolInner(
|
|
|
5114
6111
|
},
|
|
5115
6112
|
};
|
|
5116
6113
|
}
|
|
6114
|
+
// Admission precedes task cleanup, sequence allocation, listener
|
|
6115
|
+
// registration, proxy startup and process creation.
|
|
6116
|
+
await admitShellDispatch();
|
|
5117
6117
|
// Free memory from idle background tasks before adding another, so a
|
|
5118
6118
|
// long agent run can't accumulate forgotten dev servers (no-op unless
|
|
5119
6119
|
// the machine is actually under memory pressure).
|
|
@@ -5125,7 +6125,7 @@ async function executeToolInner(
|
|
|
5125
6125
|
cwd:
|
|
5126
6126
|
linuxGenericBackgroundLaunch?.options.cwd ||
|
|
5127
6127
|
(linuxDirectPluginStrongBackground
|
|
5128
|
-
?
|
|
6128
|
+
? executionPlugin.sandboxExecution.workingDirectory
|
|
5129
6129
|
: args.cwd || cwd),
|
|
5130
6130
|
status: "running",
|
|
5131
6131
|
exitCode: null,
|
|
@@ -5156,16 +6156,7 @@ async function executeToolInner(
|
|
|
5156
6156
|
// Credential proxy (opt-in, CC_CREDENTIAL_PROXY): keeps the agent's
|
|
5157
6157
|
// real long-lived secrets out of the spawned command's env — a
|
|
5158
6158
|
// no-op (same object) when disabled. See credential-proxy.js.
|
|
5159
|
-
env:
|
|
5160
|
-
...process.env,
|
|
5161
|
-
CLAUDECODE: "1",
|
|
5162
|
-
...(sessionId
|
|
5163
|
-
? {
|
|
5164
|
-
CC_SESSION_ID: String(sessionId),
|
|
5165
|
-
CLAUDE_CODE_SESSION_ID: String(sessionId),
|
|
5166
|
-
}
|
|
5167
|
-
: {}),
|
|
5168
|
-
}).env,
|
|
6159
|
+
env: shellChildEnvironment,
|
|
5169
6160
|
// POSIX: own process group so check_shell{kill}/teardown can signal
|
|
5170
6161
|
// the whole tree (shell + its grandchild command). No-op on Windows
|
|
5171
6162
|
// where the tree is killed via taskkill /T instead.
|
|
@@ -5192,18 +6183,16 @@ async function executeToolInner(
|
|
|
5192
6183
|
linuxGenericBackgroundLaunch.options,
|
|
5193
6184
|
);
|
|
5194
6185
|
} else if (pluginBinInvocation) {
|
|
5195
|
-
reattestPluginBinInvocation(pluginBinInvocation);
|
|
5196
|
-
pluginBinResult.plugin_bin.launch_identity_reattested = true;
|
|
5197
6186
|
child = broker.spawn(
|
|
5198
|
-
|
|
6187
|
+
executionPlugin.sandboxExecution?.kind ===
|
|
5199
6188
|
"strict-plugin-node-bin"
|
|
5200
|
-
?
|
|
5201
|
-
:
|
|
5202
|
-
|
|
6189
|
+
? executionPlugin.sandboxExecution.runtimePath
|
|
6190
|
+
: executionPlugin.command,
|
|
6191
|
+
executionPlugin.argv,
|
|
5203
6192
|
{
|
|
5204
6193
|
...brokerOpts,
|
|
5205
6194
|
cwd:
|
|
5206
|
-
|
|
6195
|
+
executionPlugin.sandboxExecution?.workingDirectory ||
|
|
5207
6196
|
brokerOpts.cwd,
|
|
5208
6197
|
shell: false,
|
|
5209
6198
|
},
|
|
@@ -5299,7 +6288,7 @@ async function executeToolInner(
|
|
|
5299
6288
|
);
|
|
5300
6289
|
}
|
|
5301
6290
|
|
|
5302
|
-
if (
|
|
6291
|
+
if (shellSandbox) {
|
|
5303
6292
|
if (pluginBinSandboxPolicy) {
|
|
5304
6293
|
return attachDescriptor(
|
|
5305
6294
|
{
|
|
@@ -5317,15 +6306,16 @@ async function executeToolInner(
|
|
|
5317
6306
|
override || runtimeDescriptor,
|
|
5318
6307
|
);
|
|
5319
6308
|
}
|
|
6309
|
+
await admitShellDispatch();
|
|
5320
6310
|
const { executeSandboxedShell, sandboxSummary } =
|
|
5321
6311
|
await import("../lib/agent-sandbox.js");
|
|
5322
6312
|
// Domain-restricted networking is ENFORCED by routing the sandboxed
|
|
5323
6313
|
// process's egress through a local filtering proxy (see
|
|
5324
6314
|
// sandbox-egress-proxy.js). Start it only when the policy actually
|
|
5325
6315
|
// restricts domains and network is on; tear it down after the command.
|
|
5326
|
-
const sboxPolicy =
|
|
6316
|
+
const sboxPolicy = shellSandbox.policy || {};
|
|
5327
6317
|
const needsEgress =
|
|
5328
|
-
|
|
6318
|
+
shellSandbox.network === true &&
|
|
5329
6319
|
(sboxPolicy.allowedDomains?.length || 0) +
|
|
5330
6320
|
(sboxPolicy.deniedDomains?.length || 0) >
|
|
5331
6321
|
0;
|
|
@@ -5354,20 +6344,25 @@ async function executeToolInner(
|
|
|
5354
6344
|
}
|
|
5355
6345
|
let result;
|
|
5356
6346
|
try {
|
|
5357
|
-
|
|
6347
|
+
// Proxy startup and dynamic imports are asynchronous. Re-read the
|
|
6348
|
+
// live authority once more after they settle and immediately before
|
|
6349
|
+
// the synchronous sandbox dispatch.
|
|
6350
|
+
await shellDispatchPolicyAuthority?.revalidate?.();
|
|
6351
|
+
refreshPluginExecutionAuthority();
|
|
6352
|
+
if (pluginBinSandboxPolicy) {
|
|
6353
|
+
const error = new Error(
|
|
6354
|
+
"A plugin sandbox authority appeared while the legacy shell sandbox was starting",
|
|
6355
|
+
);
|
|
6356
|
+
error.code = "ERR_PLUGIN_BIN_SANDBOX_ROUTE_CONFLICT";
|
|
6357
|
+
error.pluginBinFailClosed = true;
|
|
6358
|
+
throw error;
|
|
6359
|
+
}
|
|
6360
|
+
result = executeSandboxedShell(args.command, shellSandbox, {
|
|
5358
6361
|
cwd: args.cwd || cwd,
|
|
5359
6362
|
timeout: _resolveShellTimeout(args.timeout),
|
|
5360
6363
|
maxBuffer: 1024 * 1024,
|
|
5361
6364
|
egressProxy,
|
|
5362
|
-
env:
|
|
5363
|
-
CLAUDECODE: "1",
|
|
5364
|
-
...(sessionId
|
|
5365
|
-
? {
|
|
5366
|
-
CC_SESSION_ID: String(sessionId),
|
|
5367
|
-
CLAUDE_CODE_SESSION_ID: String(sessionId),
|
|
5368
|
-
}
|
|
5369
|
-
: {}),
|
|
5370
|
-
},
|
|
6365
|
+
env: sandboxChildEnvironment,
|
|
5371
6366
|
});
|
|
5372
6367
|
} finally {
|
|
5373
6368
|
if (proxyHandle) {
|
|
@@ -5379,7 +6374,7 @@ async function executeToolInner(
|
|
|
5379
6374
|
}
|
|
5380
6375
|
}
|
|
5381
6376
|
const common = {
|
|
5382
|
-
sandbox: sandboxSummary(
|
|
6377
|
+
sandbox: sandboxSummary(shellSandbox),
|
|
5383
6378
|
shellCommandPolicy: shellPolicy,
|
|
5384
6379
|
approval: approvalOutcome,
|
|
5385
6380
|
policyTrace: ["shell-policy", "approval", "sandbox"],
|
|
@@ -5418,18 +6413,10 @@ async function executeToolInner(
|
|
|
5418
6413
|
// Credential proxy (opt-in, CC_CREDENTIAL_PROXY): keeps the agent's
|
|
5419
6414
|
// real long-lived secrets out of the shell's env — a no-op (same
|
|
5420
6415
|
// object) when disabled. See credential-proxy.js.
|
|
5421
|
-
env:
|
|
5422
|
-
...process.env,
|
|
5423
|
-
CLAUDECODE: "1",
|
|
5424
|
-
...(sessionId
|
|
5425
|
-
? {
|
|
5426
|
-
CC_SESSION_ID: String(sessionId),
|
|
5427
|
-
CLAUDE_CODE_SESSION_ID: String(sessionId),
|
|
5428
|
-
}
|
|
5429
|
-
: {}),
|
|
5430
|
-
}).env,
|
|
6416
|
+
env: shellChildEnvironment,
|
|
5431
6417
|
};
|
|
5432
6418
|
let output;
|
|
6419
|
+
await admitShellDispatch();
|
|
5433
6420
|
const brokerExecOpts = {
|
|
5434
6421
|
...fgExecOpts,
|
|
5435
6422
|
origin: processOrigin,
|
|
@@ -5438,17 +6425,15 @@ async function executeToolInner(
|
|
|
5438
6425
|
...processProvenance,
|
|
5439
6426
|
};
|
|
5440
6427
|
if (pluginBinInvocation) {
|
|
5441
|
-
reattestPluginBinInvocation(pluginBinInvocation);
|
|
5442
|
-
pluginBinResult.plugin_bin.launch_identity_reattested = true;
|
|
5443
6428
|
const res = broker.spawnSync(
|
|
5444
|
-
|
|
5445
|
-
?
|
|
5446
|
-
:
|
|
5447
|
-
|
|
6429
|
+
executionPlugin.sandboxExecution?.kind === "strict-plugin-node-bin"
|
|
6430
|
+
? executionPlugin.sandboxExecution.runtimePath
|
|
6431
|
+
: executionPlugin.command,
|
|
6432
|
+
executionPlugin.argv,
|
|
5448
6433
|
{
|
|
5449
6434
|
...brokerExecOpts,
|
|
5450
6435
|
cwd:
|
|
5451
|
-
|
|
6436
|
+
executionPlugin.sandboxExecution?.workingDirectory ||
|
|
5452
6437
|
brokerExecOpts.cwd,
|
|
5453
6438
|
shell: false,
|
|
5454
6439
|
windowsHide: true,
|
|
@@ -5527,6 +6512,15 @@ async function executeToolInner(
|
|
|
5527
6512
|
: {}),
|
|
5528
6513
|
stderr: (err.stderr || "").substring(0, 2000),
|
|
5529
6514
|
exitCode: err.status,
|
|
6515
|
+
...(typeof err.code === "string" && err.code
|
|
6516
|
+
? {
|
|
6517
|
+
policy: {
|
|
6518
|
+
decision: "deny",
|
|
6519
|
+
via: "dispatch-revalidation",
|
|
6520
|
+
code: err.code,
|
|
6521
|
+
},
|
|
6522
|
+
}
|
|
6523
|
+
: {}),
|
|
5530
6524
|
...shellMeta,
|
|
5531
6525
|
...pluginBinResult,
|
|
5532
6526
|
shellCommandPolicy: shellPolicy,
|
|
@@ -5650,8 +6644,18 @@ async function executeToolInner(
|
|
|
5650
6644
|
approvalGate &&
|
|
5651
6645
|
typeof approvalGate.hasConfirmer === "function" &&
|
|
5652
6646
|
!approvalGate.hasConfirmer();
|
|
6647
|
+
// A durable remote-approval consumer is an explicit fail-closed mode.
|
|
6648
|
+
// run_code does not yet carry an exact lease tuple, so it must still
|
|
6649
|
+
// consult the remote confirmer in headless sessions; that confirmer
|
|
6650
|
+
// returns lease-unavailable and no code is persisted or launched.
|
|
6651
|
+
const durableAuthorizationGate =
|
|
6652
|
+
approvalGate &&
|
|
6653
|
+
typeof approvalGate.hasAuthorizationConsumer === "function" &&
|
|
6654
|
+
approvalGate.hasAuthorizationConsumer() === true;
|
|
5653
6655
|
if (
|
|
5654
|
-
(interactiveApproval ||
|
|
6656
|
+
(interactiveApproval ||
|
|
6657
|
+
policyGateNoConfirmer ||
|
|
6658
|
+
durableAuthorizationGate) &&
|
|
5655
6659
|
approvalGate &&
|
|
5656
6660
|
!ruleAllowed &&
|
|
5657
6661
|
typeof approvalGate.decide === "function"
|
|
@@ -5662,7 +6666,7 @@ async function executeToolInner(
|
|
|
5662
6666
|
sessionId,
|
|
5663
6667
|
riskLevel: APPROVAL_RISK.HIGH,
|
|
5664
6668
|
tool: "run_code",
|
|
5665
|
-
args:
|
|
6669
|
+
args: runCodeApprovalDescriptor(args, cwd),
|
|
5666
6670
|
});
|
|
5667
6671
|
if (gate.decision !== APPROVAL_DECISION.ALLOW) {
|
|
5668
6672
|
// Actionable denial (matches run_shell, Claude-Code 2.1.193): tell the
|
|
@@ -5679,6 +6683,23 @@ async function executeToolInner(
|
|
|
5679
6683
|
},
|
|
5680
6684
|
});
|
|
5681
6685
|
}
|
|
6686
|
+
if (gate.authorization) {
|
|
6687
|
+
return attachDescriptor({
|
|
6688
|
+
error:
|
|
6689
|
+
"[ApprovalGate] run_code received a durable authorization, but this tool does not yet implement an exact one-shot consume tuple. No code was launched.",
|
|
6690
|
+
approval: {
|
|
6691
|
+
decision: APPROVAL_DECISION.DENY,
|
|
6692
|
+
via: "authorization-consume-unsupported",
|
|
6693
|
+
riskLevel: "high",
|
|
6694
|
+
policy: gate.policy,
|
|
6695
|
+
},
|
|
6696
|
+
policy: {
|
|
6697
|
+
decision: "deny",
|
|
6698
|
+
via: "authorization-consume-unsupported",
|
|
6699
|
+
code: "CC_RUN_CODE_AUTHORIZATION_CONSUME_UNSUPPORTED",
|
|
6700
|
+
},
|
|
6701
|
+
});
|
|
6702
|
+
}
|
|
5682
6703
|
}
|
|
5683
6704
|
return attachDescriptor(await _executeRunCode(args, cwd));
|
|
5684
6705
|
}
|
|
@@ -8719,6 +9740,9 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
8719
9740
|
// Parent execution authority is inherited as a tighten-only bundle and is
|
|
8720
9741
|
// re-enforced by the child's executeTool path on every provider tool call.
|
|
8721
9742
|
...(ctx.permissionRules ? { permissionRules: ctx.permissionRules } : {}),
|
|
9743
|
+
...(ctx.permissionRulesProvider
|
|
9744
|
+
? { permissionRulesProvider: ctx.permissionRulesProvider }
|
|
9745
|
+
: {}),
|
|
8722
9746
|
...(childHostManagedToolPolicy
|
|
8723
9747
|
? { hostManagedToolPolicy: childHostManagedToolPolicy }
|
|
8724
9748
|
: {}),
|
|
@@ -11351,6 +12375,9 @@ export async function* agentLoop(messages, options) {
|
|
|
11351
12375
|
: options.additionalDirectories || null,
|
|
11352
12376
|
sandbox: options.sandbox || null,
|
|
11353
12377
|
permissionRules: hermeticExecution ? null : options.permissionRules || null,
|
|
12378
|
+
permissionRulesProvider: hermeticExecution
|
|
12379
|
+
? null
|
|
12380
|
+
: options.permissionRulesProvider || null,
|
|
11354
12381
|
permissionConfirm: hermeticExecution
|
|
11355
12382
|
? null
|
|
11356
12383
|
: options.permissionConfirm || null,
|