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
|
@@ -43,6 +43,7 @@ export const NODE_TYPE = Object.freeze({
|
|
|
43
43
|
PARALLEL: "parallel",
|
|
44
44
|
LOOP: "loop",
|
|
45
45
|
});
|
|
46
|
+
export const AUTOMATION_SCHEDULER_MIGRATION_SCHEMA_VERSION = 1;
|
|
46
47
|
|
|
47
48
|
/* ── Built-in connector catalog ────────────────────────────── */
|
|
48
49
|
|
|
@@ -310,6 +311,33 @@ export function ensureAutomationTables(db) {
|
|
|
310
311
|
created_at TEXT DEFAULT (datetime('now'))
|
|
311
312
|
)
|
|
312
313
|
`);
|
|
314
|
+
db.exec(`
|
|
315
|
+
CREATE TABLE IF NOT EXISTS auto_scheduler_migrations (
|
|
316
|
+
flow_id TEXT PRIMARY KEY NOT NULL,
|
|
317
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
318
|
+
state TEXT NOT NULL CHECK (state IN ('prepared', 'retired')),
|
|
319
|
+
migration_id TEXT NOT NULL UNIQUE,
|
|
320
|
+
source_digest TEXT NOT NULL,
|
|
321
|
+
target_job_id TEXT NOT NULL UNIQUE,
|
|
322
|
+
retirement_token TEXT UNIQUE,
|
|
323
|
+
prior_status TEXT NOT NULL CHECK (prior_status = 'active'),
|
|
324
|
+
prior_updated_at TEXT NOT NULL,
|
|
325
|
+
source_snapshot TEXT,
|
|
326
|
+
prepared_at TEXT NOT NULL,
|
|
327
|
+
retired_at TEXT
|
|
328
|
+
)
|
|
329
|
+
`);
|
|
330
|
+
const migrationColumns = new Set(
|
|
331
|
+
db
|
|
332
|
+
.prepare("PRAGMA table_info(auto_scheduler_migrations)")
|
|
333
|
+
.all()
|
|
334
|
+
.map((column) => column.name),
|
|
335
|
+
);
|
|
336
|
+
if (!migrationColumns.has("source_snapshot")) {
|
|
337
|
+
db.exec(
|
|
338
|
+
"ALTER TABLE auto_scheduler_migrations ADD COLUMN source_snapshot TEXT",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
313
341
|
}
|
|
314
342
|
|
|
315
343
|
/* ── Helpers ───────────────────────────────────────────────── */
|
|
@@ -322,6 +350,89 @@ function _now() {
|
|
|
322
350
|
return new Date().toISOString();
|
|
323
351
|
}
|
|
324
352
|
|
|
353
|
+
function _stableJsonValue(value) {
|
|
354
|
+
if (Array.isArray(value)) return value.map(_stableJsonValue);
|
|
355
|
+
if (value && typeof value === "object") {
|
|
356
|
+
return Object.fromEntries(
|
|
357
|
+
Object.keys(value)
|
|
358
|
+
.sort()
|
|
359
|
+
.map((key) => [key, _stableJsonValue(value[key])]),
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
return value;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function _automationMigrationField(value, field) {
|
|
366
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
367
|
+
const error = new Error(
|
|
368
|
+
`automation scheduler migration ${field} is required`,
|
|
369
|
+
);
|
|
370
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_INVALID";
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
return value.trim();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function _automationTransaction(db, callback) {
|
|
377
|
+
if (typeof db.transaction !== "function") return callback();
|
|
378
|
+
const transaction = db.transaction(callback);
|
|
379
|
+
return typeof transaction.immediate === "function"
|
|
380
|
+
? transaction.immediate()
|
|
381
|
+
: transaction();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function _rowToSchedulerMigration(row) {
|
|
385
|
+
if (!row) return null;
|
|
386
|
+
return {
|
|
387
|
+
flowId: row.flow_id,
|
|
388
|
+
schemaVersion: row.schema_version,
|
|
389
|
+
state: row.state,
|
|
390
|
+
migrationId: row.migration_id,
|
|
391
|
+
sourceDigest: row.source_digest,
|
|
392
|
+
targetJobId: row.target_job_id,
|
|
393
|
+
retirementToken: row.retirement_token || null,
|
|
394
|
+
priorStatus: row.prior_status,
|
|
395
|
+
priorUpdatedAt: row.prior_updated_at,
|
|
396
|
+
sourceSnapshot: _parseJSON(row.source_snapshot, null),
|
|
397
|
+
preparedAt: row.prepared_at,
|
|
398
|
+
retiredAt: row.retired_at || null,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function _automationMigrationOptionalField(value, field) {
|
|
403
|
+
if (value == null) return null;
|
|
404
|
+
return _automationMigrationField(value, field);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function _automationMigrationSourceDefinition(migration) {
|
|
408
|
+
const source = migration?.sourceSnapshot;
|
|
409
|
+
if (
|
|
410
|
+
!source ||
|
|
411
|
+
typeof source !== "object" ||
|
|
412
|
+
source.id !== migration.flowId ||
|
|
413
|
+
source.status !== migration.priorStatus ||
|
|
414
|
+
typeof source.schedule !== "string" ||
|
|
415
|
+
source.schedule.length === 0
|
|
416
|
+
) {
|
|
417
|
+
const error = new Error(
|
|
418
|
+
`Automation migration source definition is invalid: ${migration?.flowId || "unknown"}`,
|
|
419
|
+
);
|
|
420
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
return source;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function _assertAutomationSchedulerMutationSafe(db, flowId) {
|
|
427
|
+
const migration = getAutomationSchedulerMigration(db, flowId);
|
|
428
|
+
if (!migration) return;
|
|
429
|
+
const error = new Error(
|
|
430
|
+
`Automation flow scheduler authority is owned by migration ${migration.migrationId}: ${flowId}`,
|
|
431
|
+
);
|
|
432
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_ACTIVE";
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
|
|
325
436
|
function _parseJSON(value, fallback) {
|
|
326
437
|
if (value == null) return fallback;
|
|
327
438
|
if (typeof value !== "string") return value;
|
|
@@ -469,6 +580,292 @@ export function getFlow(db, flowId) {
|
|
|
469
580
|
);
|
|
470
581
|
}
|
|
471
582
|
|
|
583
|
+
export function automationMigrationSourceSnapshot(flow) {
|
|
584
|
+
const source = { ...flow };
|
|
585
|
+
delete source.updatedAt;
|
|
586
|
+
delete source.schedulerMigration;
|
|
587
|
+
return _stableJsonValue(source);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function automationMigrationSourceDigest(flow) {
|
|
591
|
+
return crypto
|
|
592
|
+
.createHash("sha256")
|
|
593
|
+
.update("chainlesschain.automation.scheduler-migration-source.v1\0", "utf8")
|
|
594
|
+
.update(JSON.stringify(automationMigrationSourceSnapshot(flow)), "utf8")
|
|
595
|
+
.digest("hex");
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export function getAutomationSchedulerMigration(db, flowId) {
|
|
599
|
+
return _rowToSchedulerMigration(
|
|
600
|
+
db
|
|
601
|
+
.prepare("SELECT * FROM auto_scheduler_migrations WHERE flow_id = ?")
|
|
602
|
+
.get(flowId),
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function automationEffectiveSchedulerFlow(db, flow) {
|
|
607
|
+
if (!flow) return flow;
|
|
608
|
+
const migration = getAutomationSchedulerMigration(db, flow.id);
|
|
609
|
+
if (
|
|
610
|
+
migration?.schemaVersion ===
|
|
611
|
+
AUTOMATION_SCHEDULER_MIGRATION_SCHEMA_VERSION &&
|
|
612
|
+
migration.state === "retired" &&
|
|
613
|
+
migration.priorStatus === FLOW_STATUS.ACTIVE &&
|
|
614
|
+
flow.schedule === null &&
|
|
615
|
+
[FLOW_STATUS.PAUSED, FLOW_STATUS.ACTIVE].includes(flow.status)
|
|
616
|
+
) {
|
|
617
|
+
const source = _automationMigrationSourceDefinition(migration);
|
|
618
|
+
return {
|
|
619
|
+
...flow,
|
|
620
|
+
status: source.status,
|
|
621
|
+
schedule: source.schedule,
|
|
622
|
+
updatedAt: migration.priorUpdatedAt,
|
|
623
|
+
schedulerMigration: migration,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
return flow;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export function listAutomationSchedulerFlows(db, { limit = 10_000 } = {}) {
|
|
630
|
+
return db
|
|
631
|
+
.prepare(
|
|
632
|
+
`SELECT f.* FROM auto_flows f
|
|
633
|
+
JOIN auto_scheduler_migrations m ON m.flow_id = f.id
|
|
634
|
+
WHERE m.state = 'retired' AND f.schedule IS NULL
|
|
635
|
+
AND f.status IN ('paused', 'active')
|
|
636
|
+
ORDER BY f.created_at DESC LIMIT ?`,
|
|
637
|
+
)
|
|
638
|
+
.all(limit)
|
|
639
|
+
.map(_rowToFlow)
|
|
640
|
+
.map((flow) => automationEffectiveSchedulerFlow(db, flow));
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export function prepareAutomationSchedulerMigration(
|
|
644
|
+
db,
|
|
645
|
+
flowId,
|
|
646
|
+
{ migrationId, sourceDigest, targetJobId } = {},
|
|
647
|
+
) {
|
|
648
|
+
const migration = _automationMigrationField(migrationId, "migrationId");
|
|
649
|
+
const digest = _automationMigrationField(sourceDigest, "sourceDigest");
|
|
650
|
+
const target = _automationMigrationField(targetJobId, "targetJobId");
|
|
651
|
+
return _automationTransaction(db, () => {
|
|
652
|
+
const flow = getFlow(db, flowId);
|
|
653
|
+
if (!flow) throw new Error(`Flow not found: ${flowId}`);
|
|
654
|
+
if (flow.status !== FLOW_STATUS.ACTIVE) {
|
|
655
|
+
const error = new Error(`Automation flow is not active: ${flowId}`);
|
|
656
|
+
error.code = "AUTOMATION_SCHEDULER_FLOW_NOT_ACTIVE";
|
|
657
|
+
throw error;
|
|
658
|
+
}
|
|
659
|
+
if (automationMigrationSourceDigest(flow) !== digest) {
|
|
660
|
+
const error = new Error(
|
|
661
|
+
`Automation flow changed before migration: ${flowId}`,
|
|
662
|
+
);
|
|
663
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_SOURCE_CHANGED";
|
|
664
|
+
throw error;
|
|
665
|
+
}
|
|
666
|
+
const prior = getAutomationSchedulerMigration(db, flowId);
|
|
667
|
+
if (prior) {
|
|
668
|
+
if (
|
|
669
|
+
prior.migrationId !== migration ||
|
|
670
|
+
prior.sourceDigest !== digest ||
|
|
671
|
+
prior.targetJobId !== target ||
|
|
672
|
+
prior.state !== "prepared"
|
|
673
|
+
) {
|
|
674
|
+
const error = new Error(
|
|
675
|
+
`Automation flow already belongs to another migration: ${flowId}`,
|
|
676
|
+
);
|
|
677
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
678
|
+
throw error;
|
|
679
|
+
}
|
|
680
|
+
return prior;
|
|
681
|
+
}
|
|
682
|
+
const now = _now();
|
|
683
|
+
db.prepare(
|
|
684
|
+
`INSERT INTO auto_scheduler_migrations
|
|
685
|
+
(flow_id, schema_version, state, migration_id, source_digest,
|
|
686
|
+
target_job_id, retirement_token, prior_status, prior_updated_at,
|
|
687
|
+
source_snapshot, prepared_at, retired_at)
|
|
688
|
+
VALUES (?, 1, 'prepared', ?, ?, ?, NULL, 'active', ?, ?, ?, NULL)`,
|
|
689
|
+
).run(
|
|
690
|
+
flowId,
|
|
691
|
+
migration,
|
|
692
|
+
digest,
|
|
693
|
+
target,
|
|
694
|
+
flow.updatedAt,
|
|
695
|
+
JSON.stringify(automationMigrationSourceSnapshot(flow)),
|
|
696
|
+
now,
|
|
697
|
+
);
|
|
698
|
+
return getAutomationSchedulerMigration(db, flowId);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export function retireAutomationSchedulerMigration(
|
|
703
|
+
db,
|
|
704
|
+
flowId,
|
|
705
|
+
{ migrationId, sourceDigest, targetJobId, retirementToken } = {},
|
|
706
|
+
) {
|
|
707
|
+
const migration = _automationMigrationField(migrationId, "migrationId");
|
|
708
|
+
const digest = _automationMigrationField(sourceDigest, "sourceDigest");
|
|
709
|
+
const target = _automationMigrationField(targetJobId, "targetJobId");
|
|
710
|
+
const token = _automationMigrationField(retirementToken, "retirementToken");
|
|
711
|
+
return _automationTransaction(db, () => {
|
|
712
|
+
const marker = getAutomationSchedulerMigration(db, flowId);
|
|
713
|
+
const flow = getFlow(db, flowId);
|
|
714
|
+
if (
|
|
715
|
+
!marker ||
|
|
716
|
+
marker.migrationId !== migration ||
|
|
717
|
+
marker.sourceDigest !== digest ||
|
|
718
|
+
marker.targetJobId !== target ||
|
|
719
|
+
!["prepared", "retired"].includes(marker.state) ||
|
|
720
|
+
!flow
|
|
721
|
+
) {
|
|
722
|
+
const error = new Error(
|
|
723
|
+
`Automation migration changed before retirement: ${flowId}`,
|
|
724
|
+
);
|
|
725
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
if (marker.state === "retired") {
|
|
729
|
+
if (
|
|
730
|
+
marker.retirementToken !== token ||
|
|
731
|
+
flow.schedule !== null ||
|
|
732
|
+
![FLOW_STATUS.PAUSED, FLOW_STATUS.ACTIVE].includes(flow.status)
|
|
733
|
+
) {
|
|
734
|
+
const error = new Error(
|
|
735
|
+
`Automation migration fence changed: ${flowId}`,
|
|
736
|
+
);
|
|
737
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
if (flow.status === FLOW_STATUS.ACTIVE) {
|
|
741
|
+
db.prepare(
|
|
742
|
+
`UPDATE auto_flows SET status = 'paused' WHERE id = ? AND schedule IS NULL`,
|
|
743
|
+
).run(flowId);
|
|
744
|
+
}
|
|
745
|
+
return automationEffectiveSchedulerFlow(db, flow);
|
|
746
|
+
}
|
|
747
|
+
if (automationMigrationSourceDigest(flow) !== digest) {
|
|
748
|
+
const error = new Error(
|
|
749
|
+
`Automation flow changed before retirement: ${flowId}`,
|
|
750
|
+
);
|
|
751
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_SOURCE_CHANGED";
|
|
752
|
+
throw error;
|
|
753
|
+
}
|
|
754
|
+
const now = _now();
|
|
755
|
+
db.prepare(
|
|
756
|
+
`UPDATE auto_flows SET status = 'paused', schedule = NULL, updated_at = ?
|
|
757
|
+
WHERE id = ? AND status = 'active' AND updated_at = ?`,
|
|
758
|
+
).run(now, flowId, marker.priorUpdatedAt);
|
|
759
|
+
const fenced = getFlow(db, flowId);
|
|
760
|
+
if (fenced?.status !== FLOW_STATUS.PAUSED || fenced.schedule !== null) {
|
|
761
|
+
const error = new Error(
|
|
762
|
+
`Automation flow changed during retirement: ${flowId}`,
|
|
763
|
+
);
|
|
764
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
765
|
+
throw error;
|
|
766
|
+
}
|
|
767
|
+
const changed = db
|
|
768
|
+
.prepare(
|
|
769
|
+
`UPDATE auto_scheduler_migrations
|
|
770
|
+
SET state = 'retired', retirement_token = ?, retired_at = ?
|
|
771
|
+
WHERE flow_id = ? AND state = 'prepared'`,
|
|
772
|
+
)
|
|
773
|
+
.run(token, now, flowId);
|
|
774
|
+
if (changed.changes !== 1) {
|
|
775
|
+
const error = new Error(
|
|
776
|
+
`Automation migration changed during retirement: ${flowId}`,
|
|
777
|
+
);
|
|
778
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
779
|
+
throw error;
|
|
780
|
+
}
|
|
781
|
+
return automationEffectiveSchedulerFlow(db, getFlow(db, flowId));
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
export function restoreAutomationSchedulerMigration(
|
|
786
|
+
db,
|
|
787
|
+
flowId,
|
|
788
|
+
{ migrationId, sourceDigest, targetJobId, retirementToken } = {},
|
|
789
|
+
) {
|
|
790
|
+
const migration = _automationMigrationField(migrationId, "migrationId");
|
|
791
|
+
const digest = _automationMigrationOptionalField(
|
|
792
|
+
sourceDigest,
|
|
793
|
+
"sourceDigest",
|
|
794
|
+
);
|
|
795
|
+
const target = _automationMigrationField(targetJobId, "targetJobId");
|
|
796
|
+
const token = _automationMigrationOptionalField(
|
|
797
|
+
retirementToken,
|
|
798
|
+
"retirementToken",
|
|
799
|
+
);
|
|
800
|
+
return _automationTransaction(db, () => {
|
|
801
|
+
const marker = getAutomationSchedulerMigration(db, flowId);
|
|
802
|
+
const flow = getFlow(db, flowId);
|
|
803
|
+
if (!marker) {
|
|
804
|
+
if (flow && digest && automationMigrationSourceDigest(flow) === digest) {
|
|
805
|
+
return flow;
|
|
806
|
+
}
|
|
807
|
+
const error = new Error(
|
|
808
|
+
`Automation migration fence changed before rollback: ${flowId}`,
|
|
809
|
+
);
|
|
810
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
811
|
+
throw error;
|
|
812
|
+
}
|
|
813
|
+
const source = _automationMigrationSourceDefinition(marker);
|
|
814
|
+
if (
|
|
815
|
+
marker.migrationId !== migration ||
|
|
816
|
+
marker.targetJobId !== target ||
|
|
817
|
+
(digest !== null && marker.sourceDigest !== digest) ||
|
|
818
|
+
marker.priorStatus !== FLOW_STATUS.ACTIVE ||
|
|
819
|
+
!flow
|
|
820
|
+
) {
|
|
821
|
+
const error = new Error(
|
|
822
|
+
`Automation migration fence changed before rollback: ${flowId}`,
|
|
823
|
+
);
|
|
824
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
825
|
+
throw error;
|
|
826
|
+
}
|
|
827
|
+
if (marker.state === "prepared") {
|
|
828
|
+
if (
|
|
829
|
+
token !== null ||
|
|
830
|
+
marker.retirementToken !== null ||
|
|
831
|
+
automationMigrationSourceDigest(flow) !== marker.sourceDigest
|
|
832
|
+
) {
|
|
833
|
+
const error = new Error(
|
|
834
|
+
`Automation migration fence changed before rollback: ${flowId}`,
|
|
835
|
+
);
|
|
836
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
837
|
+
throw error;
|
|
838
|
+
}
|
|
839
|
+
db.prepare("DELETE FROM auto_scheduler_migrations WHERE flow_id = ?").run(
|
|
840
|
+
flowId,
|
|
841
|
+
);
|
|
842
|
+
return flow;
|
|
843
|
+
}
|
|
844
|
+
if (
|
|
845
|
+
marker.state !== "retired" ||
|
|
846
|
+
marker.retirementToken !== token ||
|
|
847
|
+
flow.schedule !== null ||
|
|
848
|
+
![FLOW_STATUS.PAUSED, FLOW_STATUS.ACTIVE].includes(flow.status) ||
|
|
849
|
+
automationMigrationSourceDigest(
|
|
850
|
+
automationEffectiveSchedulerFlow(db, flow),
|
|
851
|
+
) !== marker.sourceDigest
|
|
852
|
+
) {
|
|
853
|
+
const error = new Error(
|
|
854
|
+
`Automation migration fence changed before rollback: ${flowId}`,
|
|
855
|
+
);
|
|
856
|
+
error.code = "AUTOMATION_SCHEDULER_MIGRATION_CONFLICT";
|
|
857
|
+
throw error;
|
|
858
|
+
}
|
|
859
|
+
db.prepare(
|
|
860
|
+
`UPDATE auto_flows SET status = ?, schedule = ?, updated_at = ? WHERE id = ?`,
|
|
861
|
+
).run(source.status, source.schedule, marker.priorUpdatedAt, flowId);
|
|
862
|
+
db.prepare("DELETE FROM auto_scheduler_migrations WHERE flow_id = ?").run(
|
|
863
|
+
flowId,
|
|
864
|
+
);
|
|
865
|
+
return getFlow(db, flowId);
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
|
|
472
869
|
export function listFlows(db, filters = {}) {
|
|
473
870
|
const { status, limit = 50 } = filters;
|
|
474
871
|
const all = db.data?.get("auto_flows") || [];
|
|
@@ -500,6 +897,7 @@ export function updateFlowStatus(db, flowId, status) {
|
|
|
500
897
|
throw new Error(`Invalid flow status: ${status}`);
|
|
501
898
|
}
|
|
502
899
|
_requireFlow(db, flowId);
|
|
900
|
+
_assertAutomationSchedulerMutationSafe(db, flowId);
|
|
503
901
|
db.prepare(
|
|
504
902
|
`UPDATE auto_flows SET status = ?, updated_at = ? WHERE id = ?`,
|
|
505
903
|
).run(status, _now(), flowId);
|
|
@@ -510,6 +908,7 @@ export function updateFlowStatus(db, flowId, status) {
|
|
|
510
908
|
|
|
511
909
|
export function deleteFlow(db, flowId) {
|
|
512
910
|
_requireFlow(db, flowId);
|
|
911
|
+
_assertAutomationSchedulerMutationSafe(db, flowId);
|
|
513
912
|
db.prepare(`DELETE FROM auto_triggers WHERE flow_id = ?`).run(flowId);
|
|
514
913
|
db.prepare(`DELETE FROM auto_executions WHERE flow_id = ?`).run(flowId);
|
|
515
914
|
db.prepare(`DELETE FROM auto_flows WHERE id = ?`).run(flowId);
|
|
@@ -527,6 +926,7 @@ export function scheduleFlow(db, flowId, cron) {
|
|
|
527
926
|
throw new Error("cron expression has no occurrence in the next 366 days");
|
|
528
927
|
}
|
|
529
928
|
_requireFlow(db, flowId);
|
|
929
|
+
_assertAutomationSchedulerMutationSafe(db, flowId);
|
|
530
930
|
db.prepare(
|
|
531
931
|
`UPDATE auto_flows SET schedule = ?, updated_at = ? WHERE id = ?`,
|
|
532
932
|
).run(cron, _now(), flowId);
|
|
@@ -840,6 +1240,100 @@ export function executeFlow(db, flowId, options = {}) {
|
|
|
840
1240
|
);
|
|
841
1241
|
}
|
|
842
1242
|
|
|
1243
|
+
export function adjudicateAutomationExecution(
|
|
1244
|
+
db,
|
|
1245
|
+
executionId,
|
|
1246
|
+
{ flowId, triggerType, decision, requestId } = {},
|
|
1247
|
+
) {
|
|
1248
|
+
const execId = _normalizeExecutionId(executionId);
|
|
1249
|
+
if (!["confirmed_applied", "confirmed_not_applied"].includes(decision)) {
|
|
1250
|
+
const error = new Error("automation adjudication decision is invalid");
|
|
1251
|
+
error.code = "AUTOMATION_ADJUDICATION_INVALID";
|
|
1252
|
+
throw error;
|
|
1253
|
+
}
|
|
1254
|
+
const marker = JSON.stringify({
|
|
1255
|
+
authority: "scheduler-adjudication",
|
|
1256
|
+
requestId,
|
|
1257
|
+
decision,
|
|
1258
|
+
});
|
|
1259
|
+
const archivedId = `${execId}:adjudication:${String(requestId).slice(-24)}`;
|
|
1260
|
+
const existing = getExecution(db, execId);
|
|
1261
|
+
const archived = getExecution(db, archivedId);
|
|
1262
|
+
if (decision === "confirmed_not_applied" && archived) {
|
|
1263
|
+
if (
|
|
1264
|
+
archived.flowId !== flowId ||
|
|
1265
|
+
archived.triggerType !== triggerType ||
|
|
1266
|
+
archived.error !== marker
|
|
1267
|
+
) {
|
|
1268
|
+
const error = new Error(
|
|
1269
|
+
`Automation adjudication archive mismatch: ${execId}`,
|
|
1270
|
+
);
|
|
1271
|
+
error.code = "AUTOMATION_ADJUDICATION_EVIDENCE_MISMATCH";
|
|
1272
|
+
throw error;
|
|
1273
|
+
}
|
|
1274
|
+
return archived;
|
|
1275
|
+
}
|
|
1276
|
+
if (!existing) {
|
|
1277
|
+
const error = new Error(`Automation execution not found: ${execId}`);
|
|
1278
|
+
error.code = "AUTOMATION_ADJUDICATION_EVIDENCE_MISMATCH";
|
|
1279
|
+
throw error;
|
|
1280
|
+
}
|
|
1281
|
+
if (existing.flowId !== flowId || existing.triggerType !== triggerType) {
|
|
1282
|
+
const error = new Error(`Automation execution binding mismatch: ${execId}`);
|
|
1283
|
+
error.code = "AUTOMATION_ADJUDICATION_EVIDENCE_MISMATCH";
|
|
1284
|
+
throw error;
|
|
1285
|
+
}
|
|
1286
|
+
if (
|
|
1287
|
+
existing.status !== EXECUTION_STATUS.RUNNING &&
|
|
1288
|
+
existing.error === marker
|
|
1289
|
+
) {
|
|
1290
|
+
return existing;
|
|
1291
|
+
}
|
|
1292
|
+
if (existing.status !== EXECUTION_STATUS.RUNNING) {
|
|
1293
|
+
const error = new Error(
|
|
1294
|
+
`Automation execution is not outcome-unknown: ${execId}`,
|
|
1295
|
+
);
|
|
1296
|
+
error.code = "AUTOMATION_ADJUDICATION_EVIDENCE_MISMATCH";
|
|
1297
|
+
throw error;
|
|
1298
|
+
}
|
|
1299
|
+
if (decision === "confirmed_not_applied") {
|
|
1300
|
+
const result = db
|
|
1301
|
+
.prepare(
|
|
1302
|
+
`UPDATE auto_executions
|
|
1303
|
+
SET id = ?, status = ?, error = ?, completed_at = ?
|
|
1304
|
+
WHERE id = ? AND status = ?`,
|
|
1305
|
+
)
|
|
1306
|
+
.run(
|
|
1307
|
+
archivedId,
|
|
1308
|
+
EXECUTION_STATUS.CANCELLED,
|
|
1309
|
+
marker,
|
|
1310
|
+
_now(),
|
|
1311
|
+
execId,
|
|
1312
|
+
EXECUTION_STATUS.RUNNING,
|
|
1313
|
+
);
|
|
1314
|
+
if (result.changes !== 1) {
|
|
1315
|
+
const error = new Error(
|
|
1316
|
+
`Automation adjudication changed concurrently: ${execId}`,
|
|
1317
|
+
);
|
|
1318
|
+
error.code = "AUTOMATION_ADJUDICATION_EVIDENCE_MISMATCH";
|
|
1319
|
+
throw error;
|
|
1320
|
+
}
|
|
1321
|
+
return getExecution(db, archivedId);
|
|
1322
|
+
}
|
|
1323
|
+
db.prepare(
|
|
1324
|
+
`UPDATE auto_executions
|
|
1325
|
+
SET status = ?, error = ?, completed_at = ?
|
|
1326
|
+
WHERE id = ? AND status = ?`,
|
|
1327
|
+
).run(
|
|
1328
|
+
EXECUTION_STATUS.SUCCESS,
|
|
1329
|
+
marker,
|
|
1330
|
+
_now(),
|
|
1331
|
+
execId,
|
|
1332
|
+
EXECUTION_STATUS.RUNNING,
|
|
1333
|
+
);
|
|
1334
|
+
return getExecution(db, execId);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
843
1337
|
function _normalizeExecutionId(value) {
|
|
844
1338
|
if (
|
|
845
1339
|
typeof value !== "string" ||
|