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,378 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import {
4
+ SchedulerKernelError,
5
+ canonicalJson,
6
+ normalizeEpochMs,
7
+ normalizeIdentifier,
8
+ } from "./contract.js";
9
+ import { SchedulerRuntime } from "./runtime.js";
10
+ import {
11
+ bindSchedulerAuthorityPolicy,
12
+ createSchedulerAuthorityResolver,
13
+ } from "./authority-resolver.js";
14
+
15
+ export const LOOP_SCHEDULER_KIND = "loop-iteration";
16
+ export const LOOP_PROCESS_CAPABILITY = "process.execute";
17
+ export const LOOP_AGENT_CAPABILITY = "agent.execute";
18
+
19
+ function loopSchedulerError(
20
+ code,
21
+ message,
22
+ details = undefined,
23
+ cause = undefined,
24
+ ) {
25
+ return new SchedulerKernelError(
26
+ code,
27
+ message,
28
+ details,
29
+ cause ? { cause } : undefined,
30
+ );
31
+ }
32
+
33
+ function normalizedCwd(cwd) {
34
+ if (typeof cwd !== "string" || cwd.length === 0) {
35
+ throw loopSchedulerError(
36
+ "LOOP_SCHEDULER_CWD_INVALID",
37
+ "Loop scheduler requires a workspace path",
38
+ );
39
+ }
40
+ const absolute = resolve(cwd);
41
+ return process.platform === "win32" ? absolute.toLowerCase() : absolute;
42
+ }
43
+
44
+ function normalizeOperands(operands) {
45
+ if (
46
+ !Array.isArray(operands) ||
47
+ operands.length === 0 ||
48
+ operands.some(
49
+ (operand) => typeof operand !== "string" || operand.length === 0,
50
+ )
51
+ ) {
52
+ throw loopSchedulerError(
53
+ "LOOP_SCHEDULER_DEFINITION_INVALID",
54
+ "Loop scheduler requires at least one non-empty operand",
55
+ );
56
+ }
57
+ return [...operands];
58
+ }
59
+
60
+ export function loopExecutionSnapshot(definition) {
61
+ if (!definition || typeof definition !== "object") {
62
+ throw loopSchedulerError(
63
+ "LOOP_SCHEDULER_DEFINITION_INVALID",
64
+ "Loop scheduler requires an execution definition",
65
+ );
66
+ }
67
+ return {
68
+ executionId: normalizeIdentifier(
69
+ definition.executionId,
70
+ "loop.executionId",
71
+ ),
72
+ cwd: normalizedCwd(definition.cwd),
73
+ execMode: definition.execMode === true,
74
+ operands: normalizeOperands(definition.operands),
75
+ dynamic: definition.dynamic === true,
76
+ };
77
+ }
78
+
79
+ export function loopExecutionDigest(definition) {
80
+ return createHash("sha256")
81
+ .update("chainlesschain.scheduler.loop-execution.v1\0", "utf8")
82
+ .update(canonicalJson(loopExecutionSnapshot(definition)), "utf8")
83
+ .digest("hex");
84
+ }
85
+
86
+ export function loopWorkspaceId(cwd) {
87
+ return createHash("sha256")
88
+ .update("chainlesschain.scheduler.loop-workspace.v1\0", "utf8")
89
+ .update(normalizedCwd(cwd), "utf8")
90
+ .digest("hex")
91
+ .slice(0, 32);
92
+ }
93
+
94
+ export function loopSchedulerJobId(executionId) {
95
+ const id = normalizeIdentifier(executionId, "loop.executionId");
96
+ const digest = createHash("sha256")
97
+ .update("chainlesschain.scheduler.loop-job.v1\0", "utf8")
98
+ .update(id, "utf8")
99
+ .digest("hex")
100
+ .slice(0, 40);
101
+ return `loop:${digest}`;
102
+ }
103
+
104
+ export function buildLoopSchedulerJob(definition) {
105
+ const snapshot = loopExecutionSnapshot(definition);
106
+ const capability = snapshot.execMode
107
+ ? LOOP_PROCESS_CAPABILITY
108
+ : LOOP_AGENT_CAPABILITY;
109
+ return {
110
+ id: loopSchedulerJobId(snapshot.executionId),
111
+ kind: LOOP_SCHEDULER_KIND,
112
+ trigger: { source: "loop", mode: "interval" },
113
+ payload: {
114
+ definition: snapshot,
115
+ snapshotDigest: loopExecutionDigest(snapshot),
116
+ },
117
+ authority: {
118
+ schemaVersion: 1,
119
+ principal: { type: "loop", id: snapshot.executionId },
120
+ tenantId: null,
121
+ workspaceId: loopWorkspaceId(snapshot.cwd),
122
+ requestedCapabilities: [capability],
123
+ authorizationRefs: {
124
+ decisionId: null,
125
+ policyRevision: null,
126
+ grantIds: [],
127
+ approvalIds: [],
128
+ delegationIds: [],
129
+ },
130
+ },
131
+ enabled: true,
132
+ // A reclaimed first attempt is not replayed. The second claim exists only
133
+ // so the adapter can publish an explicit outcome-unknown dead letter.
134
+ maxAttempts: 2,
135
+ };
136
+ }
137
+
138
+ function comparableJob(job) {
139
+ return {
140
+ kind: job.kind,
141
+ trigger: job.trigger,
142
+ payload: job.payload,
143
+ authority: job.authority,
144
+ enabled: job.enabled,
145
+ maxAttempts: job.maxAttempts,
146
+ };
147
+ }
148
+
149
+ export function syncLoopSchedulerJob(schedulerStore, definition) {
150
+ const desired = buildLoopSchedulerJob(definition);
151
+ desired.authority = bindSchedulerAuthorityPolicy(
152
+ schedulerStore,
153
+ desired.authority,
154
+ );
155
+ let current = schedulerStore.getJob(desired.id);
156
+ if (!current) {
157
+ try {
158
+ return schedulerStore.createJob(desired);
159
+ } catch (error) {
160
+ if (error?.code !== "SCHEDULER_CONFLICT") throw error;
161
+ current = schedulerStore.getJob(desired.id);
162
+ }
163
+ }
164
+ if (
165
+ canonicalJson(comparableJob(current), "currentLoopJob") ===
166
+ canonicalJson(comparableJob(desired), "desiredLoopJob")
167
+ ) {
168
+ return current;
169
+ }
170
+ throw loopSchedulerError(
171
+ "LOOP_SCHEDULER_DEFINITION_CONFLICT",
172
+ `Saved loop execution definition changed: ${definition.executionId}`,
173
+ );
174
+ }
175
+
176
+ export function enqueueLoopIteration(
177
+ schedulerStore,
178
+ definition,
179
+ iteration,
180
+ { scheduledFor, availableAt } = {},
181
+ ) {
182
+ if (!Number.isSafeInteger(iteration) || iteration < 1) {
183
+ throw loopSchedulerError(
184
+ "LOOP_SCHEDULER_ITERATION_INVALID",
185
+ "Loop iteration must be a positive integer",
186
+ );
187
+ }
188
+ const job = syncLoopSchedulerJob(schedulerStore, definition);
189
+ const scheduled = normalizeEpochMs(scheduledFor, "loop.scheduledFor");
190
+ return schedulerStore.enqueueOccurrence({
191
+ jobId: job.id,
192
+ scheduledFor: scheduled,
193
+ availableAt: normalizeEpochMs(availableAt ?? scheduled, "loop.availableAt"),
194
+ triggerKey: `iteration:${iteration}`,
195
+ });
196
+ }
197
+
198
+ export function authorizeLoopOccurrence({ job, occurrence }) {
199
+ try {
200
+ const definition = occurrence?.payload?.definition;
201
+ const snapshot = loopExecutionSnapshot(definition);
202
+ const expectedCapability = snapshot.execMode
203
+ ? LOOP_PROCESS_CAPABILITY
204
+ : LOOP_AGENT_CAPABILITY;
205
+ const authority = occurrence.authority;
206
+ const allowed =
207
+ job?.kind === LOOP_SCHEDULER_KIND &&
208
+ occurrence.payload.snapshotDigest === loopExecutionDigest(snapshot) &&
209
+ authority?.principal?.type === "loop" &&
210
+ authority?.principal?.id === snapshot.executionId &&
211
+ authority?.workspaceId === loopWorkspaceId(snapshot.cwd) &&
212
+ Array.isArray(authority?.requestedCapabilities) &&
213
+ authority.requestedCapabilities.length === 1 &&
214
+ authority.requestedCapabilities[0] === expectedCapability;
215
+ return {
216
+ allowed,
217
+ reason: allowed
218
+ ? "loop_snapshot_bound"
219
+ : "loop_snapshot_or_authority_mismatch",
220
+ };
221
+ } catch {
222
+ return { allowed: false, reason: "loop_authority_malformed" };
223
+ }
224
+ }
225
+
226
+ function normalizeOptionalInteger(value, field, { minimum = 0 } = {}) {
227
+ if (value === null || value === undefined) return null;
228
+ if (!Number.isSafeInteger(value) || value < minimum) {
229
+ throw loopSchedulerError(
230
+ "LOOP_SCHEDULER_RESULT_INVALID",
231
+ `${field} must be a safe integer greater than or equal to ${minimum}`,
232
+ );
233
+ }
234
+ return value;
235
+ }
236
+
237
+ function persistedIterationResult(iteration, result) {
238
+ const output = String(result?.output || "");
239
+ return {
240
+ iteration,
241
+ exitCode: normalizeOptionalInteger(result?.exitCode, "loop.exitCode", {
242
+ minimum: Number.MIN_SAFE_INTEGER,
243
+ }),
244
+ signal:
245
+ result?.signal === null || result?.signal === undefined
246
+ ? null
247
+ : String(result.signal).slice(0, 128),
248
+ durationMs: normalizeOptionalInteger(result?.durationMs, "loop.durationMs"),
249
+ done: result?.done === true,
250
+ nextDelayMs: normalizeOptionalInteger(
251
+ result?.nextDelayMs,
252
+ "loop.nextDelayMs",
253
+ ),
254
+ matchedUntil: result?.matchedUntil === true,
255
+ outputBytes: Buffer.byteLength(output, "utf8"),
256
+ outputDigest: createHash("sha256").update(output, "utf8").digest("hex"),
257
+ };
258
+ }
259
+
260
+ export function createLoopSchedulerAdapter({ runIteration } = {}) {
261
+ if (typeof runIteration !== "function") {
262
+ throw loopSchedulerError(
263
+ "LOOP_SCHEDULER_RUNNER_REQUIRED",
264
+ "Loop scheduler adapter requires a runIteration function",
265
+ );
266
+ }
267
+ const transientResults = new Map();
268
+ return {
269
+ kind: LOOP_SCHEDULER_KIND,
270
+ async execute({ occurrence, signal }) {
271
+ const definition = loopExecutionSnapshot(occurrence?.payload?.definition);
272
+ if (
273
+ occurrence.payload.snapshotDigest !== loopExecutionDigest(definition)
274
+ ) {
275
+ throw loopSchedulerError(
276
+ "LOOP_SCHEDULER_SNAPSHOT_INVALID",
277
+ `Loop occurrence snapshot is invalid: ${occurrence.id}`,
278
+ );
279
+ }
280
+ const match = /^iteration:(\d+)$/.exec(occurrence.triggerKey);
281
+ const iteration = match ? Number(match[1]) : NaN;
282
+ if (!Number.isSafeInteger(iteration) || iteration < 1) {
283
+ throw loopSchedulerError(
284
+ "LOOP_SCHEDULER_ITERATION_INVALID",
285
+ `Loop occurrence iteration key is invalid: ${occurrence.id}`,
286
+ );
287
+ }
288
+ if (occurrence.attempt > 1) {
289
+ throw loopSchedulerError(
290
+ "LOOP_SCHEDULER_OUTCOME_UNKNOWN",
291
+ `Loop iteration may already have produced side effects; refusing replay: ${occurrence.id}`,
292
+ );
293
+ }
294
+ const result =
295
+ (await runIteration(iteration, { signal, definition })) || {};
296
+ const persisted = persistedIterationResult(iteration, result);
297
+ transientResults.set(occurrence.id, {
298
+ ...persisted,
299
+ output: result.output,
300
+ });
301
+ return persisted;
302
+ },
303
+ classifyError() {
304
+ return { retryable: false };
305
+ },
306
+ consumeResult(occurrenceId) {
307
+ const result = transientResults.get(occurrenceId) || null;
308
+ transientResults.delete(occurrenceId);
309
+ return result;
310
+ },
311
+ };
312
+ }
313
+
314
+ export class LoopSchedulerBridge {
315
+ constructor({
316
+ schedulerStore,
317
+ definition,
318
+ runIteration,
319
+ ownerId,
320
+ leaseMs,
321
+ renewIntervalMs,
322
+ authorityResolver,
323
+ } = {}) {
324
+ if (!schedulerStore) {
325
+ throw loopSchedulerError(
326
+ "LOOP_SCHEDULER_STORE_REQUIRED",
327
+ "Loop scheduler bridge requires a scheduler store",
328
+ );
329
+ }
330
+ this.schedulerStore = schedulerStore;
331
+ this.definition = loopExecutionSnapshot(definition);
332
+ this.job = syncLoopSchedulerJob(schedulerStore, this.definition);
333
+ this.adapter = createLoopSchedulerAdapter({ runIteration });
334
+ this.runtime = new SchedulerRuntime({
335
+ store: schedulerStore,
336
+ adapters: [this.adapter],
337
+ authorize:
338
+ authorityResolver ||
339
+ createSchedulerAuthorityResolver({
340
+ store: schedulerStore,
341
+ validate: authorizeLoopOccurrence,
342
+ }),
343
+ ...(ownerId === undefined ? {} : { ownerId }),
344
+ ...(leaseMs === undefined ? {} : { leaseMs }),
345
+ ...(renewIntervalMs === undefined ? {} : { renewIntervalMs }),
346
+ });
347
+ }
348
+
349
+ async runIteration(iteration, { scheduledFor, signal } = {}) {
350
+ const occurrence = enqueueLoopIteration(
351
+ this.schedulerStore,
352
+ this.definition,
353
+ iteration,
354
+ { scheduledFor },
355
+ );
356
+ const result = await this.runtime.runOccurrence(occurrence.id, { signal });
357
+ if (result.status === "succeeded") {
358
+ const transient = this.adapter.consumeResult(occurrence.id);
359
+ return {
360
+ ...(transient || result.result || {}),
361
+ ...(result.alreadySettled || !transient ? { recovered: true } : {}),
362
+ output: transient?.output || "",
363
+ schedulerOccurrenceId: occurrence.id,
364
+ };
365
+ }
366
+ if (result.status === "busy") {
367
+ throw loopSchedulerError(
368
+ "LOOP_SCHEDULER_BUSY",
369
+ `Loop iteration is already owned by another driver: ${iteration}`,
370
+ );
371
+ }
372
+ throw loopSchedulerError(
373
+ result.error?.code || "LOOP_SCHEDULER_EXECUTION_FAILED",
374
+ result.error?.message || `Loop iteration failed: ${iteration}`,
375
+ { status: result.status, occurrenceId: occurrence.id },
376
+ );
377
+ }
378
+ }