chainlesschain 0.163.5 → 0.163.7

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 (236) 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-Cv2EE1Zy.js → AIOps-axqQfwv8.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-CcpxMLuW.js → ActionButton-DV-Ro0By.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-DE8mJoMI.js → Analytics-Bt72WEVu.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-DKn9dk9L.js → AppLayout-BtepG55E.js} +5 -5
  10. package/src/assets/web-panel/assets/{Artifacts-Cp3mG3Py.js → Artifacts-B7KaJ7S_.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-zvsQfF2C.js → Audit-FNFaUg7A.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-Bi85SsjN.js → BackgroundAgents-DFb6J2wQ.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-Cw47Dj6s.js → Backup-CC7YBhyj.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-C5Ip2afH.js → BaseInput-RkgTNVtj.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-C0iAFkJv.js → Chat-hHEmGZy-.js} +5 -5
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BV9Wv7wO.js → ChatBubbleRenderer-CAP9YhHv.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-3KZZCiAF.js → Checkbox-z5wXOQP1.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-C8OMbBAz.js → Codegen-DVaDJnxy.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-B-MB84LJ.js → Col-CJmaRDR5.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-DpR7Dxao.js → Community-DF29_2bI.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-CyDtz0OS.js → Compact-DtkQkBzn.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-5tiRa2An.js → Compliance-xoHDxp7I.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-DnQaKZ5x.js → Cowork-BUm-R7ht.js} +4 -4
  24. package/src/assets/web-panel/assets/{Cron-BaWfoEDf.js → Cron-Cgl14ZkB.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-Bu0HoHhb.js → Crosschain-BNcbmoqh.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-31zk8upX.js → DID-Bwja0Zo9.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-BmVne-e2.js → Dashboard-Bk5XjEdJ.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-B4PkoOWd.js → Dropdown-Ci36Oitq.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-BZ6tyFtI.js → EmailListRenderer--U04ZN4q.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Osozn4Ds.js → FamilyGuardDashboard-B2gNPR2s.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-DaH8TOAP.js → Federation-C4KghYn7.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-tbU0uwis.js → FormItemContext-QL7x_Q_0.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-Df8LQBrL.js → GenericCardRenderer-F3Ryltwm.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-DsMB6-14.js → Git-2HassK5k.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-BnH0dZzd.js → Governance-DoBIp7oe.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-DhnN1N_P.js → Inference--W01QhZ-.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-DnNshAgn.js → KnowledgeGraph-DmvwqGpT.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BmBFX3_N.js → Logs-BBJ4yAel.js} +2 -2
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-DCslfQU3.js → MarkdownRenderer-BLCZMzhJ.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-CPEgWDc3.js → Marketplace-D5lNngSI.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-C36qjgF9.js → McpTools-CkEVOMKH.js} +5 -5
  42. package/src/assets/web-panel/assets/{Memory-tNHvrgqN.js → Memory-B1Ow1xVE.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-Xaf6cKjc.js → MobileBridge-SgYbUfb6.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-D2E5TTeO.js → MobileProjects-BWdOvQAj.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-D4AJ2dwV.js → Mtc-DcTXSSma.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-Df-gtn2_.js → MtcAudit-Dt3WG4oj.js} +4 -4
  47. package/src/assets/web-panel/assets/{Multisig-DTCHLWld.js → Multisig-B9B38UZx.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-DruZ_1Jb.js → NLProgramming-B0S4GGvG.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-Dx3pdGDs.js → Notes-DZ5U58Ng.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-Bi3L69oL.js → NotificationSettings-PjL5ZW34.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-BHY33m7C.js → OrderTableRenderer-DrAX2ESt.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-BG2ZVEV-.js → Organization-Cv1GYs-c.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-Viwtg4Mn.js → Overflow-Bbkp_PL3.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-Clm_yc1Z.js → P2P-BD8QUgoZ.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-NKmmDiOw.js → PdhVaultBrowser-BO-2PeaB.js} +5 -5
  56. package/src/assets/web-panel/assets/{Permissions-DIL8u9Nk.js → Permissions-D_OPVa02.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-B-jM4wt-.js → PersonalDataHub-mh-pEmaB.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-627I__H5.js → Pipeline-6JC2EDdb.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-2DN2qrkW.js → Privacy-CUshzUAG.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-CN4wC0sF.js → ProjectInit-Cs2Aalri.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-K35vsh5H.js → ProjectSettings-1ak49tcj.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-B_wQadhX.js → Projects-BV4oApsm.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-MckaQUhQ.js → Providers-CXXxvafH.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-BS3mtA_O.js → QrScannerModal-PTcDsF58.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-BHp_nofL.js → QuickAsk-CDwucrJw.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-DlmnPuZj.js → Recommend-CJlLVRXR.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-tsyFbvpy.js → RemoteSession-DlN-ETsO.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-Ba1iWv2D.js → Reputation-DI23LR6m.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-NnlGC--f.js → Row-Cl7UQMrM.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-CjThSIRv.js → RssFeed-CexHNfh8.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-CojUyC5x.js → Search-BneBWvRN.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-c-sxeJEc.js → Security-DX7Phgrp.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-CtcKEEmF.js → Services-BWXO8Mte.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-CIBBXflN.js → Skeleton-DCyOjKPJ.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-D1XOOK9n.js → Skills-CXqERsTB.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-hRfEp5Sn.js → Sla-bhbrbz8E.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Dexb1f_6.js → SpeechSettings-3IPU0HBZ.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-BJsSFGYy.js → SyncSettings-DBNTQLbM.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-CfSZ8A1L.js → Tasks-quP0bkN-.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-idDf47W-.js → Templates-BouWmgho.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-C-UsX6w6.js → Tenant-qv0D9s7z.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-BgJtX5n1.js → Terminal-BxvbE8BB.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-DP0BLKTl.js → TimelineRenderer-D-IeLnJ2.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-LwCzqETp.js → Tokens-CFMKKyVG.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-D_9tVch0.js → Trigger-L8as3UuJ.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-BH2UlEWh.js → Trust-MxmA4VZG.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-CIpD0nYb.js → UkeySign-C0VvJYRi.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-9ShhYxK_.js → VideoEditing-Bf5QZj8N.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-C7afGNIG.js → Wallet-BIJ9riRz.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-bUKn6i5R.js → WebAuthn-B8p9Mhkw.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-CxgQaGyp.js → WorkflowEditor-BVsuaPen.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-DMVkmJwx.js → chat-BfkjoKpV.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-CDzQh5nS.js → colors-DeAlsWK8.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-C51kEF4D.js → compact-item-Cs97bz5j.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BTk9bXBn.js → createContext-BqbG60LH.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-CMje33rb.js → hasIn-i_RY0b1X.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DpcrFxMI.js → index-0-agYlWL.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BW4F5YDF.js → index-79V_zv1s.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DRG-aqOs.js → index-B-3tEpTc.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BCRvG9lq.js → index-B-JVTrpr.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DgivOKHA.js → index-B1CbOH1i.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BzCkNylB.js → index-B5UkyA3c.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-x53Vf7la.js → index-BESLFt4q.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CCLj9hcD.js → index-BOVkf1K3.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CypsfcEq.js → index-BYUVuwKt.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BtlEqHLP.js → index-BcUXAB2s.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Ld_4mFmC.js → index-BkBi5TmC.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DqU9njjT.js → index-Bm7lLR4c.js} +1 -1
  110. package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +1 -0
  111. package/src/assets/web-panel/assets/{index-CwGPwjUu.js → index-BuLQobzW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C5IWTMVl.js → index-CE2VtTlp.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DJfnCuMq.js → index-CED2PL_y.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-hg_emBsi.js → index-CG3PRyAL.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-togc_uYn.js → index-CIG8fQ2_.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C4rQCS8T.js → index-COdGA-8n.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-0Auljb10.js → index-CeWoNHau.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Cd0hmPxD.js → index-Cf3WCLmy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BXQBPLyE.js → index-CgsqdnRV.js} +1 -1
  120. package/src/assets/web-panel/assets/index-ChVT0p9u.js +1 -0
  121. package/src/assets/web-panel/assets/{index-h31MwrME.js → index-D2B4VPyP.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C1oZReaY.js → index-D9evtvg2.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CfC-piWZ.js → index-DUUrv141.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D9rC-fGW.js → index-DiSkx4UB.js} +3 -3
  125. package/src/assets/web-panel/assets/{index-CvSeVzqe.js → index-Dk2qouAz.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CtM8OT_7.js → index-Dk7-v4hP.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BBz9LIMS.js → index-DmbfhJUP.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BAfY7m7i.js → index-DqBgBpgp.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BVePe_Rf.js → index-PT_5sBMA.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CDLkNkLj.js → index-U4f5izHz.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BTlsE8Df.js → index-YXgLng0K.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BeEaAEIp.js → index-ZxO_-E8a.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-B0-zMtKA.js → index-dIHr4B_u.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-C1oZQmAA.js → index-gyFpvL7l.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DRLQyp2A.js → index-s1opAL-M.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-OS-vfbTA.js → index-yObj1O4n.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-vrA8K-ac.js → initDefaultProps-C9h9yKL3.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-ByEzwIAl.js → motion-BvmLsPdu.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-Ck4AWjw-.js → move-CoL4ekU1.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-6p0l9SrB.js → mtc-parser-C4pMy6xV.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-B1CUPQ6R.js → omit-DPuJfNxx.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-B1STg1iW.js → pickAttrs-D0KEeaK2.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-DTTllkBV.js → placementArrow-BpkyINxt.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-AIrrCfBb.js → responsiveObserve-DQI_wOq8.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-C_fPMjdR.js → slide-DYfdDiRg.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-D5ow2yIP.js → statusUtils-B5dtvb0D.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-Dy3hEA--.js → styleChecker-CF310mSj.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-WfzcfSh4.js → useFlexGapSupport-D1Qp4BLp.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-CKSejfas.js → useFs-C7_iyvJK.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-od3zpPBN.js → usePersonalDataHub-DZUt_fIm.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-DRD_2DFA.js → vnode-D3dsi0lZ.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-DmNrEtgf.js → zoom-WdbOlvdQ.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +5 -5
  155. package/src/command-manifest.json +1 -1
  156. package/src/commands/agenda.js +76 -4
  157. package/src/commands/agent.js +90 -1
  158. package/src/commands/automation.js +462 -3
  159. package/src/commands/background-session.js +102 -1
  160. package/src/commands/chat.js +11 -2
  161. package/src/commands/compact.js +106 -23
  162. package/src/commands/cowork.js +6 -1
  163. package/src/commands/loop.js +187 -38
  164. package/src/commands/routine.js +12 -5
  165. package/src/commands/scheduler-daemon.js +807 -0
  166. package/src/commands/session-observability.js +305 -0
  167. package/src/commands/session.js +38 -0
  168. package/src/data/changelog.json +28 -2
  169. package/src/gateways/ws/ws-agent-handler.js +439 -5
  170. package/src/harness/jsonl-session-store.js +463 -125
  171. package/src/harness/prompt-compressor.js +86 -13
  172. package/src/harness/provider-backed-compaction.js +100 -27
  173. package/src/lib/advisor-runtime.js +3 -1
  174. package/src/lib/agent-schedule-store.js +737 -47
  175. package/src/lib/agent-team/team-adjudication.js +40 -1
  176. package/src/lib/automation-center-routines.js +363 -0
  177. package/src/lib/automation-center.js +435 -0
  178. package/src/lib/automation-engine.js +568 -19
  179. package/src/lib/automation-execution-authority.js +736 -0
  180. package/src/lib/background-agent-supervisor.js +871 -215
  181. package/src/lib/background-command-argv.js +24 -0
  182. package/src/lib/background-interaction-journal.js +361 -52
  183. package/src/lib/background-interaction-resolver.js +32 -0
  184. package/src/lib/background-launch-profile.js +2 -0
  185. package/src/lib/background-needs-input-incident.js +221 -0
  186. package/src/lib/causal-observability.js +2661 -0
  187. package/src/lib/chat-session-admission.js +76 -0
  188. package/src/lib/cost-budget.js +5 -7
  189. package/src/lib/cowork/ab-comparator-cli.js +11 -0
  190. package/src/lib/cowork/debate-review-cli.js +12 -0
  191. package/src/lib/cowork-adapter.js +159 -38
  192. package/src/lib/cowork-cron.js +673 -19
  193. package/src/lib/cowork-observe.js +5 -1
  194. package/src/lib/cowork-task-runner.js +112 -17
  195. package/src/lib/delivery-coordinator.js +142 -0
  196. package/src/lib/direct-model-usage.js +121 -0
  197. package/src/lib/file-lines.js +183 -47
  198. package/src/lib/llm-pricing.js +12 -17
  199. package/src/lib/loop.js +62 -3
  200. package/src/lib/permission-engine.js +14 -7
  201. package/src/lib/public-ide-capability-manifest.js +2 -0
  202. package/src/lib/routine-store.js +546 -86
  203. package/src/lib/runtime-usage-ledger.js +182 -0
  204. package/src/lib/scheduler-kernel/agenda-adapter.js +604 -29
  205. package/src/lib/scheduler-kernel/authority-resolver.js +163 -0
  206. package/src/lib/scheduler-kernel/automation-adapter.js +994 -0
  207. package/src/lib/scheduler-kernel/automation-event-adapter.js +757 -0
  208. package/src/lib/scheduler-kernel/contract.js +11 -3
  209. package/src/lib/scheduler-kernel/cowork-cron-adapter.js +343 -15
  210. package/src/lib/scheduler-kernel/loop-adapter.js +913 -0
  211. package/src/lib/scheduler-kernel/migration-admin.js +346 -0
  212. package/src/lib/scheduler-kernel/migration-rollback-admin.js +932 -0
  213. package/src/lib/scheduler-kernel/routine-adapter.js +691 -22
  214. package/src/lib/scheduler-kernel/runtime.js +63 -1
  215. package/src/lib/scheduler-kernel/service.js +328 -0
  216. package/src/lib/scheduler-kernel/source-locator-path.js +142 -0
  217. package/src/lib/scheduler-kernel/store.js +3439 -206
  218. package/src/lib/session-host-lease.js +171 -2
  219. package/src/lib/session-host-snapshot.js +14 -3
  220. package/src/lib/session-persistence-failure.js +1 -0
  221. package/src/lib/session-tail.js +179 -34
  222. package/src/lib/session-transcript-structure.js +96 -0
  223. package/src/lib/sub-agent-context.js +328 -22
  224. package/src/lib/ws-agent-handler.js +4 -1
  225. package/src/repl/agent-repl.js +732 -91
  226. package/src/repl/chat-repl.js +5 -1
  227. package/src/runtime/agent-core.js +1274 -208
  228. package/src/runtime/coding-agent-contract-shared.cjs +5 -0
  229. package/src/runtime/fallback-model.js +5 -0
  230. package/src/runtime/headless-runner.js +324 -82
  231. package/src/runtime/headless-stream.js +232 -26
  232. package/src/runtime/policies/agent-policy.js +4 -0
  233. package/src/workers/background-agent-worker.js +468 -98
  234. package/src/assets/web-panel/assets/devWarning-CdwPwYj8.js +0 -1
  235. package/src/assets/web-panel/assets/index-BEeOdfEe.js +0 -1
  236. package/src/assets/web-panel/assets/index-BTe0TOu6.js +0 -1
@@ -1,5 +1,12 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { effectiveFireAt } from "../schedule-planner.js";
3
+ import {
4
+ agendaMigrationSourceDigest,
5
+ agendaMigrationSourceSnapshot,
6
+ nextCronTime,
7
+ normalizeTimeZone,
8
+ parseCron,
9
+ } from "../agent-schedule-store.js";
3
10
  import {
4
11
  SchedulerKernelError,
5
12
  canonicalJson,
@@ -8,10 +15,21 @@ import {
8
15
  normalizeJson,
9
16
  } from "./contract.js";
10
17
  import { SchedulerRuntime } from "./runtime.js";
18
+ import { schedulerMigrationSourceDigest } from "./store.js";
19
+ import {
20
+ bindSchedulerAuthorityPolicy,
21
+ createSchedulerAuthorityResolver,
22
+ } from "./authority-resolver.js";
23
+ import { canonicalSchedulerSourcePath } from "./source-locator-path.js";
11
24
 
12
25
  export const AGENDA_SCHEDULER_KIND = "agenda";
13
26
  export const AGENDA_SCHEDULER_CAPABILITY = "agent.execute";
14
- export const AGENDA_SCHEDULER_ENTRY_KINDS = Object.freeze(["wakeup", "cron"]);
27
+ export const AGENDA_SCHEDULER_MONITOR_CAPABILITY = "monitor.observe";
28
+ export const AGENDA_SCHEDULER_ENTRY_KINDS = Object.freeze([
29
+ "wakeup",
30
+ "cron",
31
+ "monitor",
32
+ ]);
15
33
  export const AGENDA_SCHEDULER_RETRY_DELAY_MS = 60_000;
16
34
 
17
35
  function agendaError(
@@ -45,6 +63,125 @@ function safeFailure(error) {
45
63
  };
46
64
  }
47
65
 
66
+ function capabilityForEntry(entry) {
67
+ return entry?.kind === "monitor"
68
+ ? AGENDA_SCHEDULER_MONITOR_CAPABILITY
69
+ : AGENDA_SCHEDULER_CAPABILITY;
70
+ }
71
+
72
+ function normalizeMonitorObservation(entry, observation) {
73
+ if (
74
+ !observation ||
75
+ typeof observation !== "object" ||
76
+ Array.isArray(observation) ||
77
+ typeof observation.matched !== "boolean"
78
+ ) {
79
+ throw agendaError(
80
+ "AGENDA_SCHEDULER_MONITOR_RESULT_INVALID",
81
+ `Agenda monitor returned an invalid observation: ${entry.id}`,
82
+ );
83
+ }
84
+ const mtimeMs =
85
+ observation.mtimeMs === null || observation.mtimeMs === undefined
86
+ ? null
87
+ : Number(observation.mtimeMs);
88
+ if (mtimeMs !== null && (!Number.isFinite(mtimeMs) || mtimeMs < 0)) {
89
+ throw agendaError(
90
+ "AGENDA_SCHEDULER_MONITOR_RESULT_INVALID",
91
+ `Agenda monitor returned an invalid mtime: ${entry.id}`,
92
+ );
93
+ }
94
+ const eventId = observation.matched
95
+ ? normalizeIdentifier(observation.eventId, "agenda.monitor.eventId")
96
+ : null;
97
+ const authority = observation.matched
98
+ ? normalizeIdentifier(observation.authority, "agenda.monitor.authority")
99
+ : null;
100
+ const duplicate = observation.matched && entry.lastEventId === eventId;
101
+ const notification =
102
+ observation.notification &&
103
+ typeof observation.notification === "object" &&
104
+ !Array.isArray(observation.notification)
105
+ ? {
106
+ title: String(observation.notification.title || ""),
107
+ body: String(observation.notification.body || ""),
108
+ level: String(observation.notification.level || "success"),
109
+ }
110
+ : null;
111
+ return {
112
+ matched: observation.matched,
113
+ mtimeMs,
114
+ eventId,
115
+ authority,
116
+ duplicate,
117
+ truncated: observation.truncated === true,
118
+ notification,
119
+ };
120
+ }
121
+
122
+ function validateMonitorSnapshot(snapshot) {
123
+ if (
124
+ !Number.isSafeInteger(snapshot.intervalMs) ||
125
+ snapshot.intervalMs < 1_000
126
+ ) {
127
+ throw agendaError(
128
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
129
+ `Agenda monitor interval is invalid: ${snapshot.id}`,
130
+ );
131
+ }
132
+ if (!Number.isSafeInteger(snapshot.checks) || snapshot.checks < 0) {
133
+ throw agendaError(
134
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
135
+ `Agenda monitor check count is invalid: ${snapshot.id}`,
136
+ );
137
+ }
138
+ if (!["command", "file", "http"].includes(snapshot.source)) {
139
+ throw agendaError(
140
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
141
+ `Agenda monitor source is invalid: ${snapshot.id}`,
142
+ );
143
+ }
144
+ const sourceValue =
145
+ snapshot.source === "command"
146
+ ? snapshot.command
147
+ : snapshot.source === "file"
148
+ ? snapshot.watchFile
149
+ : snapshot.watchUrl;
150
+ if (typeof sourceValue !== "string" || sourceValue.length === 0) {
151
+ throw agendaError(
152
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
153
+ `Agenda monitor source target is missing: ${snapshot.id}`,
154
+ );
155
+ }
156
+ if (
157
+ snapshot.watchChange === true &&
158
+ (snapshot.source !== "file" || snapshot.stopWhen != null)
159
+ ) {
160
+ throw agendaError(
161
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
162
+ `Agenda monitor change condition is invalid: ${snapshot.id}`,
163
+ );
164
+ }
165
+ if (snapshot.stopWhen != null) {
166
+ if (typeof snapshot.stopWhen !== "string") {
167
+ throw agendaError(
168
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
169
+ `Agenda monitor stop condition is invalid: ${snapshot.id}`,
170
+ );
171
+ }
172
+ try {
173
+ new RegExp(snapshot.stopWhen);
174
+ } catch (error) {
175
+ throw agendaError(
176
+ "AGENDA_SCHEDULER_ENTRY_INVALID",
177
+ `Agenda monitor stop condition is invalid: ${snapshot.id}`,
178
+ undefined,
179
+ error,
180
+ );
181
+ }
182
+ }
183
+ }
184
+
48
185
  function validateEntrySnapshot(entry) {
49
186
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
50
187
  throw agendaError(
@@ -55,6 +192,7 @@ function validateEntrySnapshot(entry) {
55
192
  const plain = { ...entry };
56
193
  delete plain.executionLease;
57
194
  delete plain.schedulerExecution;
195
+ delete plain.schedulerMigration;
58
196
  const snapshot = normalizeJson(plain, "agendaEntrySnapshot");
59
197
  snapshot.id = normalizeIdentifier(snapshot.id, "agenda.entry.id");
60
198
  if (!AGENDA_SCHEDULER_ENTRY_KINDS.includes(snapshot.kind)) {
@@ -63,21 +201,48 @@ function validateEntrySnapshot(entry) {
63
201
  `Agenda scheduler does not support entry kind: ${snapshot.kind}`,
64
202
  );
65
203
  }
66
- if (typeof snapshot.prompt !== "string" || snapshot.prompt.length === 0) {
204
+ if (
205
+ snapshot.kind !== "monitor" &&
206
+ (typeof snapshot.prompt !== "string" || snapshot.prompt.length === 0)
207
+ ) {
67
208
  throw agendaError(
68
209
  "AGENDA_SCHEDULER_ENTRY_INVALID",
69
210
  `Agenda prompt is missing: ${snapshot.id}`,
70
211
  );
71
212
  }
213
+ if (snapshot.kind === "cron") {
214
+ try {
215
+ parseCron(snapshot.cron);
216
+ if (snapshot.timeZone != null) {
217
+ const timeZone = normalizeTimeZone(snapshot.timeZone);
218
+ if (!timeZone) throw new Error("cron timeZone must not be empty");
219
+ const nextAt = Number(snapshot.nextAt);
220
+ if (
221
+ nextCronTime(snapshot.cron, nextAt - 60_000, { timeZone }) !== nextAt
222
+ ) {
223
+ throw new Error("cron nextAt does not match its time zone");
224
+ }
225
+ }
226
+ } catch (error) {
227
+ throw agendaError(
228
+ "AGENDA_SCHEDULER_CRON_INVALID",
229
+ `Agenda cron definition is invalid: ${snapshot.id}`,
230
+ undefined,
231
+ error,
232
+ );
233
+ }
234
+ }
72
235
  const schedulable =
73
236
  (snapshot.kind === "wakeup" && snapshot.status === "pending") ||
74
- (snapshot.kind === "cron" && snapshot.status === "active");
237
+ (["cron", "monitor"].includes(snapshot.kind) &&
238
+ snapshot.status === "active");
75
239
  if (!schedulable) {
76
240
  throw agendaError(
77
241
  "AGENDA_SCHEDULER_ENTRY_TERMINAL",
78
242
  `Agenda entry is not schedulable: ${snapshot.id}`,
79
243
  );
80
244
  }
245
+ if (snapshot.kind === "monitor") validateMonitorSnapshot(snapshot);
81
246
  normalizeEpochMs(Number(snapshot.createdAt), "agenda.entry.createdAt");
82
247
  const fireAt = effectiveFireAt(snapshot);
83
248
  if (fireAt == null) {
@@ -117,7 +282,14 @@ export function buildAgendaSchedulerJob(entry) {
117
282
  source: "agent-schedule-store",
118
283
  scheduleKind: snapshot.kind,
119
284
  expression:
120
- snapshot.kind === "cron" ? snapshot.cron : String(snapshot.dueAt),
285
+ snapshot.kind === "cron"
286
+ ? snapshot.cron
287
+ : snapshot.kind === "monitor"
288
+ ? `every:${snapshot.intervalMs}`
289
+ : String(snapshot.dueAt),
290
+ ...(snapshot.kind === "cron" && snapshot.timeZone
291
+ ? { timeZone: snapshot.timeZone }
292
+ : {}),
121
293
  },
122
294
  payload: {
123
295
  entry: snapshot,
@@ -128,7 +300,7 @@ export function buildAgendaSchedulerJob(entry) {
128
300
  principal: { type: "agenda", id: snapshot.id },
129
301
  tenantId: null,
130
302
  workspaceId: null,
131
- requestedCapabilities: [AGENDA_SCHEDULER_CAPABILITY],
303
+ requestedCapabilities: [capabilityForEntry(snapshot)],
132
304
  authorizationRefs: {
133
305
  decisionId: null,
134
306
  policyRevision: null,
@@ -163,6 +335,10 @@ function sameJob(current, desired) {
163
335
 
164
336
  export function syncAgendaSchedulerJob(schedulerStore, entry) {
165
337
  const desired = buildAgendaSchedulerJob(entry);
338
+ desired.authority = bindSchedulerAuthorityPolicy(
339
+ schedulerStore,
340
+ desired.authority,
341
+ );
166
342
  let current = schedulerStore.getJob(desired.id);
167
343
  if (!current) {
168
344
  try {
@@ -190,6 +366,223 @@ export function syncAgendaSchedulerJob(schedulerStore, entry) {
190
366
  }
191
367
  }
192
368
 
369
+ export function migrateAgendaSchedulerEntry({
370
+ agendaStore,
371
+ schedulerStore,
372
+ entry,
373
+ }) {
374
+ const snapshot = agendaEntrySnapshot(entry);
375
+ const directory = canonicalSchedulerSourcePath(agendaStore.dir);
376
+ const sourceScope = { store: "agent-schedule", directory };
377
+ const sourceLocator = {
378
+ schemaVersion: 1,
379
+ type: "agenda-store",
380
+ directory,
381
+ };
382
+ let prepared = schedulerStore.getActiveDomainMigrationBySource({
383
+ domain: "agenda",
384
+ sourceScope,
385
+ sourceId: snapshot.id,
386
+ });
387
+ if (!prepared && agendaStore.dir !== directory) {
388
+ const ambiguousLegacy = schedulerStore.getActiveDomainMigrationBySource({
389
+ domain: "agenda",
390
+ sourceScope: {
391
+ store: "agent-schedule",
392
+ directory: agendaStore.dir,
393
+ },
394
+ sourceId: snapshot.id,
395
+ });
396
+ if (ambiguousLegacy) {
397
+ throw agendaError(
398
+ "AGENDA_SCHEDULER_MIGRATION_SOURCE_UNBOUND",
399
+ `Legacy Agenda migration uses a non-canonical path scope and cannot be bound safely: ${snapshot.id}`,
400
+ );
401
+ }
402
+ }
403
+ const desired = buildAgendaSchedulerJob(snapshot);
404
+ desired.authority = bindSchedulerAuthorityPolicy(
405
+ schedulerStore,
406
+ desired.authority,
407
+ );
408
+ const sourceDigest = agendaMigrationSourceDigest(entry);
409
+ const sourceSnapshot = agendaMigrationSourceSnapshot(entry);
410
+ if (prepared) {
411
+ const migrationEntries = prepared.entries.filter(
412
+ (candidate) =>
413
+ candidate.domain === "agenda" && candidate.sourceId === snapshot.id,
414
+ );
415
+ if (prepared.entries.length !== 1 || migrationEntries.length !== 1) {
416
+ throw agendaError(
417
+ "AGENDA_SCHEDULER_MIGRATION_DOMAIN_MISMATCH",
418
+ "Agenda entry belongs to an invalid mixed-domain migration",
419
+ );
420
+ }
421
+ const [migrationEntry] = migrationEntries;
422
+ schedulerStore.bindDomainMigrationSourceLocator({
423
+ migrationId: prepared.id,
424
+ entryId: migrationEntry.entryId,
425
+ sourceLocator,
426
+ expectedSourceDigest: migrationEntry.sourceDigest,
427
+ expectedTargetJobId: migrationEntry.targetJobId,
428
+ });
429
+ prepared = schedulerStore.getDomainMigration(prepared.id);
430
+ } else {
431
+ prepared = schedulerStore.prepareDomainMigration({
432
+ entries: [
433
+ {
434
+ domain: "agenda",
435
+ sourceId: snapshot.id,
436
+ sourceScope,
437
+ sourceLocator,
438
+ source: sourceSnapshot,
439
+ targetJob: desired,
440
+ rollbackStrategy: "disable",
441
+ },
442
+ ],
443
+ });
444
+ }
445
+ if (
446
+ prepared.entries.length !== 1 ||
447
+ prepared.entries[0].domain !== "agenda"
448
+ ) {
449
+ throw agendaError(
450
+ "AGENDA_SCHEDULER_MIGRATION_DOMAIN_MISMATCH",
451
+ "Agenda entry belongs to an invalid mixed-domain migration",
452
+ );
453
+ }
454
+ const expectedJournalDigest = schedulerMigrationSourceDigest(sourceSnapshot);
455
+ if (
456
+ prepared.entries[0].sourceDigest !== expectedJournalDigest ||
457
+ prepared.entries[0].targetJobId !== desired.id
458
+ ) {
459
+ throw agendaError(
460
+ "AGENDA_SCHEDULER_MIGRATION_SOURCE_CHANGED",
461
+ `Agenda entry changed after its scheduler migration started: ${snapshot.id}`,
462
+ );
463
+ }
464
+ let migration = prepared;
465
+ if (migration.state === "rolled_back" || migration.state === "rolling_back") {
466
+ throw agendaError(
467
+ "AGENDA_SCHEDULER_MIGRATION_STATE_CONFLICT",
468
+ `Agenda migration cannot resume from state ${migration.state}`,
469
+ );
470
+ }
471
+ if (migration.state === "retired") {
472
+ const migrationEntry = migration.entries[0];
473
+ agendaStore.retireForSchedulerMigration(snapshot.id, {
474
+ migrationId: migration.id,
475
+ sourceDigest,
476
+ targetJobId: desired.id,
477
+ retirementToken: migrationEntry.retirementToken,
478
+ });
479
+ return migration;
480
+ }
481
+ if (migration.state === "prepared") {
482
+ agendaStore.prepareSchedulerMigration(snapshot.id, {
483
+ migrationId: migration.id,
484
+ sourceDigest,
485
+ targetJobId: desired.id,
486
+ });
487
+ migration = schedulerStore.applyDomainMigration(migration.id);
488
+ }
489
+ if (migration.state === "applied") {
490
+ migration = schedulerStore.verifyDomainMigration(migration.id, {
491
+ sources: [
492
+ {
493
+ entryId: migration.entries[0].entryId,
494
+ source: agendaMigrationSourceSnapshot(entry),
495
+ },
496
+ ],
497
+ });
498
+ }
499
+ const retiring =
500
+ migration.state === "retiring"
501
+ ? migration
502
+ : schedulerStore.beginDomainMigrationRetirement(migration.id);
503
+ const retiringEntry = retiring.entries[0];
504
+ const retired = agendaStore.retireForSchedulerMigration(snapshot.id, {
505
+ migrationId: prepared.id,
506
+ sourceDigest,
507
+ targetJobId: desired.id,
508
+ retirementToken: retiringEntry.retirementToken,
509
+ });
510
+ schedulerStore.confirmDomainMigrationEntryRetired({
511
+ migrationId: prepared.id,
512
+ entryId: retiringEntry.entryId,
513
+ retirementToken: retiringEntry.retirementToken,
514
+ source: retired,
515
+ });
516
+ return schedulerStore.getDomainMigration(prepared.id);
517
+ }
518
+
519
+ export function rollbackAgendaSchedulerMigration({
520
+ agendaStore,
521
+ schedulerStore,
522
+ migrationId,
523
+ }) {
524
+ const directory = canonicalSchedulerSourcePath(agendaStore.dir);
525
+ const assertRollbackBinding = (migration) => {
526
+ if (
527
+ migration?.id !== migrationId ||
528
+ !Array.isArray(migration.entries) ||
529
+ migration.entries.length === 0 ||
530
+ migration.entries.some((entry) => entry.domain !== "agenda")
531
+ ) {
532
+ throw agendaError(
533
+ "AGENDA_SCHEDULER_MIGRATION_DOMAIN_MISMATCH",
534
+ "Agenda rollback cannot operate on a mixed-domain migration",
535
+ );
536
+ }
537
+ for (const entry of migration.entries) {
538
+ const locator = entry.sourceLocator;
539
+ if (
540
+ locator?.schemaVersion !== 1 ||
541
+ locator.type !== "agenda-store" ||
542
+ locator.directory !== directory
543
+ ) {
544
+ throw agendaError(
545
+ "AGENDA_SCHEDULER_MIGRATION_SOURCE_UNBOUND",
546
+ `Agenda rollback source locator does not match its store: ${entry.sourceId}`,
547
+ );
548
+ }
549
+ }
550
+ return migration;
551
+ };
552
+ const current = assertRollbackBinding(
553
+ schedulerStore.getDomainMigration(migrationId),
554
+ );
555
+ if (current.state === "rolled_back") {
556
+ return { ...current, deduplicated: true };
557
+ }
558
+ const migration = assertRollbackBinding(
559
+ schedulerStore.beginDomainMigrationRollback(migrationId),
560
+ );
561
+ if (migration.state === "rolled_back") return migration;
562
+ const entries = migration.entries;
563
+ for (const entry of entries) {
564
+ if (entry.state === "rolled_back") continue;
565
+ schedulerStore.restoreDomainMigrationEntrySource({
566
+ migrationId,
567
+ entryId: entry.entryId,
568
+ retirementToken: entry.retirementToken,
569
+ restoreSource: () => {
570
+ const current = agendaStore.get(entry.sourceId);
571
+ const restored = current.schedulerMigration
572
+ ? agendaStore.restoreFromSchedulerMigration(entry.sourceId, {
573
+ migrationId,
574
+ sourceDigest: agendaMigrationSourceDigest(current),
575
+ targetJobId: entry.targetJobId,
576
+ retirementToken: entry.retirementToken,
577
+ })
578
+ : current;
579
+ return agendaMigrationSourceSnapshot(restored);
580
+ },
581
+ });
582
+ }
583
+ return schedulerStore.getDomainMigration(migrationId);
584
+ }
585
+
193
586
  export function enqueueAgendaEntry(schedulerStore, entry) {
194
587
  const snapshot = agendaEntrySnapshot(entry);
195
588
  const job = syncAgendaSchedulerJob(schedulerStore, snapshot);
@@ -216,7 +609,7 @@ export function authorizeAgendaOccurrence({ job, occurrence }) {
216
609
  authority?.principal?.id === entry.id &&
217
610
  Array.isArray(authority?.requestedCapabilities) &&
218
611
  authority.requestedCapabilities.length === 1 &&
219
- authority.requestedCapabilities[0] === AGENDA_SCHEDULER_CAPABILITY &&
612
+ authority.requestedCapabilities[0] === capabilityForEntry(entry) &&
220
613
  payload.snapshotDigest === expectedDigest;
221
614
  return {
222
615
  allowed,
@@ -231,7 +624,11 @@ function recoveredResult(entry, evidence) {
231
624
  const result =
232
625
  evidence.result && typeof evidence.result === "object"
233
626
  ? evidence.result
234
- : { id: entry.id, kind: entry.kind, action: "fired" };
627
+ : {
628
+ id: entry.id,
629
+ kind: entry.kind,
630
+ action: entry.kind === "monitor" ? "checked" : "fired",
631
+ };
235
632
  return { ...result, recovered: true };
236
633
  }
237
634
 
@@ -251,6 +648,8 @@ function matchingExecutionEvidence(
251
648
  export function createAgendaSchedulerAdapter({
252
649
  agendaStore,
253
650
  runAgent,
651
+ runMonitor,
652
+ notifyMonitor,
254
653
  now = Date.now,
255
654
  } = {}) {
256
655
  if (
@@ -278,6 +677,40 @@ export function createAgendaSchedulerAdapter({
278
677
  }
279
678
  return {
280
679
  kind: AGENDA_SCHEDULER_KIND,
680
+ async adjudicate({ occurrence, adjudication }) {
681
+ const payload = occurrence.payload;
682
+ const expected = agendaEntrySnapshot(payload?.entry);
683
+ const expectedDigest = agendaEntrySnapshotDigest(expected);
684
+ if (
685
+ payload.snapshotDigest !== expectedDigest ||
686
+ typeof agendaStore.adjudicateSchedulerExecution !== "function"
687
+ ) {
688
+ throw agendaError(
689
+ "AGENDA_SCHEDULER_ADJUDICATION_UNSUPPORTED",
690
+ `Agenda adjudication cannot be applied: ${occurrence.id}`,
691
+ );
692
+ }
693
+ agendaStore.adjudicateSchedulerExecution(expected.id, {
694
+ occurrenceId: occurrence.id,
695
+ snapshotDigest: expectedDigest,
696
+ attempt: adjudication.expectedAttempt,
697
+ decision: adjudication.decision,
698
+ requestId: adjudication.requestId,
699
+ atMs: now(),
700
+ });
701
+ if (adjudication.decision === "confirmed_applied") {
702
+ return {
703
+ settled: true,
704
+ result: {
705
+ id: expected.id,
706
+ kind: expected.kind,
707
+ action: "adjudicated-applied",
708
+ adjudicationRequestId: adjudication.requestId,
709
+ },
710
+ };
711
+ }
712
+ return { continue: true };
713
+ },
281
714
  async execute({ occurrence }) {
282
715
  const payload = occurrence.payload;
283
716
  const expected = agendaEntrySnapshot(payload?.entry);
@@ -309,7 +742,7 @@ export function createAgendaSchedulerAdapter({
309
742
  if (prior.status === "running") {
310
743
  throw agendaError(
311
744
  "AGENDA_SCHEDULER_OUTCOME_UNKNOWN",
312
- `Agenda agent outcome is unknown; refusing duplicate execution: ${expected.id}`,
745
+ `Agenda execution outcome is unknown; refusing duplicate execution: ${expected.id}`,
313
746
  );
314
747
  }
315
748
  } else if (prior?.status === "running") {
@@ -347,14 +780,54 @@ export function createAgendaSchedulerAdapter({
347
780
  throw stale;
348
781
  }
349
782
 
783
+ let observation = null;
784
+ let action = null;
350
785
  try {
351
- await runAgent({
352
- entry: expected,
353
- prompt: expected.prompt,
354
- runPolicy: expected.runPolicy,
355
- occurrenceId: occurrence.id,
356
- attempt: occurrence.attempt,
357
- });
786
+ if (expected.kind === "monitor") {
787
+ if (typeof runMonitor !== "function") {
788
+ throw agendaError(
789
+ "AGENDA_SCHEDULER_MONITOR_RUNNER_REQUIRED",
790
+ "Agenda scheduler monitor execution requires a monitor runner",
791
+ );
792
+ }
793
+ observation = normalizeMonitorObservation(
794
+ expected,
795
+ await runMonitor({
796
+ entry: expected,
797
+ occurrenceId: occurrence.id,
798
+ attempt: occurrence.attempt,
799
+ }),
800
+ );
801
+ action = {
802
+ id: expected.id,
803
+ kind: expected.kind,
804
+ action: observation.matched
805
+ ? observation.duplicate
806
+ ? "duplicate"
807
+ : "matched"
808
+ : "checked",
809
+ ...(observation.matched
810
+ ? {
811
+ event_id: observation.eventId,
812
+ authority: observation.authority,
813
+ }
814
+ : {}),
815
+ ...(observation.truncated ? { truncated: true } : {}),
816
+ };
817
+ } else {
818
+ await runAgent({
819
+ entry: expected,
820
+ prompt: expected.prompt,
821
+ runPolicy: expected.runPolicy,
822
+ occurrenceId: occurrence.id,
823
+ attempt: occurrence.attempt,
824
+ });
825
+ action = {
826
+ id: expected.id,
827
+ kind: expected.kind,
828
+ action: "fired",
829
+ };
830
+ }
358
831
  } catch (error) {
359
832
  let failurePersisted = false;
360
833
  try {
@@ -389,14 +862,16 @@ export function createAgendaSchedulerAdapter({
389
862
  throw agendaError(
390
863
  typeof error?.code === "string"
391
864
  ? error.code
392
- : "AGENDA_AGENT_EXECUTION_FAILED",
865
+ : expected.kind === "monitor"
866
+ ? "AGENDA_MONITOR_EXECUTION_FAILED"
867
+ : "AGENDA_AGENT_EXECUTION_FAILED",
393
868
  typeof error?.message === "string"
394
869
  ? error.message
395
- : `Agenda agent execution failed: ${expected.id}`,
870
+ : `Agenda ${expected.kind === "monitor" ? "monitor" : "agent"} execution failed: ${expected.id}`,
396
871
  undefined,
397
872
  error,
398
873
  {
399
- retryable: true,
874
+ retryable: error?.retryable !== false,
400
875
  retryAt: normalizeEpochMs(
401
876
  Number(now()) + AGENDA_SCHEDULER_RETRY_DELAY_MS,
402
877
  "agenda.retryAt",
@@ -405,21 +880,50 @@ export function createAgendaSchedulerAdapter({
405
880
  );
406
881
  }
407
882
 
408
- const action = {
409
- id: expected.id,
410
- kind: expected.kind,
411
- action: "fired",
412
- };
413
883
  try {
414
- agendaStore.completeSchedulerExecution(expected.id, {
884
+ const completed = agendaStore.completeSchedulerExecution(expected.id, {
415
885
  occurrenceId: occurrence.id,
416
886
  snapshotDigest: expectedDigest,
417
887
  attempt: occurrence.attempt,
418
888
  outcome: "succeeded",
419
889
  result: action,
890
+ ...(observation
891
+ ? {
892
+ monitorCheck: {
893
+ matched: observation.matched,
894
+ mtimeMs: observation.mtimeMs,
895
+ eventId: observation.eventId,
896
+ authority: observation.authority,
897
+ },
898
+ }
899
+ : {}),
420
900
  atMs: now(),
421
901
  });
422
- return action;
902
+ const persistedAction = completed.schedulerExecution?.result || action;
903
+ if (
904
+ observation?.matched &&
905
+ !observation.duplicate &&
906
+ observation.notification
907
+ ) {
908
+ if (typeof notifyMonitor !== "function") {
909
+ return {
910
+ ...persistedAction,
911
+ notifyError: "Agenda monitor notification handler is unavailable",
912
+ };
913
+ }
914
+ try {
915
+ await notifyMonitor(observation.notification);
916
+ } catch (notifyError) {
917
+ return {
918
+ ...persistedAction,
919
+ notifyError:
920
+ typeof notifyError?.message === "string"
921
+ ? notifyError.message
922
+ : String(notifyError),
923
+ };
924
+ }
925
+ }
926
+ return persistedAction;
423
927
  } catch (completionError) {
424
928
  const persisted = agendaStore.get(expected.id);
425
929
  if (
@@ -454,10 +958,14 @@ export class AgendaSchedulerBridge {
454
958
  agendaStore,
455
959
  schedulerStore,
456
960
  runAgent,
961
+ runMonitor,
962
+ notifyMonitor,
457
963
  now = Date.now,
458
964
  ownerId,
459
965
  leaseMs,
460
966
  renewIntervalMs,
967
+ authorityResolver,
968
+ migrateLegacy = true,
461
969
  } = {}) {
462
970
  if (typeof now !== "function") {
463
971
  throw agendaError(
@@ -468,10 +976,29 @@ export class AgendaSchedulerBridge {
468
976
  this.agendaStore = agendaStore;
469
977
  this.schedulerStore = schedulerStore;
470
978
  this.now = now;
979
+ this.migrateLegacy = migrateLegacy !== false;
471
980
  this.runtime = new SchedulerRuntime({
472
981
  store: schedulerStore,
473
- adapters: [createAgendaSchedulerAdapter({ agendaStore, runAgent, now })],
474
- authorize: authorizeAgendaOccurrence,
982
+ adapters: [
983
+ createAgendaSchedulerAdapter({
984
+ agendaStore,
985
+ runAgent,
986
+ runMonitor,
987
+ notifyMonitor,
988
+ now,
989
+ }),
990
+ ],
991
+ authorize:
992
+ authorityResolver ||
993
+ createSchedulerAuthorityResolver({
994
+ store: schedulerStore,
995
+ validate: authorizeAgendaOccurrence,
996
+ units: ({ occurrence }) =>
997
+ Math.max(
998
+ 1,
999
+ Number(occurrence?.payload?.entry?.runPolicy?.maxTurns) || 1,
1000
+ ),
1001
+ }),
475
1002
  ...(ownerId === undefined ? {} : { ownerId }),
476
1003
  ...(leaseMs === undefined ? {} : { leaseMs }),
477
1004
  ...(renewIntervalMs === undefined ? {} : { renewIntervalMs }),
@@ -481,6 +1008,31 @@ export class AgendaSchedulerBridge {
481
1008
  async runDue({ signal } = {}) {
482
1009
  const results = [];
483
1010
  const observedOccurrences = new Set();
1011
+ if (!signal?.aborted && this.migrateLegacy) {
1012
+ const sourceScope = {
1013
+ store: "agent-schedule",
1014
+ directory: canonicalSchedulerSourcePath(this.agendaStore.dir),
1015
+ };
1016
+ const candidates = this.agendaStore.list().filter((entry) => {
1017
+ const activeMigration =
1018
+ this.schedulerStore.getActiveDomainMigrationBySource({
1019
+ domain: "agenda",
1020
+ sourceScope,
1021
+ sourceId: entry.id,
1022
+ });
1023
+ if (entry.schedulerMigration) {
1024
+ return activeMigration?.state !== "retired";
1025
+ }
1026
+ return activeMigration?.state !== undefined;
1027
+ });
1028
+ for (const entry of candidates) {
1029
+ migrateAgendaSchedulerEntry({
1030
+ agendaStore: this.agendaStore,
1031
+ schedulerStore: this.schedulerStore,
1032
+ entry,
1033
+ });
1034
+ }
1035
+ }
484
1036
  const recovered = await this.runtime.runUntilIdle({
485
1037
  limit: 10_000,
486
1038
  signal,
@@ -503,8 +1055,9 @@ export class AgendaSchedulerBridge {
503
1055
 
504
1056
  const now = normalizeEpochMs(Number(this.now()), "now");
505
1057
  const due = [
506
- ...this.agendaStore.due("wakeup", now),
507
- ...this.agendaStore.due("cron", now),
1058
+ ...this.agendaStore.due("wakeup", now, { schedulerMigration: true }),
1059
+ ...this.agendaStore.due("cron", now, { schedulerMigration: true }),
1060
+ ...this.agendaStore.due("monitor", now, { schedulerMigration: true }),
508
1061
  ];
509
1062
  for (const entry of due) {
510
1063
  const occurrence = enqueueAgendaEntry(this.schedulerStore, entry);
@@ -520,6 +1073,28 @@ export class AgendaSchedulerBridge {
520
1073
  result,
521
1074
  });
522
1075
  }
1076
+ if (this.migrateLegacy) {
1077
+ const candidates = this.agendaStore
1078
+ .list()
1079
+ .filter(
1080
+ (entry) =>
1081
+ !entry.schedulerMigration &&
1082
+ [undefined, "succeeded"].includes(
1083
+ entry.schedulerExecution?.status,
1084
+ ) &&
1085
+ !entry.executionLease &&
1086
+ ((entry.kind === "wakeup" && entry.status === "pending") ||
1087
+ (["cron", "monitor"].includes(entry.kind) &&
1088
+ entry.status === "active")),
1089
+ );
1090
+ for (const entry of candidates) {
1091
+ migrateAgendaSchedulerEntry({
1092
+ agendaStore: this.agendaStore,
1093
+ schedulerStore: this.schedulerStore,
1094
+ entry,
1095
+ });
1096
+ }
1097
+ }
523
1098
  return results;
524
1099
  }
525
1100
  }