chainlesschain 0.164.0 → 0.165.2
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/README.md +248 -2
- package/package.json +7 -4
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-D2MEn7fI.js → AIOps-lS1R0Cza.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-lCW_4Gf0.js → ActionButton-BmlgBPsj.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Bs7i-nsS.js → Analytics-CNKczv1S.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CK7B6S_j.js → AppLayout-BuZGJARQ.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-C8ILjpyo.js → Artifacts-Dm73Sl-O.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bb9cNaWc.js → Audit-BGMW7A5e.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-sC9ooXIf.js → BackgroundAgents-BQ6M54JR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D-DrsplT.js → Backup-DBzGP0bR.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-uCNZpwSK.js → BaseInput-D1mLCoOV.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CsDK1uE9.js → Chat-BUfcN7p8.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-OqSDt50w.js → ChatBubbleRenderer-CY5m8ZHg.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-JhTXM1-K.js → Checkbox-Du0Yg5Pi.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BbzZLnyD.js → Codegen-B2RXziHW.js} +1 -1
- package/src/assets/web-panel/assets/{Col-D4VW4m4j.js → Col-DWhekOrR.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BXrYawxV.js → Community-HQhrkon4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DCihGy1u.js → Compact-izHNb4uz.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DlyZ-lk8.js → Compliance-BDwLy41-.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DvGZrhbO.js → Cowork-DFJhLa7K.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DUncZi8h.js → Cron-DYY9RG7-.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-C_hLuUcm.js → Crosschain-rr7tJTtv.js} +1 -1
- package/src/assets/web-panel/assets/{DID-b6YSUqxr.js → DID-D2sfHRLi.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-uvM15Cfb.js → Dashboard-q_eGdhha.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-D1-Nd4qY.js → Dropdown-Cufrh2Bz.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-8ApvL6Zc.js → EmailListRenderer-B-kdv10f.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-LYJt8811.js → FamilyGuardDashboard-D21XjgfG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BbI3HGpv.js → Federation-CAwRTLZH.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DSoEKuFu.js → FormItemContext-vx3daNDF.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B0S9OWd5.js → GenericCardRenderer-CRWY2wU4.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DIs9Zhf3.js → Git-Q6SwauyD.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-jzZXrxcl.js → Governance-BJ1q4IQZ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-JUPjf2ZI.js → Inference-CSWjROgD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DxnfJvlP.js → KnowledgeGraph-fzuQsWuY.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-iXiz8BEE.js → Logs-DVF8Mnns.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Y3Xch312.js → Marketplace-BQP7LOA4.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D7kGGbO7.js → McpTools-BNlJCmNc.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-Do_hVxzh.js → Memory-DLLg5WQ4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CtrR0Tas.js → MobileBridge-CR2skNAF.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-BxVavObl.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-qo5QCTGd.js → Mtc-B17pgqMI.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-cq5mnjYj.js → MtcAudit-4UmV5cIL.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-Bq0MAQHw.js → Multisig-DoMEXEcU.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DdWE6Vi2.js → NLProgramming-h9DMXFX4.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BxMBFcIp.js → Notes-Cihn-xX8.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-RTrAcbUe.js → NotificationSettings-BE6ZGa3R.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DJbpGqzT.js → OrderTableRenderer-CnwojoSI.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-SQzIPdqC.js → Organization-B2vpwmjb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-02yECh22.js → Overflow-C4z28R0-.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BNg4pNCM.js → P2P-BVlFkwhW.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BxPJZsD3.js → PdhVaultBrowser-BXbEdL5B.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CLnDnPAz.js → Permissions-C28xs4lp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C5m0AjGf.js → PersonalDataHub-Dgl_rBjM.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-VwOf5Dhv.js → Pipeline-3QUIhrKI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CC0W2hZd.js → Privacy-8Gh33OZ5.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Dz893Bob.js → ProjectInit-BNmg4tZi.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CxQkvl0k.js → ProjectSettings-vgMfeCf9.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BhtAVilc.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DeB9IkxK.js → Providers-CXoOa8eX.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-OCeoaswn.js → QrScannerModal-BhfTF0oS.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DenotaAj.js → QuickAsk-C746HEAj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cies_jjI.js → Recommend-BSej8Eo0.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-f3gmnlG3.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-CW0mvxgZ.js → Reputation-CyvybpkN.js} +1 -1
- package/src/assets/web-panel/assets/{Row-SEd32qZU.js → Row-DwV2PeYn.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BSwnJ3Lm.js → RssFeed-11GrzjZy.js} +3 -3
- package/src/assets/web-panel/assets/{Search-xe2m8bkC.js → Search-BjKXkrAY.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Mq2F9Nro.js → Security-eWiFb9Yk.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BKGw2V3P.js → Services-CdCsMAtt.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DW95-Rxf.js → Skeleton-4Q2UTKQ5.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CHgChCv_.js → Skills-v4rtYrRj.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-tKh6k3R7.js → Sla-CRhsFvd_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-D0Fw8fVL.js → SyncSettings-D16zonl7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-20x1GB3D.js → Tasks-Xxozz0i_.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DJ-lts-a.js → Templates-CXJpPtCT.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BFdGpbs7.js → Tenant-XRV0L1ZF.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Xpx61vHL.js → Terminal-CYuO1i_w.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C6UIvyZQ.js → TimelineRenderer-BXqitWsu.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Dellq08f.js → Tokens-BvPrDHIy.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-VKw8d0J-.js → Trigger-DhCrSTf5.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DNftdKT5.js → Trust-DmsuZD6K.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DX9VAKJI.js → UkeySign-D_yrts5C.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B2h3jO8q.js → VideoEditing-B01ihPrm.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CjGkof7f.js → Wallet-DpUjKa-N.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-2Zfcu55P.js → WebAuthn-CL07yON6.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B9eZ8UxZ.js → WorkflowEditor-CLMflnRr.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CQNfD-YO.js → chat-DtUqe8h_.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D4usyFT2.js → colors-DPKrgVfy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-s4TpjvjX.js → compact-item-BTt2GtOm.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BRCSvYxj.js → createContext-D_VeUc1J.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-AdkUbkV-.js → hasIn-1kndiylj.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIoG5yYf.js → index-2eyAXE3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-nXyk8GZ9.js → index-4bna2_SR.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2V-eKfK.js → index-8nz_fmlj.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_XBHopw.js → index-B9KvcS7R.js} +1 -1
- package/src/assets/web-panel/assets/{index-s_buFMEG.js → index-BAXfRKfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZT36ZYq.js → index-BHpBMWCh.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMcimU-2.js → index-BaZAbw9A.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzRjHxEv.js → index-BepLZwgB.js} +1 -1
- package/src/assets/web-panel/assets/{index--00U6vXZ.js → index-Bhudc_Ez.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7Y8Aluc.js → index-BlDNVgBt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtobShro.js → index-By5n8up9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cvyt-zhm.js → index-CHG-KcS4.js} +1 -1
- package/src/assets/web-panel/assets/index-CPcM-eqo.js +1 -0
- package/src/assets/web-panel/assets/{index-MMN9V6JF.js → index-CX81qSM4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bad4VNvz.js → index-CauoBQkH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCsM_Ybx.js → index-CcO04eDn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DARsAyUp.js → index-CtF7EN49.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5KAQFPQ.js → index-CzjkKUmR.js} +1 -1
- package/src/assets/web-panel/assets/{index-RfMr79MW.js → index-D95OFht7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdCB70SS.js → index-DFIZ_1Sf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYdoQsa1.js → index-DG2MZQ3f.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPhkFZ1I.js → index-DJ8vXGOa.js} +1 -1
- package/src/assets/web-panel/assets/{index-_IRVsV9X.js → index-Dbbymxpj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVKfQ0Ux.js → index-DdkplL9R.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkwiSLhU.js → index-DfGl8CqG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJVEOa4i.js → index-DfKBRdSS.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1mJtE6q.js → index-DsJCDUJG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgiXR_-_.js → index-Dwqmtk65.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJCFe2Qf.js → index-DxdTdBix.js} +1 -1
- package/src/assets/web-panel/assets/{index-C484EJiS.js → index-FzdwcSy2.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4JJCfQ2.js → index-GsoUQ2M7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQhAVU5t.js → index-brCInrzq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKC1t3Ic.js → index-dVc-CXY4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQ3DXBtD.js → index-fk2Rjacw.js} +3 -3
- package/src/assets/web-panel/assets/{index-Cduu2Hhr.js → index-l1BETrDb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bw2MEnUk.js → index-m31Ka8hZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2GjI2oe.js → index-ocf06EVQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJdQrZth.js → index-sDoMHTNA.js} +1 -1
- package/src/assets/web-panel/assets/index-xeSyVoRe.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-Blmpk7HL.js → initDefaultProps-CvyYJd7z.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DPBJMv1n.js → motion-DYZkhUdz.js} +1 -1
- package/src/assets/web-panel/assets/{move-ButdzCM7.js → move-slnJRVT1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-cyRC_QCe.js → mtc-parser-DVc0CKmy.js} +1 -1
- package/src/assets/web-panel/assets/{omit-1epSV7hz.js → omit-BCXAtHCq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DuPY8xJs.js → pickAttrs-DBsemz_K.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-6XZqarhe.js → placementArrow-BRtkJ-nv.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-B40pXBWM.js → responsiveObserve-Cebq7EZn.js} +1 -1
- package/src/assets/web-panel/assets/{slide-jbZ9Wu4n.js → slide-DT7qvId2.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BKQQcVy4.js → statusUtils-pQiITLkG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-NiKfWjUT.js → styleChecker-c-19BVKQ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-bQUCvPeb.js → useFlexGapSupport-DsS5-0bE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CUGji7ph.js → useFs-CxGiMe5n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-4veyN2N2.js → usePersonalDataHub-D9btVID5.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-1-nLug-Z.js → vnode-CUYFsFEN.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-dhCeRDMo.js → zoom-CXmn_35j.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/cowork.js +559 -62
- package/src/commands/plugin.js +819 -64
- package/src/commands/session-location.js +318 -7
- package/src/constants.js +2 -2
- package/src/data/changelog.json +28 -2
- package/src/gateways/ws/action-protocol.js +293 -19
- package/src/gateways/ws/message-dispatcher.js +30 -0
- package/src/gateways/ws/remote-session-protocol.js +542 -35
- package/src/gateways/ws/ws-approval-gate.js +5 -0
- package/src/gateways/ws/ws-server.js +462 -38
- package/src/harness/jsonl-session-store.js +638 -2
- package/src/harness/prompt-compressor.js +4 -0
- package/src/harness/remote-session-audit.js +1 -0
- package/src/harness/remote-session-crypto.js +750 -50
- package/src/harness/remote-session-registry.js +795 -27
- package/src/lib/auto-mode-config.js +98 -3
- package/src/lib/background-agent-keeper-client.js +29 -0
- package/src/lib/background-agent-keeper-protocol.js +158 -10
- package/src/lib/background-agent-supervisor.js +870 -48
- package/src/lib/background-session-transport.js +23 -6
- package/src/lib/background-turn-bootstrap-protocol.js +64 -0
- package/src/lib/cowork-task-runner.js +70 -0
- package/src/lib/cowork-workflow.js +887 -62
- package/src/lib/delivery-production-adapter.js +38 -2
- package/src/lib/doctor-checkup.js +34 -10
- package/src/lib/dynamic-workflow-draft.js +535 -0
- package/src/lib/dynamic-workflow-facade.js +1105 -17
- package/src/lib/dynamic-workflow-runtime.js +2974 -0
- package/src/lib/execution-location-contract.js +111 -7
- package/src/lib/execution-location-runtime.js +3 -1
- package/src/lib/execution-location-target.js +910 -0
- package/src/lib/llm-providers.js +1 -0
- package/src/lib/mcp-call-ledger-store.js +35 -0
- package/src/lib/mcp-call-ledger.js +30 -0
- package/src/lib/mcp-stdio-package-materialization.js +1 -1
- package/src/lib/plugin-runtime/__tests__/remote-source.test.js +34 -16
- package/src/lib/plugin-runtime/install.js +4956 -657
- package/src/lib/plugin-runtime/manifest.js +3 -1
- package/src/lib/plugin-runtime/marketplace-artifact-readback.js +671 -31
- package/src/lib/plugin-runtime/marketplace-catalog.js +68 -2
- package/src/lib/plugin-runtime/marketplace-impact.js +208 -15
- package/src/lib/plugin-runtime/marketplace-network.js +317 -0
- package/src/lib/plugin-runtime/marketplace-pac-worker.js +15 -0
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +25 -2
- package/src/lib/plugin-runtime/marketplace-source-cache.js +359 -0
- package/src/lib/plugin-runtime/policy.js +21 -6
- package/src/lib/plugin-runtime/publisher-trust.js +332 -0
- package/src/lib/plugin-runtime/remote-source.js +323 -27
- package/src/lib/plugin-runtime/scopes.js +317 -41
- package/src/lib/plugin-runtime/transaction-journal.js +806 -0
- package/src/lib/public-ide-capability-manifest.js +1 -1
- package/src/lib/remote-approval-bridge.js +745 -108
- package/src/lib/remote-control.js +2 -0
- package/src/lib/remote-membership-coordinator.js +612 -46
- package/src/lib/remote-membership-host-store.js +59 -10
- package/src/lib/runtime-provenance-ledger.js +171 -46
- package/src/lib/shell-approval.js +169 -6
- package/src/lib/sub-agent-context.js +268 -2
- package/src/lib/task-model-selector.js +1 -1
- package/src/lib/with-file-lock.js +167 -42
- package/src/repl/agent-repl.js +70 -29
- package/src/repl/remote-approval.js +29 -7
- package/src/runtime/agent-core.js +1695 -194
- package/src/runtime/headless-runner.js +22 -5
- package/src/runtime/headless-stream.js +19 -3
- package/src/workers/background-agent-keeper.js +279 -58
- package/src/workers/background-agent-worker.js +7 -0
- package/src/workers/background-turn-bootstrap.js +29 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-GmB6DOr6.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-BL0Czcoj.js +0 -1
- package/src/assets/web-panel/assets/Projects-DDAjkUkT.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-Dxzl5n2T.js +0 -5
- package/src/assets/web-panel/assets/SpeechSettings-D6UL3dA5.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DKJjSWRn.js +0 -1
- package/src/assets/web-panel/assets/index-C61cnl3K.js +0 -1
- package/src/assets/web-panel/assets/index-D4XFHxBr.js +0 -1
|
@@ -1460,6 +1460,8 @@ async function runAgentHeadlessInWorkspace(
|
|
|
1460
1460
|
if (!hermeticExecution) {
|
|
1461
1461
|
try {
|
|
1462
1462
|
approvalGate = await getApprovalGate();
|
|
1463
|
+
approvalGate =
|
|
1464
|
+
approvalGate?.createSessionScope?.(sessionId) || approvalGate;
|
|
1463
1465
|
if (approvalGate && (options.permissionMode || "default") === "auto") {
|
|
1464
1466
|
// autoMode.decisions: user-configured riskLevel → allow/ask/deny
|
|
1465
1467
|
// classifier. Only wrap when settings actually customize the map so the
|
|
@@ -1483,6 +1485,7 @@ async function runAgentHeadlessInWorkspace(
|
|
|
1483
1485
|
}
|
|
1484
1486
|
}
|
|
1485
1487
|
if (approvalGate) {
|
|
1488
|
+
approvalGate.setAuthorizationConsumer?.(null);
|
|
1486
1489
|
if (typeof approvalGate.setSessionPolicy === "function") {
|
|
1487
1490
|
approvalGate.setSessionPolicy(sessionId, perm.sessionPolicy);
|
|
1488
1491
|
}
|
|
@@ -1980,6 +1983,18 @@ async function runAgentHeadlessInWorkspace(
|
|
|
1980
1983
|
!options.permissionPromptTool
|
|
1981
1984
|
) {
|
|
1982
1985
|
try {
|
|
1986
|
+
if (
|
|
1987
|
+
!approvalGate ||
|
|
1988
|
+
typeof approvalGate.setConfirmer !== "function" ||
|
|
1989
|
+
typeof approvalGate.setAuthorizationConsumer !== "function" ||
|
|
1990
|
+
typeof approvalGate.consumeAuthorization !== "function"
|
|
1991
|
+
) {
|
|
1992
|
+
const error = new Error(
|
|
1993
|
+
"installed session-core cannot bind and consume durable remote approval",
|
|
1994
|
+
);
|
|
1995
|
+
error.code = "CC_REMOTE_APPROVAL_GATE_UNAVAILABLE";
|
|
1996
|
+
throw error;
|
|
1997
|
+
}
|
|
1983
1998
|
const { startHeadlessRemoteApproval } =
|
|
1984
1999
|
await import("../lib/remote-approval-bridge.js");
|
|
1985
2000
|
_remoteApproval = await (
|
|
@@ -1989,11 +2004,12 @@ async function runAgentHeadlessInWorkspace(
|
|
|
1989
2004
|
writeErr,
|
|
1990
2005
|
isText,
|
|
1991
2006
|
});
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2007
|
+
approvalGate.setConfirmer(
|
|
2008
|
+
_bgPhase.wrapConfirmer(_remoteApproval.confirmer),
|
|
2009
|
+
);
|
|
2010
|
+
approvalGate.setAuthorizationConsumer(
|
|
2011
|
+
_remoteApproval.consumeAuthorization,
|
|
2012
|
+
);
|
|
1997
2013
|
} catch (err) {
|
|
1998
2014
|
// Remote approval could not come up → keep headless fail-closed rather
|
|
1999
2015
|
// than running un-gated; say why so the user can fix pairing.
|
|
@@ -3274,6 +3290,7 @@ async function runAgentHeadlessInWorkspace(
|
|
|
3274
3290
|
await cleanupDeadline.run("remote-approval", () =>
|
|
3275
3291
|
_remoteApproval?.close?.(),
|
|
3276
3292
|
);
|
|
3293
|
+
approvalGate?.setAuthorizationConsumer?.(null);
|
|
3277
3294
|
// settings.json Stop + SessionEnd hooks when the run finishes. Stop is the
|
|
3278
3295
|
// canonical async-hook trigger ("run the test suite at turn end"); fire its
|
|
3279
3296
|
// async hooks fire-and-forget, then settle so a fast background check can
|
|
@@ -2059,6 +2059,8 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2059
2059
|
let approvalGate = null;
|
|
2060
2060
|
try {
|
|
2061
2061
|
approvalGate = await getApprovalGate();
|
|
2062
|
+
approvalGate =
|
|
2063
|
+
approvalGate?.createSessionScope?.(sessionId) || approvalGate;
|
|
2062
2064
|
if (approvalGate && (options.permissionMode || "default") === "auto") {
|
|
2063
2065
|
// autoMode.decisions: user-configured riskLevel → allow/ask/deny
|
|
2064
2066
|
// classifier (same wiring as headless-runner). Only wrap when settings
|
|
@@ -2082,6 +2084,7 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2082
2084
|
}
|
|
2083
2085
|
}
|
|
2084
2086
|
if (approvalGate) {
|
|
2087
|
+
approvalGate.setAuthorizationConsumer?.(null);
|
|
2085
2088
|
approvalGate.setSessionPolicy?.(sessionId, perm.sessionPolicy);
|
|
2086
2089
|
approvalGate.setConfirmer?.(
|
|
2087
2090
|
interactive && perm.allowInteractiveApprovals
|
|
@@ -2742,6 +2745,18 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2742
2745
|
options.interactiveApprovals !== true
|
|
2743
2746
|
) {
|
|
2744
2747
|
try {
|
|
2748
|
+
if (
|
|
2749
|
+
!approvalGate ||
|
|
2750
|
+
typeof approvalGate.setConfirmer !== "function" ||
|
|
2751
|
+
typeof approvalGate.setAuthorizationConsumer !== "function" ||
|
|
2752
|
+
typeof approvalGate.consumeAuthorization !== "function"
|
|
2753
|
+
) {
|
|
2754
|
+
const error = new Error(
|
|
2755
|
+
"installed session-core cannot bind and consume durable remote approval",
|
|
2756
|
+
);
|
|
2757
|
+
error.code = "CC_REMOTE_APPROVAL_GATE_UNAVAILABLE";
|
|
2758
|
+
throw error;
|
|
2759
|
+
}
|
|
2745
2760
|
const startRemoteApproval =
|
|
2746
2761
|
deps.startHeadlessRemoteApproval ||
|
|
2747
2762
|
(await import("../lib/remote-approval-bridge.js"))
|
|
@@ -2757,9 +2772,10 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2757
2772
|
remote_session_id: _remoteApproval.pairing.remoteSessionId,
|
|
2758
2773
|
expires_at: _remoteApproval.pairing.expiresAt,
|
|
2759
2774
|
});
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2775
|
+
approvalGate.setConfirmer(_remoteApproval.confirmer);
|
|
2776
|
+
approvalGate.setAuthorizationConsumer(
|
|
2777
|
+
_remoteApproval.consumeAuthorization,
|
|
2778
|
+
);
|
|
2763
2779
|
} catch (err) {
|
|
2764
2780
|
emit({
|
|
2765
2781
|
type: "remote_control",
|
|
@@ -27,11 +27,15 @@ import {
|
|
|
27
27
|
BACKGROUND_AGENT_KEEPER_ARMED,
|
|
28
28
|
BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS,
|
|
29
29
|
BACKGROUND_AGENT_KEEPER_HELLO,
|
|
30
|
+
BACKGROUND_AGENT_KEEPER_HEARTBEAT,
|
|
31
|
+
BACKGROUND_AGENT_KEEPER_HEARTBEAT_TIMEOUT_MS,
|
|
32
|
+
BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
|
|
30
33
|
BACKGROUND_AGENT_KEEPER_PROTOCOL_VERSION,
|
|
31
34
|
BACKGROUND_AGENT_KEEPER_READY,
|
|
32
35
|
BACKGROUND_AGENT_KEEPER_RETIRE,
|
|
33
36
|
BACKGROUND_AGENT_KEEPER_RETIRED,
|
|
34
37
|
BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS,
|
|
38
|
+
cleanupBackgroundAgentKeeperPipeDirectory,
|
|
35
39
|
createBackgroundAgentKeeperMessage,
|
|
36
40
|
normalizeBackgroundAgentKeeperHello,
|
|
37
41
|
normalizeBackgroundAgentKeeperTurn,
|
|
@@ -40,6 +44,50 @@ import {
|
|
|
40
44
|
|
|
41
45
|
const STARTUP_TIMEOUT_MS = 30_000;
|
|
42
46
|
const HEARTBEAT_INTERVAL_MS = 1_000;
|
|
47
|
+
const KEEPER_PERSIST_RETRY_DELAY_MS = 50;
|
|
48
|
+
|
|
49
|
+
function delay(ms) {
|
|
50
|
+
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Retry a cleanup-critical state mutation after a worker is hard-killed.
|
|
55
|
+
*
|
|
56
|
+
* A worker can disappear while it owns the cross-process state lock. Windows
|
|
57
|
+
* may keep that terminated PID observable briefly, so the strict lock cannot
|
|
58
|
+
* immediately prove the owner dead and correctly fails closed. The keeper is
|
|
59
|
+
* the only remaining owner of the armed turn; it must wait for that transient
|
|
60
|
+
* fence instead of turning a lock exception into an unhandled rejection and
|
|
61
|
+
* exiting with the durable projection still `armed`.
|
|
62
|
+
*/
|
|
63
|
+
export async function retryKeeperPersistence(operation, options = {}) {
|
|
64
|
+
const now = options.now || Date.now;
|
|
65
|
+
const sleep = options.sleep || delay;
|
|
66
|
+
const timeoutMs = Math.max(
|
|
67
|
+
0,
|
|
68
|
+
Number(
|
|
69
|
+
options.timeoutMs ?? BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
const retryDelayMs = Math.max(
|
|
73
|
+
1,
|
|
74
|
+
Number(options.retryDelayMs ?? KEEPER_PERSIST_RETRY_DELAY_MS),
|
|
75
|
+
);
|
|
76
|
+
const deadline = now() + timeoutMs;
|
|
77
|
+
let attempts = 0;
|
|
78
|
+
let error = null;
|
|
79
|
+
for (;;) {
|
|
80
|
+
attempts += 1;
|
|
81
|
+
try {
|
|
82
|
+
return { result: operation(), error: null, attempts };
|
|
83
|
+
} catch (cause) {
|
|
84
|
+
error = cause;
|
|
85
|
+
}
|
|
86
|
+
const remaining = deadline - now();
|
|
87
|
+
if (remaining <= 0) return { result: null, error, attempts };
|
|
88
|
+
await sleep(Math.min(retryDelayMs, remaining));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
43
91
|
|
|
44
92
|
function writeMessage(socket, message) {
|
|
45
93
|
if (!socket || socket.destroyed) return false;
|
|
@@ -59,6 +107,71 @@ export function keeperWorkerIdentityAlive(
|
|
|
59
107
|
return probe(Number(workerPid), Number(workerStartedAt));
|
|
60
108
|
}
|
|
61
109
|
|
|
110
|
+
function reportKeeperPersistenceFailure(stage, error) {
|
|
111
|
+
try {
|
|
112
|
+
writeSync(
|
|
113
|
+
2,
|
|
114
|
+
`[background-agent-keeper] ${stage} persistence failed: ${error?.stack || error?.message || String(error)}\n`,
|
|
115
|
+
);
|
|
116
|
+
} catch {
|
|
117
|
+
// The inherited diagnostic handle may already be closed.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function runBackgroundAgentKeeperHeartbeat({
|
|
122
|
+
finishing,
|
|
123
|
+
workerSocket,
|
|
124
|
+
authenticatedHello,
|
|
125
|
+
authenticatedWorkerStartedAt,
|
|
126
|
+
workerHeartbeatAt,
|
|
127
|
+
armedTurn,
|
|
128
|
+
persistKeeper,
|
|
129
|
+
finishForWorkerDisconnect,
|
|
130
|
+
now = Date.now,
|
|
131
|
+
workerIdentityAlive = keeperWorkerIdentityAlive,
|
|
132
|
+
reportPersistenceFailure = reportKeeperPersistenceFailure,
|
|
133
|
+
}) {
|
|
134
|
+
if (finishing) return false;
|
|
135
|
+
const currentTime = now();
|
|
136
|
+
const workerHeartbeatExpired =
|
|
137
|
+
workerSocket &&
|
|
138
|
+
authenticatedHello &&
|
|
139
|
+
Number.isFinite(workerHeartbeatAt) &&
|
|
140
|
+
currentTime - workerHeartbeatAt >
|
|
141
|
+
BACKGROUND_AGENT_KEEPER_HEARTBEAT_TIMEOUT_MS;
|
|
142
|
+
if (
|
|
143
|
+
workerSocket &&
|
|
144
|
+
authenticatedHello &&
|
|
145
|
+
(workerHeartbeatExpired ||
|
|
146
|
+
(armedTurn &&
|
|
147
|
+
!workerIdentityAlive(
|
|
148
|
+
authenticatedHello.workerPid,
|
|
149
|
+
authenticatedWorkerStartedAt,
|
|
150
|
+
)))
|
|
151
|
+
) {
|
|
152
|
+
// A Windows named-pipe handle and process object can remain observable
|
|
153
|
+
// after its worker dies if a platform helper retained a duplicate.
|
|
154
|
+
// Socket EOF and PID visibility are therefore not sufficient lifetime
|
|
155
|
+
// signals. The authenticated application heartbeat provides an
|
|
156
|
+
// independent bounded fence, while the PID/start anchor remains the
|
|
157
|
+
// immediate path on platforms that report death promptly.
|
|
158
|
+
finishForWorkerDisconnect();
|
|
159
|
+
workerSocket.destroy();
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
persistKeeper({ keeperHeartbeatAt: currentTime });
|
|
164
|
+
return true;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
try {
|
|
167
|
+
reportPersistenceFailure("heartbeat", error);
|
|
168
|
+
} catch {
|
|
169
|
+
// Diagnostics are best effort and must not escape the timer callback.
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
62
175
|
function stateOwnsTurn(state, turn) {
|
|
63
176
|
return Boolean(
|
|
64
177
|
state &&
|
|
@@ -90,35 +203,77 @@ function persistKeeperTurn(job, turn, patch) {
|
|
|
90
203
|
);
|
|
91
204
|
}
|
|
92
205
|
|
|
206
|
+
export function stopBackgroundAgentKeeperTurnTrees(targets, options = {}) {
|
|
207
|
+
const platform = options.platform || process.platform;
|
|
208
|
+
const processAlive = options.isProcessAlive || isProcessAlive;
|
|
209
|
+
const processTreeExecutionAlive =
|
|
210
|
+
options.isProcessTreeExecutionAlive ||
|
|
211
|
+
isBackgroundProcessTreeExecutionAlive;
|
|
212
|
+
const stopTree = options.stopProcessTree || stopBackgroundAgentChildTree;
|
|
213
|
+
const failures = [];
|
|
214
|
+
let precedingTreeStopSucceeded = false;
|
|
215
|
+
for (const target of targets) {
|
|
216
|
+
// On Windows the wrapper's taskkill /T also retires its runtime child.
|
|
217
|
+
// Avoid starting a second bounded taskkill for a target the first tree
|
|
218
|
+
// operation already proved absent; under 20-way churn that redundant
|
|
219
|
+
// command can consume the keeper's entire cleanup SLO. Never skip the
|
|
220
|
+
// first root merely because its leader exited: POSIX descendants can keep
|
|
221
|
+
// executing in the leader's process group and still require a group kill.
|
|
222
|
+
if (precedingTreeStopSucceeded && !processAlive(target.pid)) continue;
|
|
223
|
+
try {
|
|
224
|
+
stopTree(target.pid, {
|
|
225
|
+
signal: "SIGKILL",
|
|
226
|
+
expectedStartedAt: target.startedAt,
|
|
227
|
+
processGroup: target.processGroup,
|
|
228
|
+
strictIdentity: true,
|
|
229
|
+
allowDirectFallback: false,
|
|
230
|
+
});
|
|
231
|
+
precedingTreeStopSucceeded = true;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
// A detached POSIX group can keep executing after its leader exits. Do
|
|
234
|
+
// not discard the strict stop failure from a leader-only liveness probe;
|
|
235
|
+
// the group-aware residual check preserves the actionable identity
|
|
236
|
+
// reason while descendants remain executable.
|
|
237
|
+
const leaderAlive = processAlive(target.pid);
|
|
238
|
+
const residualAlive =
|
|
239
|
+
leaderAlive ||
|
|
240
|
+
(platform !== "win32" &&
|
|
241
|
+
target.processGroup === true &&
|
|
242
|
+
processTreeExecutionAlive(target.pid, target.startedAt));
|
|
243
|
+
if (residualAlive) {
|
|
244
|
+
failures.push(error?.message || String(error));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return failures;
|
|
249
|
+
}
|
|
250
|
+
|
|
93
251
|
async function cleanupTurn(job, turn, reason) {
|
|
94
252
|
const requestedAt = Date.now();
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
253
|
+
const requestedPersistence = await retryKeeperPersistence(() =>
|
|
254
|
+
persistKeeperTurn(job, turn, {
|
|
255
|
+
turnKeeperStatus: "cleanup-requested",
|
|
256
|
+
turnKeeperCleanupReason: reason,
|
|
257
|
+
turnKeeperCleanupRequestedAt: requestedAt,
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
100
260
|
|
|
101
261
|
const targets = [
|
|
102
|
-
{
|
|
262
|
+
{
|
|
263
|
+
pid: turn.agentPid,
|
|
264
|
+
startedAt: turn.agentStartedAt,
|
|
265
|
+
processGroup: true,
|
|
266
|
+
},
|
|
103
267
|
{
|
|
104
268
|
pid: turn.agentRuntimePid,
|
|
105
269
|
startedAt: turn.agentRuntimeStartedAt,
|
|
270
|
+
processGroup: false,
|
|
106
271
|
},
|
|
107
272
|
].filter(
|
|
108
273
|
(target, index, values) =>
|
|
109
274
|
values.findIndex((candidate) => candidate.pid === target.pid) === index,
|
|
110
275
|
);
|
|
111
|
-
const failures =
|
|
112
|
-
for (const target of targets) {
|
|
113
|
-
try {
|
|
114
|
-
stopBackgroundAgentChildTree(target.pid, { signal: "SIGKILL" });
|
|
115
|
-
} catch (error) {
|
|
116
|
-
// A preceding wrapper/group signal may already have retired this root.
|
|
117
|
-
if (isProcessAlive(target.pid)) {
|
|
118
|
-
failures.push(error?.message || String(error));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
276
|
+
const failures = stopBackgroundAgentKeeperTurnTrees(targets);
|
|
122
277
|
|
|
123
278
|
const deadline =
|
|
124
279
|
Date.now() + BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS;
|
|
@@ -132,19 +287,37 @@ async function cleanupTurn(job, turn, reason) {
|
|
|
132
287
|
);
|
|
133
288
|
}
|
|
134
289
|
const confirmed = alive.length === 0 && failures.length === 0;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
290
|
+
let finalPersistenceAttempts = 0;
|
|
291
|
+
const finalPersistence = await retryKeeperPersistence(() => {
|
|
292
|
+
finalPersistenceAttempts += 1;
|
|
293
|
+
return persistKeeperTurn(job, turn, {
|
|
294
|
+
turnKeeperStatus: confirmed ? "retired" : "cleanup-unconfirmed",
|
|
295
|
+
turnKeeperCleanupReason: reason,
|
|
296
|
+
turnKeeperCleanupRequestedAt: requestedAt,
|
|
297
|
+
turnKeeperCleanupConfirmedAt: confirmed ? Date.now() : null,
|
|
298
|
+
turnKeeperCleanupError:
|
|
299
|
+
failures.length > 0
|
|
300
|
+
? failures.join("; ").slice(0, 1_000)
|
|
301
|
+
: alive.length > 0
|
|
302
|
+
? `process tree still executable: ${alive.map(({ pid }) => pid).join(",")}`
|
|
303
|
+
: null,
|
|
304
|
+
turnKeeperPersistenceRetries:
|
|
305
|
+
Math.max(0, requestedPersistence.attempts - 1) +
|
|
306
|
+
Math.max(0, finalPersistenceAttempts - 1),
|
|
307
|
+
});
|
|
146
308
|
});
|
|
147
|
-
|
|
309
|
+
const persistenceError = finalPersistence.error
|
|
310
|
+
? finalPersistence.error?.message || String(finalPersistence.error)
|
|
311
|
+
: finalPersistence.result?.applied === false
|
|
312
|
+
? "keeper cleanup state no longer owned"
|
|
313
|
+
: null;
|
|
314
|
+
if (persistenceError) failures.push(persistenceError);
|
|
315
|
+
return {
|
|
316
|
+
confirmed: confirmed && !persistenceError,
|
|
317
|
+
failures,
|
|
318
|
+
alive,
|
|
319
|
+
persistenceError,
|
|
320
|
+
};
|
|
148
321
|
}
|
|
149
322
|
|
|
150
323
|
export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
@@ -182,6 +355,7 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
182
355
|
let candidateSocket = null;
|
|
183
356
|
let authenticatedHello = null;
|
|
184
357
|
let authenticatedWorkerStartedAt = null;
|
|
358
|
+
let workerHeartbeatAt = null;
|
|
185
359
|
let armedTurn = null;
|
|
186
360
|
let finishing = false;
|
|
187
361
|
let finishResolve;
|
|
@@ -206,17 +380,50 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
206
380
|
if (finishing) return false;
|
|
207
381
|
finishing = true;
|
|
208
382
|
const active = armedTurn;
|
|
209
|
-
void (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
383
|
+
void (async () => {
|
|
384
|
+
let cleanup;
|
|
385
|
+
try {
|
|
386
|
+
cleanup = active
|
|
387
|
+
? await cleanupTurn(job, active, "worker-disconnected")
|
|
388
|
+
: { confirmed: true, failures: [], alive: [] };
|
|
389
|
+
} catch (error) {
|
|
390
|
+
cleanup = {
|
|
391
|
+
confirmed: false,
|
|
392
|
+
failures: [error?.message || String(error)],
|
|
393
|
+
alive: [],
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
// Closing the keeper must never be skipped because one final metadata
|
|
397
|
+
// update throws. Retry the critical status write, retain the failure in
|
|
398
|
+
// the log, and always settle the server promise without an unhandled
|
|
399
|
+
// rejection.
|
|
400
|
+
const persisted = await retryKeeperPersistence(() =>
|
|
401
|
+
persistKeeper({
|
|
402
|
+
keeperStatus: active
|
|
403
|
+
? cleanup.confirmed
|
|
404
|
+
? "worker-disconnected"
|
|
405
|
+
: "cleanup-unconfirmed"
|
|
406
|
+
: "closed",
|
|
407
|
+
keeperEndedAt: Date.now(),
|
|
408
|
+
...(cleanup.failures.length > 0
|
|
409
|
+
? {
|
|
410
|
+
keeperError: cleanup.failures.join("; ").slice(0, 1_000),
|
|
411
|
+
}
|
|
412
|
+
: {}),
|
|
413
|
+
}),
|
|
414
|
+
);
|
|
415
|
+
if (persisted.error) {
|
|
416
|
+
try {
|
|
417
|
+
writeSync(
|
|
418
|
+
2,
|
|
419
|
+
`[background-agent-keeper] final state persistence failed: ${persisted.error?.stack || persisted.error?.message || String(persisted.error)}\n`,
|
|
420
|
+
);
|
|
421
|
+
} catch {
|
|
422
|
+
// The inherited diagnostic handle may already be closed.
|
|
423
|
+
}
|
|
424
|
+
}
|
|
218
425
|
server.close(() => finishResolve({ status: "closed" }));
|
|
219
|
-
});
|
|
426
|
+
})();
|
|
220
427
|
return true;
|
|
221
428
|
};
|
|
222
429
|
|
|
@@ -267,6 +474,7 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
267
474
|
authenticated = true;
|
|
268
475
|
authenticatedHello = hello;
|
|
269
476
|
authenticatedWorkerStartedAt = Number(current.startedAt);
|
|
477
|
+
workerHeartbeatAt = Date.now();
|
|
270
478
|
workerSocket = socket;
|
|
271
479
|
clearTimeout(helloTimer);
|
|
272
480
|
persistKeeper({ keeperStatus: "ready", keeperReadyAt: Date.now() });
|
|
@@ -284,6 +492,20 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
284
492
|
return;
|
|
285
493
|
}
|
|
286
494
|
|
|
495
|
+
if (message.type === BACKGROUND_AGENT_KEEPER_HEARTBEAT) {
|
|
496
|
+
if (
|
|
497
|
+
message.id !== authenticatedHello.id ||
|
|
498
|
+
message.workerGeneration !==
|
|
499
|
+
authenticatedHello.workerGeneration ||
|
|
500
|
+
Number(message.workerPid) !== authenticatedHello.workerPid
|
|
501
|
+
) {
|
|
502
|
+
socket.destroy();
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
workerHeartbeatAt = Date.now();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
287
509
|
if (
|
|
288
510
|
message.type !== BACKGROUND_AGENT_KEEPER_ARM &&
|
|
289
511
|
message.type !== BACKGROUND_AGENT_KEEPER_RETIRE
|
|
@@ -413,26 +635,20 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
413
635
|
socket.once("error", disconnected);
|
|
414
636
|
});
|
|
415
637
|
|
|
416
|
-
const heartbeat = setInterval(
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
authenticatedHello.workerPid,
|
|
638
|
+
const heartbeat = setInterval(
|
|
639
|
+
() =>
|
|
640
|
+
runBackgroundAgentKeeperHeartbeat({
|
|
641
|
+
finishing,
|
|
642
|
+
workerSocket,
|
|
643
|
+
authenticatedHello,
|
|
423
644
|
authenticatedWorkerStartedAt,
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
workerSocket.destroy();
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
persistKeeper({ keeperHeartbeatAt: Date.now() });
|
|
435
|
-
}, HEARTBEAT_INTERVAL_MS);
|
|
645
|
+
workerHeartbeatAt,
|
|
646
|
+
armedTurn,
|
|
647
|
+
persistKeeper,
|
|
648
|
+
finishForWorkerDisconnect,
|
|
649
|
+
}),
|
|
650
|
+
HEARTBEAT_INTERVAL_MS,
|
|
651
|
+
);
|
|
436
652
|
heartbeat.unref?.();
|
|
437
653
|
const startupTimer = setTimeout(
|
|
438
654
|
() => {
|
|
@@ -464,6 +680,11 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
464
680
|
} catch {
|
|
465
681
|
// Already removed or never materialized.
|
|
466
682
|
}
|
|
683
|
+
try {
|
|
684
|
+
cleanupBackgroundAgentKeeperPipeDirectory(job.pipePath);
|
|
685
|
+
} catch {
|
|
686
|
+
// Another keeper can still own a sibling socket in the shared namespace.
|
|
687
|
+
}
|
|
467
688
|
}
|
|
468
689
|
return {
|
|
469
690
|
...result,
|
|
@@ -1472,6 +1472,13 @@ async function main() {
|
|
|
1472
1472
|
},
|
|
1473
1473
|
onStop: () => {
|
|
1474
1474
|
if (!child) return;
|
|
1475
|
+
// An explicit stop owns this turn's termination. Detach the bootstrap
|
|
1476
|
+
// release callback before killing the child so a late IPC EPIPE cannot
|
|
1477
|
+
// reclassify the intentional stop as a post-spawn launch failure. The
|
|
1478
|
+
// child termination settlement remains attached and advances any
|
|
1479
|
+
// queued follow-up turn after the tree is confirmed closed.
|
|
1480
|
+
detachTurnBootstrapHandler?.();
|
|
1481
|
+
detachTurnBootstrapHandler = null;
|
|
1475
1482
|
// The agent child is detached into its own group on POSIX; Windows
|
|
1476
1483
|
// requires brokered taskkill /T /F so tool grandchildren cannot
|
|
1477
1484
|
// survive an attached-session stop.
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
BACKGROUND_TURN_BOOTSTRAP_RELEASE,
|
|
15
15
|
backgroundTurnBootstrapBindingFromEnvironment,
|
|
16
16
|
clearBackgroundTurnBootstrapEnvironment,
|
|
17
|
+
containReleasedBackgroundTurnDisconnect,
|
|
17
18
|
createBackgroundTurnBootstrapMessage,
|
|
18
19
|
matchesBackgroundTurnBootstrapMessage,
|
|
19
20
|
removeBackgroundTurnBootstrapImport,
|
|
@@ -31,12 +32,16 @@ function releaseTimeoutMs() {
|
|
|
31
32
|
return Math.max(10, Math.min(TEST_RELEASE_TIMEOUT_MAX_MS, requested));
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
function
|
|
35
|
+
function reportFailure(message) {
|
|
35
36
|
try {
|
|
36
37
|
writeSync(2, `[background-turn-bootstrap] ${message}\n`);
|
|
37
38
|
} catch {
|
|
38
39
|
// The inherited diagnostic handle may already be closed.
|
|
39
40
|
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function failClosed(message) {
|
|
44
|
+
reportFailure(message);
|
|
40
45
|
process.exit(1);
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -68,6 +73,28 @@ await new Promise((resolve) => {
|
|
|
68
73
|
failClosed(message);
|
|
69
74
|
};
|
|
70
75
|
|
|
76
|
+
const containDisconnectOnce = (message) => {
|
|
77
|
+
if (failed) return;
|
|
78
|
+
failed = true;
|
|
79
|
+
if (timer) clearTimeout(timer);
|
|
80
|
+
if (readyTimer) clearInterval(readyTimer);
|
|
81
|
+
// Emit the original diagnostic before SIGSTOP freezes this process and
|
|
82
|
+
// every descendant in its detached POSIX group. If the stop fails (or a
|
|
83
|
+
// later SIGCONT lets the helper's fail-safe SIGKILL fail), retain the
|
|
84
|
+
// historical single-process fail-closed exit.
|
|
85
|
+
reportFailure(message);
|
|
86
|
+
if (
|
|
87
|
+
containReleasedBackgroundTurnDisconnect({
|
|
88
|
+
released,
|
|
89
|
+
currentPid: binding.pid,
|
|
90
|
+
onResumedKillFailure: () => process.exit(1),
|
|
91
|
+
})
|
|
92
|
+
) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
process.exit(1);
|
|
96
|
+
};
|
|
97
|
+
|
|
71
98
|
const onMessage = (message) => {
|
|
72
99
|
if (message?.type !== BACKGROUND_TURN_BOOTSTRAP_RELEASE) return;
|
|
73
100
|
if (released) {
|
|
@@ -109,7 +136,7 @@ await new Promise((resolve) => {
|
|
|
109
136
|
// supervising worker remains fail-closed even after the real CLI starts.
|
|
110
137
|
process.on("message", onMessage);
|
|
111
138
|
process.once("disconnect", () =>
|
|
112
|
-
|
|
139
|
+
containDisconnectOnce("worker IPC disconnected before turn completion"),
|
|
113
140
|
);
|
|
114
141
|
timer = setTimeout(
|
|
115
142
|
() => failOnce("timed out waiting for durable turn release"),
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{P as c,J as p,b as s}from"./vendor-Bn1JzzAn.js";import{r as a}from"./markdown-Yo_c6Krv.js";import{_ as d}from"./index-BQ3DXBtD.js";const _={__name:"MarkdownRenderer",props:{content:{type:String,default:""}},setup(t,{expose:o}){o();const e=t,r=s(()=>a(e.content||"")),n={props:e,rendered:r,computed:s,get renderMarkdown(){return a}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}},i=["innerHTML"];function m(t,o,e,r,n,u){return c(),p("div",{class:"markdown-content",innerHTML:r.rendered},null,8,i)}const M=d(_,[["render",m],["__scopeId","data-v-0b0b9f63"],["__file","/tmp/cc-web-panel-J6U3Mt/repo/packages/web-panel/src/components/MarkdownRenderer.vue"]]);export{M};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as n,J as g,c as l,K as r,N as u,P as h,U as o,R as t,Q as b}from"./vendor-Bn1JzzAn.js";import{_ as v,b as m}from"./index-BQ3DXBtD.js";import{a8 as M,M as y}from"./icons-DG5UD-6K.js";const B={__name:"MobileProjects",setup(p,{expose:i}){i();const c=u(),{t:e}=m();function a(){c.push("/projects")}function _(){c.push("/mobile-bridge")}const s={router:c,t:e,goToProjects:a,goToMobileBridge:_,get useRouter(){return u},get useI18n(){return m},get MobileOutlined(){return y},get FolderOutlined(){return M}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},x={class:"mobile-projects-placeholder"},O={class:"title"},k={class:"subtitle"},w={class:"explainer"};function T(p,i,c,e,a,_){const s=n("a-alert"),d=n("a-button"),f=n("a-space"),P=n("a-empty"),j=n("a-card");return h(),g("div",x,[l(j,null,{default:r(()=>[l(P,{description:!1},{image:r(()=>[l(e.MobileOutlined,{style:{fontSize:"64px",color:"#bfbfbf"}})]),default:r(()=>[o("h2",O,t(e.t("mobileProjects.title")),1),o("p",k,t(e.t("mobileProjects.subtitle")),1),l(s,{message:e.t("mobileProjects.v02Banner"),type:"info","show-icon":"",class:"banner"},null,8,["message"]),o("div",w,[o("h3",null,t(e.t("mobileProjects.currentDirection")),1),o("p",null,t(e.t("mobileProjects.currentDirectionBody")),1),o("ul",null,[o("li",null,t(e.t("mobileProjects.stepPhone")),1),o("li",null,t(e.t("mobileProjects.stepTap")),1),o("li",null,t(e.t("mobileProjects.stepPull")),1)]),o("h3",null,t(e.t("mobileProjects.reverseDirection")),1),o("p",null,t(e.t("mobileProjects.reverseDirectionBody")),1)]),l(f,{class:"actions"},{default:r(()=>[l(d,{type:"primary",onClick:e.goToProjects},{default:r(()=>[l(e.FolderOutlined),b(" "+t(e.t("mobileProjects.viewLocalProjects")),1)]),_:1}),l(d,{onClick:e.goToMobileBridge},{default:r(()=>[l(e.MobileOutlined),b(" "+t(e.t("mobileProjects.checkBridge")),1)]),_:1})]),_:1})]),_:1})]),_:1})])}const N=v(B,[["render",T],["__scopeId","data-v-9262cc45"],["__file","/tmp/cc-web-panel-J6U3Mt/repo/packages/web-panel/src/views/MobileProjects.vue"]]);export{N as default};
|