@ixo/editor 6.32.1 → 6.34.0
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/dist/BidConfig-YLGSEIOS.js +154 -0
- package/dist/BidConfig-YLGSEIOS.js.map +1 -0
- package/dist/BidFlowDetail-TJCJBJZZ.js +365 -0
- package/dist/BidFlowDetail-TJCJBJZZ.js.map +1 -0
- package/dist/CalendarEventCreateConfig-J2LU6LJO.js +155 -0
- package/dist/CalendarEventCreateConfig-J2LU6LJO.js.map +1 -0
- package/dist/CalendarEventCreateFlowDetail-ZWHZWDOK.js +174 -0
- package/dist/CalendarEventCreateFlowDetail-ZWHZWDOK.js.map +1 -0
- package/dist/CalendarEventListConfig-GQNNF4ZB.js +116 -0
- package/dist/CalendarEventListConfig-GQNNF4ZB.js.map +1 -0
- package/dist/CalendarEventListFlowDetail-SSRTOXIX.js +58 -0
- package/dist/CalendarEventListFlowDetail-SSRTOXIX.js.map +1 -0
- package/dist/CalendarEventUpdateConfig-KACZA6GH.js +133 -0
- package/dist/CalendarEventUpdateConfig-KACZA6GH.js.map +1 -0
- package/dist/CalendarEventUpdateFlowDetail-CD6NHHOU.js +155 -0
- package/dist/CalendarEventUpdateFlowDetail-CD6NHHOU.js.map +1 -0
- package/dist/ClaimConfig-3HKPGK55.js +200 -0
- package/dist/ClaimConfig-3HKPGK55.js.map +1 -0
- package/dist/ClaimFlowDetail-UVEQMEI5.js +641 -0
- package/dist/ClaimFlowDetail-UVEQMEI5.js.map +1 -0
- package/dist/CollectionLifecycleConfig-LCCGZ3CJ.js +149 -0
- package/dist/CollectionLifecycleConfig-LCCGZ3CJ.js.map +1 -0
- package/dist/CollectionLifecycleFlowDetail-WFCSVIIG.js +647 -0
- package/dist/CollectionLifecycleFlowDetail-WFCSVIIG.js.map +1 -0
- package/dist/CollectionUsersConfig-RI2X3YY2.js +271 -0
- package/dist/CollectionUsersConfig-RI2X3YY2.js.map +1 -0
- package/dist/CollectionUsersFlowDetail-ZLCJBWHG.js +1276 -0
- package/dist/CollectionUsersFlowDetail-ZLCJBWHG.js.map +1 -0
- package/dist/CredentialStoreConfig-G66AQDGZ.js +75 -0
- package/dist/CredentialStoreConfig-G66AQDGZ.js.map +1 -0
- package/dist/CredentialStoreFlowDetail-FL7QAM3S.js +99 -0
- package/dist/CredentialStoreFlowDetail-FL7QAM3S.js.map +1 -0
- package/dist/DesignPodConfig-LJ7H2CZ6.js +8 -0
- package/dist/DesignPodConfig-LJ7H2CZ6.js.map +1 -0
- package/dist/DesignPodFlowDetail-JHJWGNVY.js +309 -0
- package/dist/DesignPodFlowDetail-JHJWGNVY.js.map +1 -0
- package/dist/DomainCardPreviewConfig-Q3X5M4WF.js +37 -0
- package/dist/DomainCardPreviewConfig-Q3X5M4WF.js.map +1 -0
- package/dist/DomainCardPreviewFlowDetail-Y2FE7O5Z.js +131 -0
- package/dist/DomainCardPreviewFlowDetail-Y2FE7O5Z.js.map +1 -0
- package/dist/DomainIndexerLookupConfig-N4VONQUY.js +41 -0
- package/dist/DomainIndexerLookupConfig-N4VONQUY.js.map +1 -0
- package/dist/DomainIndexerLookupFlowDetail-GJ7WWK7I.js +70 -0
- package/dist/DomainIndexerLookupFlowDetail-GJ7WWK7I.js.map +1 -0
- package/dist/DomainSignConfig-XFIXROVK.js +47 -0
- package/dist/DomainSignConfig-XFIXROVK.js.map +1 -0
- package/dist/DomainSignFlowDetail-BXZ6JBPR.js +206 -0
- package/dist/DomainSignFlowDetail-BXZ6JBPR.js.map +1 -0
- package/dist/DomainSingleSelectionConfig-ZNKIXB4P.js +101 -0
- package/dist/DomainSingleSelectionConfig-ZNKIXB4P.js.map +1 -0
- package/dist/DomainSingleSelectionFlowDetail-HWTQXSLA.js +147 -0
- package/dist/DomainSingleSelectionFlowDetail-HWTQXSLA.js.map +1 -0
- package/dist/EmailSendConfig-YEGZ5Q77.js +215 -0
- package/dist/EmailSendConfig-YEGZ5Q77.js.map +1 -0
- package/dist/EntityCreateOracleConfig-UZ6ZIMP5.js +150 -0
- package/dist/EntityCreateOracleConfig-UZ6ZIMP5.js.map +1 -0
- package/dist/EntityCreateOracleFlowDetail-H2X7CPYU.js +120 -0
- package/dist/EntityCreateOracleFlowDetail-H2X7CPYU.js.map +1 -0
- package/dist/EntitySingleSelectionConfig-DL57KNKM.js +30 -0
- package/dist/EntitySingleSelectionConfig-DL57KNKM.js.map +1 -0
- package/dist/EntitySingleSelectionFlowDetail-GFQGX3GA.js +118 -0
- package/dist/EntitySingleSelectionFlowDetail-GFQGX3GA.js.map +1 -0
- package/dist/EntityTransferConfig-JVZ3H35B.js +62 -0
- package/dist/EntityTransferConfig-JVZ3H35B.js.map +1 -0
- package/dist/EntityTransferFlowDetail-XEXM34XB.js +132 -0
- package/dist/EntityTransferFlowDetail-XEXM34XB.js.map +1 -0
- package/dist/EvalEngineConfig-CHC4IRN3.js +464 -0
- package/dist/EvalEngineConfig-CHC4IRN3.js.map +1 -0
- package/dist/EvalEngineFlowDetail-PUFRNO4U.js +4291 -0
- package/dist/EvalEngineFlowDetail-PUFRNO4U.js.map +1 -0
- package/dist/EvaluateBidConfig-OE4CM43G.js +151 -0
- package/dist/EvaluateBidConfig-OE4CM43G.js.map +1 -0
- package/dist/EvaluateBidFlowDetail-2S2N4YTW.js +564 -0
- package/dist/EvaluateBidFlowDetail-2S2N4YTW.js.map +1 -0
- package/dist/EvaluateClaimConfig-3D4F5LHU.js +297 -0
- package/dist/EvaluateClaimConfig-3D4F5LHU.js.map +1 -0
- package/dist/EvaluateClaimFlowDetail-QYR7PJRD.js +2237 -0
- package/dist/EvaluateClaimFlowDetail-QYR7PJRD.js.map +1 -0
- package/dist/FormSubmitConfig-UFZYMLUN.js +59 -0
- package/dist/FormSubmitConfig-UFZYMLUN.js.map +1 -0
- package/dist/FormSubmitFlowDetail-IAHDJS6P.js +169 -0
- package/dist/FormSubmitFlowDetail-IAHDJS6P.js.map +1 -0
- package/dist/GovernanceAuthzExecFlowDetail-SQBGU5BX.js +166 -0
- package/dist/GovernanceAuthzExecFlowDetail-SQBGU5BX.js.map +1 -0
- package/dist/GovernanceAuthzGrantFlowDetail-DZPNLANT.js +88 -0
- package/dist/GovernanceAuthzGrantFlowDetail-DZPNLANT.js.map +1 -0
- package/dist/GovernanceAuthzRevokeFlowDetail-3JZ4IUJG.js +88 -0
- package/dist/GovernanceAuthzRevokeFlowDetail-3JZ4IUJG.js.map +1 -0
- package/dist/GovernanceChainVoteFlowDetail-475UDNG6.js +83 -0
- package/dist/GovernanceChainVoteFlowDetail-475UDNG6.js.map +1 -0
- package/dist/GovernanceConfigConfig-V3WM4WBP.js +30 -0
- package/dist/GovernanceConfigConfig-V3WM4WBP.js.map +1 -0
- package/dist/GovernanceConfigFlowDetail-SQ5PPDRX.js +267 -0
- package/dist/GovernanceConfigFlowDetail-SQ5PPDRX.js.map +1 -0
- package/dist/GovernanceContractExecuteFlowDetail-H2ZV6W2E.js +113 -0
- package/dist/GovernanceContractExecuteFlowDetail-H2ZV6W2E.js.map +1 -0
- package/dist/GovernanceContractInstantiateFlowDetail-YJJMGM54.js +144 -0
- package/dist/GovernanceContractInstantiateFlowDetail-YJJMGM54.js.map +1 -0
- package/dist/GovernanceContractManageCw20FlowDetail-OCBMW3YJ.js +72 -0
- package/dist/GovernanceContractManageCw20FlowDetail-OCBMW3YJ.js.map +1 -0
- package/dist/GovernanceContractMigrateFlowDetail-JDNIKTT3.js +104 -0
- package/dist/GovernanceContractMigrateFlowDetail-JDNIKTT3.js.map +1 -0
- package/dist/GovernanceContractUpdateAdminFlowDetail-SZBOAOCC.js +68 -0
- package/dist/GovernanceContractUpdateAdminFlowDetail-SZBOAOCC.js.map +1 -0
- package/dist/GovernanceCustomMessageFlowDetail-CXD4ZATO.js +78 -0
- package/dist/GovernanceCustomMessageFlowDetail-CXD4ZATO.js.map +1 -0
- package/dist/GovernanceDaoAcceptToMarketplaceFlowDetail-Q6ZBNY7Z.js +98 -0
- package/dist/GovernanceDaoAcceptToMarketplaceFlowDetail-Q6ZBNY7Z.js.map +1 -0
- package/dist/GovernanceDaoAdminExecFlowDetail-JAEAY2US.js +94 -0
- package/dist/GovernanceDaoAdminExecFlowDetail-JAEAY2US.js.map +1 -0
- package/dist/GovernanceDaoCreateEntityFlowDetail-7RVNTVAB.js +96 -0
- package/dist/GovernanceDaoCreateEntityFlowDetail-7RVNTVAB.js.map +1 -0
- package/dist/GovernanceDaoJoinFlowDetail-XYCTCNHO.js +68 -0
- package/dist/GovernanceDaoJoinFlowDetail-XYCTCNHO.js.map +1 -0
- package/dist/GovernanceDaoManageStorageFlowDetail-LMLYLYYC.js +93 -0
- package/dist/GovernanceDaoManageStorageFlowDetail-LMLYLYYC.js.map +1 -0
- package/dist/GovernanceDaoManageSubDaosFlowDetail-YUFQ67JN.js +84 -0
- package/dist/GovernanceDaoManageSubDaosFlowDetail-YUFQ67JN.js.map +1 -0
- package/dist/GovernanceDaoUpdateInfoFlowDetail-G5FIBY4L.js +104 -0
- package/dist/GovernanceDaoUpdateInfoFlowDetail-G5FIBY4L.js.map +1 -0
- package/dist/GovernanceMemberProposalConfig-IGPXEFDB.js +42 -0
- package/dist/GovernanceMemberProposalConfig-IGPXEFDB.js.map +1 -0
- package/dist/GovernanceMemberProposalFlowDetail-T3VURONF.js +240 -0
- package/dist/GovernanceMemberProposalFlowDetail-T3VURONF.js.map +1 -0
- package/dist/GovernanceNftBurnFlowDetail-AG4I2O7H.js +69 -0
- package/dist/GovernanceNftBurnFlowDetail-AG4I2O7H.js.map +1 -0
- package/dist/GovernanceNftManageCollectionsFlowDetail-UAAGO3Q6.js +71 -0
- package/dist/GovernanceNftManageCollectionsFlowDetail-UAAGO3Q6.js.map +1 -0
- package/dist/GovernanceNftTransferFlowDetail-KXM7DTOV.js +130 -0
- package/dist/GovernanceNftTransferFlowDetail-KXM7DTOV.js.map +1 -0
- package/dist/GovernanceSettingsProposalConfig-4SLJTBLW.js +42 -0
- package/dist/GovernanceSettingsProposalConfig-4SLJTBLW.js.map +1 -0
- package/dist/GovernanceSettingsProposalFlowDetail-VFZPSCLR.js +165 -0
- package/dist/GovernanceSettingsProposalFlowDetail-VFZPSCLR.js.map +1 -0
- package/dist/GovernanceStakingStakeFlowDetail-ULLJ7FLM.js +123 -0
- package/dist/GovernanceStakingStakeFlowDetail-ULLJ7FLM.js.map +1 -0
- package/dist/GovernanceStakingStakeToGroupFlowDetail-CSPKJA3H.js +84 -0
- package/dist/GovernanceStakingStakeToGroupFlowDetail-CSPKJA3H.js.map +1 -0
- package/dist/GovernanceSubmissionConfigProposalFlowDetail-MZCPSZF2.js +111 -0
- package/dist/GovernanceSubmissionConfigProposalFlowDetail-MZCPSZF2.js.map +1 -0
- package/dist/GovernanceTransactionMintFlowDetail-Y2WRAODH.js +66 -0
- package/dist/GovernanceTransactionMintFlowDetail-Y2WRAODH.js.map +1 -0
- package/dist/GovernanceTransactionPerformTokenSwapFlowDetail-GEK6PMMI.js +133 -0
- package/dist/GovernanceTransactionPerformTokenSwapFlowDetail-GEK6PMMI.js.map +1 -0
- package/dist/GovernanceTransactionSendFundsConfig-VBKMLSF3.js +42 -0
- package/dist/GovernanceTransactionSendFundsConfig-VBKMLSF3.js.map +1 -0
- package/dist/GovernanceTransactionSendFundsFlowDetail-5TZB64VL.js +182 -0
- package/dist/GovernanceTransactionSendFundsFlowDetail-5TZB64VL.js.map +1 -0
- package/dist/GovernanceTransactionSendGroupTokenFlowDetail-EGOWVZM2.js +155 -0
- package/dist/GovernanceTransactionSendGroupTokenFlowDetail-EGOWVZM2.js.map +1 -0
- package/dist/GovernanceTransactionWithdrawTokenSwapFlowDetail-6XWVO5O6.js +56 -0
- package/dist/GovernanceTransactionWithdrawTokenSwapFlowDetail-6XWVO5O6.js.map +1 -0
- package/dist/GovernanceValidatorActionsFlowDetail-ZMLPAFUB.js +117 -0
- package/dist/GovernanceValidatorActionsFlowDetail-ZMLPAFUB.js.map +1 -0
- package/dist/HarvestConfig-AIGOGBFR.js +56 -0
- package/dist/HarvestConfig-AIGOGBFR.js.map +1 -0
- package/dist/HarvestFlowDetail-X3RUH3X3.js +131 -0
- package/dist/HarvestFlowDetail-X3RUH3X3.js.map +1 -0
- package/dist/HttpRequestConfig-OQEJIIJY.js +194 -0
- package/dist/HttpRequestConfig-OQEJIIJY.js.map +1 -0
- package/dist/HttpRequestFlowDetail-JVLV533Z.js +116 -0
- package/dist/HttpRequestFlowDetail-JVLV533Z.js.map +1 -0
- package/dist/IdentityCreateConfig-ZBUOJ6AL.js +94 -0
- package/dist/IdentityCreateConfig-ZBUOJ6AL.js.map +1 -0
- package/dist/IdentityCreateFlowDetail-WGKO43IH.js +83 -0
- package/dist/IdentityCreateFlowDetail-WGKO43IH.js.map +1 -0
- package/dist/IidCreateConfig-RRV2DYXI.js +75 -0
- package/dist/IidCreateConfig-RRV2DYXI.js.map +1 -0
- package/dist/IidCreateFlowDetail-6M32S6XS.js +57 -0
- package/dist/IidCreateFlowDetail-6M32S6XS.js.map +1 -0
- package/dist/KycVerifyConfig-VCG65K5K.js +65 -0
- package/dist/KycVerifyConfig-VCG65K5K.js.map +1 -0
- package/dist/KycVerifyFlowDetail-EWOMXSD3.js +421 -0
- package/dist/KycVerifyFlowDetail-EWOMXSD3.js.map +1 -0
- package/dist/ListDomainFlowsConfig-3JZEPTOJ.js +37 -0
- package/dist/ListDomainFlowsConfig-3JZEPTOJ.js.map +1 -0
- package/dist/ListDomainFlowsFlowDetail-XX5AXDZZ.js +257 -0
- package/dist/ListDomainFlowsFlowDetail-XX5AXDZZ.js.map +1 -0
- package/dist/LoadBatchesConfig-IMODIWRT.js +38 -0
- package/dist/LoadBatchesConfig-IMODIWRT.js.map +1 -0
- package/dist/LoadBatchesFlowDetail-GB24T76O.js +101 -0
- package/dist/LoadBatchesFlowDetail-GB24T76O.js.map +1 -0
- package/dist/MatrixDmConfig-NBII27CS.js +120 -0
- package/dist/MatrixDmConfig-NBII27CS.js.map +1 -0
- package/dist/MatrixRegisterConfig-N2U4FBYF.js +62 -0
- package/dist/MatrixRegisterConfig-N2U4FBYF.js.map +1 -0
- package/dist/MatrixRegisterFlowDetail-D5YNL5QX.js +86 -0
- package/dist/MatrixRegisterFlowDetail-D5YNL5QX.js.map +1 -0
- package/dist/MemberMultiSelectConfig-E7WUB2WD.js +30 -0
- package/dist/MemberMultiSelectConfig-E7WUB2WD.js.map +1 -0
- package/dist/MemberMultiSelectFlowDetail-6C4ZMSDR.js +392 -0
- package/dist/MemberMultiSelectFlowDetail-6C4ZMSDR.js.map +1 -0
- package/dist/OracleConfig-R7TDTTKN.js +56 -0
- package/dist/OracleConfig-R7TDTTKN.js.map +1 -0
- package/dist/OracleConfigureOracleConfig-QFF74X3N.js +198 -0
- package/dist/OracleConfigureOracleConfig-QFF74X3N.js.map +1 -0
- package/dist/OracleConfigureOracleFlowDetail-73BHAN76.js +255 -0
- package/dist/OracleConfigureOracleFlowDetail-73BHAN76.js.map +1 -0
- package/dist/OracleContractConfig-PHY5L7W2.js +38 -0
- package/dist/OracleContractConfig-PHY5L7W2.js.map +1 -0
- package/dist/OracleContractFlowDetail-M3PM4AU4.js +78 -0
- package/dist/OracleContractFlowDetail-M3PM4AU4.js.map +1 -0
- package/dist/OracleDeployConfig-QBG76ATZ.js +10 -0
- package/dist/OracleDeployConfig-QBG76ATZ.js.map +1 -0
- package/dist/OracleDeployFlowDetail-WKTMD2AD.js +231 -0
- package/dist/OracleDeployFlowDetail-WKTMD2AD.js.map +1 -0
- package/dist/OracleDeploySetupConfig-75XPGFJQ.js +10 -0
- package/dist/OracleDeploySetupConfig-75XPGFJQ.js.map +1 -0
- package/dist/OracleDeploySetupFlowDetail-R3BQ7EAU.js +165 -0
- package/dist/OracleDeploySetupFlowDetail-R3BQ7EAU.js.map +1 -0
- package/dist/OracleDeployStartConfig-NA6GZ4VQ.js +10 -0
- package/dist/OracleDeployStartConfig-NA6GZ4VQ.js.map +1 -0
- package/dist/OracleDeployStartFlowDetail-EL3CMDMG.js +89 -0
- package/dist/OracleDeployStartFlowDetail-EL3CMDMG.js.map +1 -0
- package/dist/OracleFlowDetail-5JCJK3GH.js +76 -0
- package/dist/OracleFlowDetail-5JCJK3GH.js.map +1 -0
- package/dist/OraclePromptConfig-YBRESISH.js +39 -0
- package/dist/OraclePromptConfig-YBRESISH.js.map +1 -0
- package/dist/OraclePromptFlowDetail-O3XNK7HY.js +112 -0
- package/dist/OraclePromptFlowDetail-O3XNK7HY.js.map +1 -0
- package/dist/OracleStoreConfigConfig-TUZWTJMA.js +134 -0
- package/dist/OracleStoreConfigConfig-TUZWTJMA.js.map +1 -0
- package/dist/OracleStoreConfigFlowDetail-Q6AUUSUL.js +99 -0
- package/dist/OracleStoreConfigFlowDetail-Q6AUUSUL.js.map +1 -0
- package/dist/OracleStoreSecretsAndConfigConfig-SZPYEK7U.js +198 -0
- package/dist/OracleStoreSecretsAndConfigConfig-SZPYEK7U.js.map +1 -0
- package/dist/OracleStoreSecretsAndConfigFlowDetail-BFV2EHLT.js +220 -0
- package/dist/OracleStoreSecretsAndConfigFlowDetail-BFV2EHLT.js.map +1 -0
- package/dist/OracleStoreSecretsConfig-PQMF7LFT.js +191 -0
- package/dist/OracleStoreSecretsConfig-PQMF7LFT.js.map +1 -0
- package/dist/OracleStoreSecretsFlowDetail-DNNOPHVA.js +197 -0
- package/dist/OracleStoreSecretsFlowDetail-DNNOPHVA.js.map +1 -0
- package/dist/ProposalCreateConfig-U5IHZNT7.js +161 -0
- package/dist/ProposalCreateConfig-U5IHZNT7.js.map +1 -0
- package/dist/ProposalCreateFlowDetail-4VZZPAWH.js +235 -0
- package/dist/ProposalCreateFlowDetail-4VZZPAWH.js.map +1 -0
- package/dist/ProposalVoteConfig-MH4MU7SL.js +150 -0
- package/dist/ProposalVoteConfig-MH4MU7SL.js.map +1 -0
- package/dist/ProposalVoteFlowDetail-IJJ3OHAB.js +280 -0
- package/dist/ProposalVoteFlowDetail-IJJ3OHAB.js.map +1 -0
- package/dist/ProtocolSelectConfig-S5U3P6K7.js +48 -0
- package/dist/ProtocolSelectConfig-S5U3P6K7.js.map +1 -0
- package/dist/ProtocolSelectFlowDetail-XBSJIEIH.js +116 -0
- package/dist/ProtocolSelectFlowDetail-XBSJIEIH.js.map +1 -0
- package/dist/RetireConfig-MV37WWZ6.js +59 -0
- package/dist/RetireConfig-MV37WWZ6.js.map +1 -0
- package/dist/RetireFlowDetail-VZ5O4SXI.js +147 -0
- package/dist/RetireFlowDetail-VZ5O4SXI.js.map +1 -0
- package/dist/SandboxProvisionConfig-ZGG4IRHM.js +46 -0
- package/dist/SandboxProvisionConfig-ZGG4IRHM.js.map +1 -0
- package/dist/SandboxProvisionFlowDetail-RUY64INV.js +79 -0
- package/dist/SandboxProvisionFlowDetail-RUY64INV.js.map +1 -0
- package/dist/WalletFundConfig-CMM3EP3B.js +117 -0
- package/dist/WalletFundConfig-CMM3EP3B.js.map +1 -0
- package/dist/WalletFundFlowDetail-TXWCZA4K.js +279 -0
- package/dist/WalletFundFlowDetail-TXWCZA4K.js.map +1 -0
- package/dist/WalletGenerateAndFundConfig-364RSEND.js +10 -0
- package/dist/WalletGenerateAndFundConfig-364RSEND.js.map +1 -0
- package/dist/WalletGenerateAndFundFlowDetail-C2LZW3NO.js +54 -0
- package/dist/WalletGenerateAndFundFlowDetail-C2LZW3NO.js.map +1 -0
- package/dist/WalletGenerateConfig-YYLCS65G.js +10 -0
- package/dist/WalletGenerateConfig-YYLCS65G.js.map +1 -0
- package/dist/WalletGenerateFlowDetail-CQCNDGJA.js +53 -0
- package/dist/WalletGenerateFlowDetail-CQCNDGJA.js.map +1 -0
- package/dist/XeroContactCreateConfig-OWG5PA3B.js +102 -0
- package/dist/XeroContactCreateConfig-OWG5PA3B.js.map +1 -0
- package/dist/XeroContactCreateFlowDetail-6MAINCF6.js +127 -0
- package/dist/XeroContactCreateFlowDetail-6MAINCF6.js.map +1 -0
- package/dist/XeroInvoiceCreateConfig-O74RUSZM.js +403 -0
- package/dist/XeroInvoiceCreateConfig-O74RUSZM.js.map +1 -0
- package/dist/XeroInvoiceCreateFlowDetail-UL7KS6CZ.js +631 -0
- package/dist/XeroInvoiceCreateFlowDetail-UL7KS6CZ.js.map +1 -0
- package/dist/XeroInvoiceListConfig-UR3GONBJ.js +113 -0
- package/dist/XeroInvoiceListConfig-UR3GONBJ.js.map +1 -0
- package/dist/XeroInvoiceListFlowDetail-LYHODWH6.js +58 -0
- package/dist/XeroInvoiceListFlowDetail-LYHODWH6.js.map +1 -0
- package/dist/XeroPaymentCreateConfig-RPMAIK6E.js +116 -0
- package/dist/XeroPaymentCreateConfig-RPMAIK6E.js.map +1 -0
- package/dist/XeroPaymentCreateFlowDetail-CF35MVIM.js +573 -0
- package/dist/XeroPaymentCreateFlowDetail-CF35MVIM.js.map +1 -0
- package/dist/action-manifest.json +2091 -393
- package/dist/chunk-2GAYKKTL.js +9 -0
- package/dist/chunk-2GAYKKTL.js.map +1 -0
- package/dist/chunk-2WPEATI7.js +315 -0
- package/dist/chunk-2WPEATI7.js.map +1 -0
- package/dist/chunk-3O42FYT3.js +3333 -0
- package/dist/chunk-3O42FYT3.js.map +1 -0
- package/dist/chunk-3TUYY4HK.js +20 -0
- package/dist/chunk-3TUYY4HK.js.map +1 -0
- package/dist/chunk-3XSLBJYI.js +554 -0
- package/dist/chunk-3XSLBJYI.js.map +1 -0
- package/dist/chunk-4DGCCXD6.js +710 -0
- package/dist/chunk-4DGCCXD6.js.map +1 -0
- package/dist/chunk-4NAFQF6V.js +11 -0
- package/dist/chunk-4NAFQF6V.js.map +1 -0
- package/dist/chunk-4PONO4US.js +799 -0
- package/dist/chunk-4PONO4US.js.map +1 -0
- package/dist/chunk-4TMOIK5M.js +21 -0
- package/dist/chunk-4TMOIK5M.js.map +1 -0
- package/dist/chunk-5IH3VC5I.js +102 -0
- package/dist/chunk-5IH3VC5I.js.map +1 -0
- package/dist/chunk-5NTKDOP6.js +89 -0
- package/dist/chunk-5NTKDOP6.js.map +1 -0
- package/dist/chunk-5O2SFRGU.js +18 -0
- package/dist/chunk-5O2SFRGU.js.map +1 -0
- package/dist/chunk-632JLECF.js +55 -0
- package/dist/chunk-632JLECF.js.map +1 -0
- package/dist/chunk-676HDB7K.js +95 -0
- package/dist/chunk-676HDB7K.js.map +1 -0
- package/dist/chunk-6ESTNTSH.js +223 -0
- package/dist/chunk-6ESTNTSH.js.map +1 -0
- package/dist/chunk-6GKJR52N.js +15 -0
- package/dist/chunk-6GKJR52N.js.map +1 -0
- package/dist/chunk-6L4ZIKYU.js +23 -0
- package/dist/chunk-6L4ZIKYU.js.map +1 -0
- package/dist/chunk-76YX5R4L.js +11 -0
- package/dist/chunk-76YX5R4L.js.map +1 -0
- package/dist/chunk-7J3GFMN3.js +32 -0
- package/dist/chunk-7J3GFMN3.js.map +1 -0
- package/dist/chunk-7LSARC2G.js +102 -0
- package/dist/chunk-7LSARC2G.js.map +1 -0
- package/dist/chunk-7W7UUUIE.js +609 -0
- package/dist/chunk-7W7UUUIE.js.map +1 -0
- package/dist/chunk-7ZLW7H37.js +21 -0
- package/dist/chunk-7ZLW7H37.js.map +1 -0
- package/dist/chunk-AAZHI22G.js +18 -0
- package/dist/chunk-AAZHI22G.js.map +1 -0
- package/dist/chunk-ACNE455I.js +76 -0
- package/dist/chunk-ACNE455I.js.map +1 -0
- package/dist/chunk-BKLSSLPZ.js +27 -0
- package/dist/chunk-BKLSSLPZ.js.map +1 -0
- package/dist/chunk-BZ7UPUTY.js +417 -0
- package/dist/chunk-BZ7UPUTY.js.map +1 -0
- package/dist/chunk-CGFAICUE.js +28 -0
- package/dist/chunk-CGFAICUE.js.map +1 -0
- package/dist/chunk-CMCEWQ3C.js +19 -0
- package/dist/chunk-CMCEWQ3C.js.map +1 -0
- package/dist/chunk-CP4UV436.js +41 -0
- package/dist/chunk-CP4UV436.js.map +1 -0
- package/dist/chunk-D2CQPNQG.js +27508 -0
- package/dist/chunk-D2CQPNQG.js.map +1 -0
- package/dist/chunk-DIQRZP7Z.js +51 -0
- package/dist/chunk-DIQRZP7Z.js.map +1 -0
- package/dist/chunk-EBFFDDAS.js +13 -0
- package/dist/chunk-EBFFDDAS.js.map +1 -0
- package/dist/chunk-EGVS23DR.js +85 -0
- package/dist/chunk-EGVS23DR.js.map +1 -0
- package/dist/chunk-EKBQ7K4N.js +15 -0
- package/dist/chunk-EKBQ7K4N.js.map +1 -0
- package/dist/chunk-EKQCZRAN.js +24 -0
- package/dist/chunk-EKQCZRAN.js.map +1 -0
- package/dist/chunk-EYKZIZ5J.js +48 -0
- package/dist/chunk-EYKZIZ5J.js.map +1 -0
- package/dist/{chunk-NEOPTPDF.js → chunk-F4PTFXZH.js} +11554 -22046
- package/dist/chunk-F4PTFXZH.js.map +1 -0
- package/dist/chunk-FFYZXA75.js +58 -0
- package/dist/chunk-FFYZXA75.js.map +1 -0
- package/dist/chunk-GFGDHPZ2.js +22 -0
- package/dist/chunk-GFGDHPZ2.js.map +1 -0
- package/dist/chunk-GR3CDFID.js +18 -0
- package/dist/chunk-GR3CDFID.js.map +1 -0
- package/dist/chunk-GR4SIU32.js +104 -0
- package/dist/chunk-GR4SIU32.js.map +1 -0
- package/dist/chunk-H5E46IIS.js +18 -0
- package/dist/chunk-H5E46IIS.js.map +1 -0
- package/dist/chunk-HFHVAMZL.js +22 -0
- package/dist/chunk-HFHVAMZL.js.map +1 -0
- package/dist/chunk-HRRI4BLF.js +4456 -0
- package/dist/chunk-HRRI4BLF.js.map +1 -0
- package/dist/chunk-HT6L7A5M.js +12 -0
- package/dist/chunk-HT6L7A5M.js.map +1 -0
- package/dist/chunk-I2FPFISM.js +301 -0
- package/dist/chunk-I2FPFISM.js.map +1 -0
- package/dist/chunk-IADAQJDH.js +28 -0
- package/dist/chunk-IADAQJDH.js.map +1 -0
- package/dist/chunk-IBOBCMQQ.js +99 -0
- package/dist/chunk-IBOBCMQQ.js.map +1 -0
- package/dist/chunk-IFWKRCFF.js +56 -0
- package/dist/chunk-IFWKRCFF.js.map +1 -0
- package/dist/chunk-IH4T7V4C.js +40 -0
- package/dist/chunk-IH4T7V4C.js.map +1 -0
- package/dist/chunk-IIIZZ4E4.js +1679 -0
- package/dist/chunk-IIIZZ4E4.js.map +1 -0
- package/dist/chunk-ILSFU3WD.js +83 -0
- package/dist/chunk-ILSFU3WD.js.map +1 -0
- package/dist/chunk-IX3VBV3L.js +43 -0
- package/dist/chunk-IX3VBV3L.js.map +1 -0
- package/dist/chunk-JBBFSORW.js +846 -0
- package/dist/chunk-JBBFSORW.js.map +1 -0
- package/dist/chunk-JIDTNKKL.js +1183 -0
- package/dist/chunk-JIDTNKKL.js.map +1 -0
- package/dist/chunk-JSPE6NGZ.js +22 -0
- package/dist/chunk-JSPE6NGZ.js.map +1 -0
- package/dist/chunk-JVROGQ7Q.js +150 -0
- package/dist/chunk-JVROGQ7Q.js.map +1 -0
- package/dist/chunk-K4WUMWNJ.js +32 -0
- package/dist/chunk-K4WUMWNJ.js.map +1 -0
- package/dist/chunk-K74XQ4Q5.js +11 -0
- package/dist/chunk-K74XQ4Q5.js.map +1 -0
- package/dist/chunk-KLLMJ4U3.js +22 -0
- package/dist/chunk-KLLMJ4U3.js.map +1 -0
- package/dist/chunk-LTKVCJ2E.js +27 -0
- package/dist/chunk-LTKVCJ2E.js.map +1 -0
- package/dist/chunk-M36DJTFZ.js +34 -0
- package/dist/chunk-M36DJTFZ.js.map +1 -0
- package/dist/chunk-MHM52LCJ.js +154 -0
- package/dist/chunk-MHM52LCJ.js.map +1 -0
- package/dist/chunk-MQCDMHRZ.js +59 -0
- package/dist/chunk-MQCDMHRZ.js.map +1 -0
- package/dist/chunk-N3ORMGAI.js +348 -0
- package/dist/chunk-N3ORMGAI.js.map +1 -0
- package/dist/chunk-NCSGIRRQ.js +28 -0
- package/dist/chunk-NCSGIRRQ.js.map +1 -0
- package/dist/chunk-NIXT2SBH.js +1423 -0
- package/dist/chunk-NIXT2SBH.js.map +1 -0
- package/dist/chunk-NJAII6CO.js +148 -0
- package/dist/chunk-NJAII6CO.js.map +1 -0
- package/dist/chunk-NKE4TL43.js +92 -0
- package/dist/chunk-NKE4TL43.js.map +1 -0
- package/dist/chunk-NZ7AXA7B.js +1 -0
- package/dist/chunk-NZ7AXA7B.js.map +1 -0
- package/dist/chunk-OCAYEGI2.js +26 -0
- package/dist/chunk-OCAYEGI2.js.map +1 -0
- package/dist/chunk-OMLI45ER.js +90 -0
- package/dist/chunk-OMLI45ER.js.map +1 -0
- package/dist/chunk-P7OBLJAG.js +88 -0
- package/dist/chunk-P7OBLJAG.js.map +1 -0
- package/dist/chunk-PAY3CBWL.js +2321 -0
- package/dist/chunk-PAY3CBWL.js.map +1 -0
- package/dist/chunk-PFF6VY35.js +257 -0
- package/dist/chunk-PFF6VY35.js.map +1 -0
- package/dist/chunk-PYNDJDWZ.js +1758 -0
- package/dist/chunk-PYNDJDWZ.js.map +1 -0
- package/dist/chunk-QDQ6UMMN.js +53 -0
- package/dist/chunk-QDQ6UMMN.js.map +1 -0
- package/dist/chunk-QGT3G2XG.js +24 -0
- package/dist/chunk-QGT3G2XG.js.map +1 -0
- package/dist/chunk-QZYCJ2GZ.js +55 -0
- package/dist/chunk-QZYCJ2GZ.js.map +1 -0
- package/dist/chunk-REZ7RFSA.js +124 -0
- package/dist/chunk-REZ7RFSA.js.map +1 -0
- package/dist/chunk-RRYBAE6E.js +2449 -0
- package/dist/chunk-RRYBAE6E.js.map +1 -0
- package/dist/chunk-RWUZOB62.js +34 -0
- package/dist/chunk-RWUZOB62.js.map +1 -0
- package/dist/chunk-SB5UCPZH.js +116 -0
- package/dist/chunk-SB5UCPZH.js.map +1 -0
- package/dist/chunk-SEJJ7CRI.js +99 -0
- package/dist/chunk-SEJJ7CRI.js.map +1 -0
- package/dist/chunk-SHBFGRPV.js +45 -0
- package/dist/chunk-SHBFGRPV.js.map +1 -0
- package/dist/chunk-SOFJ4GC5.js +45 -0
- package/dist/chunk-SOFJ4GC5.js.map +1 -0
- package/dist/chunk-STH2HPZK.js +44 -0
- package/dist/chunk-STH2HPZK.js.map +1 -0
- package/dist/chunk-TK3JNKME.js +28 -0
- package/dist/chunk-TK3JNKME.js.map +1 -0
- package/dist/chunk-TMONHFE3.js +46 -0
- package/dist/chunk-TMONHFE3.js.map +1 -0
- package/dist/chunk-TR4KO3AG.js +44 -0
- package/dist/chunk-TR4KO3AG.js.map +1 -0
- package/dist/chunk-TS2LNBF2.js +68 -0
- package/dist/chunk-TS2LNBF2.js.map +1 -0
- package/dist/chunk-UZUMBFPP.js +26 -0
- package/dist/chunk-UZUMBFPP.js.map +1 -0
- package/dist/chunk-VRZ5LMOW.js +176 -0
- package/dist/chunk-VRZ5LMOW.js.map +1 -0
- package/dist/chunk-WLGGY6Z6.js +75 -0
- package/dist/chunk-WLGGY6Z6.js.map +1 -0
- package/dist/chunk-WO7V6ZNC.js +11 -0
- package/dist/chunk-WO7V6ZNC.js.map +1 -0
- package/dist/chunk-WXSENSRP.js +64 -0
- package/dist/chunk-WXSENSRP.js.map +1 -0
- package/dist/chunk-XEKBZ2SA.js +19 -0
- package/dist/chunk-XEKBZ2SA.js.map +1 -0
- package/dist/chunk-XPBKU3JX.js +61 -0
- package/dist/chunk-XPBKU3JX.js.map +1 -0
- package/dist/chunk-XRYXBXWH.js +24 -0
- package/dist/chunk-XRYXBXWH.js.map +1 -0
- package/dist/chunk-XVA7QRVH.js +22 -0
- package/dist/chunk-XVA7QRVH.js.map +1 -0
- package/dist/chunk-YCTHHBSS.js +37 -0
- package/dist/chunk-YCTHHBSS.js.map +1 -0
- package/dist/chunk-YHPWD5NE.js +70 -0
- package/dist/chunk-YHPWD5NE.js.map +1 -0
- package/dist/chunk-YLNEKJQ5.js +126 -0
- package/dist/chunk-YLNEKJQ5.js.map +1 -0
- package/dist/chunk-YN4CEKCF.js +73 -0
- package/dist/chunk-YN4CEKCF.js.map +1 -0
- package/dist/chunk-YYNMESIF.js +17 -0
- package/dist/chunk-YYNMESIF.js.map +1 -0
- package/dist/chunk-Z6PRMCV6.js +1 -0
- package/dist/chunk-Z6PRMCV6.js.map +1 -0
- package/dist/chunk-ZDNW6VQG.js +11 -0
- package/dist/chunk-ZDNW6VQG.js.map +1 -0
- package/dist/chunk-ZP4D73DO.js +36 -0
- package/dist/chunk-ZP4D73DO.js.map +1 -0
- package/dist/chunk-ZUATADUV.js +26 -0
- package/dist/chunk-ZUATADUV.js.map +1 -0
- package/dist/{connections-1NtNQrsY.d.ts → connections-CEcurtWI.d.ts} +74 -9
- package/dist/core/index.cjs +14041 -6973
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +422 -138
- package/dist/core/index.js +425 -183
- package/dist/core/index.js.map +1 -1
- package/dist/flow-vnext/index.cjs +2842 -0
- package/dist/flow-vnext/index.cjs.map +1 -0
- package/dist/flow-vnext/index.d.ts +3 -0
- package/dist/flow-vnext/index.js +119 -0
- package/dist/flow-vnext/index.js.map +1 -0
- package/dist/{graphql-client-Bn0tN049.d.ts → graphql-client-B2tREsx6.d.ts} +3 -1
- package/dist/index-b9xxXGFs.d.ts +1230 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +202 -37
- package/dist/index.js.map +1 -1
- package/dist/legacy-BpgIqaQ5.d.ts +14 -0
- package/dist/mantine/index.d.ts +7 -4
- package/dist/mantine/index.js +84 -30
- package/dist/mantine/index.js.map +1 -1
- package/dist/{runtime-BFwvCQDF.d.ts → types-Dqk5MTfW.d.ts} +6096 -5386
- package/package.json +12 -2
- package/dist/chunk-5JF23UP3.js +0 -59687
- package/dist/chunk-5JF23UP3.js.map +0 -1
- package/dist/chunk-A4CWC4AG.js +0 -374
- package/dist/chunk-A4CWC4AG.js.map +0 -1
- package/dist/chunk-NEOPTPDF.js.map +0 -1
|
@@ -0,0 +1,4456 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FLOW_CONNECTION_BINDINGS_MAP_KEY,
|
|
3
|
+
MAX_RUN_ACTION_OUTPUT_BYTES,
|
|
4
|
+
appendRunTimelineEvent,
|
|
5
|
+
appendRunTimelineEvents,
|
|
6
|
+
boundRunActionOutput,
|
|
7
|
+
boundRunEventJson,
|
|
8
|
+
createRunEventIdempotencyKey
|
|
9
|
+
} from "./chunk-F4PTFXZH.js";
|
|
10
|
+
import {
|
|
11
|
+
DESIGN_POD_METHODS,
|
|
12
|
+
INVOCATIONS_MAP_KEY,
|
|
13
|
+
canonicalDesignPodJson,
|
|
14
|
+
digestDesignPodValue,
|
|
15
|
+
readDesignPodDefinition,
|
|
16
|
+
readDesignPodState
|
|
17
|
+
} from "./chunk-JIDTNKKL.js";
|
|
18
|
+
import {
|
|
19
|
+
getNestedValue
|
|
20
|
+
} from "./chunk-JVROGQ7Q.js";
|
|
21
|
+
import {
|
|
22
|
+
DM_NOTIFICATIONS_MAP_KEY,
|
|
23
|
+
RUNS_MAP_KEY,
|
|
24
|
+
RUNS_TERMINAL_MAP_KEY,
|
|
25
|
+
RUNS_TERMINAL_PENDING_MAP_KEY,
|
|
26
|
+
assertRunActionExecutionAllowed,
|
|
27
|
+
collectPhantomLegacyRun,
|
|
28
|
+
createRunRuntimeReaderWithDoc,
|
|
29
|
+
createRunScopedRuntimeManager,
|
|
30
|
+
ensureRun,
|
|
31
|
+
ensureRunScopedMap,
|
|
32
|
+
getAction,
|
|
33
|
+
getExistingRunsMap,
|
|
34
|
+
getExistingRunsTerminalMap,
|
|
35
|
+
getExistingRunsTerminalPendingMap,
|
|
36
|
+
getRunMeta,
|
|
37
|
+
getRunScopedMap,
|
|
38
|
+
getRunsTerminalMap,
|
|
39
|
+
getRunsTerminalPendingMap,
|
|
40
|
+
isBelowTheLineBlock,
|
|
41
|
+
isRecordInRun,
|
|
42
|
+
isRepeatableAction,
|
|
43
|
+
readActionState,
|
|
44
|
+
resetActionState,
|
|
45
|
+
resolveActiveRunId,
|
|
46
|
+
resolveRecordRunId,
|
|
47
|
+
resolveRunIdForExecution,
|
|
48
|
+
seedRunFromLegacyRuntime,
|
|
49
|
+
setMultiRunStorage,
|
|
50
|
+
usesLegacyRuntimeCompatibility,
|
|
51
|
+
writeActionState
|
|
52
|
+
} from "./chunk-IIIZZ4E4.js";
|
|
53
|
+
import {
|
|
54
|
+
warnOnce
|
|
55
|
+
} from "./chunk-HT6L7A5M.js";
|
|
56
|
+
import {
|
|
57
|
+
assertLegacyFlowOperationAllowed
|
|
58
|
+
} from "./chunk-RRYBAE6E.js";
|
|
59
|
+
import {
|
|
60
|
+
getExistingYMap,
|
|
61
|
+
isYDocLike
|
|
62
|
+
} from "./chunk-NCSGIRRQ.js";
|
|
63
|
+
|
|
64
|
+
// src/core/types/baseUcan.ts
|
|
65
|
+
function isRuntimeRef(value) {
|
|
66
|
+
return typeof value === "object" && value !== null && "$ref" in value && typeof value.$ref === "string";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/core/lib/flowEngine/triggers.ts
|
|
70
|
+
import * as Y from "yjs";
|
|
71
|
+
var RUN_RECORD_AUDIT_TYPE = "block.run";
|
|
72
|
+
function computePendingInvocationId(args) {
|
|
73
|
+
const { sourceBlockId, sourceRunId, listenerBlockId, eventName, eventIndex, sessionRunId } = args;
|
|
74
|
+
const input = `${sessionRunId ? `${sessionRunId}:` : ""}${sourceBlockId}:${sourceRunId}:${listenerBlockId}:${eventName}:${eventIndex}`;
|
|
75
|
+
return `pi-${fnv1a32(input)}`;
|
|
76
|
+
}
|
|
77
|
+
function snapshotInputRefs(inputs, getNodeOutput2) {
|
|
78
|
+
const snapshots = {};
|
|
79
|
+
walkRefs(inputs, (ref) => {
|
|
80
|
+
if (ref.$ref.startsWith("trigger.")) return;
|
|
81
|
+
const parsed = parseOutputRef(ref.$ref);
|
|
82
|
+
if (!parsed) return;
|
|
83
|
+
const output = getNodeOutput2(parsed.nodeId);
|
|
84
|
+
if (!output) return;
|
|
85
|
+
snapshots[ref.$ref] = getNestedValue(output, parsed.fieldPath);
|
|
86
|
+
});
|
|
87
|
+
return snapshots;
|
|
88
|
+
}
|
|
89
|
+
function walkRefs(value, visit) {
|
|
90
|
+
if (isRuntimeRef(value)) {
|
|
91
|
+
visit(value);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
for (const item of value) walkRefs(item, visit);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === "object" && value !== null) {
|
|
99
|
+
for (const v of Object.values(value)) walkRefs(v, visit);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function parseOutputRef(ref) {
|
|
103
|
+
const outputIndex = ref.indexOf(".output.");
|
|
104
|
+
if (outputIndex === -1) return null;
|
|
105
|
+
return {
|
|
106
|
+
nodeId: ref.slice(0, outputIndex),
|
|
107
|
+
fieldPath: ref.slice(outputIndex + ".output.".length)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function fnv1a32(input) {
|
|
111
|
+
let hash = 2166136261;
|
|
112
|
+
for (let i = 0; i < input.length; i++) {
|
|
113
|
+
hash ^= input.charCodeAt(i);
|
|
114
|
+
hash = Math.imul(hash, 16777619);
|
|
115
|
+
}
|
|
116
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
117
|
+
}
|
|
118
|
+
var PENDING_INVOCATIONS_MAP_KEY = "pendingInvocations";
|
|
119
|
+
function getPendingInvocationsMap(yDoc, sessionRunId) {
|
|
120
|
+
if (usesLegacyRuntimeCompatibility(yDoc)) {
|
|
121
|
+
return yDoc.getMap(PENDING_INVOCATIONS_MAP_KEY);
|
|
122
|
+
}
|
|
123
|
+
return ensureRunScopedMap(yDoc, resolveRunIdForExecution(yDoc, sessionRunId), PENDING_INVOCATIONS_MAP_KEY);
|
|
124
|
+
}
|
|
125
|
+
function readPendingInvocationsMap(yDoc, sessionRunId) {
|
|
126
|
+
if (usesLegacyRuntimeCompatibility(yDoc)) {
|
|
127
|
+
return getExistingYMap(yDoc, PENDING_INVOCATIONS_MAP_KEY);
|
|
128
|
+
}
|
|
129
|
+
const runId = sessionRunId || resolveActiveRunId(yDoc);
|
|
130
|
+
if (!runId) return void 0;
|
|
131
|
+
return getRunScopedMap(yDoc, runId, PENDING_INVOCATIONS_MAP_KEY);
|
|
132
|
+
}
|
|
133
|
+
function countPendingInvocations(yDoc, sessionRunId) {
|
|
134
|
+
const outer = readPendingInvocationsMap(yDoc, sessionRunId);
|
|
135
|
+
if (!outer) return 0;
|
|
136
|
+
let count = 0;
|
|
137
|
+
outer.forEach((inner) => {
|
|
138
|
+
if (inner && typeof inner.forEach === "function") {
|
|
139
|
+
inner.forEach(() => {
|
|
140
|
+
count += 1;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return count;
|
|
145
|
+
}
|
|
146
|
+
function getOrCreateBlockPendingMap(yDoc, blockId, sessionRunId) {
|
|
147
|
+
const outer = getPendingInvocationsMap(yDoc, sessionRunId);
|
|
148
|
+
let inner = outer.get(blockId);
|
|
149
|
+
if (!inner) {
|
|
150
|
+
inner = new Y.Map();
|
|
151
|
+
outer.set(blockId, inner);
|
|
152
|
+
}
|
|
153
|
+
return inner;
|
|
154
|
+
}
|
|
155
|
+
function readPendingInvocations(yDoc, blockId, sessionRunId) {
|
|
156
|
+
const outer = readPendingInvocationsMap(yDoc, sessionRunId);
|
|
157
|
+
if (!outer) return [];
|
|
158
|
+
const inner = outer.get(blockId);
|
|
159
|
+
if (!inner) return [];
|
|
160
|
+
const items = [];
|
|
161
|
+
inner.forEach((value) => {
|
|
162
|
+
if (value && typeof value === "object") {
|
|
163
|
+
items.push(value);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
items.sort((a, b) => a.emittedAt.localeCompare(b.emittedAt));
|
|
167
|
+
return items;
|
|
168
|
+
}
|
|
169
|
+
function queuePendingInvocation(yDoc, listenerBlockId, invocation, sessionRunId) {
|
|
170
|
+
let created = false;
|
|
171
|
+
yDoc.transact(() => {
|
|
172
|
+
const inner = getOrCreateBlockPendingMap(yDoc, listenerBlockId, sessionRunId || invocation.sessionRunId);
|
|
173
|
+
if (inner.has(invocation.id)) return;
|
|
174
|
+
inner.set(invocation.id, invocation);
|
|
175
|
+
created = true;
|
|
176
|
+
});
|
|
177
|
+
return created;
|
|
178
|
+
}
|
|
179
|
+
function removePendingInvocation(yDoc, listenerBlockId, pendingInvocationId, sessionRunId) {
|
|
180
|
+
const outer = readPendingInvocationsMap(yDoc, sessionRunId);
|
|
181
|
+
if (!outer) return false;
|
|
182
|
+
const inner = outer.get(listenerBlockId);
|
|
183
|
+
if (!inner) return false;
|
|
184
|
+
if (!inner.has(pendingInvocationId)) return false;
|
|
185
|
+
inner.delete(pendingInvocationId);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
function readConsumedPendingInvocationIds(yDoc, listenerBlockId, sessionRunId) {
|
|
189
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
190
|
+
for (const record of readRunRecords(yDoc, listenerBlockId, sessionRunId)) {
|
|
191
|
+
if (record.fromPendingInvocationId) consumed.add(record.fromPendingInvocationId);
|
|
192
|
+
}
|
|
193
|
+
return consumed;
|
|
194
|
+
}
|
|
195
|
+
function sweepExpiredPendingInvocations(yDoc, now = Date.now(), sessionRunId) {
|
|
196
|
+
const outer = readPendingInvocationsMap(yDoc, sessionRunId);
|
|
197
|
+
if (!outer) return 0;
|
|
198
|
+
let removed = 0;
|
|
199
|
+
outer.forEach((inner, listenerBlockId) => {
|
|
200
|
+
const innerMap = inner;
|
|
201
|
+
if (!innerMap || typeof innerMap.forEach !== "function") return;
|
|
202
|
+
const expiredIds = [];
|
|
203
|
+
innerMap.forEach((value, id) => {
|
|
204
|
+
const expiresAt = value?.expiresAt;
|
|
205
|
+
if (expiresAt && Date.parse(expiresAt) < now) expiredIds.push(id);
|
|
206
|
+
});
|
|
207
|
+
for (const id of expiredIds) {
|
|
208
|
+
innerMap.delete(id);
|
|
209
|
+
removed += 1;
|
|
210
|
+
}
|
|
211
|
+
void listenerBlockId;
|
|
212
|
+
});
|
|
213
|
+
return removed;
|
|
214
|
+
}
|
|
215
|
+
var BARRIER_STATE_MAP_KEY = "barrierState";
|
|
216
|
+
function getBarrierStateMap(yDoc, sessionRunId) {
|
|
217
|
+
if (usesLegacyRuntimeCompatibility(yDoc)) {
|
|
218
|
+
return yDoc.getMap(BARRIER_STATE_MAP_KEY);
|
|
219
|
+
}
|
|
220
|
+
return ensureRunScopedMap(yDoc, resolveRunIdForExecution(yDoc, sessionRunId), BARRIER_STATE_MAP_KEY);
|
|
221
|
+
}
|
|
222
|
+
function readBarrierStateMap(yDoc, sessionRunId) {
|
|
223
|
+
if (usesLegacyRuntimeCompatibility(yDoc)) {
|
|
224
|
+
return getExistingYMap(yDoc, BARRIER_STATE_MAP_KEY);
|
|
225
|
+
}
|
|
226
|
+
const runId = sessionRunId || resolveActiveRunId(yDoc);
|
|
227
|
+
if (!runId) return void 0;
|
|
228
|
+
return getRunScopedMap(yDoc, runId, BARRIER_STATE_MAP_KEY);
|
|
229
|
+
}
|
|
230
|
+
function getOrCreateListenerBarrierMap(yDoc, listenerBlockId, sessionRunId) {
|
|
231
|
+
const outer = getBarrierStateMap(yDoc, sessionRunId);
|
|
232
|
+
let inner = outer.get(listenerBlockId);
|
|
233
|
+
if (!inner) {
|
|
234
|
+
inner = new Y.Map();
|
|
235
|
+
outer.set(listenerBlockId, inner);
|
|
236
|
+
}
|
|
237
|
+
return inner;
|
|
238
|
+
}
|
|
239
|
+
function recordBarrierEvent(yDoc, listenerBlockId, entry, sessionRunId) {
|
|
240
|
+
let written = false;
|
|
241
|
+
yDoc.transact(() => {
|
|
242
|
+
const inner = getOrCreateListenerBarrierMap(yDoc, listenerBlockId, sessionRunId || entry.sessionRunId);
|
|
243
|
+
const key = `${entry.sourceBlockId}::${entry.eventName}`;
|
|
244
|
+
const existing = inner.get(key);
|
|
245
|
+
if (existing && typeof existing === "object" && existing.runId === entry.runId) return;
|
|
246
|
+
inner.set(key, entry);
|
|
247
|
+
written = true;
|
|
248
|
+
});
|
|
249
|
+
return written;
|
|
250
|
+
}
|
|
251
|
+
function readBarrierState(yDoc, listenerBlockId, sessionRunId) {
|
|
252
|
+
const outer = readBarrierStateMap(yDoc, sessionRunId);
|
|
253
|
+
if (!outer) return [];
|
|
254
|
+
const inner = outer.get(listenerBlockId);
|
|
255
|
+
if (!inner) return [];
|
|
256
|
+
const entries = [];
|
|
257
|
+
inner.forEach((value) => {
|
|
258
|
+
if (value && typeof value === "object") {
|
|
259
|
+
entries.push(value);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return entries;
|
|
263
|
+
}
|
|
264
|
+
function clearBarrierState(yDoc, listenerBlockId, sessionRunId) {
|
|
265
|
+
const outer = readBarrierStateMap(yDoc, sessionRunId);
|
|
266
|
+
if (!outer) return;
|
|
267
|
+
yDoc.transact(() => {
|
|
268
|
+
outer.delete(listenerBlockId);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function computeBarrierInvocationId(entries, listenerBlockId, sessionRunId) {
|
|
272
|
+
const sorted = [...entries].sort((a, b) => a.sourceBlockId.localeCompare(b.sourceBlockId));
|
|
273
|
+
const input = `${sessionRunId ? `${sessionRunId}|` : ""}${sorted.map((e) => `${e.sourceBlockId}:${e.runId}:${e.eventName}`).join("|")}|>${listenerBlockId}`;
|
|
274
|
+
return `bi-${fnv1a32(input)}`;
|
|
275
|
+
}
|
|
276
|
+
function mergeBarrierPayloads(entries) {
|
|
277
|
+
const merged = {};
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
if (entry.alias) {
|
|
280
|
+
merged[entry.alias] = entry.payload;
|
|
281
|
+
} else {
|
|
282
|
+
Object.assign(merged, entry.payload);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return merged;
|
|
286
|
+
}
|
|
287
|
+
function appendRunRecord(yDoc, blockId, details, userId) {
|
|
288
|
+
const auditMap = yDoc.getMap("auditTrail");
|
|
289
|
+
yDoc.transact(() => {
|
|
290
|
+
let arr = auditMap.get(blockId);
|
|
291
|
+
if (!arr) {
|
|
292
|
+
arr = new Y.Array();
|
|
293
|
+
auditMap.set(blockId, arr);
|
|
294
|
+
}
|
|
295
|
+
const event = {
|
|
296
|
+
id: `${details.runId}`,
|
|
297
|
+
blockId,
|
|
298
|
+
type: RUN_RECORD_AUDIT_TYPE,
|
|
299
|
+
details,
|
|
300
|
+
message: void 0,
|
|
301
|
+
meta: {
|
|
302
|
+
timestamp: details.completedAt,
|
|
303
|
+
userId,
|
|
304
|
+
editable: false
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
arr.push([event]);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function readRunRecords(yDoc, blockId, sessionRunId) {
|
|
311
|
+
const auditMap = getExistingYMap(yDoc, "auditTrail");
|
|
312
|
+
if (!auditMap) return [];
|
|
313
|
+
const arr = auditMap.get(blockId);
|
|
314
|
+
if (!arr) return [];
|
|
315
|
+
const records = [];
|
|
316
|
+
arr.forEach((entry) => {
|
|
317
|
+
if (!entry || typeof entry !== "object") return;
|
|
318
|
+
const e = entry;
|
|
319
|
+
if (e.type === RUN_RECORD_AUDIT_TYPE && e.details) {
|
|
320
|
+
if (sessionRunId && !isRecordInRun(e.details.sessionRunId, sessionRunId)) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
records.push(e.details);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
return records;
|
|
327
|
+
}
|
|
328
|
+
function findFailedListenersForSourceRun(yDoc, sourceBlockId, sourceRunId, listenerBlockIds) {
|
|
329
|
+
const failures = [];
|
|
330
|
+
for (const listenerBlockId of listenerBlockIds) {
|
|
331
|
+
const records = readRunRecords(yDoc, listenerBlockId);
|
|
332
|
+
for (const record of records) {
|
|
333
|
+
if (!record.error) continue;
|
|
334
|
+
if (record.triggeredBy?.sourceBlockId !== sourceBlockId) continue;
|
|
335
|
+
if (record.fromPendingInvocationId == null) continue;
|
|
336
|
+
const sourceRunIdField = record.sourceRunId;
|
|
337
|
+
if (sourceRunIdField && sourceRunIdField !== sourceRunId) continue;
|
|
338
|
+
failures.push({ listenerBlockId, record });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return failures;
|
|
342
|
+
}
|
|
343
|
+
function replayFailedListenerRun(yDoc, failedRecord, listenerBlockId, originalPayload, originalRefSnapshots, assigneeDid, sessionRunId) {
|
|
344
|
+
if (!failedRecord.triggeredBy) return false;
|
|
345
|
+
const replayId = `${failedRecord.runId}:replay-${Date.now().toString(36)}`;
|
|
346
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
347
|
+
const replay = {
|
|
348
|
+
id: replayId,
|
|
349
|
+
triggeringBlockId: failedRecord.triggeredBy.sourceBlockId,
|
|
350
|
+
sourceRunId: failedRecord.sourceRunId || failedRecord.runId,
|
|
351
|
+
sessionRunId: sessionRunId || failedRecord.sessionRunId,
|
|
352
|
+
eventName: failedRecord.triggeredBy.eventName,
|
|
353
|
+
eventIndex: 0,
|
|
354
|
+
payload: originalPayload,
|
|
355
|
+
refSnapshots: originalRefSnapshots,
|
|
356
|
+
assigneeDid,
|
|
357
|
+
emittedAt: now,
|
|
358
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
|
|
359
|
+
};
|
|
360
|
+
return queuePendingInvocation(yDoc, listenerBlockId, replay, sessionRunId || failedRecord.sessionRunId);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/core/lib/flowEngine/versionManifest.ts
|
|
364
|
+
var VERSION_MANIFEST = {
|
|
365
|
+
"0.3": {
|
|
366
|
+
version: "0.3",
|
|
367
|
+
label: "Legacy",
|
|
368
|
+
ucanRequired: false,
|
|
369
|
+
delegationRootRequired: false,
|
|
370
|
+
whitelistOnlyAllowed: true,
|
|
371
|
+
unrestrictedAllowed: true,
|
|
372
|
+
executionPath: "legacy",
|
|
373
|
+
authorizationFn: "v1",
|
|
374
|
+
allowedAuthModes: ["anyone", "actors", "capability"],
|
|
375
|
+
ui: {
|
|
376
|
+
showDelegationPanel: false,
|
|
377
|
+
showWhitelistConfig: true,
|
|
378
|
+
showAnyoneConfig: true,
|
|
379
|
+
showMigrationBanner: true,
|
|
380
|
+
requirePinForExecution: false
|
|
381
|
+
},
|
|
382
|
+
description: "Legacy version. UCAN optional, whitelist-only authorization accepted."
|
|
383
|
+
},
|
|
384
|
+
"1.0.0": {
|
|
385
|
+
version: "1.0.0",
|
|
386
|
+
label: "UCAN Required",
|
|
387
|
+
ucanRequired: true,
|
|
388
|
+
delegationRootRequired: true,
|
|
389
|
+
whitelistOnlyAllowed: false,
|
|
390
|
+
unrestrictedAllowed: false,
|
|
391
|
+
executionPath: "invocation",
|
|
392
|
+
authorizationFn: "v2",
|
|
393
|
+
allowedAuthModes: ["capability"],
|
|
394
|
+
ui: {
|
|
395
|
+
showDelegationPanel: true,
|
|
396
|
+
showWhitelistConfig: false,
|
|
397
|
+
showAnyoneConfig: false,
|
|
398
|
+
showMigrationBanner: false,
|
|
399
|
+
requirePinForExecution: true
|
|
400
|
+
},
|
|
401
|
+
description: "UCAN-enforced. Every block execution requires a valid delegation chain."
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
var LATEST_VERSION = "1.0.0";
|
|
405
|
+
var tempUcanEnforcementDisabled = false;
|
|
406
|
+
function isTempUcanEnforcementDisabled() {
|
|
407
|
+
return tempUcanEnforcementDisabled;
|
|
408
|
+
}
|
|
409
|
+
var MIGRATION_PATH = ["0.3", "1.0.0"];
|
|
410
|
+
function getVersionPolicy(version) {
|
|
411
|
+
const policy = VERSION_MANIFEST[version] ?? VERSION_MANIFEST[LATEST_VERSION];
|
|
412
|
+
if (tempUcanEnforcementDisabled && policy.ucanRequired) {
|
|
413
|
+
return {
|
|
414
|
+
...policy,
|
|
415
|
+
ucanRequired: false,
|
|
416
|
+
delegationRootRequired: false,
|
|
417
|
+
ui: { ...policy.ui, requirePinForExecution: false }
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return policy;
|
|
421
|
+
}
|
|
422
|
+
function isVersionAtLeast(current, minimum) {
|
|
423
|
+
const currentIdx = MIGRATION_PATH.indexOf(current);
|
|
424
|
+
const minimumIdx = MIGRATION_PATH.indexOf(minimum);
|
|
425
|
+
const effectiveCurrent = currentIdx === -1 ? MIGRATION_PATH.length : currentIdx;
|
|
426
|
+
return effectiveCurrent >= minimumIdx;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/core/lib/flowEngine/reconcile.ts
|
|
430
|
+
var activeReconciliations = /* @__PURE__ */ new WeakMap();
|
|
431
|
+
function flattenBlocks(blocks) {
|
|
432
|
+
const flattened = [];
|
|
433
|
+
const visit = (block) => {
|
|
434
|
+
flattened.push(block);
|
|
435
|
+
if (Array.isArray(block?.children)) block.children.forEach(visit);
|
|
436
|
+
};
|
|
437
|
+
blocks.forEach(visit);
|
|
438
|
+
return flattened;
|
|
439
|
+
}
|
|
440
|
+
function reconcilePendingInvocations(editor, runId) {
|
|
441
|
+
const yDoc = editor._yDoc;
|
|
442
|
+
if (!yDoc) return;
|
|
443
|
+
const sessionRunId = resolveRunIdForExecution(yDoc, runId);
|
|
444
|
+
let activeRuns = activeReconciliations.get(yDoc);
|
|
445
|
+
if (!activeRuns) {
|
|
446
|
+
activeRuns = /* @__PURE__ */ new Set();
|
|
447
|
+
activeReconciliations.set(yDoc, activeRuns);
|
|
448
|
+
}
|
|
449
|
+
if (activeRuns.has(sessionRunId)) return;
|
|
450
|
+
activeRuns.add(sessionRunId);
|
|
451
|
+
try {
|
|
452
|
+
_reconcilePendingInvocationsInner(editor, yDoc, sessionRunId);
|
|
453
|
+
} finally {
|
|
454
|
+
activeRuns.delete(sessionRunId);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function _reconcilePendingInvocationsInner(editor, yDoc, runId) {
|
|
458
|
+
const blocks = flattenBlocks(editor.document || []);
|
|
459
|
+
if (blocks.length === 0) return;
|
|
460
|
+
const listenersBySource = /* @__PURE__ */ new Map();
|
|
461
|
+
const barrierListenersBySource = /* @__PURE__ */ new Map();
|
|
462
|
+
for (const block of blocks) {
|
|
463
|
+
const trigger = parseTrigger(block);
|
|
464
|
+
if (!trigger) continue;
|
|
465
|
+
if (trigger.type === "block.event") {
|
|
466
|
+
if (!trigger.sourceBlockId || !trigger.eventName) continue;
|
|
467
|
+
const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
|
|
468
|
+
if (!listenersBySource.has(key)) listenersBySource.set(key, []);
|
|
469
|
+
listenersBySource.get(key).push({ block, trigger });
|
|
470
|
+
} else if (trigger.type === "block.event.all" && trigger.sources) {
|
|
471
|
+
for (const source of trigger.sources) {
|
|
472
|
+
if (!source.sourceBlockId || !source.eventName) continue;
|
|
473
|
+
const key = `${source.sourceBlockId}::${source.eventName}`;
|
|
474
|
+
if (!barrierListenersBySource.has(key)) barrierListenersBySource.set(key, []);
|
|
475
|
+
barrierListenersBySource.get(key).push({ block, trigger, source });
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (listenersBySource.size === 0 && barrierListenersBySource.size === 0) return;
|
|
480
|
+
const now = Date.now();
|
|
481
|
+
sweepExpiredPendingInvocations(yDoc, now, runId);
|
|
482
|
+
const consumedByListener = /* @__PURE__ */ new Map();
|
|
483
|
+
const isConsumed = (listenerBlockId, pendingId) => {
|
|
484
|
+
let consumed = consumedByListener.get(listenerBlockId);
|
|
485
|
+
if (!consumed) {
|
|
486
|
+
consumed = readConsumedPendingInvocationIds(yDoc, listenerBlockId, runId);
|
|
487
|
+
consumedByListener.set(listenerBlockId, consumed);
|
|
488
|
+
}
|
|
489
|
+
return consumed.has(pendingId);
|
|
490
|
+
};
|
|
491
|
+
const schemaVersionRaw = yDoc.getMap("root").get("schema_version");
|
|
492
|
+
const ucanRequired = typeof schemaVersionRaw === "string" ? !!getVersionPolicy(schemaVersionRaw)?.ucanRequired : false;
|
|
493
|
+
const invocationsMap = yDoc.getMap("invocations");
|
|
494
|
+
const getNodeOutput2 = (nodeId) => {
|
|
495
|
+
const out = readActionState(yDoc, runId, nodeId).output;
|
|
496
|
+
return out && typeof out === "object" && !Array.isArray(out) ? out : void 0;
|
|
497
|
+
};
|
|
498
|
+
for (const block of blocks) {
|
|
499
|
+
const sourceBlockId = block?.id;
|
|
500
|
+
if (!sourceBlockId) continue;
|
|
501
|
+
let hasAnyListener = false;
|
|
502
|
+
for (const key of listenersBySource.keys()) {
|
|
503
|
+
if (key.startsWith(`${sourceBlockId}::`)) {
|
|
504
|
+
hasAnyListener = true;
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
for (const key of barrierListenersBySource.keys()) {
|
|
509
|
+
if (key.startsWith(`${sourceBlockId}::`)) {
|
|
510
|
+
hasAnyListener = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (!hasAnyListener) continue;
|
|
515
|
+
const records = readRunRecords(yDoc, sourceBlockId, runId);
|
|
516
|
+
for (const record of records) {
|
|
517
|
+
if (!isTrustedRunRecord(record, ucanRequired, invocationsMap)) continue;
|
|
518
|
+
processRunRecord(yDoc, runId, sourceBlockId, record, listenersBySource, getNodeOutput2, isConsumed, now);
|
|
519
|
+
processBarrierRunRecord(yDoc, runId, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, isConsumed, now);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function dispatchFlowStartTriggers(params) {
|
|
524
|
+
const blocks = flattenBlocks(params.blocks);
|
|
525
|
+
const sourceRunId = params.sourceEventId || `${params.runId}:run.started`;
|
|
526
|
+
const emittedAt = new Date(params.startedAt).toISOString();
|
|
527
|
+
const getNodeOutput2 = (nodeId) => {
|
|
528
|
+
const output = readActionState(params.yDoc, params.runId, nodeId).output;
|
|
529
|
+
return output && typeof output === "object" && !Array.isArray(output) ? output : void 0;
|
|
530
|
+
};
|
|
531
|
+
let queued = 0;
|
|
532
|
+
for (const block of blocks) {
|
|
533
|
+
const trigger = parseTrigger(block);
|
|
534
|
+
if (trigger?.type !== "flow.start" || !block?.id) continue;
|
|
535
|
+
const id = computePendingInvocationId({
|
|
536
|
+
sourceBlockId: "__flow_start__",
|
|
537
|
+
sourceRunId,
|
|
538
|
+
listenerBlockId: block.id,
|
|
539
|
+
eventName: "flow.start",
|
|
540
|
+
eventIndex: 0,
|
|
541
|
+
sessionRunId: params.runId
|
|
542
|
+
});
|
|
543
|
+
const inputs = parseInputs(block);
|
|
544
|
+
const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
|
|
545
|
+
const invocation = {
|
|
546
|
+
id,
|
|
547
|
+
triggeringBlockId: "__flow_start__",
|
|
548
|
+
sourceRunId,
|
|
549
|
+
sessionRunId: params.runId,
|
|
550
|
+
eventName: "flow.start",
|
|
551
|
+
eventIndex: 0,
|
|
552
|
+
payload: {
|
|
553
|
+
runId: params.runId,
|
|
554
|
+
startedAt: params.startedAt,
|
|
555
|
+
...params.sourceEventId ? { eventId: params.sourceEventId } : {}
|
|
556
|
+
},
|
|
557
|
+
refSnapshots,
|
|
558
|
+
assigneeDid: resolveAssignee(block) || "unassigned",
|
|
559
|
+
emittedAt,
|
|
560
|
+
expiresAt: computeExpiry(block, emittedAt)
|
|
561
|
+
};
|
|
562
|
+
if (queuePendingInvocation(params.yDoc, block.id, invocation, params.runId)) {
|
|
563
|
+
queued += 1;
|
|
564
|
+
}
|
|
565
|
+
writeActionState(params.yDoc, params.runId, block.id, {
|
|
566
|
+
pendingPayload: { ...invocation.payload, ...refSnapshots }
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return queued;
|
|
570
|
+
}
|
|
571
|
+
function isTrustedRunRecord(record, ucanRequired, invocationsMap) {
|
|
572
|
+
if (!ucanRequired) return true;
|
|
573
|
+
if (!record.invocationCid) return false;
|
|
574
|
+
return invocationsMap.get(record.invocationCid) !== void 0;
|
|
575
|
+
}
|
|
576
|
+
function processRunRecord(yDoc, runId, sourceBlockId, record, listenersBySource, getNodeOutput2, isConsumed, now) {
|
|
577
|
+
if (!Array.isArray(record.events)) return;
|
|
578
|
+
record.events.forEach((event, eventIndex) => {
|
|
579
|
+
if (!event?.name) return;
|
|
580
|
+
const key = `${sourceBlockId}::${event.name}`;
|
|
581
|
+
const listeners = listenersBySource.get(key);
|
|
582
|
+
if (!listeners || listeners.length === 0) return;
|
|
583
|
+
for (const { block: listenerBlock } of listeners) {
|
|
584
|
+
const listenerBlockId = listenerBlock.id;
|
|
585
|
+
if (!listenerBlockId) continue;
|
|
586
|
+
const id = computePendingInvocationId({
|
|
587
|
+
sourceBlockId,
|
|
588
|
+
sourceRunId: record.runId,
|
|
589
|
+
listenerBlockId,
|
|
590
|
+
eventName: event.name,
|
|
591
|
+
eventIndex,
|
|
592
|
+
sessionRunId: runId
|
|
593
|
+
});
|
|
594
|
+
if (isConsumed(listenerBlockId, id)) continue;
|
|
595
|
+
const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
|
|
596
|
+
const inputs = parseInputs(listenerBlock);
|
|
597
|
+
const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
|
|
598
|
+
const expiresAt = computeExpiry(listenerBlock, record.completedAt);
|
|
599
|
+
if (expiresAt && Date.parse(expiresAt) < now) continue;
|
|
600
|
+
const invocation = {
|
|
601
|
+
id,
|
|
602
|
+
triggeringBlockId: sourceBlockId,
|
|
603
|
+
sourceRunId: record.runId,
|
|
604
|
+
sessionRunId: runId,
|
|
605
|
+
eventName: event.name,
|
|
606
|
+
eventIndex,
|
|
607
|
+
payload: event.payload,
|
|
608
|
+
refSnapshots,
|
|
609
|
+
assigneeDid,
|
|
610
|
+
emittedAt: record.completedAt,
|
|
611
|
+
expiresAt
|
|
612
|
+
};
|
|
613
|
+
queuePendingInvocation(yDoc, listenerBlockId, invocation, runId);
|
|
614
|
+
writeActionState(yDoc, runId, listenerBlockId, {
|
|
615
|
+
pendingPayload: { ...event.payload, ...refSnapshots }
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
function processBarrierRunRecord(yDoc, runId, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, isConsumed, now) {
|
|
621
|
+
if (!Array.isArray(record.events)) return;
|
|
622
|
+
for (const event of record.events) {
|
|
623
|
+
if (!event?.name) continue;
|
|
624
|
+
const key = `${sourceBlockId}::${event.name}`;
|
|
625
|
+
const listeners = barrierListenersBySource.get(key);
|
|
626
|
+
if (!listeners || listeners.length === 0) continue;
|
|
627
|
+
for (const { block: listenerBlock, trigger, source } of listeners) {
|
|
628
|
+
const listenerBlockId = listenerBlock.id;
|
|
629
|
+
if (!listenerBlockId) continue;
|
|
630
|
+
const entry = {
|
|
631
|
+
sourceBlockId,
|
|
632
|
+
eventName: event.name,
|
|
633
|
+
alias: source.alias,
|
|
634
|
+
runId: record.runId,
|
|
635
|
+
sessionRunId: runId,
|
|
636
|
+
payload: event.payload || {},
|
|
637
|
+
emittedAt: record.completedAt
|
|
638
|
+
};
|
|
639
|
+
recordBarrierEvent(yDoc, listenerBlockId, entry, runId);
|
|
640
|
+
const existing = readActionState(yDoc, runId, listenerBlockId).pendingPayload || {};
|
|
641
|
+
writeActionState(yDoc, runId, listenerBlockId, {
|
|
642
|
+
pendingPayload: { ...existing, ...event.payload || {} }
|
|
643
|
+
});
|
|
644
|
+
const allSources = trigger.sources || [];
|
|
645
|
+
const currentState = readBarrierState(yDoc, listenerBlockId, runId);
|
|
646
|
+
const requiredSources = allSources.filter((s) => s.optional !== true);
|
|
647
|
+
const gatingSources = requiredSources.length > 0 ? requiredSources : allSources;
|
|
648
|
+
const allFired = gatingSources.length > 0 && gatingSources.every((s) => currentState.some((e) => e.sourceBlockId === s.sourceBlockId && e.eventName === s.eventName));
|
|
649
|
+
if (!allFired) continue;
|
|
650
|
+
const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
|
|
651
|
+
const id = computeBarrierInvocationId(currentState, listenerBlockId, runId);
|
|
652
|
+
if (isConsumed(listenerBlockId, id)) continue;
|
|
653
|
+
const mergedPayload = mergeBarrierPayloads(currentState);
|
|
654
|
+
const inputs = parseInputs(listenerBlock);
|
|
655
|
+
const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
|
|
656
|
+
const expiresAt = computeExpiry(listenerBlock, record.completedAt);
|
|
657
|
+
if (expiresAt && Date.parse(expiresAt) < now) continue;
|
|
658
|
+
const invocation = {
|
|
659
|
+
id,
|
|
660
|
+
triggeringBlockId: sourceBlockId,
|
|
661
|
+
sourceRunId: record.runId,
|
|
662
|
+
sessionRunId: runId,
|
|
663
|
+
eventName: `barrier:${allSources.map((s) => s.alias).join("+")}`,
|
|
664
|
+
eventIndex: 0,
|
|
665
|
+
payload: mergedPayload,
|
|
666
|
+
refSnapshots,
|
|
667
|
+
assigneeDid,
|
|
668
|
+
emittedAt: record.completedAt,
|
|
669
|
+
expiresAt
|
|
670
|
+
};
|
|
671
|
+
queuePendingInvocation(yDoc, listenerBlockId, invocation, runId);
|
|
672
|
+
clearBarrierState(yDoc, listenerBlockId, runId);
|
|
673
|
+
writeActionState(yDoc, runId, listenerBlockId, {
|
|
674
|
+
pendingPayload: { ...mergedPayload, ...refSnapshots }
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function parseTrigger(block) {
|
|
680
|
+
const raw = block?.props?.trigger;
|
|
681
|
+
if (!raw || typeof raw !== "string") return null;
|
|
682
|
+
try {
|
|
683
|
+
const parsed = JSON.parse(raw);
|
|
684
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
685
|
+
return parsed;
|
|
686
|
+
}
|
|
687
|
+
} catch {
|
|
688
|
+
warnOnce(`trigger-parse:${block?.id}`, `[flow-config] block ${block?.id}: props.trigger is not valid JSON; trigger ignored`);
|
|
689
|
+
}
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
function parseInputs(block) {
|
|
693
|
+
const raw = block?.props?.inputs;
|
|
694
|
+
if (!raw || typeof raw !== "string") return {};
|
|
695
|
+
try {
|
|
696
|
+
const parsed = JSON.parse(raw);
|
|
697
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
698
|
+
} catch {
|
|
699
|
+
warnOnce(`inputs-parse:${block?.id}`, `[flow-config] block ${block?.id}: props.inputs is not valid JSON; treated as empty`);
|
|
700
|
+
}
|
|
701
|
+
return {};
|
|
702
|
+
}
|
|
703
|
+
function resolveAssignee(block) {
|
|
704
|
+
const raw = block?.props?.assignment;
|
|
705
|
+
if (!raw) return void 0;
|
|
706
|
+
try {
|
|
707
|
+
const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
708
|
+
const did = parsed?.assignedActor?.did;
|
|
709
|
+
return typeof did === "string" && did.length > 0 ? did : void 0;
|
|
710
|
+
} catch {
|
|
711
|
+
warnOnce(`assignment-parse:${block?.id}`, `[flow-config] block ${block?.id}: props.assignment is not valid JSON; assignee unresolved`);
|
|
712
|
+
return void 0;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function computeExpiry(block, emittedAt) {
|
|
716
|
+
const ttlAbsolute = block?.props?.ttlAbsoluteDueDate;
|
|
717
|
+
if (typeof ttlAbsolute === "string" && ttlAbsolute) {
|
|
718
|
+
return ttlAbsolute;
|
|
719
|
+
}
|
|
720
|
+
const ttlFromEnablement = block?.props?.ttlFromEnablement;
|
|
721
|
+
if (typeof ttlFromEnablement === "string" && ttlFromEnablement) {
|
|
722
|
+
const ms = parseIsoDurationToMs(ttlFromEnablement);
|
|
723
|
+
if (ms != null) {
|
|
724
|
+
return new Date(new Date(emittedAt).getTime() + ms).toISOString();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return new Date(new Date(emittedAt).getTime() + 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
728
|
+
}
|
|
729
|
+
function parseIsoDurationToMs(duration) {
|
|
730
|
+
const match = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/.exec(duration);
|
|
731
|
+
if (!match) return null;
|
|
732
|
+
const [, d, h, m, s] = match;
|
|
733
|
+
let ms = 0;
|
|
734
|
+
if (d) ms += parseInt(d, 10) * 24 * 60 * 60 * 1e3;
|
|
735
|
+
if (h) ms += parseInt(h, 10) * 60 * 60 * 1e3;
|
|
736
|
+
if (m) ms += parseInt(m, 10) * 60 * 1e3;
|
|
737
|
+
if (s) ms += parseInt(s, 10) * 1e3;
|
|
738
|
+
return ms;
|
|
739
|
+
}
|
|
740
|
+
function getActionForBlock(block) {
|
|
741
|
+
const actionType = block?.props?.actionType;
|
|
742
|
+
if (typeof actionType !== "string") return void 0;
|
|
743
|
+
return getAction(actionType);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/core/lib/flowEngine/emitEvents.ts
|
|
747
|
+
function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid, detailsPatch = {}, runId) {
|
|
748
|
+
const yDoc = editor._yDoc;
|
|
749
|
+
if (!yDoc) return;
|
|
750
|
+
if (events.length === 0) return;
|
|
751
|
+
const executionId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
752
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
753
|
+
const details = {
|
|
754
|
+
runId: executionId,
|
|
755
|
+
sessionRunId: runId || resolveActiveRunId(yDoc),
|
|
756
|
+
output,
|
|
757
|
+
events,
|
|
758
|
+
startedAt: now,
|
|
759
|
+
completedAt: now,
|
|
760
|
+
actorDid,
|
|
761
|
+
...detailsPatch
|
|
762
|
+
};
|
|
763
|
+
appendRunRecord(yDoc, blockId, details, actorDid);
|
|
764
|
+
reconcilePendingInvocations(editor, details.sessionRunId);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// src/core/lib/flowEngine/runtime.ts
|
|
768
|
+
var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
|
|
769
|
+
var XERO_WORK_TERMINAL_MAP_NAME = "xeroWorkItemsTerminal";
|
|
770
|
+
var XERO_CONNECTION_MAP_NAME = "xeroConnection";
|
|
771
|
+
var ensureStateObject = (value) => {
|
|
772
|
+
if (!value || typeof value !== "object") {
|
|
773
|
+
return {};
|
|
774
|
+
}
|
|
775
|
+
return { ...value };
|
|
776
|
+
};
|
|
777
|
+
var createYMapManager = (map) => {
|
|
778
|
+
return {
|
|
779
|
+
get: (nodeId) => {
|
|
780
|
+
const stored = map.get(nodeId);
|
|
781
|
+
return ensureStateObject(stored);
|
|
782
|
+
},
|
|
783
|
+
update: (nodeId, updates) => {
|
|
784
|
+
const current = ensureStateObject(map.get(nodeId));
|
|
785
|
+
map.set(nodeId, { ...current, ...updates });
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
};
|
|
789
|
+
var createMemoryManager = () => {
|
|
790
|
+
const memory = /* @__PURE__ */ new Map();
|
|
791
|
+
return {
|
|
792
|
+
get: (nodeId) => ensureStateObject(memory.get(nodeId)),
|
|
793
|
+
update: (nodeId, updates) => {
|
|
794
|
+
const current = ensureStateObject(memory.get(nodeId));
|
|
795
|
+
memory.set(nodeId, { ...current, ...updates });
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
};
|
|
799
|
+
function resolveEditorYDoc(editor) {
|
|
800
|
+
const direct = editor?._yDoc;
|
|
801
|
+
if (direct && typeof direct.getMap === "function") return direct;
|
|
802
|
+
const viaRuntime = editor?._yRuntime?.doc;
|
|
803
|
+
return viaRuntime && typeof viaRuntime.getMap === "function" ? viaRuntime : void 0;
|
|
804
|
+
}
|
|
805
|
+
var createRuntimeStateManager = (editor, runId) => {
|
|
806
|
+
const yDoc = resolveEditorYDoc(editor);
|
|
807
|
+
if (yDoc) {
|
|
808
|
+
return createRunScopedRuntimeManager(yDoc, resolveRunIdForExecution(yDoc, runId));
|
|
809
|
+
}
|
|
810
|
+
if (editor?._yRuntime) {
|
|
811
|
+
return createYMapManager(editor._yRuntime);
|
|
812
|
+
}
|
|
813
|
+
return createMemoryManager();
|
|
814
|
+
};
|
|
815
|
+
var createYDocRuntimeManager = (yDoc, runId) => {
|
|
816
|
+
return createRunScopedRuntimeManager(yDoc, resolveRunIdForExecution(yDoc, runId));
|
|
817
|
+
};
|
|
818
|
+
function clearRuntimeForTemplateClone(yDoc) {
|
|
819
|
+
const runtime = yDoc.getMap("runtime");
|
|
820
|
+
const runs = yDoc.getMap(RUNS_MAP_KEY);
|
|
821
|
+
const runsTerminalPending = yDoc.getMap(RUNS_TERMINAL_PENDING_MAP_KEY);
|
|
822
|
+
const runsTerminal = yDoc.getMap(RUNS_TERMINAL_MAP_KEY);
|
|
823
|
+
const invocations = yDoc.getMap("invocations");
|
|
824
|
+
const pendingInvocations = yDoc.getMap("pendingInvocations");
|
|
825
|
+
const barrierState = yDoc.getMap(BARRIER_STATE_MAP_KEY);
|
|
826
|
+
const dmNotificationState = yDoc.getMap(DM_NOTIFICATIONS_MAP_KEY);
|
|
827
|
+
const agentOutbox = yDoc.getMap("agentOutbox");
|
|
828
|
+
const agentLeases = yDoc.getMap("agentLeases");
|
|
829
|
+
const auditTrail = yDoc.getMap("auditTrail");
|
|
830
|
+
const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME);
|
|
831
|
+
const xeroWorkTerminal = yDoc.getMap(XERO_WORK_TERMINAL_MAP_NAME);
|
|
832
|
+
const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
|
|
833
|
+
const connectionBindings = yDoc.getMap(FLOW_CONNECTION_BINDINGS_MAP_KEY);
|
|
834
|
+
const designPodState = yDoc.getMap("designPodState");
|
|
835
|
+
const root = yDoc.getMap("root");
|
|
836
|
+
yDoc.transact(() => {
|
|
837
|
+
runtime.forEach((_, key) => runtime.delete(key));
|
|
838
|
+
runs.forEach((_, key) => runs.delete(key));
|
|
839
|
+
runsTerminalPending.forEach((_, key) => runsTerminalPending.delete(key));
|
|
840
|
+
runsTerminal.forEach((_, key) => runsTerminal.delete(key));
|
|
841
|
+
invocations.forEach((_, key) => invocations.delete(key));
|
|
842
|
+
pendingInvocations.forEach((_, key) => pendingInvocations.delete(key));
|
|
843
|
+
barrierState.forEach((_, key) => barrierState.delete(key));
|
|
844
|
+
dmNotificationState.forEach((_, key) => dmNotificationState.delete(key));
|
|
845
|
+
agentOutbox.forEach((_, key) => agentOutbox.delete(key));
|
|
846
|
+
agentLeases.forEach((_, key) => agentLeases.delete(key));
|
|
847
|
+
auditTrail.forEach((_, key) => auditTrail.delete(key));
|
|
848
|
+
xeroWorkItems.forEach((_, key) => xeroWorkItems.delete(key));
|
|
849
|
+
xeroWorkTerminal.forEach((_, key) => xeroWorkTerminal.delete(key));
|
|
850
|
+
xeroConnection.forEach((_, key) => xeroConnection.delete(key));
|
|
851
|
+
connectionBindings.forEach((_, key) => connectionBindings.delete(key));
|
|
852
|
+
designPodState.forEach((_, key) => designPodState.delete(key));
|
|
853
|
+
root.delete("activeRunId");
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/core/lib/flowEngine/conditions.ts
|
|
858
|
+
var OPERATOR_INVERSIONS = {
|
|
859
|
+
equals: "not_equals",
|
|
860
|
+
not_equals: "equals",
|
|
861
|
+
contains: "not_contains",
|
|
862
|
+
not_contains: "contains",
|
|
863
|
+
is_empty: "is_not_empty",
|
|
864
|
+
is_not_empty: "is_empty",
|
|
865
|
+
greater_than: "less_than",
|
|
866
|
+
less_than: "greater_than"
|
|
867
|
+
};
|
|
868
|
+
var ACTION_INVERSIONS = {
|
|
869
|
+
disable: "enable",
|
|
870
|
+
hide: "show",
|
|
871
|
+
enable: null,
|
|
872
|
+
show: null
|
|
873
|
+
};
|
|
874
|
+
function normalizeCondition(condition) {
|
|
875
|
+
const canonicalAction = ACTION_INVERSIONS[condition.effect.action];
|
|
876
|
+
if (canonicalAction === null) return condition;
|
|
877
|
+
let value = condition.rule.value;
|
|
878
|
+
if ((condition.rule.operator === "equals" || condition.rule.operator === "not_equals") && typeof value === "boolean") {
|
|
879
|
+
value = !value;
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
...condition,
|
|
883
|
+
rule: {
|
|
884
|
+
...condition.rule,
|
|
885
|
+
operator: OPERATOR_INVERSIONS[condition.rule.operator],
|
|
886
|
+
value
|
|
887
|
+
},
|
|
888
|
+
effect: { ...condition.effect, action: canonicalAction }
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
function normalizeConditions(conditions) {
|
|
892
|
+
return conditions.map(normalizeCondition);
|
|
893
|
+
}
|
|
894
|
+
function parseConditionConfig(value) {
|
|
895
|
+
if (!value) return { enabled: false, mode: "all_must_pass", conditions: [] };
|
|
896
|
+
try {
|
|
897
|
+
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
898
|
+
if (!parsed || typeof parsed !== "object") {
|
|
899
|
+
return { enabled: false, mode: "all_must_pass", conditions: [] };
|
|
900
|
+
}
|
|
901
|
+
const candidate = parsed;
|
|
902
|
+
return {
|
|
903
|
+
enabled: candidate.enabled === true,
|
|
904
|
+
mode: candidate.mode === "any_can_pass" ? "any_can_pass" : "all_must_pass",
|
|
905
|
+
conditions: Array.isArray(candidate.conditions) ? candidate.conditions : []
|
|
906
|
+
};
|
|
907
|
+
} catch {
|
|
908
|
+
return { enabled: false, mode: "all_must_pass", conditions: [] };
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
var InvalidConditionConfigError = class extends Error {
|
|
912
|
+
constructor(message) {
|
|
913
|
+
super(message);
|
|
914
|
+
this.name = "InvalidConditionConfigError";
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
function parseConditionConfigStrict(value) {
|
|
918
|
+
if (value == null || value === "") {
|
|
919
|
+
return { enabled: false, mode: "all_must_pass", conditions: [] };
|
|
920
|
+
}
|
|
921
|
+
let parsed;
|
|
922
|
+
try {
|
|
923
|
+
parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
924
|
+
} catch {
|
|
925
|
+
throw new InvalidConditionConfigError("Block conditions are not valid JSON.");
|
|
926
|
+
}
|
|
927
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
928
|
+
throw new InvalidConditionConfigError("Block conditions must be an object.");
|
|
929
|
+
}
|
|
930
|
+
const candidate = parsed;
|
|
931
|
+
if (candidate.enabled !== true) {
|
|
932
|
+
return { enabled: false, mode: "all_must_pass", conditions: [] };
|
|
933
|
+
}
|
|
934
|
+
if (candidate.mode !== "all_must_pass" && candidate.mode !== "any_can_pass") {
|
|
935
|
+
throw new InvalidConditionConfigError("Enabled block conditions require a valid mode.");
|
|
936
|
+
}
|
|
937
|
+
if (!Array.isArray(candidate.conditions)) {
|
|
938
|
+
throw new InvalidConditionConfigError("Enabled block conditions require a conditions array.");
|
|
939
|
+
}
|
|
940
|
+
for (const condition of candidate.conditions) {
|
|
941
|
+
if (!condition || typeof condition !== "object" || typeof condition.sourceBlockId !== "string" || !condition.rule || !condition.effect) {
|
|
942
|
+
throw new InvalidConditionConfigError("Block conditions contain an invalid condition.");
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return {
|
|
946
|
+
enabled: true,
|
|
947
|
+
mode: candidate.mode,
|
|
948
|
+
conditions: candidate.conditions
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function flattenBlocks2(blocks) {
|
|
952
|
+
const flattened = [];
|
|
953
|
+
const visit = (block) => {
|
|
954
|
+
flattened.push(block);
|
|
955
|
+
const children = block?.children;
|
|
956
|
+
if (Array.isArray(children)) children.forEach(visit);
|
|
957
|
+
};
|
|
958
|
+
blocks.forEach(visit);
|
|
959
|
+
return flattened;
|
|
960
|
+
}
|
|
961
|
+
function resolveRuntimeValue(runtime, property) {
|
|
962
|
+
if (!runtime) return void 0;
|
|
963
|
+
if (property.startsWith("output.")) {
|
|
964
|
+
return getNestedValue(runtime.output, property.slice("output.".length));
|
|
965
|
+
}
|
|
966
|
+
const direct = getNestedValue(runtime, property);
|
|
967
|
+
if (direct !== void 0) return direct;
|
|
968
|
+
return getNestedValue(runtime.output, property);
|
|
969
|
+
}
|
|
970
|
+
function resolveConditionSourceValue(condition, sourceBlock, options = {}) {
|
|
971
|
+
const runtimeValue = resolveRuntimeValue(options.getRuntimeState?.(condition.sourceBlockId), condition.rule.property);
|
|
972
|
+
if (runtimeValue !== void 0 || options.allowPropsFallback === false) return runtimeValue;
|
|
973
|
+
const props = sourceBlock?.props;
|
|
974
|
+
return getNestedValue(props, condition.rule.property);
|
|
975
|
+
}
|
|
976
|
+
function compareConditionValue(sourceValue, operator, expected) {
|
|
977
|
+
switch (operator) {
|
|
978
|
+
case "equals":
|
|
979
|
+
return sourceValue === expected;
|
|
980
|
+
case "not_equals":
|
|
981
|
+
return sourceValue !== expected;
|
|
982
|
+
case "greater_than":
|
|
983
|
+
return typeof sourceValue === "number" && typeof expected === "number" && sourceValue > expected;
|
|
984
|
+
case "less_than":
|
|
985
|
+
return typeof sourceValue === "number" && typeof expected === "number" && sourceValue < expected;
|
|
986
|
+
case "contains":
|
|
987
|
+
return typeof sourceValue === "string" && typeof expected === "string" && sourceValue.toLowerCase().includes(expected.toLowerCase());
|
|
988
|
+
case "not_contains":
|
|
989
|
+
return typeof sourceValue === "string" && typeof expected === "string" && !sourceValue.toLowerCase().includes(expected.toLowerCase());
|
|
990
|
+
case "is_empty":
|
|
991
|
+
return sourceValue === "" || sourceValue === null || sourceValue === void 0;
|
|
992
|
+
case "is_not_empty":
|
|
993
|
+
return sourceValue !== "" && sourceValue !== null && sourceValue !== void 0;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
function evaluateCondition(condition, sourceBlock, options = {}) {
|
|
997
|
+
if (!sourceBlock) {
|
|
998
|
+
return {
|
|
999
|
+
passes: false,
|
|
1000
|
+
message: `Required block not found: ${condition.name || condition.sourceBlockType}`
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
const passes = compareConditionValue(resolveConditionSourceValue(condition, sourceBlock, options), condition.rule.operator, condition.rule.value);
|
|
1004
|
+
return { passes, message: passes ? void 0 : condition.effect.message };
|
|
1005
|
+
}
|
|
1006
|
+
function evaluateBlockConditions(conditionConfig, editorDocument, options = {}) {
|
|
1007
|
+
if (!conditionConfig.enabled || conditionConfig.conditions.length === 0) return { actions: [] };
|
|
1008
|
+
const blocks = flattenBlocks2(editorDocument);
|
|
1009
|
+
const evaluations = normalizeConditions(conditionConfig.conditions).map((condition) => {
|
|
1010
|
+
const sourceBlock = blocks.find((block) => {
|
|
1011
|
+
const candidate = block;
|
|
1012
|
+
return candidate.id === condition.sourceBlockId && candidate.type === condition.sourceBlockType;
|
|
1013
|
+
});
|
|
1014
|
+
return { condition, result: evaluateCondition(condition, sourceBlock, options) };
|
|
1015
|
+
});
|
|
1016
|
+
const passed = conditionConfig.mode === "all_must_pass" ? evaluations.every(({ result }) => result.passes) ? evaluations : [] : evaluations.filter(({ result }) => result.passes);
|
|
1017
|
+
return {
|
|
1018
|
+
actions: passed.map(({ condition }) => ({
|
|
1019
|
+
action: condition.effect.action,
|
|
1020
|
+
message: condition.effect.message,
|
|
1021
|
+
conditionName: condition.name
|
|
1022
|
+
}))
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
function evaluateBlockConditionsForRun(conditionConfig, editorDocument, yDoc, sessionRunId) {
|
|
1026
|
+
return evaluateBlockConditions(conditionConfig, editorDocument, {
|
|
1027
|
+
getRuntimeState: (blockId) => readActionState(yDoc, sessionRunId, blockId),
|
|
1028
|
+
allowPropsFallback: usesLegacyRuntimeCompatibility(yDoc)
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
function evaluateBlockExecutionEligibilityForRun(conditionConfig, editorDocument, yDoc, sessionRunId) {
|
|
1032
|
+
const normalized = normalizeConditions(conditionConfig.conditions);
|
|
1033
|
+
const result = evaluateBlockConditionsForRun(conditionConfig, editorDocument, yDoc, sessionRunId);
|
|
1034
|
+
const requiresEnable = normalized.some((condition) => condition.effect.action === "enable");
|
|
1035
|
+
const requiresShow = normalized.some((condition) => condition.effect.action === "show");
|
|
1036
|
+
const enabled = !conditionConfig.enabled || !requiresEnable || result.actions.some((action) => action.action === "enable");
|
|
1037
|
+
const visible = !conditionConfig.enabled || !requiresShow || result.actions.some((action) => action.action === "show");
|
|
1038
|
+
const failedMessage = normalized.map((condition) => condition.effect.message).find((message) => !!message);
|
|
1039
|
+
return {
|
|
1040
|
+
eligible: enabled && visible,
|
|
1041
|
+
enabled,
|
|
1042
|
+
visible,
|
|
1043
|
+
...enabled && visible ? {} : { message: failedMessage || "Block conditions are not satisfied." },
|
|
1044
|
+
result
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/core/lib/flowEngine/utils.ts
|
|
1049
|
+
var buildAuthzFromProps = (props) => {
|
|
1050
|
+
const linkedClaimCollectionId = typeof props.linkedClaimCollectionId === "string" ? props.linkedClaimCollectionId.trim() : "";
|
|
1051
|
+
const authz = {};
|
|
1052
|
+
if (linkedClaimCollectionId) {
|
|
1053
|
+
authz.linkedClaim = { collectionId: linkedClaimCollectionId };
|
|
1054
|
+
}
|
|
1055
|
+
return authz;
|
|
1056
|
+
};
|
|
1057
|
+
var buildFlowNodeFromBlock = (block) => {
|
|
1058
|
+
const base = {
|
|
1059
|
+
id: block.id,
|
|
1060
|
+
type: block.type,
|
|
1061
|
+
props: block.props || {}
|
|
1062
|
+
};
|
|
1063
|
+
const authz = buildAuthzFromProps(block.props || {});
|
|
1064
|
+
return {
|
|
1065
|
+
...base,
|
|
1066
|
+
...authz
|
|
1067
|
+
};
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
// src/core/lib/flowEngine/proof.ts
|
|
1071
|
+
var PROOF_MISSING_CODE = "PROOF_MISSING";
|
|
1072
|
+
function getOutputValue(output, path) {
|
|
1073
|
+
return path.split(".").reduce((acc, key) => {
|
|
1074
|
+
if (acc && typeof acc === "object") return acc[key];
|
|
1075
|
+
return void 0;
|
|
1076
|
+
}, output);
|
|
1077
|
+
}
|
|
1078
|
+
function isProofValue(value) {
|
|
1079
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
1080
|
+
if (typeof value === "number") return !Number.isNaN(value);
|
|
1081
|
+
return !!value;
|
|
1082
|
+
}
|
|
1083
|
+
function validateActionProof(action, output) {
|
|
1084
|
+
const proof = action.proof;
|
|
1085
|
+
if (proof === "none") return { valid: true };
|
|
1086
|
+
if (proof === void 0) {
|
|
1087
|
+
if (action.sideEffect) {
|
|
1088
|
+
return {
|
|
1089
|
+
valid: false,
|
|
1090
|
+
reason: `Action '${action.type}' has a side effect but declares no proof of execution. Declare proof (or 'none') on its ActionDefinition.`
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
return { valid: true };
|
|
1094
|
+
}
|
|
1095
|
+
if ("validate" in proof) {
|
|
1096
|
+
let valid = false;
|
|
1097
|
+
try {
|
|
1098
|
+
valid = proof.validate(output);
|
|
1099
|
+
} catch {
|
|
1100
|
+
valid = false;
|
|
1101
|
+
}
|
|
1102
|
+
return valid ? { valid: true } : { valid: false, reason: `Action '${action.type}' returned success but its proof validator rejected the output.` };
|
|
1103
|
+
}
|
|
1104
|
+
const satisfied = proof.fields.some((field) => isProofValue(getOutputValue(output, field)));
|
|
1105
|
+
if (satisfied) return { valid: true };
|
|
1106
|
+
return {
|
|
1107
|
+
valid: false,
|
|
1108
|
+
reason: `Action '${action.type}' returned success without proof. Expected at least one of [${proof.fields.join(", ")}] in the output. Check the handler logs.`
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// src/core/lib/flowEngine/completion.ts
|
|
1113
|
+
function isActionDone(action, actionState, runContext) {
|
|
1114
|
+
if (!action) return false;
|
|
1115
|
+
return action.done.isDone(actionState, runContext);
|
|
1116
|
+
}
|
|
1117
|
+
function isActionTypeDone(actionType, actionState, runContext) {
|
|
1118
|
+
return isActionDone(actionType ? getAction(actionType) : void 0, actionState, runContext);
|
|
1119
|
+
}
|
|
1120
|
+
async function reconcileCompletion(params) {
|
|
1121
|
+
const action = getAction(params.actionType);
|
|
1122
|
+
if (!action) throw new Error(`Unknown action type "${params.actionType}"`);
|
|
1123
|
+
const previous = readActionState(params.yDoc, params.sessionRunId, params.blockId);
|
|
1124
|
+
const ctx = {
|
|
1125
|
+
runId: params.sessionRunId,
|
|
1126
|
+
flowId: params.flowId,
|
|
1127
|
+
flowUri: params.flowUri,
|
|
1128
|
+
actorDid: params.actorDid,
|
|
1129
|
+
nodeId: params.blockId,
|
|
1130
|
+
services: params.services,
|
|
1131
|
+
handlers: params.handlers,
|
|
1132
|
+
now: params.now?.() ?? Date.now()
|
|
1133
|
+
};
|
|
1134
|
+
const check = action.done.check ? await action.done.check({ inputs: params.inputs, actionState: previous, ctx }) : {
|
|
1135
|
+
done: isActionDone(action, previous, ctx),
|
|
1136
|
+
source: "runtime-proof"
|
|
1137
|
+
};
|
|
1138
|
+
if (check.done === "unknown") {
|
|
1139
|
+
return { transition: "unknown", check, previous, next: previous };
|
|
1140
|
+
}
|
|
1141
|
+
if (action.done.reopenable && previous.state !== "completed") {
|
|
1142
|
+
return { transition: "unchanged", check, previous, next: previous };
|
|
1143
|
+
}
|
|
1144
|
+
if (check.done === true) {
|
|
1145
|
+
if (isActionDone(action, previous, ctx)) {
|
|
1146
|
+
return { transition: "unchanged", check, previous, next: previous };
|
|
1147
|
+
}
|
|
1148
|
+
const mergedOutput = {
|
|
1149
|
+
...previous.output || {},
|
|
1150
|
+
...check.output || {}
|
|
1151
|
+
};
|
|
1152
|
+
const proof = validateActionProof(action, mergedOutput);
|
|
1153
|
+
const bounded = boundRunActionOutput(mergedOutput);
|
|
1154
|
+
if (!proof.valid || bounded.exceeded) {
|
|
1155
|
+
const { next: next2 } = writeActionState(params.yDoc, params.sessionRunId, params.blockId, {
|
|
1156
|
+
state: "needs_verification",
|
|
1157
|
+
output: bounded.output,
|
|
1158
|
+
error: {
|
|
1159
|
+
message: bounded.exceeded ? "External completion output exceeds the run-head size limit." : proof.reason || "External completion did not return the action proof.",
|
|
1160
|
+
code: bounded.exceeded ? "RUN_OUTPUT_TOO_LARGE" : "COMPLETION_PROOF_MISSING",
|
|
1161
|
+
at: ctx.now
|
|
1162
|
+
},
|
|
1163
|
+
reconciledAt: ctx.now,
|
|
1164
|
+
completionSource: check.source
|
|
1165
|
+
});
|
|
1166
|
+
return {
|
|
1167
|
+
transition: "needs_verification",
|
|
1168
|
+
check,
|
|
1169
|
+
previous,
|
|
1170
|
+
next: next2
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
const { next } = writeActionState(params.yDoc, params.sessionRunId, params.blockId, {
|
|
1174
|
+
state: "completed",
|
|
1175
|
+
output: bounded.output,
|
|
1176
|
+
error: void 0,
|
|
1177
|
+
executedAt: previous.executedAt ?? ctx.now,
|
|
1178
|
+
reconciledAt: ctx.now,
|
|
1179
|
+
completionSource: check.source
|
|
1180
|
+
});
|
|
1181
|
+
return { transition: "adopted", check, previous, next };
|
|
1182
|
+
}
|
|
1183
|
+
if (previous.state === "completed" && action.done.reopenable) {
|
|
1184
|
+
if (!check.output) {
|
|
1185
|
+
return { transition: "unchanged", check, previous, next: previous };
|
|
1186
|
+
}
|
|
1187
|
+
const wasDone = isActionDone(action, previous, ctx);
|
|
1188
|
+
const bounded = boundRunActionOutput({
|
|
1189
|
+
...previous.output || {},
|
|
1190
|
+
...check.output
|
|
1191
|
+
});
|
|
1192
|
+
if (bounded.exceeded) {
|
|
1193
|
+
return { transition: "unchanged", check, previous, next: previous };
|
|
1194
|
+
}
|
|
1195
|
+
const { next } = writeActionState(params.yDoc, params.sessionRunId, params.blockId, {
|
|
1196
|
+
output: bounded.output,
|
|
1197
|
+
reconciledAt: ctx.now,
|
|
1198
|
+
completionSource: check.source
|
|
1199
|
+
});
|
|
1200
|
+
return { transition: wasDone ? "reopened" : "unchanged", check, previous, next };
|
|
1201
|
+
}
|
|
1202
|
+
if (previous.state === "completed") {
|
|
1203
|
+
const { next } = writeActionState(params.yDoc, params.sessionRunId, params.blockId, {
|
|
1204
|
+
state: "needs_verification",
|
|
1205
|
+
error: {
|
|
1206
|
+
message: check.reason || "External completion check contradicts the completed run state.",
|
|
1207
|
+
code: "COMPLETION_MISMATCH",
|
|
1208
|
+
at: ctx.now
|
|
1209
|
+
},
|
|
1210
|
+
reconciledAt: ctx.now,
|
|
1211
|
+
completionSource: check.source
|
|
1212
|
+
});
|
|
1213
|
+
return { transition: "needs_verification", check, previous, next };
|
|
1214
|
+
}
|
|
1215
|
+
return { transition: "unchanged", check, previous, next: previous };
|
|
1216
|
+
}
|
|
1217
|
+
async function reconcileCheckableCompletions(params) {
|
|
1218
|
+
const results = {};
|
|
1219
|
+
for (const [blockId, actionType] of Object.entries(params.actionTypes)) {
|
|
1220
|
+
const action = getAction(actionType);
|
|
1221
|
+
if (!action?.done.check) continue;
|
|
1222
|
+
try {
|
|
1223
|
+
results[blockId] = await reconcileCompletion({
|
|
1224
|
+
yDoc: params.yDoc,
|
|
1225
|
+
sessionRunId: params.sessionRunId,
|
|
1226
|
+
blockId,
|
|
1227
|
+
actionType,
|
|
1228
|
+
inputs: params.inputsFor(blockId, actionType),
|
|
1229
|
+
flowId: params.flowId,
|
|
1230
|
+
flowUri: params.flowUri,
|
|
1231
|
+
actorDid: params.actorDid,
|
|
1232
|
+
services: params.services,
|
|
1233
|
+
handlers: params.handlers,
|
|
1234
|
+
now: params.now
|
|
1235
|
+
});
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
const previous = readActionState(params.yDoc, params.sessionRunId, blockId);
|
|
1238
|
+
results[blockId] = {
|
|
1239
|
+
transition: "unknown",
|
|
1240
|
+
check: { done: "unknown", source: "reconcile", reason: error instanceof Error ? error.message : String(error) },
|
|
1241
|
+
previous,
|
|
1242
|
+
next: previous
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return results;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// src/core/lib/flowAgent/store.ts
|
|
1250
|
+
import * as Y2 from "yjs";
|
|
1251
|
+
|
|
1252
|
+
// src/core/lib/flowAgent/utils.ts
|
|
1253
|
+
function stableStringify(value) {
|
|
1254
|
+
if (value === void 0) return "null";
|
|
1255
|
+
if (value === null || typeof value !== "object") {
|
|
1256
|
+
return JSON.stringify(value);
|
|
1257
|
+
}
|
|
1258
|
+
if (Array.isArray(value)) {
|
|
1259
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
1260
|
+
}
|
|
1261
|
+
const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
|
1262
|
+
return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableStringify(val)}`).join(",")}}`;
|
|
1263
|
+
}
|
|
1264
|
+
function fnv1a322(input) {
|
|
1265
|
+
let hash = 2166136261;
|
|
1266
|
+
for (let i = 0; i < input.length; i++) {
|
|
1267
|
+
hash ^= input.charCodeAt(i);
|
|
1268
|
+
hash = Math.imul(hash, 16777619);
|
|
1269
|
+
}
|
|
1270
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1271
|
+
}
|
|
1272
|
+
function createIntentHash(intent) {
|
|
1273
|
+
return fnv1a322(stableStringify(intent));
|
|
1274
|
+
}
|
|
1275
|
+
function parseJsonProp(value, fallback) {
|
|
1276
|
+
if (value == null || value === "") return fallback;
|
|
1277
|
+
if (typeof value === "object") return value;
|
|
1278
|
+
if (typeof value !== "string") return fallback;
|
|
1279
|
+
try {
|
|
1280
|
+
const parsed = JSON.parse(value);
|
|
1281
|
+
return parsed;
|
|
1282
|
+
} catch {
|
|
1283
|
+
return fallback;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function getBlockActionType(block) {
|
|
1287
|
+
const props = block?.props;
|
|
1288
|
+
const actionType = props?.actionType;
|
|
1289
|
+
return typeof actionType === "string" && actionType.length > 0 ? actionType : void 0;
|
|
1290
|
+
}
|
|
1291
|
+
function getBlockTitle(block) {
|
|
1292
|
+
const props = block?.props;
|
|
1293
|
+
const title = props?.title;
|
|
1294
|
+
return typeof title === "string" && title.length > 0 ? title : void 0;
|
|
1295
|
+
}
|
|
1296
|
+
function getBlockType(block) {
|
|
1297
|
+
const type = block?.type;
|
|
1298
|
+
return typeof type === "string" && type.length > 0 ? type : "block";
|
|
1299
|
+
}
|
|
1300
|
+
function getBlockProps(block) {
|
|
1301
|
+
return block?.props || {};
|
|
1302
|
+
}
|
|
1303
|
+
function getBlockId(block) {
|
|
1304
|
+
const id = block?.id;
|
|
1305
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
1306
|
+
}
|
|
1307
|
+
function getAssigneeDid(block) {
|
|
1308
|
+
const props = getBlockProps(block);
|
|
1309
|
+
const assignment = parseJsonProp(props.assignment, {});
|
|
1310
|
+
const did = assignment?.assignedActor?.did;
|
|
1311
|
+
return typeof did === "string" && did.length > 0 ? did : void 0;
|
|
1312
|
+
}
|
|
1313
|
+
function getDueAt(block) {
|
|
1314
|
+
const props = getBlockProps(block);
|
|
1315
|
+
const ttlAbsoluteDueDate = props.ttlAbsoluteDueDate;
|
|
1316
|
+
if (typeof ttlAbsoluteDueDate !== "string" || ttlAbsoluteDueDate.length === 0) return void 0;
|
|
1317
|
+
const parsed = new Date(ttlAbsoluteDueDate).getTime();
|
|
1318
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1319
|
+
}
|
|
1320
|
+
function capabilityForCommand(can, flowUri, nodeId) {
|
|
1321
|
+
return {
|
|
1322
|
+
can,
|
|
1323
|
+
with: `${flowUri}:${nodeId}`
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// src/core/lib/flowAgent/store.ts
|
|
1328
|
+
var FLOW_AGENT_OUTBOX_KEY = "agentOutbox";
|
|
1329
|
+
var FLOW_AGENT_LEASES_KEY = "agentLeases";
|
|
1330
|
+
var FLOW_AGENT_AUDIT_SCOPE = "__flow_agent__";
|
|
1331
|
+
function readExistingMap(yDoc, key) {
|
|
1332
|
+
return getExistingYMap(yDoc, key);
|
|
1333
|
+
}
|
|
1334
|
+
function getFlowAgentMaps(yDoc) {
|
|
1335
|
+
return {
|
|
1336
|
+
outbox: yDoc.getMap(FLOW_AGENT_OUTBOX_KEY),
|
|
1337
|
+
leases: yDoc.getMap(FLOW_AGENT_LEASES_KEY)
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function computeAgentCommandId(params) {
|
|
1341
|
+
const intentHash = createIntentHash(params);
|
|
1342
|
+
return `${params.flowId}:${params.nodeId}:${intentHash}`;
|
|
1343
|
+
}
|
|
1344
|
+
function queueAgentCommand(yDoc, command) {
|
|
1345
|
+
const { outbox } = getFlowAgentMaps(yDoc);
|
|
1346
|
+
const existing = outbox.get(command.id);
|
|
1347
|
+
if (existing) {
|
|
1348
|
+
const retryFailedAssignment = existing.type === "assign_actor" && existing.status === "failed";
|
|
1349
|
+
const retryFailedDiagnosis = existing.type === "diagnose_blocker" && existing.status === "failed";
|
|
1350
|
+
const retryUnprovenDiagnosis = existing.type === "diagnose_blocker" && existing.status === "confirmed" && typeof existing.payload.delegatedInvocationId !== "string";
|
|
1351
|
+
if (retryFailedAssignment || retryFailedDiagnosis || retryUnprovenDiagnosis) {
|
|
1352
|
+
const retried = {
|
|
1353
|
+
...existing,
|
|
1354
|
+
status: "queued",
|
|
1355
|
+
error: void 0,
|
|
1356
|
+
lease: void 0,
|
|
1357
|
+
reason: command.reason,
|
|
1358
|
+
payload: command.payload,
|
|
1359
|
+
capability: command.capability,
|
|
1360
|
+
actorDid: command.actorDid,
|
|
1361
|
+
updatedAt: command.updatedAt
|
|
1362
|
+
};
|
|
1363
|
+
outbox.set(command.id, retried);
|
|
1364
|
+
try {
|
|
1365
|
+
appendAgentLedgerEvent(yDoc, {
|
|
1366
|
+
type: "agent.command",
|
|
1367
|
+
flowId: retried.flowId,
|
|
1368
|
+
sessionRunId: retried.sessionRunId,
|
|
1369
|
+
nodeId: retried.nodeId,
|
|
1370
|
+
commandId: retried.id,
|
|
1371
|
+
actorDid: retried.actorDid,
|
|
1372
|
+
timestamp: retried.updatedAt,
|
|
1373
|
+
details: {
|
|
1374
|
+
commandType: retried.type,
|
|
1375
|
+
status: retried.status,
|
|
1376
|
+
idempotencyKey: retried.idempotencyKey,
|
|
1377
|
+
reason: retried.reason,
|
|
1378
|
+
retry: true,
|
|
1379
|
+
retryCause: retryFailedDiagnosis ? "diagnosis_failed" : retryUnprovenDiagnosis ? "missing_delegated_invocation" : "assignment_failed"
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
const message = error instanceof Error ? error.message : "Failed to append flow-agent ledger event";
|
|
1384
|
+
if (!message.includes("Unexpected content type") && !message.includes("already been defined with a different constructor")) {
|
|
1385
|
+
throw error;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return { command: retried, created: true };
|
|
1389
|
+
}
|
|
1390
|
+
return { command: existing, created: false };
|
|
1391
|
+
}
|
|
1392
|
+
outbox.set(command.id, command);
|
|
1393
|
+
try {
|
|
1394
|
+
appendAgentLedgerEvent(yDoc, {
|
|
1395
|
+
type: "agent.command",
|
|
1396
|
+
flowId: command.flowId,
|
|
1397
|
+
sessionRunId: command.sessionRunId,
|
|
1398
|
+
nodeId: command.nodeId,
|
|
1399
|
+
commandId: command.id,
|
|
1400
|
+
actorDid: command.actorDid,
|
|
1401
|
+
timestamp: command.createdAt,
|
|
1402
|
+
details: {
|
|
1403
|
+
commandType: command.type,
|
|
1404
|
+
status: command.status,
|
|
1405
|
+
idempotencyKey: command.idempotencyKey,
|
|
1406
|
+
reason: command.reason
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
const message = error instanceof Error ? error.message : "Failed to append flow-agent ledger event";
|
|
1411
|
+
if (!message.includes("Unexpected content type") && !message.includes("already been defined with a different constructor")) {
|
|
1412
|
+
throw error;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
return { command, created: true };
|
|
1416
|
+
}
|
|
1417
|
+
function belongsToSessionRun(value, sessionRunId) {
|
|
1418
|
+
return !sessionRunId || isRecordInRun(value.sessionRunId, sessionRunId);
|
|
1419
|
+
}
|
|
1420
|
+
function readAgentCommands(yDoc, sessionRunId) {
|
|
1421
|
+
const outbox = readExistingMap(yDoc, FLOW_AGENT_OUTBOX_KEY);
|
|
1422
|
+
if (!outbox) return [];
|
|
1423
|
+
const commands = [];
|
|
1424
|
+
outbox.forEach((command) => {
|
|
1425
|
+
if (belongsToSessionRun(command, sessionRunId)) {
|
|
1426
|
+
commands.push({
|
|
1427
|
+
...command,
|
|
1428
|
+
sessionRunId: resolveRecordRunId(command.sessionRunId, sessionRunId)
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
commands.sort(
|
|
1433
|
+
(a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0) || (a.id ?? a.commandId ?? "").localeCompare(b.id ?? b.commandId ?? "")
|
|
1434
|
+
);
|
|
1435
|
+
return commands;
|
|
1436
|
+
}
|
|
1437
|
+
function readQueuedAgentCommands(yDoc, sessionRunId) {
|
|
1438
|
+
return readAgentCommands(yDoc, sessionRunId).filter((command) => command.status === "queued" || command.status === "leased" || command.status === "running");
|
|
1439
|
+
}
|
|
1440
|
+
function readFlowAgentLeases(yDoc, sessionRunId) {
|
|
1441
|
+
const leases = readExistingMap(yDoc, FLOW_AGENT_LEASES_KEY);
|
|
1442
|
+
if (!leases) return [];
|
|
1443
|
+
const results = [];
|
|
1444
|
+
leases.forEach((lease) => {
|
|
1445
|
+
if (belongsToSessionRun(lease, sessionRunId)) {
|
|
1446
|
+
results.push({
|
|
1447
|
+
...lease,
|
|
1448
|
+
sessionRunId: resolveRecordRunId(lease.sessionRunId, sessionRunId)
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
results.sort((a, b) => (a.acquiredAt ?? 0) - (b.acquiredAt ?? 0) || (a.id ?? a.commandId ?? "").localeCompare(b.id ?? b.commandId ?? ""));
|
|
1453
|
+
return results;
|
|
1454
|
+
}
|
|
1455
|
+
function updateAgentCommand(yDoc, commandId, patch) {
|
|
1456
|
+
const { outbox } = getFlowAgentMaps(yDoc);
|
|
1457
|
+
const current = outbox.get(commandId);
|
|
1458
|
+
if (!current) return null;
|
|
1459
|
+
const next = { ...current, ...patch };
|
|
1460
|
+
outbox.set(commandId, next);
|
|
1461
|
+
return next;
|
|
1462
|
+
}
|
|
1463
|
+
function appendAgentLedgerEvent(yDoc, event) {
|
|
1464
|
+
const auditMap = yDoc.getMap("auditTrail");
|
|
1465
|
+
let arr = auditMap.get(FLOW_AGENT_AUDIT_SCOPE);
|
|
1466
|
+
if (!arr) {
|
|
1467
|
+
arr = new Y2.Array();
|
|
1468
|
+
auditMap.set(FLOW_AGENT_AUDIT_SCOPE, arr);
|
|
1469
|
+
}
|
|
1470
|
+
const fullEvent = {
|
|
1471
|
+
id: event.id || `${event.type}:${event.commandId || event.nodeId || "flow"}:${createIntentHash(event.details)}:${event.timestamp}`,
|
|
1472
|
+
type: event.type,
|
|
1473
|
+
flowId: event.flowId,
|
|
1474
|
+
sessionRunId: event.sessionRunId,
|
|
1475
|
+
actorDid: event.actorDid,
|
|
1476
|
+
timestamp: event.timestamp,
|
|
1477
|
+
details: JSON.parse(stableStringify(event.details))
|
|
1478
|
+
};
|
|
1479
|
+
if (event.nodeId) fullEvent.nodeId = event.nodeId;
|
|
1480
|
+
if (event.commandId) fullEvent.commandId = event.commandId;
|
|
1481
|
+
arr.push([
|
|
1482
|
+
{
|
|
1483
|
+
id: fullEvent.id,
|
|
1484
|
+
type: fullEvent.type,
|
|
1485
|
+
details: fullEvent,
|
|
1486
|
+
meta: {
|
|
1487
|
+
timestamp: new Date(fullEvent.timestamp).toISOString(),
|
|
1488
|
+
userId: fullEvent.actorDid,
|
|
1489
|
+
editable: false
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
]);
|
|
1493
|
+
return fullEvent;
|
|
1494
|
+
}
|
|
1495
|
+
function readAgentLedgerEvents(yDoc, eventType, sessionRunId) {
|
|
1496
|
+
const auditMap = readExistingMap(yDoc, "auditTrail");
|
|
1497
|
+
if (!auditMap) return [];
|
|
1498
|
+
const arr = auditMap.get(FLOW_AGENT_AUDIT_SCOPE);
|
|
1499
|
+
if (!arr) return [];
|
|
1500
|
+
const events = [];
|
|
1501
|
+
arr.forEach((value) => {
|
|
1502
|
+
if (!value || typeof value !== "object") return;
|
|
1503
|
+
const entry = value;
|
|
1504
|
+
if (!entry.details) return;
|
|
1505
|
+
if (eventType && entry.details.type !== eventType) return;
|
|
1506
|
+
if (sessionRunId && !isRecordInRun(entry.details.sessionRunId, sessionRunId)) {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
events.push({
|
|
1510
|
+
...entry.details,
|
|
1511
|
+
sessionRunId: resolveRecordRunId(entry.details.sessionRunId, sessionRunId)
|
|
1512
|
+
});
|
|
1513
|
+
});
|
|
1514
|
+
events.sort((a, b) => a.timestamp - b.timestamp || a.id.localeCompare(b.id));
|
|
1515
|
+
return events;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// src/core/lib/flowEngine/runMigration.ts
|
|
1519
|
+
function stampMapValues(yDoc, key, sessionRunId) {
|
|
1520
|
+
const map = getExistingYMap(yDoc, key);
|
|
1521
|
+
if (!map) return 0;
|
|
1522
|
+
let stamped = 0;
|
|
1523
|
+
map.forEach((value, entryKey) => {
|
|
1524
|
+
if (!value || typeof value !== "object" || value.sessionRunId) return;
|
|
1525
|
+
map.set(entryKey, { ...value, sessionRunId });
|
|
1526
|
+
stamped += 1;
|
|
1527
|
+
});
|
|
1528
|
+
return stamped;
|
|
1529
|
+
}
|
|
1530
|
+
function stampAuditEntries(yDoc, scope, sessionRunId, matches) {
|
|
1531
|
+
const auditMap = getExistingYMap(yDoc, "auditTrail");
|
|
1532
|
+
const arr = auditMap?.get(scope);
|
|
1533
|
+
if (!arr) return 0;
|
|
1534
|
+
let stamped = 0;
|
|
1535
|
+
for (let index = 0; index < arr.length; index += 1) {
|
|
1536
|
+
const value = arr.get(index);
|
|
1537
|
+
if (!value || typeof value !== "object") continue;
|
|
1538
|
+
const entry = value;
|
|
1539
|
+
if (!entry.details || entry.details.sessionRunId || !matches(entry)) continue;
|
|
1540
|
+
arr.delete(index, 1);
|
|
1541
|
+
arr.insert(index, [{ ...entry, details: { ...entry.details, sessionRunId } }]);
|
|
1542
|
+
stamped += 1;
|
|
1543
|
+
}
|
|
1544
|
+
return stamped;
|
|
1545
|
+
}
|
|
1546
|
+
function stampPreSessionRecords(yDoc, sessionRunId) {
|
|
1547
|
+
const auditScopes = getExistingYMap(yDoc, "auditTrail");
|
|
1548
|
+
let runRecords = 0;
|
|
1549
|
+
auditScopes?.forEach((_, scope) => {
|
|
1550
|
+
if (scope === FLOW_AGENT_AUDIT_SCOPE) return;
|
|
1551
|
+
runRecords += stampAuditEntries(yDoc, scope, sessionRunId, (entry) => entry.type === RUN_RECORD_AUDIT_TYPE);
|
|
1552
|
+
});
|
|
1553
|
+
return {
|
|
1554
|
+
commands: stampMapValues(yDoc, FLOW_AGENT_OUTBOX_KEY, sessionRunId),
|
|
1555
|
+
leases: stampMapValues(yDoc, FLOW_AGENT_LEASES_KEY, sessionRunId),
|
|
1556
|
+
ledgerEvents: stampAuditEntries(yDoc, FLOW_AGENT_AUDIT_SCOPE, sessionRunId, () => true),
|
|
1557
|
+
invocations: stampInvocations(yDoc, sessionRunId),
|
|
1558
|
+
runRecords
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
function stampInvocations(yDoc, sessionRunId) {
|
|
1562
|
+
const map = getExistingYMap(yDoc, INVOCATIONS_MAP_KEY);
|
|
1563
|
+
if (!map) return 0;
|
|
1564
|
+
let stamped = 0;
|
|
1565
|
+
map.forEach((value, cid) => {
|
|
1566
|
+
if (!value || typeof value !== "object" || value.sessionRunId) return;
|
|
1567
|
+
map.set(cid, { ...value, sessionRunId });
|
|
1568
|
+
stamped += 1;
|
|
1569
|
+
});
|
|
1570
|
+
return stamped;
|
|
1571
|
+
}
|
|
1572
|
+
function deriveMigratedStartedAt(yDoc) {
|
|
1573
|
+
let earliest;
|
|
1574
|
+
getExistingYMap(yDoc, "runtime")?.forEach((value) => {
|
|
1575
|
+
if (!value || typeof value !== "object") return;
|
|
1576
|
+
const executedAt = value.executedAt;
|
|
1577
|
+
if (typeof executedAt !== "number" || !Number.isFinite(executedAt)) return;
|
|
1578
|
+
if (earliest === void 0 || executedAt < earliest) earliest = executedAt;
|
|
1579
|
+
});
|
|
1580
|
+
if (earliest !== void 0) return earliest;
|
|
1581
|
+
const createdAt = getExistingYMap(yDoc, "root")?.get("createdAt");
|
|
1582
|
+
if (typeof createdAt === "number" && Number.isFinite(createdAt)) return createdAt;
|
|
1583
|
+
if (typeof createdAt === "string") {
|
|
1584
|
+
const parsed = Date.parse(createdAt);
|
|
1585
|
+
if (!Number.isNaN(parsed)) return parsed;
|
|
1586
|
+
}
|
|
1587
|
+
return 0;
|
|
1588
|
+
}
|
|
1589
|
+
var CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
1590
|
+
function fnv1a(input) {
|
|
1591
|
+
let hash = 2166136261;
|
|
1592
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
1593
|
+
hash ^= input.charCodeAt(index);
|
|
1594
|
+
hash = Math.imul(hash, 16777619);
|
|
1595
|
+
}
|
|
1596
|
+
return hash >>> 0;
|
|
1597
|
+
}
|
|
1598
|
+
function encodeCrockford(value, length) {
|
|
1599
|
+
let remaining = value;
|
|
1600
|
+
let encoded = "";
|
|
1601
|
+
for (let index = 0; index < length; index += 1) {
|
|
1602
|
+
encoded = CROCKFORD[remaining % 32] + encoded;
|
|
1603
|
+
remaining = Math.floor(remaining / 32);
|
|
1604
|
+
}
|
|
1605
|
+
return encoded;
|
|
1606
|
+
}
|
|
1607
|
+
function deriveMigratedRunId(params) {
|
|
1608
|
+
const seed = `${params.docId}\0${params.defHash}\0${params.startedAt}`;
|
|
1609
|
+
return `r-0001-${encodeCrockford(fnv1a(seed), 8)}${encodeCrockford(fnv1a(`${seed}\0salt`), 8)}`;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// src/core/lib/flowEngine/runLifecycle.ts
|
|
1613
|
+
var RunLifecycleAuthorizationError = class extends Error {
|
|
1614
|
+
constructor(capability, message) {
|
|
1615
|
+
super(message);
|
|
1616
|
+
this.capability = capability;
|
|
1617
|
+
this.name = "RunLifecycleAuthorizationError";
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
var RunNotReadyError = class extends Error {
|
|
1621
|
+
constructor(incompleteBlockIds) {
|
|
1622
|
+
super(`Run cannot close while manifest actions remain incomplete: ${incompleteBlockIds.join(", ")}`);
|
|
1623
|
+
this.incompleteBlockIds = incompleteBlockIds;
|
|
1624
|
+
this.name = "RunNotReadyError";
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
var FlowNotMigratedError = class extends Error {
|
|
1628
|
+
constructor(flowUri) {
|
|
1629
|
+
super("This flow predates sessions. Migrate it to enable them.");
|
|
1630
|
+
this.flowUri = flowUri;
|
|
1631
|
+
this.name = "FlowNotMigratedError";
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
var RunNotMigratedError = class extends Error {
|
|
1635
|
+
constructor(runId) {
|
|
1636
|
+
super(`Run "${runId}" has no frozen manifest, so it is not a session.`);
|
|
1637
|
+
this.runId = runId;
|
|
1638
|
+
this.name = "RunNotMigratedError";
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
function canonicalize(value) {
|
|
1642
|
+
if (value === null || typeof value !== "object") return value;
|
|
1643
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
1644
|
+
const result = {};
|
|
1645
|
+
for (const key of Object.keys(value).sort()) {
|
|
1646
|
+
const nested = value[key];
|
|
1647
|
+
if (nested !== void 0 && typeof nested !== "function") result[key] = canonicalize(nested);
|
|
1648
|
+
}
|
|
1649
|
+
return result;
|
|
1650
|
+
}
|
|
1651
|
+
function fnv1a323(input) {
|
|
1652
|
+
let hash = 2166136261;
|
|
1653
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
1654
|
+
hash ^= input.charCodeAt(i);
|
|
1655
|
+
hash = Math.imul(hash, 16777619);
|
|
1656
|
+
}
|
|
1657
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1658
|
+
}
|
|
1659
|
+
function computeRunDefinitionHash(definition) {
|
|
1660
|
+
return `def-${fnv1a323(JSON.stringify(canonicalize(definition)))}`;
|
|
1661
|
+
}
|
|
1662
|
+
function toRunJsonValue(value) {
|
|
1663
|
+
if (value === void 0) return null;
|
|
1664
|
+
try {
|
|
1665
|
+
return JSON.parse(JSON.stringify(value));
|
|
1666
|
+
} catch {
|
|
1667
|
+
return String(value);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
function walkBlocks(blocks, visit) {
|
|
1671
|
+
for (const block of blocks) {
|
|
1672
|
+
visit(block);
|
|
1673
|
+
if (Array.isArray(block.children)) walkBlocks(block.children, visit);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
function buildRunDefinitionSnapshot(blocks, definition = blocks) {
|
|
1677
|
+
const blockIds = [];
|
|
1678
|
+
const actionTypes = {};
|
|
1679
|
+
walkBlocks(blocks, (block) => {
|
|
1680
|
+
const actionType = block.props?.actionType;
|
|
1681
|
+
if (!block.id || typeof actionType !== "string" || isBelowTheLineBlock(block) || actionType === "qi/flow.run.start" || actionType === "qi/flow.run.close") {
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
blockIds.push(block.id);
|
|
1685
|
+
actionTypes[block.id] = actionType;
|
|
1686
|
+
});
|
|
1687
|
+
return {
|
|
1688
|
+
manifest: {
|
|
1689
|
+
blockIds,
|
|
1690
|
+
defHash: computeRunDefinitionHash(definition)
|
|
1691
|
+
},
|
|
1692
|
+
actionTypes
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
var CROCKFORD2 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
1696
|
+
function encodeTime(timestamp) {
|
|
1697
|
+
let value = Math.max(0, Math.floor(timestamp));
|
|
1698
|
+
let encoded = "";
|
|
1699
|
+
for (let i = 0; i < 10; i += 1) {
|
|
1700
|
+
encoded = CROCKFORD2[value % 32] + encoded;
|
|
1701
|
+
value = Math.floor(value / 32);
|
|
1702
|
+
}
|
|
1703
|
+
return encoded;
|
|
1704
|
+
}
|
|
1705
|
+
function randomBytes(length) {
|
|
1706
|
+
const bytes = new Uint8Array(length);
|
|
1707
|
+
const cryptoApi = globalThis.crypto;
|
|
1708
|
+
if (cryptoApi?.getRandomValues) return cryptoApi.getRandomValues(bytes);
|
|
1709
|
+
for (let i = 0; i < length; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
1710
|
+
return bytes;
|
|
1711
|
+
}
|
|
1712
|
+
function createRunUlid(timestamp = Date.now()) {
|
|
1713
|
+
const bytes = randomBytes(16);
|
|
1714
|
+
let randomPart = "";
|
|
1715
|
+
for (let i = 0; i < 16; i += 1) randomPart += CROCKFORD2[bytes[i] & 31];
|
|
1716
|
+
return `${encodeTime(timestamp)}${randomPart}`;
|
|
1717
|
+
}
|
|
1718
|
+
function nextRunSequence(yDoc) {
|
|
1719
|
+
let max = 0;
|
|
1720
|
+
getExistingRunsMap(yDoc)?.forEach((_, runId) => {
|
|
1721
|
+
max = Math.max(max, getRunMeta(yDoc, runId)?.seq || 0);
|
|
1722
|
+
});
|
|
1723
|
+
return max + 1;
|
|
1724
|
+
}
|
|
1725
|
+
function createRunId(seq, timestamp = Date.now()) {
|
|
1726
|
+
return `r-${String(seq).padStart(4, "0")}-${createRunUlid(timestamp)}`;
|
|
1727
|
+
}
|
|
1728
|
+
function listRuns(yDoc) {
|
|
1729
|
+
const terminal = getExistingRunsTerminalMap(yDoc);
|
|
1730
|
+
const runs = [];
|
|
1731
|
+
getExistingRunsMap(yDoc)?.forEach((_, id) => {
|
|
1732
|
+
const meta = getRunMeta(yDoc, id);
|
|
1733
|
+
if (!meta) return;
|
|
1734
|
+
const terminalRecord = terminal?.get(id);
|
|
1735
|
+
runs.push({
|
|
1736
|
+
id,
|
|
1737
|
+
meta: { ...meta, manifest: meta.manifest ? { ...meta.manifest, blockIds: [...meta.manifest.blockIds] } : void 0 },
|
|
1738
|
+
terminal: terminalRecord ? { ...terminalRecord, summary: terminalRecord.summary ? { ...terminalRecord.summary } : void 0 } : void 0
|
|
1739
|
+
});
|
|
1740
|
+
});
|
|
1741
|
+
return runs.sort((a, b) => b.meta.seq - a.meta.seq || b.meta.startedAt - a.meta.startedAt || b.id.localeCompare(a.id));
|
|
1742
|
+
}
|
|
1743
|
+
function listOpenRuns(yDoc) {
|
|
1744
|
+
return listRuns(yDoc).filter((run) => !run.terminal);
|
|
1745
|
+
}
|
|
1746
|
+
function setAdvisoryActiveRunId(yDoc, runId) {
|
|
1747
|
+
if (!getExistingRunsMap(yDoc)?.has(runId)) throw new Error(`Unknown run "${runId}"`);
|
|
1748
|
+
yDoc.getMap("root").set("activeRunId", runId);
|
|
1749
|
+
}
|
|
1750
|
+
async function requireAuthorization(authorize, request) {
|
|
1751
|
+
if (isTempUcanEnforcementDisabled()) {
|
|
1752
|
+
return { allowed: true };
|
|
1753
|
+
}
|
|
1754
|
+
const decision = await authorize(request);
|
|
1755
|
+
if (!decision.allowed) {
|
|
1756
|
+
throw new RunLifecycleAuthorizationError(request.can, decision.reason || `${request.actorDid} is not authorized for ${request.can}`);
|
|
1757
|
+
}
|
|
1758
|
+
return decision;
|
|
1759
|
+
}
|
|
1760
|
+
var lifecycleQueues = /* @__PURE__ */ new WeakMap();
|
|
1761
|
+
async function withLifecycleLock(yDoc, key, operation) {
|
|
1762
|
+
let queues = lifecycleQueues.get(yDoc);
|
|
1763
|
+
if (!queues) {
|
|
1764
|
+
queues = /* @__PURE__ */ new Map();
|
|
1765
|
+
lifecycleQueues.set(yDoc, queues);
|
|
1766
|
+
}
|
|
1767
|
+
const previous = queues.get(key) || Promise.resolve();
|
|
1768
|
+
let release;
|
|
1769
|
+
const gate = new Promise((resolve) => {
|
|
1770
|
+
release = resolve;
|
|
1771
|
+
});
|
|
1772
|
+
const current = previous.then(() => gate);
|
|
1773
|
+
queues.set(key, current);
|
|
1774
|
+
await previous;
|
|
1775
|
+
try {
|
|
1776
|
+
return await operation();
|
|
1777
|
+
} finally {
|
|
1778
|
+
release();
|
|
1779
|
+
if (queues.get(key) === current) queues.delete(key);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
async function startRun(params) {
|
|
1783
|
+
return withLifecycleLock(params.yDoc, "__start__", async () => {
|
|
1784
|
+
assertLegacyFlowOperationAllowed(params.protocolGate, "run.start");
|
|
1785
|
+
if (usesLegacyRuntimeCompatibility(params.yDoc)) {
|
|
1786
|
+
throw new FlowNotMigratedError(params.flowUri);
|
|
1787
|
+
}
|
|
1788
|
+
const authorization = await requireAuthorization(params.authorize, {
|
|
1789
|
+
can: "flow/run.start",
|
|
1790
|
+
with: params.flowUri,
|
|
1791
|
+
actorDid: params.actorDid
|
|
1792
|
+
});
|
|
1793
|
+
const now = params.now?.() ?? Date.now();
|
|
1794
|
+
const seq = nextRunSequence(params.yDoc);
|
|
1795
|
+
const runId = createRunId(seq, now);
|
|
1796
|
+
const meta = {
|
|
1797
|
+
seq,
|
|
1798
|
+
startedAt: now,
|
|
1799
|
+
startedByDid: params.actorDid,
|
|
1800
|
+
origin: "started",
|
|
1801
|
+
...params.label ? { label: params.label } : {},
|
|
1802
|
+
manifest: {
|
|
1803
|
+
blockIds: [...params.definition.manifest.blockIds],
|
|
1804
|
+
defHash: params.definition.manifest.defHash
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
const eventId = await params.eventLog.append({
|
|
1808
|
+
runId,
|
|
1809
|
+
kind: "run.started",
|
|
1810
|
+
payload: {
|
|
1811
|
+
...authorization.invocationCid ? { invocationCid: authorization.invocationCid } : {},
|
|
1812
|
+
definitionRevision: params.definition.manifest.defHash,
|
|
1813
|
+
...params.label ? { label: params.label } : {}
|
|
1814
|
+
},
|
|
1815
|
+
idempotencyKey: createRunEventIdempotencyKey("run.started", runId),
|
|
1816
|
+
ts: now
|
|
1817
|
+
});
|
|
1818
|
+
let flowStartInvocations = 0;
|
|
1819
|
+
params.yDoc.transact(() => {
|
|
1820
|
+
setMultiRunStorage(params.yDoc);
|
|
1821
|
+
ensureRun(params.yDoc, runId, meta);
|
|
1822
|
+
for (const blockId of meta.manifest.blockIds) {
|
|
1823
|
+
resetActionState(params.yDoc, runId, blockId, { state: "idle" });
|
|
1824
|
+
}
|
|
1825
|
+
if (params.document) {
|
|
1826
|
+
flowStartInvocations = dispatchFlowStartTriggers({
|
|
1827
|
+
yDoc: params.yDoc,
|
|
1828
|
+
runId,
|
|
1829
|
+
blocks: params.document,
|
|
1830
|
+
startedAt: now,
|
|
1831
|
+
sourceEventId: eventId
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
params.yDoc.getMap("root").set("activeRunId", runId);
|
|
1835
|
+
});
|
|
1836
|
+
return {
|
|
1837
|
+
run: { id: runId, meta },
|
|
1838
|
+
eventId,
|
|
1839
|
+
flowStartInvocations
|
|
1840
|
+
};
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
async function migrateFlowToSessions(params) {
|
|
1844
|
+
return withLifecycleLock(params.yDoc, "__start__", async () => {
|
|
1845
|
+
const empty = { actionStates: 0, pendingInvocations: 0, barrierEntries: 0 };
|
|
1846
|
+
if (!usesLegacyRuntimeCompatibility(params.yDoc)) {
|
|
1847
|
+
return { seeded: empty, phantomCollected: false, alreadyMigrated: true };
|
|
1848
|
+
}
|
|
1849
|
+
const authorization = await requireAuthorization(params.authorize, {
|
|
1850
|
+
can: "flow/run.start",
|
|
1851
|
+
with: params.flowUri,
|
|
1852
|
+
actorDid: params.actorDid
|
|
1853
|
+
});
|
|
1854
|
+
const startedAt = deriveMigratedStartedAt(params.yDoc);
|
|
1855
|
+
const runId = deriveMigratedRunId({
|
|
1856
|
+
docId: String(params.yDoc.getMap("root").get("doc_id") || params.flowUri),
|
|
1857
|
+
defHash: params.definition.manifest.defHash,
|
|
1858
|
+
startedAt
|
|
1859
|
+
});
|
|
1860
|
+
const meta = {
|
|
1861
|
+
seq: 1,
|
|
1862
|
+
startedAt,
|
|
1863
|
+
startedByDid: params.actorDid,
|
|
1864
|
+
origin: "migrated",
|
|
1865
|
+
label: params.label || "Session 1 (migrated)",
|
|
1866
|
+
manifest: {
|
|
1867
|
+
blockIds: [...params.definition.manifest.blockIds],
|
|
1868
|
+
defHash: params.definition.manifest.defHash
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
const eventId = await params.eventLog.append({
|
|
1872
|
+
runId,
|
|
1873
|
+
kind: "run.started",
|
|
1874
|
+
payload: {
|
|
1875
|
+
...authorization.invocationCid ? { invocationCid: authorization.invocationCid } : {},
|
|
1876
|
+
definitionRevision: params.definition.manifest.defHash,
|
|
1877
|
+
label: meta.label
|
|
1878
|
+
},
|
|
1879
|
+
idempotencyKey: createRunEventIdempotencyKey("run.started", runId),
|
|
1880
|
+
ts: startedAt
|
|
1881
|
+
});
|
|
1882
|
+
let seeded = empty;
|
|
1883
|
+
let stamped;
|
|
1884
|
+
let phantomCollected = false;
|
|
1885
|
+
params.yDoc.transact(() => {
|
|
1886
|
+
phantomCollected = collectPhantomLegacyRun(params.yDoc);
|
|
1887
|
+
ensureRun(params.yDoc, runId, meta);
|
|
1888
|
+
seeded = seedRunFromLegacyRuntime(params.yDoc, runId);
|
|
1889
|
+
stamped = stampPreSessionRecords(params.yDoc, runId);
|
|
1890
|
+
setMultiRunStorage(params.yDoc);
|
|
1891
|
+
params.yDoc.getMap("root").set("activeRunId", runId);
|
|
1892
|
+
});
|
|
1893
|
+
return {
|
|
1894
|
+
run: { id: runId, meta },
|
|
1895
|
+
eventId,
|
|
1896
|
+
seeded,
|
|
1897
|
+
stamped,
|
|
1898
|
+
phantomCollected,
|
|
1899
|
+
alreadyMigrated: false
|
|
1900
|
+
};
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
function computeRunSummary(yDoc, runId, actionTypes, context) {
|
|
1904
|
+
const meta = getRunMeta(yDoc, runId);
|
|
1905
|
+
if (!meta?.manifest) throw new RunNotMigratedError(runId);
|
|
1906
|
+
const runContext = { ...context, runId };
|
|
1907
|
+
const summary = {};
|
|
1908
|
+
for (const blockId of meta.manifest.blockIds) {
|
|
1909
|
+
summary[blockId] = isActionDone(actionTypes[blockId] ? getAction(actionTypes[blockId]) : void 0, readActionState(yDoc, runId, blockId), runContext);
|
|
1910
|
+
}
|
|
1911
|
+
return summary;
|
|
1912
|
+
}
|
|
1913
|
+
function inspectPinnedDesignPodRunForClosure(yDoc, runId) {
|
|
1914
|
+
const designPodDefinition = getExistingYMap(yDoc, "designPodDefinition");
|
|
1915
|
+
const designPodState = getExistingYMap(yDoc, "designPodState");
|
|
1916
|
+
const designPodIdentity = designPodState?.get("identity");
|
|
1917
|
+
if (designPodDefinition?.get("manifest") && !designPodIdentity?.sessionRunId) {
|
|
1918
|
+
throw new Error("A Design POD session cannot close or cancel before its project identity is pinned");
|
|
1919
|
+
}
|
|
1920
|
+
const isPinnedDesignPodRun = designPodIdentity?.sessionRunId === runId;
|
|
1921
|
+
const designPodRelease = designPodState?.get("release");
|
|
1922
|
+
if (isPinnedDesignPodRun && designPodRelease?.status !== "published") {
|
|
1923
|
+
throw new Error("The pinned Design POD session cannot close until its current independently reviewed release is published");
|
|
1924
|
+
}
|
|
1925
|
+
if (!isPinnedDesignPodRun) return { pinned: false };
|
|
1926
|
+
const releaseDigest = designPodRelease?.digest;
|
|
1927
|
+
return {
|
|
1928
|
+
pinned: true,
|
|
1929
|
+
snapshot: JSON.stringify(
|
|
1930
|
+
canonicalize({
|
|
1931
|
+
manifest: designPodDefinition?.get("manifest"),
|
|
1932
|
+
identity: designPodIdentity,
|
|
1933
|
+
release: designPodRelease,
|
|
1934
|
+
publication: typeof releaseDigest === "string" ? designPodState?.get(`publication:${releaseDigest}`) : void 0,
|
|
1935
|
+
root: {
|
|
1936
|
+
doc_id: getExistingYMap(yDoc, "root")?.get("doc_id"),
|
|
1937
|
+
flowOwnerDid: getExistingYMap(yDoc, "root")?.get("flowOwnerDid")
|
|
1938
|
+
}
|
|
1939
|
+
})
|
|
1940
|
+
)
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
async function authenticatePinnedDesignPodClosure(params) {
|
|
1944
|
+
const captured = inspectPinnedDesignPodRunForClosure(params.yDoc, params.runId);
|
|
1945
|
+
if (!captured.pinned) return captured;
|
|
1946
|
+
if (!params.verifyDesignPodPublication) {
|
|
1947
|
+
throw new Error("The pinned Design POD session requires authenticated publication verification before close or cancel");
|
|
1948
|
+
}
|
|
1949
|
+
const verification = await params.verifyDesignPodPublication({ yDoc: params.yDoc, runId: params.runId, flowUri: params.flowUri });
|
|
1950
|
+
if (!verification.valid) {
|
|
1951
|
+
throw new Error(`The pinned Design POD publication could not be authenticated${verification.error ? `: ${verification.error}` : ""}`);
|
|
1952
|
+
}
|
|
1953
|
+
return captured;
|
|
1954
|
+
}
|
|
1955
|
+
function incompleteBlocks(summary) {
|
|
1956
|
+
return Object.entries(summary).filter(([, done]) => !done).map(([blockId]) => blockId);
|
|
1957
|
+
}
|
|
1958
|
+
function terminalSummary(value) {
|
|
1959
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1960
|
+
const entries = Object.entries(value);
|
|
1961
|
+
if (entries.some(([, done]) => typeof done !== "boolean")) return void 0;
|
|
1962
|
+
return Object.fromEntries(entries);
|
|
1963
|
+
}
|
|
1964
|
+
function terminalAuthorizationFromEvent(event) {
|
|
1965
|
+
if (event.content.kind !== "run.closed" && event.content.kind !== "run.cancelled") return void 0;
|
|
1966
|
+
const value = event.content.payload.authorization;
|
|
1967
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1968
|
+
const proof = value;
|
|
1969
|
+
const actionInputs = proof.actionInputs;
|
|
1970
|
+
const capability = proof.capability;
|
|
1971
|
+
if (typeof proof.invocationCid !== "string" || typeof proof.invocation !== "string" || typeof proof.actorDid !== "string" || proof.actionType !== "qi/flow.run.close" || typeof proof.actionBlockId !== "string" || typeof proof.inputDigest !== "string" || !actionInputs || typeof actionInputs !== "object" || Array.isArray(actionInputs) || !capability || typeof capability !== "object" || Array.isArray(capability) || typeof capability.can !== "string" || typeof capability.with !== "string" || !Array.isArray(proof.proofCids) || proof.proofCids.some((cid) => typeof cid !== "string")) {
|
|
1972
|
+
return void 0;
|
|
1973
|
+
}
|
|
1974
|
+
return {
|
|
1975
|
+
invocationCid: proof.invocationCid,
|
|
1976
|
+
invocation: proof.invocation,
|
|
1977
|
+
actorDid: proof.actorDid,
|
|
1978
|
+
actionType: "qi/flow.run.close",
|
|
1979
|
+
actionBlockId: proof.actionBlockId,
|
|
1980
|
+
inputDigest: proof.inputDigest,
|
|
1981
|
+
actionInputs,
|
|
1982
|
+
capability,
|
|
1983
|
+
proofCids: [...proof.proofCids]
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
function terminalReservationFromEvent(event, authorization) {
|
|
1987
|
+
if (event.content.kind !== "run.closed" && event.content.kind !== "run.cancelled") return void 0;
|
|
1988
|
+
const summary = terminalSummary(event.content.payload.summary);
|
|
1989
|
+
if (!summary) return void 0;
|
|
1990
|
+
return {
|
|
1991
|
+
reservation: {
|
|
1992
|
+
reservationId: `matrix:${event.eventId}`,
|
|
1993
|
+
status: event.content.kind === "run.closed" ? "closed" : "cancelled",
|
|
1994
|
+
at: event.originServerTs,
|
|
1995
|
+
...authorization?.actorDid || event.content.payload.actorDid ? { by: authorization?.actorDid || event.content.payload.actorDid } : {},
|
|
1996
|
+
...authorization?.invocationCid || event.content.payload.invocationCid ? { closeInvocationCid: authorization?.invocationCid || event.content.payload.invocationCid } : {},
|
|
1997
|
+
...event.content.kind === "run.cancelled" && event.content.payload.reason ? { reason: event.content.payload.reason } : {},
|
|
1998
|
+
summary
|
|
1999
|
+
},
|
|
2000
|
+
eventId: event.eventId
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
function sameTerminalSummary(left, right) {
|
|
2004
|
+
return JSON.stringify(canonicalize(left)) === JSON.stringify(canonicalize(right));
|
|
2005
|
+
}
|
|
2006
|
+
async function authenticatedTerminalFromEvent(event, params) {
|
|
2007
|
+
if (event.content.runId !== params.runId || event.content.kind !== "run.closed" && event.content.kind !== "run.cancelled") return void 0;
|
|
2008
|
+
const authorization = terminalAuthorizationFromEvent(event);
|
|
2009
|
+
if (!authorization || event.content.payload.invocationCid !== authorization.invocationCid) return void 0;
|
|
2010
|
+
if (await digestDesignPodValue(authorization.actionInputs) !== authorization.inputDigest) return void 0;
|
|
2011
|
+
const cancelled = authorization.actionInputs.cancel === true;
|
|
2012
|
+
const expectedStatus = cancelled ? "cancelled" : "closed";
|
|
2013
|
+
const eventStatus = event.content.kind === "run.cancelled" ? "cancelled" : "closed";
|
|
2014
|
+
if (eventStatus !== expectedStatus) return void 0;
|
|
2015
|
+
const expectedReason = cancelled && typeof authorization.actionInputs.reason === "string" ? authorization.actionInputs.reason : void 0;
|
|
2016
|
+
const eventReason = event.content.kind === "run.cancelled" ? event.content.payload.reason : void 0;
|
|
2017
|
+
if (eventReason !== expectedReason) return void 0;
|
|
2018
|
+
const terminal = terminalReservationFromEvent(event, authorization);
|
|
2019
|
+
if (!terminal) return void 0;
|
|
2020
|
+
const authorizedSummary = terminalSummary(authorization.actionInputs.terminalSummary);
|
|
2021
|
+
if (!authorizedSummary || !sameTerminalSummary(terminal.reservation.summary, authorizedSummary)) return void 0;
|
|
2022
|
+
const blockCapability = authorization.capability.can === "flow/block/execute" && authorization.capability.with === `${params.flowUri}:${authorization.actionBlockId}`;
|
|
2023
|
+
const directCapability = authorization.capability.can === "flow/run.close" && authorization.capability.with === params.flowUri;
|
|
2024
|
+
if (!blockCapability && !directCapability) return void 0;
|
|
2025
|
+
const invocation = {
|
|
2026
|
+
cid: authorization.invocationCid,
|
|
2027
|
+
invocation: authorization.invocation,
|
|
2028
|
+
invokerDid: authorization.actorDid,
|
|
2029
|
+
capability: authorization.capability,
|
|
2030
|
+
executedAt: event.originServerTs,
|
|
2031
|
+
flowId: params.flowUri.replace(/^ixo:flow:/, ""),
|
|
2032
|
+
sessionRunId: params.runId,
|
|
2033
|
+
blockId: authorization.actionBlockId,
|
|
2034
|
+
result: "success",
|
|
2035
|
+
proofCids: authorization.proofCids,
|
|
2036
|
+
facts: { actionType: authorization.actionType, inputDigest: authorization.inputDigest, sessionRunId: params.runId }
|
|
2037
|
+
};
|
|
2038
|
+
const verified = await params.verifyInvocation({
|
|
2039
|
+
invocation,
|
|
2040
|
+
expected: {
|
|
2041
|
+
actorDid: authorization.actorDid,
|
|
2042
|
+
actionType: authorization.actionType,
|
|
2043
|
+
blockId: authorization.actionBlockId,
|
|
2044
|
+
inputDigest: authorization.inputDigest,
|
|
2045
|
+
flowUri: params.flowUri,
|
|
2046
|
+
flowOwnerDid: params.flowOwnerDid,
|
|
2047
|
+
sessionRunId: params.runId,
|
|
2048
|
+
capability: authorization.capability
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2051
|
+
return verified.valid ? terminal : void 0;
|
|
2052
|
+
}
|
|
2053
|
+
async function readMatrixTerminalState(eventLog, params) {
|
|
2054
|
+
if (!eventLog.read) throw new Error("Matrix ordered readback is required to elect a terminal transition");
|
|
2055
|
+
let from;
|
|
2056
|
+
const seenTokens = /* @__PURE__ */ new Set();
|
|
2057
|
+
do {
|
|
2058
|
+
const page = await eventLog.read(params.runId, { direction: "forward", limit: 100, ...from ? { from } : {} });
|
|
2059
|
+
for (const event of page.events) {
|
|
2060
|
+
if (event.content.kind !== "run.closed" && event.content.kind !== "run.cancelled") continue;
|
|
2061
|
+
if (params.verifyInvocation) {
|
|
2062
|
+
const authenticated = await authenticatedTerminalFromEvent(event, { ...params, verifyInvocation: params.verifyInvocation });
|
|
2063
|
+
if (authenticated) return { terminal: authenticated };
|
|
2064
|
+
if (terminalAuthorizationFromEvent(event)) continue;
|
|
2065
|
+
}
|
|
2066
|
+
if (!params.requireAuthenticated) {
|
|
2067
|
+
const legacy = terminalReservationFromEvent(event);
|
|
2068
|
+
if (legacy && (!params.legacyExpectedSummary || sameTerminalSummary(legacy.reservation.summary, params.legacyExpectedSummary))) return { terminal: legacy };
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
from = page.nextToken;
|
|
2072
|
+
if (from && seenTokens.has(from)) throw new Error("Matrix repeated a terminal-event pagination token");
|
|
2073
|
+
if (from) seenTokens.add(from);
|
|
2074
|
+
} while (from);
|
|
2075
|
+
return {};
|
|
2076
|
+
}
|
|
2077
|
+
function clearOwnTerminalReservation(yDoc, runId, reservationId) {
|
|
2078
|
+
yDoc.transact(() => {
|
|
2079
|
+
const pending = getExistingRunsTerminalPendingMap(yDoc)?.get(runId);
|
|
2080
|
+
if (pending?.reservationId === reservationId) getRunsTerminalPendingMap(yDoc).delete(runId);
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
async function reconcileTerminalEvent(eventLog, event, reservation, params) {
|
|
2084
|
+
let eventId;
|
|
2085
|
+
let appendError;
|
|
2086
|
+
try {
|
|
2087
|
+
eventId = await eventLog.append(event);
|
|
2088
|
+
} catch (error) {
|
|
2089
|
+
appendError = error;
|
|
2090
|
+
}
|
|
2091
|
+
if (eventLog.read) {
|
|
2092
|
+
const state = await readMatrixTerminalState(eventLog, {
|
|
2093
|
+
runId: event.runId,
|
|
2094
|
+
flowUri: params.flowUri,
|
|
2095
|
+
flowOwnerDid: params.flowOwnerDid,
|
|
2096
|
+
legacyExpectedSummary: reservation.summary,
|
|
2097
|
+
verifyInvocation: params.verifyInvocation,
|
|
2098
|
+
requireAuthenticated: params.requireAuthenticated
|
|
2099
|
+
});
|
|
2100
|
+
if (state.terminal) return state.terminal;
|
|
2101
|
+
if (appendError) throw appendError;
|
|
2102
|
+
throw new Error("Matrix did not retain an authoritative terminal event");
|
|
2103
|
+
}
|
|
2104
|
+
if (eventId) return { reservation, eventId };
|
|
2105
|
+
throw appendError;
|
|
2106
|
+
}
|
|
2107
|
+
function prepareTerminalTransition(params, status, authorization, now, pinnedDesignPod, reason) {
|
|
2108
|
+
let existing;
|
|
2109
|
+
let reservation;
|
|
2110
|
+
const matrixAuthoritative = pinnedDesignPod.pinned || Boolean(params.terminalAuthorization);
|
|
2111
|
+
params.yDoc.transact(() => {
|
|
2112
|
+
const current = getExistingRunsTerminalMap(params.yDoc)?.get(params.runId);
|
|
2113
|
+
if (current && !matrixAuthoritative) {
|
|
2114
|
+
existing = current;
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
if (pinnedDesignPod.pinned) {
|
|
2118
|
+
const current2 = inspectPinnedDesignPodRunForClosure(params.yDoc, params.runId);
|
|
2119
|
+
if (current2.snapshot !== pinnedDesignPod.snapshot) {
|
|
2120
|
+
throw new Error("The authenticated Design POD publication changed before its terminal transition could be reserved");
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
const finalSummary = computeRunSummary(params.yDoc, params.runId, params.actionTypes, {
|
|
2124
|
+
flowId: params.flowUri,
|
|
2125
|
+
actorDid: params.actorDid,
|
|
2126
|
+
now
|
|
2127
|
+
});
|
|
2128
|
+
const incomplete = incompleteBlocks(finalSummary);
|
|
2129
|
+
if (status === "closed" && incomplete.length > 0 && (!params.allowIncomplete || pinnedDesignPod.pinned)) {
|
|
2130
|
+
throw new RunNotReadyError(incomplete);
|
|
2131
|
+
}
|
|
2132
|
+
reservation = {
|
|
2133
|
+
reservationId: `${status}:${params.runId}:${createRunUlid(now)}`,
|
|
2134
|
+
status,
|
|
2135
|
+
at: now,
|
|
2136
|
+
by: params.actorDid,
|
|
2137
|
+
...params.terminalAuthorization?.invocationCid || authorization.invocationCid ? { closeInvocationCid: params.terminalAuthorization?.invocationCid || authorization.invocationCid } : {},
|
|
2138
|
+
...reason ? { reason } : {},
|
|
2139
|
+
summary: finalSummary
|
|
2140
|
+
};
|
|
2141
|
+
});
|
|
2142
|
+
return { reservation, existing };
|
|
2143
|
+
}
|
|
2144
|
+
function installTerminalReservation(params, reservation, pinnedDesignPod) {
|
|
2145
|
+
let existing;
|
|
2146
|
+
const matrixAuthoritative = pinnedDesignPod.pinned || Boolean(params.terminalAuthorization);
|
|
2147
|
+
params.yDoc.transact(() => {
|
|
2148
|
+
const current = getExistingRunsTerminalMap(params.yDoc)?.get(params.runId);
|
|
2149
|
+
if (current && !matrixAuthoritative) {
|
|
2150
|
+
existing = current;
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
if (pinnedDesignPod.pinned) {
|
|
2154
|
+
const current2 = inspectPinnedDesignPodRunForClosure(params.yDoc, params.runId);
|
|
2155
|
+
if (current2.snapshot !== pinnedDesignPod.snapshot) {
|
|
2156
|
+
throw new Error("The authenticated Design POD publication changed before its terminal transition could be reserved");
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
const finalSummary = computeRunSummary(params.yDoc, params.runId, params.actionTypes, {
|
|
2160
|
+
flowId: params.flowUri,
|
|
2161
|
+
actorDid: params.actorDid,
|
|
2162
|
+
now: reservation.at
|
|
2163
|
+
});
|
|
2164
|
+
const incomplete = incompleteBlocks(finalSummary);
|
|
2165
|
+
if (reservation.status === "closed" && incomplete.length > 0 && (!params.allowIncomplete || pinnedDesignPod.pinned)) {
|
|
2166
|
+
throw new RunNotReadyError(incomplete);
|
|
2167
|
+
}
|
|
2168
|
+
if (JSON.stringify(canonicalize(finalSummary)) !== JSON.stringify(canonicalize(reservation.summary))) {
|
|
2169
|
+
throw new Error("The run completion summary changed while its terminal event was being prepared");
|
|
2170
|
+
}
|
|
2171
|
+
getRunsTerminalPendingMap(params.yDoc).set(params.runId, reservation);
|
|
2172
|
+
});
|
|
2173
|
+
return existing;
|
|
2174
|
+
}
|
|
2175
|
+
function finalizeTerminalTransition(yDoc, runId, reservation, eventId, matrixAuthoritative = false) {
|
|
2176
|
+
const terminal = {
|
|
2177
|
+
status: reservation.status,
|
|
2178
|
+
at: reservation.at,
|
|
2179
|
+
by: reservation.by,
|
|
2180
|
+
terminalEventId: eventId,
|
|
2181
|
+
...reservation.closeInvocationCid ? { closeInvocationCid: reservation.closeInvocationCid } : {},
|
|
2182
|
+
summary: reservation.summary
|
|
2183
|
+
};
|
|
2184
|
+
let created = false;
|
|
2185
|
+
yDoc.transact(() => {
|
|
2186
|
+
const terminalMap = getRunsTerminalMap(yDoc);
|
|
2187
|
+
if (matrixAuthoritative || !terminalMap.has(runId)) {
|
|
2188
|
+
created = !terminalMap.has(runId);
|
|
2189
|
+
terminalMap.set(runId, terminal);
|
|
2190
|
+
}
|
|
2191
|
+
if (getExistingRunsTerminalPendingMap(yDoc)?.has(runId)) getRunsTerminalPendingMap(yDoc).delete(runId);
|
|
2192
|
+
});
|
|
2193
|
+
const winner = getRunsTerminalMap(yDoc).get(runId) || terminal;
|
|
2194
|
+
return {
|
|
2195
|
+
terminal: { ...winner, summary: winner.summary ? { ...winner.summary } : void 0 },
|
|
2196
|
+
created,
|
|
2197
|
+
eventId: winner.terminalEventId || eventId
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
async function closeRun(params) {
|
|
2201
|
+
return withLifecycleLock(params.yDoc, params.runId, async () => {
|
|
2202
|
+
const existing = getExistingRunsTerminalMap(params.yDoc)?.get(params.runId);
|
|
2203
|
+
const initialPinnedState = inspectPinnedDesignPodRunForClosure(params.yDoc, params.runId);
|
|
2204
|
+
if (existing && !initialPinnedState.pinned && !params.terminalAuthorization) {
|
|
2205
|
+
return {
|
|
2206
|
+
terminal: {
|
|
2207
|
+
...existing,
|
|
2208
|
+
summary: existing.summary ? { ...existing.summary } : void 0
|
|
2209
|
+
},
|
|
2210
|
+
created: false,
|
|
2211
|
+
...existing.terminalEventId ? { eventId: existing.terminalEventId } : {}
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
const meta = getRunMeta(params.yDoc, params.runId);
|
|
2215
|
+
if (!meta) throw new Error(`Unknown run "${params.runId}"`);
|
|
2216
|
+
const authorization = await requireAuthorization(params.authorize, {
|
|
2217
|
+
can: "flow/run.close",
|
|
2218
|
+
with: params.flowUri,
|
|
2219
|
+
actorDid: params.actorDid,
|
|
2220
|
+
runId: params.runId
|
|
2221
|
+
});
|
|
2222
|
+
const now = params.now?.() ?? Date.now();
|
|
2223
|
+
const summary = computeRunSummary(params.yDoc, params.runId, params.actionTypes, {
|
|
2224
|
+
flowId: params.flowUri,
|
|
2225
|
+
actorDid: params.actorDid,
|
|
2226
|
+
now
|
|
2227
|
+
});
|
|
2228
|
+
const incomplete = incompleteBlocks(summary);
|
|
2229
|
+
const pinnedDesignPod = await authenticatePinnedDesignPodClosure(params);
|
|
2230
|
+
const authenticatedTerminalRequired = pinnedDesignPod.pinned || Boolean(params.terminalAuthorization);
|
|
2231
|
+
if (authenticatedTerminalRequired && !params.eventLog.read) {
|
|
2232
|
+
throw new Error("Matrix ordered readback is required to elect an authenticated terminal transition");
|
|
2233
|
+
}
|
|
2234
|
+
if (authenticatedTerminalRequired && (!params.terminalAuthorization || !params.verifyTerminalInvocation)) {
|
|
2235
|
+
throw new Error("This terminal transition requires a cryptographically verified signed Flow invocation");
|
|
2236
|
+
}
|
|
2237
|
+
if (authenticatedTerminalRequired) {
|
|
2238
|
+
const retained = await readMatrixTerminalState(params.eventLog, {
|
|
2239
|
+
runId: params.runId,
|
|
2240
|
+
flowUri: params.flowUri,
|
|
2241
|
+
flowOwnerDid: String(getExistingYMap(params.yDoc, "root")?.get("flowOwnerDid") || ""),
|
|
2242
|
+
verifyInvocation: params.verifyTerminalInvocation,
|
|
2243
|
+
requireAuthenticated: true
|
|
2244
|
+
});
|
|
2245
|
+
if (retained.terminal) {
|
|
2246
|
+
return finalizeTerminalTransition(params.yDoc, params.runId, retained.terminal.reservation, retained.terminal.eventId, true);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
if (incomplete.length > 0 && (!params.allowIncomplete || pinnedDesignPod.pinned)) {
|
|
2250
|
+
throw new RunNotReadyError(incomplete);
|
|
2251
|
+
}
|
|
2252
|
+
const transition = prepareTerminalTransition(params, "closed", authorization, now, pinnedDesignPod);
|
|
2253
|
+
if (transition.existing) {
|
|
2254
|
+
return {
|
|
2255
|
+
terminal: { ...transition.existing, summary: transition.existing.summary ? { ...transition.existing.summary } : void 0 },
|
|
2256
|
+
created: false,
|
|
2257
|
+
...transition.existing.terminalEventId ? { eventId: transition.existing.terminalEventId } : {}
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
const reservation = transition.reservation;
|
|
2261
|
+
const installedTerminal = installTerminalReservation(params, reservation, pinnedDesignPod);
|
|
2262
|
+
if (installedTerminal) {
|
|
2263
|
+
return {
|
|
2264
|
+
terminal: { ...installedTerminal, summary: installedTerminal.summary ? { ...installedTerminal.summary } : void 0 },
|
|
2265
|
+
created: false,
|
|
2266
|
+
...installedTerminal.terminalEventId ? { eventId: installedTerminal.terminalEventId } : {}
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
const event = {
|
|
2270
|
+
runId: params.runId,
|
|
2271
|
+
kind: "run.closed",
|
|
2272
|
+
payload: {
|
|
2273
|
+
...reservation.closeInvocationCid ? { invocationCid: reservation.closeInvocationCid } : {},
|
|
2274
|
+
...reservation.by ? { actorDid: reservation.by } : {},
|
|
2275
|
+
summary: reservation.summary,
|
|
2276
|
+
...params.terminalAuthorization ? { authorization: toRunJsonValue(params.terminalAuthorization) } : {}
|
|
2277
|
+
},
|
|
2278
|
+
idempotencyKey: createRunEventIdempotencyKey("run.closed", params.runId, reservation.closeInvocationCid || reservation.reservationId),
|
|
2279
|
+
ts: reservation.at
|
|
2280
|
+
};
|
|
2281
|
+
try {
|
|
2282
|
+
const reconciled = await reconcileTerminalEvent(params.eventLog, event, reservation, {
|
|
2283
|
+
flowUri: params.flowUri,
|
|
2284
|
+
flowOwnerDid: String(getExistingYMap(params.yDoc, "root")?.get("flowOwnerDid") || ""),
|
|
2285
|
+
verifyInvocation: params.verifyTerminalInvocation,
|
|
2286
|
+
requireAuthenticated: authenticatedTerminalRequired
|
|
2287
|
+
});
|
|
2288
|
+
return finalizeTerminalTransition(params.yDoc, params.runId, reconciled.reservation, reconciled.eventId, authenticatedTerminalRequired);
|
|
2289
|
+
} catch (error) {
|
|
2290
|
+
clearOwnTerminalReservation(params.yDoc, params.runId, reservation.reservationId);
|
|
2291
|
+
throw error;
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
async function cancelRun(params) {
|
|
2296
|
+
return withLifecycleLock(params.yDoc, params.runId, async () => {
|
|
2297
|
+
const existing = getExistingRunsTerminalMap(params.yDoc)?.get(params.runId);
|
|
2298
|
+
const initialPinnedState = inspectPinnedDesignPodRunForClosure(params.yDoc, params.runId);
|
|
2299
|
+
if (existing && !initialPinnedState.pinned && !params.terminalAuthorization) {
|
|
2300
|
+
return {
|
|
2301
|
+
terminal: {
|
|
2302
|
+
...existing,
|
|
2303
|
+
summary: existing.summary ? { ...existing.summary } : void 0
|
|
2304
|
+
},
|
|
2305
|
+
created: false,
|
|
2306
|
+
...existing.terminalEventId ? { eventId: existing.terminalEventId } : {}
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
if (!getRunMeta(params.yDoc, params.runId)) {
|
|
2310
|
+
throw new Error(`Unknown run "${params.runId}"`);
|
|
2311
|
+
}
|
|
2312
|
+
const authorization = await requireAuthorization(params.authorize, {
|
|
2313
|
+
can: "flow/run.close",
|
|
2314
|
+
with: params.flowUri,
|
|
2315
|
+
actorDid: params.actorDid,
|
|
2316
|
+
runId: params.runId
|
|
2317
|
+
});
|
|
2318
|
+
const now = params.now?.() ?? Date.now();
|
|
2319
|
+
const pinnedDesignPod = await authenticatePinnedDesignPodClosure(params);
|
|
2320
|
+
const authenticatedTerminalRequired = pinnedDesignPod.pinned || Boolean(params.terminalAuthorization);
|
|
2321
|
+
if (authenticatedTerminalRequired && !params.eventLog.read) {
|
|
2322
|
+
throw new Error("Matrix ordered readback is required to elect an authenticated terminal transition");
|
|
2323
|
+
}
|
|
2324
|
+
if (authenticatedTerminalRequired && (!params.terminalAuthorization || !params.verifyTerminalInvocation)) {
|
|
2325
|
+
throw new Error("This terminal transition requires a cryptographically verified signed Flow invocation");
|
|
2326
|
+
}
|
|
2327
|
+
if (authenticatedTerminalRequired) {
|
|
2328
|
+
const retained = await readMatrixTerminalState(params.eventLog, {
|
|
2329
|
+
runId: params.runId,
|
|
2330
|
+
flowUri: params.flowUri,
|
|
2331
|
+
flowOwnerDid: String(getExistingYMap(params.yDoc, "root")?.get("flowOwnerDid") || ""),
|
|
2332
|
+
verifyInvocation: params.verifyTerminalInvocation,
|
|
2333
|
+
requireAuthenticated: true
|
|
2334
|
+
});
|
|
2335
|
+
if (retained.terminal) {
|
|
2336
|
+
return finalizeTerminalTransition(params.yDoc, params.runId, retained.terminal.reservation, retained.terminal.eventId, true);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
const transition = prepareTerminalTransition(params, "cancelled", authorization, now, pinnedDesignPod, params.reason);
|
|
2340
|
+
if (transition.existing) {
|
|
2341
|
+
return {
|
|
2342
|
+
terminal: { ...transition.existing, summary: transition.existing.summary ? { ...transition.existing.summary } : void 0 },
|
|
2343
|
+
created: false,
|
|
2344
|
+
...transition.existing.terminalEventId ? { eventId: transition.existing.terminalEventId } : {}
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
const reservation = transition.reservation;
|
|
2348
|
+
const installedTerminal = installTerminalReservation(params, reservation, pinnedDesignPod);
|
|
2349
|
+
if (installedTerminal) {
|
|
2350
|
+
return {
|
|
2351
|
+
terminal: { ...installedTerminal, summary: installedTerminal.summary ? { ...installedTerminal.summary } : void 0 },
|
|
2352
|
+
created: false,
|
|
2353
|
+
...installedTerminal.terminalEventId ? { eventId: installedTerminal.terminalEventId } : {}
|
|
2354
|
+
};
|
|
2355
|
+
}
|
|
2356
|
+
const event = {
|
|
2357
|
+
runId: params.runId,
|
|
2358
|
+
kind: "run.cancelled",
|
|
2359
|
+
payload: {
|
|
2360
|
+
...reservation.closeInvocationCid ? { invocationCid: reservation.closeInvocationCid } : {},
|
|
2361
|
+
...reservation.by ? { actorDid: reservation.by } : {},
|
|
2362
|
+
...reservation.reason ? { reason: reservation.reason } : {},
|
|
2363
|
+
summary: reservation.summary,
|
|
2364
|
+
...params.terminalAuthorization ? { authorization: toRunJsonValue(params.terminalAuthorization) } : {}
|
|
2365
|
+
},
|
|
2366
|
+
idempotencyKey: createRunEventIdempotencyKey("run.closed", params.runId, reservation.closeInvocationCid || reservation.reservationId),
|
|
2367
|
+
ts: reservation.at
|
|
2368
|
+
};
|
|
2369
|
+
try {
|
|
2370
|
+
const reconciled = await reconcileTerminalEvent(params.eventLog, event, reservation, {
|
|
2371
|
+
flowUri: params.flowUri,
|
|
2372
|
+
flowOwnerDid: String(getExistingYMap(params.yDoc, "root")?.get("flowOwnerDid") || ""),
|
|
2373
|
+
verifyInvocation: params.verifyTerminalInvocation,
|
|
2374
|
+
requireAuthenticated: authenticatedTerminalRequired
|
|
2375
|
+
});
|
|
2376
|
+
return finalizeTerminalTransition(params.yDoc, params.runId, reconciled.reservation, reconciled.eventId, authenticatedTerminalRequired);
|
|
2377
|
+
} catch (error) {
|
|
2378
|
+
clearOwnTerminalReservation(params.yDoc, params.runId, reservation.reservationId);
|
|
2379
|
+
throw error;
|
|
2380
|
+
}
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
function detectRunDefinitionDrift(yDoc, runId, definition) {
|
|
2384
|
+
const manifest = getRunMeta(yDoc, runId)?.manifest;
|
|
2385
|
+
if (!manifest) throw new RunNotMigratedError(runId);
|
|
2386
|
+
const before = new Set(manifest.blockIds);
|
|
2387
|
+
const after = new Set(definition.manifest.blockIds);
|
|
2388
|
+
const changedBlockIds = [.../* @__PURE__ */ new Set([...before, ...after])].filter((id) => before.has(id) !== after.has(id));
|
|
2389
|
+
return {
|
|
2390
|
+
// With no frozen hash — a run the flow manager opened — the step list is
|
|
2391
|
+
// the whole of what can be compared. It catches a step added or removed
|
|
2392
|
+
// and misses one whose configuration was edited in place, which is less
|
|
2393
|
+
// than the hash sees and more than a hash comparison against a differently
|
|
2394
|
+
// computed value would be worth: that would read "changed" always, on
|
|
2395
|
+
// every scheduled run, and mean nothing.
|
|
2396
|
+
changed: manifest.defHash === void 0 ? changedBlockIds.length > 0 : manifest.defHash !== definition.manifest.defHash,
|
|
2397
|
+
previousRevision: manifest.defHash,
|
|
2398
|
+
revision: definition.manifest.defHash,
|
|
2399
|
+
changedBlockIds
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
async function recordRunDefinitionDrift(yDoc, runId, definition, eventLog, now = Date.now()) {
|
|
2403
|
+
const drift = detectRunDefinitionDrift(yDoc, runId, definition);
|
|
2404
|
+
if (!drift.changed) return drift;
|
|
2405
|
+
const eventId = await eventLog.append({
|
|
2406
|
+
runId,
|
|
2407
|
+
kind: "definition.changed",
|
|
2408
|
+
payload: {
|
|
2409
|
+
revision: drift.revision,
|
|
2410
|
+
previousRevision: drift.previousRevision,
|
|
2411
|
+
changedBlockIds: drift.changedBlockIds,
|
|
2412
|
+
summary: "The live flow definition changed after this session started."
|
|
2413
|
+
},
|
|
2414
|
+
idempotencyKey: createRunEventIdempotencyKey("definition.changed", runId, drift.revision),
|
|
2415
|
+
ts: now
|
|
2416
|
+
});
|
|
2417
|
+
return { ...drift, eventId };
|
|
2418
|
+
}
|
|
2419
|
+
function readRunActionStates(yDoc, runId, blockIds) {
|
|
2420
|
+
return Object.fromEntries(blockIds.map((blockId) => [blockId, readActionState(yDoc, runId, blockId)]));
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// src/core/lib/flowCompiler/resolveRefs.ts
|
|
2424
|
+
function resolveRuntimeRefs(nb, getNodeOutput2, triggerContext) {
|
|
2425
|
+
return resolveValue(nb, getNodeOutput2, triggerContext);
|
|
2426
|
+
}
|
|
2427
|
+
function resolveValue(value, getNodeOutput2, triggerContext) {
|
|
2428
|
+
if (isRuntimeRef(value)) {
|
|
2429
|
+
return resolveRef(value.$ref, getNodeOutput2, triggerContext);
|
|
2430
|
+
}
|
|
2431
|
+
if (Array.isArray(value)) {
|
|
2432
|
+
return value.map((item) => resolveValue(item, getNodeOutput2, triggerContext));
|
|
2433
|
+
}
|
|
2434
|
+
if (typeof value === "object" && value !== null) {
|
|
2435
|
+
const result = {};
|
|
2436
|
+
for (const [key, val] of Object.entries(value)) {
|
|
2437
|
+
result[key] = resolveValue(val, getNodeOutput2, triggerContext);
|
|
2438
|
+
}
|
|
2439
|
+
return result;
|
|
2440
|
+
}
|
|
2441
|
+
return value;
|
|
2442
|
+
}
|
|
2443
|
+
function resolveRef(ref, getNodeOutput2, triggerContext) {
|
|
2444
|
+
if (ref.startsWith("trigger.payload.")) {
|
|
2445
|
+
if (!triggerContext) {
|
|
2446
|
+
throw new Error(`Trigger ref "${ref}" used outside of a listener invocation context. trigger.payload.* refs are only valid on block.event-triggered blocks.`);
|
|
2447
|
+
}
|
|
2448
|
+
const fieldPath2 = ref.slice("trigger.payload.".length);
|
|
2449
|
+
return getNestedValue(triggerContext.payload, fieldPath2);
|
|
2450
|
+
}
|
|
2451
|
+
if (triggerContext && Object.prototype.hasOwnProperty.call(triggerContext.refSnapshots, ref)) {
|
|
2452
|
+
return triggerContext.refSnapshots[ref];
|
|
2453
|
+
}
|
|
2454
|
+
const outputIndex = ref.indexOf(".output.");
|
|
2455
|
+
if (outputIndex === -1) {
|
|
2456
|
+
throw new Error(`Invalid runtime reference "${ref}". Expected format: "nodeId.output.fieldPath" or "trigger.payload.fieldPath"`);
|
|
2457
|
+
}
|
|
2458
|
+
const nodeId = ref.slice(0, outputIndex);
|
|
2459
|
+
const fieldPath = ref.slice(outputIndex + ".output.".length);
|
|
2460
|
+
const output = getNodeOutput2(nodeId);
|
|
2461
|
+
if (!output) {
|
|
2462
|
+
return void 0;
|
|
2463
|
+
}
|
|
2464
|
+
return getNestedValue(output, fieldPath);
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
// src/core/lib/flowEngine/authorization.ts
|
|
2468
|
+
var isAuthorized = async (blockId, actorDid, ucanService, flowUri, schemaVersion, options = {}) => {
|
|
2469
|
+
const policy = schemaVersion ? getVersionPolicy(schemaVersion) : null;
|
|
2470
|
+
const requireUcan = options.requireSignedInvocation === true;
|
|
2471
|
+
if (policy && !policy.ucanRequired && !requireUcan) {
|
|
2472
|
+
return { authorized: true };
|
|
2473
|
+
}
|
|
2474
|
+
if (!ucanService) {
|
|
2475
|
+
if (!policy && !requireUcan) {
|
|
2476
|
+
return { authorized: true };
|
|
2477
|
+
}
|
|
2478
|
+
return {
|
|
2479
|
+
authorized: false,
|
|
2480
|
+
reason: requireUcan ? "UCAN service is not configured. This governed action requires a signed Flow invocation." : "UCAN service is not configured. This flow version requires UCAN authorization."
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
const capability = {
|
|
2484
|
+
can: "flow/block/execute",
|
|
2485
|
+
with: `${flowUri}:${blockId}`
|
|
2486
|
+
};
|
|
2487
|
+
const result = await ucanService.validateDelegationChain(actorDid, capability);
|
|
2488
|
+
if (!result.valid) {
|
|
2489
|
+
return {
|
|
2490
|
+
authorized: false,
|
|
2491
|
+
reason: result.error || "No valid capability chain found"
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
const proofCids = result.proofChain?.map((d) => d.cid) || [];
|
|
2495
|
+
if (requireUcan && proofCids.length === 0) {
|
|
2496
|
+
return {
|
|
2497
|
+
authorized: false,
|
|
2498
|
+
reason: "A signed invocation requires a non-empty UCAN delegation proof chain."
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
return {
|
|
2502
|
+
authorized: true,
|
|
2503
|
+
capabilityId: proofCids[0],
|
|
2504
|
+
proofCids
|
|
2505
|
+
};
|
|
2506
|
+
};
|
|
2507
|
+
|
|
2508
|
+
// src/core/lib/flowEngine/executor.ts
|
|
2509
|
+
var updateRuntimeAfterSuccess = (node, actorDid, runtime, actionResult, invocationCid, now) => {
|
|
2510
|
+
const updates = {
|
|
2511
|
+
submittedByDid: actionResult.submittedByDid || actorDid,
|
|
2512
|
+
evaluationStatus: actionResult.evaluationStatus || "pending",
|
|
2513
|
+
executionTimestamp: now ? now() : Date.now(),
|
|
2514
|
+
lastInvocationCid: invocationCid
|
|
2515
|
+
};
|
|
2516
|
+
if (actionResult.claimId) {
|
|
2517
|
+
updates.claimId = actionResult.claimId;
|
|
2518
|
+
}
|
|
2519
|
+
runtime.update(node.id, updates);
|
|
2520
|
+
};
|
|
2521
|
+
var executeNode = async ({
|
|
2522
|
+
node,
|
|
2523
|
+
actorDid,
|
|
2524
|
+
actorType,
|
|
2525
|
+
entityRoomId,
|
|
2526
|
+
context,
|
|
2527
|
+
action,
|
|
2528
|
+
invocationFacts = {},
|
|
2529
|
+
requireSignedInvocation = false,
|
|
2530
|
+
pin
|
|
2531
|
+
}) => {
|
|
2532
|
+
const { runtime, ucanService, invocationStore, flowUri, flowId, sessionRunId, schemaVersion, now } = context;
|
|
2533
|
+
const auth = await isAuthorized(node.id, actorDid, ucanService, flowUri, schemaVersion, { requireSignedInvocation });
|
|
2534
|
+
if (!auth.authorized) {
|
|
2535
|
+
return { success: false, stage: "authorization", error: auth.reason };
|
|
2536
|
+
}
|
|
2537
|
+
if (requireSignedInvocation && !invocationStore) {
|
|
2538
|
+
return { success: false, stage: "authorization", error: "A persisted invocation store is required for this governed action." };
|
|
2539
|
+
}
|
|
2540
|
+
if (node.linkedClaim && !node.linkedClaim.collectionId) {
|
|
2541
|
+
return { success: false, stage: "claim", error: "Linked claim collection is required but missing." };
|
|
2542
|
+
}
|
|
2543
|
+
let invocationCid;
|
|
2544
|
+
let invocationData;
|
|
2545
|
+
if (ucanService && auth.proofCids && auth.proofCids.length > 0) {
|
|
2546
|
+
const capability = {
|
|
2547
|
+
can: "flow/block/execute",
|
|
2548
|
+
with: `${flowUri}:${node.id}`
|
|
2549
|
+
};
|
|
2550
|
+
try {
|
|
2551
|
+
const invocationResult = await ucanService.createAndValidateInvocation(
|
|
2552
|
+
{
|
|
2553
|
+
invokerDid: actorDid,
|
|
2554
|
+
invokerType: actorType,
|
|
2555
|
+
entityRoomId,
|
|
2556
|
+
capability,
|
|
2557
|
+
proofs: auth.proofCids,
|
|
2558
|
+
facts: invocationFacts,
|
|
2559
|
+
pin
|
|
2560
|
+
},
|
|
2561
|
+
flowId,
|
|
2562
|
+
node.id
|
|
2563
|
+
);
|
|
2564
|
+
if (!invocationResult.valid) {
|
|
2565
|
+
return {
|
|
2566
|
+
success: false,
|
|
2567
|
+
stage: "authorization",
|
|
2568
|
+
error: `Invocation validation failed: ${invocationResult.error}`
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
invocationCid = invocationResult.cid;
|
|
2572
|
+
invocationData = invocationResult.invocation;
|
|
2573
|
+
} catch (error) {
|
|
2574
|
+
const message = error instanceof Error ? error.message : "Failed to create invocation";
|
|
2575
|
+
return { success: false, stage: "authorization", error: message };
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
if (requireSignedInvocation && (!invocationCid || !invocationData)) {
|
|
2579
|
+
return { success: false, stage: "authorization", error: "This governed action requires a signed Flow invocation." };
|
|
2580
|
+
}
|
|
2581
|
+
try {
|
|
2582
|
+
const result = await action({
|
|
2583
|
+
invocationCid,
|
|
2584
|
+
invocation: invocationData,
|
|
2585
|
+
capabilityId: auth.capabilityId,
|
|
2586
|
+
proofCids: auth.proofCids || [],
|
|
2587
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
2588
|
+
facts: invocationFacts
|
|
2589
|
+
});
|
|
2590
|
+
if (node.linkedClaim && !result.claimId) {
|
|
2591
|
+
if (invocationStore && invocationCid && invocationData) {
|
|
2592
|
+
const storedInvocation = {
|
|
2593
|
+
cid: invocationCid,
|
|
2594
|
+
invocation: invocationData,
|
|
2595
|
+
invokerDid: actorDid,
|
|
2596
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
2597
|
+
executedAt: now ? now() : Date.now(),
|
|
2598
|
+
flowId,
|
|
2599
|
+
sessionRunId,
|
|
2600
|
+
blockId: node.id,
|
|
2601
|
+
result: "failure",
|
|
2602
|
+
error: "Execution did not return a claimId for linked claim requirement.",
|
|
2603
|
+
proofCids: auth.proofCids || [],
|
|
2604
|
+
facts: invocationFacts
|
|
2605
|
+
};
|
|
2606
|
+
invocationStore.add(storedInvocation);
|
|
2607
|
+
}
|
|
2608
|
+
return {
|
|
2609
|
+
success: false,
|
|
2610
|
+
stage: "claim",
|
|
2611
|
+
error: "Execution did not return a claimId for linked claim requirement.",
|
|
2612
|
+
invocationCid
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
if (invocationStore && invocationCid && invocationData) {
|
|
2616
|
+
const storedInvocation = {
|
|
2617
|
+
cid: invocationCid,
|
|
2618
|
+
invocation: invocationData,
|
|
2619
|
+
invokerDid: actorDid,
|
|
2620
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
2621
|
+
executedAt: now ? now() : Date.now(),
|
|
2622
|
+
flowId,
|
|
2623
|
+
sessionRunId,
|
|
2624
|
+
blockId: node.id,
|
|
2625
|
+
result: "success",
|
|
2626
|
+
proofCids: auth.proofCids || [],
|
|
2627
|
+
facts: invocationFacts,
|
|
2628
|
+
claimId: result.claimId
|
|
2629
|
+
};
|
|
2630
|
+
invocationStore.add(storedInvocation);
|
|
2631
|
+
}
|
|
2632
|
+
updateRuntimeAfterSuccess(node, actorDid, runtime, result, invocationCid || auth.capabilityId, now);
|
|
2633
|
+
return {
|
|
2634
|
+
success: true,
|
|
2635
|
+
stage: "complete",
|
|
2636
|
+
result,
|
|
2637
|
+
capabilityId: auth.capabilityId,
|
|
2638
|
+
invocationCid
|
|
2639
|
+
};
|
|
2640
|
+
} catch (error) {
|
|
2641
|
+
const message = error instanceof Error ? error.message : "Execution failed";
|
|
2642
|
+
if (invocationStore && invocationCid && invocationData) {
|
|
2643
|
+
const storedInvocation = {
|
|
2644
|
+
cid: invocationCid,
|
|
2645
|
+
invocation: invocationData,
|
|
2646
|
+
invokerDid: actorDid,
|
|
2647
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
2648
|
+
executedAt: now ? now() : Date.now(),
|
|
2649
|
+
flowId,
|
|
2650
|
+
sessionRunId,
|
|
2651
|
+
blockId: node.id,
|
|
2652
|
+
result: "failure",
|
|
2653
|
+
error: message,
|
|
2654
|
+
proofCids: auth.proofCids || [],
|
|
2655
|
+
facts: invocationFacts
|
|
2656
|
+
};
|
|
2657
|
+
invocationStore.add(storedInvocation);
|
|
2658
|
+
}
|
|
2659
|
+
return { success: false, stage: "action", error: message, invocationCid };
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
|
|
2663
|
+
// src/core/lib/flowEngine/readBackReconciler.ts
|
|
2664
|
+
async function appendReadBackTimeline(eventLog, event, runtime, blockId, now) {
|
|
2665
|
+
const result = await appendRunTimelineEvent({
|
|
2666
|
+
eventLog,
|
|
2667
|
+
event,
|
|
2668
|
+
runtime,
|
|
2669
|
+
blockId,
|
|
2670
|
+
now
|
|
2671
|
+
});
|
|
2672
|
+
return result.ok;
|
|
2673
|
+
}
|
|
2674
|
+
function isYDoc(value) {
|
|
2675
|
+
return isYDocLike(value);
|
|
2676
|
+
}
|
|
2677
|
+
function getYDoc(editorOrYDoc) {
|
|
2678
|
+
if (!editorOrYDoc) return void 0;
|
|
2679
|
+
if (isYDoc(editorOrYDoc)) return editorOrYDoc;
|
|
2680
|
+
return editorOrYDoc._yDoc;
|
|
2681
|
+
}
|
|
2682
|
+
function getEditor(editorOrYDoc) {
|
|
2683
|
+
return editorOrYDoc && !isYDoc(editorOrYDoc) ? editorOrYDoc : void 0;
|
|
2684
|
+
}
|
|
2685
|
+
function getRuntime(editorOrYDoc, runtime, runId) {
|
|
2686
|
+
if (runtime) return runtime;
|
|
2687
|
+
const yDoc = getYDoc(editorOrYDoc);
|
|
2688
|
+
if (yDoc) return createRunScopedRuntimeManager(yDoc, resolveRunIdForExecution(yDoc, runId));
|
|
2689
|
+
return createRuntimeStateManager(!isYDoc(editorOrYDoc) ? editorOrYDoc : void 0);
|
|
2690
|
+
}
|
|
2691
|
+
function getReconcileEditor(params, yDoc, editor) {
|
|
2692
|
+
if (editor) return editor;
|
|
2693
|
+
if (!yDoc || !params.document) return void 0;
|
|
2694
|
+
return {
|
|
2695
|
+
_yDoc: yDoc,
|
|
2696
|
+
_yRuntime: createRunRuntimeReaderWithDoc(yDoc, resolveRunIdForExecution(yDoc, params.runId)),
|
|
2697
|
+
document: params.document
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
function makeExecutionId(now) {
|
|
2701
|
+
return `readback-${now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2702
|
+
}
|
|
2703
|
+
function asOutput(value) {
|
|
2704
|
+
return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
|
|
2705
|
+
}
|
|
2706
|
+
function boundActionEvents(events) {
|
|
2707
|
+
return events.map((event) => ({
|
|
2708
|
+
name: event.name,
|
|
2709
|
+
payload: boundRunEventJson(event.payload)
|
|
2710
|
+
}));
|
|
2711
|
+
}
|
|
2712
|
+
function errorRecord(error) {
|
|
2713
|
+
if (!error) return void 0;
|
|
2714
|
+
return typeof error === "string" ? { message: error } : { message: error.message, code: error.code };
|
|
2715
|
+
}
|
|
2716
|
+
function normalizeActionReadBackMetadata(params) {
|
|
2717
|
+
const base = params.readBack && typeof params.readBack === "object" && !Array.isArray(params.readBack) ? { ...params.readBack } : {};
|
|
2718
|
+
const kind = typeof base.kind === "string" && base.kind.trim() ? base.kind.trim() : params.actionType;
|
|
2719
|
+
const normalized = {
|
|
2720
|
+
...base,
|
|
2721
|
+
kind,
|
|
2722
|
+
status: "pending",
|
|
2723
|
+
requestedAt: typeof base.requestedAt === "string" ? base.requestedAt : new Date(params.requestedAt).toISOString(),
|
|
2724
|
+
blockId: params.blockId,
|
|
2725
|
+
actionType: params.actionType,
|
|
2726
|
+
actorDid: params.actorDid,
|
|
2727
|
+
invocationCid: params.invocationCid,
|
|
2728
|
+
capabilityId: params.capabilityId
|
|
2729
|
+
};
|
|
2730
|
+
if (params.pendingInvocation) {
|
|
2731
|
+
normalized.pendingInvocation = {
|
|
2732
|
+
id: params.pendingInvocation.id,
|
|
2733
|
+
triggeringBlockId: params.pendingInvocation.triggeringBlockId,
|
|
2734
|
+
eventName: params.pendingInvocation.eventName,
|
|
2735
|
+
sourceRunId: params.pendingInvocation.sourceRunId
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
return normalized;
|
|
2739
|
+
}
|
|
2740
|
+
function writeReconciliationRunRecord(params) {
|
|
2741
|
+
if (!params.yDoc) return void 0;
|
|
2742
|
+
const completedAt = params.now();
|
|
2743
|
+
const executionId = makeExecutionId(params.now);
|
|
2744
|
+
const pendingInvocation = params.readBack.pendingInvocation;
|
|
2745
|
+
const details = {
|
|
2746
|
+
runId: executionId,
|
|
2747
|
+
sessionRunId: params.sessionRunId,
|
|
2748
|
+
output: params.output,
|
|
2749
|
+
events: params.events,
|
|
2750
|
+
startedAt: params.readBack.requestedAt || new Date(completedAt).toISOString(),
|
|
2751
|
+
completedAt: new Date(completedAt).toISOString(),
|
|
2752
|
+
actorDid: params.actorDid,
|
|
2753
|
+
invocationCid: params.readBack.invocationCid,
|
|
2754
|
+
capabilityId: params.readBack.capabilityId,
|
|
2755
|
+
error: params.error,
|
|
2756
|
+
readBack: params.readBack,
|
|
2757
|
+
reconciled: true
|
|
2758
|
+
};
|
|
2759
|
+
if (pendingInvocation) {
|
|
2760
|
+
details.fromPendingInvocationId = pendingInvocation.id;
|
|
2761
|
+
details.triggeredBy = {
|
|
2762
|
+
sourceBlockId: pendingInvocation.triggeringBlockId,
|
|
2763
|
+
eventName: pendingInvocation.eventName
|
|
2764
|
+
};
|
|
2765
|
+
details.sourceRunId = pendingInvocation.sourceRunId;
|
|
2766
|
+
}
|
|
2767
|
+
appendRunRecord(params.yDoc, params.blockId, details, params.actorDid);
|
|
2768
|
+
if (params.editor && params.events.length > 0) {
|
|
2769
|
+
reconcilePendingInvocations(params.editor, params.sessionRunId);
|
|
2770
|
+
}
|
|
2771
|
+
return executionId;
|
|
2772
|
+
}
|
|
2773
|
+
async function reconcileActionReadBack(params) {
|
|
2774
|
+
const now = params.now || Date.now;
|
|
2775
|
+
const yDoc = getYDoc(params.editorOrYDoc);
|
|
2776
|
+
const editor = getEditor(params.editorOrYDoc);
|
|
2777
|
+
const eventLog = params.runEventLog || editor?._runEventLog;
|
|
2778
|
+
const sessionRunId = yDoc ? resolveRunIdForExecution(yDoc, params.runId) : params.runId;
|
|
2779
|
+
const runtime = getRuntime(params.editorOrYDoc, params.runtime, sessionRunId);
|
|
2780
|
+
const current = runtime.get(params.blockId);
|
|
2781
|
+
const output = asOutput(current.output);
|
|
2782
|
+
const readBack = current.readBack;
|
|
2783
|
+
const timelineRequired = Boolean(yDoc && !usesLegacyRuntimeCompatibility(yDoc));
|
|
2784
|
+
if (timelineRequired && !eventLog) {
|
|
2785
|
+
return {
|
|
2786
|
+
success: false,
|
|
2787
|
+
blockId: params.blockId,
|
|
2788
|
+
state: "pending",
|
|
2789
|
+
output,
|
|
2790
|
+
events: [],
|
|
2791
|
+
error: "Read-back was not reconciled because a canonical run event log is required.",
|
|
2792
|
+
pendingInvocationRemoved: false,
|
|
2793
|
+
readBack
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
if (current.state !== "awaiting_readback" || !readBack?.kind) {
|
|
2797
|
+
return {
|
|
2798
|
+
success: false,
|
|
2799
|
+
blockId: params.blockId,
|
|
2800
|
+
state: current.state === "failed" ? "failed" : "pending",
|
|
2801
|
+
output,
|
|
2802
|
+
events: [],
|
|
2803
|
+
error: `Block "${params.blockId}" is not awaiting read-back`,
|
|
2804
|
+
pendingInvocationRemoved: false,
|
|
2805
|
+
readBack
|
|
2806
|
+
};
|
|
2807
|
+
}
|
|
2808
|
+
const resolver = params.resolver || params.resolvers?.[readBack.kind];
|
|
2809
|
+
if (!resolver) {
|
|
2810
|
+
return {
|
|
2811
|
+
success: false,
|
|
2812
|
+
blockId: params.blockId,
|
|
2813
|
+
state: "pending",
|
|
2814
|
+
output,
|
|
2815
|
+
events: [],
|
|
2816
|
+
error: `No read-back resolver registered for "${readBack.kind}"`,
|
|
2817
|
+
pendingInvocationRemoved: false,
|
|
2818
|
+
readBack
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
const resolution = await resolver({
|
|
2822
|
+
blockId: params.blockId,
|
|
2823
|
+
runtime: current,
|
|
2824
|
+
output,
|
|
2825
|
+
readBack
|
|
2826
|
+
});
|
|
2827
|
+
const checkedAt = now();
|
|
2828
|
+
const checkedIso = new Date(checkedAt).toISOString();
|
|
2829
|
+
const nextReadBack = {
|
|
2830
|
+
...readBack,
|
|
2831
|
+
...resolution.readBack || {},
|
|
2832
|
+
status: resolution.state,
|
|
2833
|
+
lastCheckedAt: checkedIso
|
|
2834
|
+
};
|
|
2835
|
+
if (resolution.state === "pending") {
|
|
2836
|
+
runtime.update(params.blockId, {
|
|
2837
|
+
readBack: nextReadBack
|
|
2838
|
+
});
|
|
2839
|
+
return {
|
|
2840
|
+
success: true,
|
|
2841
|
+
blockId: params.blockId,
|
|
2842
|
+
state: "pending",
|
|
2843
|
+
output,
|
|
2844
|
+
events: resolution.events || [],
|
|
2845
|
+
pendingInvocationRemoved: false,
|
|
2846
|
+
readBack: nextReadBack
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
const events = boundActionEvents(resolution.events || []);
|
|
2850
|
+
const mergedOutput = {
|
|
2851
|
+
...output,
|
|
2852
|
+
...resolution.output || {}
|
|
2853
|
+
};
|
|
2854
|
+
const boundedOutput = boundRunActionOutput(mergedOutput);
|
|
2855
|
+
const finalOutput = boundedOutput.output;
|
|
2856
|
+
const actorDid = params.actorDid || readBack.actorDid || current.executedByDid || "system:readback";
|
|
2857
|
+
if (resolution.state === "failed") {
|
|
2858
|
+
const error = errorRecord(resolution.error) || { message: "External read-back failed" };
|
|
2859
|
+
const failedReadBack = {
|
|
2860
|
+
...nextReadBack,
|
|
2861
|
+
terminalAt: checkedIso
|
|
2862
|
+
};
|
|
2863
|
+
runtime.update(params.blockId, {
|
|
2864
|
+
state: "failed",
|
|
2865
|
+
output: finalOutput,
|
|
2866
|
+
error: { ...error, at: checkedAt },
|
|
2867
|
+
readBack: failedReadBack
|
|
2868
|
+
});
|
|
2869
|
+
const executionId2 = writeReconciliationRunRecord({
|
|
2870
|
+
yDoc,
|
|
2871
|
+
editor: getReconcileEditor(params, yDoc, editor),
|
|
2872
|
+
sessionRunId,
|
|
2873
|
+
blockId: params.blockId,
|
|
2874
|
+
actorDid,
|
|
2875
|
+
output: finalOutput,
|
|
2876
|
+
events,
|
|
2877
|
+
readBack: failedReadBack,
|
|
2878
|
+
error,
|
|
2879
|
+
now
|
|
2880
|
+
});
|
|
2881
|
+
if (sessionRunId && eventLog) {
|
|
2882
|
+
const timelineExecutionId = current.executionId || executionId2 || readBack.requestedAt || `readback-${checkedAt}`;
|
|
2883
|
+
await appendReadBackTimeline(
|
|
2884
|
+
eventLog,
|
|
2885
|
+
{
|
|
2886
|
+
runId: sessionRunId,
|
|
2887
|
+
blockId: params.blockId,
|
|
2888
|
+
kind: "action.failed",
|
|
2889
|
+
payload: {
|
|
2890
|
+
attempt: current.attempt || 1,
|
|
2891
|
+
executionId: timelineExecutionId,
|
|
2892
|
+
...current.executionStartedAt ? { durationMs: Math.max(0, checkedAt - current.executionStartedAt) } : {},
|
|
2893
|
+
error: {
|
|
2894
|
+
message: error.message,
|
|
2895
|
+
...error.code ? { code: error.code } : {}
|
|
2896
|
+
}
|
|
2897
|
+
},
|
|
2898
|
+
idempotencyKey: createRunEventIdempotencyKey("action.failed", sessionRunId, params.blockId, timelineExecutionId, "readback"),
|
|
2899
|
+
ts: checkedAt
|
|
2900
|
+
},
|
|
2901
|
+
runtime,
|
|
2902
|
+
params.blockId,
|
|
2903
|
+
now
|
|
2904
|
+
);
|
|
2905
|
+
}
|
|
2906
|
+
return {
|
|
2907
|
+
success: false,
|
|
2908
|
+
blockId: params.blockId,
|
|
2909
|
+
state: "failed",
|
|
2910
|
+
output: finalOutput,
|
|
2911
|
+
events,
|
|
2912
|
+
error: error.message,
|
|
2913
|
+
runId: executionId2,
|
|
2914
|
+
executionId: executionId2,
|
|
2915
|
+
pendingInvocationRemoved: false,
|
|
2916
|
+
readBack: failedReadBack
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2919
|
+
const readBackActionType = readBack.actionType || readBack.kind;
|
|
2920
|
+
const actionDef = readBackActionType ? getAction(readBackActionType) : void 0;
|
|
2921
|
+
if (actionDef) {
|
|
2922
|
+
const proofCheck = validateActionProof(actionDef, mergedOutput);
|
|
2923
|
+
if (!proofCheck.valid) {
|
|
2924
|
+
const message = proofCheck.reason || "Read-back resolved completed without proof of execution.";
|
|
2925
|
+
const proofFailedReadBack = {
|
|
2926
|
+
...nextReadBack,
|
|
2927
|
+
terminalAt: checkedIso
|
|
2928
|
+
};
|
|
2929
|
+
runtime.update(params.blockId, {
|
|
2930
|
+
state: actionDef.sideEffect ? "needs_verification" : "failed",
|
|
2931
|
+
output: finalOutput,
|
|
2932
|
+
error: { message, code: PROOF_MISSING_CODE, at: checkedAt },
|
|
2933
|
+
readBack: proofFailedReadBack
|
|
2934
|
+
});
|
|
2935
|
+
const failedExecutionId = writeReconciliationRunRecord({
|
|
2936
|
+
yDoc,
|
|
2937
|
+
editor: getReconcileEditor(params, yDoc, editor),
|
|
2938
|
+
sessionRunId,
|
|
2939
|
+
blockId: params.blockId,
|
|
2940
|
+
actorDid,
|
|
2941
|
+
output: finalOutput,
|
|
2942
|
+
events,
|
|
2943
|
+
readBack: proofFailedReadBack,
|
|
2944
|
+
error: { message, code: PROOF_MISSING_CODE },
|
|
2945
|
+
now
|
|
2946
|
+
});
|
|
2947
|
+
if (sessionRunId && eventLog) {
|
|
2948
|
+
const timelineExecutionId = current.executionId || failedExecutionId || readBack.requestedAt || `readback-${checkedAt}`;
|
|
2949
|
+
await appendReadBackTimeline(
|
|
2950
|
+
eventLog,
|
|
2951
|
+
{
|
|
2952
|
+
runId: sessionRunId,
|
|
2953
|
+
blockId: params.blockId,
|
|
2954
|
+
kind: "action.failed",
|
|
2955
|
+
payload: {
|
|
2956
|
+
attempt: current.attempt || 1,
|
|
2957
|
+
executionId: timelineExecutionId,
|
|
2958
|
+
...current.executionStartedAt ? { durationMs: Math.max(0, checkedAt - current.executionStartedAt) } : {},
|
|
2959
|
+
error: { message, code: PROOF_MISSING_CODE }
|
|
2960
|
+
},
|
|
2961
|
+
idempotencyKey: createRunEventIdempotencyKey("action.failed", sessionRunId, params.blockId, timelineExecutionId, "readback"),
|
|
2962
|
+
ts: checkedAt
|
|
2963
|
+
},
|
|
2964
|
+
runtime,
|
|
2965
|
+
params.blockId,
|
|
2966
|
+
now
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
return {
|
|
2970
|
+
success: false,
|
|
2971
|
+
blockId: params.blockId,
|
|
2972
|
+
state: "failed",
|
|
2973
|
+
output: finalOutput,
|
|
2974
|
+
events,
|
|
2975
|
+
error: message,
|
|
2976
|
+
runId: failedExecutionId,
|
|
2977
|
+
executionId: failedExecutionId,
|
|
2978
|
+
pendingInvocationRemoved: false,
|
|
2979
|
+
readBack: proofFailedReadBack
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
if (boundedOutput.exceeded) {
|
|
2984
|
+
const message = `Read-back output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.`;
|
|
2985
|
+
const oversizedReadBack = {
|
|
2986
|
+
...nextReadBack,
|
|
2987
|
+
terminalAt: checkedIso
|
|
2988
|
+
};
|
|
2989
|
+
runtime.update(params.blockId, {
|
|
2990
|
+
state: actionDef?.sideEffect === false ? "failed" : "needs_verification",
|
|
2991
|
+
output: finalOutput,
|
|
2992
|
+
error: {
|
|
2993
|
+
message,
|
|
2994
|
+
code: "RUN_OUTPUT_TOO_LARGE",
|
|
2995
|
+
at: checkedAt,
|
|
2996
|
+
data: { byteLength: boundedOutput.byteLength }
|
|
2997
|
+
},
|
|
2998
|
+
readBack: oversizedReadBack
|
|
2999
|
+
});
|
|
3000
|
+
const oversizedExecutionId = writeReconciliationRunRecord({
|
|
3001
|
+
yDoc,
|
|
3002
|
+
editor: getReconcileEditor(params, yDoc, editor),
|
|
3003
|
+
sessionRunId,
|
|
3004
|
+
blockId: params.blockId,
|
|
3005
|
+
actorDid,
|
|
3006
|
+
output: finalOutput,
|
|
3007
|
+
events,
|
|
3008
|
+
readBack: oversizedReadBack,
|
|
3009
|
+
error: { message, code: "RUN_OUTPUT_TOO_LARGE" },
|
|
3010
|
+
now
|
|
3011
|
+
});
|
|
3012
|
+
if (sessionRunId && eventLog) {
|
|
3013
|
+
const attempt = current.attempt || 1;
|
|
3014
|
+
const timelineExecutionId = current.executionId || oversizedExecutionId || readBack.requestedAt || `readback-${checkedAt}`;
|
|
3015
|
+
await appendRunTimelineEvents({
|
|
3016
|
+
eventLog,
|
|
3017
|
+
events: [
|
|
3018
|
+
{
|
|
3019
|
+
runId: sessionRunId,
|
|
3020
|
+
blockId: params.blockId,
|
|
3021
|
+
kind: "action.output",
|
|
3022
|
+
payload: {
|
|
3023
|
+
attempt,
|
|
3024
|
+
executionId: timelineExecutionId,
|
|
3025
|
+
output: boundRunEventJson(finalOutput)
|
|
3026
|
+
},
|
|
3027
|
+
idempotencyKey: createRunEventIdempotencyKey("action.output", sessionRunId, params.blockId, timelineExecutionId, "readback"),
|
|
3028
|
+
ts: checkedAt
|
|
3029
|
+
},
|
|
3030
|
+
{
|
|
3031
|
+
runId: sessionRunId,
|
|
3032
|
+
blockId: params.blockId,
|
|
3033
|
+
kind: "action.failed",
|
|
3034
|
+
payload: {
|
|
3035
|
+
attempt,
|
|
3036
|
+
executionId: timelineExecutionId,
|
|
3037
|
+
...current.executionStartedAt ? {
|
|
3038
|
+
durationMs: Math.max(0, checkedAt - current.executionStartedAt)
|
|
3039
|
+
} : {},
|
|
3040
|
+
error: {
|
|
3041
|
+
message,
|
|
3042
|
+
code: "RUN_OUTPUT_TOO_LARGE",
|
|
3043
|
+
details: {
|
|
3044
|
+
byteLength: boundedOutput.byteLength,
|
|
3045
|
+
maxBytes: MAX_RUN_ACTION_OUTPUT_BYTES
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
},
|
|
3049
|
+
idempotencyKey: createRunEventIdempotencyKey("action.failed", sessionRunId, params.blockId, timelineExecutionId, "readback"),
|
|
3050
|
+
ts: checkedAt
|
|
3051
|
+
}
|
|
3052
|
+
],
|
|
3053
|
+
runtime,
|
|
3054
|
+
blockId: params.blockId,
|
|
3055
|
+
now
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
return {
|
|
3059
|
+
success: false,
|
|
3060
|
+
blockId: params.blockId,
|
|
3061
|
+
state: "failed",
|
|
3062
|
+
output: finalOutput,
|
|
3063
|
+
events,
|
|
3064
|
+
error: message,
|
|
3065
|
+
runId: oversizedExecutionId,
|
|
3066
|
+
executionId: oversizedExecutionId,
|
|
3067
|
+
pendingInvocationRemoved: false,
|
|
3068
|
+
readBack: oversizedReadBack
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
const completedReadBack = {
|
|
3072
|
+
...nextReadBack,
|
|
3073
|
+
terminalAt: checkedIso
|
|
3074
|
+
};
|
|
3075
|
+
runtime.update(params.blockId, {
|
|
3076
|
+
state: "completed",
|
|
3077
|
+
output: finalOutput,
|
|
3078
|
+
executedAt: checkedAt,
|
|
3079
|
+
error: void 0,
|
|
3080
|
+
readBack: completedReadBack
|
|
3081
|
+
});
|
|
3082
|
+
const executionId = writeReconciliationRunRecord({
|
|
3083
|
+
yDoc,
|
|
3084
|
+
editor: getReconcileEditor(params, yDoc, editor),
|
|
3085
|
+
sessionRunId,
|
|
3086
|
+
blockId: params.blockId,
|
|
3087
|
+
actorDid,
|
|
3088
|
+
output: finalOutput,
|
|
3089
|
+
events,
|
|
3090
|
+
readBack: completedReadBack,
|
|
3091
|
+
now
|
|
3092
|
+
});
|
|
3093
|
+
let timelineComplete = true;
|
|
3094
|
+
if (sessionRunId && eventLog) {
|
|
3095
|
+
const attempt = current.attempt || 1;
|
|
3096
|
+
const timelineExecutionId = current.executionId || executionId || readBack.requestedAt || `readback-${checkedAt}`;
|
|
3097
|
+
const timelineResult = await appendRunTimelineEvents({
|
|
3098
|
+
eventLog,
|
|
3099
|
+
events: [
|
|
3100
|
+
{
|
|
3101
|
+
runId: sessionRunId,
|
|
3102
|
+
blockId: params.blockId,
|
|
3103
|
+
kind: "action.output",
|
|
3104
|
+
payload: {
|
|
3105
|
+
attempt,
|
|
3106
|
+
executionId: timelineExecutionId,
|
|
3107
|
+
output: boundRunEventJson(finalOutput)
|
|
3108
|
+
},
|
|
3109
|
+
idempotencyKey: createRunEventIdempotencyKey("action.output", sessionRunId, params.blockId, timelineExecutionId, "readback"),
|
|
3110
|
+
ts: checkedAt
|
|
3111
|
+
},
|
|
3112
|
+
{
|
|
3113
|
+
runId: sessionRunId,
|
|
3114
|
+
blockId: params.blockId,
|
|
3115
|
+
kind: "action.done",
|
|
3116
|
+
payload: {
|
|
3117
|
+
attempt,
|
|
3118
|
+
executionId: timelineExecutionId,
|
|
3119
|
+
...current.executionStartedAt ? {
|
|
3120
|
+
durationMs: Math.max(0, checkedAt - current.executionStartedAt)
|
|
3121
|
+
} : {}
|
|
3122
|
+
},
|
|
3123
|
+
idempotencyKey: createRunEventIdempotencyKey("action.done", sessionRunId, params.blockId, timelineExecutionId, "readback"),
|
|
3124
|
+
ts: checkedAt
|
|
3125
|
+
}
|
|
3126
|
+
],
|
|
3127
|
+
runtime,
|
|
3128
|
+
blockId: params.blockId,
|
|
3129
|
+
now
|
|
3130
|
+
});
|
|
3131
|
+
timelineComplete = timelineResult.ok && timelineResult.remaining === 0;
|
|
3132
|
+
}
|
|
3133
|
+
const pendingInvocationRemoved = timelineComplete && Boolean(yDoc && completedReadBack.pendingInvocation?.id) && removePendingInvocation(yDoc, params.blockId, completedReadBack.pendingInvocation.id, sessionRunId);
|
|
3134
|
+
return {
|
|
3135
|
+
success: timelineComplete,
|
|
3136
|
+
blockId: params.blockId,
|
|
3137
|
+
state: "completed",
|
|
3138
|
+
output: finalOutput,
|
|
3139
|
+
events,
|
|
3140
|
+
...!timelineComplete ? { error: "Canonical run history replay remains incomplete." } : {},
|
|
3141
|
+
runId: executionId,
|
|
3142
|
+
executionId,
|
|
3143
|
+
pendingInvocationRemoved,
|
|
3144
|
+
readBack: completedReadBack
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
// src/core/lib/flowEngine/actionExecutor.ts
|
|
3149
|
+
var COMPACT_TIMELINE_ACTION_TYPES = new Set(DESIGN_POD_METHODS["design-yoma/v1"].operationalActions);
|
|
3150
|
+
function buildActionTimelineInput(actionType, inputs, inputDigest) {
|
|
3151
|
+
if (actionType.startsWith("qi/blueprint.") || COMPACT_TIMELINE_ACTION_TYPES.has(actionType)) return { inputDigest };
|
|
3152
|
+
return inputs;
|
|
3153
|
+
}
|
|
3154
|
+
function isYDoc2(value) {
|
|
3155
|
+
return isYDocLike(value);
|
|
3156
|
+
}
|
|
3157
|
+
function getYDoc2(editorOrYDoc) {
|
|
3158
|
+
if (!editorOrYDoc) return void 0;
|
|
3159
|
+
if (isYDoc2(editorOrYDoc)) return editorOrYDoc;
|
|
3160
|
+
return editorOrYDoc._yDoc;
|
|
3161
|
+
}
|
|
3162
|
+
function parseInputs2(value) {
|
|
3163
|
+
if (value == null || value === "") return {};
|
|
3164
|
+
if (typeof value === "object" && !Array.isArray(value)) return { ...value };
|
|
3165
|
+
if (typeof value !== "string") return {};
|
|
3166
|
+
try {
|
|
3167
|
+
const parsed = JSON.parse(value);
|
|
3168
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3169
|
+
} catch {
|
|
3170
|
+
warnOnce(`executor-inputs-parse:${value}`, `[flow-config] saved block inputs are not valid JSON; treated as empty`);
|
|
3171
|
+
return {};
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
function getRuntime2(editorOrYDoc, runtime, runId) {
|
|
3175
|
+
if (runtime) return runtime;
|
|
3176
|
+
const yDoc = getYDoc2(editorOrYDoc);
|
|
3177
|
+
if (yDoc) {
|
|
3178
|
+
return createRunScopedRuntimeManager(yDoc, resolveRunIdForExecution(yDoc, runId));
|
|
3179
|
+
}
|
|
3180
|
+
return createRuntimeStateManager(!isYDoc2(editorOrYDoc) ? editorOrYDoc : void 0);
|
|
3181
|
+
}
|
|
3182
|
+
function getEditor2(editorOrYDoc) {
|
|
3183
|
+
return editorOrYDoc && !isYDoc2(editorOrYDoc) ? editorOrYDoc : void 0;
|
|
3184
|
+
}
|
|
3185
|
+
function findBlock(params) {
|
|
3186
|
+
if (params.block) return params.block;
|
|
3187
|
+
const blockId = params.blockId;
|
|
3188
|
+
if (!blockId) return void 0;
|
|
3189
|
+
const editor = getEditor2(params.editorOrYDoc);
|
|
3190
|
+
const visit = (blocks) => {
|
|
3191
|
+
for (const block of blocks) {
|
|
3192
|
+
if (block?.id === blockId) return block;
|
|
3193
|
+
const nested = block?.children;
|
|
3194
|
+
if (Array.isArray(nested)) {
|
|
3195
|
+
const found = visit(nested);
|
|
3196
|
+
if (found) return found;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
return void 0;
|
|
3200
|
+
};
|
|
3201
|
+
return visit(params.document || editor?.document || []);
|
|
3202
|
+
}
|
|
3203
|
+
function getFlowMetadata(editor) {
|
|
3204
|
+
const metadata = editor?.getFlowMetadata?.();
|
|
3205
|
+
const flowId = metadata?.doc_id || "";
|
|
3206
|
+
return {
|
|
3207
|
+
flowId,
|
|
3208
|
+
flowUri: flowId ? `ixo:flow:${flowId}` : "",
|
|
3209
|
+
flowOwnerDid: editor?.getFlowOwnerDid?.() || metadata?.flowOwnerDid || "",
|
|
3210
|
+
schemaVersion: metadata?.schema_version || "0.3"
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
function getPendingInvocation(yDoc, blockId, pendingInvocationId, sessionRunId) {
|
|
3214
|
+
if (!pendingInvocationId) return void 0;
|
|
3215
|
+
if (!yDoc) {
|
|
3216
|
+
throw new Error(`Cannot load pending invocation "${pendingInvocationId}" without a Y.Doc`);
|
|
3217
|
+
}
|
|
3218
|
+
if (!blockId) {
|
|
3219
|
+
throw new Error(`Cannot load pending invocation "${pendingInvocationId}" without a block id`);
|
|
3220
|
+
}
|
|
3221
|
+
const pending = readPendingInvocations(yDoc, blockId, sessionRunId).find((invocation) => invocation.id === pendingInvocationId);
|
|
3222
|
+
if (!pending) {
|
|
3223
|
+
throw new Error(`Pending invocation "${pendingInvocationId}" was not found for block "${blockId}"`);
|
|
3224
|
+
}
|
|
3225
|
+
return pending;
|
|
3226
|
+
}
|
|
3227
|
+
function getNodeOutput(runtime, nodeId) {
|
|
3228
|
+
const state = runtime.get(nodeId);
|
|
3229
|
+
const output = state.output;
|
|
3230
|
+
return output && typeof output === "object" && !Array.isArray(output) ? output : void 0;
|
|
3231
|
+
}
|
|
3232
|
+
function buildActionRunInputs(params) {
|
|
3233
|
+
const blockId = params.blockId || params.block?.id;
|
|
3234
|
+
const yDoc = getYDoc2(params.editorOrYDoc);
|
|
3235
|
+
const runtime = getRuntime2(params.editorOrYDoc, params.runtime, params.runId);
|
|
3236
|
+
const savedInputs = parseInputs2(params.savedInputs ?? params.block?.props?.inputs);
|
|
3237
|
+
const pendingInvocation = getPendingInvocation(yDoc, blockId, params.pendingInvocationId, params.runId);
|
|
3238
|
+
const triggerContext = pendingInvocation ? {
|
|
3239
|
+
payload: pendingInvocation.payload || {},
|
|
3240
|
+
refSnapshots: pendingInvocation.refSnapshots || {}
|
|
3241
|
+
} : void 0;
|
|
3242
|
+
const mergedInputs = {
|
|
3243
|
+
...savedInputs,
|
|
3244
|
+
...pendingInvocation?.payload || {},
|
|
3245
|
+
...pendingInvocation?.refSnapshots || {},
|
|
3246
|
+
...params.runtimeInputs || {}
|
|
3247
|
+
};
|
|
3248
|
+
return {
|
|
3249
|
+
inputs: resolveRuntimeRefs(mergedInputs, (nodeId) => getNodeOutput(runtime, nodeId), triggerContext),
|
|
3250
|
+
savedInputs,
|
|
3251
|
+
mergedInputs,
|
|
3252
|
+
pendingInvocation,
|
|
3253
|
+
triggerContext
|
|
3254
|
+
};
|
|
3255
|
+
}
|
|
3256
|
+
function buildFailureResult(params) {
|
|
3257
|
+
return {
|
|
3258
|
+
success: false,
|
|
3259
|
+
stage: params.stage,
|
|
3260
|
+
blockId: params.blockId,
|
|
3261
|
+
actionType: params.actionType,
|
|
3262
|
+
events: [],
|
|
3263
|
+
error: params.error,
|
|
3264
|
+
completionState: "failed",
|
|
3265
|
+
pendingInvocation: params.pendingInvocation
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
function updateRuntimeFailure(runtime, blockId, message, now) {
|
|
3269
|
+
runtime.update(blockId, {
|
|
3270
|
+
state: "failed",
|
|
3271
|
+
error: { message, at: now() }
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
function makeExecutionId2(now) {
|
|
3275
|
+
return `run-${now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3276
|
+
}
|
|
3277
|
+
function getReconcileEditor2(params, yDoc, editor) {
|
|
3278
|
+
if (editor) return editor;
|
|
3279
|
+
if (!yDoc || !params.document) return void 0;
|
|
3280
|
+
return {
|
|
3281
|
+
_yDoc: yDoc,
|
|
3282
|
+
_yRuntime: createRunRuntimeReaderWithDoc(yDoc, resolveRunIdForExecution(yDoc, params.runId)),
|
|
3283
|
+
document: params.document
|
|
3284
|
+
};
|
|
3285
|
+
}
|
|
3286
|
+
function persistEvents(params) {
|
|
3287
|
+
if (!params.yDoc || params.events.length === 0) return void 0;
|
|
3288
|
+
const details = {
|
|
3289
|
+
runId: params.executionId,
|
|
3290
|
+
sessionRunId: params.sessionRunId,
|
|
3291
|
+
output: params.output,
|
|
3292
|
+
events: params.events,
|
|
3293
|
+
startedAt: new Date(params.startedAt).toISOString(),
|
|
3294
|
+
completedAt: new Date(params.completedAt).toISOString(),
|
|
3295
|
+
actorDid: params.actorDid,
|
|
3296
|
+
invocationCid: params.invocationCid,
|
|
3297
|
+
capabilityId: params.capabilityId
|
|
3298
|
+
};
|
|
3299
|
+
if (params.pendingInvocation) {
|
|
3300
|
+
details.fromPendingInvocationId = params.pendingInvocation.id;
|
|
3301
|
+
details.triggeredBy = {
|
|
3302
|
+
sourceBlockId: params.pendingInvocation.triggeringBlockId,
|
|
3303
|
+
eventName: params.pendingInvocation.eventName
|
|
3304
|
+
};
|
|
3305
|
+
details.sourceRunId = params.pendingInvocation.sourceRunId;
|
|
3306
|
+
}
|
|
3307
|
+
appendRunRecord(params.yDoc, params.blockId, details, params.actorDid);
|
|
3308
|
+
if (params.editor) {
|
|
3309
|
+
reconcilePendingInvocations(params.editor, params.sessionRunId);
|
|
3310
|
+
}
|
|
3311
|
+
return params.executionId;
|
|
3312
|
+
}
|
|
3313
|
+
function cleanupCompletedPendingInvocation(yDoc, blockId, pendingInvocation, sessionRunId) {
|
|
3314
|
+
if (!yDoc || !pendingInvocation) return false;
|
|
3315
|
+
return removePendingInvocation(yDoc, blockId, pendingInvocation.id, sessionRunId || pendingInvocation.sessionRunId);
|
|
3316
|
+
}
|
|
3317
|
+
async function executeActionBlock(params) {
|
|
3318
|
+
const block = findBlock(params);
|
|
3319
|
+
const blockId = params.blockId || block?.id;
|
|
3320
|
+
const editor = getEditor2(params.editorOrYDoc);
|
|
3321
|
+
const yDoc = getYDoc2(params.editorOrYDoc);
|
|
3322
|
+
const now = params.now || Date.now;
|
|
3323
|
+
const eventLog = params.runEventLog || editor?._runEventLog;
|
|
3324
|
+
if (!block || !blockId) {
|
|
3325
|
+
return buildFailureResult({
|
|
3326
|
+
blockId: blockId || "",
|
|
3327
|
+
stage: "input",
|
|
3328
|
+
error: blockId ? `Block "${blockId}" was not found` : "executeActionBlock requires a blockId or block"
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
let sessionRunId = params.runId;
|
|
3332
|
+
try {
|
|
3333
|
+
if (yDoc) {
|
|
3334
|
+
sessionRunId = resolveRunIdForExecution(yDoc, params.runId);
|
|
3335
|
+
assertRunActionExecutionAllowed(yDoc, sessionRunId, block);
|
|
3336
|
+
}
|
|
3337
|
+
} catch (error) {
|
|
3338
|
+
return buildFailureResult({
|
|
3339
|
+
blockId,
|
|
3340
|
+
stage: "input",
|
|
3341
|
+
error: error instanceof Error ? error.message : "The requested session run cannot execute this action."
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
const runtime = getRuntime2(params.editorOrYDoc, params.runtime, sessionRunId);
|
|
3345
|
+
const actionType = typeof block.props?.actionType === "string" ? block.props.actionType : void 0;
|
|
3346
|
+
if (!actionType) {
|
|
3347
|
+
updateRuntimeFailure(runtime, blockId, "Action block is missing props.actionType", now);
|
|
3348
|
+
return buildFailureResult({
|
|
3349
|
+
blockId,
|
|
3350
|
+
stage: "input",
|
|
3351
|
+
error: "Action block is missing props.actionType"
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3354
|
+
const action = getAction(actionType);
|
|
3355
|
+
if (!action) {
|
|
3356
|
+
const message = `No registered action found for ${actionType}`;
|
|
3357
|
+
updateRuntimeFailure(runtime, blockId, message, now);
|
|
3358
|
+
return buildFailureResult({ blockId, actionType, stage: "action_lookup", error: message });
|
|
3359
|
+
}
|
|
3360
|
+
if (yDoc && sessionRunId) {
|
|
3361
|
+
const document = params.document || editor?.document || [block];
|
|
3362
|
+
let eligibility;
|
|
3363
|
+
try {
|
|
3364
|
+
eligibility = evaluateBlockExecutionEligibilityForRun(parseConditionConfigStrict(block.props?.conditions), document, yDoc, sessionRunId);
|
|
3365
|
+
} catch (error) {
|
|
3366
|
+
return buildFailureResult({
|
|
3367
|
+
blockId,
|
|
3368
|
+
actionType,
|
|
3369
|
+
stage: "input",
|
|
3370
|
+
error: error instanceof Error ? error.message : "Block conditions are invalid."
|
|
3371
|
+
});
|
|
3372
|
+
}
|
|
3373
|
+
if (!eligibility.eligible) {
|
|
3374
|
+
return buildFailureResult({
|
|
3375
|
+
blockId,
|
|
3376
|
+
actionType,
|
|
3377
|
+
stage: "input",
|
|
3378
|
+
error: eligibility.message || "Block conditions are not satisfied."
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
let inputBuild;
|
|
3383
|
+
try {
|
|
3384
|
+
inputBuild = buildActionRunInputs({ ...params, block, blockId, runtime });
|
|
3385
|
+
if (actionType.startsWith("qi/blueprint.") && !["qi/blueprint.workspace-start", "qi/blueprint.journey-resume", "qi/blueprint.guided-authoring", "qi/blueprint.phase-confirm"].includes(actionType)) {
|
|
3386
|
+
const authority = params.designPodAuthority;
|
|
3387
|
+
if (!authority || !sessionRunId || authority.sessionRunId !== sessionRunId) {
|
|
3388
|
+
throw new Error("Portal-owned Design Project entity, Flow room, and session authority are required");
|
|
3389
|
+
}
|
|
3390
|
+
if (!yDoc) throw new Error("Portal-native Design POD actions require the active Flow Y.Doc");
|
|
3391
|
+
const manifest = readDesignPodDefinition(yDoc);
|
|
3392
|
+
const state = readDesignPodState(yDoc);
|
|
3393
|
+
const registeredManifest = manifest && DESIGN_POD_METHODS[manifest.methodId];
|
|
3394
|
+
if (!manifest || !state || !registeredManifest || canonicalDesignPodJson(manifest) !== canonicalDesignPodJson(registeredManifest)) {
|
|
3395
|
+
throw new Error("The active Design POD method is not the registered Portal method");
|
|
3396
|
+
}
|
|
3397
|
+
inputBuild.inputs = {
|
|
3398
|
+
...inputBuild.inputs,
|
|
3399
|
+
designProjectEntityDid: authority.designProjectEntityDid,
|
|
3400
|
+
flowRoomId: authority.flowRoomId,
|
|
3401
|
+
sessionRunId: authority.sessionRunId,
|
|
3402
|
+
methodManifestDigest: await digestDesignPodValue(registeredManifest)
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
} catch (error) {
|
|
3406
|
+
const message = error instanceof Error ? error.message : "Failed to build action inputs";
|
|
3407
|
+
updateRuntimeFailure(runtime, blockId, message, now);
|
|
3408
|
+
return buildFailureResult({ blockId, actionType, stage: "input", error: message });
|
|
3409
|
+
}
|
|
3410
|
+
const flowNode = buildFlowNodeFromBlock(block);
|
|
3411
|
+
const metadata = getFlowMetadata(editor);
|
|
3412
|
+
const flowId = params.designPodAuthority?.flowRoomId || params.flowId || metadata.flowId || blockId;
|
|
3413
|
+
const flowUri = params.designPodAuthority ? `ixo:flow:${params.designPodAuthority.flowRoomId}` : params.flowUri || metadata.flowUri || `ixo:flow:${flowId}`;
|
|
3414
|
+
const flowOwnerDid = params.designPodAuthority?.flowOwnerDid || params.flowOwnerDid || metadata.flowOwnerDid;
|
|
3415
|
+
const schemaVersion = params.schemaVersion || metadata.schemaVersion;
|
|
3416
|
+
if (actionType === "qi/flow.run.close" && yDoc && sessionRunId) {
|
|
3417
|
+
const document = params.document || editor?.document || [block];
|
|
3418
|
+
inputBuild.inputs = {
|
|
3419
|
+
...inputBuild.inputs,
|
|
3420
|
+
// Bind the exact completion projection into the signed invocation. The
|
|
3421
|
+
// lifecycle re-computes it before append and rejects any Matrix event
|
|
3422
|
+
// whose payload differs from these signed inputs.
|
|
3423
|
+
terminalSummary: computeRunSummary(yDoc, sessionRunId, buildRunDefinitionSnapshot(document).actionTypes, {
|
|
3424
|
+
flowId: flowUri,
|
|
3425
|
+
actorDid: params.actorDid,
|
|
3426
|
+
now: now()
|
|
3427
|
+
})
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
const events = [];
|
|
3431
|
+
let completionState = "completed";
|
|
3432
|
+
let readBack;
|
|
3433
|
+
let rawReadBack;
|
|
3434
|
+
let requestedAwaitingReadBack = false;
|
|
3435
|
+
let proofFailureReason = null;
|
|
3436
|
+
let proofFailureOutput;
|
|
3437
|
+
let topicRecords = [];
|
|
3438
|
+
let terminalRaceError = null;
|
|
3439
|
+
const startedAt = now();
|
|
3440
|
+
const previousState = runtime.get(blockId);
|
|
3441
|
+
const attempt = (previousState.attempt || 0) + 1;
|
|
3442
|
+
const executionId = makeExecutionId2(now);
|
|
3443
|
+
const inputDigest = await digestDesignPodValue(inputBuild.inputs);
|
|
3444
|
+
const invocationFacts = {
|
|
3445
|
+
actionType,
|
|
3446
|
+
inputDigest,
|
|
3447
|
+
...sessionRunId ? { sessionRunId } : {}
|
|
3448
|
+
};
|
|
3449
|
+
const recordTopicPhase = async (status, options = {}) => {
|
|
3450
|
+
if (!params.topic || !params.topicBridge) return void 0;
|
|
3451
|
+
try {
|
|
3452
|
+
return await params.topicBridge.recordFlowPhase({
|
|
3453
|
+
topic: params.topic,
|
|
3454
|
+
actionType,
|
|
3455
|
+
actorDid: params.actorDid,
|
|
3456
|
+
executorDid: params.executorDid || params.actorDid,
|
|
3457
|
+
executionId,
|
|
3458
|
+
status,
|
|
3459
|
+
input: inputBuild.inputs,
|
|
3460
|
+
output: options.output,
|
|
3461
|
+
semanticRecords: topicRecords,
|
|
3462
|
+
flowUri,
|
|
3463
|
+
sessionRunId,
|
|
3464
|
+
nodeId: blockId,
|
|
3465
|
+
invocationReference: options.invocationReference,
|
|
3466
|
+
traceReference: sessionRunId ? `${flowUri}/session/${sessionRunId}/execution/${executionId}` : `${flowUri}/execution/${executionId}`,
|
|
3467
|
+
error: options.error
|
|
3468
|
+
});
|
|
3469
|
+
} catch (error) {
|
|
3470
|
+
return { state: "queued", error: error instanceof Error ? error.message : "Topic receipt bridge failed" };
|
|
3471
|
+
}
|
|
3472
|
+
};
|
|
3473
|
+
const timelineRequired = !!yDoc && !usesLegacyRuntimeCompatibility(yDoc);
|
|
3474
|
+
if (sessionRunId && (eventLog || timelineRequired)) {
|
|
3475
|
+
const startedLogged = await appendRunTimelineEvent({
|
|
3476
|
+
eventLog,
|
|
3477
|
+
event: {
|
|
3478
|
+
runId: sessionRunId,
|
|
3479
|
+
blockId,
|
|
3480
|
+
kind: "action.started",
|
|
3481
|
+
payload: {
|
|
3482
|
+
attempt,
|
|
3483
|
+
executionId,
|
|
3484
|
+
input: boundRunEventJson(buildActionTimelineInput(actionType, inputBuild.inputs, inputDigest))
|
|
3485
|
+
},
|
|
3486
|
+
idempotencyKey: createRunEventIdempotencyKey("action.started", sessionRunId, blockId, executionId),
|
|
3487
|
+
ts: startedAt
|
|
3488
|
+
},
|
|
3489
|
+
runtime,
|
|
3490
|
+
blockId,
|
|
3491
|
+
retainOnFailure: false,
|
|
3492
|
+
now
|
|
3493
|
+
});
|
|
3494
|
+
if (!startedLogged.ok) {
|
|
3495
|
+
const message = "Action was not executed because its run timeline could not be opened.";
|
|
3496
|
+
runtime.update(blockId, {
|
|
3497
|
+
state: "failed",
|
|
3498
|
+
error: {
|
|
3499
|
+
message,
|
|
3500
|
+
code: "RUN_EVENT_APPEND_FAILED",
|
|
3501
|
+
at: now(),
|
|
3502
|
+
data: startedLogged.error
|
|
3503
|
+
}
|
|
3504
|
+
});
|
|
3505
|
+
return {
|
|
3506
|
+
...buildFailureResult({ blockId, actionType, stage: "action", error: message, pendingInvocation: inputBuild.pendingInvocation }),
|
|
3507
|
+
executionId,
|
|
3508
|
+
runId: executionId
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
if (yDoc && sessionRunId) {
|
|
3513
|
+
try {
|
|
3514
|
+
assertRunActionExecutionAllowed(yDoc, sessionRunId, block);
|
|
3515
|
+
} catch (error) {
|
|
3516
|
+
const message = error instanceof Error ? error.message : "The requested session became terminal before this action could execute.";
|
|
3517
|
+
return {
|
|
3518
|
+
...buildFailureResult({ blockId, actionType, stage: "action", error: message, pendingInvocation: inputBuild.pendingInvocation }),
|
|
3519
|
+
executionId,
|
|
3520
|
+
runId: executionId
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
runtime.update(blockId, {
|
|
3525
|
+
state: "running",
|
|
3526
|
+
output: void 0,
|
|
3527
|
+
error: void 0,
|
|
3528
|
+
executedByDid: params.actorDid,
|
|
3529
|
+
attempt,
|
|
3530
|
+
executionId,
|
|
3531
|
+
executionStartedAt: startedAt
|
|
3532
|
+
});
|
|
3533
|
+
const runningWriteBack = await recordTopicPhase("running");
|
|
3534
|
+
if (runningWriteBack?.state === "rejected") {
|
|
3535
|
+
const message = runningWriteBack.error || "Topic Action execution was rejected by the receipt bridge.";
|
|
3536
|
+
updateRuntimeFailure(runtime, blockId, message, now);
|
|
3537
|
+
return {
|
|
3538
|
+
...buildFailureResult({ blockId, actionType, stage: "authorization", error: message, pendingInvocation: inputBuild.pendingInvocation }),
|
|
3539
|
+
executionId,
|
|
3540
|
+
runId: executionId,
|
|
3541
|
+
topicWriteBack: runningWriteBack
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
const actionServices = action.type.startsWith("qi/topic.") ? params.services || {} : { ...params.services || {}, topic: void 0 };
|
|
3545
|
+
const outcome = await executeNode({
|
|
3546
|
+
node: flowNode,
|
|
3547
|
+
actorDid: params.actorDid,
|
|
3548
|
+
actorType: params.actorType,
|
|
3549
|
+
entityRoomId: params.entityRoomId,
|
|
3550
|
+
context: {
|
|
3551
|
+
runtime,
|
|
3552
|
+
ucanService: params.ucanService || editor?.getUcanService?.() || void 0,
|
|
3553
|
+
invocationStore: params.invocationStore || editor?._invocationStore,
|
|
3554
|
+
flowUri,
|
|
3555
|
+
flowId,
|
|
3556
|
+
sessionRunId,
|
|
3557
|
+
flowOwnerDid,
|
|
3558
|
+
schemaVersion,
|
|
3559
|
+
now
|
|
3560
|
+
},
|
|
3561
|
+
invocationFacts,
|
|
3562
|
+
requireSignedInvocation: action.requiresSignedInvocation === true,
|
|
3563
|
+
action: async (executionProof) => {
|
|
3564
|
+
if (yDoc && sessionRunId) {
|
|
3565
|
+
try {
|
|
3566
|
+
assertRunActionExecutionAllowed(yDoc, sessionRunId, block);
|
|
3567
|
+
} catch (error) {
|
|
3568
|
+
terminalRaceError = error instanceof Error ? error.message : "The requested session became terminal before this action could execute.";
|
|
3569
|
+
throw error;
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
const result = await action.run(inputBuild.inputs, {
|
|
3573
|
+
actorDid: params.actorDid,
|
|
3574
|
+
flowId,
|
|
3575
|
+
sessionRunId,
|
|
3576
|
+
designPodAuthority: params.designPodAuthority,
|
|
3577
|
+
flowUri,
|
|
3578
|
+
nodeId: blockId,
|
|
3579
|
+
flowNode,
|
|
3580
|
+
runtime,
|
|
3581
|
+
services: actionServices,
|
|
3582
|
+
runEventLog: params.runEventLog,
|
|
3583
|
+
handlers: params.handlers,
|
|
3584
|
+
editor,
|
|
3585
|
+
yDoc,
|
|
3586
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
3587
|
+
topic: params.topic,
|
|
3588
|
+
flowRevision: params.flowRevision,
|
|
3589
|
+
...executionProof.invocationCid ? {
|
|
3590
|
+
executionProof: {
|
|
3591
|
+
invocationCid: executionProof.invocationCid,
|
|
3592
|
+
invocation: executionProof.invocation,
|
|
3593
|
+
capabilityId: executionProof.capabilityId,
|
|
3594
|
+
proofCids: executionProof.proofCids,
|
|
3595
|
+
capability: executionProof.capability,
|
|
3596
|
+
facts: executionProof.facts,
|
|
3597
|
+
inputDigest
|
|
3598
|
+
}
|
|
3599
|
+
} : {}
|
|
3600
|
+
});
|
|
3601
|
+
if (result.events?.length) events.push(...result.events);
|
|
3602
|
+
if (result.topicRecords?.length) topicRecords = result.topicRecords;
|
|
3603
|
+
if (result.completion?.state === "awaiting_readback") {
|
|
3604
|
+
requestedAwaitingReadBack = true;
|
|
3605
|
+
rawReadBack = result.completion.readBack;
|
|
3606
|
+
} else {
|
|
3607
|
+
const proofCheck = validateActionProof(action, result.output || {});
|
|
3608
|
+
if (!proofCheck.valid) {
|
|
3609
|
+
proofFailureReason = proofCheck.reason || "Action returned success without proof of execution.";
|
|
3610
|
+
proofFailureOutput = result.output || {};
|
|
3611
|
+
throw new Error(proofFailureReason);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
return {
|
|
3615
|
+
payload: result.output,
|
|
3616
|
+
submittedByDid: params.actorDid || void 0
|
|
3617
|
+
};
|
|
3618
|
+
},
|
|
3619
|
+
pin: params.pin || ""
|
|
3620
|
+
});
|
|
3621
|
+
if (!outcome.success) {
|
|
3622
|
+
const message = outcome.error || "Action execution failed";
|
|
3623
|
+
if (terminalRaceError) {
|
|
3624
|
+
return {
|
|
3625
|
+
...buildFailureResult({ blockId, actionType, stage: outcome.stage, error: terminalRaceError, pendingInvocation: inputBuild.pendingInvocation }),
|
|
3626
|
+
invocationCid: outcome.invocationCid,
|
|
3627
|
+
capabilityId: outcome.capabilityId,
|
|
3628
|
+
executionId,
|
|
3629
|
+
runId: executionId
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
if (proofFailureReason) {
|
|
3633
|
+
const proofFailureState = action.sideEffect ? "needs_verification" : "failed";
|
|
3634
|
+
const boundedProofFailure = boundRunActionOutput(proofFailureOutput || {});
|
|
3635
|
+
runtime.update(blockId, {
|
|
3636
|
+
state: proofFailureState,
|
|
3637
|
+
output: boundedProofFailure.output,
|
|
3638
|
+
error: { message, code: PROOF_MISSING_CODE, at: now() }
|
|
3639
|
+
});
|
|
3640
|
+
if (sessionRunId && (eventLog || timelineRequired)) {
|
|
3641
|
+
await appendRunTimelineEvent({
|
|
3642
|
+
eventLog,
|
|
3643
|
+
event: {
|
|
3644
|
+
runId: sessionRunId,
|
|
3645
|
+
blockId,
|
|
3646
|
+
kind: "action.failed",
|
|
3647
|
+
payload: {
|
|
3648
|
+
attempt,
|
|
3649
|
+
executionId,
|
|
3650
|
+
durationMs: Math.max(0, now() - startedAt),
|
|
3651
|
+
error: { message, code: PROOF_MISSING_CODE }
|
|
3652
|
+
},
|
|
3653
|
+
idempotencyKey: createRunEventIdempotencyKey("action.failed", sessionRunId, blockId, executionId),
|
|
3654
|
+
ts: now()
|
|
3655
|
+
},
|
|
3656
|
+
runtime,
|
|
3657
|
+
blockId,
|
|
3658
|
+
now
|
|
3659
|
+
});
|
|
3660
|
+
}
|
|
3661
|
+
return {
|
|
3662
|
+
...buildFailureResult({
|
|
3663
|
+
blockId,
|
|
3664
|
+
actionType,
|
|
3665
|
+
stage: outcome.stage,
|
|
3666
|
+
error: message,
|
|
3667
|
+
pendingInvocation: inputBuild.pendingInvocation
|
|
3668
|
+
}),
|
|
3669
|
+
completionState: proofFailureState === "needs_verification" ? "needs_verification" : "failed",
|
|
3670
|
+
output: proofFailureOutput,
|
|
3671
|
+
invocationCid: outcome.invocationCid,
|
|
3672
|
+
capabilityId: outcome.capabilityId,
|
|
3673
|
+
executionId,
|
|
3674
|
+
runId: executionId,
|
|
3675
|
+
topicWriteBack: await recordTopicPhase(proofFailureState === "needs_verification" ? "needs_verification" : "failed", {
|
|
3676
|
+
output: proofFailureOutput,
|
|
3677
|
+
error: { code: PROOF_MISSING_CODE, message },
|
|
3678
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
3679
|
+
})
|
|
3680
|
+
};
|
|
3681
|
+
}
|
|
3682
|
+
if (outcome.stage === "authorization") {
|
|
3683
|
+
runtime.update(blockId, {
|
|
3684
|
+
state: previousState.state,
|
|
3685
|
+
output: previousState.output,
|
|
3686
|
+
error: previousState.error,
|
|
3687
|
+
executedByDid: previousState.executedByDid,
|
|
3688
|
+
attempt: previousState.attempt,
|
|
3689
|
+
executionId: previousState.executionId,
|
|
3690
|
+
executionStartedAt: previousState.executionStartedAt
|
|
3691
|
+
});
|
|
3692
|
+
return {
|
|
3693
|
+
...buildFailureResult({
|
|
3694
|
+
blockId,
|
|
3695
|
+
actionType,
|
|
3696
|
+
stage: outcome.stage,
|
|
3697
|
+
error: message,
|
|
3698
|
+
pendingInvocation: inputBuild.pendingInvocation
|
|
3699
|
+
}),
|
|
3700
|
+
invocationCid: outcome.invocationCid,
|
|
3701
|
+
capabilityId: outcome.capabilityId,
|
|
3702
|
+
executionId,
|
|
3703
|
+
runId: executionId
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
updateRuntimeFailure(runtime, blockId, message, now);
|
|
3707
|
+
if (sessionRunId && (eventLog || timelineRequired)) {
|
|
3708
|
+
await appendRunTimelineEvent({
|
|
3709
|
+
eventLog,
|
|
3710
|
+
event: {
|
|
3711
|
+
runId: sessionRunId,
|
|
3712
|
+
blockId,
|
|
3713
|
+
kind: "action.failed",
|
|
3714
|
+
payload: {
|
|
3715
|
+
attempt,
|
|
3716
|
+
executionId,
|
|
3717
|
+
durationMs: Math.max(0, now() - startedAt),
|
|
3718
|
+
error: { message }
|
|
3719
|
+
},
|
|
3720
|
+
idempotencyKey: createRunEventIdempotencyKey("action.failed", sessionRunId, blockId, executionId),
|
|
3721
|
+
ts: now()
|
|
3722
|
+
},
|
|
3723
|
+
runtime,
|
|
3724
|
+
blockId,
|
|
3725
|
+
now
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
const topicWriteBack2 = await recordTopicPhase("failed", {
|
|
3729
|
+
error: { message },
|
|
3730
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
3731
|
+
});
|
|
3732
|
+
return {
|
|
3733
|
+
...buildFailureResult({
|
|
3734
|
+
blockId,
|
|
3735
|
+
actionType,
|
|
3736
|
+
stage: outcome.stage,
|
|
3737
|
+
error: message,
|
|
3738
|
+
pendingInvocation: inputBuild.pendingInvocation
|
|
3739
|
+
}),
|
|
3740
|
+
invocationCid: outcome.invocationCid,
|
|
3741
|
+
capabilityId: outcome.capabilityId,
|
|
3742
|
+
executionId,
|
|
3743
|
+
runId: executionId,
|
|
3744
|
+
topicWriteBack: topicWriteBack2
|
|
3745
|
+
};
|
|
3746
|
+
}
|
|
3747
|
+
const output = outcome.result?.payload || {};
|
|
3748
|
+
const boundedOutput = boundRunActionOutput(output);
|
|
3749
|
+
const completedAt = now();
|
|
3750
|
+
if (requestedAwaitingReadBack) {
|
|
3751
|
+
completionState = "awaiting_readback";
|
|
3752
|
+
readBack = normalizeActionReadBackMetadata({
|
|
3753
|
+
readBack: rawReadBack,
|
|
3754
|
+
blockId,
|
|
3755
|
+
actionType,
|
|
3756
|
+
actorDid: params.actorDid,
|
|
3757
|
+
invocationCid: outcome.invocationCid,
|
|
3758
|
+
capabilityId: outcome.capabilityId,
|
|
3759
|
+
requestedAt: startedAt,
|
|
3760
|
+
pendingInvocation: inputBuild.pendingInvocation
|
|
3761
|
+
});
|
|
3762
|
+
}
|
|
3763
|
+
if (boundedOutput.exceeded) {
|
|
3764
|
+
completionState = action.sideEffect ? "needs_verification" : "failed";
|
|
3765
|
+
}
|
|
3766
|
+
runtime.update(blockId, {
|
|
3767
|
+
state: completionState,
|
|
3768
|
+
output: boundedOutput.output,
|
|
3769
|
+
executedByDid: params.actorDid,
|
|
3770
|
+
executedAt: completedAt,
|
|
3771
|
+
lastInvocationCid: outcome.invocationCid || outcome.capabilityId,
|
|
3772
|
+
readBack,
|
|
3773
|
+
...boundedOutput.exceeded ? {
|
|
3774
|
+
error: {
|
|
3775
|
+
message: `Action output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.`,
|
|
3776
|
+
code: "RUN_OUTPUT_TOO_LARGE",
|
|
3777
|
+
at: completedAt,
|
|
3778
|
+
data: { byteLength: boundedOutput.byteLength }
|
|
3779
|
+
}
|
|
3780
|
+
} : { error: void 0 }
|
|
3781
|
+
});
|
|
3782
|
+
persistEvents({
|
|
3783
|
+
yDoc,
|
|
3784
|
+
editor: getReconcileEditor2(params, yDoc, editor),
|
|
3785
|
+
sessionRunId,
|
|
3786
|
+
blockId,
|
|
3787
|
+
actorDid: params.actorDid,
|
|
3788
|
+
output: boundedOutput.output,
|
|
3789
|
+
events,
|
|
3790
|
+
invocationCid: outcome.invocationCid,
|
|
3791
|
+
capabilityId: outcome.capabilityId,
|
|
3792
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
3793
|
+
startedAt,
|
|
3794
|
+
completedAt,
|
|
3795
|
+
now,
|
|
3796
|
+
executionId
|
|
3797
|
+
});
|
|
3798
|
+
if (sessionRunId && (eventLog || timelineRequired)) {
|
|
3799
|
+
const timelineEvents = [
|
|
3800
|
+
{
|
|
3801
|
+
runId: sessionRunId,
|
|
3802
|
+
blockId,
|
|
3803
|
+
kind: "action.output",
|
|
3804
|
+
payload: {
|
|
3805
|
+
attempt,
|
|
3806
|
+
executionId,
|
|
3807
|
+
output: boundRunEventJson(boundedOutput.output)
|
|
3808
|
+
},
|
|
3809
|
+
idempotencyKey: createRunEventIdempotencyKey("action.output", sessionRunId, blockId, executionId, 1),
|
|
3810
|
+
ts: completedAt
|
|
3811
|
+
}
|
|
3812
|
+
];
|
|
3813
|
+
if (completionState === "completed") {
|
|
3814
|
+
timelineEvents.push({
|
|
3815
|
+
runId: sessionRunId,
|
|
3816
|
+
blockId,
|
|
3817
|
+
kind: "action.done",
|
|
3818
|
+
payload: {
|
|
3819
|
+
attempt,
|
|
3820
|
+
executionId,
|
|
3821
|
+
durationMs: Math.max(0, completedAt - startedAt)
|
|
3822
|
+
},
|
|
3823
|
+
idempotencyKey: createRunEventIdempotencyKey("action.done", sessionRunId, blockId, executionId),
|
|
3824
|
+
ts: completedAt
|
|
3825
|
+
});
|
|
3826
|
+
} else if (boundedOutput.exceeded) {
|
|
3827
|
+
timelineEvents.push({
|
|
3828
|
+
runId: sessionRunId,
|
|
3829
|
+
blockId,
|
|
3830
|
+
kind: "action.failed",
|
|
3831
|
+
payload: {
|
|
3832
|
+
attempt,
|
|
3833
|
+
executionId,
|
|
3834
|
+
durationMs: Math.max(0, completedAt - startedAt),
|
|
3835
|
+
error: {
|
|
3836
|
+
message: `Action output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.`,
|
|
3837
|
+
code: "RUN_OUTPUT_TOO_LARGE",
|
|
3838
|
+
details: {
|
|
3839
|
+
byteLength: boundedOutput.byteLength,
|
|
3840
|
+
maxBytes: MAX_RUN_ACTION_OUTPUT_BYTES
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
},
|
|
3844
|
+
idempotencyKey: createRunEventIdempotencyKey("action.failed", sessionRunId, blockId, executionId),
|
|
3845
|
+
ts: completedAt
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3848
|
+
await appendRunTimelineEvents({
|
|
3849
|
+
eventLog,
|
|
3850
|
+
events: timelineEvents,
|
|
3851
|
+
runtime,
|
|
3852
|
+
blockId,
|
|
3853
|
+
now
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
const pendingInvocationRemoved = completionState === "completed" ? cleanupCompletedPendingInvocation(yDoc, blockId, inputBuild.pendingInvocation, sessionRunId) : false;
|
|
3857
|
+
const topicWriteBack = await recordTopicPhase(completionState === "completed" ? "succeeded" : completionState === "needs_verification" ? "needs_verification" : "failed", {
|
|
3858
|
+
output,
|
|
3859
|
+
...boundedOutput.exceeded ? { error: { code: "RUN_OUTPUT_TOO_LARGE", message: `Action output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.` } } : {},
|
|
3860
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
3861
|
+
});
|
|
3862
|
+
return {
|
|
3863
|
+
success: !boundedOutput.exceeded,
|
|
3864
|
+
stage: outcome.stage,
|
|
3865
|
+
blockId,
|
|
3866
|
+
actionType,
|
|
3867
|
+
output,
|
|
3868
|
+
...boundedOutput.exceeded ? {
|
|
3869
|
+
error: `Action output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.`
|
|
3870
|
+
} : {},
|
|
3871
|
+
events,
|
|
3872
|
+
invocationCid: outcome.invocationCid,
|
|
3873
|
+
capabilityId: outcome.capabilityId,
|
|
3874
|
+
// Both names, same value — see the field docs on ExecuteActionBlockResult.
|
|
3875
|
+
runId: executionId,
|
|
3876
|
+
executionId,
|
|
3877
|
+
pendingInvocationRemoved,
|
|
3878
|
+
completionState,
|
|
3879
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
3880
|
+
topicWriteBack
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
// src/core/lib/flowEngine/verifiedCompletion.ts
|
|
3885
|
+
var RUN_PROJECTIONS_MAP_KEY = "qi.flow.runProjections";
|
|
3886
|
+
function runLedgerAttestations(yDoc) {
|
|
3887
|
+
let index = null;
|
|
3888
|
+
const build = () => {
|
|
3889
|
+
const found = /* @__PURE__ */ new Map();
|
|
3890
|
+
if (!yDoc) return found;
|
|
3891
|
+
const projections = yDoc.getMap(RUN_PROJECTIONS_MAP_KEY);
|
|
3892
|
+
projections.forEach((raw) => {
|
|
3893
|
+
const projection = raw;
|
|
3894
|
+
if (!projection || typeof projection !== "object") return;
|
|
3895
|
+
const runId = typeof projection.runId === "string" ? projection.runId : void 0;
|
|
3896
|
+
const attempts = projection.attempts;
|
|
3897
|
+
if (!attempts || typeof attempts !== "object") return;
|
|
3898
|
+
for (const [attemptId, value] of Object.entries(attempts)) {
|
|
3899
|
+
const attempt = value;
|
|
3900
|
+
if (!attempt || typeof attempt !== "object") continue;
|
|
3901
|
+
const existing = found.get(attemptId);
|
|
3902
|
+
if (existing?.disposition === "succeeded") continue;
|
|
3903
|
+
found.set(attemptId, {
|
|
3904
|
+
attemptId,
|
|
3905
|
+
...runId ? { runId } : {},
|
|
3906
|
+
...typeof attempt.stepId === "string" ? { stepId: attempt.stepId } : {},
|
|
3907
|
+
...typeof attempt.disposition === "string" ? { disposition: attempt.disposition } : {}
|
|
3908
|
+
});
|
|
3909
|
+
}
|
|
3910
|
+
});
|
|
3911
|
+
return found;
|
|
3912
|
+
};
|
|
3913
|
+
return (attemptId) => (index ?? (index = build())).get(attemptId);
|
|
3914
|
+
}
|
|
3915
|
+
function verifyCompletion(params) {
|
|
3916
|
+
const { runtime, blockId, actionType, getInvocation, getRunAttestation, schemaVersion } = params;
|
|
3917
|
+
if (!runtime || runtime.state !== "completed") {
|
|
3918
|
+
return { status: "not_completed" };
|
|
3919
|
+
}
|
|
3920
|
+
if (runtime.manuallyVerified === true) {
|
|
3921
|
+
return { status: "verified" };
|
|
3922
|
+
}
|
|
3923
|
+
if (actionType) {
|
|
3924
|
+
const action = getAction(actionType);
|
|
3925
|
+
if (action) {
|
|
3926
|
+
const proofCheck = validateActionProof(action, runtime.output || {});
|
|
3927
|
+
if (!proofCheck.valid) {
|
|
3928
|
+
return { status: "unverified", reason: proofCheck.reason || "Completed without proof of execution." };
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
const policy = schemaVersion ? getVersionPolicy(schemaVersion) : null;
|
|
3933
|
+
if (!policy?.ucanRequired) {
|
|
3934
|
+
return { status: "verified" };
|
|
3935
|
+
}
|
|
3936
|
+
const attemptId = typeof runtime.attemptId === "string" ? runtime.attemptId : void 0;
|
|
3937
|
+
const attestation = attemptId ? getRunAttestation?.(attemptId) : void 0;
|
|
3938
|
+
if (attestation) {
|
|
3939
|
+
if (attestation.disposition !== "succeeded") {
|
|
3940
|
+
return {
|
|
3941
|
+
status: "unverified",
|
|
3942
|
+
reason: `The run ledger records attempt ${attemptId} as ${attestation.disposition ?? "unfinished"}, not succeeded.`
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
if (attestation.stepId && attestation.stepId !== blockId) {
|
|
3946
|
+
return { status: "unverified", reason: "The run ledger records this attempt against a different block." };
|
|
3947
|
+
}
|
|
3948
|
+
return { status: "verified" };
|
|
3949
|
+
}
|
|
3950
|
+
const invocationCid = runtime.lastInvocationCid;
|
|
3951
|
+
if (!invocationCid) {
|
|
3952
|
+
return {
|
|
3953
|
+
status: "unverified",
|
|
3954
|
+
reason: attemptId ? `Completed with no invocation record and no run-ledger attestation for attempt ${attemptId}.` : "Completed without an invocation record on a UCAN-enforced flow."
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
if (!getInvocation) {
|
|
3958
|
+
return { status: "unverified", reason: "No invocation store available to verify the completion." };
|
|
3959
|
+
}
|
|
3960
|
+
const invocation = getInvocation(invocationCid);
|
|
3961
|
+
if (!invocation) {
|
|
3962
|
+
return { status: "unverified", reason: `Invocation ${invocationCid} not found in the invocation store.` };
|
|
3963
|
+
}
|
|
3964
|
+
if (invocation.result !== "success") {
|
|
3965
|
+
return { status: "unverified", reason: "Completion references a failed invocation." };
|
|
3966
|
+
}
|
|
3967
|
+
if (invocation.blockId && invocation.blockId !== blockId) {
|
|
3968
|
+
return { status: "unverified", reason: "Completion references an invocation for a different block." };
|
|
3969
|
+
}
|
|
3970
|
+
return { status: "verified" };
|
|
3971
|
+
}
|
|
3972
|
+
function isVerifiedCompletion(params) {
|
|
3973
|
+
return verifyCompletion(params).status === "verified";
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
// src/core/lib/invocationStore.ts
|
|
3977
|
+
var createInvocationStore = (yMap) => {
|
|
3978
|
+
const add = (invocation) => {
|
|
3979
|
+
yMap.set(invocation.cid, invocation);
|
|
3980
|
+
};
|
|
3981
|
+
const get = (cid) => {
|
|
3982
|
+
const raw = yMap.get(cid);
|
|
3983
|
+
if (!raw || typeof raw !== "object") return null;
|
|
3984
|
+
return raw;
|
|
3985
|
+
};
|
|
3986
|
+
const remove = (cid) => {
|
|
3987
|
+
yMap.delete(cid);
|
|
3988
|
+
};
|
|
3989
|
+
const getAll = () => {
|
|
3990
|
+
const invocations = [];
|
|
3991
|
+
yMap.forEach((value) => {
|
|
3992
|
+
if (value && typeof value === "object" && "cid" in value) {
|
|
3993
|
+
invocations.push(value);
|
|
3994
|
+
}
|
|
3995
|
+
});
|
|
3996
|
+
return invocations.sort((a, b) => b.executedAt - a.executedAt);
|
|
3997
|
+
};
|
|
3998
|
+
const getByInvoker = (invokerDid) => {
|
|
3999
|
+
return getAll().filter((i) => i.invokerDid === invokerDid);
|
|
4000
|
+
};
|
|
4001
|
+
const getByFlow = (flowId) => {
|
|
4002
|
+
return getAll().filter((i) => i.flowId === flowId);
|
|
4003
|
+
};
|
|
4004
|
+
const getByBlock = (flowId, blockId) => {
|
|
4005
|
+
return getAll().filter((i) => i.flowId === flowId && i.blockId === blockId);
|
|
4006
|
+
};
|
|
4007
|
+
const getByCapability = (can, withUri) => {
|
|
4008
|
+
return getAll().filter((i) => i.capability.can === can && i.capability.with === withUri);
|
|
4009
|
+
};
|
|
4010
|
+
const hasBeenInvoked = (cid) => {
|
|
4011
|
+
return yMap.has(cid);
|
|
4012
|
+
};
|
|
4013
|
+
const getInDateRange = (startMs, endMs) => {
|
|
4014
|
+
return getAll().filter((i) => i.executedAt >= startMs && i.executedAt <= endMs);
|
|
4015
|
+
};
|
|
4016
|
+
const getSuccessful = () => {
|
|
4017
|
+
return getAll().filter((i) => i.result === "success");
|
|
4018
|
+
};
|
|
4019
|
+
const getFailures = () => {
|
|
4020
|
+
return getAll().filter((i) => i.result === "failure");
|
|
4021
|
+
};
|
|
4022
|
+
const getPendingSubmission = () => {
|
|
4023
|
+
return getAll().filter((i) => i.result === "success" && !i.transactionHash);
|
|
4024
|
+
};
|
|
4025
|
+
const markSubmitted = (cid, transactionHash) => {
|
|
4026
|
+
const invocation = get(cid);
|
|
4027
|
+
if (invocation) {
|
|
4028
|
+
const updated = {
|
|
4029
|
+
...invocation,
|
|
4030
|
+
transactionHash
|
|
4031
|
+
};
|
|
4032
|
+
yMap.set(cid, updated);
|
|
4033
|
+
}
|
|
4034
|
+
};
|
|
4035
|
+
const getCount = () => {
|
|
4036
|
+
return getAll().length;
|
|
4037
|
+
};
|
|
4038
|
+
const getCountByResult = () => {
|
|
4039
|
+
const all = getAll();
|
|
4040
|
+
return {
|
|
4041
|
+
success: all.filter((i) => i.result === "success").length,
|
|
4042
|
+
failure: all.filter((i) => i.result === "failure").length
|
|
4043
|
+
};
|
|
4044
|
+
};
|
|
4045
|
+
return {
|
|
4046
|
+
add,
|
|
4047
|
+
get,
|
|
4048
|
+
remove,
|
|
4049
|
+
getAll,
|
|
4050
|
+
getByInvoker,
|
|
4051
|
+
getByFlow,
|
|
4052
|
+
getByBlock,
|
|
4053
|
+
getByCapability,
|
|
4054
|
+
hasBeenInvoked,
|
|
4055
|
+
getInDateRange,
|
|
4056
|
+
getSuccessful,
|
|
4057
|
+
getFailures,
|
|
4058
|
+
getPendingSubmission,
|
|
4059
|
+
markSubmitted,
|
|
4060
|
+
getCount,
|
|
4061
|
+
getCountByResult
|
|
4062
|
+
};
|
|
4063
|
+
};
|
|
4064
|
+
var createMemoryInvocationStore = () => {
|
|
4065
|
+
const store = /* @__PURE__ */ new Map();
|
|
4066
|
+
const add = (invocation) => {
|
|
4067
|
+
store.set(invocation.cid, invocation);
|
|
4068
|
+
};
|
|
4069
|
+
const get = (cid) => {
|
|
4070
|
+
return store.get(cid) || null;
|
|
4071
|
+
};
|
|
4072
|
+
const remove = (cid) => {
|
|
4073
|
+
store.delete(cid);
|
|
4074
|
+
};
|
|
4075
|
+
const getAll = () => {
|
|
4076
|
+
const invocations = Array.from(store.values());
|
|
4077
|
+
return invocations.sort((a, b) => b.executedAt - a.executedAt);
|
|
4078
|
+
};
|
|
4079
|
+
const getByInvoker = (invokerDid) => {
|
|
4080
|
+
return getAll().filter((i) => i.invokerDid === invokerDid);
|
|
4081
|
+
};
|
|
4082
|
+
const getByFlow = (flowId) => {
|
|
4083
|
+
return getAll().filter((i) => i.flowId === flowId);
|
|
4084
|
+
};
|
|
4085
|
+
const getByBlock = (flowId, blockId) => {
|
|
4086
|
+
return getAll().filter((i) => i.flowId === flowId && i.blockId === blockId);
|
|
4087
|
+
};
|
|
4088
|
+
const getByCapability = (can, withUri) => {
|
|
4089
|
+
return getAll().filter((i) => i.capability.can === can && i.capability.with === withUri);
|
|
4090
|
+
};
|
|
4091
|
+
const hasBeenInvoked = (cid) => {
|
|
4092
|
+
return store.has(cid);
|
|
4093
|
+
};
|
|
4094
|
+
const getInDateRange = (startMs, endMs) => {
|
|
4095
|
+
return getAll().filter((i) => i.executedAt >= startMs && i.executedAt <= endMs);
|
|
4096
|
+
};
|
|
4097
|
+
const getSuccessful = () => {
|
|
4098
|
+
return getAll().filter((i) => i.result === "success");
|
|
4099
|
+
};
|
|
4100
|
+
const getFailures = () => {
|
|
4101
|
+
return getAll().filter((i) => i.result === "failure");
|
|
4102
|
+
};
|
|
4103
|
+
const getPendingSubmission = () => {
|
|
4104
|
+
return getAll().filter((i) => i.result === "success" && !i.transactionHash);
|
|
4105
|
+
};
|
|
4106
|
+
const markSubmitted = (cid, transactionHash) => {
|
|
4107
|
+
const invocation = get(cid);
|
|
4108
|
+
if (invocation) {
|
|
4109
|
+
store.set(cid, { ...invocation, transactionHash });
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
const getCount = () => {
|
|
4113
|
+
return store.size;
|
|
4114
|
+
};
|
|
4115
|
+
const getCountByResult = () => {
|
|
4116
|
+
const all = getAll();
|
|
4117
|
+
return {
|
|
4118
|
+
success: all.filter((i) => i.result === "success").length,
|
|
4119
|
+
failure: all.filter((i) => i.result === "failure").length
|
|
4120
|
+
};
|
|
4121
|
+
};
|
|
4122
|
+
return {
|
|
4123
|
+
add,
|
|
4124
|
+
get,
|
|
4125
|
+
remove,
|
|
4126
|
+
getAll,
|
|
4127
|
+
getByInvoker,
|
|
4128
|
+
getByFlow,
|
|
4129
|
+
getByBlock,
|
|
4130
|
+
getByCapability,
|
|
4131
|
+
hasBeenInvoked,
|
|
4132
|
+
getInDateRange,
|
|
4133
|
+
getSuccessful,
|
|
4134
|
+
getFailures,
|
|
4135
|
+
getPendingSubmission,
|
|
4136
|
+
markSubmitted,
|
|
4137
|
+
getCount,
|
|
4138
|
+
getCountByResult
|
|
4139
|
+
};
|
|
4140
|
+
};
|
|
4141
|
+
|
|
4142
|
+
// src/core/lib/flowEngine/blockPhase.ts
|
|
4143
|
+
function deriveBlockPhase(params) {
|
|
4144
|
+
const { runtime, actionType, pendingInvocationCount = 0, runCount, done = false } = params;
|
|
4145
|
+
const repeatable = isRepeatableAction(actionType);
|
|
4146
|
+
const state = runtime?.state ?? "idle";
|
|
4147
|
+
const base = { repeatable, ...repeatable && runCount !== void 0 ? { runCount } : {} };
|
|
4148
|
+
switch (state) {
|
|
4149
|
+
case "idle":
|
|
4150
|
+
if (pendingInvocationCount > 0) {
|
|
4151
|
+
return { ...base, phase: "active", detail: "queued", safeToRetry: true };
|
|
4152
|
+
}
|
|
4153
|
+
return { ...base, phase: "pending", detail: "idle", safeToRetry: true };
|
|
4154
|
+
case "running":
|
|
4155
|
+
return { ...base, phase: "active", detail: "running", safeToRetry: false };
|
|
4156
|
+
case "pending":
|
|
4157
|
+
return { ...base, phase: "active", detail: "pending", safeToRetry: false };
|
|
4158
|
+
case "retry_scheduled":
|
|
4159
|
+
return { ...base, phase: "active", detail: "retry_scheduled", safeToRetry: false };
|
|
4160
|
+
case "awaiting_readback":
|
|
4161
|
+
return { ...base, phase: "active", detail: "awaiting_readback", safeToRetry: false };
|
|
4162
|
+
case "awaiting_human":
|
|
4163
|
+
return { ...base, phase: "failed", detail: "awaiting_human", safeToRetry: false };
|
|
4164
|
+
case "completed":
|
|
4165
|
+
if (repeatable) {
|
|
4166
|
+
if (done && pendingInvocationCount === 0) {
|
|
4167
|
+
return { ...base, phase: "complete", detail: "drained", safeToRetry: true };
|
|
4168
|
+
}
|
|
4169
|
+
return { ...base, phase: "active", detail: "standing", safeToRetry: true };
|
|
4170
|
+
}
|
|
4171
|
+
return { ...base, phase: "complete", detail: "completed", safeToRetry: false };
|
|
4172
|
+
case "cancelled":
|
|
4173
|
+
return { ...base, phase: "complete", detail: "cancelled", safeToRetry: false };
|
|
4174
|
+
case "failed":
|
|
4175
|
+
return { ...base, phase: "failed", detail: "failed", safeToRetry: true };
|
|
4176
|
+
case "needs_verification":
|
|
4177
|
+
return { ...base, phase: "failed", detail: "needs_verification", safeToRetry: false };
|
|
4178
|
+
default:
|
|
4179
|
+
return { ...base, phase: "failed", detail: "needs_verification", safeToRetry: false };
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
function countSuccessfulRuns(yDoc, blockId) {
|
|
4183
|
+
return readRunRecords(yDoc, blockId).filter((record) => !record.error).length;
|
|
4184
|
+
}
|
|
4185
|
+
|
|
4186
|
+
// src/core/lib/flowEngine/validateBlockConfig.ts
|
|
4187
|
+
function validateBlockConfig(block) {
|
|
4188
|
+
const issues = [];
|
|
4189
|
+
if (!block?.props) return issues;
|
|
4190
|
+
const checkJson = (propName, value, expectObject = true) => {
|
|
4191
|
+
if (value == null || value === "") return;
|
|
4192
|
+
if (typeof value !== "string") return;
|
|
4193
|
+
try {
|
|
4194
|
+
const parsed = JSON.parse(value);
|
|
4195
|
+
if (expectObject && (parsed == null || typeof parsed !== "object")) {
|
|
4196
|
+
issues.push(`${propName} is valid JSON but not an object`);
|
|
4197
|
+
}
|
|
4198
|
+
} catch {
|
|
4199
|
+
issues.push(`${propName} is not valid JSON`);
|
|
4200
|
+
}
|
|
4201
|
+
};
|
|
4202
|
+
checkJson("inputs", block.props.inputs);
|
|
4203
|
+
checkJson("trigger", block.props.trigger);
|
|
4204
|
+
checkJson("assignment", block.props.assignment);
|
|
4205
|
+
checkJson("conditions", block.props.conditions);
|
|
4206
|
+
return issues;
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
// src/core/lib/flowEngine/migration.ts
|
|
4210
|
+
var MIGRATION_REGISTRY = {};
|
|
4211
|
+
function registerMigration(definition) {
|
|
4212
|
+
const key = `${definition.from}->${definition.to}`;
|
|
4213
|
+
MIGRATION_REGISTRY[key] = definition;
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
// src/core/lib/flowEngine/migrations/v0_3_to_v1_0_0.ts
|
|
4217
|
+
var NON_EXECUTABLE_BLOCK_TYPES = /* @__PURE__ */ new Set(["paragraph", "heading", "bulletListItem", "numberedListItem", "image", "table"]);
|
|
4218
|
+
function hasDelegationForBlock(ctx, blockId) {
|
|
4219
|
+
const delegations = ctx.ucanService.getAllDelegations();
|
|
4220
|
+
const resourceUri = `${ctx.flowUri}:${blockId}`;
|
|
4221
|
+
return delegations.some((d) => d.capabilities.some((c) => c.can === "flow/block/execute" && c.with === resourceUri));
|
|
4222
|
+
}
|
|
4223
|
+
var migration_0_3_to_1_0_0 = {
|
|
4224
|
+
from: "0.3",
|
|
4225
|
+
to: "1.0.0",
|
|
4226
|
+
preconditions: [
|
|
4227
|
+
{
|
|
4228
|
+
id: "has_flow_owner",
|
|
4229
|
+
description: "Flow must have a flowOwnerDid set",
|
|
4230
|
+
check: (ctx) => {
|
|
4231
|
+
const ownerDid = ctx.editor.getFlowOwnerDid?.() || ctx.flowOwnerDid;
|
|
4232
|
+
return !!ownerDid;
|
|
4233
|
+
},
|
|
4234
|
+
blocking: true
|
|
4235
|
+
},
|
|
4236
|
+
{
|
|
4237
|
+
id: "is_owner",
|
|
4238
|
+
description: "Only the flow owner can initiate migration",
|
|
4239
|
+
check: (ctx) => {
|
|
4240
|
+
const ownerDid = ctx.editor.getFlowOwnerDid?.() || ctx.flowOwnerDid;
|
|
4241
|
+
return ctx.actorDid === ownerDid;
|
|
4242
|
+
},
|
|
4243
|
+
blocking: true
|
|
4244
|
+
},
|
|
4245
|
+
{
|
|
4246
|
+
id: "ucan_service_available",
|
|
4247
|
+
description: "UCAN service must be configured",
|
|
4248
|
+
check: (ctx) => {
|
|
4249
|
+
return !!ctx.ucanService && ctx.ucanService.isConfigured();
|
|
4250
|
+
},
|
|
4251
|
+
blocking: true
|
|
4252
|
+
}
|
|
4253
|
+
],
|
|
4254
|
+
async analyze(ctx) {
|
|
4255
|
+
const items = [];
|
|
4256
|
+
const blocks = ctx.editor.document;
|
|
4257
|
+
const rootDelegation = ctx.ucanService.getRootDelegation();
|
|
4258
|
+
if (!rootDelegation) {
|
|
4259
|
+
items.push({
|
|
4260
|
+
id: "__root_delegation__",
|
|
4261
|
+
nodeId: "",
|
|
4262
|
+
type: "create_root_delegation",
|
|
4263
|
+
status: "pending",
|
|
4264
|
+
requiresUserAction: true,
|
|
4265
|
+
description: "Create root delegation for flow owner"
|
|
4266
|
+
});
|
|
4267
|
+
}
|
|
4268
|
+
if (blocks) {
|
|
4269
|
+
for (const block of blocks) {
|
|
4270
|
+
if (NON_EXECUTABLE_BLOCK_TYPES.has(block.type)) continue;
|
|
4271
|
+
if (!hasDelegationForBlock(ctx, block.id)) {
|
|
4272
|
+
items.push({
|
|
4273
|
+
id: `node_delegation_${block.id}`,
|
|
4274
|
+
nodeId: block.id,
|
|
4275
|
+
type: "create_node_delegation",
|
|
4276
|
+
status: "pending",
|
|
4277
|
+
requiresUserAction: true,
|
|
4278
|
+
description: `Create delegation for block "${block.type}" (${block.id})`
|
|
4279
|
+
});
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
return items;
|
|
4284
|
+
},
|
|
4285
|
+
async executeItem(ctx, item) {
|
|
4286
|
+
try {
|
|
4287
|
+
switch (item.type) {
|
|
4288
|
+
case "create_root_delegation": {
|
|
4289
|
+
await ctx.ucanService.createRootDelegation({
|
|
4290
|
+
flowOwnerDid: ctx.flowOwnerDid,
|
|
4291
|
+
issuerType: ctx.actorType,
|
|
4292
|
+
entityRoomId: ctx.entityRoomId,
|
|
4293
|
+
flowUri: ctx.flowUri,
|
|
4294
|
+
pin: ctx.pin
|
|
4295
|
+
});
|
|
4296
|
+
return { success: true };
|
|
4297
|
+
}
|
|
4298
|
+
case "create_node_delegation":
|
|
4299
|
+
case "backfill_capability": {
|
|
4300
|
+
const capability = {
|
|
4301
|
+
can: "flow/block/execute",
|
|
4302
|
+
with: `${ctx.flowUri}:${item.nodeId}`
|
|
4303
|
+
};
|
|
4304
|
+
await ctx.ucanService.createDelegation({
|
|
4305
|
+
issuerDid: ctx.flowOwnerDid,
|
|
4306
|
+
issuerType: ctx.actorType,
|
|
4307
|
+
entityRoomId: ctx.entityRoomId,
|
|
4308
|
+
audience: ctx.flowOwnerDid,
|
|
4309
|
+
// Owner delegates to self initially; actors get sub-delegations
|
|
4310
|
+
capabilities: [capability],
|
|
4311
|
+
pin: ctx.pin
|
|
4312
|
+
});
|
|
4313
|
+
return { success: true };
|
|
4314
|
+
}
|
|
4315
|
+
default:
|
|
4316
|
+
return { success: false, error: `Unknown work item type: ${item.type}` };
|
|
4317
|
+
}
|
|
4318
|
+
} catch (err) {
|
|
4319
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4320
|
+
return { success: false, error: message };
|
|
4321
|
+
}
|
|
4322
|
+
},
|
|
4323
|
+
async postValidate(ctx) {
|
|
4324
|
+
const errors = [];
|
|
4325
|
+
const blocks = ctx.editor.document;
|
|
4326
|
+
const rootDelegation = ctx.ucanService.getRootDelegation();
|
|
4327
|
+
if (!rootDelegation) {
|
|
4328
|
+
errors.push("Root delegation is missing after migration");
|
|
4329
|
+
}
|
|
4330
|
+
if (blocks) {
|
|
4331
|
+
for (const block of blocks) {
|
|
4332
|
+
if (NON_EXECUTABLE_BLOCK_TYPES.has(block.type)) continue;
|
|
4333
|
+
const requiredCapability = {
|
|
4334
|
+
can: "flow/block/execute",
|
|
4335
|
+
with: `${ctx.flowUri}:${block.id}`
|
|
4336
|
+
};
|
|
4337
|
+
const validation = await ctx.ucanService.validateDelegationChain(ctx.flowOwnerDid, requiredCapability);
|
|
4338
|
+
if (!validation.valid) {
|
|
4339
|
+
errors.push(`Block "${block.type}" (${block.id}): delegation chain invalid \u2014 ${validation.error || "unknown error"}`);
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
return { valid: errors.length === 0, errors };
|
|
4344
|
+
},
|
|
4345
|
+
finalize(ctx) {
|
|
4346
|
+
const root = ctx.editor._yRoot;
|
|
4347
|
+
if (!root) {
|
|
4348
|
+
throw new Error("Cannot finalize: editor _yRoot not available");
|
|
4349
|
+
}
|
|
4350
|
+
root.set("schema_version", "1.0.0");
|
|
4351
|
+
root.set("@context", "https://ixo.world/flow/1.0");
|
|
4352
|
+
}
|
|
4353
|
+
};
|
|
4354
|
+
registerMigration(migration_0_3_to_1_0_0);
|
|
4355
|
+
|
|
4356
|
+
export {
|
|
4357
|
+
isRuntimeRef,
|
|
4358
|
+
RUN_RECORD_AUDIT_TYPE,
|
|
4359
|
+
computePendingInvocationId,
|
|
4360
|
+
snapshotInputRefs,
|
|
4361
|
+
getPendingInvocationsMap,
|
|
4362
|
+
countPendingInvocations,
|
|
4363
|
+
getOrCreateBlockPendingMap,
|
|
4364
|
+
readPendingInvocations,
|
|
4365
|
+
queuePendingInvocation,
|
|
4366
|
+
removePendingInvocation,
|
|
4367
|
+
appendRunRecord,
|
|
4368
|
+
readRunRecords,
|
|
4369
|
+
findFailedListenersForSourceRun,
|
|
4370
|
+
replayFailedListenerRun,
|
|
4371
|
+
VERSION_MANIFEST,
|
|
4372
|
+
LATEST_VERSION,
|
|
4373
|
+
getVersionPolicy,
|
|
4374
|
+
isVersionAtLeast,
|
|
4375
|
+
reconcilePendingInvocations,
|
|
4376
|
+
dispatchFlowStartTriggers,
|
|
4377
|
+
getActionForBlock,
|
|
4378
|
+
writeRunRecordAndReconcile,
|
|
4379
|
+
resolveEditorYDoc,
|
|
4380
|
+
createRuntimeStateManager,
|
|
4381
|
+
createYDocRuntimeManager,
|
|
4382
|
+
clearRuntimeForTemplateClone,
|
|
4383
|
+
normalizeCondition,
|
|
4384
|
+
normalizeConditions,
|
|
4385
|
+
parseConditionConfig,
|
|
4386
|
+
InvalidConditionConfigError,
|
|
4387
|
+
parseConditionConfigStrict,
|
|
4388
|
+
resolveConditionSourceValue,
|
|
4389
|
+
evaluateCondition,
|
|
4390
|
+
evaluateBlockConditions,
|
|
4391
|
+
evaluateBlockConditionsForRun,
|
|
4392
|
+
evaluateBlockExecutionEligibilityForRun,
|
|
4393
|
+
buildAuthzFromProps,
|
|
4394
|
+
buildFlowNodeFromBlock,
|
|
4395
|
+
PROOF_MISSING_CODE,
|
|
4396
|
+
validateActionProof,
|
|
4397
|
+
isActionDone,
|
|
4398
|
+
isActionTypeDone,
|
|
4399
|
+
reconcileCompletion,
|
|
4400
|
+
reconcileCheckableCompletions,
|
|
4401
|
+
createIntentHash,
|
|
4402
|
+
parseJsonProp,
|
|
4403
|
+
getBlockActionType,
|
|
4404
|
+
getBlockTitle,
|
|
4405
|
+
getBlockType,
|
|
4406
|
+
getBlockProps,
|
|
4407
|
+
getBlockId,
|
|
4408
|
+
getAssigneeDid,
|
|
4409
|
+
getDueAt,
|
|
4410
|
+
capabilityForCommand,
|
|
4411
|
+
getFlowAgentMaps,
|
|
4412
|
+
computeAgentCommandId,
|
|
4413
|
+
queueAgentCommand,
|
|
4414
|
+
readAgentCommands,
|
|
4415
|
+
readQueuedAgentCommands,
|
|
4416
|
+
readFlowAgentLeases,
|
|
4417
|
+
updateAgentCommand,
|
|
4418
|
+
appendAgentLedgerEvent,
|
|
4419
|
+
readAgentLedgerEvents,
|
|
4420
|
+
RunLifecycleAuthorizationError,
|
|
4421
|
+
RunNotReadyError,
|
|
4422
|
+
FlowNotMigratedError,
|
|
4423
|
+
RunNotMigratedError,
|
|
4424
|
+
computeRunDefinitionHash,
|
|
4425
|
+
toRunJsonValue,
|
|
4426
|
+
buildRunDefinitionSnapshot,
|
|
4427
|
+
createRunUlid,
|
|
4428
|
+
createRunId,
|
|
4429
|
+
listRuns,
|
|
4430
|
+
listOpenRuns,
|
|
4431
|
+
setAdvisoryActiveRunId,
|
|
4432
|
+
startRun,
|
|
4433
|
+
migrateFlowToSessions,
|
|
4434
|
+
computeRunSummary,
|
|
4435
|
+
closeRun,
|
|
4436
|
+
cancelRun,
|
|
4437
|
+
detectRunDefinitionDrift,
|
|
4438
|
+
recordRunDefinitionDrift,
|
|
4439
|
+
readRunActionStates,
|
|
4440
|
+
resolveRuntimeRefs,
|
|
4441
|
+
isAuthorized,
|
|
4442
|
+
executeNode,
|
|
4443
|
+
normalizeActionReadBackMetadata,
|
|
4444
|
+
reconcileActionReadBack,
|
|
4445
|
+
buildActionRunInputs,
|
|
4446
|
+
executeActionBlock,
|
|
4447
|
+
runLedgerAttestations,
|
|
4448
|
+
verifyCompletion,
|
|
4449
|
+
isVerifiedCompletion,
|
|
4450
|
+
deriveBlockPhase,
|
|
4451
|
+
countSuccessfulRuns,
|
|
4452
|
+
validateBlockConfig,
|
|
4453
|
+
createInvocationStore,
|
|
4454
|
+
createMemoryInvocationStore
|
|
4455
|
+
};
|
|
4456
|
+
//# sourceMappingURL=chunk-HRRI4BLF.js.map
|