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,527 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import {
3
+ OCCURRENCE_STATUS,
4
+ SchedulerKernelError,
5
+ normalizeEpochMs,
6
+ normalizeIdentifier,
7
+ normalizeJson,
8
+ } from "./contract.js";
9
+
10
+ export const DEFAULT_RUNTIME_LEASE_MS = 60_000;
11
+ export const MIN_RUNTIME_LEASE_MS = 1_000;
12
+ export const DEFAULT_RUN_LIMIT = 100;
13
+ export const MAX_RUN_LIMIT = 10_000;
14
+
15
+ const TERMINAL_OCCURRENCE_STATUSES = new Set([
16
+ OCCURRENCE_STATUS.SUCCEEDED,
17
+ OCCURRENCE_STATUS.DEAD_LETTER,
18
+ ]);
19
+
20
+ function runtimeError(code, message, details = undefined, cause = undefined) {
21
+ return new SchedulerKernelError(
22
+ code,
23
+ message,
24
+ details,
25
+ cause ? { cause } : undefined,
26
+ );
27
+ }
28
+
29
+ function assertStore(store) {
30
+ const required = [
31
+ "claimNext",
32
+ "claimOccurrence",
33
+ "getJob",
34
+ "getOccurrence",
35
+ "renew",
36
+ "settle",
37
+ ];
38
+ if (
39
+ !store ||
40
+ required.some((method) => typeof store[method] !== "function")
41
+ ) {
42
+ throw runtimeError(
43
+ "SCHEDULER_RUNTIME_INVALID_STORE",
44
+ "Scheduler runtime requires a compatible scheduler store",
45
+ );
46
+ }
47
+ return store;
48
+ }
49
+
50
+ function normalizeLeaseMs(value = DEFAULT_RUNTIME_LEASE_MS) {
51
+ if (!Number.isSafeInteger(value) || value < MIN_RUNTIME_LEASE_MS) {
52
+ throw runtimeError(
53
+ "SCHEDULER_RUNTIME_INVALID_LEASE",
54
+ `leaseMs must be an integer >= ${MIN_RUNTIME_LEASE_MS}`,
55
+ );
56
+ }
57
+ return value;
58
+ }
59
+
60
+ function normalizeRenewInterval(value, leaseMs) {
61
+ const interval =
62
+ value === undefined ? Math.max(250, Math.floor(leaseMs / 3)) : value;
63
+ if (!Number.isSafeInteger(interval) || interval < 1 || interval >= leaseMs) {
64
+ throw runtimeError(
65
+ "SCHEDULER_RUNTIME_INVALID_RENEW_INTERVAL",
66
+ "renewIntervalMs must be a positive integer smaller than leaseMs",
67
+ );
68
+ }
69
+ return interval;
70
+ }
71
+
72
+ function normalizeRunLimit(value = DEFAULT_RUN_LIMIT) {
73
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_RUN_LIMIT) {
74
+ throw runtimeError(
75
+ "SCHEDULER_RUNTIME_INVALID_LIMIT",
76
+ `limit must be an integer between 1 and ${MAX_RUN_LIMIT}`,
77
+ );
78
+ }
79
+ return value;
80
+ }
81
+
82
+ function normalizeAdapter(adapter) {
83
+ if (!adapter || typeof adapter !== "object" || Array.isArray(adapter)) {
84
+ throw runtimeError(
85
+ "SCHEDULER_RUNTIME_INVALID_ADAPTER",
86
+ "Scheduler adapter must be an object",
87
+ );
88
+ }
89
+ const kind = normalizeIdentifier(adapter.kind, "adapter.kind", {
90
+ maxLength: 128,
91
+ });
92
+ if (typeof adapter.execute !== "function") {
93
+ throw runtimeError(
94
+ "SCHEDULER_RUNTIME_INVALID_ADAPTER",
95
+ `Scheduler adapter must implement execute(): ${kind}`,
96
+ );
97
+ }
98
+ return { kind, adapter };
99
+ }
100
+
101
+ function normalizeAdapters(adapters = []) {
102
+ const map = new Map();
103
+ const values = adapters instanceof Map ? adapters.values() : adapters;
104
+ if (!values || typeof values[Symbol.iterator] !== "function") {
105
+ throw runtimeError(
106
+ "SCHEDULER_RUNTIME_INVALID_ADAPTER",
107
+ "adapters must be iterable",
108
+ );
109
+ }
110
+ for (const value of values) {
111
+ const { kind, adapter } = normalizeAdapter(value);
112
+ if (map.has(kind)) {
113
+ throw runtimeError(
114
+ "SCHEDULER_RUNTIME_DUPLICATE_ADAPTER",
115
+ `Scheduler adapter is registered more than once: ${kind}`,
116
+ );
117
+ }
118
+ map.set(kind, adapter);
119
+ }
120
+ return map;
121
+ }
122
+
123
+ function safeError(error, fallbackCode = "execution_failed") {
124
+ const code =
125
+ typeof error?.code === "string" && error.code.trim()
126
+ ? error.code.trim().slice(0, 128)
127
+ : fallbackCode;
128
+ const message =
129
+ typeof error?.message === "string" && error.message
130
+ ? error.message.slice(0, 2_000)
131
+ : String(error ?? "Execution failed").slice(0, 2_000);
132
+ let details;
133
+ try {
134
+ if (error?.details !== undefined) {
135
+ details = normalizeJson(error.details, "runtime.error.details");
136
+ }
137
+ } catch {
138
+ details = { omitted: "invalid_error_details" };
139
+ }
140
+ const base = {
141
+ code,
142
+ message,
143
+ ...(typeof error?.name === "string"
144
+ ? { name: error.name.slice(0, 128) }
145
+ : {}),
146
+ };
147
+ try {
148
+ return normalizeJson(
149
+ { ...base, ...(details === undefined ? {} : { details }) },
150
+ "runtime.error",
151
+ );
152
+ } catch {
153
+ return normalizeJson(
154
+ { ...base, details: { omitted: "error_details_too_large" } },
155
+ "runtime.error",
156
+ );
157
+ }
158
+ }
159
+
160
+ function executionPolicy(error, adapter, context) {
161
+ let policy = null;
162
+ if (typeof adapter?.classifyError === "function") {
163
+ policy = adapter.classifyError(error, context);
164
+ }
165
+ if (policy !== null && policy !== undefined) {
166
+ if (typeof policy !== "object" || Array.isArray(policy)) {
167
+ throw runtimeError(
168
+ "SCHEDULER_RUNTIME_INVALID_ERROR_POLICY",
169
+ "adapter.classifyError() must return an object",
170
+ );
171
+ }
172
+ return {
173
+ retryable: policy.retryable !== false,
174
+ ...(policy.retryAt === undefined
175
+ ? {}
176
+ : { retryAt: normalizeEpochMs(policy.retryAt, "retryAt") }),
177
+ };
178
+ }
179
+ return {
180
+ retryable: error?.retryable !== false,
181
+ ...(error?.retryAt === undefined
182
+ ? {}
183
+ : { retryAt: normalizeEpochMs(error.retryAt, "retryAt") }),
184
+ };
185
+ }
186
+
187
+ function createLinkedAbortController(signal) {
188
+ const controller = new AbortController();
189
+ const abort = () => controller.abort(signal?.reason);
190
+ if (signal?.aborted) abort();
191
+ else signal?.addEventListener?.("abort", abort, { once: true });
192
+ return {
193
+ controller,
194
+ dispose() {
195
+ signal?.removeEventListener?.("abort", abort);
196
+ },
197
+ };
198
+ }
199
+
200
+ function settledResult(occurrence, extra = {}) {
201
+ return {
202
+ status: occurrence.status,
203
+ occurrence,
204
+ result: occurrence.result,
205
+ error: occurrence.lastError,
206
+ ...extra,
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Host-owned execution service for scheduler occurrences.
212
+ *
213
+ * The runtime is deliberately stricter than the SQLite store: it resolves the
214
+ * exact job revision, requires an explicit authorization decision, selects a
215
+ * registered adapter, renews the durable lease while the adapter is running,
216
+ * and settles only under the same owner/fence token. Adapters receive the
217
+ * occurrence snapshots, never mutable host-side schedule definitions by
218
+ * implication.
219
+ */
220
+ export class SchedulerRuntime {
221
+ constructor({
222
+ store,
223
+ adapters = [],
224
+ authorize,
225
+ ownerId = `scheduler-runtime:${process.pid}:${randomUUID()}`,
226
+ leaseMs = DEFAULT_RUNTIME_LEASE_MS,
227
+ renewIntervalMs,
228
+ setIntervalFn = setInterval,
229
+ clearIntervalFn = clearInterval,
230
+ } = {}) {
231
+ this.store = assertStore(store);
232
+ this.adapters = normalizeAdapters(adapters);
233
+ if (typeof authorize !== "function") {
234
+ throw runtimeError(
235
+ "SCHEDULER_RUNTIME_AUTHORIZER_REQUIRED",
236
+ "Scheduler runtime requires an explicit authorize() callback",
237
+ );
238
+ }
239
+ this.authorize = authorize;
240
+ this.ownerId = normalizeIdentifier(ownerId, "ownerId");
241
+ this.leaseMs = normalizeLeaseMs(leaseMs);
242
+ this.renewIntervalMs = normalizeRenewInterval(
243
+ renewIntervalMs,
244
+ this.leaseMs,
245
+ );
246
+ if (
247
+ typeof setIntervalFn !== "function" ||
248
+ typeof clearIntervalFn !== "function"
249
+ ) {
250
+ throw runtimeError(
251
+ "SCHEDULER_RUNTIME_INVALID_TIMER",
252
+ "Scheduler runtime requires timer functions",
253
+ );
254
+ }
255
+ this.setIntervalFn = setIntervalFn;
256
+ this.clearIntervalFn = clearIntervalFn;
257
+ }
258
+
259
+ registerAdapter(adapter) {
260
+ const normalized = normalizeAdapter(adapter);
261
+ if (this.adapters.has(normalized.kind)) {
262
+ throw runtimeError(
263
+ "SCHEDULER_RUNTIME_DUPLICATE_ADAPTER",
264
+ `Scheduler adapter is registered more than once: ${normalized.kind}`,
265
+ );
266
+ }
267
+ this.adapters.set(normalized.kind, normalized.adapter);
268
+ return this;
269
+ }
270
+
271
+ async _settleRejected(occurrence, error, retryable = false) {
272
+ const settled = this.store.settle({
273
+ occurrenceId: occurrence.id,
274
+ ownerId: this.ownerId,
275
+ fence: occurrence.fence,
276
+ outcome: "failed",
277
+ error: safeError(error, "scheduler_rejected"),
278
+ retryable,
279
+ });
280
+ return settledResult(settled);
281
+ }
282
+
283
+ async _executeClaim(occurrence, { signal } = {}) {
284
+ const job = this.store.getJob(occurrence.jobId);
285
+ if (!job) {
286
+ return this._settleRejected(
287
+ occurrence,
288
+ runtimeError(
289
+ "SCHEDULER_RUNTIME_JOB_MISSING",
290
+ `Scheduler job disappeared before execution: ${occurrence.jobId}`,
291
+ ),
292
+ );
293
+ }
294
+ if (job.revision !== occurrence.jobRevision) {
295
+ return this._settleRejected(
296
+ occurrence,
297
+ runtimeError(
298
+ "SCHEDULER_RUNTIME_STALE_REVISION",
299
+ `Occurrence references stale job revision: ${occurrence.id}`,
300
+ {
301
+ occurrenceRevision: occurrence.jobRevision,
302
+ currentRevision: job.revision,
303
+ },
304
+ ),
305
+ );
306
+ }
307
+ const adapter = this.adapters.get(job.kind);
308
+ if (!adapter) {
309
+ return this._settleRejected(
310
+ occurrence,
311
+ runtimeError(
312
+ "SCHEDULER_RUNTIME_ADAPTER_MISSING",
313
+ `No scheduler adapter is registered for job kind: ${job.kind}`,
314
+ ),
315
+ );
316
+ }
317
+
318
+ let decision;
319
+ let allowed = false;
320
+ let deniedReason = "not_allowed";
321
+ try {
322
+ decision = await this.authorize({ job, occurrence, signal });
323
+ allowed =
324
+ decision !== null &&
325
+ typeof decision === "object" &&
326
+ decision.allowed === true;
327
+ if (typeof decision?.reason === "string") {
328
+ deniedReason = decision.reason.slice(0, 256);
329
+ }
330
+ } catch (error) {
331
+ const authError = runtimeError(
332
+ "SCHEDULER_RUNTIME_AUTHORIZATION_UNAVAILABLE",
333
+ "Scheduler authorization could not be completed",
334
+ undefined,
335
+ error,
336
+ );
337
+ authError.retryable = true;
338
+ return this._settleRejected(occurrence, authError, true);
339
+ }
340
+ if (!allowed) {
341
+ return this._settleRejected(
342
+ occurrence,
343
+ runtimeError(
344
+ "SCHEDULER_RUNTIME_AUTHORIZATION_DENIED",
345
+ "Scheduler occurrence was denied by the authority resolver",
346
+ { reason: deniedReason },
347
+ ),
348
+ );
349
+ }
350
+
351
+ // Authorization may involve remote policy evaluation. Revalidate ownership
352
+ // immediately before any adapter side effect so an expired/reclaimed claim
353
+ // cannot execute under a stale fence.
354
+ occurrence = this.store.renew({
355
+ occurrenceId: occurrence.id,
356
+ ownerId: this.ownerId,
357
+ fence: occurrence.fence,
358
+ leaseMs: this.leaseMs,
359
+ });
360
+
361
+ const linked = createLinkedAbortController(signal);
362
+ if (linked.controller.signal.aborted) {
363
+ linked.dispose();
364
+ const aborted = runtimeError(
365
+ "SCHEDULER_RUNTIME_ABORTED",
366
+ "Scheduler occurrence was aborted before adapter execution",
367
+ );
368
+ aborted.retryable = true;
369
+ return this._settleRejected(occurrence, aborted, true);
370
+ }
371
+ let leaseError = null;
372
+ let renewing = false;
373
+ const renew = () => {
374
+ if (renewing || leaseError || linked.controller.signal.aborted) return;
375
+ renewing = true;
376
+ try {
377
+ this.store.renew({
378
+ occurrenceId: occurrence.id,
379
+ ownerId: this.ownerId,
380
+ fence: occurrence.fence,
381
+ leaseMs: this.leaseMs,
382
+ });
383
+ } catch (error) {
384
+ leaseError = error;
385
+ linked.controller.abort(error);
386
+ } finally {
387
+ renewing = false;
388
+ }
389
+ };
390
+ let timer;
391
+ try {
392
+ timer = this.setIntervalFn(renew, this.renewIntervalMs);
393
+ } catch (error) {
394
+ linked.dispose();
395
+ return this._settleRejected(
396
+ occurrence,
397
+ runtimeError(
398
+ "SCHEDULER_RUNTIME_TIMER_FAILED",
399
+ "Scheduler lease-renewal timer could not be started",
400
+ undefined,
401
+ error,
402
+ ),
403
+ true,
404
+ );
405
+ }
406
+ timer?.unref?.();
407
+ const context = {
408
+ job,
409
+ occurrence,
410
+ authority: occurrence.authority,
411
+ decision,
412
+ signal: linked.controller.signal,
413
+ renewLease: renew,
414
+ };
415
+ try {
416
+ const result = await adapter.execute(context);
417
+ if (leaseError) throw leaseError;
418
+ if (linked.controller.signal.aborted) {
419
+ const aborted = runtimeError(
420
+ "SCHEDULER_RUNTIME_ABORTED",
421
+ "Scheduler occurrence was aborted before settlement",
422
+ );
423
+ aborted.retryable = true;
424
+ throw aborted;
425
+ }
426
+ const settled = this.store.settle({
427
+ occurrenceId: occurrence.id,
428
+ ownerId: this.ownerId,
429
+ fence: occurrence.fence,
430
+ outcome: "succeeded",
431
+ result: result ?? null,
432
+ });
433
+ return settledResult(settled);
434
+ } catch (error) {
435
+ if (leaseError) throw leaseError;
436
+ let policy;
437
+ let settlementError = error;
438
+ try {
439
+ policy = executionPolicy(error, adapter, context);
440
+ } catch (classificationError) {
441
+ settlementError = runtimeError(
442
+ "SCHEDULER_RUNTIME_ERROR_POLICY_FAILED",
443
+ "Scheduler adapter error policy failed",
444
+ undefined,
445
+ classificationError,
446
+ );
447
+ policy = { retryable: false };
448
+ }
449
+ const settled = this.store.settle({
450
+ occurrenceId: occurrence.id,
451
+ ownerId: this.ownerId,
452
+ fence: occurrence.fence,
453
+ outcome: "failed",
454
+ error: safeError(settlementError),
455
+ retryable: policy.retryable,
456
+ ...(policy.retryAt === undefined ? {} : { retryAt: policy.retryAt }),
457
+ });
458
+ return settledResult(settled);
459
+ } finally {
460
+ this.clearIntervalFn(timer);
461
+ linked.dispose();
462
+ }
463
+ }
464
+
465
+ async runOccurrence(occurrenceId, options = {}) {
466
+ const id = normalizeIdentifier(occurrenceId, "occurrenceId");
467
+ const current = this.store.getOccurrence(id);
468
+ if (!current) {
469
+ throw runtimeError(
470
+ "SCHEDULER_NOT_FOUND",
471
+ `Scheduler occurrence does not exist: ${id}`,
472
+ );
473
+ }
474
+ if (TERMINAL_OCCURRENCE_STATUSES.has(current.status)) {
475
+ return settledResult(current, { alreadySettled: true });
476
+ }
477
+ if (options.signal?.aborted) {
478
+ return { status: "aborted", occurrence: current };
479
+ }
480
+ const claimed = this.store.claimOccurrence({
481
+ occurrenceId: id,
482
+ ownerId: this.ownerId,
483
+ leaseMs: this.leaseMs,
484
+ });
485
+ if (!claimed) {
486
+ return {
487
+ status: "busy",
488
+ occurrence: this.store.getOccurrence(id),
489
+ };
490
+ }
491
+ return this._executeClaim(claimed, options);
492
+ }
493
+
494
+ async runNext(options = {}) {
495
+ if (options.signal?.aborted) return { status: "aborted" };
496
+ const claimed = this.store.claimNext({
497
+ ownerId: this.ownerId,
498
+ leaseMs: this.leaseMs,
499
+ ...(options.jobKind === undefined ? {} : { jobKind: options.jobKind }),
500
+ ...(options.workspaceId === undefined
501
+ ? {}
502
+ : { workspaceId: options.workspaceId }),
503
+ });
504
+ if (!claimed) return { status: "idle" };
505
+ return this._executeClaim(claimed, options);
506
+ }
507
+
508
+ async runUntilIdle({ limit, signal, jobKind, workspaceId } = {}) {
509
+ const bounded = normalizeRunLimit(limit);
510
+ const results = [];
511
+ while (results.length < bounded && !signal?.aborted) {
512
+ const result = await this.runNext({ signal, jobKind, workspaceId });
513
+ if (result.status === "idle" || result.status === "aborted") {
514
+ return { status: result.status, results };
515
+ }
516
+ results.push(result);
517
+ }
518
+ return {
519
+ status: signal?.aborted ? "aborted" : "limit_reached",
520
+ results,
521
+ };
522
+ }
523
+ }
524
+
525
+ export function createSchedulerRuntime(options) {
526
+ return new SchedulerRuntime(options);
527
+ }