chainlesschain 0.163.6 → 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.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DDIUofA8.js → AIOps-axqQfwv8.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Bm-5xYRn.js → ActionButton-DV-Ro0By.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D5UH4ljI.js → Analytics-Bt72WEVu.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D_ifjOOP.js → AppLayout-BtepG55E.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BY1mXdC7.js → Artifacts-B7KaJ7S_.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bujr4Ml3.js → Audit-FNFaUg7A.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-sioKSTcC.js → BackgroundAgents-DFb6J2wQ.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DDOAK8RK.js → Backup-CC7YBhyj.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CtCsVVke.js → BaseInput-RkgTNVtj.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DpNj0vnd.js → Chat-hHEmGZy-.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZta3Ftr.js → ChatBubbleRenderer-CAP9YhHv.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DoHVNSXq.js → Checkbox-z5wXOQP1.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DlVl7Qmv.js → Codegen-DVaDJnxy.js} +1 -1
- package/src/assets/web-panel/assets/{Col-GIqoPYty.js → Col-CJmaRDR5.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CIAPEVji.js → Community-DF29_2bI.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BN7JiI_h.js → Compact-DtkQkBzn.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CqUWCM9B.js → Compliance-xoHDxp7I.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-A4iPp7A4.js → Cowork-BUm-R7ht.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-B2zeXXWb.js → Cron-Cgl14ZkB.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BR2xmWr8.js → Crosschain-BNcbmoqh.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BzFDjrIv.js → DID-Bwja0Zo9.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DKtDo3yA.js → Dashboard-Bk5XjEdJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-RNx43_YG.js → Dropdown-Ci36Oitq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-SwMt4Rcy.js → EmailListRenderer--U04ZN4q.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CsYebZ4X.js → FamilyGuardDashboard-B2gNPR2s.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BmRKvBTy.js → Federation-C4KghYn7.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CVxGTA-0.js → FormItemContext-QL7x_Q_0.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-x2iXpyB7.js → GenericCardRenderer-F3Ryltwm.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DfqbkiOP.js → Git-2HassK5k.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-RuHyyj5O.js → Governance-DoBIp7oe.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Bn7uZ7rI.js → Inference--W01QhZ-.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DlP7XefZ.js → KnowledgeGraph-DmvwqGpT.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BYNdOiCd.js → Logs-BBJ4yAel.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-aMtgN-R6.js → MarkdownRenderer-BLCZMzhJ.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-JQYmDqM8.js → Marketplace-D5lNngSI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Cyx4yZ-9.js → McpTools-CkEVOMKH.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-VDuGZTN2.js → Memory-B1Ow1xVE.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-_yKo97gi.js → MobileBridge-SgYbUfb6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CXRXW3q7.js → MobileProjects-BWdOvQAj.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-acTGtfLH.js → Mtc-DcTXSSma.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-ClpjXfkY.js → MtcAudit-Dt3WG4oj.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-_6Tn3o96.js → Multisig-B9B38UZx.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CK7pbZzP.js → NLProgramming-B0S4GGvG.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Czy6V_P8.js → Notes-DZ5U58Ng.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Cqx-UVv3.js → NotificationSettings-PjL5ZW34.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C69WDxGy.js → OrderTableRenderer-DrAX2ESt.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DFnA9vLQ.js → Organization-Cv1GYs-c.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-ReZkk2yT.js → Overflow-Bbkp_PL3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DzjSI0aI.js → P2P-BD8QUgoZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DKTf5xIP.js → PdhVaultBrowser-BO-2PeaB.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DhQ5klhR.js → Permissions-D_OPVa02.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CUL1iHrW.js → PersonalDataHub-mh-pEmaB.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DU88iu4N.js → Pipeline-6JC2EDdb.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Bw-sYfyK.js → Privacy-CUshzUAG.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Bq3Q5y0z.js → ProjectInit-Cs2Aalri.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C8spzpho.js → ProjectSettings-1ak49tcj.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BV4oApsm.js +1 -0
- package/src/assets/web-panel/assets/{Providers-Bf4eTP9W.js → Providers-CXXxvafH.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Dme9BDU1.js → QrScannerModal-PTcDsF58.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CIXfv1rE.js → QuickAsk-CDwucrJw.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CIxvdeZ-.js → Recommend-CJlLVRXR.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C6k7MExI.js → RemoteSession-DlN-ETsO.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CISDz2bl.js → Reputation-DI23LR6m.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CdnDxXyf.js → Row-Cl7UQMrM.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C-QpRHsl.js → RssFeed-CexHNfh8.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DBjaGzSn.js → Search-BneBWvRN.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CQKukOeE.js → Security-DX7Phgrp.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DZz86jWb.js → Services-BWXO8Mte.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-jb8RMK0d.js → Skeleton-DCyOjKPJ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CdY76fZG.js → Skills-CXqERsTB.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C_CSiZX9.js → Sla-bhbrbz8E.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-3IPU0HBZ.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-qvimGO0O.js → SyncSettings-DBNTQLbM.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Dng5LPJC.js → Tasks-quP0bkN-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Bwqq5Wll.js → Templates-BouWmgho.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DNWuwP7Z.js → Tenant-qv0D9s7z.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B-6Othpp.js → Terminal-BxvbE8BB.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DKpKZfzx.js → TimelineRenderer-D-IeLnJ2.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DzfGMd2J.js → Tokens-CFMKKyVG.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BXwUi74J.js → Trigger-L8as3UuJ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BNzUq4ui.js → Trust-MxmA4VZG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BYugxww4.js → UkeySign-C0VvJYRi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DdauhtSJ.js → VideoEditing-Bf5QZj8N.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CSC9bCe_.js → Wallet-BIJ9riRz.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Q8AN3bTO.js → WebAuthn-B8p9Mhkw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-d-LBGCuA.js → WorkflowEditor-BVsuaPen.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BLVDCgsZ.js → chat-BfkjoKpV.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BZlPSEHl.js → colors-DeAlsWK8.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-IWseP6R6.js → compact-item-Cs97bz5j.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DnVRV3Ao.js → createContext-BqbG60LH.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLQZA_Wn.js → hasIn-i_RY0b1X.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_N4-Gq9.js → index-0-agYlWL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DB0MJgF_.js → index-79V_zv1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOZJBTWZ.js → index-B-3tEpTc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUq0c7gq.js → index-B-JVTrpr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dy0R5ZED.js → index-B1CbOH1i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bxp7uDD8.js → index-B5UkyA3c.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpVrK6fr.js → index-BESLFt4q.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT9vVN-X.js → index-BOVkf1K3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOoQpuQY.js → index-BYUVuwKt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CINJQEj6.js → index-BcUXAB2s.js} +1 -1
- package/src/assets/web-panel/assets/{index-CarMTIyQ.js → index-BkBi5TmC.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5dgnilC.js → index-Bm7lLR4c.js} +1 -1
- package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +1 -0
- package/src/assets/web-panel/assets/{index-CbQNL0ue.js → index-BuLQobzW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpBaJ3TD.js → index-CE2VtTlp.js} +1 -1
- package/src/assets/web-panel/assets/{index-jWb70ZgZ.js → index-CED2PL_y.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEu2lMBy.js → index-CG3PRyAL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocEhinm.js → index-CIG8fQ2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_LQodeJ.js → index-COdGA-8n.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZpYeqrh.js → index-CeWoNHau.js} +1 -1
- package/src/assets/web-panel/assets/{index-gt8bNefW.js → index-Cf3WCLmy.js} +1 -1
- package/src/assets/web-panel/assets/{index-N_GoWZX4.js → index-CgsqdnRV.js} +1 -1
- package/src/assets/web-panel/assets/index-ChVT0p9u.js +1 -0
- package/src/assets/web-panel/assets/{index-CZDekj7T.js → index-D2B4VPyP.js} +1 -1
- package/src/assets/web-panel/assets/{index-YSw_gxlZ.js → index-D9evtvg2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTbxp4UH.js → index-DUUrv141.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bbj2NS_I.js → index-DiSkx4UB.js} +3 -3
- package/src/assets/web-panel/assets/{index-BZs5bBnk.js → index-Dk2qouAz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DalA0TKC.js → index-Dk7-v4hP.js} +1 -1
- package/src/assets/web-panel/assets/{index-1jeAudHd.js → index-DmbfhJUP.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1ifZtXo.js → index-DqBgBpgp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhA3QEni.js → index-PT_5sBMA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSjilZ9L.js → index-U4f5izHz.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2FqfMvU.js → index-YXgLng0K.js} +1 -1
- package/src/assets/web-panel/assets/{index-COev0W2g.js → index-ZxO_-E8a.js} +1 -1
- package/src/assets/web-panel/assets/{index-M7wxfAbm.js → index-dIHr4B_u.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjWnONdy.js → index-gyFpvL7l.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dkwsi1Kn.js → index-s1opAL-M.js} +1 -1
- package/src/assets/web-panel/assets/{index-hwVDeoC0.js → index-yObj1O4n.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Co67Kqmb.js → initDefaultProps-C9h9yKL3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BMOxQSF_.js → motion-BvmLsPdu.js} +1 -1
- package/src/assets/web-panel/assets/{move-C5fWDw-N.js → move-CoL4ekU1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BJevpL0e.js → mtc-parser-C4pMy6xV.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCzNU8s6.js → omit-DPuJfNxx.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CybWvDPk.js → pickAttrs-D0KEeaK2.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BMWS9HnW.js → placementArrow-BpkyINxt.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BoL2UnGY.js → responsiveObserve-DQI_wOq8.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DWNfXraU.js → slide-DYfdDiRg.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BquTM3tX.js → statusUtils-B5dtvb0D.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BEkCleim.js → styleChecker-CF310mSj.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Oecnq7EA.js → useFlexGapSupport-D1Qp4BLp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CkDffJ4F.js → useFs-C7_iyvJK.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-qbvKXesB.js → usePersonalDataHub-DZUt_fIm.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C6v8n6mA.js → vnode-D3dsi0lZ.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-4CTeFILV.js → zoom-WdbOlvdQ.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agent.js +90 -1
- package/src/commands/chat.js +11 -2
- package/src/commands/compact.js +106 -23
- package/src/commands/loop.js +28 -10
- package/src/commands/routine.js +1 -1
- package/src/commands/scheduler-daemon.js +359 -2
- package/src/commands/session-observability.js +305 -0
- package/src/commands/session.js +38 -0
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/ws-agent-handler.js +439 -5
- package/src/harness/jsonl-session-store.js +463 -125
- package/src/harness/prompt-compressor.js +86 -13
- package/src/harness/provider-backed-compaction.js +100 -27
- package/src/lib/advisor-runtime.js +3 -1
- package/src/lib/agent-schedule-store.js +414 -4
- package/src/lib/agent-team/team-adjudication.js +40 -1
- package/src/lib/automation-engine.js +494 -0
- package/src/lib/background-agent-supervisor.js +793 -215
- package/src/lib/background-command-argv.js +24 -0
- package/src/lib/background-interaction-journal.js +361 -52
- package/src/lib/background-interaction-resolver.js +32 -0
- package/src/lib/background-launch-profile.js +2 -0
- package/src/lib/causal-observability.js +2661 -0
- package/src/lib/chat-session-admission.js +76 -0
- package/src/lib/cost-budget.js +5 -7
- package/src/lib/cowork/ab-comparator-cli.js +11 -0
- package/src/lib/cowork/debate-review-cli.js +12 -0
- package/src/lib/cowork-adapter.js +159 -38
- package/src/lib/cowork-cron.js +357 -1
- package/src/lib/cowork-task-runner.js +112 -17
- package/src/lib/delivery-coordinator.js +142 -0
- package/src/lib/direct-model-usage.js +121 -0
- package/src/lib/file-lines.js +183 -47
- package/src/lib/llm-pricing.js +12 -17
- package/src/lib/loop.js +26 -2
- package/src/lib/routine-store.js +380 -21
- package/src/lib/runtime-usage-ledger.js +182 -0
- package/src/lib/scheduler-kernel/agenda-adapter.js +308 -3
- package/src/lib/scheduler-kernel/automation-adapter.js +410 -8
- package/src/lib/scheduler-kernel/automation-event-adapter.js +36 -0
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +285 -3
- package/src/lib/scheduler-kernel/loop-adapter.js +541 -6
- package/src/lib/scheduler-kernel/migration-admin.js +346 -0
- package/src/lib/scheduler-kernel/migration-rollback-admin.js +932 -0
- package/src/lib/scheduler-kernel/routine-adapter.js +394 -20
- package/src/lib/scheduler-kernel/runtime.js +63 -1
- package/src/lib/scheduler-kernel/source-locator-path.js +142 -0
- package/src/lib/scheduler-kernel/store.js +2586 -85
- package/src/lib/session-host-lease.js +171 -2
- package/src/lib/session-host-snapshot.js +14 -3
- package/src/lib/session-persistence-failure.js +1 -0
- package/src/lib/session-tail.js +179 -34
- package/src/lib/session-transcript-structure.js +96 -0
- package/src/lib/sub-agent-context.js +328 -22
- package/src/lib/ws-agent-handler.js +4 -1
- package/src/repl/agent-repl.js +732 -91
- package/src/repl/chat-repl.js +5 -1
- package/src/runtime/agent-core.js +1272 -207
- package/src/runtime/fallback-model.js +5 -0
- package/src/runtime/headless-runner.js +324 -82
- package/src/runtime/headless-stream.js +232 -26
- package/src/runtime/policies/agent-policy.js +4 -0
- package/src/workers/background-agent-worker.js +403 -96
- package/src/assets/web-panel/assets/Projects-Dd-xuPIc.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-C7tT9iad.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Jzu62CLC.js +0 -1
- package/src/assets/web-panel/assets/index-Zb2MeDSy.js +0 -1
- package/src/assets/web-panel/assets/index-fikqml4k.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { nextCronTime } from "../agent-schedule-store.js";
|
|
3
3
|
import {
|
|
4
4
|
automationExecutionAuthorityDigest,
|
|
@@ -11,10 +11,19 @@ import {
|
|
|
11
11
|
EXECUTION_STATUS,
|
|
12
12
|
FLOW_STATUS,
|
|
13
13
|
TRIGGER_TYPE,
|
|
14
|
+
automationEffectiveSchedulerFlow,
|
|
15
|
+
automationMigrationSourceDigest,
|
|
16
|
+
automationMigrationSourceSnapshot,
|
|
14
17
|
executeFlow,
|
|
18
|
+
adjudicateAutomationExecution,
|
|
15
19
|
getExecution,
|
|
16
20
|
getFlow,
|
|
21
|
+
getAutomationSchedulerMigration,
|
|
17
22
|
listFlows,
|
|
23
|
+
listAutomationSchedulerFlows,
|
|
24
|
+
prepareAutomationSchedulerMigration,
|
|
25
|
+
restoreAutomationSchedulerMigration,
|
|
26
|
+
retireAutomationSchedulerMigration,
|
|
18
27
|
} from "../automation-engine.js";
|
|
19
28
|
import {
|
|
20
29
|
SchedulerKernelError,
|
|
@@ -24,15 +33,43 @@ import {
|
|
|
24
33
|
normalizeJson,
|
|
25
34
|
} from "./contract.js";
|
|
26
35
|
import { SchedulerRuntime } from "./runtime.js";
|
|
36
|
+
import {
|
|
37
|
+
schedulerJobDefinitionDigest,
|
|
38
|
+
schedulerMigrationSourceDigest,
|
|
39
|
+
} from "./store.js";
|
|
27
40
|
import {
|
|
28
41
|
bindSchedulerAuthorityPolicy,
|
|
29
42
|
createSchedulerAuthorityResolver,
|
|
30
43
|
} from "./authority-resolver.js";
|
|
44
|
+
import { canonicalSchedulerSourcePath } from "./source-locator-path.js";
|
|
31
45
|
|
|
32
46
|
export const AUTOMATION_SCHEDULER_KIND = "automation";
|
|
33
47
|
export const AUTOMATION_SCHEDULER_CAPABILITY = "automation.execute";
|
|
34
48
|
export const AUTOMATION_SCHEDULER_CHANNEL = "scheduled";
|
|
35
49
|
export const AUTOMATION_SCHEDULER_MAX_CATCHUP_STEPS = 527_040;
|
|
50
|
+
const AUTOMATION_MEMORY_DATABASE_IDENTITIES = new WeakMap();
|
|
51
|
+
|
|
52
|
+
export function automationDatabaseIdentity(db, pathOptions = undefined) {
|
|
53
|
+
if (typeof db?.databasePath === "string" && db.databasePath.length > 0) {
|
|
54
|
+
return canonicalSchedulerSourcePath(db.databasePath, pathOptions);
|
|
55
|
+
}
|
|
56
|
+
const main = db
|
|
57
|
+
.prepare("PRAGMA database_list")
|
|
58
|
+
.all()
|
|
59
|
+
.find((entry) => entry.name === "main");
|
|
60
|
+
const file = typeof main?.file === "string" ? main.file : "";
|
|
61
|
+
if (!file) {
|
|
62
|
+
// In-memory stores exist only for one process and are never safe to reopen
|
|
63
|
+
// from an operator command. Keep their scope isolated to this handle.
|
|
64
|
+
let identity = AUTOMATION_MEMORY_DATABASE_IDENTITIES.get(db);
|
|
65
|
+
if (!identity) {
|
|
66
|
+
identity = `memory:${randomUUID()}`;
|
|
67
|
+
AUTOMATION_MEMORY_DATABASE_IDENTITIES.set(db, identity);
|
|
68
|
+
}
|
|
69
|
+
return identity;
|
|
70
|
+
}
|
|
71
|
+
return canonicalSchedulerSourcePath(file, pathOptions);
|
|
72
|
+
}
|
|
36
73
|
|
|
37
74
|
function automationError(
|
|
38
75
|
code,
|
|
@@ -231,6 +268,311 @@ export function syncAutomationSchedulerJob(
|
|
|
231
268
|
}
|
|
232
269
|
}
|
|
233
270
|
|
|
271
|
+
export function migrateAutomationFlow({
|
|
272
|
+
db,
|
|
273
|
+
schedulerStore,
|
|
274
|
+
flow,
|
|
275
|
+
executionAuthority,
|
|
276
|
+
}) {
|
|
277
|
+
if (db?.__isSqlJsCompat === true) {
|
|
278
|
+
throw automationError(
|
|
279
|
+
"AUTOMATION_SCHEDULER_MIGRATION_DURABILITY_UNAVAILABLE",
|
|
280
|
+
"Automation scheduler migration requires native SQLite durability",
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const effective = automationEffectiveSchedulerFlow(db, flow);
|
|
284
|
+
const snapshot = automationFlowSnapshot(effective);
|
|
285
|
+
const database = automationDatabaseIdentity(db);
|
|
286
|
+
const sourceScope = { store: "automation-engine", database };
|
|
287
|
+
const sourceLocator = {
|
|
288
|
+
schemaVersion: 1,
|
|
289
|
+
type: "automation-database",
|
|
290
|
+
database,
|
|
291
|
+
};
|
|
292
|
+
const desired = buildAutomationSchedulerJob(effective, executionAuthority);
|
|
293
|
+
desired.authority = bindSchedulerAuthorityPolicy(
|
|
294
|
+
schedulerStore,
|
|
295
|
+
desired.authority,
|
|
296
|
+
);
|
|
297
|
+
const sourceSnapshot = automationMigrationSourceSnapshot(effective);
|
|
298
|
+
const sourceDigest = automationMigrationSourceDigest(effective);
|
|
299
|
+
const targetJobDigest = schedulerJobDefinitionDigest(desired);
|
|
300
|
+
const marker = getAutomationSchedulerMigration(db, snapshot.id);
|
|
301
|
+
let prepared;
|
|
302
|
+
if (marker) {
|
|
303
|
+
if (
|
|
304
|
+
marker.schemaVersion !== 1 ||
|
|
305
|
+
!["prepared", "retired"].includes(marker.state) ||
|
|
306
|
+
marker.sourceDigest !== sourceDigest ||
|
|
307
|
+
marker.targetJobId !== desired.id ||
|
|
308
|
+
(marker.state === "prepared" && marker.retirementToken !== null)
|
|
309
|
+
) {
|
|
310
|
+
throw automationError(
|
|
311
|
+
"AUTOMATION_SCHEDULER_MIGRATION_CONFLICT",
|
|
312
|
+
`Automation migration marker is invalid: ${snapshot.id}`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
prepared = schedulerStore.getDomainMigration(marker.migrationId);
|
|
316
|
+
if (
|
|
317
|
+
prepared.entries.length !== 1 ||
|
|
318
|
+
prepared.entries[0].domain !== "automation" ||
|
|
319
|
+
prepared.entries[0].sourceId !== snapshot.id ||
|
|
320
|
+
prepared.entries[0].sourceDigest !==
|
|
321
|
+
schedulerMigrationSourceDigest(sourceSnapshot) ||
|
|
322
|
+
prepared.entries[0].targetJobId !== desired.id ||
|
|
323
|
+
prepared.entries[0].targetJobDigest !== targetJobDigest ||
|
|
324
|
+
(marker.state === "retired" &&
|
|
325
|
+
prepared.entries[0].retirementToken !== marker.retirementToken)
|
|
326
|
+
) {
|
|
327
|
+
throw automationError(
|
|
328
|
+
"AUTOMATION_SCHEDULER_MIGRATION_CONFLICT",
|
|
329
|
+
`Automation migration marker does not match its journal: ${snapshot.id}`,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
schedulerStore.bindDomainMigrationSourceLocator({
|
|
333
|
+
migrationId: prepared.id,
|
|
334
|
+
entryId: prepared.entries[0].entryId,
|
|
335
|
+
sourceLocator,
|
|
336
|
+
expectedSourceDigest: prepared.entries[0].sourceDigest,
|
|
337
|
+
expectedTargetJobId: desired.id,
|
|
338
|
+
});
|
|
339
|
+
prepared = schedulerStore.getDomainMigration(prepared.id);
|
|
340
|
+
} else {
|
|
341
|
+
const unboundLegacy = schedulerStore.getActiveDomainMigrationBySource({
|
|
342
|
+
domain: "automation",
|
|
343
|
+
sourceScope: { store: "automation-engine", database: "current" },
|
|
344
|
+
sourceId: snapshot.id,
|
|
345
|
+
});
|
|
346
|
+
if (unboundLegacy) {
|
|
347
|
+
throw automationError(
|
|
348
|
+
"AUTOMATION_SCHEDULER_MIGRATION_SOURCE_UNBOUND",
|
|
349
|
+
`Legacy Automation migration has no source marker and cannot be bound safely: ${snapshot.id}`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
prepared = schedulerStore.prepareDomainMigration({
|
|
353
|
+
entries: [
|
|
354
|
+
{
|
|
355
|
+
domain: "automation",
|
|
356
|
+
sourceId: snapshot.id,
|
|
357
|
+
sourceScope,
|
|
358
|
+
sourceLocator,
|
|
359
|
+
source: sourceSnapshot,
|
|
360
|
+
targetJob: desired,
|
|
361
|
+
rollbackStrategy: "disable",
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (
|
|
367
|
+
prepared.entries.length !== 1 ||
|
|
368
|
+
prepared.entries[0].domain !== "automation"
|
|
369
|
+
) {
|
|
370
|
+
throw automationError(
|
|
371
|
+
"AUTOMATION_SCHEDULER_MIGRATION_DOMAIN_MISMATCH",
|
|
372
|
+
"Automation flow belongs to an invalid mixed-domain migration",
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (
|
|
376
|
+
prepared.entries[0].sourceDigest !==
|
|
377
|
+
schedulerMigrationSourceDigest(sourceSnapshot) ||
|
|
378
|
+
prepared.entries[0].targetJobId !== desired.id ||
|
|
379
|
+
prepared.entries[0].targetJobDigest !== targetJobDigest
|
|
380
|
+
) {
|
|
381
|
+
throw automationError(
|
|
382
|
+
"AUTOMATION_SCHEDULER_MIGRATION_SOURCE_CHANGED",
|
|
383
|
+
`Automation flow changed after migration started: ${snapshot.id}`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
let migration = prepared;
|
|
387
|
+
if (["rolling_back", "rolled_back"].includes(migration.state)) {
|
|
388
|
+
throw automationError(
|
|
389
|
+
"AUTOMATION_SCHEDULER_MIGRATION_STATE_CONFLICT",
|
|
390
|
+
`Automation migration cannot resume from state ${migration.state}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
if (migration.state === "retired") {
|
|
394
|
+
retireAutomationSchedulerMigration(db, snapshot.id, {
|
|
395
|
+
migrationId: migration.id,
|
|
396
|
+
sourceDigest,
|
|
397
|
+
targetJobId: desired.id,
|
|
398
|
+
retirementToken: migration.entries[0].retirementToken,
|
|
399
|
+
});
|
|
400
|
+
return migration;
|
|
401
|
+
}
|
|
402
|
+
if (migration.state === "prepared") {
|
|
403
|
+
prepareAutomationSchedulerMigration(db, snapshot.id, {
|
|
404
|
+
migrationId: migration.id,
|
|
405
|
+
sourceDigest,
|
|
406
|
+
targetJobId: desired.id,
|
|
407
|
+
});
|
|
408
|
+
migration = schedulerStore.applyDomainMigration(migration.id);
|
|
409
|
+
}
|
|
410
|
+
if (migration.state === "applied") {
|
|
411
|
+
migration = schedulerStore.verifyDomainMigration(migration.id, {
|
|
412
|
+
sources: [
|
|
413
|
+
{ entryId: migration.entries[0].entryId, source: sourceSnapshot },
|
|
414
|
+
],
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const retiring =
|
|
418
|
+
migration.state === "retiring"
|
|
419
|
+
? migration
|
|
420
|
+
: schedulerStore.beginDomainMigrationRetirement(migration.id);
|
|
421
|
+
const entry = retiring.entries[0];
|
|
422
|
+
const retired = retireAutomationSchedulerMigration(db, snapshot.id, {
|
|
423
|
+
migrationId: migration.id,
|
|
424
|
+
sourceDigest,
|
|
425
|
+
targetJobId: desired.id,
|
|
426
|
+
retirementToken: entry.retirementToken,
|
|
427
|
+
});
|
|
428
|
+
schedulerStore.confirmDomainMigrationEntryRetired({
|
|
429
|
+
migrationId: migration.id,
|
|
430
|
+
entryId: entry.entryId,
|
|
431
|
+
retirementToken: entry.retirementToken,
|
|
432
|
+
source: retired,
|
|
433
|
+
});
|
|
434
|
+
return schedulerStore.getDomainMigration(migration.id);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function assertAutomationRollbackBinding({
|
|
438
|
+
db,
|
|
439
|
+
migration,
|
|
440
|
+
migrationId,
|
|
441
|
+
database,
|
|
442
|
+
}) {
|
|
443
|
+
if (
|
|
444
|
+
migration?.id !== migrationId ||
|
|
445
|
+
!Array.isArray(migration.entries) ||
|
|
446
|
+
migration.entries.length === 0 ||
|
|
447
|
+
migration.entries.some((entry) => entry.domain !== "automation")
|
|
448
|
+
) {
|
|
449
|
+
throw automationError(
|
|
450
|
+
"AUTOMATION_SCHEDULER_MIGRATION_DOMAIN_MISMATCH",
|
|
451
|
+
"Automation rollback cannot operate on a mixed-domain migration",
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
for (const entry of migration.entries) {
|
|
455
|
+
const locator = entry.sourceLocator;
|
|
456
|
+
if (
|
|
457
|
+
locator?.schemaVersion !== 1 ||
|
|
458
|
+
locator.type !== "automation-database" ||
|
|
459
|
+
locator.database !== database
|
|
460
|
+
) {
|
|
461
|
+
throw automationError(
|
|
462
|
+
"AUTOMATION_SCHEDULER_MIGRATION_SOURCE_UNBOUND",
|
|
463
|
+
`Automation rollback source locator does not match its database: ${entry.sourceId}`,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const current = getFlow(db, entry.sourceId);
|
|
467
|
+
if (!current) {
|
|
468
|
+
throw automationError(
|
|
469
|
+
"AUTOMATION_SCHEDULER_MIGRATION_SOURCE_CHANGED",
|
|
470
|
+
`Automation rollback source does not exist: ${entry.sourceId}`,
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
const marker = getAutomationSchedulerMigration(db, entry.sourceId);
|
|
474
|
+
const effective = automationEffectiveSchedulerFlow(db, current);
|
|
475
|
+
if (
|
|
476
|
+
schedulerMigrationSourceDigest(
|
|
477
|
+
automationMigrationSourceSnapshot(effective),
|
|
478
|
+
) !== entry.sourceDigest
|
|
479
|
+
) {
|
|
480
|
+
throw automationError(
|
|
481
|
+
"AUTOMATION_SCHEDULER_MIGRATION_SOURCE_CHANGED",
|
|
482
|
+
`Automation rollback source changed: ${entry.sourceId}`,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
if (!marker) {
|
|
486
|
+
if (
|
|
487
|
+
!["prepared", "rolling_back", "rolled_back"].includes(migration.state)
|
|
488
|
+
) {
|
|
489
|
+
throw automationError(
|
|
490
|
+
"AUTOMATION_SCHEDULER_MIGRATION_CONFLICT",
|
|
491
|
+
`Automation rollback source marker is missing: ${entry.sourceId}`,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (
|
|
497
|
+
marker.schemaVersion !== 1 ||
|
|
498
|
+
!["prepared", "retired"].includes(marker.state) ||
|
|
499
|
+
marker.migrationId !== migrationId ||
|
|
500
|
+
marker.targetJobId !== entry.targetJobId ||
|
|
501
|
+
marker.sourceDigest !== automationMigrationSourceDigest(effective) ||
|
|
502
|
+
(marker.state === "prepared" && marker.retirementToken !== null) ||
|
|
503
|
+
(marker.state === "retired" &&
|
|
504
|
+
marker.retirementToken !== entry.retirementToken)
|
|
505
|
+
) {
|
|
506
|
+
throw automationError(
|
|
507
|
+
"AUTOMATION_SCHEDULER_MIGRATION_CONFLICT",
|
|
508
|
+
`Automation rollback source marker does not match its journal: ${entry.sourceId}`,
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return migration;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function rollbackAutomationMigration({
|
|
516
|
+
db,
|
|
517
|
+
schedulerStore,
|
|
518
|
+
migrationId,
|
|
519
|
+
}) {
|
|
520
|
+
const database = automationDatabaseIdentity(db);
|
|
521
|
+
assertAutomationRollbackBinding({
|
|
522
|
+
db,
|
|
523
|
+
migration: schedulerStore.getDomainMigration(migrationId),
|
|
524
|
+
migrationId,
|
|
525
|
+
database,
|
|
526
|
+
});
|
|
527
|
+
const migration = assertAutomationRollbackBinding({
|
|
528
|
+
db,
|
|
529
|
+
migration: schedulerStore.beginDomainMigrationRollback(migrationId),
|
|
530
|
+
migrationId,
|
|
531
|
+
database,
|
|
532
|
+
});
|
|
533
|
+
if (migration.state === "rolled_back") return migration;
|
|
534
|
+
for (const entry of migration.entries) {
|
|
535
|
+
if (entry.state === "rolled_back") continue;
|
|
536
|
+
schedulerStore.restoreDomainMigrationEntrySource({
|
|
537
|
+
migrationId,
|
|
538
|
+
entryId: entry.entryId,
|
|
539
|
+
retirementToken: entry.retirementToken,
|
|
540
|
+
restoreSource: () => {
|
|
541
|
+
const current = getFlow(db, entry.sourceId);
|
|
542
|
+
const marker = getAutomationSchedulerMigration(db, entry.sourceId);
|
|
543
|
+
let restored;
|
|
544
|
+
if (!marker) {
|
|
545
|
+
const source = automationMigrationSourceSnapshot(current);
|
|
546
|
+
if (schedulerMigrationSourceDigest(source) !== entry.sourceDigest) {
|
|
547
|
+
throw automationError(
|
|
548
|
+
"AUTOMATION_SCHEDULER_MIGRATION_SOURCE_CHANGED",
|
|
549
|
+
`Automation flow changed after rollback source restoration: ${entry.sourceId}`,
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
restored = current;
|
|
553
|
+
} else {
|
|
554
|
+
const effective = automationEffectiveSchedulerFlow(db, current);
|
|
555
|
+
restored = restoreAutomationSchedulerMigration(db, entry.sourceId, {
|
|
556
|
+
migrationId,
|
|
557
|
+
sourceDigest: automationMigrationSourceDigest(effective),
|
|
558
|
+
targetJobId: entry.targetJobId,
|
|
559
|
+
// The scheduler journal allocates its retirement token before the
|
|
560
|
+
// Automation source transaction advances its marker from prepared
|
|
561
|
+
// to retired. A crash in that window therefore has a journal token
|
|
562
|
+
// but an intentionally null source-marker token. Keep the source
|
|
563
|
+
// marker authoritative for this phase; the restore primitive still
|
|
564
|
+
// verifies migration/source/target identity before deleting it.
|
|
565
|
+
retirementToken:
|
|
566
|
+
marker.state === "prepared" ? null : entry.retirementToken,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return automationMigrationSourceSnapshot(restored);
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
return schedulerStore.getDomainMigration(migrationId);
|
|
574
|
+
}
|
|
575
|
+
|
|
234
576
|
function latestEnqueuedScheduledFor(schedulerStore, jobId) {
|
|
235
577
|
const event = schedulerStore
|
|
236
578
|
.history({ jobId, limit: 100 })
|
|
@@ -399,6 +741,37 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
399
741
|
}
|
|
400
742
|
return {
|
|
401
743
|
kind: AUTOMATION_SCHEDULER_KIND,
|
|
744
|
+
async adjudicate({ occurrence, adjudication }) {
|
|
745
|
+
const payload = occurrence?.payload;
|
|
746
|
+
const expected = automationFlowSnapshot(payload?.flow);
|
|
747
|
+
if (payload?.snapshotDigest !== automationFlowSnapshotDigest(expected)) {
|
|
748
|
+
throw automationError(
|
|
749
|
+
"AUTOMATION_SCHEDULER_ADJUDICATION_SNAPSHOT_INVALID",
|
|
750
|
+
`Automation adjudication snapshot is invalid: ${occurrence.id}`,
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
adjudicateAutomationExecution(
|
|
754
|
+
db,
|
|
755
|
+
automationSchedulerExecutionId(occurrence.id),
|
|
756
|
+
{
|
|
757
|
+
flowId: expected.id,
|
|
758
|
+
triggerType: TRIGGER_TYPE.SCHEDULE,
|
|
759
|
+
decision: adjudication.decision,
|
|
760
|
+
requestId: adjudication.requestId,
|
|
761
|
+
},
|
|
762
|
+
);
|
|
763
|
+
if (adjudication.decision === "confirmed_applied") {
|
|
764
|
+
return {
|
|
765
|
+
settled: true,
|
|
766
|
+
result: {
|
|
767
|
+
executionId: automationSchedulerExecutionId(occurrence.id),
|
|
768
|
+
status: "adjudicated-applied",
|
|
769
|
+
adjudicationRequestId: adjudication.requestId,
|
|
770
|
+
},
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
return { continue: true };
|
|
774
|
+
},
|
|
402
775
|
async execute({ occurrence }) {
|
|
403
776
|
const payload = occurrence?.payload;
|
|
404
777
|
const expected = automationFlowSnapshot(payload?.flow);
|
|
@@ -426,13 +799,14 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
426
799
|
`Automation flow disappeared before execution: ${expected.id}`,
|
|
427
800
|
);
|
|
428
801
|
}
|
|
429
|
-
|
|
802
|
+
const effectiveCurrent = automationEffectiveSchedulerFlow(db, current);
|
|
803
|
+
if (effectiveCurrent.status !== FLOW_STATUS.ACTIVE) {
|
|
430
804
|
throw automationError(
|
|
431
805
|
"AUTOMATION_SCHEDULER_FLOW_NOT_ACTIVE",
|
|
432
806
|
`Automation flow is not active: ${expected.id}`,
|
|
433
807
|
);
|
|
434
808
|
}
|
|
435
|
-
if (automationFlowSnapshotDigest(
|
|
809
|
+
if (automationFlowSnapshotDigest(effectiveCurrent) !== expectedDigest) {
|
|
436
810
|
throw automationError(
|
|
437
811
|
"AUTOMATION_SCHEDULER_STALE_SNAPSHOT",
|
|
438
812
|
`Automation flow changed after occurrence enqueue: ${expected.id}`,
|
|
@@ -441,7 +815,7 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
441
815
|
|
|
442
816
|
reserveAutomationExecutionAuthority(
|
|
443
817
|
db,
|
|
444
|
-
|
|
818
|
+
effectiveCurrent,
|
|
445
819
|
occurrence.id,
|
|
446
820
|
payload.executionAuthority,
|
|
447
821
|
payload.executionAuthorityDigest,
|
|
@@ -490,6 +864,7 @@ export class AutomationSchedulerBridge {
|
|
|
490
864
|
leaseMs,
|
|
491
865
|
renewIntervalMs,
|
|
492
866
|
authorityResolver,
|
|
867
|
+
migrateLegacy = true,
|
|
493
868
|
} = {}) {
|
|
494
869
|
if (typeof now !== "function") {
|
|
495
870
|
throw automationError(
|
|
@@ -500,6 +875,7 @@ export class AutomationSchedulerBridge {
|
|
|
500
875
|
this.db = db;
|
|
501
876
|
this.schedulerStore = schedulerStore;
|
|
502
877
|
this.now = now;
|
|
878
|
+
this.migrateLegacy = migrateLegacy !== false;
|
|
503
879
|
this.runtime = new SchedulerRuntime({
|
|
504
880
|
store: schedulerStore,
|
|
505
881
|
adapters: [createAutomationSchedulerAdapter({ db, now })],
|
|
@@ -545,10 +921,36 @@ export class AutomationSchedulerBridge {
|
|
|
545
921
|
if (signal?.aborted) return results;
|
|
546
922
|
|
|
547
923
|
const now = normalizeEpochMs(Number(this.now()), "now");
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
924
|
+
if (this.migrateLegacy) {
|
|
925
|
+
const candidates = listFlows(this.db, {
|
|
926
|
+
status: FLOW_STATUS.ACTIVE,
|
|
927
|
+
limit: 10_000,
|
|
928
|
+
}).filter((flow) => typeof flow.schedule === "string" && flow.schedule);
|
|
929
|
+
for (const flow of candidates) {
|
|
930
|
+
try {
|
|
931
|
+
migrateAutomationFlow({
|
|
932
|
+
db: this.db,
|
|
933
|
+
schedulerStore: this.schedulerStore,
|
|
934
|
+
flow,
|
|
935
|
+
executionAuthority: automationExecutionAuthoritySnapshot(
|
|
936
|
+
this.db,
|
|
937
|
+
flow,
|
|
938
|
+
),
|
|
939
|
+
});
|
|
940
|
+
} catch (error) {
|
|
941
|
+
if (!String(error?.code || "").startsWith("AUTOMATION_EXECUTION_")) {
|
|
942
|
+
throw error;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
const flows = [
|
|
948
|
+
...listFlows(this.db, {
|
|
949
|
+
status: FLOW_STATUS.ACTIVE,
|
|
950
|
+
limit: 10_000,
|
|
951
|
+
}),
|
|
952
|
+
...listAutomationSchedulerFlows(this.db, { limit: 10_000 }),
|
|
953
|
+
].filter((flow) => typeof flow.schedule === "string" && flow.schedule);
|
|
552
954
|
for (const flow of flows) {
|
|
553
955
|
let occurrence;
|
|
554
956
|
try {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
EXECUTION_STATUS,
|
|
12
12
|
FLOW_STATUS,
|
|
13
13
|
TRIGGER_TYPE,
|
|
14
|
+
adjudicateAutomationExecution,
|
|
14
15
|
executeFlow,
|
|
15
16
|
getExecution,
|
|
16
17
|
getFlow,
|
|
@@ -552,6 +553,41 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
552
553
|
}
|
|
553
554
|
return {
|
|
554
555
|
kind: AUTOMATION_EVENT_KIND,
|
|
556
|
+
async adjudicate({ occurrence, adjudication }) {
|
|
557
|
+
const payload = occurrence?.payload;
|
|
558
|
+
const expectedFlow = automationEventFlowSnapshot(payload?.flow);
|
|
559
|
+
const expectedTrigger = automationEventTriggerSnapshot(payload?.trigger);
|
|
560
|
+
if (
|
|
561
|
+
payload?.definitionDigest !==
|
|
562
|
+
automationEventDefinitionDigest(expectedFlow, expectedTrigger)
|
|
563
|
+
) {
|
|
564
|
+
throw eventError(
|
|
565
|
+
"AUTOMATION_EVENT_ADJUDICATION_SNAPSHOT_INVALID",
|
|
566
|
+
`automation event adjudication snapshot is invalid: ${occurrence.id}`,
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
adjudicateAutomationExecution(
|
|
570
|
+
db,
|
|
571
|
+
automationEventExecutionId(occurrence.id),
|
|
572
|
+
{
|
|
573
|
+
flowId: expectedFlow.id,
|
|
574
|
+
triggerType: TRIGGER_TYPE.EVENT,
|
|
575
|
+
decision: adjudication.decision,
|
|
576
|
+
requestId: adjudication.requestId,
|
|
577
|
+
},
|
|
578
|
+
);
|
|
579
|
+
if (adjudication.decision === "confirmed_applied") {
|
|
580
|
+
return {
|
|
581
|
+
settled: true,
|
|
582
|
+
result: {
|
|
583
|
+
executionId: automationEventExecutionId(occurrence.id),
|
|
584
|
+
status: "adjudicated-applied",
|
|
585
|
+
adjudicationRequestId: adjudication.requestId,
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
return { continue: true };
|
|
590
|
+
},
|
|
555
591
|
async execute({ occurrence }) {
|
|
556
592
|
const payload = occurrence?.payload;
|
|
557
593
|
const expectedFlow = automationEventFlowSnapshot(payload?.flow);
|