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,812 @@
1
+ import { createHash } from "node:crypto";
2
+ import { effectiveFireAt } from "../schedule-planner.js";
3
+ import {
4
+ nextCronTime,
5
+ normalizeTimeZone,
6
+ parseCron,
7
+ } from "../agent-schedule-store.js";
8
+ import {
9
+ SchedulerKernelError,
10
+ canonicalJson,
11
+ normalizeEpochMs,
12
+ normalizeIdentifier,
13
+ normalizeJson,
14
+ } from "./contract.js";
15
+ import { SchedulerRuntime } from "./runtime.js";
16
+ import {
17
+ bindSchedulerAuthorityPolicy,
18
+ createSchedulerAuthorityResolver,
19
+ } from "./authority-resolver.js";
20
+
21
+ export const AGENDA_SCHEDULER_KIND = "agenda";
22
+ export const AGENDA_SCHEDULER_CAPABILITY = "agent.execute";
23
+ export const AGENDA_SCHEDULER_MONITOR_CAPABILITY = "monitor.observe";
24
+ export const AGENDA_SCHEDULER_ENTRY_KINDS = Object.freeze([
25
+ "wakeup",
26
+ "cron",
27
+ "monitor",
28
+ ]);
29
+ export const AGENDA_SCHEDULER_RETRY_DELAY_MS = 60_000;
30
+
31
+ function agendaError(
32
+ code,
33
+ message,
34
+ details = undefined,
35
+ cause = undefined,
36
+ { retryable = false, retryAt } = {},
37
+ ) {
38
+ const error = new SchedulerKernelError(
39
+ code,
40
+ message,
41
+ details,
42
+ cause ? { cause } : undefined,
43
+ );
44
+ error.retryable = retryable;
45
+ if (retryAt !== undefined) error.retryAt = retryAt;
46
+ return error;
47
+ }
48
+
49
+ function safeFailure(error) {
50
+ return {
51
+ code:
52
+ typeof error?.code === "string" && error.code
53
+ ? error.code.slice(0, 128)
54
+ : "AGENDA_AGENT_EXECUTION_FAILED",
55
+ message:
56
+ typeof error?.message === "string" && error.message
57
+ ? error.message.slice(0, 2_000)
58
+ : "Agenda agent execution failed",
59
+ };
60
+ }
61
+
62
+ function capabilityForEntry(entry) {
63
+ return entry?.kind === "monitor"
64
+ ? AGENDA_SCHEDULER_MONITOR_CAPABILITY
65
+ : AGENDA_SCHEDULER_CAPABILITY;
66
+ }
67
+
68
+ function normalizeMonitorObservation(entry, observation) {
69
+ if (
70
+ !observation ||
71
+ typeof observation !== "object" ||
72
+ Array.isArray(observation) ||
73
+ typeof observation.matched !== "boolean"
74
+ ) {
75
+ throw agendaError(
76
+ "AGENDA_SCHEDULER_MONITOR_RESULT_INVALID",
77
+ `Agenda monitor returned an invalid observation: ${entry.id}`,
78
+ );
79
+ }
80
+ const mtimeMs =
81
+ observation.mtimeMs === null || observation.mtimeMs === undefined
82
+ ? null
83
+ : Number(observation.mtimeMs);
84
+ if (mtimeMs !== null && (!Number.isFinite(mtimeMs) || mtimeMs < 0)) {
85
+ throw agendaError(
86
+ "AGENDA_SCHEDULER_MONITOR_RESULT_INVALID",
87
+ `Agenda monitor returned an invalid mtime: ${entry.id}`,
88
+ );
89
+ }
90
+ const eventId = observation.matched
91
+ ? normalizeIdentifier(observation.eventId, "agenda.monitor.eventId")
92
+ : null;
93
+ const authority = observation.matched
94
+ ? normalizeIdentifier(observation.authority, "agenda.monitor.authority")
95
+ : null;
96
+ const duplicate = observation.matched && entry.lastEventId === eventId;
97
+ const notification =
98
+ observation.notification &&
99
+ typeof observation.notification === "object" &&
100
+ !Array.isArray(observation.notification)
101
+ ? {
102
+ title: String(observation.notification.title || ""),
103
+ body: String(observation.notification.body || ""),
104
+ level: String(observation.notification.level || "success"),
105
+ }
106
+ : null;
107
+ return {
108
+ matched: observation.matched,
109
+ mtimeMs,
110
+ eventId,
111
+ authority,
112
+ duplicate,
113
+ truncated: observation.truncated === true,
114
+ notification,
115
+ };
116
+ }
117
+
118
+ function validateMonitorSnapshot(snapshot) {
119
+ if (
120
+ !Number.isSafeInteger(snapshot.intervalMs) ||
121
+ snapshot.intervalMs < 1_000
122
+ ) {
123
+ throw agendaError(
124
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
125
+ `Agenda monitor interval is invalid: ${snapshot.id}`,
126
+ );
127
+ }
128
+ if (!Number.isSafeInteger(snapshot.checks) || snapshot.checks < 0) {
129
+ throw agendaError(
130
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
131
+ `Agenda monitor check count is invalid: ${snapshot.id}`,
132
+ );
133
+ }
134
+ if (!["command", "file", "http"].includes(snapshot.source)) {
135
+ throw agendaError(
136
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
137
+ `Agenda monitor source is invalid: ${snapshot.id}`,
138
+ );
139
+ }
140
+ const sourceValue =
141
+ snapshot.source === "command"
142
+ ? snapshot.command
143
+ : snapshot.source === "file"
144
+ ? snapshot.watchFile
145
+ : snapshot.watchUrl;
146
+ if (typeof sourceValue !== "string" || sourceValue.length === 0) {
147
+ throw agendaError(
148
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
149
+ `Agenda monitor source target is missing: ${snapshot.id}`,
150
+ );
151
+ }
152
+ if (
153
+ snapshot.watchChange === true &&
154
+ (snapshot.source !== "file" || snapshot.stopWhen != null)
155
+ ) {
156
+ throw agendaError(
157
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
158
+ `Agenda monitor change condition is invalid: ${snapshot.id}`,
159
+ );
160
+ }
161
+ if (snapshot.stopWhen != null) {
162
+ if (typeof snapshot.stopWhen !== "string") {
163
+ throw agendaError(
164
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
165
+ `Agenda monitor stop condition is invalid: ${snapshot.id}`,
166
+ );
167
+ }
168
+ try {
169
+ new RegExp(snapshot.stopWhen);
170
+ } catch (error) {
171
+ throw agendaError(
172
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
173
+ `Agenda monitor stop condition is invalid: ${snapshot.id}`,
174
+ undefined,
175
+ error,
176
+ );
177
+ }
178
+ }
179
+ }
180
+
181
+ function validateEntrySnapshot(entry) {
182
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
183
+ throw agendaError(
184
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
185
+ "Agenda scheduler adapter requires an entry object",
186
+ );
187
+ }
188
+ const plain = { ...entry };
189
+ delete plain.executionLease;
190
+ delete plain.schedulerExecution;
191
+ const snapshot = normalizeJson(plain, "agendaEntrySnapshot");
192
+ snapshot.id = normalizeIdentifier(snapshot.id, "agenda.entry.id");
193
+ if (!AGENDA_SCHEDULER_ENTRY_KINDS.includes(snapshot.kind)) {
194
+ throw agendaError(
195
+ "AGENDA_SCHEDULER_KIND_UNSUPPORTED",
196
+ `Agenda scheduler does not support entry kind: ${snapshot.kind}`,
197
+ );
198
+ }
199
+ if (
200
+ snapshot.kind !== "monitor" &&
201
+ (typeof snapshot.prompt !== "string" || snapshot.prompt.length === 0)
202
+ ) {
203
+ throw agendaError(
204
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
205
+ `Agenda prompt is missing: ${snapshot.id}`,
206
+ );
207
+ }
208
+ if (snapshot.kind === "cron") {
209
+ try {
210
+ parseCron(snapshot.cron);
211
+ if (snapshot.timeZone != null) {
212
+ const timeZone = normalizeTimeZone(snapshot.timeZone);
213
+ if (!timeZone) throw new Error("cron timeZone must not be empty");
214
+ const nextAt = Number(snapshot.nextAt);
215
+ if (
216
+ nextCronTime(snapshot.cron, nextAt - 60_000, { timeZone }) !== nextAt
217
+ ) {
218
+ throw new Error("cron nextAt does not match its time zone");
219
+ }
220
+ }
221
+ } catch (error) {
222
+ throw agendaError(
223
+ "AGENDA_SCHEDULER_CRON_INVALID",
224
+ `Agenda cron definition is invalid: ${snapshot.id}`,
225
+ undefined,
226
+ error,
227
+ );
228
+ }
229
+ }
230
+ const schedulable =
231
+ (snapshot.kind === "wakeup" && snapshot.status === "pending") ||
232
+ (["cron", "monitor"].includes(snapshot.kind) &&
233
+ snapshot.status === "active");
234
+ if (!schedulable) {
235
+ throw agendaError(
236
+ "AGENDA_SCHEDULER_ENTRY_TERMINAL",
237
+ `Agenda entry is not schedulable: ${snapshot.id}`,
238
+ );
239
+ }
240
+ if (snapshot.kind === "monitor") validateMonitorSnapshot(snapshot);
241
+ normalizeEpochMs(Number(snapshot.createdAt), "agenda.entry.createdAt");
242
+ const fireAt = effectiveFireAt(snapshot);
243
+ if (fireAt == null) {
244
+ throw agendaError(
245
+ "AGENDA_SCHEDULER_FIRE_TIME_INVALID",
246
+ `Agenda entry has no fire time: ${snapshot.id}`,
247
+ );
248
+ }
249
+ normalizeEpochMs(Number(fireAt), "agenda.entry.fireAt");
250
+ return snapshot;
251
+ }
252
+
253
+ export function agendaEntrySnapshot(entry) {
254
+ return validateEntrySnapshot(entry);
255
+ }
256
+
257
+ export function agendaEntrySnapshotDigest(entry) {
258
+ return createHash("sha256")
259
+ .update("chainlesschain.scheduler.agenda-entry.v1\0", "utf8")
260
+ .update(
261
+ canonicalJson(agendaEntrySnapshot(entry), "agendaEntrySnapshot"),
262
+ "utf8",
263
+ )
264
+ .digest("hex");
265
+ }
266
+
267
+ export function agendaSchedulerJobId(entryId) {
268
+ return `agenda:${normalizeIdentifier(entryId, "agendaEntryId")}`;
269
+ }
270
+
271
+ export function buildAgendaSchedulerJob(entry) {
272
+ const snapshot = agendaEntrySnapshot(entry);
273
+ return {
274
+ id: agendaSchedulerJobId(snapshot.id),
275
+ kind: AGENDA_SCHEDULER_KIND,
276
+ trigger: {
277
+ source: "agent-schedule-store",
278
+ scheduleKind: snapshot.kind,
279
+ expression:
280
+ snapshot.kind === "cron"
281
+ ? snapshot.cron
282
+ : snapshot.kind === "monitor"
283
+ ? `every:${snapshot.intervalMs}`
284
+ : String(snapshot.dueAt),
285
+ ...(snapshot.kind === "cron" && snapshot.timeZone
286
+ ? { timeZone: snapshot.timeZone }
287
+ : {}),
288
+ },
289
+ payload: {
290
+ entry: snapshot,
291
+ snapshotDigest: agendaEntrySnapshotDigest(snapshot),
292
+ },
293
+ authority: {
294
+ schemaVersion: 1,
295
+ principal: { type: "agenda", id: snapshot.id },
296
+ tenantId: null,
297
+ workspaceId: null,
298
+ requestedCapabilities: [capabilityForEntry(snapshot)],
299
+ authorizationRefs: {
300
+ decisionId: null,
301
+ policyRevision: null,
302
+ grantIds: [],
303
+ approvalIds: [],
304
+ delegationIds: [],
305
+ },
306
+ },
307
+ enabled: true,
308
+ maxAttempts: 3,
309
+ };
310
+ }
311
+
312
+ function comparableJob(job) {
313
+ return {
314
+ kind: job.kind,
315
+ trigger: job.trigger,
316
+ payload: job.payload,
317
+ authority: job.authority,
318
+ enabled: job.enabled,
319
+ maxAttempts: job.maxAttempts,
320
+ };
321
+ }
322
+
323
+ function sameJob(current, desired) {
324
+ return (
325
+ current !== null &&
326
+ canonicalJson(comparableJob(current), "currentAgendaJob") ===
327
+ canonicalJson(comparableJob(desired), "desiredAgendaJob")
328
+ );
329
+ }
330
+
331
+ export function syncAgendaSchedulerJob(schedulerStore, entry) {
332
+ const desired = buildAgendaSchedulerJob(entry);
333
+ desired.authority = bindSchedulerAuthorityPolicy(
334
+ schedulerStore,
335
+ desired.authority,
336
+ );
337
+ let current = schedulerStore.getJob(desired.id);
338
+ if (!current) {
339
+ try {
340
+ return schedulerStore.createJob(desired);
341
+ } catch (error) {
342
+ if (error?.code !== "SCHEDULER_CONFLICT") throw error;
343
+ current = schedulerStore.getJob(desired.id);
344
+ }
345
+ }
346
+ if (sameJob(current, desired)) return current;
347
+ try {
348
+ return schedulerStore.updateJob(current.id, current.revision, {
349
+ kind: desired.kind,
350
+ trigger: desired.trigger,
351
+ payload: desired.payload,
352
+ authority: desired.authority,
353
+ enabled: desired.enabled,
354
+ maxAttempts: desired.maxAttempts,
355
+ });
356
+ } catch (error) {
357
+ if (error?.code !== "SCHEDULER_REVISION_CONFLICT") throw error;
358
+ const latest = schedulerStore.getJob(desired.id);
359
+ if (sameJob(latest, desired)) return latest;
360
+ throw error;
361
+ }
362
+ }
363
+
364
+ export function enqueueAgendaEntry(schedulerStore, entry) {
365
+ const snapshot = agendaEntrySnapshot(entry);
366
+ const job = syncAgendaSchedulerJob(schedulerStore, snapshot);
367
+ const scheduledFor = normalizeEpochMs(
368
+ Number(effectiveFireAt(snapshot)),
369
+ "agenda.entry.fireAt",
370
+ );
371
+ return schedulerStore.enqueueOccurrence({
372
+ jobId: job.id,
373
+ scheduledFor,
374
+ triggerKey: `${snapshot.kind}:${scheduledFor}`,
375
+ });
376
+ }
377
+
378
+ export function authorizeAgendaOccurrence({ job, occurrence }) {
379
+ try {
380
+ const payload = occurrence?.payload;
381
+ const entry = payload?.entry;
382
+ const authority = occurrence?.authority;
383
+ const expectedDigest = agendaEntrySnapshotDigest(entry);
384
+ const allowed =
385
+ job?.kind === AGENDA_SCHEDULER_KIND &&
386
+ authority?.principal?.type === "agenda" &&
387
+ authority?.principal?.id === entry.id &&
388
+ Array.isArray(authority?.requestedCapabilities) &&
389
+ authority.requestedCapabilities.length === 1 &&
390
+ authority.requestedCapabilities[0] === capabilityForEntry(entry) &&
391
+ payload.snapshotDigest === expectedDigest;
392
+ return {
393
+ allowed,
394
+ reason: allowed ? "agenda_snapshot_bound" : "agenda_authority_mismatch",
395
+ };
396
+ } catch {
397
+ return { allowed: false, reason: "agenda_authority_malformed" };
398
+ }
399
+ }
400
+
401
+ function recoveredResult(entry, evidence) {
402
+ const result =
403
+ evidence.result && typeof evidence.result === "object"
404
+ ? evidence.result
405
+ : {
406
+ id: entry.id,
407
+ kind: entry.kind,
408
+ action: entry.kind === "monitor" ? "checked" : "fired",
409
+ };
410
+ return { ...result, recovered: true };
411
+ }
412
+
413
+ function matchingExecutionEvidence(
414
+ entry,
415
+ { occurrenceId, snapshotDigest, attempt, status },
416
+ ) {
417
+ const evidence = entry?.schedulerExecution;
418
+ return (
419
+ evidence?.occurrenceId === occurrenceId &&
420
+ evidence.snapshotDigest === snapshotDigest &&
421
+ evidence.attempt === attempt &&
422
+ evidence.status === status
423
+ );
424
+ }
425
+
426
+ export function createAgendaSchedulerAdapter({
427
+ agendaStore,
428
+ runAgent,
429
+ runMonitor,
430
+ notifyMonitor,
431
+ now = Date.now,
432
+ } = {}) {
433
+ if (
434
+ !agendaStore ||
435
+ typeof agendaStore.get !== "function" ||
436
+ typeof agendaStore.bindSchedulerExecution !== "function" ||
437
+ typeof agendaStore.completeSchedulerExecution !== "function"
438
+ ) {
439
+ throw agendaError(
440
+ "AGENDA_SCHEDULER_STORE_REQUIRED",
441
+ "Agenda scheduler adapter requires a compatible AgentScheduleStore",
442
+ );
443
+ }
444
+ if (typeof runAgent !== "function") {
445
+ throw agendaError(
446
+ "AGENDA_SCHEDULER_RUNNER_REQUIRED",
447
+ "Agenda scheduler adapter requires a runAgent function",
448
+ );
449
+ }
450
+ if (typeof now !== "function") {
451
+ throw agendaError(
452
+ "AGENDA_SCHEDULER_CLOCK_REQUIRED",
453
+ "Agenda scheduler adapter requires a clock function",
454
+ );
455
+ }
456
+ return {
457
+ kind: AGENDA_SCHEDULER_KIND,
458
+ async execute({ occurrence }) {
459
+ const payload = occurrence.payload;
460
+ const expected = agendaEntrySnapshot(payload?.entry);
461
+ const expectedDigest = agendaEntrySnapshotDigest(expected);
462
+ if (payload.snapshotDigest !== expectedDigest) {
463
+ throw agendaError(
464
+ "AGENDA_SCHEDULER_SNAPSHOT_INVALID",
465
+ `Agenda occurrence snapshot is invalid: ${occurrence.id}`,
466
+ );
467
+ }
468
+ let current = agendaStore.get(expected.id);
469
+ if (!current) {
470
+ throw agendaError(
471
+ "AGENDA_SCHEDULER_ENTRY_NOT_FOUND",
472
+ `Agenda entry disappeared before execution: ${expected.id}`,
473
+ );
474
+ }
475
+ const prior = current.schedulerExecution;
476
+ if (prior?.occurrenceId === occurrence.id) {
477
+ if (prior.snapshotDigest !== expectedDigest) {
478
+ throw agendaError(
479
+ "AGENDA_SCHEDULER_BINDING_MISMATCH",
480
+ `Agenda execution evidence has a different snapshot: ${expected.id}`,
481
+ );
482
+ }
483
+ if (prior.status === "succeeded") {
484
+ return recoveredResult(expected, prior);
485
+ }
486
+ if (prior.status === "running") {
487
+ throw agendaError(
488
+ "AGENDA_SCHEDULER_OUTCOME_UNKNOWN",
489
+ `Agenda execution outcome is unknown; refusing duplicate execution: ${expected.id}`,
490
+ );
491
+ }
492
+ } else if (prior?.status === "running") {
493
+ throw agendaError(
494
+ "AGENDA_SCHEDULER_EXECUTION_CONFLICT",
495
+ `Agenda entry has another unresolved scheduler execution: ${expected.id}`,
496
+ );
497
+ }
498
+ if (agendaEntrySnapshotDigest(current) !== expectedDigest) {
499
+ throw agendaError(
500
+ "AGENDA_SCHEDULER_STALE_SNAPSHOT",
501
+ `Agenda entry changed after occurrence enqueue: ${expected.id}`,
502
+ );
503
+ }
504
+
505
+ current = agendaStore.bindSchedulerExecution(expected.id, {
506
+ occurrenceId: occurrence.id,
507
+ snapshotDigest: expectedDigest,
508
+ attempt: occurrence.attempt,
509
+ atMs: now(),
510
+ });
511
+ if (agendaEntrySnapshotDigest(current) !== expectedDigest) {
512
+ const stale = agendaError(
513
+ "AGENDA_SCHEDULER_STALE_SNAPSHOT",
514
+ `Agenda entry changed while binding occurrence: ${expected.id}`,
515
+ );
516
+ agendaStore.completeSchedulerExecution(expected.id, {
517
+ occurrenceId: occurrence.id,
518
+ snapshotDigest: expectedDigest,
519
+ attempt: occurrence.attempt,
520
+ outcome: "failed",
521
+ error: safeFailure(stale),
522
+ atMs: now(),
523
+ });
524
+ throw stale;
525
+ }
526
+
527
+ let observation = null;
528
+ let action = null;
529
+ try {
530
+ if (expected.kind === "monitor") {
531
+ if (typeof runMonitor !== "function") {
532
+ throw agendaError(
533
+ "AGENDA_SCHEDULER_MONITOR_RUNNER_REQUIRED",
534
+ "Agenda scheduler monitor execution requires a monitor runner",
535
+ );
536
+ }
537
+ observation = normalizeMonitorObservation(
538
+ expected,
539
+ await runMonitor({
540
+ entry: expected,
541
+ occurrenceId: occurrence.id,
542
+ attempt: occurrence.attempt,
543
+ }),
544
+ );
545
+ action = {
546
+ id: expected.id,
547
+ kind: expected.kind,
548
+ action: observation.matched
549
+ ? observation.duplicate
550
+ ? "duplicate"
551
+ : "matched"
552
+ : "checked",
553
+ ...(observation.matched
554
+ ? {
555
+ event_id: observation.eventId,
556
+ authority: observation.authority,
557
+ }
558
+ : {}),
559
+ ...(observation.truncated ? { truncated: true } : {}),
560
+ };
561
+ } else {
562
+ await runAgent({
563
+ entry: expected,
564
+ prompt: expected.prompt,
565
+ runPolicy: expected.runPolicy,
566
+ occurrenceId: occurrence.id,
567
+ attempt: occurrence.attempt,
568
+ });
569
+ action = {
570
+ id: expected.id,
571
+ kind: expected.kind,
572
+ action: "fired",
573
+ };
574
+ }
575
+ } catch (error) {
576
+ let failurePersisted = false;
577
+ try {
578
+ agendaStore.completeSchedulerExecution(expected.id, {
579
+ occurrenceId: occurrence.id,
580
+ snapshotDigest: expectedDigest,
581
+ attempt: occurrence.attempt,
582
+ outcome: "failed",
583
+ error: safeFailure(error),
584
+ atMs: now(),
585
+ });
586
+ failurePersisted = true;
587
+ } catch (completionError) {
588
+ failurePersisted = matchingExecutionEvidence(
589
+ agendaStore.get(expected.id),
590
+ {
591
+ occurrenceId: occurrence.id,
592
+ snapshotDigest: expectedDigest,
593
+ attempt: occurrence.attempt,
594
+ status: "failed",
595
+ },
596
+ );
597
+ if (!failurePersisted) {
598
+ throw agendaError(
599
+ "AGENDA_SCHEDULER_FAILURE_PERSIST_FAILED",
600
+ `Agenda execution failed and its evidence could not be persisted: ${expected.id}`,
601
+ undefined,
602
+ completionError,
603
+ );
604
+ }
605
+ }
606
+ throw agendaError(
607
+ typeof error?.code === "string"
608
+ ? error.code
609
+ : expected.kind === "monitor"
610
+ ? "AGENDA_MONITOR_EXECUTION_FAILED"
611
+ : "AGENDA_AGENT_EXECUTION_FAILED",
612
+ typeof error?.message === "string"
613
+ ? error.message
614
+ : `Agenda ${expected.kind === "monitor" ? "monitor" : "agent"} execution failed: ${expected.id}`,
615
+ undefined,
616
+ error,
617
+ {
618
+ retryable: error?.retryable !== false,
619
+ retryAt: normalizeEpochMs(
620
+ Number(now()) + AGENDA_SCHEDULER_RETRY_DELAY_MS,
621
+ "agenda.retryAt",
622
+ ),
623
+ },
624
+ );
625
+ }
626
+
627
+ try {
628
+ const completed = agendaStore.completeSchedulerExecution(expected.id, {
629
+ occurrenceId: occurrence.id,
630
+ snapshotDigest: expectedDigest,
631
+ attempt: occurrence.attempt,
632
+ outcome: "succeeded",
633
+ result: action,
634
+ ...(observation
635
+ ? {
636
+ monitorCheck: {
637
+ matched: observation.matched,
638
+ mtimeMs: observation.mtimeMs,
639
+ eventId: observation.eventId,
640
+ authority: observation.authority,
641
+ },
642
+ }
643
+ : {}),
644
+ atMs: now(),
645
+ });
646
+ const persistedAction = completed.schedulerExecution?.result || action;
647
+ if (
648
+ observation?.matched &&
649
+ !observation.duplicate &&
650
+ observation.notification
651
+ ) {
652
+ if (typeof notifyMonitor !== "function") {
653
+ return {
654
+ ...persistedAction,
655
+ notifyError: "Agenda monitor notification handler is unavailable",
656
+ };
657
+ }
658
+ try {
659
+ await notifyMonitor(observation.notification);
660
+ } catch (notifyError) {
661
+ return {
662
+ ...persistedAction,
663
+ notifyError:
664
+ typeof notifyError?.message === "string"
665
+ ? notifyError.message
666
+ : String(notifyError),
667
+ };
668
+ }
669
+ }
670
+ return persistedAction;
671
+ } catch (completionError) {
672
+ const persisted = agendaStore.get(expected.id);
673
+ if (
674
+ matchingExecutionEvidence(persisted, {
675
+ occurrenceId: occurrence.id,
676
+ snapshotDigest: expectedDigest,
677
+ attempt: occurrence.attempt,
678
+ status: "succeeded",
679
+ })
680
+ ) {
681
+ return recoveredResult(expected, persisted.schedulerExecution);
682
+ }
683
+ throw agendaError(
684
+ "AGENDA_SCHEDULER_OUTCOME_UNKNOWN",
685
+ `Agenda agent finished but its durable outcome is unknown; refusing a retry: ${expected.id}`,
686
+ undefined,
687
+ completionError,
688
+ );
689
+ }
690
+ },
691
+ classifyError(error) {
692
+ return {
693
+ retryable: error?.retryable === true,
694
+ ...(error?.retryAt === undefined ? {} : { retryAt: error.retryAt }),
695
+ };
696
+ },
697
+ };
698
+ }
699
+
700
+ export class AgendaSchedulerBridge {
701
+ constructor({
702
+ agendaStore,
703
+ schedulerStore,
704
+ runAgent,
705
+ runMonitor,
706
+ notifyMonitor,
707
+ now = Date.now,
708
+ ownerId,
709
+ leaseMs,
710
+ renewIntervalMs,
711
+ authorityResolver,
712
+ } = {}) {
713
+ if (typeof now !== "function") {
714
+ throw agendaError(
715
+ "AGENDA_SCHEDULER_CLOCK_REQUIRED",
716
+ "Agenda scheduler bridge requires a clock function",
717
+ );
718
+ }
719
+ this.agendaStore = agendaStore;
720
+ this.schedulerStore = schedulerStore;
721
+ this.now = now;
722
+ this.runtime = new SchedulerRuntime({
723
+ store: schedulerStore,
724
+ adapters: [
725
+ createAgendaSchedulerAdapter({
726
+ agendaStore,
727
+ runAgent,
728
+ runMonitor,
729
+ notifyMonitor,
730
+ now,
731
+ }),
732
+ ],
733
+ authorize:
734
+ authorityResolver ||
735
+ createSchedulerAuthorityResolver({
736
+ store: schedulerStore,
737
+ validate: authorizeAgendaOccurrence,
738
+ units: ({ occurrence }) =>
739
+ Math.max(
740
+ 1,
741
+ Number(occurrence?.payload?.entry?.runPolicy?.maxTurns) || 1,
742
+ ),
743
+ }),
744
+ ...(ownerId === undefined ? {} : { ownerId }),
745
+ ...(leaseMs === undefined ? {} : { leaseMs }),
746
+ ...(renewIntervalMs === undefined ? {} : { renewIntervalMs }),
747
+ });
748
+ }
749
+
750
+ async runDue({ signal } = {}) {
751
+ const results = [];
752
+ const observedOccurrences = new Set();
753
+ const recovered = await this.runtime.runUntilIdle({
754
+ limit: 10_000,
755
+ signal,
756
+ jobKind: AGENDA_SCHEDULER_KIND,
757
+ });
758
+ for (const result of recovered.results) {
759
+ const occurrence = result.occurrence;
760
+ const entry = occurrence?.payload?.entry;
761
+ if (!entry?.id) continue;
762
+ observedOccurrences.add(occurrence.id);
763
+ results.push({
764
+ entry: entry.id,
765
+ kind: entry.kind,
766
+ occurrence: occurrence.id,
767
+ recovered: true,
768
+ result,
769
+ });
770
+ }
771
+ if (signal?.aborted) return results;
772
+
773
+ const now = normalizeEpochMs(Number(this.now()), "now");
774
+ const due = [
775
+ ...this.agendaStore.due("wakeup", now),
776
+ ...this.agendaStore.due("cron", now),
777
+ ...this.agendaStore.due("monitor", now),
778
+ ];
779
+ for (const entry of due) {
780
+ const occurrence = enqueueAgendaEntry(this.schedulerStore, entry);
781
+ if (observedOccurrences.has(occurrence.id)) continue;
782
+ const result = await this.runtime.runOccurrence(occurrence.id, {
783
+ signal,
784
+ });
785
+ results.push({
786
+ entry: entry.id,
787
+ kind: entry.kind,
788
+ occurrence: occurrence.id,
789
+ deduplicated: occurrence.deduplicated,
790
+ result,
791
+ });
792
+ }
793
+ return results;
794
+ }
795
+ }
796
+
797
+ export function agendaBridgeAction(entry) {
798
+ const result = entry?.result;
799
+ if (result?.status === "succeeded") {
800
+ return result.result;
801
+ }
802
+ if (result?.status === "busy" || result?.status === "idle") return null;
803
+ return {
804
+ id: entry.entry,
805
+ kind: entry.kind,
806
+ action: "error",
807
+ error: result?.error?.message || `agenda occurrence ${result?.status}`,
808
+ ...(typeof result?.error?.code === "string"
809
+ ? { errorCode: result.error.code }
810
+ : {}),
811
+ };
812
+ }