chainlesschain 0.163.4 → 0.163.6

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.
Files changed (200) hide show
  1. package/completions/_cc +1 -1
  2. package/completions/cc.bash +1 -1
  3. package/completions/cc.fish +1 -1
  4. package/completions/cc.ps1 +1 -1
  5. package/package.json +1 -1
  6. package/src/assets/web-panel/assets/{AIOps-DB5X7qqV.js → AIOps-DDIUofA8.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-DxHRtKLk.js → ActionButton-Bm-5xYRn.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-DIH5QrxT.js → Analytics-D5UH4ljI.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-uu0oxVz7.js → AppLayout-D_ifjOOP.js} +5 -5
  10. package/src/assets/web-panel/assets/{Artifacts-BNSMiW9Y.js → Artifacts-BY1mXdC7.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-DcAzB4_x.js → Audit-Bujr4Ml3.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-ClSsvjIc.js → BackgroundAgents-sioKSTcC.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-BMciY0js.js → Backup-DDOAK8RK.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-Hn3b57Sh.js → BaseInput-CtCsVVke.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-CoSlC53V.js → Chat-DpNj0vnd.js} +4 -4
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CFevJ3So.js → ChatBubbleRenderer-CZta3Ftr.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-B4L4Eu4w.js → Checkbox-DoHVNSXq.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-C77FgRxF.js → Codegen-DlVl7Qmv.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-BrLtJLH3.js → Col-GIqoPYty.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-Be7pXXhA.js → Community-CIAPEVji.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-DuGPecjo.js → Compact-BN7JiI_h.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-CzS66mjP.js → Compliance-CqUWCM9B.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-CEhjCoyK.js → Cowork-A4iPp7A4.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-D7ir8Vja.js → Cron-B2zeXXWb.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-BcXmuLxf.js → Crosschain-BR2xmWr8.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-Cto1dtBy.js → DID-BzFDjrIv.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-B7KjgwOX.js → Dashboard-DKtDo3yA.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-COMqxijg.js → Dropdown-RNx43_YG.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-BZrya-nf.js → EmailListRenderer-SwMt4Rcy.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-AmVeMq0J.js → FamilyGuardDashboard-CsYebZ4X.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-kZU8c-27.js → Federation-BmRKvBTy.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-sOV22v2-.js → FormItemContext-CVxGTA-0.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-BUuLtGCz.js → GenericCardRenderer-x2iXpyB7.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-K-ZxsLsH.js → Git-DfqbkiOP.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-D3OEnawI.js → Governance-RuHyyj5O.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-Bll0gRmP.js → Inference-Bn7uZ7rI.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-DBXwjr53.js → KnowledgeGraph-DlP7XefZ.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-CEzPwEfP.js → Logs-BYNdOiCd.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-aMtgN-R6.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-BYyRyOPz.js → Marketplace-JQYmDqM8.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-DVu8w_rM.js → McpTools-Cyx4yZ-9.js} +4 -4
  42. package/src/assets/web-panel/assets/{Memory-tenuZ_hO.js → Memory-VDuGZTN2.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-Bxgy4ozS.js → MobileBridge-_yKo97gi.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-Dgx1D6sc.js → MobileProjects-CXRXW3q7.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-CRfJ8r8j.js → Mtc-acTGtfLH.js} +5 -5
  46. package/src/assets/web-panel/assets/{MtcAudit-BBJq4Aiz.js → MtcAudit-ClpjXfkY.js} +4 -4
  47. package/src/assets/web-panel/assets/{Multisig-B3ii9lai.js → Multisig-_6Tn3o96.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-FIi-Cubv.js → NLProgramming-CK7pbZzP.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-DIq8MPf1.js → Notes-Czy6V_P8.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-BFOD9eGd.js → NotificationSettings-Cqx-UVv3.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer--41u7vre.js → OrderTableRenderer-C69WDxGy.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-B_0AWztW.js → Organization-DFnA9vLQ.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-B6I7TuXv.js → Overflow-ReZkk2yT.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-Cdf6qens.js → P2P-DzjSI0aI.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-B_0FcQJY.js → PdhVaultBrowser-DKTf5xIP.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-6nFBjsQp.js → Permissions-DhQ5klhR.js} +3 -3
  57. package/src/assets/web-panel/assets/{PersonalDataHub-nQYV7xpn.js → PersonalDataHub-CUL1iHrW.js} +3 -3
  58. package/src/assets/web-panel/assets/{Pipeline-CBVod5jm.js → Pipeline-DU88iu4N.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-D52jW1Nt.js → Privacy-Bw-sYfyK.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-Bdx3BYhf.js → ProjectInit-Bq3Q5y0z.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-b7xr4DUa.js → ProjectSettings-C8spzpho.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-DhfifCKx.js → Projects-Dd-xuPIc.js} +1 -1
  63. package/src/assets/web-panel/assets/Providers-Bf4eTP9W.js +1 -0
  64. package/src/assets/web-panel/assets/{QrScannerModal-FQKSmc3e.js → QrScannerModal-Dme9BDU1.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-CSTrhzcF.js → QuickAsk-CIXfv1rE.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-CxMABOUh.js → Recommend-CIxvdeZ-.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-Cv2T8yRa.js → RemoteSession-C6k7MExI.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-CE1k4Vvu.js → Reputation-CISDz2bl.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-BNgNcAV_.js → Row-CdnDxXyf.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-5ZiBZ3ph.js → RssFeed-C-QpRHsl.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-mDOIF1UV.js → Search-DBjaGzSn.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-CtuCTC-e.js → Security-CQKukOeE.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-DxFO2Zg7.js → Services-DZz86jWb.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-CKmKkH9y.js → Skeleton-jb8RMK0d.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-Bmzl9qkk.js → Skills-CdY76fZG.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-9RqaWHOC.js → Sla-C_CSiZX9.js} +1 -1
  77. package/src/assets/web-panel/assets/SpeechSettings-C7tT9iad.js +1 -0
  78. package/src/assets/web-panel/assets/{SyncSettings-CvNuCeBT.js → SyncSettings-qvimGO0O.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-B_sLdcnw.js → Tasks-Dng5LPJC.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-D9_YijdK.js → Templates-Bwqq5Wll.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-Cuc7BUSP.js → Tenant-DNWuwP7Z.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-6VYRnJgd.js → Terminal-B-6Othpp.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-GtK8xd8Y.js → TimelineRenderer-DKpKZfzx.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-BZ94Lt5W.js → Tokens-DzfGMd2J.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-DEcZgDaX.js → Trigger-BXwUi74J.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-CC3z9THe.js → Trust-BNzUq4ui.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-Cvtw8cV3.js → UkeySign-BYugxww4.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-CL4e6Bn3.js → VideoEditing-DdauhtSJ.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-ycEemP27.js → Wallet-CSC9bCe_.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DgZ4X_aw.js → WebAuthn-Q8AN3bTO.js} +5 -5
  91. package/src/assets/web-panel/assets/{WorkflowEditor-D0qMhSv7.js → WorkflowEditor-d-LBGCuA.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-DsAg01Zn.js → chat-BLVDCgsZ.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-B0oqAhrQ.js → colors-BZlPSEHl.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CmX0Rabz.js → compact-item-IWseP6R6.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-Be4cn1Ub.js → createContext-DnVRV3Ao.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-Jzu62CLC.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-D5v12kVq.js → hasIn-DLQZA_Wn.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BA4oulaP.js → index-1jeAudHd.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DM724NgU.js → index-BEu2lMBy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-5LGY0JbG.js → index-BOZJBTWZ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DikJ0Qqe.js → index-BSjilZ9L.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BqltWojQ.js → index-BZs5bBnk.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-7VKDn-t1.js → index-B_N4-Gq9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BXGSbelg.js → index-Bbj2NS_I.js} +3 -3
  105. package/src/assets/web-panel/assets/{index-CGiIyTZw.js → index-BhA3QEni.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BKmsS2jj.js → index-Bxp7uDD8.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B3mUHuVc.js → index-C1ifZtXo.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BBvyHguy.js → index-C2FqfMvU.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-q_fQ94SE.js → index-C5dgnilC.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D5YVopyr.js → index-CINJQEj6.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-0eWLkQKT.js → index-COev0W2g.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BoAIAZ5C.js → index-CT9vVN-X.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CqoCZ5vR.js → index-CZDekj7T.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-301ogenv.js → index-CarMTIyQ.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Dn2dSdXx.js → index-CbQNL0ue.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DKUBj36_.js → index-CpBaJ3TD.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BBREnn1V.js → index-DB0MJgF_.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BRIqLy1m.js → index-DOoQpuQY.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-jK3ax6Z6.js → index-DTbxp4UH.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B1YGjOWC.js → index-DUq0c7gq.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CyyGTMKd.js → index-DZpYeqrh.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-_JjRLSD0.js → index-D_LQodeJ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DyUaFWEk.js → index-DalA0TKC.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BaE0Ui07.js → index-DjWnONdy.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CyWBir7N.js → index-Dkwsi1Kn.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-qMuZ-NRR.js → index-DocEhinm.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Ckrk3pIV.js → index-DpVrK6fr.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DQRsQen0.js → index-Dy0R5ZED.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-frSojpHj.js → index-M7wxfAbm.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D0BSkxYQ.js → index-N_GoWZX4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-8WvYGnON.js → index-YSw_gxlZ.js} +1 -1
  132. package/src/assets/web-panel/assets/index-Zb2MeDSy.js +1 -0
  133. package/src/assets/web-panel/assets/index-fikqml4k.js +1 -0
  134. package/src/assets/web-panel/assets/{index-Hfe_dEGx.js → index-gt8bNefW.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CZ2wUEgr.js → index-hwVDeoC0.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CZpfzcuy.js → index-jWb70ZgZ.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-D4VdhJ9_.js → initDefaultProps-Co67Kqmb.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-CZJA0MQW.js → motion-BMOxQSF_.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-BqEs7Sb1.js → move-C5fWDw-N.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-BpHEtsu1.js → mtc-parser-BJevpL0e.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-BW94tY9W.js → omit-BCzNU8s6.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-BnBjPWP9.js → pickAttrs-CybWvDPk.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-uUkjQ_hu.js → placementArrow-BMWS9HnW.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-B-klUqXK.js → responsiveObserve-BoL2UnGY.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-CvY1cNAs.js → slide-DWNfXraU.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-Ix5sEDT2.js → statusUtils-BquTM3tX.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-C8qcXOne.js → styleChecker-BEkCleim.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-KNF6Kj8f.js → useFlexGapSupport-Oecnq7EA.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-DGJr345n.js → useFs-CkDffJ4F.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BboN99rI.js → usePersonalDataHub-qbvKXesB.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-Bgt8Vgte.js → vnode-C6v8n6mA.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-BhJtErgt.js → zoom-4CTeFILV.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +3 -3
  155. package/src/command-manifest.json +1 -1
  156. package/src/commands/agenda.js +125 -7
  157. package/src/commands/automation.js +462 -3
  158. package/src/commands/background-session.js +102 -1
  159. package/src/commands/cowork.js +30 -13
  160. package/src/commands/loop.js +167 -36
  161. package/src/commands/routine.js +50 -13
  162. package/src/commands/scheduler-daemon.js +450 -0
  163. package/src/data/changelog.json +27 -1
  164. package/src/lib/agent-schedule-store.js +509 -36
  165. package/src/lib/automation-center-routines.js +363 -0
  166. package/src/lib/automation-center.js +435 -0
  167. package/src/lib/automation-engine.js +74 -19
  168. package/src/lib/automation-execution-authority.js +736 -0
  169. package/src/lib/background-agent-supervisor.js +78 -0
  170. package/src/lib/background-needs-input-incident.js +221 -0
  171. package/src/lib/cowork-cron.js +555 -39
  172. package/src/lib/cowork-observe.js +5 -1
  173. package/src/lib/loop.js +37 -2
  174. package/src/lib/micro-compact.js +11 -5
  175. package/src/lib/permission-engine.js +14 -7
  176. package/src/lib/process-execution-broker/index.js +14 -3
  177. package/src/lib/public-ide-capability-manifest.js +2 -0
  178. package/src/lib/routine-store.js +242 -68
  179. package/src/lib/scheduler-kernel/agenda-adapter.js +812 -0
  180. package/src/lib/scheduler-kernel/authority-resolver.js +163 -0
  181. package/src/lib/scheduler-kernel/automation-adapter.js +592 -0
  182. package/src/lib/scheduler-kernel/automation-event-adapter.js +721 -0
  183. package/src/lib/scheduler-kernel/contract.js +11 -3
  184. package/src/lib/scheduler-kernel/cowork-cron-adapter.js +820 -0
  185. package/src/lib/scheduler-kernel/loop-adapter.js +378 -0
  186. package/src/lib/scheduler-kernel/routine-adapter.js +812 -0
  187. package/src/lib/scheduler-kernel/runtime.js +527 -0
  188. package/src/lib/scheduler-kernel/service.js +328 -0
  189. package/src/lib/scheduler-kernel/store.js +940 -98
  190. package/src/lib/session-message-provenance.js +4 -1
  191. package/src/repl/agent-repl.js +24 -5
  192. package/src/runtime/agent-core.js +38 -17
  193. package/src/runtime/coding-agent-contract-shared.cjs +5 -0
  194. package/src/workers/background-agent-worker.js +91 -28
  195. package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +0 -1
  196. package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +0 -1
  197. package/src/assets/web-panel/assets/SpeechSettings-CGdzFIYw.js +0 -1
  198. package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +0 -1
  199. package/src/assets/web-panel/assets/index-CKB0vbJU.js +0 -1
  200. package/src/assets/web-panel/assets/index-DgoXmwqB.js +0 -1
@@ -0,0 +1,721 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Buffer } from "node:buffer";
3
+ import {
4
+ automationExecutionAuthorityDigest,
5
+ automationExecutionAuthoritySnapshot,
6
+ automationSchedulerAuthority,
7
+ normalizeAutomationExecutionAuthoritySnapshot,
8
+ reserveAutomationExecutionAuthority,
9
+ } from "../automation-execution-authority.js";
10
+ import {
11
+ EXECUTION_STATUS,
12
+ FLOW_STATUS,
13
+ TRIGGER_TYPE,
14
+ executeFlow,
15
+ getExecution,
16
+ getFlow,
17
+ getTrigger,
18
+ listTriggers,
19
+ } from "../automation-engine.js";
20
+ import {
21
+ SchedulerKernelError,
22
+ canonicalJson,
23
+ normalizeEpochMs,
24
+ normalizeIdentifier,
25
+ normalizeJson,
26
+ } from "./contract.js";
27
+ import { SchedulerRuntime } from "./runtime.js";
28
+
29
+ export const AUTOMATION_EVENT_KIND = "automation-event";
30
+ export const AUTOMATION_EVENT_CAPABILITY = "automation.execute";
31
+ export const AUTOMATION_EVENT_CHANNEL = "channel_event";
32
+ export const AUTOMATION_EVENT_TYPE = "channel.event";
33
+ export const AUTOMATION_EVENT_ORIGINS = Object.freeze(["telegram", "webhook"]);
34
+
35
+ const SCOPE_FIELDS = new Set(["origins", "senders"]);
36
+
37
+ function eventError(code, message, details = undefined, cause = undefined) {
38
+ const error = new SchedulerKernelError(
39
+ code,
40
+ message,
41
+ details,
42
+ cause ? { cause } : undefined,
43
+ );
44
+ error.retryable = false;
45
+ return error;
46
+ }
47
+
48
+ function digest(namespace, value) {
49
+ return createHash("sha256")
50
+ .update(namespace, "utf8")
51
+ .update("\0", "utf8")
52
+ .update(canonicalJson(value, namespace), "utf8")
53
+ .digest("hex");
54
+ }
55
+
56
+ function normalizeIsoTime(value, field) {
57
+ const epoch = Date.parse(value);
58
+ if (!Number.isSafeInteger(epoch) || epoch < 0) {
59
+ throw eventError(
60
+ "AUTOMATION_EVENT_DEFINITION_INVALID",
61
+ `${field} must be a valid timestamp`,
62
+ );
63
+ }
64
+ return new Date(epoch).toISOString();
65
+ }
66
+
67
+ function normalizeIdentifierList(value, field, { required = false } = {}) {
68
+ if (value == null) {
69
+ if (required) {
70
+ throw eventError(
71
+ "AUTOMATION_EVENT_SCOPE_REQUIRED",
72
+ `${field} is required`,
73
+ );
74
+ }
75
+ return [];
76
+ }
77
+ if (!Array.isArray(value) || value.length > 64) {
78
+ throw eventError(
79
+ "AUTOMATION_EVENT_SCOPE_INVALID",
80
+ `${field} must be an array with at most 64 entries`,
81
+ );
82
+ }
83
+ const normalized = [
84
+ ...new Set(
85
+ value.map((entry, index) =>
86
+ normalizeIdentifier(entry, `${field}[${index}]`),
87
+ ),
88
+ ),
89
+ ].sort();
90
+ if (required && normalized.length === 0) {
91
+ throw eventError(
92
+ "AUTOMATION_EVENT_SCOPE_REQUIRED",
93
+ `${field} must not be empty`,
94
+ );
95
+ }
96
+ return normalized;
97
+ }
98
+
99
+ function normalizeEventScope(value) {
100
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
101
+ throw eventError(
102
+ "AUTOMATION_EVENT_SCOPE_REQUIRED",
103
+ "event trigger config.scope is required",
104
+ );
105
+ }
106
+ const unknown = Object.keys(value).filter((key) => !SCOPE_FIELDS.has(key));
107
+ if (unknown.length > 0) {
108
+ throw eventError(
109
+ "AUTOMATION_EVENT_SCOPE_INVALID",
110
+ "event trigger scope contains unknown fields",
111
+ { fields: unknown.sort() },
112
+ );
113
+ }
114
+ const origins = normalizeIdentifierList(value.origins, "scope.origins", {
115
+ required: true,
116
+ });
117
+ for (const origin of origins) {
118
+ if (!AUTOMATION_EVENT_ORIGINS.includes(origin)) {
119
+ throw eventError(
120
+ "AUTOMATION_EVENT_SCOPE_INVALID",
121
+ `unsupported channel event origin: ${origin}`,
122
+ );
123
+ }
124
+ }
125
+ return {
126
+ origins,
127
+ senders: normalizeIdentifierList(value.senders, "scope.senders"),
128
+ };
129
+ }
130
+
131
+ export function automationEventFlowSnapshot(flow) {
132
+ if (!flow || typeof flow !== "object" || Array.isArray(flow)) {
133
+ throw eventError(
134
+ "AUTOMATION_EVENT_DEFINITION_INVALID",
135
+ "channel event dispatch requires a flow object",
136
+ );
137
+ }
138
+ const plain = normalizeJson(flow, "automationEventFlow");
139
+ const id = normalizeIdentifier(plain.id, "automationEventFlow.id");
140
+ if (
141
+ typeof plain.name !== "string" ||
142
+ !Array.isArray(plain.nodes) ||
143
+ !Array.isArray(plain.edges) ||
144
+ !Object.values(FLOW_STATUS).includes(plain.status)
145
+ ) {
146
+ throw eventError(
147
+ "AUTOMATION_EVENT_DEFINITION_INVALID",
148
+ `automation flow is malformed: ${id}`,
149
+ );
150
+ }
151
+ return {
152
+ id,
153
+ name: plain.name,
154
+ description: typeof plain.description === "string" ? plain.description : "",
155
+ nodes: plain.nodes,
156
+ edges: plain.edges,
157
+ status: plain.status,
158
+ createdBy: typeof plain.createdBy === "string" ? plain.createdBy : null,
159
+ sharedWith: Array.isArray(plain.sharedWith) ? plain.sharedWith : [],
160
+ createdAt: normalizeIsoTime(
161
+ plain.createdAt,
162
+ "automationEventFlow.createdAt",
163
+ ),
164
+ updatedAt: normalizeIsoTime(
165
+ plain.updatedAt,
166
+ "automationEventFlow.updatedAt",
167
+ ),
168
+ };
169
+ }
170
+
171
+ export function automationEventTriggerSnapshot(trigger) {
172
+ if (!trigger || typeof trigger !== "object" || Array.isArray(trigger)) {
173
+ throw eventError(
174
+ "AUTOMATION_EVENT_DEFINITION_INVALID",
175
+ "channel event dispatch requires a trigger object",
176
+ );
177
+ }
178
+ const plain = normalizeJson(trigger, "automationEventTrigger");
179
+ const id = normalizeIdentifier(plain.id, "automationEventTrigger.id");
180
+ const flowId = normalizeIdentifier(
181
+ plain.flowId,
182
+ "automationEventTrigger.flowId",
183
+ );
184
+ if (plain.type !== TRIGGER_TYPE.EVENT) {
185
+ throw eventError(
186
+ "AUTOMATION_EVENT_TRIGGER_UNSUPPORTED",
187
+ `automation trigger is not an event trigger: ${id}`,
188
+ );
189
+ }
190
+ const config = normalizeJson(
191
+ plain.config || {},
192
+ "automationEventTrigger.config",
193
+ );
194
+ if (config.event !== AUTOMATION_EVENT_TYPE) {
195
+ throw eventError(
196
+ "AUTOMATION_EVENT_TRIGGER_UNSUPPORTED",
197
+ `event trigger must declare config.event=${AUTOMATION_EVENT_TYPE}: ${id}`,
198
+ );
199
+ }
200
+ return {
201
+ id,
202
+ flowId,
203
+ type: TRIGGER_TYPE.EVENT,
204
+ config: {
205
+ event: AUTOMATION_EVENT_TYPE,
206
+ scope: normalizeEventScope(config.scope),
207
+ },
208
+ enabled: plain.enabled === true,
209
+ createdAt: normalizeIsoTime(
210
+ plain.createdAt,
211
+ "automationEventTrigger.createdAt",
212
+ ),
213
+ };
214
+ }
215
+
216
+ export function normalizeAutomationChannelEvent(event) {
217
+ if (!event || typeof event !== "object" || Array.isArray(event)) {
218
+ throw eventError(
219
+ "AUTOMATION_EVENT_INVALID",
220
+ "channel event must be an object",
221
+ );
222
+ }
223
+ const plain = normalizeJson(event, "automationChannelEvent");
224
+ const origin = normalizeIdentifier(plain.origin, "channelEvent.origin", {
225
+ maxLength: 64,
226
+ });
227
+ if (!AUTOMATION_EVENT_ORIGINS.includes(origin)) {
228
+ throw eventError(
229
+ "AUTOMATION_EVENT_INVALID",
230
+ `unsupported channel event origin: ${origin}`,
231
+ );
232
+ }
233
+ const type = normalizeIdentifier(
234
+ plain.type || AUTOMATION_EVENT_TYPE,
235
+ "channelEvent.type",
236
+ );
237
+ if (type !== AUTOMATION_EVENT_TYPE) {
238
+ throw eventError(
239
+ "AUTOMATION_EVENT_INVALID",
240
+ `channel event type must be ${AUTOMATION_EVENT_TYPE}`,
241
+ );
242
+ }
243
+ const text = typeof plain.text === "string" ? plain.text.trim() : "";
244
+ if (!text || Buffer.byteLength(text, "utf8") > 64 * 1024) {
245
+ throw eventError(
246
+ "AUTOMATION_EVENT_INVALID",
247
+ "channel event text must contain 1 to 65536 UTF-8 bytes",
248
+ );
249
+ }
250
+ return {
251
+ id: normalizeIdentifier(plain.id, "channelEvent.id"),
252
+ type,
253
+ origin,
254
+ sender:
255
+ plain.sender == null
256
+ ? null
257
+ : normalizeIdentifier(plain.sender, "channelEvent.sender"),
258
+ text,
259
+ meta: normalizeJson(plain.meta || {}, "channelEvent.meta"),
260
+ producedAt: normalizeEpochMs(
261
+ Number(plain.producedAt),
262
+ "channelEvent.producedAt",
263
+ ),
264
+ };
265
+ }
266
+
267
+ export function automationChannelEventDigest(event) {
268
+ return digest(
269
+ "chainlesschain.scheduler.automation-channel-event.v1",
270
+ normalizeAutomationChannelEvent(event),
271
+ );
272
+ }
273
+
274
+ export function automationEventDefinitionDigest(flow, trigger) {
275
+ return digest("chainlesschain.scheduler.automation-event-definition.v1", {
276
+ flow: automationEventFlowSnapshot(flow),
277
+ trigger: automationEventTriggerSnapshot(trigger),
278
+ });
279
+ }
280
+
281
+ export function automationEventJobId(triggerId) {
282
+ return `automation-event:${normalizeIdentifier(triggerId, "triggerId")}`;
283
+ }
284
+
285
+ export function automationEventExecutionId(occurrenceId) {
286
+ const id = normalizeIdentifier(occurrenceId, "occurrenceId");
287
+ return `exec-event-${createHash("sha256")
288
+ .update(
289
+ "chainlesschain.scheduler.automation-channel-execution.v1\0",
290
+ "utf8",
291
+ )
292
+ .update(id, "utf8")
293
+ .digest("hex")}`;
294
+ }
295
+
296
+ export function automationEventTriggerKey(event) {
297
+ const normalized = normalizeAutomationChannelEvent(event);
298
+ return `channel:${createHash("sha256")
299
+ .update("chainlesschain.scheduler.automation-channel-trigger.v1\0", "utf8")
300
+ .update(normalized.origin, "utf8")
301
+ .update("\0", "utf8")
302
+ .update(normalized.id, "utf8")
303
+ .digest("hex")}`;
304
+ }
305
+
306
+ export function matchesAutomationChannelEvent(trigger, event) {
307
+ const snapshot = automationEventTriggerSnapshot(trigger);
308
+ const normalized = normalizeAutomationChannelEvent(event);
309
+ const scope = snapshot.config.scope;
310
+ return (
311
+ scope.origins.includes(normalized.origin) &&
312
+ (scope.senders.length === 0 ||
313
+ (normalized.sender != null && scope.senders.includes(normalized.sender)))
314
+ );
315
+ }
316
+
317
+ export function buildAutomationEventJob(flow, trigger, executionAuthority) {
318
+ const flowSnapshot = automationEventFlowSnapshot(flow);
319
+ const triggerSnapshot = automationEventTriggerSnapshot(trigger);
320
+ if (triggerSnapshot.flowId !== flowSnapshot.id) {
321
+ throw eventError(
322
+ "AUTOMATION_EVENT_DEFINITION_INVALID",
323
+ `automation trigger does not belong to flow: ${triggerSnapshot.id}`,
324
+ );
325
+ }
326
+ const definitionDigest = automationEventDefinitionDigest(
327
+ flowSnapshot,
328
+ triggerSnapshot,
329
+ );
330
+ const normalizedExecutionAuthority =
331
+ normalizeAutomationExecutionAuthoritySnapshot(
332
+ executionAuthority,
333
+ flowSnapshot,
334
+ );
335
+ return {
336
+ id: automationEventJobId(triggerSnapshot.id),
337
+ kind: AUTOMATION_EVENT_KIND,
338
+ trigger: {
339
+ source: "event-runtime",
340
+ channel: AUTOMATION_EVENT_CHANNEL,
341
+ type: AUTOMATION_EVENT_TYPE,
342
+ origins: triggerSnapshot.config.scope.origins,
343
+ },
344
+ payload: {
345
+ channel: AUTOMATION_EVENT_CHANNEL,
346
+ flow: flowSnapshot,
347
+ trigger: triggerSnapshot,
348
+ definitionDigest,
349
+ executionAuthority: normalizedExecutionAuthority,
350
+ executionAuthorityDigest: automationExecutionAuthorityDigest(
351
+ normalizedExecutionAuthority,
352
+ flowSnapshot,
353
+ ),
354
+ },
355
+ authority: automationSchedulerAuthority(
356
+ normalizedExecutionAuthority,
357
+ flowSnapshot,
358
+ AUTOMATION_EVENT_CAPABILITY,
359
+ ),
360
+ enabled:
361
+ flowSnapshot.status === FLOW_STATUS.ACTIVE && triggerSnapshot.enabled,
362
+ maxAttempts: 3,
363
+ };
364
+ }
365
+
366
+ function comparableJob(job) {
367
+ return {
368
+ kind: job.kind,
369
+ trigger: job.trigger,
370
+ payload: job.payload,
371
+ authority: job.authority,
372
+ enabled: job.enabled,
373
+ maxAttempts: job.maxAttempts,
374
+ };
375
+ }
376
+
377
+ function sameJob(current, desired) {
378
+ return (
379
+ current != null &&
380
+ canonicalJson(comparableJob(current), "currentAutomationEventJob") ===
381
+ canonicalJson(comparableJob(desired), "desiredAutomationEventJob")
382
+ );
383
+ }
384
+
385
+ export function syncAutomationEventJob(
386
+ schedulerStore,
387
+ flow,
388
+ trigger,
389
+ executionAuthority,
390
+ ) {
391
+ const desired = buildAutomationEventJob(flow, trigger, executionAuthority);
392
+ let current = schedulerStore.getJob(desired.id);
393
+ if (!current) {
394
+ try {
395
+ return schedulerStore.createJob(desired);
396
+ } catch (error) {
397
+ if (error?.code !== "SCHEDULER_CONFLICT") throw error;
398
+ current = schedulerStore.getJob(desired.id);
399
+ }
400
+ }
401
+ if (sameJob(current, desired)) return current;
402
+ try {
403
+ return schedulerStore.updateJob(
404
+ current.id,
405
+ current.revision,
406
+ comparableJob(desired),
407
+ );
408
+ } catch (error) {
409
+ if (error?.code !== "SCHEDULER_REVISION_CONFLICT") throw error;
410
+ const latest = schedulerStore.getJob(desired.id);
411
+ if (sameJob(latest, desired)) return latest;
412
+ throw error;
413
+ }
414
+ }
415
+
416
+ export function enqueueAutomationChannelEvent(
417
+ schedulerStore,
418
+ flow,
419
+ trigger,
420
+ event,
421
+ executionAuthority,
422
+ ) {
423
+ const normalizedEvent = normalizeAutomationChannelEvent(event);
424
+ if (!matchesAutomationChannelEvent(trigger, normalizedEvent)) {
425
+ throw eventError(
426
+ "AUTOMATION_EVENT_SCOPE_DENIED",
427
+ `channel event is outside trigger scope: ${trigger.id}`,
428
+ );
429
+ }
430
+ const triggerKey = automationEventTriggerKey(normalizedEvent);
431
+ const eventDigest = automationChannelEventDigest(normalizedEvent);
432
+ const job = syncAutomationEventJob(
433
+ schedulerStore,
434
+ flow,
435
+ trigger,
436
+ executionAuthority,
437
+ );
438
+ let occurrence;
439
+ try {
440
+ occurrence = schedulerStore.enqueueOccurrenceOncePerTrigger({
441
+ jobId: job.id,
442
+ scheduledFor: normalizedEvent.producedAt,
443
+ availableAt: normalizedEvent.producedAt,
444
+ triggerKey,
445
+ payload: {
446
+ ...job.payload,
447
+ event: normalizedEvent,
448
+ eventDigest,
449
+ },
450
+ });
451
+ } catch (error) {
452
+ if (error?.code === "SCHEDULER_JOB_DISABLED") {
453
+ throw eventError(
454
+ "AUTOMATION_EVENT_DEFINITION_INACTIVE",
455
+ `automation event trigger is not active: ${trigger.id}`,
456
+ undefined,
457
+ error,
458
+ );
459
+ }
460
+ throw error;
461
+ }
462
+ if (occurrence.payload?.eventDigest !== eventDigest) {
463
+ throw eventError(
464
+ "AUTOMATION_EVENT_ID_COLLISION",
465
+ `channel event id was reused with different content: ${normalizedEvent.id}`,
466
+ );
467
+ }
468
+ return occurrence;
469
+ }
470
+
471
+ export function authorizeAutomationEventOccurrence({ job, occurrence }) {
472
+ try {
473
+ const payload = occurrence?.payload;
474
+ const flow = automationEventFlowSnapshot(payload?.flow);
475
+ const trigger = automationEventTriggerSnapshot(payload?.trigger);
476
+ const event = normalizeAutomationChannelEvent(payload?.event);
477
+ const executionAuthority = normalizeAutomationExecutionAuthoritySnapshot(
478
+ payload?.executionAuthority,
479
+ flow,
480
+ );
481
+ const executionAuthorityDigest = automationExecutionAuthorityDigest(
482
+ executionAuthority,
483
+ flow,
484
+ );
485
+ const expectedAuthority = automationSchedulerAuthority(
486
+ executionAuthority,
487
+ flow,
488
+ AUTOMATION_EVENT_CAPABILITY,
489
+ );
490
+ const allowed =
491
+ job?.kind === AUTOMATION_EVENT_KIND &&
492
+ payload?.channel === AUTOMATION_EVENT_CHANNEL &&
493
+ payload?.definitionDigest ===
494
+ automationEventDefinitionDigest(flow, trigger) &&
495
+ payload?.eventDigest === automationChannelEventDigest(event) &&
496
+ trigger.flowId === flow.id &&
497
+ matchesAutomationChannelEvent(trigger, event) &&
498
+ occurrence?.triggerKey === automationEventTriggerKey(event) &&
499
+ payload?.executionAuthorityDigest === executionAuthorityDigest &&
500
+ canonicalJson(
501
+ occurrence?.authority,
502
+ "automationEventOccurrenceAuthority",
503
+ ) ===
504
+ canonicalJson(expectedAuthority, "expectedAutomationEventAuthority");
505
+ return {
506
+ allowed,
507
+ reason: allowed
508
+ ? "automation_event_snapshot_bound"
509
+ : "automation_event_authority_mismatch",
510
+ };
511
+ } catch {
512
+ return { allowed: false, reason: "automation_event_authority_malformed" };
513
+ }
514
+ }
515
+
516
+ function existingExecutionResult(db, occurrence, expectedFlow) {
517
+ const executionId = automationEventExecutionId(occurrence.id);
518
+ const execution = getExecution(db, executionId);
519
+ if (!execution) return null;
520
+ if (
521
+ execution.flowId !== expectedFlow.id ||
522
+ execution.triggerType !== TRIGGER_TYPE.EVENT
523
+ ) {
524
+ throw eventError(
525
+ "AUTOMATION_EVENT_EXECUTION_BINDING_MISMATCH",
526
+ `automation event execution evidence is mismatched: ${occurrence.id}`,
527
+ );
528
+ }
529
+ if (execution.status === EXECUTION_STATUS.RUNNING) {
530
+ throw eventError(
531
+ "AUTOMATION_EVENT_OUTCOME_UNKNOWN",
532
+ `automation event outcome is unknown; refusing replay: ${execution.id}`,
533
+ { executionId: execution.id },
534
+ );
535
+ }
536
+ if (execution.status !== EXECUTION_STATUS.SUCCESS) {
537
+ throw eventError(
538
+ "AUTOMATION_EVENT_EXECUTION_FAILED",
539
+ `automation event execution did not succeed: ${execution.id}`,
540
+ { executionId: execution.id, status: execution.status },
541
+ );
542
+ }
543
+ return execution;
544
+ }
545
+
546
+ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
547
+ if (!db || typeof db.prepare !== "function") {
548
+ throw eventError(
549
+ "AUTOMATION_EVENT_DATABASE_REQUIRED",
550
+ "automation event adapter requires a compatible database",
551
+ );
552
+ }
553
+ return {
554
+ kind: AUTOMATION_EVENT_KIND,
555
+ async execute({ occurrence }) {
556
+ const payload = occurrence?.payload;
557
+ const expectedFlow = automationEventFlowSnapshot(payload?.flow);
558
+ const expectedTrigger = automationEventTriggerSnapshot(payload?.trigger);
559
+ const expectedEvent = normalizeAutomationChannelEvent(payload?.event);
560
+ if (
561
+ payload?.channel !== AUTOMATION_EVENT_CHANNEL ||
562
+ payload?.definitionDigest !==
563
+ automationEventDefinitionDigest(expectedFlow, expectedTrigger) ||
564
+ payload?.eventDigest !== automationChannelEventDigest(expectedEvent)
565
+ ) {
566
+ throw eventError(
567
+ "AUTOMATION_EVENT_SNAPSHOT_INVALID",
568
+ `automation event occurrence snapshot is invalid: ${occurrence?.id}`,
569
+ );
570
+ }
571
+
572
+ const recovered = existingExecutionResult(db, occurrence, expectedFlow);
573
+ if (recovered) return recovered;
574
+
575
+ const currentFlow = getFlow(db, expectedFlow.id);
576
+ const currentTrigger = getTrigger(db, expectedTrigger.id);
577
+ if (
578
+ !currentFlow ||
579
+ currentFlow.status !== FLOW_STATUS.ACTIVE ||
580
+ !currentTrigger?.enabled
581
+ ) {
582
+ throw eventError(
583
+ "AUTOMATION_EVENT_DEFINITION_INACTIVE",
584
+ `automation event definition is no longer active: ${expectedTrigger.id}`,
585
+ );
586
+ }
587
+ if (
588
+ automationEventDefinitionDigest(currentFlow, currentTrigger) !==
589
+ payload.definitionDigest
590
+ ) {
591
+ throw eventError(
592
+ "AUTOMATION_EVENT_STALE_SNAPSHOT",
593
+ `automation event definition changed after enqueue: ${expectedTrigger.id}`,
594
+ );
595
+ }
596
+
597
+ reserveAutomationExecutionAuthority(
598
+ db,
599
+ currentFlow,
600
+ occurrence.id,
601
+ payload.executionAuthority,
602
+ payload.executionAuthorityDigest,
603
+ { now },
604
+ );
605
+
606
+ let execution;
607
+ try {
608
+ execution = executeFlow(db, expectedFlow.id, {
609
+ inputData: { event: expectedEvent },
610
+ triggerType: TRIGGER_TYPE.EVENT,
611
+ executionId: automationEventExecutionId(occurrence.id),
612
+ });
613
+ } catch (cause) {
614
+ if (cause?.code === "AUTOMATION_EXECUTION_OUTCOME_UNKNOWN") {
615
+ throw eventError(
616
+ "AUTOMATION_EVENT_OUTCOME_UNKNOWN",
617
+ cause.message,
618
+ { executionId: automationEventExecutionId(occurrence.id) },
619
+ cause,
620
+ );
621
+ }
622
+ throw cause;
623
+ }
624
+ if (execution.status !== EXECUTION_STATUS.SUCCESS) {
625
+ throw eventError(
626
+ "AUTOMATION_EVENT_EXECUTION_FAILED",
627
+ `automation event execution failed: ${execution.id}`,
628
+ { executionId: execution.id, error: execution.error },
629
+ );
630
+ }
631
+ return execution;
632
+ },
633
+ classifyError() {
634
+ return { retryable: false };
635
+ },
636
+ };
637
+ }
638
+
639
+ export class AutomationEventDispatcher {
640
+ constructor({
641
+ db,
642
+ schedulerStore,
643
+ ownerId,
644
+ leaseMs,
645
+ renewIntervalMs,
646
+ now = Date.now,
647
+ } = {}) {
648
+ this.db = db;
649
+ this.schedulerStore = schedulerStore;
650
+ this.runtime = new SchedulerRuntime({
651
+ store: schedulerStore,
652
+ adapters: [createAutomationEventAdapter({ db, now })],
653
+ authorize: authorizeAutomationEventOccurrence,
654
+ ...(ownerId === undefined ? {} : { ownerId }),
655
+ ...(leaseMs === undefined ? {} : { leaseMs }),
656
+ ...(renewIntervalMs === undefined ? {} : { renewIntervalMs }),
657
+ });
658
+ }
659
+
660
+ async dispatch(event, { signal } = {}) {
661
+ const normalizedEvent = normalizeAutomationChannelEvent(event);
662
+ const matches = [];
663
+ const rejected = [];
664
+ for (const trigger of listTriggers(this.db)) {
665
+ if (!trigger.enabled || trigger.type !== TRIGGER_TYPE.EVENT) continue;
666
+ try {
667
+ if (!matchesAutomationChannelEvent(trigger, normalizedEvent)) continue;
668
+ const flow = getFlow(this.db, trigger.flowId);
669
+ if (!flow || flow.status !== FLOW_STATUS.ACTIVE) continue;
670
+ matches.push({ flow, trigger });
671
+ } catch (error) {
672
+ rejected.push({
673
+ triggerId: trigger.id,
674
+ code: error?.code || "AUTOMATION_EVENT_SCOPE_INVALID",
675
+ message: String(error?.message || error).slice(0, 1000),
676
+ });
677
+ }
678
+ }
679
+ matches.sort((a, b) => a.trigger.id.localeCompare(b.trigger.id));
680
+
681
+ const results = [];
682
+ for (const match of matches) {
683
+ let occurrence;
684
+ try {
685
+ occurrence = enqueueAutomationChannelEvent(
686
+ this.schedulerStore,
687
+ match.flow,
688
+ match.trigger,
689
+ normalizedEvent,
690
+ automationExecutionAuthoritySnapshot(this.db, match.flow),
691
+ );
692
+ } catch (error) {
693
+ if (!String(error?.code || "").startsWith("AUTOMATION_EXECUTION_")) {
694
+ throw error;
695
+ }
696
+ rejected.push({
697
+ triggerId: match.trigger.id,
698
+ code: error?.code || "AUTOMATION_EXECUTION_PREFLIGHT_REJECTED",
699
+ message: String(error?.message || error).slice(0, 1000),
700
+ });
701
+ continue;
702
+ }
703
+ const result = await this.runtime.runOccurrence(occurrence.id, {
704
+ signal,
705
+ });
706
+ results.push({
707
+ flowId: match.flow.id,
708
+ triggerId: match.trigger.id,
709
+ occurrenceId: occurrence.id,
710
+ deduplicated: occurrence.deduplicated === true,
711
+ result,
712
+ });
713
+ }
714
+ return {
715
+ eventId: normalizedEvent.id,
716
+ matched: matches.length,
717
+ rejected,
718
+ results,
719
+ };
720
+ }
721
+ }