chainlesschain 0.163.6 → 0.163.8

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 (234) hide show
  1. package/package.json +2 -1
  2. package/src/assets/web-panel/assets/{AIOps-DDIUofA8.js → AIOps-CavM7W3k.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Bm-5xYRn.js → ActionButton-CUvZGabQ.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-D5UH4ljI.js → Analytics-DeBnWnbe.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-D_ifjOOP.js → AppLayout-DbI5kxvd.js} +4 -4
  6. package/src/assets/web-panel/assets/{Artifacts-BY1mXdC7.js → Artifacts-D93bigKm.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-Bujr4Ml3.js → Audit-CBNvfUZa.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-sioKSTcC.js → BackgroundAgents-CJLyh_YU.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DDOAK8RK.js → Backup-D_0yoehD.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CtCsVVke.js → BaseInput-TJxmbObW.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-DpNj0vnd.js → Chat-CE0dto1q.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZta3Ftr.js → ChatBubbleRenderer-CZ5-yYiD.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DoHVNSXq.js → Checkbox-Ck1RVIVo.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DlVl7Qmv.js → Codegen-qpoyf6DK.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-GIqoPYty.js → Col-DUyPWjIE.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-CIAPEVji.js → Community-D_S1BemS.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BN7JiI_h.js → Compact-BjxeK-Gc.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CqUWCM9B.js → Compliance-Dd3CEJsu.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-A4iPp7A4.js → Cowork-B4QG7-ad.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-B2zeXXWb.js → Cron-BtZnids5.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BR2xmWr8.js → Crosschain-CHPfCBFD.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BzFDjrIv.js → DID-DT8DCEOK.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DKtDo3yA.js → Dashboard-COQab5_U.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-RNx43_YG.js → Dropdown-BY1PsQuU.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-SwMt4Rcy.js → EmailListRenderer-CsuAuaq3.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CsYebZ4X.js → FamilyGuardDashboard-BqpinD-j.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BmRKvBTy.js → Federation-D0gkfQFv.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CVxGTA-0.js → FormItemContext-BqgE-axK.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-x2iXpyB7.js → GenericCardRenderer-D0hJ2KvC.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DfqbkiOP.js → Git-CmMB0uPY.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-RuHyyj5O.js → Governance-BnVEI0Dk.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-Bn7uZ7rI.js → Inference-YTAMihRP.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DlP7XefZ.js → KnowledgeGraph-0ax7VHop.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-BYNdOiCd.js → Logs-B9TRcokL.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-yySN5YNM.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-JQYmDqM8.js → Marketplace-B6CSuD_H.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-Cyx4yZ-9.js → McpTools-CjMXvOVU.js} +4 -4
  38. package/src/assets/web-panel/assets/{Memory-VDuGZTN2.js → Memory-CXRF71U3.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-_yKo97gi.js → MobileBridge-DQgNp_oS.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-CXRXW3q7.js → MobileProjects-DlPwcCbq.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-acTGtfLH.js → Mtc-6hDTRPe1.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-ClpjXfkY.js → MtcAudit-BlVHU1DS.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-_6Tn3o96.js → Multisig-DP9pzEbV.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CK7pbZzP.js → NLProgramming-Djorh8EV.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Czy6V_P8.js → Notes-oLZ-JkUg.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-Cqx-UVv3.js → NotificationSettings-Cg288VH3.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-C69WDxGy.js → OrderTableRenderer-Bb1O3Ofr.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-DFnA9vLQ.js → Organization-CBdo-bHs.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-ReZkk2yT.js → Overflow-qZdnVumx.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DzjSI0aI.js → P2P-D4kNfGNQ.js} +2 -2
  51. package/src/assets/web-panel/assets/PdhVaultBrowser-DnlMemM7.js +7 -0
  52. package/src/assets/web-panel/assets/{Permissions-DhQ5klhR.js → Permissions-DHucHFBJ.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CUL1iHrW.js → PersonalDataHub-BovlOIPR.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-DU88iu4N.js → Pipeline-Deru3CQY.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-Bw-sYfyK.js → Privacy-Bq5oTTbU.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-Bq3Q5y0z.js → ProjectInit-BfzxVWAz.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-C8spzpho.js → ProjectSettings-CKC6XBtH.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-CMolGhae.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-Bf4eTP9W.js → Providers-C_jx4TWX.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-Dme9BDU1.js → QrScannerModal-BdIHeVGC.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CIXfv1rE.js → QuickAsk-BwZwAcPD.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CIxvdeZ-.js → Recommend-BNTt0L-m.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C6k7MExI.js → RemoteSession-Biyn2-Ro.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-CISDz2bl.js → Reputation-D6VpG32B.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-CdnDxXyf.js → Row-D_74OvQQ.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C-QpRHsl.js → RssFeed-BTr0a-BQ.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DBjaGzSn.js → Search-C3MCFoNU.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CQKukOeE.js → Security-CqW9aJ6s.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-DZz86jWb.js → Services-Cw5GNaHF.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-jb8RMK0d.js → Skeleton-DZps5qeY.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CdY76fZG.js → Skills-C6NQ1znT.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-C_CSiZX9.js → Sla-DFP8G2Sh.js} +1 -1
  73. package/src/assets/web-panel/assets/SpeechSettings-ESt6MB6v.js +1 -0
  74. package/src/assets/web-panel/assets/{SyncSettings-qvimGO0O.js → SyncSettings-BHnN98aM.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-Dng5LPJC.js → Tasks-NiSCaOf-.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-Bwqq5Wll.js → Templates-DsKFhW2C.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DNWuwP7Z.js → Tenant-CQ0E0ze5.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-B-6Othpp.js → Terminal-D_rBu_by.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-DKpKZfzx.js → TimelineRenderer-D3Vm4Po4.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DzfGMd2J.js → Tokens-DNTxRH6q.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-BXwUi74J.js → Trigger-Dgprz1ms.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BNzUq4ui.js → Trust-B2rUnVBD.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-BYugxww4.js → UkeySign-DEGQNc6_.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DdauhtSJ.js → VideoEditing-jlsAx3Eh.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-CSC9bCe_.js → Wallet-8jQih1ij.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Q8AN3bTO.js → WebAuthn-XLeO6xIB.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-d-LBGCuA.js → WorkflowEditor-De_dgDAG.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-BLVDCgsZ.js → chat-CI19R_Sk.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BZlPSEHl.js → colors-DsYTOYN3.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-IWseP6R6.js → compact-item-CM3-t_Qr.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DnVRV3Ao.js → createContext-BjxlBRZv.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-S1ld2LwU.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DLQZA_Wn.js → hasIn-RDaWX_4i.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BSjilZ9L.js → index-9R6BxAr0.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-B_N4-Gq9.js → index-B-Z8eFUJ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-Dy0R5ZED.js → index-B0PMlbMs.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-C1ifZtXo.js → index-B5REnKt8.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CINJQEj6.js → index-B7WBt_4T.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DocEhinm.js → index-B9O9YyWE.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DjWnONdy.js → index-BCnZyIMA.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-hwVDeoC0.js → index-BGqqJfL0.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bbj2NS_I.js → index-BHNYPO0b.js} +3 -3
  103. package/src/assets/web-panel/assets/{index-C5dgnilC.js → index-BKJr-TZp.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CT9vVN-X.js → index-BN6UPD2E.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DpVrK6fr.js → index-Bi46Xu4u.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CpBaJ3TD.js → index-BlT2VAEK.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-1jeAudHd.js → index-C0yx-jtQ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DOoQpuQY.js → index-C4gkzxkw.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DZpYeqrh.js → index-CVQMeiwH.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BhA3QEni.js → index-Cd3euP_4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-N_GoWZX4.js → index-Cd9OJhHG.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BOZJBTWZ.js → index-CiECJi-Q.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CarMTIyQ.js → index-CjTrRt13.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CZDekj7T.js → index-CnHH7bnD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-M7wxfAbm.js → index-Cpqm1qfU.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Bxp7uDD8.js → index-CyOGN6a5.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DalA0TKC.js → index-D0zcy54x.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-COev0W2g.js → index-D6sm5-Xq.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DUq0c7gq.js → index-DIUnNOjG.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BZs5bBnk.js → index-DX7tuWVJ.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CbQNL0ue.js → index-DXXtGMUJ.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DTbxp4UH.js → index-DcPFB8qJ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-YSw_gxlZ.js → index-DoQA0-52.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D_LQodeJ.js → index-Drlnwr8F.js} +1 -1
  125. package/src/assets/web-panel/assets/index-Drz6yq0m.js +1 -0
  126. package/src/assets/web-panel/assets/{index-jWb70ZgZ.js → index-Dsij_KsN.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DB0MJgF_.js → index-Dxge-xWl.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BEu2lMBy.js → index-DyIWssPC.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-C2FqfMvU.js → index-IeiL9RKc.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dkwsi1Kn.js → index-nAH87mQP.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-gt8bNefW.js → index-pPL66J85.js} +1 -1
  132. package/src/assets/web-panel/assets/index-uWiUs7S_.js +1 -0
  133. package/src/assets/web-panel/assets/{initDefaultProps-Co67Kqmb.js → initDefaultProps-BTL88of4.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BMOxQSF_.js → motion-BUB2pXwB.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-C5fWDw-N.js → move-egCkkOMm.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-BJevpL0e.js → mtc-parser-DAWhZC8c.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BCzNU8s6.js → omit-CvOxe_6d.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-CybWvDPk.js → pickAttrs-DZUSLZ5Z.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BMWS9HnW.js → placementArrow-BERxGNMY.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-BoL2UnGY.js → responsiveObserve-DZuDfZ9n.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-DWNfXraU.js → slide-DO756sgI.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BquTM3tX.js → statusUtils-BRQlIz28.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BEkCleim.js → styleChecker-DCSfdybl.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Oecnq7EA.js → useFlexGapSupport-LD8dWVve.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-CkDffJ4F.js → useFs-BX9uW2LK.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-qbvKXesB.js → usePersonalDataHub-BPSlPuHU.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-C6v8n6mA.js → vnode-BmOILC9V.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-4CTeFILV.js → zoom-1iKowKGg.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +5 -5
  151. package/src/commands/agent.js +90 -1
  152. package/src/commands/automation.js +103 -1
  153. package/src/commands/chat.js +11 -2
  154. package/src/commands/compact.js +106 -23
  155. package/src/commands/loop.js +28 -10
  156. package/src/commands/routine.js +1 -1
  157. package/src/commands/scheduler-daemon.js +359 -2
  158. package/src/commands/session-observability.js +305 -0
  159. package/src/commands/session.js +38 -0
  160. package/src/commands/team-merge-review.js +937 -0
  161. package/src/commands/team.js +2 -0
  162. package/src/data/changelog.json +27 -1
  163. package/src/gateways/ws/ws-agent-handler.js +439 -5
  164. package/src/harness/jsonl-session-store.js +463 -125
  165. package/src/harness/prompt-compressor.js +86 -13
  166. package/src/harness/provider-backed-compaction.js +100 -27
  167. package/src/lib/advisor-runtime.js +3 -1
  168. package/src/lib/agent-schedule-store.js +549 -28
  169. package/src/lib/agent-team/team-adjudication.js +40 -1
  170. package/src/lib/agent-team/team-merge-review-store.js +765 -0
  171. package/src/lib/agent-team/team-merge-review-transaction.js +1223 -0
  172. package/src/lib/agent-team/team-merge-review.js +1847 -0
  173. package/src/lib/automation-center-incidents.js +314 -0
  174. package/src/lib/automation-center-runtime.js +448 -0
  175. package/src/lib/automation-center.js +182 -38
  176. package/src/lib/automation-engine.js +535 -0
  177. package/src/lib/automation-execution-authority.js +241 -12
  178. package/src/lib/automation-execution-incident.js +737 -0
  179. package/src/lib/background-agent-supervisor.js +793 -215
  180. package/src/lib/background-command-argv.js +24 -0
  181. package/src/lib/background-interaction-journal.js +361 -52
  182. package/src/lib/background-interaction-resolver.js +32 -0
  183. package/src/lib/background-launch-profile.js +2 -0
  184. package/src/lib/causal-observability.js +2661 -0
  185. package/src/lib/chat-session-admission.js +76 -0
  186. package/src/lib/cost-budget.js +5 -7
  187. package/src/lib/cowork/ab-comparator-cli.js +11 -0
  188. package/src/lib/cowork/debate-review-cli.js +12 -0
  189. package/src/lib/cowork-adapter.js +159 -38
  190. package/src/lib/cowork-cron.js +442 -15
  191. package/src/lib/cowork-task-runner.js +112 -17
  192. package/src/lib/delivery-coordinator.js +142 -0
  193. package/src/lib/direct-model-usage.js +121 -0
  194. package/src/lib/file-lines.js +183 -47
  195. package/src/lib/llm-pricing.js +12 -17
  196. package/src/lib/loop.js +26 -2
  197. package/src/lib/routine-store.js +380 -21
  198. package/src/lib/runtime-usage-ledger.js +182 -0
  199. package/src/lib/scheduler-kernel/agenda-adapter.js +325 -10
  200. package/src/lib/scheduler-kernel/automation-adapter.js +514 -18
  201. package/src/lib/scheduler-kernel/automation-event-adapter.js +123 -11
  202. package/src/lib/scheduler-kernel/contract.js +91 -0
  203. package/src/lib/scheduler-kernel/cowork-cron-adapter.js +297 -4
  204. package/src/lib/scheduler-kernel/loop-adapter.js +547 -6
  205. package/src/lib/scheduler-kernel/migration-admin.js +346 -0
  206. package/src/lib/scheduler-kernel/migration-rollback-admin.js +932 -0
  207. package/src/lib/scheduler-kernel/routine-adapter.js +400 -20
  208. package/src/lib/scheduler-kernel/runtime-control-capabilities.js +31 -0
  209. package/src/lib/scheduler-kernel/runtime.js +237 -2
  210. package/src/lib/scheduler-kernel/source-locator-path.js +142 -0
  211. package/src/lib/scheduler-kernel/store.js +3957 -248
  212. package/src/lib/session-host-lease.js +171 -2
  213. package/src/lib/session-host-snapshot.js +14 -3
  214. package/src/lib/session-persistence-failure.js +1 -0
  215. package/src/lib/session-tail.js +179 -34
  216. package/src/lib/session-transcript-structure.js +96 -0
  217. package/src/lib/sub-agent-context.js +328 -22
  218. package/src/lib/ws-agent-handler.js +4 -1
  219. package/src/repl/agent-repl.js +732 -91
  220. package/src/repl/chat-repl.js +5 -1
  221. package/src/runtime/agent-core.js +1282 -207
  222. package/src/runtime/fallback-model.js +5 -0
  223. package/src/runtime/headless-runner.js +324 -82
  224. package/src/runtime/headless-stream.js +232 -26
  225. package/src/runtime/mcp-config.js +81 -10
  226. package/src/runtime/policies/agent-policy.js +4 -0
  227. package/src/workers/background-agent-worker.js +403 -96
  228. package/src/assets/web-panel/assets/MarkdownRenderer-aMtgN-R6.js +0 -1
  229. package/src/assets/web-panel/assets/PdhVaultBrowser-DKTf5xIP.js +0 -7
  230. package/src/assets/web-panel/assets/Projects-Dd-xuPIc.js +0 -1
  231. package/src/assets/web-panel/assets/SpeechSettings-C7tT9iad.js +0 -1
  232. package/src/assets/web-panel/assets/devWarning-Jzu62CLC.js +0 -1
  233. package/src/assets/web-panel/assets/index-Zb2MeDSy.js +0 -1
  234. package/src/assets/web-panel/assets/index-fikqml4k.js +0 -1
@@ -22,7 +22,7 @@
22
22
  import fs from "fs";
23
23
  import os from "os";
24
24
  import path from "path";
25
- import { randomUUID } from "crypto";
25
+ import { createHash, randomUUID } from "crypto";
26
26
  import { isEntryExpired, effectiveFireAt } from "./schedule-planner.js";
27
27
  import { SUBAGENT_PERMISSION_MODES } from "./subagent-contract.js";
28
28
  import { parseGoalCondition } from "./goal-condition-engine.js";
@@ -30,6 +30,7 @@ import { normalizeActionClass } from "./unattended-action-policy.js";
30
30
  import { withFileLock } from "./with-file-lock.js";
31
31
 
32
32
  export const SCHEDULE_KINDS = Object.freeze(["wakeup", "cron", "monitor"]);
33
+ export const AGENDA_SCHEDULER_MIGRATION_SCHEMA_VERSION = 1;
33
34
 
34
35
  /** A valid absolute-epoch-ms expiry, or null (never expires). */
35
36
  function normalizeExpiresAt(v) {
@@ -168,6 +169,87 @@ function hasLiveExecutionLease(entry, now) {
168
169
  return Number(entry.executionLease?.expiresAt) > Number(now);
169
170
  }
170
171
 
172
+ function stableJsonValue(value) {
173
+ if (Array.isArray(value)) return value.map(stableJsonValue);
174
+ if (value && typeof value === "object") {
175
+ return Object.fromEntries(
176
+ Object.keys(value)
177
+ .sort()
178
+ .map((key) => [key, stableJsonValue(value[key])]),
179
+ );
180
+ }
181
+ return value;
182
+ }
183
+
184
+ function agendaMigrationSource(entry) {
185
+ const source = { ...entry };
186
+ delete source.executionLease;
187
+ delete source.schedulerExecution;
188
+ delete source.schedulerMigration;
189
+ delete source.firedAt;
190
+ delete source.lastRunAt;
191
+ delete source.runs;
192
+ delete source.nextAt;
193
+ delete source.lastCheckAt;
194
+ delete source.checks;
195
+ delete source.lastMtimeMs;
196
+ delete source.lastEventId;
197
+ delete source.lastAuthority;
198
+ delete source.status;
199
+ return source;
200
+ }
201
+
202
+ export function agendaMigrationSourceDigest(entry) {
203
+ return createHash("sha256")
204
+ .update("chainlesschain.agenda.scheduler-migration-source.v1\0", "utf8")
205
+ .update(
206
+ JSON.stringify(stableJsonValue(agendaMigrationSource(entry))),
207
+ "utf8",
208
+ )
209
+ .digest("hex");
210
+ }
211
+
212
+ export function agendaMigrationSourceSnapshot(entry) {
213
+ return stableJsonValue(agendaMigrationSource(entry));
214
+ }
215
+
216
+ function schedulerMigrationFence(entry, now) {
217
+ const migration = entry.schedulerMigration;
218
+ if (
219
+ migration?.schemaVersion !== AGENDA_SCHEDULER_MIGRATION_SCHEMA_VERSION ||
220
+ typeof migration.retirementToken !== "string" ||
221
+ migration.retirementToken.length === 0
222
+ ) {
223
+ return false;
224
+ }
225
+ return (
226
+ hasLiveExecutionLease(entry, now) &&
227
+ entry.executionLease?.owner ===
228
+ `scheduler-migration:${migration.retirementToken}`
229
+ );
230
+ }
231
+
232
+ function applySchedulerMigrationFence(entry, now) {
233
+ const token = entry.schedulerMigration?.retirementToken;
234
+ if (typeof token !== "string" || token.length === 0) return false;
235
+ entry.executionLease = {
236
+ owner: `scheduler-migration:${token}`,
237
+ claimedAt: now,
238
+ expiresAt: Number.MAX_SAFE_INTEGER,
239
+ };
240
+ return true;
241
+ }
242
+
243
+ function normalizeMigrationField(value, field) {
244
+ if (typeof value !== "string" || value.trim().length === 0) {
245
+ throw agendaStoreError(
246
+ "AGENDA_SCHEDULER_MIGRATION_INVALID",
247
+ `Agenda scheduler migration ${field} is required`,
248
+ );
249
+ }
250
+ return value.trim();
251
+ }
252
+
171
253
  function agendaStoreError(code, message, { retryable = false, retryAt } = {}) {
172
254
  const error = new Error(message);
173
255
  error.code = code;
@@ -176,6 +258,21 @@ function agendaStoreError(code, message, { retryable = false, retryAt } = {}) {
176
258
  return error;
177
259
  }
178
260
 
261
+ function agendaPersistenceError(
262
+ code,
263
+ message,
264
+ cause,
265
+ { filePath, commitState } = {},
266
+ ) {
267
+ const error = new Error(message, { cause });
268
+ error.name = "AgendaScheduleStoreError";
269
+ error.code = code;
270
+ error.filePath = filePath;
271
+ if (typeof cause?.code === "string") error.fsCode = cause.code;
272
+ if (commitState) error.commitState = commitState;
273
+ return error;
274
+ }
275
+
179
276
  function applyScheduledAgentSuccess(entry, atMs) {
180
277
  if (entry.kind === "wakeup") {
181
278
  entry.status = "fired";
@@ -584,9 +681,16 @@ export function previousCronTime(expr, fromMs, options = {}) {
584
681
  // ─── store ───────────────────────────────────────────────────────────────
585
682
 
586
683
  export class AgentScheduleStore {
587
- constructor({ dir = null, now = () => Date.now() } = {}) {
684
+ constructor({
685
+ dir = null,
686
+ now = () => Date.now(),
687
+ fileSystem = fs,
688
+ platform = process.platform,
689
+ } = {}) {
588
690
  this.dir = dir || agentScheduleDir();
589
691
  this._now = typeof now === "function" ? now : () => now;
692
+ this._fs = fileSystem;
693
+ this._platform = platform;
590
694
  }
591
695
 
592
696
  _file(kind) {
@@ -594,37 +698,124 @@ export class AgentScheduleStore {
594
698
  }
595
699
 
596
700
  _ensureDir() {
597
- fs.mkdirSync(this.dir, { recursive: true, mode: 0o700 });
701
+ this._fs.mkdirSync(this.dir, { recursive: true, mode: 0o700 });
598
702
  }
599
703
 
600
- _readAll(kind) {
704
+ _readAll(kind, { failOnMalformed = false } = {}) {
705
+ const filePath = this._file(kind);
601
706
  let raw;
602
707
  try {
603
- raw = fs.readFileSync(this._file(kind), "utf-8");
604
- } catch {
605
- return [];
708
+ raw = this._fs.readFileSync(filePath, "utf-8");
709
+ } catch (cause) {
710
+ if (cause?.code === "ENOENT") return [];
711
+ throw agendaPersistenceError(
712
+ "AGENDA_SCHEDULE_STORE_READ_FAILED",
713
+ `Agenda schedule store read failed (${filePath})`,
714
+ cause,
715
+ { filePath },
716
+ );
606
717
  }
607
718
  const entries = [];
719
+ let lineNumber = 0;
608
720
  for (const line of raw.split("\n")) {
721
+ lineNumber += 1;
609
722
  const trimmed = line.trim();
610
723
  if (!trimmed) continue;
611
724
  try {
612
- entries.push(JSON.parse(trimmed));
613
- } catch {
725
+ const entry = JSON.parse(trimmed);
726
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
727
+ throw new TypeError("schedule entry must be an object");
728
+ }
729
+ entries.push(entry);
730
+ } catch (cause) {
731
+ if (failOnMalformed) {
732
+ throw agendaPersistenceError(
733
+ "AGENDA_SCHEDULE_STORE_MALFORMED",
734
+ `Agenda schedule store contains malformed JSONL (${filePath}:${lineNumber})`,
735
+ cause,
736
+ { filePath },
737
+ );
738
+ }
614
739
  // Skip a corrupt line rather than throwing — one bad append must not
615
- // poison the whole store (per-row resilience, like the JSON.parse sweep).
740
+ // poison advisory reads. Authoritative mutations opt into strict mode
741
+ // so they never replace a store after silently dropping durable rows.
616
742
  }
617
743
  }
618
744
  return entries;
619
745
  }
620
746
 
621
747
  _writeAll(kind, entries) {
622
- this._ensureDir();
748
+ const filePath = this._file(kind);
623
749
  const body = entries.map((entry) => JSON.stringify(entry)).join("\n");
624
- fs.writeFileSync(this._file(kind), body ? body + "\n" : "", {
625
- encoding: "utf-8",
626
- mode: 0o600,
627
- });
750
+ const contents = Buffer.from(body ? `${body}\n` : "", "utf8");
751
+ const temporaryPath = path.join(
752
+ this.dir,
753
+ `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
754
+ );
755
+ let descriptor = null;
756
+ let renamed = false;
757
+
758
+ try {
759
+ this._ensureDir();
760
+ descriptor = this._fs.openSync(temporaryPath, "wx", 0o600);
761
+ let offset = 0;
762
+ while (offset < contents.length) {
763
+ const written = this._fs.writeSync(
764
+ descriptor,
765
+ contents,
766
+ offset,
767
+ contents.length - offset,
768
+ null,
769
+ );
770
+ if (!Number.isSafeInteger(written) || written <= 0) {
771
+ const cause = new Error("write returned no progress");
772
+ cause.code = "EIO";
773
+ throw cause;
774
+ }
775
+ offset += written;
776
+ }
777
+ this._fs.fsyncSync(descriptor);
778
+ this._fs.closeSync(descriptor);
779
+ descriptor = null;
780
+ this._fs.renameSync(temporaryPath, filePath);
781
+ renamed = true;
782
+
783
+ if (this._platform !== "win32") {
784
+ const directoryDescriptor = this._fs.openSync(this.dir, "r");
785
+ try {
786
+ this._fs.fsyncSync(directoryDescriptor);
787
+ } finally {
788
+ this._fs.closeSync(directoryDescriptor);
789
+ }
790
+ }
791
+ } catch (cause) {
792
+ if (descriptor !== null) {
793
+ try {
794
+ this._fs.closeSync(descriptor);
795
+ } catch {
796
+ // Preserve the original persistence failure.
797
+ }
798
+ }
799
+ if (!renamed) {
800
+ try {
801
+ this._fs.unlinkSync(temporaryPath);
802
+ } catch {
803
+ // Best-effort orphan cleanup; the authoritative file is untouched.
804
+ }
805
+ }
806
+ throw agendaPersistenceError(
807
+ "AGENDA_SCHEDULE_STORE_WRITE_FAILED",
808
+ `Agenda schedule store write failed (${filePath})`,
809
+ cause,
810
+ {
811
+ filePath,
812
+ // A failure before rename leaves the old generation authoritative.
813
+ // A directory-fsync failure after rename makes crash durability
814
+ // unknowable even though an immediate read sees the new generation.
815
+ commitState: renamed ? "unknown" : "not-committed",
816
+ },
817
+ );
818
+ }
628
819
  }
629
820
 
630
821
  // ── create ──────────────────────────────────────────────────────────────
@@ -659,7 +850,7 @@ export class AgentScheduleStore {
659
850
  createdAt: now,
660
851
  status: "pending",
661
852
  };
662
- const entries = this._readAll("wakeup");
853
+ const entries = this._readAll("wakeup", { failOnMalformed: true });
663
854
  entries.push(entry);
664
855
  this._writeAll("wakeup", entries);
665
856
  return entry;
@@ -704,7 +895,7 @@ export class AgentScheduleStore {
704
895
  runs: 0,
705
896
  status: "active",
706
897
  };
707
- const entries = this._readAll("cron");
898
+ const entries = this._readAll("cron", { failOnMalformed: true });
708
899
  entries.push(entry);
709
900
  this._writeAll("cron", entries);
710
901
  return entry;
@@ -800,7 +991,7 @@ export class AgentScheduleStore {
800
991
  checks: 0,
801
992
  status: "active",
802
993
  };
803
- const entries = this._readAll("monitor");
994
+ const entries = this._readAll("monitor", { failOnMalformed: true });
804
995
  entries.push(entry);
805
996
  this._writeAll("monitor", entries);
806
997
  return entry;
@@ -820,15 +1011,246 @@ export class AgentScheduleStore {
820
1011
  return this.list().find((entry) => entry.id === id) || null;
821
1012
  }
822
1013
 
1014
+ prepareSchedulerMigration(
1015
+ id,
1016
+ { migrationId, sourceDigest, targetJobId } = {},
1017
+ ) {
1018
+ const migration = normalizeMigrationField(migrationId, "migrationId");
1019
+ const expectedDigest = normalizeMigrationField(
1020
+ sourceDigest,
1021
+ "sourceDigest",
1022
+ );
1023
+ const target = normalizeMigrationField(targetJobId, "targetJobId");
1024
+ this._ensureDir();
1025
+ return withFileLock(
1026
+ path.join(this.dir, ".agenda-claims"),
1027
+ () => {
1028
+ for (const kind of SCHEDULE_KINDS) {
1029
+ const entries = this._readAll(kind, { failOnMalformed: true });
1030
+ const entry = entries.find((candidate) => candidate.id === id);
1031
+ if (!entry) continue;
1032
+ if (!isSchedulableStatus(entry)) {
1033
+ throw agendaStoreError(
1034
+ "AGENDA_SCHEDULER_ENTRY_TERMINAL",
1035
+ `Agenda entry is not schedulable: ${id}`,
1036
+ );
1037
+ }
1038
+ const actualDigest = agendaMigrationSourceDigest(entry);
1039
+ if (actualDigest !== expectedDigest) {
1040
+ throw agendaStoreError(
1041
+ "AGENDA_SCHEDULER_MIGRATION_SOURCE_CHANGED",
1042
+ `Agenda entry changed before migration: ${id}`,
1043
+ );
1044
+ }
1045
+ const prior = entry.schedulerMigration;
1046
+ if (prior) {
1047
+ if (
1048
+ prior.schemaVersion !==
1049
+ AGENDA_SCHEDULER_MIGRATION_SCHEMA_VERSION ||
1050
+ prior.sourceDigest !== expectedDigest ||
1051
+ prior.targetJobId !== target ||
1052
+ prior.migrationId !== migration ||
1053
+ prior.state !== "prepared"
1054
+ ) {
1055
+ throw agendaStoreError(
1056
+ "AGENDA_SCHEDULER_MIGRATION_CONFLICT",
1057
+ `Agenda entry already belongs to another scheduler migration: ${id}`,
1058
+ );
1059
+ }
1060
+ return entry;
1061
+ }
1062
+ entry.schedulerMigration = {
1063
+ schemaVersion: AGENDA_SCHEDULER_MIGRATION_SCHEMA_VERSION,
1064
+ state: "prepared",
1065
+ migrationId: migration,
1066
+ sourceDigest: expectedDigest,
1067
+ targetJobId: target,
1068
+ preparedAt: this._now(),
1069
+ };
1070
+ this._writeAll(kind, entries);
1071
+ return entry;
1072
+ }
1073
+ throw agendaStoreError(
1074
+ "AGENDA_SCHEDULER_ENTRY_NOT_FOUND",
1075
+ `Agenda entry does not exist: ${id}`,
1076
+ );
1077
+ },
1078
+ { failIfUnavailable: true },
1079
+ );
1080
+ }
1081
+
1082
+ retireForSchedulerMigration(
1083
+ id,
1084
+ {
1085
+ migrationId,
1086
+ sourceDigest,
1087
+ targetJobId,
1088
+ retirementToken,
1089
+ atMs = null,
1090
+ } = {},
1091
+ ) {
1092
+ const migration = normalizeMigrationField(migrationId, "migrationId");
1093
+ const expectedDigest = normalizeMigrationField(
1094
+ sourceDigest,
1095
+ "sourceDigest",
1096
+ );
1097
+ const target = normalizeMigrationField(targetJobId, "targetJobId");
1098
+ const token = normalizeMigrationField(retirementToken, "retirementToken");
1099
+ const now = atMs != null ? Number(atMs) : this._now();
1100
+ this._ensureDir();
1101
+ return withFileLock(
1102
+ path.join(this.dir, ".agenda-claims"),
1103
+ () => {
1104
+ for (const kind of SCHEDULE_KINDS) {
1105
+ const entries = this._readAll(kind, { failOnMalformed: true });
1106
+ const entry = entries.find((candidate) => candidate.id === id);
1107
+ if (!entry) continue;
1108
+ const prior = entry.schedulerMigration;
1109
+ if (
1110
+ prior?.schemaVersion !==
1111
+ AGENDA_SCHEDULER_MIGRATION_SCHEMA_VERSION ||
1112
+ prior.sourceDigest !== expectedDigest ||
1113
+ prior.targetJobId !== target ||
1114
+ prior.migrationId !== migration ||
1115
+ !["prepared", "retired"].includes(prior.state)
1116
+ ) {
1117
+ throw agendaStoreError(
1118
+ "AGENDA_SCHEDULER_MIGRATION_CONFLICT",
1119
+ `Agenda migration preparation changed before retirement: ${id}`,
1120
+ );
1121
+ }
1122
+ if (prior.state === "retired") {
1123
+ if (
1124
+ prior.retirementToken !== token ||
1125
+ !schedulerMigrationFence(entry, now)
1126
+ ) {
1127
+ throw agendaStoreError(
1128
+ "AGENDA_SCHEDULER_MIGRATION_CONFLICT",
1129
+ `Agenda migration retirement fence changed: ${id}`,
1130
+ );
1131
+ }
1132
+ return entry;
1133
+ }
1134
+ if (agendaMigrationSourceDigest(entry) !== expectedDigest) {
1135
+ throw agendaStoreError(
1136
+ "AGENDA_SCHEDULER_MIGRATION_SOURCE_CHANGED",
1137
+ `Agenda entry changed before retirement: ${id}`,
1138
+ );
1139
+ }
1140
+ if (hasLiveExecutionLease(entry, now)) {
1141
+ throw agendaStoreError(
1142
+ "AGENDA_SCHEDULER_LEGACY_CLAIM_ACTIVE",
1143
+ `Agenda entry is already claimed by a legacy runner: ${id}`,
1144
+ {
1145
+ retryable: true,
1146
+ retryAt: Number(entry.executionLease.expiresAt),
1147
+ },
1148
+ );
1149
+ }
1150
+ entry.schedulerMigration = {
1151
+ ...prior,
1152
+ state: "retired",
1153
+ retirementToken: token,
1154
+ retiredAt: now,
1155
+ };
1156
+ applySchedulerMigrationFence(entry, now);
1157
+ this._writeAll(kind, entries);
1158
+ return entry;
1159
+ }
1160
+ throw agendaStoreError(
1161
+ "AGENDA_SCHEDULER_ENTRY_NOT_FOUND",
1162
+ `Agenda entry does not exist: ${id}`,
1163
+ );
1164
+ },
1165
+ { failIfUnavailable: true },
1166
+ );
1167
+ }
1168
+
1169
+ restoreFromSchedulerMigration(
1170
+ id,
1171
+ { migrationId, sourceDigest, targetJobId, retirementToken } = {},
1172
+ ) {
1173
+ const migrationIdValue = normalizeMigrationField(
1174
+ migrationId,
1175
+ "migrationId",
1176
+ );
1177
+ const expectedDigest = normalizeMigrationField(
1178
+ sourceDigest,
1179
+ "sourceDigest",
1180
+ );
1181
+ const target = normalizeMigrationField(targetJobId, "targetJobId");
1182
+ const token =
1183
+ retirementToken === null || retirementToken === undefined
1184
+ ? null
1185
+ : normalizeMigrationField(retirementToken, "retirementToken");
1186
+ this._ensureDir();
1187
+ return withFileLock(
1188
+ path.join(this.dir, ".agenda-claims"),
1189
+ () => {
1190
+ for (const kind of SCHEDULE_KINDS) {
1191
+ const entries = this._readAll(kind, { failOnMalformed: true });
1192
+ const entry = entries.find((candidate) => candidate.id === id);
1193
+ if (!entry) continue;
1194
+ const migration = entry.schedulerMigration;
1195
+ if (!migration) {
1196
+ if (
1197
+ agendaMigrationSourceDigest(entry) !== expectedDigest ||
1198
+ String(entry.executionLease?.owner || "").startsWith(
1199
+ "scheduler-migration:",
1200
+ )
1201
+ ) {
1202
+ throw agendaStoreError(
1203
+ "AGENDA_SCHEDULER_MIGRATION_CONFLICT",
1204
+ `Agenda source changed after rollback restoration: ${id}`,
1205
+ );
1206
+ }
1207
+ return entry;
1208
+ }
1209
+ if (
1210
+ migration?.schemaVersion !==
1211
+ AGENDA_SCHEDULER_MIGRATION_SCHEMA_VERSION ||
1212
+ migration.sourceDigest !== expectedDigest ||
1213
+ agendaMigrationSourceDigest(entry) !== expectedDigest ||
1214
+ migration.targetJobId !== target ||
1215
+ migration.migrationId !== migrationIdValue ||
1216
+ !["prepared", "retired"].includes(migration.state) ||
1217
+ (migration.state === "retired" &&
1218
+ (migration.retirementToken !== token ||
1219
+ entry.executionLease?.owner !== `scheduler-migration:${token}`))
1220
+ ) {
1221
+ throw agendaStoreError(
1222
+ "AGENDA_SCHEDULER_MIGRATION_CONFLICT",
1223
+ `Agenda migration fence changed before rollback: ${id}`,
1224
+ );
1225
+ }
1226
+ delete entry.schedulerMigration;
1227
+ if (migration.state === "retired") delete entry.executionLease;
1228
+ this._writeAll(kind, entries);
1229
+ return entry;
1230
+ }
1231
+ throw agendaStoreError(
1232
+ "AGENDA_SCHEDULER_ENTRY_NOT_FOUND",
1233
+ `Agenda entry does not exist: ${id}`,
1234
+ );
1235
+ },
1236
+ { failIfUnavailable: true },
1237
+ );
1238
+ }
1239
+
823
1240
  /** Entries whose time has come (pending wakeup / active cron|monitor). */
824
- due(kind = null, atMs = null) {
1241
+ due(kind = null, atMs = null, { schedulerMigration = false } = {}) {
825
1242
  const now = atMs != null ? atMs : this._now();
826
1243
  return this.list(kind).filter((entry) => {
827
1244
  // An entry past its expiry never fires — even if it is not yet retired
828
1245
  // (defense-in-depth: retireExpired need not have run first).
829
1246
  if (isEntryExpired(entry, now)) return false;
830
1247
  if (!isSchedulableStatus(entry)) return false;
831
- if (hasLiveExecutionLease(entry, now)) return false;
1248
+ if (
1249
+ hasLiveExecutionLease(entry, now) &&
1250
+ !(schedulerMigration && schedulerMigrationFence(entry, now))
1251
+ ) {
1252
+ return false;
1253
+ }
832
1254
  // The effective fire time applies the entry's OWN deterministic jitter
833
1255
  // (`entry.jitterMs`, default 0 → base fire time, byte-identical to the
834
1256
  // prior `dueAt`/`nextAt` comparison). A jittered entry fires slightly
@@ -858,7 +1280,9 @@ export class AgentScheduleStore {
858
1280
  () => {
859
1281
  const claimed = [];
860
1282
  for (const scheduleKind of kinds) {
861
- const entries = this._readAll(scheduleKind);
1283
+ const entries = this._readAll(scheduleKind, {
1284
+ failOnMalformed: true,
1285
+ });
862
1286
  let changed = false;
863
1287
  for (const entry of entries) {
864
1288
  if (!isSchedulableStatus(entry) || isEntryExpired(entry, now))
@@ -929,7 +1353,7 @@ export class AgentScheduleStore {
929
1353
  path.join(this.dir, ".agenda-claims"),
930
1354
  () => {
931
1355
  for (const kind of SCHEDULE_KINDS) {
932
- const entries = this._readAll(kind);
1356
+ const entries = this._readAll(kind, { failOnMalformed: true });
933
1357
  const entry = entries.find((candidate) => candidate.id === id);
934
1358
  if (!entry) continue;
935
1359
  const prior = entry.schedulerExecution;
@@ -946,7 +1370,8 @@ export class AgentScheduleStore {
946
1370
  ) {
947
1371
  return entry;
948
1372
  }
949
- if (hasLiveExecutionLease(entry, now)) {
1373
+ const migrationFence = schedulerMigrationFence(entry, now);
1374
+ if (hasLiveExecutionLease(entry, now) && !migrationFence) {
950
1375
  throw agendaStoreError(
951
1376
  "AGENDA_SCHEDULER_LEGACY_CLAIM_ACTIVE",
952
1377
  `Agenda entry is already claimed by a legacy runner: ${id}`,
@@ -1016,7 +1441,7 @@ export class AgentScheduleStore {
1016
1441
  path.join(this.dir, ".agenda-claims"),
1017
1442
  () => {
1018
1443
  for (const kind of SCHEDULE_KINDS) {
1019
- const entries = this._readAll(kind);
1444
+ const entries = this._readAll(kind, { failOnMalformed: true });
1020
1445
  const entry = entries.find((candidate) => candidate.id === id);
1021
1446
  if (!entry) continue;
1022
1447
  const evidence = entry.schedulerExecution;
@@ -1051,6 +1476,102 @@ export class AgentScheduleStore {
1051
1476
  ...(outcome === "succeeded" ? { result: storedResult } : { error }),
1052
1477
  };
1053
1478
  delete entry.executionLease;
1479
+ if (isSchedulableStatus(entry))
1480
+ applySchedulerMigrationFence(entry, now);
1481
+ this._writeAll(kind, entries);
1482
+ return entry;
1483
+ }
1484
+ throw agendaStoreError(
1485
+ "AGENDA_SCHEDULER_ENTRY_NOT_FOUND",
1486
+ `Agenda entry does not exist: ${id}`,
1487
+ );
1488
+ },
1489
+ { failIfUnavailable: true },
1490
+ );
1491
+ }
1492
+
1493
+ /**
1494
+ * Resolve start-only scheduler evidence after an operator has verified the
1495
+ * external outcome. This is intentionally separate from normal completion:
1496
+ * it preserves the adjudication identity and can release the non-expiring
1497
+ * legacy fence without pretending that a machine observed a terminal result.
1498
+ */
1499
+ adjudicateSchedulerExecution(
1500
+ id,
1501
+ {
1502
+ occurrenceId,
1503
+ snapshotDigest,
1504
+ attempt,
1505
+ decision,
1506
+ requestId,
1507
+ atMs = null,
1508
+ } = {},
1509
+ ) {
1510
+ if (!["confirmed_applied", "confirmed_not_applied"].includes(decision)) {
1511
+ throw agendaStoreError(
1512
+ "AGENDA_SCHEDULER_ADJUDICATION_INVALID",
1513
+ "Agenda scheduler adjudication decision is invalid",
1514
+ );
1515
+ }
1516
+ const now = atMs != null ? Number(atMs) : this._now();
1517
+ this._ensureDir();
1518
+ return withFileLock(
1519
+ path.join(this.dir, ".agenda-claims"),
1520
+ () => {
1521
+ for (const kind of SCHEDULE_KINDS) {
1522
+ const entries = this._readAll(kind, { failOnMalformed: true });
1523
+ const entry = entries.find((candidate) => candidate.id === id);
1524
+ if (!entry) continue;
1525
+ const evidence = entry.schedulerExecution;
1526
+ if (
1527
+ evidence?.occurrenceId === occurrenceId &&
1528
+ evidence.snapshotDigest === snapshotDigest &&
1529
+ evidence.adjudication?.requestId === requestId &&
1530
+ evidence.adjudication.decision === decision &&
1531
+ evidence.status ===
1532
+ (decision === "confirmed_applied"
1533
+ ? "adjudicated_applied"
1534
+ : "adjudicated_not_applied")
1535
+ ) {
1536
+ return entry;
1537
+ }
1538
+ if (
1539
+ !evidence ||
1540
+ evidence.occurrenceId !== occurrenceId ||
1541
+ evidence.snapshotDigest !== snapshotDigest ||
1542
+ !Number.isSafeInteger(evidence.attempt) ||
1543
+ evidence.attempt < 1 ||
1544
+ evidence.attempt > attempt ||
1545
+ evidence.status !== "running"
1546
+ ) {
1547
+ throw agendaStoreError(
1548
+ "AGENDA_SCHEDULER_ADJUDICATION_EVIDENCE_MISMATCH",
1549
+ `Agenda scheduler adjudication evidence does not match: ${id}`,
1550
+ );
1551
+ }
1552
+ if (decision === "confirmed_applied") {
1553
+ if (entry.kind === "monitor") {
1554
+ // A generic operator decision proves the prior check ran, but
1555
+ // not whether its observation matched. Pause the monitor rather
1556
+ // than inventing a domain result or rearming a duplicate check.
1557
+ entry.status = "adjudicated";
1558
+ entry.lastCheckAt = now;
1559
+ } else {
1560
+ applyScheduledAgentSuccess(entry, now);
1561
+ }
1562
+ }
1563
+ entry.schedulerExecution = {
1564
+ ...evidence,
1565
+ status:
1566
+ decision === "confirmed_applied"
1567
+ ? "adjudicated_applied"
1568
+ : "adjudicated_not_applied",
1569
+ endedAt: now,
1570
+ adjudication: { requestId, decision },
1571
+ };
1572
+ delete entry.executionLease;
1573
+ if (isSchedulableStatus(entry))
1574
+ applySchedulerMigrationFence(entry, now);
1054
1575
  this._writeAll(kind, entries);
1055
1576
  return entry;
1056
1577
  }
@@ -1074,7 +1595,7 @@ export class AgentScheduleStore {
1074
1595
  const now = atMs != null ? atMs : this._now();
1075
1596
  const retired = [];
1076
1597
  for (const kind of SCHEDULE_KINDS) {
1077
- const entries = this._readAll(kind);
1598
+ const entries = this._readAll(kind, { failOnMalformed: true });
1078
1599
  let changed = false;
1079
1600
  for (const entry of entries) {
1080
1601
  if (isSchedulableStatus(entry) && isEntryExpired(entry, now)) {
@@ -1091,7 +1612,7 @@ export class AgentScheduleStore {
1091
1612
 
1092
1613
  cancel(id) {
1093
1614
  for (const kind of SCHEDULE_KINDS) {
1094
- const entries = this._readAll(kind);
1615
+ const entries = this._readAll(kind, { failOnMalformed: true });
1095
1616
  const idx = entries.findIndex((entry) => entry.id === id);
1096
1617
  if (idx >= 0) {
1097
1618
  const [removed] = entries.splice(idx, 1);
@@ -1112,7 +1633,7 @@ export class AgentScheduleStore {
1112
1633
  pruneTerminal({ before = Infinity } = {}) {
1113
1634
  const removed = [];
1114
1635
  for (const kind of SCHEDULE_KINDS) {
1115
- const entries = this._readAll(kind);
1636
+ const entries = this._readAll(kind, { failOnMalformed: true });
1116
1637
  const keep = entries.filter((entry) => {
1117
1638
  const prunable =
1118
1639
  TERMINAL_SET.has(entry.status) && terminalAt(entry) <= before;
@@ -1176,7 +1697,7 @@ export class AgentScheduleStore {
1176
1697
  }
1177
1698
 
1178
1699
  _mutate(kind, id, fn) {
1179
- const entries = this._readAll(kind);
1700
+ const entries = this._readAll(kind, { failOnMalformed: true });
1180
1701
  const entry = entries.find((e) => e.id === id);
1181
1702
  if (!entry) return null;
1182
1703
  fn(entry);