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
@@ -29,6 +29,11 @@ import { monitorEventEnvelope, capEventPayload } from "../lib/monitor-event.js";
29
29
  import { unattendedDisallowedTools } from "../lib/unattended-action-policy.js";
30
30
  import executionBroker from "../lib/process-execution-broker/index.js";
31
31
  import { collectWorkspacePluginBinSandboxPolicy } from "../lib/plugin-runtime/bin.js";
32
+ import {
33
+ AgendaSchedulerBridge,
34
+ agendaBridgeAction,
35
+ } from "../lib/scheduler-kernel/agenda-adapter.js";
36
+ import { openSchedulerStore } from "../lib/scheduler-kernel/store.js";
32
37
  import sharedShellPolicy from "../runtime/coding-agent-shell-policy.cjs";
33
38
 
34
39
  const { evaluateShellCommandPolicy } = sharedShellPolicy;
@@ -144,6 +149,70 @@ function isExecutionPolicyFailure(error) {
144
149
  );
145
150
  }
146
151
 
152
+ export async function evaluateAgendaMonitor(
153
+ entry,
154
+ { runCommand, readWatchedFile, fetchUrl, now },
155
+ ) {
156
+ let output = "";
157
+ let matched = false;
158
+ let mtimeMs = null;
159
+ if (entry.source === "file") {
160
+ const file = await readWatchedFile(entry.watchFile);
161
+ output = file.content;
162
+ mtimeMs = file.mtimeMs ?? null;
163
+ if (entry.watchChange) {
164
+ matched =
165
+ entry.lastMtimeMs != null &&
166
+ mtimeMs != null &&
167
+ mtimeMs > entry.lastMtimeMs;
168
+ } else {
169
+ matched = entry.stopWhen
170
+ ? new RegExp(entry.stopWhen).test(file.content)
171
+ : file.exists;
172
+ }
173
+ } else if (entry.source === "http") {
174
+ const res = await fetchUrl(entry.watchUrl);
175
+ output = res.body;
176
+ matched = entry.stopWhen
177
+ ? new RegExp(entry.stopWhen).test(res.body)
178
+ : res.ok;
179
+ } else {
180
+ const workspaceCwd = resolveMonitorWorkspaceCwd(entry);
181
+ assertMonitorShellPolicy(entry.command);
182
+ output = await runCommand(entry.command, { workspaceCwd });
183
+ matched = entry.stopWhen ? new RegExp(entry.stopWhen).test(output) : false;
184
+ }
185
+ if (!matched) return { matched: false, mtimeMs };
186
+
187
+ const what =
188
+ entry.source === "file"
189
+ ? entry.watchFile
190
+ : entry.source === "http"
191
+ ? entry.watchUrl
192
+ : entry.command;
193
+ const envelope = monitorEventEnvelope(
194
+ entry.id,
195
+ { what, output },
196
+ { at: now, source: entry.source },
197
+ );
198
+ const duplicate =
199
+ entry.lastEventId != null && entry.lastEventId === envelope.event_id;
200
+ return {
201
+ matched: true,
202
+ mtimeMs,
203
+ eventId: envelope.event_id,
204
+ authority: envelope.authority,
205
+ truncated: envelope.truncated === true,
206
+ notification: duplicate
207
+ ? null
208
+ : {
209
+ title: entry.notify?.title || `Monitor matched: ${what}`,
210
+ body: capEventPayload(output, 500).value,
211
+ level: "success",
212
+ },
213
+ };
214
+ }
215
+
147
216
  export function registerAgendaCommand(program) {
148
217
  const cmd = program
149
218
  .command("agenda")
@@ -228,7 +297,7 @@ export function runAgendaList(options = {}, _deps = {}) {
228
297
  e.kind === "wakeup"
229
298
  ? new Date(e.dueAt).toISOString()
230
299
  : e.kind === "cron"
231
- ? `${e.cron} (next ${new Date(e.nextAt).toISOString()})`
300
+ ? `${e.cron}${e.timeZone ? ` [${e.timeZone}]` : ""} (next ${new Date(e.nextAt).toISOString()})`
232
301
  : `${monitorWhat} (next ${new Date(e.nextAt).toISOString()})`;
233
302
  const expiry =
234
303
  e.expiresAt != null
@@ -339,18 +408,63 @@ export async function runAgendaRun(options = {}, _deps = {}) {
339
408
  const readWatchedFile = _deps.readWatchedFile || defaultReadWatchedFile;
340
409
  const fetchUrl = _deps.fetchUrl || defaultFetchUrl;
341
410
  const notify = _deps.notify || sendAgentNotification;
342
- const now = _deps.now ? _deps.now() : Date.now();
411
+ const clock = _deps.now || Date.now;
412
+ const now = clock();
343
413
  // Retire expired entries BEFORE firing due ones — an expired task never gets
344
414
  // a final fire (schedule-planner semantics). Dry-run inspects without mutating.
345
415
  const retired = options.dryRun
346
416
  ? partitionSchedule(store.list(), { now }).expired
347
417
  : store.retireExpired(now);
418
+ const actions = [];
419
+ const useSchedulerKernel =
420
+ !options.dryRun &&
421
+ (_deps.useSchedulerKernel === true ||
422
+ _deps.schedulerStore != null ||
423
+ _deps.store == null);
424
+ let schedulerEntries = [];
425
+ if (useSchedulerKernel) {
426
+ let schedulerStore = _deps.schedulerStore;
427
+ const ownsSchedulerStore = schedulerStore == null;
428
+ try {
429
+ schedulerStore ||= openSchedulerStore();
430
+ const bridge = new AgendaSchedulerBridge({
431
+ agendaStore: store,
432
+ schedulerStore,
433
+ runAgent: ({ prompt, runPolicy }) => spawnAgent(prompt, runPolicy),
434
+ runMonitor: ({ entry }) =>
435
+ evaluateAgendaMonitor(entry, {
436
+ runCommand,
437
+ readWatchedFile,
438
+ fetchUrl,
439
+ now: clock(),
440
+ }),
441
+ notifyMonitor: notify,
442
+ now: clock,
443
+ ...(options.leaseMs === undefined
444
+ ? {}
445
+ : { leaseMs: Number(options.leaseMs) }),
446
+ ...(_deps.schedulerOwnerId === undefined
447
+ ? {}
448
+ : { ownerId: _deps.schedulerOwnerId }),
449
+ });
450
+ schedulerEntries = await bridge.runDue({
451
+ signal: options.signal || _deps.signal,
452
+ });
453
+ for (const scheduled of schedulerEntries) {
454
+ const action = agendaBridgeAction(scheduled);
455
+ if (action) actions.push(action);
456
+ }
457
+ } finally {
458
+ if (ownsSchedulerStore) schedulerStore?.close();
459
+ }
460
+ }
348
461
  const due = options.dryRun
349
462
  ? store.due(null, now)
350
- : typeof store.claimDue === "function"
351
- ? store.claimDue(now, options.leaseMs)
352
- : store.due(null, now);
353
- const actions = [];
463
+ : useSchedulerKernel
464
+ ? []
465
+ : typeof store.claimDue === "function"
466
+ ? store.claimDue(now, options.leaseMs)
467
+ : store.due(null, now);
354
468
 
355
469
  for (const entry of due) {
356
470
  if (options.dryRun) {
@@ -488,7 +602,11 @@ export async function runAgendaRun(options = {}, _deps = {}) {
488
602
  if (options.json) {
489
603
  log(
490
604
  JSON.stringify(
491
- { due: due.length, retired: retiredSummary, actions },
605
+ {
606
+ due: due.length + schedulerEntries.length,
607
+ retired: retiredSummary,
608
+ actions,
609
+ },
492
610
  null,
493
611
  2,
494
612
  ),
@@ -4,8 +4,30 @@
4
4
  */
5
5
 
6
6
  import chalk from "chalk";
7
+ import {
8
+ inspectAutomationExecutionAuthority,
9
+ setAutomationExecutionBudget,
10
+ } from "../lib/automation-execution-authority.js";
11
+ import {
12
+ buildAutomationCenterProjection,
13
+ runAutomationCenterAction,
14
+ } from "../lib/automation-center.js";
15
+ import {
16
+ createAutomationCenterRoutine,
17
+ editAutomationCenterRoutine,
18
+ runAutomationCenterRoutineAction,
19
+ } from "../lib/automation-center-routines.js";
7
20
  import { logger } from "../lib/logger.js";
8
21
  import { parseJsonOption } from "../lib/parse-json-option.js";
22
+ import { AutomationSchedulerBridge } from "../lib/scheduler-kernel/automation-adapter.js";
23
+ import { AutomationEventDispatcher } from "../lib/scheduler-kernel/automation-event-adapter.js";
24
+ import { openSchedulerStore } from "../lib/scheduler-kernel/store.js";
25
+ import {
26
+ RoutineSchedulerBridge,
27
+ routineBridgeRunId,
28
+ } from "../lib/scheduler-kernel/routine-adapter.js";
29
+ import { RoutineStore } from "../lib/routine-store.js";
30
+ import { defaultRunAgent } from "./routine.js";
9
31
  import { bootstrap, shutdown } from "../runtime/bootstrap.js";
10
32
  import {
11
33
  ensureAutomationTables,
@@ -68,8 +90,56 @@ import {
68
90
  } from "../lib/automation-engine.js";
69
91
 
70
92
  function _dbFromCtx(cmd) {
71
- const root = cmd?.parent?.parent ?? cmd?.parent;
72
- return root?._db;
93
+ let current = cmd;
94
+ while (current) {
95
+ if (current._db) return current._db;
96
+ current = current.parent;
97
+ }
98
+ return null;
99
+ }
100
+
101
+ async function readBoundedJsonStdin(options, maximum = 80 * 1024) {
102
+ if (options?.jsonStdin !== true) {
103
+ throw new Error("--json-stdin is required for routine definitions");
104
+ }
105
+ let serialized = "";
106
+ for await (const chunk of process.stdin) {
107
+ serialized += chunk;
108
+ if (Buffer.byteLength(serialized, "utf8") > maximum) {
109
+ throw new Error(`routine definition exceeds ${maximum} UTF-8 bytes`);
110
+ }
111
+ }
112
+ let parsed;
113
+ try {
114
+ parsed = JSON.parse(serialized);
115
+ } catch {
116
+ throw new Error("routine definition stdin must be valid JSON");
117
+ }
118
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
119
+ throw new Error("routine definition stdin must be a JSON object");
120
+ }
121
+ return parsed;
122
+ }
123
+
124
+ async function triggerCenterRoutine(store, routine) {
125
+ const schedulerStore = openSchedulerStore();
126
+ try {
127
+ const bridge = new RoutineSchedulerBridge({
128
+ routineStore: store,
129
+ schedulerStore,
130
+ runAgent: defaultRunAgent,
131
+ });
132
+ const fired = await bridge.trigger(routine);
133
+ const runId = routineBridgeRunId(fired);
134
+ if (!runId) {
135
+ throw new Error(
136
+ `routine occurrence did not execute: ${fired.result?.status || "unknown"}`,
137
+ );
138
+ }
139
+ return store.getRun(runId) || { runId, status: "unknown" };
140
+ } finally {
141
+ schedulerStore.close();
142
+ }
73
143
  }
74
144
 
75
145
  async function _prepare(cmd) {
@@ -84,6 +154,129 @@ async function _prepare(cmd) {
84
154
  return db;
85
155
  }
86
156
 
157
+ export async function runAutomationScheduled(db, options = {}, _deps = {}) {
158
+ const log = _deps.log || ((message) => console.log(message));
159
+ let schedulerStore = _deps.schedulerStore;
160
+ const ownsSchedulerStore = schedulerStore == null;
161
+ try {
162
+ schedulerStore ||= openSchedulerStore();
163
+ const bridge = new AutomationSchedulerBridge({
164
+ db,
165
+ schedulerStore,
166
+ now: _deps.now || Date.now,
167
+ ...(_deps.ownerId === undefined ? {} : { ownerId: _deps.ownerId }),
168
+ ...(options.leaseMs === undefined
169
+ ? {}
170
+ : { leaseMs: Number(options.leaseMs) }),
171
+ });
172
+ const results = await bridge.runDue({ signal: _deps.signal });
173
+ const summary = results.map((entry) => ({
174
+ flow: entry.flow,
175
+ occurrence: entry.occurrence,
176
+ status: entry.result?.status ?? "unknown",
177
+ recovered: entry.recovered === true,
178
+ deduplicated: entry.deduplicated === true,
179
+ executionId:
180
+ typeof entry.result?.result?.id === "string"
181
+ ? entry.result.result.id
182
+ : null,
183
+ error: entry.result?.error ?? null,
184
+ }));
185
+ if (options.json) {
186
+ log(
187
+ JSON.stringify({ due: summary.length, executions: summary }, null, 2),
188
+ );
189
+ } else if (summary.length === 0) {
190
+ log(chalk.gray("Nothing due."));
191
+ } else {
192
+ for (const entry of summary) {
193
+ const color = entry.status === "succeeded" ? chalk.green : chalk.red;
194
+ log(
195
+ color(
196
+ `${entry.flow} → ${entry.status}${entry.executionId ? ` (${entry.executionId})` : ""}`,
197
+ ),
198
+ );
199
+ }
200
+ }
201
+ return summary.some(
202
+ (entry) =>
203
+ entry.status !== "succeeded" &&
204
+ entry.status !== "idle" &&
205
+ entry.status !== "busy",
206
+ )
207
+ ? 1
208
+ : 0;
209
+ } finally {
210
+ if (ownsSchedulerStore) schedulerStore?.close();
211
+ }
212
+ }
213
+
214
+ export async function runAutomationChannelEvent(db, options = {}, _deps = {}) {
215
+ const log = _deps.log || ((message) => console.log(message));
216
+ let schedulerStore = _deps.schedulerStore;
217
+ const ownsSchedulerStore = schedulerStore == null;
218
+ try {
219
+ schedulerStore ||= openSchedulerStore();
220
+ const dispatcher = new AutomationEventDispatcher({
221
+ db,
222
+ schedulerStore,
223
+ now: _deps.now || Date.now,
224
+ ...(_deps.ownerId === undefined ? {} : { ownerId: _deps.ownerId }),
225
+ ...(options.leaseMs === undefined
226
+ ? {}
227
+ : { leaseMs: Number(options.leaseMs) }),
228
+ });
229
+ const result = await dispatcher.dispatch(
230
+ {
231
+ id: options.eventId,
232
+ type: "channel.event",
233
+ origin: options.origin,
234
+ sender: options.sender || null,
235
+ text: options.text,
236
+ meta: options.meta || {},
237
+ producedAt:
238
+ options.producedAt === undefined
239
+ ? Number((_deps.now || Date.now)())
240
+ : Number(options.producedAt),
241
+ },
242
+ { signal: _deps.signal },
243
+ );
244
+ const summary = {
245
+ eventId: result.eventId,
246
+ matched: result.matched,
247
+ rejected: result.rejected,
248
+ executions: result.results.map((entry) => ({
249
+ flowId: entry.flowId,
250
+ triggerId: entry.triggerId,
251
+ occurrenceId: entry.occurrenceId,
252
+ deduplicated: entry.deduplicated,
253
+ status: entry.result?.status || "unknown",
254
+ executionId: entry.result?.result?.id || null,
255
+ error: entry.result?.error || null,
256
+ })),
257
+ };
258
+ if (options.json) {
259
+ log(JSON.stringify(summary, null, 2));
260
+ } else if (summary.matched === 0) {
261
+ log(
262
+ chalk.gray(`No scoped automation trigger matched ${summary.eventId}.`),
263
+ );
264
+ } else {
265
+ for (const entry of summary.executions) {
266
+ const color = entry.status === "succeeded" ? chalk.green : chalk.red;
267
+ log(
268
+ color(
269
+ `${entry.triggerId} → ${entry.status}${entry.executionId ? ` (${entry.executionId})` : ""}`,
270
+ ),
271
+ );
272
+ }
273
+ }
274
+ return summary;
275
+ } finally {
276
+ if (ownsSchedulerStore) schedulerStore?.close();
277
+ }
278
+ }
279
+
87
280
  export function registerAutomationCommand(program) {
88
281
  const make = (name) => {
89
282
  const cmd = program
@@ -92,7 +285,12 @@ export function registerAutomationCommand(program) {
92
285
  "Workflow automation engine — 12 SaaS connectors + triggers (Phase 96)",
93
286
  )
94
287
  .hook("preAction", async (thisCommand, actionCommand) => {
95
- if (actionCommand && actionCommand.name().endsWith("-v2")) return;
288
+ if (
289
+ actionCommand &&
290
+ (actionCommand.name().endsWith("-v2") ||
291
+ actionCommand.name().startsWith("center-routine-"))
292
+ )
293
+ return;
96
294
  const db = await _prepare(thisCommand);
97
295
  thisCommand._db = db;
98
296
  });
@@ -241,6 +439,140 @@ function _wire(root) {
241
439
  }
242
440
  });
243
441
 
442
+ root
443
+ .command("center-projection")
444
+ .description("Emit the versioned Automation Center projection for IDEs")
445
+ .option("-l, --limit <n>", "Maximum flows", "100")
446
+ .option("--json", "Output as JSON")
447
+ .action(async (opts, cmd) => {
448
+ const db = _dbFromCtx(cmd);
449
+ try {
450
+ const projection = buildAutomationCenterProjection(db, {
451
+ limit: Number(opts.limit),
452
+ });
453
+ if (opts.json) console.log(JSON.stringify(projection, null, 2));
454
+ else {
455
+ logger.info(
456
+ `${projection.summary.total} items (${projection.summary.flows} flows, ${projection.summary.routines} routines), ${projection.summary.needsAttention} need attention`,
457
+ );
458
+ for (const flow of projection.items) {
459
+ logger.log(
460
+ ` ${chalk.cyan(flow.id)} ${flow.kind.padEnd(7)} ${flow.status.padEnd(8)} ${flow.name} preflight=${flow.security.state}`,
461
+ );
462
+ }
463
+ }
464
+ } catch (e) {
465
+ logger.error(e.message);
466
+ process.exitCode = 1;
467
+ } finally {
468
+ await shutdown();
469
+ }
470
+ });
471
+
472
+ root
473
+ .command("center-routine-create")
474
+ .description("Create a revision-gated Routine from JSON stdin")
475
+ .requiredOption(
476
+ "--expected-revision <revision>",
477
+ "Exact Routine catalog revision shown by center-projection",
478
+ )
479
+ .requiredOption("--json-stdin", "Read the Routine definition from stdin")
480
+ .option("--json", "Output as JSON")
481
+ .action(async (opts) => {
482
+ try {
483
+ const result = createAutomationCenterRoutine(new RoutineStore(), {
484
+ expectedRevision: opts.expectedRevision,
485
+ definition: await readBoundedJsonStdin(opts),
486
+ });
487
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
488
+ else logger.success(`Created routine ${result.routineId}`);
489
+ } catch (e) {
490
+ logger.error(e.message);
491
+ process.exitCode = 1;
492
+ } finally {
493
+ await shutdown();
494
+ }
495
+ });
496
+
497
+ root
498
+ .command("center-routine-edit <routineId>")
499
+ .description("Edit a revision-gated Routine from JSON stdin")
500
+ .requiredOption(
501
+ "--expected-revision <revision>",
502
+ "Exact Routine item revision shown by center-projection",
503
+ )
504
+ .requiredOption("--json-stdin", "Read the Routine definition from stdin")
505
+ .option("--json", "Output as JSON")
506
+ .action(async (routineId, opts) => {
507
+ try {
508
+ const result = editAutomationCenterRoutine(new RoutineStore(), {
509
+ routineId,
510
+ expectedRevision: opts.expectedRevision,
511
+ definition: await readBoundedJsonStdin(opts),
512
+ });
513
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
514
+ else logger.success(`Edited routine ${result.routineId}`);
515
+ } catch (e) {
516
+ logger.error(e.message);
517
+ process.exitCode = 1;
518
+ } finally {
519
+ await shutdown();
520
+ }
521
+ });
522
+
523
+ root
524
+ .command("center-routine-action <routineId> <action>")
525
+ .description("Run a revision-gated Routine action")
526
+ .requiredOption(
527
+ "--expected-revision <revision>",
528
+ "Exact Routine item revision shown by center-projection",
529
+ )
530
+ .option("--json", "Output as JSON")
531
+ .action(async (routineId, action, opts) => {
532
+ try {
533
+ const store = new RoutineStore();
534
+ const result = await runAutomationCenterRoutineAction(store, {
535
+ routineId,
536
+ action,
537
+ expectedRevision: opts.expectedRevision,
538
+ triggerRoutine: (routine) => triggerCenterRoutine(store, routine),
539
+ });
540
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
541
+ else logger.success(`${routineId} ${action} completed`);
542
+ } catch (e) {
543
+ logger.error(e.message);
544
+ process.exitCode = 1;
545
+ } finally {
546
+ await shutdown();
547
+ }
548
+ });
549
+
550
+ root
551
+ .command("center-action <flowId> <action>")
552
+ .description("Run a revision-gated Automation Center action")
553
+ .requiredOption(
554
+ "--expected-revision <revision>",
555
+ "Exact flow revision shown by center-projection",
556
+ )
557
+ .option("--json", "Output as JSON")
558
+ .action(async (flowId, action, opts, cmd) => {
559
+ const db = _dbFromCtx(cmd);
560
+ try {
561
+ const result = runAutomationCenterAction(db, {
562
+ flowId,
563
+ action,
564
+ expectedRevision: opts.expectedRevision,
565
+ });
566
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
567
+ else logger.success(`${flowId} ${action} completed`);
568
+ } catch (e) {
569
+ logger.error(e.message);
570
+ process.exitCode = 1;
571
+ } finally {
572
+ await shutdown();
573
+ }
574
+ });
575
+
244
576
  root
245
577
  .command("show <flowId>")
246
578
  .description("Show flow detail")
@@ -360,6 +692,94 @@ function _wire(root) {
360
692
  }
361
693
  });
362
694
 
695
+ root
696
+ .command("run-scheduled")
697
+ .description("Run due cron flows through the durable scheduler kernel")
698
+ .option("--json", "Output as JSON")
699
+ .option("--lease-ms <ms>", "Scheduler execution lease in milliseconds")
700
+ .action(async (opts, cmd) => {
701
+ const db = _dbFromCtx(cmd);
702
+ try {
703
+ const code = await runAutomationScheduled(db, opts);
704
+ if (code !== 0) process.exitCode = code;
705
+ } catch (e) {
706
+ logger.error(e.message);
707
+ process.exitCode = 1;
708
+ } finally {
709
+ await shutdown();
710
+ }
711
+ });
712
+
713
+ root
714
+ .command("set-execution-budget <flowId>")
715
+ .description("Set the durable unattended-execution budget for a flow")
716
+ .requiredOption("--window-ms <ms>", "Fixed budget window in milliseconds")
717
+ .requiredOption("--max-runs <n>", "Maximum executions per window")
718
+ .requiredOption(
719
+ "--max-action-steps <n>",
720
+ "Maximum connector action steps per window",
721
+ )
722
+ .option("--json", "Output as JSON")
723
+ .action(async (flowId, opts, cmd) => {
724
+ const db = _dbFromCtx(cmd);
725
+ try {
726
+ const budget = setAutomationExecutionBudget(db, flowId, {
727
+ windowMs: Number(opts.windowMs),
728
+ maxRuns: Number(opts.maxRuns),
729
+ maxActionSteps: Number(opts.maxActionSteps),
730
+ });
731
+ if (opts.json) console.log(JSON.stringify(budget, null, 2));
732
+ else {
733
+ logger.success(`Execution budget set for ${flowId}`);
734
+ logger.log(
735
+ ` revision=${budget.revision} window=${budget.windowMs}ms runs=${budget.maxRuns} actionSteps=${budget.maxActionSteps}`,
736
+ );
737
+ }
738
+ } catch (e) {
739
+ logger.error(e.message);
740
+ process.exitCode = 1;
741
+ } finally {
742
+ await shutdown();
743
+ }
744
+ });
745
+
746
+ root
747
+ .command("execution-preflight <flowId>")
748
+ .description(
749
+ "Check live RBAC connector permissions and durable budget availability",
750
+ )
751
+ .option("--json", "Output as JSON")
752
+ .action(async (flowId, opts, cmd) => {
753
+ const db = _dbFromCtx(cmd);
754
+ try {
755
+ const flow = getFlow(db, flowId);
756
+ if (!flow) throw new Error(`Flow not found: ${flowId}`);
757
+ const result = inspectAutomationExecutionAuthority(db, flow);
758
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
759
+ else {
760
+ const color = result.ready ? chalk.green : chalk.red;
761
+ logger.log(
762
+ color(`Execution preflight: ${result.ready ? "READY" : "DENIED"}`),
763
+ );
764
+ logger.log(` principal: ${result.snapshot.principalId}`);
765
+ for (const permission of result.permissions) {
766
+ logger.log(
767
+ ` ${permission.allowed ? "ALLOW" : "DENY "} ${permission.permission}`,
768
+ );
769
+ }
770
+ logger.log(
771
+ ` remaining: runs=${result.window.remainingRuns} actionSteps=${result.window.remainingActionSteps}`,
772
+ );
773
+ }
774
+ if (!result.ready) process.exitCode = 1;
775
+ } catch (e) {
776
+ logger.error(e.message);
777
+ process.exitCode = 1;
778
+ } finally {
779
+ await shutdown();
780
+ }
781
+ });
782
+
363
783
  root
364
784
  .command("share <flowId>")
365
785
  .description("Record flow share to another org")
@@ -530,6 +950,45 @@ function _wire(root) {
530
950
  }
531
951
  });
532
952
 
953
+ root
954
+ .command("dispatch-channel-event")
955
+ .description(
956
+ "Dispatch one durable, scope-checked webhook or Telegram event",
957
+ )
958
+ .requiredOption(
959
+ "--event-id <id>",
960
+ "Stable source event id for deduplication",
961
+ )
962
+ .requiredOption("--origin <origin>", "Event origin (webhook|telegram)")
963
+ .requiredOption("--text <text>", "Inbound event text")
964
+ .option("--sender <id>", "Inbound sender id")
965
+ .option("--meta <json>", "Bounded event metadata as JSON")
966
+ .option("--produced-at <ms>", "Trusted source event epoch milliseconds")
967
+ .option("--lease-ms <ms>", "Scheduler execution lease milliseconds")
968
+ .option("--json", "Output as JSON")
969
+ .action(async (opts, cmd) => {
970
+ const db = _dbFromCtx(cmd);
971
+ try {
972
+ const summary = await runAutomationChannelEvent(db, {
973
+ ...opts,
974
+ meta: parseJsonOption(opts.meta, "--meta") || {},
975
+ });
976
+ if (
977
+ summary.rejected.length > 0 ||
978
+ summary.executions.some(
979
+ (entry) => !["succeeded", "busy"].includes(entry.status),
980
+ )
981
+ ) {
982
+ process.exitCode = 1;
983
+ }
984
+ } catch (e) {
985
+ logger.error(e.message);
986
+ process.exitCode = 1;
987
+ } finally {
988
+ await shutdown();
989
+ }
990
+ });
991
+
533
992
  // ─── Execution ────────────────────────────────────────────
534
993
 
535
994
  root