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
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { posix as posixPath, win32 as win32Path } from "node:path";
|
|
3
|
+
import { types as utilTypes } from "node:util";
|
|
1
4
|
import {
|
|
5
|
+
COWORK_WORKFLOW_RUN_RECORD_SCHEMA,
|
|
2
6
|
MAX_FAN_OUT,
|
|
7
|
+
executeWorkflow as executeCoworkWorkflow,
|
|
3
8
|
loopIterationCap,
|
|
4
9
|
planBatches,
|
|
10
|
+
resolveForEachItems,
|
|
11
|
+
shouldRunStep,
|
|
5
12
|
validateWorkflow,
|
|
6
13
|
} from "./cowork-workflow.js";
|
|
7
14
|
import {
|
|
@@ -16,10 +23,25 @@ import {
|
|
|
16
23
|
createWorkflowDefinitionAuthority,
|
|
17
24
|
normalizeWorkflowDefinitionDigest,
|
|
18
25
|
} from "./workflow-definition-contract.js";
|
|
26
|
+
import { canonicalJson } from "./scheduler-kernel/contract.js";
|
|
19
27
|
|
|
20
28
|
export const DYNAMIC_WORKFLOW_DEFINITION_SCHEMA = WORKFLOW_DEFINITION_SCHEMA;
|
|
21
29
|
export const DYNAMIC_WORKFLOW_PREFLIGHT_SCHEMA =
|
|
22
30
|
"cc-dynamic-workflow-preflight/v1";
|
|
31
|
+
export const DYNAMIC_WORKFLOW_RUN_ADMISSION_SCHEMA =
|
|
32
|
+
"cc-dynamic-workflow-run-admission/v1";
|
|
33
|
+
export const SESSION_EXECUTION_LOCATION_AUTHORITY_SCHEMA =
|
|
34
|
+
"cc-session-execution-location-authority/v1";
|
|
35
|
+
export const DYNAMIC_WORKFLOW_PREFLIGHT_BLOCKED_CODE =
|
|
36
|
+
"CC_DYNAMIC_WORKFLOW_PREFLIGHT_BLOCKED";
|
|
37
|
+
export const DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE =
|
|
38
|
+
"CC_DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID";
|
|
39
|
+
export const DYNAMIC_WORKFLOW_DEFINITION_AUTHORITY_INVALID_CODE =
|
|
40
|
+
"CC_DYNAMIC_WORKFLOW_DEFINITION_AUTHORITY_INVALID";
|
|
41
|
+
export const DYNAMIC_WORKFLOW_EXECUTION_OPTIONS_INVALID_CODE =
|
|
42
|
+
"CC_DYNAMIC_WORKFLOW_EXECUTION_OPTIONS_INVALID";
|
|
43
|
+
export const DYNAMIC_WORKFLOW_EXECUTION_RESULT_INVALID_CODE =
|
|
44
|
+
"CC_DYNAMIC_WORKFLOW_EXECUTION_RESULT_INVALID";
|
|
23
45
|
|
|
24
46
|
export const DYNAMIC_WORKFLOW_ENGINE_CAPABILITIES = Object.freeze([
|
|
25
47
|
"condition",
|
|
@@ -68,19 +90,291 @@ const DATA_BOUNDARY_KINDS = new Set([
|
|
|
68
90
|
const SANDBOX_RANK = Object.freeze({ none: 0, partial: 1, strong: 2 });
|
|
69
91
|
const DEFAULT_SOFT_TASK_GUIDELINE = 16;
|
|
70
92
|
const DEFAULT_HARD_TASK_LIMIT = 64;
|
|
93
|
+
const MAX_AUTHORITY_SNAPSHOT_NODES = 25_000;
|
|
94
|
+
const MAX_CANONICAL_SNAPSHOT_BYTES = 1024 * 1024;
|
|
95
|
+
const MAX_RUN_RECORD_STEPS = DEFAULT_HARD_TASK_LIMIT;
|
|
96
|
+
const RUN_INPUT_FIELDS = new Set([
|
|
97
|
+
"definitionAuthority",
|
|
98
|
+
"executionAuthoritySessionId",
|
|
99
|
+
"maxParallel",
|
|
100
|
+
"execution",
|
|
101
|
+
"onAdmitted",
|
|
102
|
+
]);
|
|
103
|
+
const RUN_DEPENDENCY_FIELDS = new Set(["executeWorkflow", "verifyAuthorities"]);
|
|
104
|
+
const VERIFIED_AUTHORITIES_FIELDS = new Set([
|
|
105
|
+
"definitionAuthority",
|
|
106
|
+
"executionLocationAuthority",
|
|
107
|
+
]);
|
|
108
|
+
const VERIFIED_SESSION_AUTHORITY_FIELDS = new Set([
|
|
109
|
+
"schema",
|
|
110
|
+
"authority",
|
|
111
|
+
"sessionId",
|
|
112
|
+
"headHash",
|
|
113
|
+
"eventCount",
|
|
114
|
+
"binding",
|
|
115
|
+
]);
|
|
116
|
+
const DEFINITION_AUTHORITY_FIELDS = new Set([
|
|
117
|
+
"status",
|
|
118
|
+
"recordSchema",
|
|
119
|
+
"definitionSchema",
|
|
120
|
+
"definitionDigest",
|
|
121
|
+
"definition",
|
|
122
|
+
]);
|
|
123
|
+
const EXECUTION_OPTION_FIELDS = new Set([
|
|
124
|
+
"cwd",
|
|
125
|
+
"continueOnError",
|
|
126
|
+
"pipeline",
|
|
127
|
+
"provider",
|
|
128
|
+
"model",
|
|
129
|
+
"llmOptions",
|
|
130
|
+
"onStepStart",
|
|
131
|
+
"onStepComplete",
|
|
132
|
+
]);
|
|
133
|
+
const RUN_RECORD_FIELDS = new Set([
|
|
134
|
+
"schema",
|
|
135
|
+
"workflowId",
|
|
136
|
+
"workflowName",
|
|
137
|
+
"definitionSchema",
|
|
138
|
+
"definitionDigest",
|
|
139
|
+
"runAdmission",
|
|
140
|
+
"status",
|
|
141
|
+
"steps",
|
|
142
|
+
"startedAt",
|
|
143
|
+
"finishedAt",
|
|
144
|
+
]);
|
|
145
|
+
const RUN_RECORD_STATUSES = new Set(["completed", "failed", "partial"]);
|
|
146
|
+
const RUN_RECORD_STEP_FIELDS = new Set(["id", "status", "taskId", "result"]);
|
|
147
|
+
const RUN_RECORD_STEP_STATUSES = new Set([
|
|
148
|
+
"completed",
|
|
149
|
+
"failed",
|
|
150
|
+
"partial",
|
|
151
|
+
"skipped",
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
export class DynamicWorkflowAdmissionError extends Error {
|
|
155
|
+
constructor(code, message, details = undefined, options = {}) {
|
|
156
|
+
super(message);
|
|
157
|
+
this.name = "DynamicWorkflowAdmissionError";
|
|
158
|
+
this.code = code;
|
|
159
|
+
this.record = null;
|
|
160
|
+
this.executionStarted = options.executionStarted === true;
|
|
161
|
+
if (details !== undefined) this.details = details;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function admissionError(code, message, details, options) {
|
|
166
|
+
return new DynamicWorkflowAdmissionError(code, message, details, options);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function snapshotObjectFields(
|
|
170
|
+
value,
|
|
171
|
+
fields,
|
|
172
|
+
code,
|
|
173
|
+
field,
|
|
174
|
+
{ exact = true } = {},
|
|
175
|
+
) {
|
|
176
|
+
if (
|
|
177
|
+
!value ||
|
|
178
|
+
typeof value !== "object" ||
|
|
179
|
+
Array.isArray(value) ||
|
|
180
|
+
utilTypes.isProxy(value)
|
|
181
|
+
) {
|
|
182
|
+
throw admissionError(code, `${field} must be a non-proxy plain object`);
|
|
183
|
+
}
|
|
184
|
+
const prototype = Object.getPrototypeOf(value);
|
|
185
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
186
|
+
throw admissionError(code, `${field} must be a plain object`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const keys = Reflect.ownKeys(value);
|
|
190
|
+
if (
|
|
191
|
+
(exact && keys.length !== fields.size) ||
|
|
192
|
+
keys.some((key) => typeof key !== "string" || !fields.has(key))
|
|
193
|
+
) {
|
|
194
|
+
throw admissionError(code, `${field} contains missing or unknown fields`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const snapshot = {};
|
|
198
|
+
for (const key of keys) {
|
|
199
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
200
|
+
if (
|
|
201
|
+
!descriptor ||
|
|
202
|
+
descriptor.enumerable !== true ||
|
|
203
|
+
!Object.hasOwn(descriptor, "value")
|
|
204
|
+
) {
|
|
205
|
+
throw admissionError(
|
|
206
|
+
code,
|
|
207
|
+
`${field}.${key} must be an enumerable data property`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
Object.defineProperty(snapshot, key, {
|
|
211
|
+
configurable: true,
|
|
212
|
+
enumerable: true,
|
|
213
|
+
writable: true,
|
|
214
|
+
value: descriptor.value,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return Object.freeze(snapshot);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function snapshotCanonicalValue(
|
|
221
|
+
value,
|
|
222
|
+
code,
|
|
223
|
+
field,
|
|
224
|
+
{
|
|
225
|
+
maxBytes = MAX_CANONICAL_SNAPSHOT_BYTES,
|
|
226
|
+
maxNodes = MAX_AUTHORITY_SNAPSHOT_NODES,
|
|
227
|
+
} = {},
|
|
228
|
+
) {
|
|
229
|
+
const seen = new WeakSet();
|
|
230
|
+
let nodes = 0;
|
|
231
|
+
let roughCharacters = 0;
|
|
232
|
+
|
|
233
|
+
const invalid = (reason) => {
|
|
234
|
+
throw admissionError(code, `${field} ${reason}`);
|
|
235
|
+
};
|
|
236
|
+
const account = (count = 1) => {
|
|
237
|
+
nodes += 1;
|
|
238
|
+
roughCharacters += count;
|
|
239
|
+
if (nodes > maxNodes || roughCharacters > maxBytes) {
|
|
240
|
+
invalid("exceeds snapshot limits");
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const accountCharacters = (count) => {
|
|
244
|
+
roughCharacters += count;
|
|
245
|
+
if (roughCharacters > maxBytes) invalid("exceeds snapshot limits");
|
|
246
|
+
};
|
|
247
|
+
const visit = (current, depth) => {
|
|
248
|
+
if (depth > 64) invalid("exceeds snapshot depth");
|
|
249
|
+
if (current === null || typeof current === "boolean") {
|
|
250
|
+
account();
|
|
251
|
+
return current;
|
|
252
|
+
}
|
|
253
|
+
if (typeof current === "string") {
|
|
254
|
+
account(current.length);
|
|
255
|
+
return current;
|
|
256
|
+
}
|
|
257
|
+
if (typeof current === "number") {
|
|
258
|
+
if (!Number.isFinite(current)) invalid("contains a non-finite number");
|
|
259
|
+
account();
|
|
260
|
+
return current;
|
|
261
|
+
}
|
|
262
|
+
if (!current || typeof current !== "object" || utilTypes.isProxy(current)) {
|
|
263
|
+
invalid("must contain only non-proxy canonical JSON values");
|
|
264
|
+
}
|
|
265
|
+
if (seen.has(current)) invalid("contains a cycle or repeated reference");
|
|
266
|
+
seen.add(current);
|
|
267
|
+
account();
|
|
268
|
+
|
|
269
|
+
if (Array.isArray(current)) {
|
|
270
|
+
if (Object.getPrototypeOf(current) !== Array.prototype) {
|
|
271
|
+
invalid("contains a non-plain array");
|
|
272
|
+
}
|
|
273
|
+
if (current.length > maxNodes) invalid("contains an oversized array");
|
|
274
|
+
const keys = Reflect.ownKeys(current);
|
|
275
|
+
if (keys.length !== current.length + 1) {
|
|
276
|
+
invalid("contains a sparse or extended array");
|
|
277
|
+
}
|
|
278
|
+
const output = [];
|
|
279
|
+
for (let index = 0; index < current.length; index += 1) {
|
|
280
|
+
const key = String(index);
|
|
281
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
282
|
+
if (
|
|
283
|
+
!descriptor ||
|
|
284
|
+
descriptor.enumerable !== true ||
|
|
285
|
+
!Object.hasOwn(descriptor, "value")
|
|
286
|
+
) {
|
|
287
|
+
invalid("contains an accessor or sparse array entry");
|
|
288
|
+
}
|
|
289
|
+
output.push(visit(descriptor.value, depth + 1));
|
|
290
|
+
}
|
|
291
|
+
if (
|
|
292
|
+
keys.some(
|
|
293
|
+
(key) =>
|
|
294
|
+
key !== "length" &&
|
|
295
|
+
(typeof key !== "string" ||
|
|
296
|
+
!/^(0|[1-9][0-9]*)$/u.test(key) ||
|
|
297
|
+
Number(key) >= current.length),
|
|
298
|
+
)
|
|
299
|
+
) {
|
|
300
|
+
invalid("contains an extended array");
|
|
301
|
+
}
|
|
302
|
+
return Object.freeze(output);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const prototype = Object.getPrototypeOf(current);
|
|
306
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
307
|
+
invalid("contains a non-plain object");
|
|
308
|
+
}
|
|
309
|
+
const keys = Reflect.ownKeys(current);
|
|
310
|
+
if (keys.length > maxNodes) invalid("contains an oversized object");
|
|
311
|
+
const output = {};
|
|
312
|
+
for (const key of keys) {
|
|
313
|
+
if (typeof key !== "string") invalid("contains a symbol property");
|
|
314
|
+
accountCharacters(key.length);
|
|
315
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
316
|
+
if (
|
|
317
|
+
!descriptor ||
|
|
318
|
+
descriptor.enumerable !== true ||
|
|
319
|
+
!Object.hasOwn(descriptor, "value")
|
|
320
|
+
) {
|
|
321
|
+
invalid("contains an accessor or non-enumerable property");
|
|
322
|
+
}
|
|
323
|
+
Object.defineProperty(output, key, {
|
|
324
|
+
configurable: false,
|
|
325
|
+
enumerable: true,
|
|
326
|
+
writable: false,
|
|
327
|
+
value: visit(descriptor.value, depth + 1),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return Object.freeze(output);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const snapshot = visit(value, 0);
|
|
334
|
+
let encoded;
|
|
335
|
+
try {
|
|
336
|
+
encoded = canonicalJson(snapshot, field);
|
|
337
|
+
} catch {
|
|
338
|
+
invalid("is not canonical JSON");
|
|
339
|
+
}
|
|
340
|
+
if (Buffer.byteLength(encoded, "utf8") > maxBytes) {
|
|
341
|
+
invalid("exceeds encoded byte limits");
|
|
342
|
+
}
|
|
343
|
+
return snapshot;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function domainSeparatedDigest(domain, value, field) {
|
|
347
|
+
return `sha256:${createHash("sha256")
|
|
348
|
+
.update(domain, "utf8")
|
|
349
|
+
.update(canonicalJson(value, field), "utf8")
|
|
350
|
+
.digest("hex")}`;
|
|
351
|
+
}
|
|
71
352
|
|
|
72
353
|
function safeString(value, max = 256) {
|
|
73
354
|
if (typeof value !== "string") return null;
|
|
74
355
|
const output = value.trim();
|
|
75
|
-
|
|
76
|
-
|
|
356
|
+
let containsControlCharacter = false;
|
|
357
|
+
for (let index = 0; index < output.length; index += 1) {
|
|
358
|
+
const code = output.charCodeAt(index);
|
|
359
|
+
if (code <= 0x1f || code === 0x7f) {
|
|
360
|
+
containsControlCharacter = true;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (!output || output.length > max || containsControlCharacter) {
|
|
77
365
|
return null;
|
|
78
366
|
}
|
|
79
367
|
return output;
|
|
80
368
|
}
|
|
81
369
|
|
|
82
370
|
function finiteNumber(value, { integer = false, min = 0 } = {}) {
|
|
83
|
-
const output =
|
|
371
|
+
const output =
|
|
372
|
+
typeof value === "number"
|
|
373
|
+
? value
|
|
374
|
+
: typeof value === "string" &&
|
|
375
|
+
/^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u.test(value)
|
|
376
|
+
? Number(value)
|
|
377
|
+
: Number.NaN;
|
|
84
378
|
if (!Number.isFinite(output) || output < min) return null;
|
|
85
379
|
if (integer && !Number.isSafeInteger(output)) return null;
|
|
86
380
|
return output;
|
|
@@ -105,7 +399,10 @@ function usedCapabilities(workflow, batches) {
|
|
|
105
399
|
if (workflow.pipeline === true) capabilities.add("pipeline");
|
|
106
400
|
for (const step of workflow.steps) {
|
|
107
401
|
if (step.when != null) capabilities.add("condition");
|
|
108
|
-
if (step.forEach != null)
|
|
402
|
+
if (step.forEach != null) {
|
|
403
|
+
capabilities.add("for-each");
|
|
404
|
+
capabilities.add("parallel");
|
|
405
|
+
}
|
|
109
406
|
if (step.loopWhile != null || step.loopUntil != null)
|
|
110
407
|
capabilities.add("loop");
|
|
111
408
|
if (Number(step.retries) > 0) capabilities.add("retry");
|
|
@@ -396,19 +693,19 @@ function projectedDurationSlots(plan, requestedParallel) {
|
|
|
396
693
|
const byId = new Map(plan.steps.map((step) => [step.id, step]));
|
|
397
694
|
let total = 0;
|
|
398
695
|
for (const batch of plan.batches) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
696
|
+
const steps = batch.steps.map((id) => byId.get(id));
|
|
697
|
+
const taskCalls = steps.reduce(
|
|
698
|
+
(sum, step) => sum + (step?.worstCaseTaskCalls ?? 0),
|
|
699
|
+
0,
|
|
700
|
+
);
|
|
701
|
+
if (!Number.isSafeInteger(taskCalls)) return null;
|
|
702
|
+
const criticalPath = Math.max(
|
|
703
|
+
0,
|
|
704
|
+
...steps.map((step) => step?.worstCaseDurationSlots ?? 0),
|
|
705
|
+
);
|
|
706
|
+
const scheduledSlots = Math.ceil(taskCalls / requestedParallel);
|
|
707
|
+
total += Math.max(criticalPath, scheduledSlots);
|
|
708
|
+
if (!Number.isSafeInteger(total)) return null;
|
|
412
709
|
}
|
|
413
710
|
return total;
|
|
414
711
|
}
|
|
@@ -421,6 +718,793 @@ function multiplyProjection(...values) {
|
|
|
421
718
|
: null;
|
|
422
719
|
}
|
|
423
720
|
|
|
721
|
+
function normalizeDefinitionAuthorityForRun(value) {
|
|
722
|
+
const code = DYNAMIC_WORKFLOW_DEFINITION_AUTHORITY_INVALID_CODE;
|
|
723
|
+
const authority = snapshotObjectFields(
|
|
724
|
+
value,
|
|
725
|
+
DEFINITION_AUTHORITY_FIELDS,
|
|
726
|
+
code,
|
|
727
|
+
"definitionAuthority",
|
|
728
|
+
);
|
|
729
|
+
if (!new Set(["versioned", "legacy-unversioned"]).has(authority.status)) {
|
|
730
|
+
throw admissionError(code, "definitionAuthority status is invalid");
|
|
731
|
+
}
|
|
732
|
+
if (
|
|
733
|
+
authority.definitionSchema !== WORKFLOW_DEFINITION_SCHEMA ||
|
|
734
|
+
(authority.status === "versioned"
|
|
735
|
+
? authority.recordSchema !== COWORK_WORKFLOW_RECORD_SCHEMA
|
|
736
|
+
: authority.recordSchema !== null)
|
|
737
|
+
) {
|
|
738
|
+
throw admissionError(code, "definitionAuthority schema is invalid");
|
|
739
|
+
}
|
|
740
|
+
const declaredDigest = normalizeWorkflowDefinitionDigest(
|
|
741
|
+
authority.definitionDigest,
|
|
742
|
+
);
|
|
743
|
+
if (!declaredDigest || declaredDigest !== authority.definitionDigest) {
|
|
744
|
+
throw admissionError(code, "definitionAuthority digest is invalid");
|
|
745
|
+
}
|
|
746
|
+
let computed;
|
|
747
|
+
try {
|
|
748
|
+
computed = createWorkflowDefinitionAuthority(
|
|
749
|
+
snapshotCanonicalValue(
|
|
750
|
+
authority.definition,
|
|
751
|
+
code,
|
|
752
|
+
"definitionAuthority.definition",
|
|
753
|
+
),
|
|
754
|
+
);
|
|
755
|
+
} catch {
|
|
756
|
+
throw admissionError(code, "definitionAuthority definition is invalid");
|
|
757
|
+
}
|
|
758
|
+
if (computed.definitionDigest !== declaredDigest) {
|
|
759
|
+
throw admissionError(code, "definitionAuthority digest does not match");
|
|
760
|
+
}
|
|
761
|
+
return Object.freeze({
|
|
762
|
+
status: authority.status,
|
|
763
|
+
recordSchema: authority.recordSchema,
|
|
764
|
+
definitionSchema: computed.schema,
|
|
765
|
+
definitionDigest: computed.definitionDigest,
|
|
766
|
+
definition: computed.definition,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function canonicalObservedAt(value) {
|
|
771
|
+
if (typeof value !== "string") return false;
|
|
772
|
+
const epoch = Date.parse(value);
|
|
773
|
+
return Number.isFinite(epoch) && new Date(epoch).toISOString() === value;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export function normalizeSessionExecutionLocationAuthority(value) {
|
|
777
|
+
const code = DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE;
|
|
778
|
+
const authority = snapshotObjectFields(
|
|
779
|
+
value,
|
|
780
|
+
VERIFIED_SESSION_AUTHORITY_FIELDS,
|
|
781
|
+
code,
|
|
782
|
+
"executionLocationAuthority",
|
|
783
|
+
);
|
|
784
|
+
if (authority.schema !== SESSION_EXECUTION_LOCATION_AUTHORITY_SCHEMA) {
|
|
785
|
+
throw admissionError(
|
|
786
|
+
code,
|
|
787
|
+
`executionLocationAuthority must use ${SESSION_EXECUTION_LOCATION_AUTHORITY_SCHEMA}`,
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
const verified = new Set([
|
|
791
|
+
"verified-session-start",
|
|
792
|
+
"verified-session-location-handoff",
|
|
793
|
+
]).has(authority.authority);
|
|
794
|
+
if (!verified) {
|
|
795
|
+
throw admissionError(
|
|
796
|
+
code,
|
|
797
|
+
"executionLocationAuthority must be a verified session location authority",
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
let binding;
|
|
801
|
+
try {
|
|
802
|
+
const bindingSnapshot = snapshotCanonicalValue(
|
|
803
|
+
authority.binding,
|
|
804
|
+
code,
|
|
805
|
+
"executionLocationAuthority.binding",
|
|
806
|
+
);
|
|
807
|
+
binding = normalizeExecutionLocationBinding(bindingSnapshot);
|
|
808
|
+
if (
|
|
809
|
+
canonicalJson(bindingSnapshot, "executionLocationAuthority.binding") !==
|
|
810
|
+
canonicalJson(binding, "executionLocationAuthority.binding")
|
|
811
|
+
) {
|
|
812
|
+
throw new TypeError("binding is not canonical");
|
|
813
|
+
}
|
|
814
|
+
} catch {
|
|
815
|
+
throw admissionError(
|
|
816
|
+
code,
|
|
817
|
+
"executionLocationAuthority binding is invalid or non-canonical",
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (binding.observed !== true || !canonicalObservedAt(binding.observedAt)) {
|
|
821
|
+
throw admissionError(
|
|
822
|
+
code,
|
|
823
|
+
"executionLocationAuthority binding is not an observed instant",
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const sessionId = safeString(authority.sessionId);
|
|
828
|
+
if (
|
|
829
|
+
sessionId !== authority.sessionId ||
|
|
830
|
+
!/^[a-f0-9]{64}$/u.test(authority.headHash) ||
|
|
831
|
+
!Number.isSafeInteger(authority.eventCount) ||
|
|
832
|
+
authority.eventCount < 1
|
|
833
|
+
) {
|
|
834
|
+
throw admissionError(
|
|
835
|
+
code,
|
|
836
|
+
"executionLocationAuthority session proof is invalid",
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
return Object.freeze({
|
|
840
|
+
schema: SESSION_EXECUTION_LOCATION_AUTHORITY_SCHEMA,
|
|
841
|
+
authority: authority.authority,
|
|
842
|
+
sessionId,
|
|
843
|
+
headHash: authority.headHash,
|
|
844
|
+
eventCount: authority.eventCount,
|
|
845
|
+
binding,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function pathWithinDataBoundary(cwd, boundary) {
|
|
850
|
+
const root = boundary?.root;
|
|
851
|
+
if (typeof cwd !== "string" || typeof root !== "string") return false;
|
|
852
|
+
const windowsPath =
|
|
853
|
+
/^[A-Za-z]:[\\/]/u.test(root) ||
|
|
854
|
+
root.startsWith("\\\\") ||
|
|
855
|
+
/^[A-Za-z]:[\\/]/u.test(cwd) ||
|
|
856
|
+
cwd.startsWith("\\\\");
|
|
857
|
+
const paths = windowsPath ? win32Path : posixPath;
|
|
858
|
+
if (!paths.isAbsolute(root) || !paths.isAbsolute(cwd)) return false;
|
|
859
|
+
const relative = paths.relative(paths.normalize(root), paths.normalize(cwd));
|
|
860
|
+
return (
|
|
861
|
+
relative === "" ||
|
|
862
|
+
(relative !== ".." &&
|
|
863
|
+
!relative.startsWith(`..${paths.sep}`) &&
|
|
864
|
+
!paths.isAbsolute(relative))
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function normalizeExecutionOptions(value, definition, executionAuthority) {
|
|
869
|
+
const code = DYNAMIC_WORKFLOW_EXECUTION_OPTIONS_INVALID_CODE;
|
|
870
|
+
const execution =
|
|
871
|
+
value == null
|
|
872
|
+
? Object.freeze({})
|
|
873
|
+
: snapshotObjectFields(
|
|
874
|
+
value,
|
|
875
|
+
EXECUTION_OPTION_FIELDS,
|
|
876
|
+
code,
|
|
877
|
+
"execution",
|
|
878
|
+
{ exact: false },
|
|
879
|
+
);
|
|
880
|
+
if (Object.hasOwn(execution, "llmOptions")) {
|
|
881
|
+
throw admissionError(
|
|
882
|
+
code,
|
|
883
|
+
"execution.llmOptions is not admitted; use the verified model binding",
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
for (const callback of ["onStepStart", "onStepComplete"]) {
|
|
888
|
+
if (
|
|
889
|
+
Object.hasOwn(execution, callback) &&
|
|
890
|
+
(typeof execution[callback] !== "function" ||
|
|
891
|
+
utilTypes.isProxy(execution[callback]))
|
|
892
|
+
) {
|
|
893
|
+
throw admissionError(code, `execution.${callback} must be a function`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (
|
|
897
|
+
Object.hasOwn(execution, "continueOnError") &&
|
|
898
|
+
typeof execution.continueOnError !== "boolean"
|
|
899
|
+
) {
|
|
900
|
+
throw admissionError(code, "execution.continueOnError must be a boolean");
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const binding = executionAuthority.binding;
|
|
904
|
+
const cwd = binding.source.cwd;
|
|
905
|
+
const dataBoundary = binding.policy.dataBoundary;
|
|
906
|
+
if (!pathWithinDataBoundary(cwd, dataBoundary)) {
|
|
907
|
+
throw admissionError(
|
|
908
|
+
DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE,
|
|
909
|
+
"verified execution cwd is outside its data boundary",
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
const policy = Object.freeze({
|
|
913
|
+
cwd,
|
|
914
|
+
continueOnError: execution.continueOnError ?? false,
|
|
915
|
+
pipeline: definition.pipeline === true,
|
|
916
|
+
provider: binding.model.provider,
|
|
917
|
+
model: binding.model.name,
|
|
918
|
+
});
|
|
919
|
+
for (const field of ["cwd", "pipeline", "provider", "model"]) {
|
|
920
|
+
if (Object.hasOwn(execution, field) && execution[field] !== policy[field]) {
|
|
921
|
+
throw admissionError(
|
|
922
|
+
code,
|
|
923
|
+
`execution.${field} does not match verified authority`,
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
return Object.freeze({
|
|
929
|
+
policy,
|
|
930
|
+
onStepStart: execution.onStepStart,
|
|
931
|
+
onStepComplete: execution.onStepComplete,
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function normalizeRunInput(input) {
|
|
936
|
+
const code = DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE;
|
|
937
|
+
const snapshot = snapshotObjectFields(
|
|
938
|
+
input,
|
|
939
|
+
RUN_INPUT_FIELDS,
|
|
940
|
+
code,
|
|
941
|
+
"dynamicWorkflowRun",
|
|
942
|
+
{ exact: false },
|
|
943
|
+
);
|
|
944
|
+
if (
|
|
945
|
+
!Object.hasOwn(snapshot, "definitionAuthority") ||
|
|
946
|
+
!Object.hasOwn(snapshot, "executionAuthoritySessionId")
|
|
947
|
+
) {
|
|
948
|
+
throw admissionError(
|
|
949
|
+
code,
|
|
950
|
+
"definitionAuthority and executionAuthoritySessionId are required",
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
const executionAuthoritySessionId = safeString(
|
|
954
|
+
snapshot.executionAuthoritySessionId,
|
|
955
|
+
);
|
|
956
|
+
if (executionAuthoritySessionId !== snapshot.executionAuthoritySessionId) {
|
|
957
|
+
throw admissionError(code, "executionAuthoritySessionId is invalid");
|
|
958
|
+
}
|
|
959
|
+
if (
|
|
960
|
+
Object.hasOwn(snapshot, "onAdmitted") &&
|
|
961
|
+
(typeof snapshot.onAdmitted !== "function" ||
|
|
962
|
+
utilTypes.isProxy(snapshot.onAdmitted))
|
|
963
|
+
) {
|
|
964
|
+
throw admissionError(
|
|
965
|
+
DYNAMIC_WORKFLOW_EXECUTION_OPTIONS_INVALID_CODE,
|
|
966
|
+
"onAdmitted must be a function",
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
const definitionAuthority = normalizeDefinitionAuthorityForRun(
|
|
970
|
+
snapshot.definitionAuthority,
|
|
971
|
+
);
|
|
972
|
+
return Object.freeze({
|
|
973
|
+
definitionAuthority,
|
|
974
|
+
executionAuthoritySessionId,
|
|
975
|
+
maxParallel: snapshot.maxParallel,
|
|
976
|
+
execution: snapshot.execution,
|
|
977
|
+
onAdmitted: snapshot.onAdmitted,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function normalizeRunDependencies(deps) {
|
|
982
|
+
const code = DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE;
|
|
983
|
+
const snapshot = snapshotObjectFields(
|
|
984
|
+
deps,
|
|
985
|
+
RUN_DEPENDENCY_FIELDS,
|
|
986
|
+
code,
|
|
987
|
+
"dynamicWorkflowRunDependencies",
|
|
988
|
+
{ exact: false },
|
|
989
|
+
);
|
|
990
|
+
if (
|
|
991
|
+
typeof snapshot.verifyAuthorities !== "function" ||
|
|
992
|
+
utilTypes.isProxy(snapshot.verifyAuthorities)
|
|
993
|
+
) {
|
|
994
|
+
throw admissionError(code, "verifyAuthorities dependency is required");
|
|
995
|
+
}
|
|
996
|
+
if (
|
|
997
|
+
Object.hasOwn(snapshot, "executeWorkflow") &&
|
|
998
|
+
(typeof snapshot.executeWorkflow !== "function" ||
|
|
999
|
+
utilTypes.isProxy(snapshot.executeWorkflow))
|
|
1000
|
+
) {
|
|
1001
|
+
throw admissionError(
|
|
1002
|
+
DYNAMIC_WORKFLOW_EXECUTION_OPTIONS_INVALID_CODE,
|
|
1003
|
+
"executeWorkflow dependency must be a function",
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
return snapshot;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
function authorityLookup(runInput) {
|
|
1010
|
+
return Object.freeze({
|
|
1011
|
+
workflowId: runInput.definitionAuthority.definition.id,
|
|
1012
|
+
definitionDigest: runInput.definitionAuthority.definitionDigest,
|
|
1013
|
+
executionAuthoritySessionId: runInput.executionAuthoritySessionId,
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function normalizeVerifiedAuthorities(value, runInput) {
|
|
1018
|
+
const code = DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE;
|
|
1019
|
+
const snapshot = snapshotObjectFields(
|
|
1020
|
+
value,
|
|
1021
|
+
VERIFIED_AUTHORITIES_FIELDS,
|
|
1022
|
+
code,
|
|
1023
|
+
"verifiedAuthorities",
|
|
1024
|
+
);
|
|
1025
|
+
const definitionAuthority = normalizeDefinitionAuthorityForRun(
|
|
1026
|
+
snapshot.definitionAuthority,
|
|
1027
|
+
);
|
|
1028
|
+
const executionLocationAuthority = normalizeSessionExecutionLocationAuthority(
|
|
1029
|
+
snapshot.executionLocationAuthority,
|
|
1030
|
+
);
|
|
1031
|
+
if (
|
|
1032
|
+
canonicalJson(definitionAuthority, "verifiedDefinitionAuthority") !==
|
|
1033
|
+
canonicalJson(runInput.definitionAuthority, "inputDefinitionAuthority")
|
|
1034
|
+
) {
|
|
1035
|
+
throw admissionError(
|
|
1036
|
+
code,
|
|
1037
|
+
"verified definition authority does not exactly match the requested record",
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
if (
|
|
1041
|
+
executionLocationAuthority.sessionId !==
|
|
1042
|
+
runInput.executionAuthoritySessionId
|
|
1043
|
+
) {
|
|
1044
|
+
throw admissionError(
|
|
1045
|
+
code,
|
|
1046
|
+
"verified execution authority does not match the requested session",
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
return Object.freeze({ definitionAuthority, executionLocationAuthority });
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function verifyAuthoritiesSynchronously(verifier, lookup, runInput) {
|
|
1053
|
+
let verified;
|
|
1054
|
+
try {
|
|
1055
|
+
verified = verifier(lookup);
|
|
1056
|
+
} catch {
|
|
1057
|
+
throw admissionError(
|
|
1058
|
+
DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE,
|
|
1059
|
+
"authority verification failed",
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
if (utilTypes.isPromise(verified)) {
|
|
1063
|
+
throw admissionError(
|
|
1064
|
+
DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE,
|
|
1065
|
+
"buildDynamicWorkflowRunAdmission requires a synchronous authority verifier",
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
return normalizeVerifiedAuthorities(verified, runInput);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
async function verifyAuthoritiesAsynchronously(verifier, lookup, runInput) {
|
|
1072
|
+
let verified;
|
|
1073
|
+
try {
|
|
1074
|
+
verified = await verifier(lookup);
|
|
1075
|
+
} catch {
|
|
1076
|
+
throw admissionError(
|
|
1077
|
+
DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE,
|
|
1078
|
+
"authority verification failed",
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
return normalizeVerifiedAuthorities(verified, runInput);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
function authoritiesExactlyMatch(left, right) {
|
|
1085
|
+
return (
|
|
1086
|
+
canonicalJson(left, "firstVerifiedAuthorities") ===
|
|
1087
|
+
canonicalJson(right, "finalVerifiedAuthorities")
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
function blockedError(preflight) {
|
|
1092
|
+
return Object.freeze({
|
|
1093
|
+
name: "DynamicWorkflowAdmissionError",
|
|
1094
|
+
code: DYNAMIC_WORKFLOW_PREFLIGHT_BLOCKED_CODE,
|
|
1095
|
+
message: "dynamic workflow execution was blocked by preflight",
|
|
1096
|
+
details: Object.freeze({ blockers: preflight.blockers }),
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
function prepareDynamicWorkflowRun(runInput, verifiedAuthorities, execution) {
|
|
1101
|
+
const { definitionAuthority, executionLocationAuthority } =
|
|
1102
|
+
verifiedAuthorities;
|
|
1103
|
+
const workflow = definitionAuthority.definition;
|
|
1104
|
+
const preflight = buildDynamicWorkflowPreflight({
|
|
1105
|
+
workflow,
|
|
1106
|
+
definitionAuthority,
|
|
1107
|
+
executionLocation: executionLocationAuthority.binding,
|
|
1108
|
+
maxParallel: runInput.maxParallel,
|
|
1109
|
+
requireDefinitionAuthority: true,
|
|
1110
|
+
});
|
|
1111
|
+
const executionLocationDigest = domainSeparatedDigest(
|
|
1112
|
+
"chainlesschain.dynamic-workflow.execution-location.v1\0",
|
|
1113
|
+
executionLocationAuthority,
|
|
1114
|
+
"executionLocationAuthority",
|
|
1115
|
+
);
|
|
1116
|
+
const preflightDigest = domainSeparatedDigest(
|
|
1117
|
+
"chainlesschain.dynamic-workflow.preflight.v1\0",
|
|
1118
|
+
preflight,
|
|
1119
|
+
"dynamicWorkflowPreflight",
|
|
1120
|
+
);
|
|
1121
|
+
|
|
1122
|
+
if (!preflight.allowed) {
|
|
1123
|
+
return Object.freeze({
|
|
1124
|
+
workflow: definitionAuthority.definition,
|
|
1125
|
+
outcome: Object.freeze({
|
|
1126
|
+
allowed: false,
|
|
1127
|
+
admission: null,
|
|
1128
|
+
preflight,
|
|
1129
|
+
definitionDigest: preflight.definition.definitionDigest,
|
|
1130
|
+
executionLocationDigest,
|
|
1131
|
+
preflightDigest,
|
|
1132
|
+
admissionDigest: null,
|
|
1133
|
+
authorityVerified: true,
|
|
1134
|
+
error: blockedError(preflight),
|
|
1135
|
+
}),
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
const admissionMaterial = Object.freeze({
|
|
1140
|
+
schema: DYNAMIC_WORKFLOW_RUN_ADMISSION_SCHEMA,
|
|
1141
|
+
definition: Object.freeze({
|
|
1142
|
+
schema: preflight.definition.schema,
|
|
1143
|
+
definitionDigest: preflight.definition.definitionDigest,
|
|
1144
|
+
authority: preflight.definition.authority,
|
|
1145
|
+
}),
|
|
1146
|
+
executionLocation: Object.freeze({
|
|
1147
|
+
authoritySchema: executionLocationAuthority.schema,
|
|
1148
|
+
authority: executionLocationAuthority.authority,
|
|
1149
|
+
session: Object.freeze({
|
|
1150
|
+
sessionId: executionLocationAuthority.sessionId,
|
|
1151
|
+
headHash: executionLocationAuthority.headHash,
|
|
1152
|
+
eventCount: executionLocationAuthority.eventCount,
|
|
1153
|
+
}),
|
|
1154
|
+
bindingSchema: executionLocationAuthority.binding.schema,
|
|
1155
|
+
location: executionLocationAuthority.binding.location,
|
|
1156
|
+
}),
|
|
1157
|
+
definitionDigest: preflight.definition.definitionDigest,
|
|
1158
|
+
executionLocationDigest,
|
|
1159
|
+
preflightDigest,
|
|
1160
|
+
maxParallel: preflight.scale.requestedParallel,
|
|
1161
|
+
executionPolicy: execution.policy,
|
|
1162
|
+
credentialValuesTransferred: false,
|
|
1163
|
+
});
|
|
1164
|
+
const admissionDigest = domainSeparatedDigest(
|
|
1165
|
+
"chainlesschain.dynamic-workflow.admission.v1\0",
|
|
1166
|
+
admissionMaterial,
|
|
1167
|
+
"dynamicWorkflowAdmission",
|
|
1168
|
+
);
|
|
1169
|
+
const admission = Object.freeze({
|
|
1170
|
+
...admissionMaterial,
|
|
1171
|
+
admissionDigest,
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
return Object.freeze({
|
|
1175
|
+
workflow: definitionAuthority.definition,
|
|
1176
|
+
outcome: Object.freeze({
|
|
1177
|
+
allowed: true,
|
|
1178
|
+
admission,
|
|
1179
|
+
preflight,
|
|
1180
|
+
definitionDigest: admission.definitionDigest,
|
|
1181
|
+
executionLocationDigest,
|
|
1182
|
+
preflightDigest,
|
|
1183
|
+
admissionDigest,
|
|
1184
|
+
authorityVerified: true,
|
|
1185
|
+
error: null,
|
|
1186
|
+
}),
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
export function buildDynamicWorkflowRunAdmission(input = {}, deps = {}) {
|
|
1191
|
+
const runInput = normalizeRunInput(input);
|
|
1192
|
+
const dependencies = normalizeRunDependencies(deps);
|
|
1193
|
+
const verifiedAuthorities = verifyAuthoritiesSynchronously(
|
|
1194
|
+
dependencies.verifyAuthorities,
|
|
1195
|
+
authorityLookup(runInput),
|
|
1196
|
+
runInput,
|
|
1197
|
+
);
|
|
1198
|
+
const execution = normalizeExecutionOptions(
|
|
1199
|
+
runInput.execution,
|
|
1200
|
+
verifiedAuthorities.definitionAuthority.definition,
|
|
1201
|
+
verifiedAuthorities.executionLocationAuthority,
|
|
1202
|
+
);
|
|
1203
|
+
return prepareDynamicWorkflowRun(runInput, verifiedAuthorities, execution)
|
|
1204
|
+
.outcome;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function canonicalTimestamp(value) {
|
|
1208
|
+
if (typeof value !== "string") return false;
|
|
1209
|
+
const epoch = Date.parse(value);
|
|
1210
|
+
return Number.isFinite(epoch) && new Date(epoch).toISOString() === value;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function declaredDependencyIds(step, stepsById) {
|
|
1214
|
+
const dependencies = new Set();
|
|
1215
|
+
const pending = [...(step.dependsOn || [])];
|
|
1216
|
+
while (pending.length > 0) {
|
|
1217
|
+
const dependencyId = pending.pop();
|
|
1218
|
+
if (dependencies.has(dependencyId)) continue;
|
|
1219
|
+
dependencies.add(dependencyId);
|
|
1220
|
+
const dependency = stepsById.get(dependencyId);
|
|
1221
|
+
if (dependency) pending.push(...(dependency.dependsOn || []));
|
|
1222
|
+
}
|
|
1223
|
+
return dependencies;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function runtimeForEachSourceId(value) {
|
|
1227
|
+
if (typeof value !== "string") return null;
|
|
1228
|
+
const match = value.trim().match(/^\$\{step\.([^.{}]+)\.[^.{}]+\}$/u);
|
|
1229
|
+
return match?.[1] || null;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function assertCompletedRecordCoverage(record, workflow, code) {
|
|
1233
|
+
const outcomesById = new Map(record.steps.map((step) => [step.id, step]));
|
|
1234
|
+
const expectedOutcomeIds = new Set();
|
|
1235
|
+
const resultsById = new Map();
|
|
1236
|
+
const stepsById = new Map(workflow.steps.map((step) => [step.id, step]));
|
|
1237
|
+
|
|
1238
|
+
const requireOutcome = (id) => {
|
|
1239
|
+
if (expectedOutcomeIds.has(id)) {
|
|
1240
|
+
throw admissionError(
|
|
1241
|
+
code,
|
|
1242
|
+
"completed workflow definition produces colliding outcome ids",
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
const outcome = outcomesById.get(id);
|
|
1246
|
+
if (!outcome) {
|
|
1247
|
+
throw admissionError(
|
|
1248
|
+
code,
|
|
1249
|
+
`completed workflow record is missing outcome '${id}'`,
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
expectedOutcomeIds.add(id);
|
|
1253
|
+
return outcome;
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
for (const batch of planBatches(workflow.steps)) {
|
|
1257
|
+
// Outcomes from independent steps in the same batch are not authoritative
|
|
1258
|
+
// inputs to one another. Publish only parent outcomes after the whole batch
|
|
1259
|
+
// has been reconstructed.
|
|
1260
|
+
const batchResults = [];
|
|
1261
|
+
for (const step of batch) {
|
|
1262
|
+
if (!shouldRunStep(step, resultsById)) {
|
|
1263
|
+
throw admissionError(
|
|
1264
|
+
code,
|
|
1265
|
+
`completed workflow record includes non-runnable step '${step.id}'`,
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
if (step.forEach === undefined) {
|
|
1270
|
+
batchResults.push([step.id, requireOutcome(step.id)]);
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
if (typeof step.forEach === "string") {
|
|
1275
|
+
const sourceId = runtimeForEachSourceId(step.forEach);
|
|
1276
|
+
if (
|
|
1277
|
+
!sourceId ||
|
|
1278
|
+
!declaredDependencyIds(step, stepsById).has(sourceId)
|
|
1279
|
+
) {
|
|
1280
|
+
throw admissionError(
|
|
1281
|
+
code,
|
|
1282
|
+
`runtime forEach on '${step.id}' is not bound to a declared dependency`,
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
const items = resolveForEachItems(step.forEach, resultsById);
|
|
1288
|
+
if (items.length === 0) {
|
|
1289
|
+
throw admissionError(
|
|
1290
|
+
code,
|
|
1291
|
+
`completed workflow record cannot cover empty forEach '${step.id}'`,
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
const children = [];
|
|
1295
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
1296
|
+
children.push(requireOutcome(`${step.id}[${index}]`));
|
|
1297
|
+
}
|
|
1298
|
+
batchResults.push([
|
|
1299
|
+
step.id,
|
|
1300
|
+
Object.freeze({
|
|
1301
|
+
id: step.id,
|
|
1302
|
+
status: "completed",
|
|
1303
|
+
taskId: null,
|
|
1304
|
+
result: Object.freeze({
|
|
1305
|
+
summary: children
|
|
1306
|
+
.map((child) => child.result?.summary ?? "")
|
|
1307
|
+
.join("\n"),
|
|
1308
|
+
children: children.length,
|
|
1309
|
+
}),
|
|
1310
|
+
}),
|
|
1311
|
+
]);
|
|
1312
|
+
}
|
|
1313
|
+
for (const [stepId, outcome] of batchResults) {
|
|
1314
|
+
resultsById.set(stepId, outcome);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
if (
|
|
1319
|
+
expectedOutcomeIds.size !== record.steps.length ||
|
|
1320
|
+
record.steps.some((step) => !expectedOutcomeIds.has(step.id))
|
|
1321
|
+
) {
|
|
1322
|
+
throw admissionError(
|
|
1323
|
+
code,
|
|
1324
|
+
"completed workflow record contains outcomes outside its definition",
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function normalizeExecutionRecord(value, prepared) {
|
|
1330
|
+
const code = DYNAMIC_WORKFLOW_EXECUTION_RESULT_INVALID_CODE;
|
|
1331
|
+
try {
|
|
1332
|
+
const fields = snapshotObjectFields(
|
|
1333
|
+
value,
|
|
1334
|
+
RUN_RECORD_FIELDS,
|
|
1335
|
+
code,
|
|
1336
|
+
"executeWorkflowRecord",
|
|
1337
|
+
);
|
|
1338
|
+
if (
|
|
1339
|
+
!Array.isArray(fields.steps) ||
|
|
1340
|
+
utilTypes.isProxy(fields.steps) ||
|
|
1341
|
+
fields.steps.length > MAX_RUN_RECORD_STEPS
|
|
1342
|
+
) {
|
|
1343
|
+
throw admissionError(code, "executeWorkflowRecord steps are invalid");
|
|
1344
|
+
}
|
|
1345
|
+
const record = snapshotCanonicalValue(
|
|
1346
|
+
fields,
|
|
1347
|
+
code,
|
|
1348
|
+
"executeWorkflowRecord",
|
|
1349
|
+
);
|
|
1350
|
+
const admission = prepared.outcome.admission;
|
|
1351
|
+
const workflow = prepared.workflow;
|
|
1352
|
+
if (
|
|
1353
|
+
record.schema !== COWORK_WORKFLOW_RUN_RECORD_SCHEMA ||
|
|
1354
|
+
record.workflowId !== workflow.id ||
|
|
1355
|
+
record.workflowName !== workflow.name ||
|
|
1356
|
+
record.definitionSchema !== WORKFLOW_DEFINITION_SCHEMA ||
|
|
1357
|
+
record.definitionDigest !== admission.definitionDigest ||
|
|
1358
|
+
!RUN_RECORD_STATUSES.has(record.status) ||
|
|
1359
|
+
!canonicalTimestamp(record.startedAt) ||
|
|
1360
|
+
!canonicalTimestamp(record.finishedAt) ||
|
|
1361
|
+
Date.parse(record.finishedAt) < Date.parse(record.startedAt)
|
|
1362
|
+
) {
|
|
1363
|
+
throw admissionError(
|
|
1364
|
+
code,
|
|
1365
|
+
"executeWorkflowRecord does not match the admitted workflow",
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
if (
|
|
1369
|
+
!record.runAdmission ||
|
|
1370
|
+
record.runAdmission.admissionDigest !== admission.admissionDigest ||
|
|
1371
|
+
canonicalJson(record.runAdmission, "recordRunAdmission") !==
|
|
1372
|
+
canonicalJson(admission, "admittedRunAdmission")
|
|
1373
|
+
) {
|
|
1374
|
+
throw admissionError(
|
|
1375
|
+
code,
|
|
1376
|
+
"executeWorkflowRecord run admission does not match",
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const stepIds = new Set();
|
|
1381
|
+
for (const [index, step] of record.steps.entries()) {
|
|
1382
|
+
const normalizedStep = snapshotObjectFields(
|
|
1383
|
+
step,
|
|
1384
|
+
RUN_RECORD_STEP_FIELDS,
|
|
1385
|
+
code,
|
|
1386
|
+
`executeWorkflowRecord.steps[${index}]`,
|
|
1387
|
+
);
|
|
1388
|
+
const id = safeString(normalizedStep.id, 512);
|
|
1389
|
+
if (
|
|
1390
|
+
id !== normalizedStep.id ||
|
|
1391
|
+
stepIds.has(id) ||
|
|
1392
|
+
!RUN_RECORD_STEP_STATUSES.has(normalizedStep.status) ||
|
|
1393
|
+
(normalizedStep.taskId !== null &&
|
|
1394
|
+
safeString(normalizedStep.taskId, 512) !== normalizedStep.taskId)
|
|
1395
|
+
) {
|
|
1396
|
+
throw admissionError(
|
|
1397
|
+
code,
|
|
1398
|
+
"executeWorkflowRecord contains an invalid step outcome",
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
stepIds.add(id);
|
|
1402
|
+
}
|
|
1403
|
+
const expectedStatus = record.steps.every(
|
|
1404
|
+
(step) => step.status === "completed",
|
|
1405
|
+
)
|
|
1406
|
+
? "completed"
|
|
1407
|
+
: record.steps.some((step) => step.status === "completed")
|
|
1408
|
+
? "partial"
|
|
1409
|
+
: "failed";
|
|
1410
|
+
if (record.status !== expectedStatus) {
|
|
1411
|
+
throw admissionError(
|
|
1412
|
+
code,
|
|
1413
|
+
"executeWorkflowRecord status does not match its step outcomes",
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
if (record.status === "completed") {
|
|
1417
|
+
assertCompletedRecordCoverage(record, workflow, code);
|
|
1418
|
+
}
|
|
1419
|
+
return Object.freeze({ ...record, runAdmission: admission });
|
|
1420
|
+
} catch {
|
|
1421
|
+
throw admissionError(
|
|
1422
|
+
code,
|
|
1423
|
+
"executeWorkflow returned an invalid admitted run record",
|
|
1424
|
+
Object.freeze({
|
|
1425
|
+
expectedDefinitionDigest: prepared.outcome.admission.definitionDigest,
|
|
1426
|
+
expectedAdmissionDigest: prepared.outcome.admission.admissionDigest,
|
|
1427
|
+
}),
|
|
1428
|
+
{ executionStarted: true },
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
export async function executeDynamicWorkflowWithAdmission(
|
|
1434
|
+
input = {},
|
|
1435
|
+
deps = {},
|
|
1436
|
+
) {
|
|
1437
|
+
const runInput = normalizeRunInput(input);
|
|
1438
|
+
const dependencies = normalizeRunDependencies(deps);
|
|
1439
|
+
const executor = Object.hasOwn(dependencies, "executeWorkflow")
|
|
1440
|
+
? dependencies.executeWorkflow
|
|
1441
|
+
: executeCoworkWorkflow;
|
|
1442
|
+
const lookup = authorityLookup(runInput);
|
|
1443
|
+
const verifiedAuthorities = await verifyAuthoritiesAsynchronously(
|
|
1444
|
+
dependencies.verifyAuthorities,
|
|
1445
|
+
lookup,
|
|
1446
|
+
runInput,
|
|
1447
|
+
);
|
|
1448
|
+
const execution = normalizeExecutionOptions(
|
|
1449
|
+
runInput.execution,
|
|
1450
|
+
verifiedAuthorities.definitionAuthority.definition,
|
|
1451
|
+
verifiedAuthorities.executionLocationAuthority,
|
|
1452
|
+
);
|
|
1453
|
+
const prepared = prepareDynamicWorkflowRun(
|
|
1454
|
+
runInput,
|
|
1455
|
+
verifiedAuthorities,
|
|
1456
|
+
execution,
|
|
1457
|
+
);
|
|
1458
|
+
if (!prepared.outcome.allowed) {
|
|
1459
|
+
return Object.freeze({
|
|
1460
|
+
...prepared.outcome,
|
|
1461
|
+
record: null,
|
|
1462
|
+
executionStarted: false,
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
const finalAuthorities = await verifyAuthoritiesAsynchronously(
|
|
1467
|
+
dependencies.verifyAuthorities,
|
|
1468
|
+
lookup,
|
|
1469
|
+
runInput,
|
|
1470
|
+
);
|
|
1471
|
+
if (!authoritiesExactlyMatch(verifiedAuthorities, finalAuthorities)) {
|
|
1472
|
+
throw admissionError(
|
|
1473
|
+
DYNAMIC_WORKFLOW_EXECUTION_AUTHORITY_INVALID_CODE,
|
|
1474
|
+
"verified authorities changed during run admission",
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
if (runInput.onAdmitted) {
|
|
1478
|
+
await runInput.onAdmitted(
|
|
1479
|
+
prepared.outcome.admission,
|
|
1480
|
+
prepared.outcome.preflight,
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
const policy = prepared.outcome.admission.executionPolicy;
|
|
1484
|
+
const record = await executor({
|
|
1485
|
+
cwd: policy.cwd,
|
|
1486
|
+
continueOnError: policy.continueOnError,
|
|
1487
|
+
pipeline: policy.pipeline,
|
|
1488
|
+
llmOptions: Object.freeze({
|
|
1489
|
+
provider: policy.provider,
|
|
1490
|
+
model: policy.model,
|
|
1491
|
+
}),
|
|
1492
|
+
onStepStart: execution.onStepStart,
|
|
1493
|
+
onStepComplete: execution.onStepComplete,
|
|
1494
|
+
workflow: prepared.workflow,
|
|
1495
|
+
definitionDigest: prepared.outcome.admission.definitionDigest,
|
|
1496
|
+
maxParallel: prepared.outcome.admission.maxParallel,
|
|
1497
|
+
runAdmission: prepared.outcome.admission,
|
|
1498
|
+
});
|
|
1499
|
+
const normalizedRecord = normalizeExecutionRecord(record, prepared);
|
|
1500
|
+
|
|
1501
|
+
return Object.freeze({
|
|
1502
|
+
...prepared.outcome,
|
|
1503
|
+
record: normalizedRecord,
|
|
1504
|
+
executionStarted: true,
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
|
|
424
1508
|
export function buildDynamicWorkflowPreflight(input = {}) {
|
|
425
1509
|
const suppliedManifest =
|
|
426
1510
|
input.manifest?.schema === DYNAMIC_WORKFLOW_DEFINITION_SCHEMA
|
|
@@ -452,6 +1536,10 @@ export function buildDynamicWorkflowPreflight(input = {}) {
|
|
|
452
1536
|
})
|
|
453
1537
|
: null;
|
|
454
1538
|
|
|
1539
|
+
if (input.requireDefinitionAuthority === true && !definitionAuthority) {
|
|
1540
|
+
addUnique(blockers, "definition-authority-missing");
|
|
1541
|
+
}
|
|
1542
|
+
|
|
455
1543
|
if (
|
|
456
1544
|
suppliedManifest &&
|
|
457
1545
|
suppliedManifest.definitionDigest !== manifest.definitionDigest
|