chainlesschain 0.163.7 → 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.
- package/package.json +2 -1
- package/src/assets/web-panel/assets/{AIOps-axqQfwv8.js → AIOps-CavM7W3k.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DV-Ro0By.js → ActionButton-CUvZGabQ.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Bt72WEVu.js → Analytics-DeBnWnbe.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BtepG55E.js → AppLayout-DbI5kxvd.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-B7KaJ7S_.js → Artifacts-D93bigKm.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-FNFaUg7A.js → Audit-CBNvfUZa.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DFb6J2wQ.js → BackgroundAgents-CJLyh_YU.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CC7YBhyj.js → Backup-D_0yoehD.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-RkgTNVtj.js → BaseInput-TJxmbObW.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-hHEmGZy-.js → Chat-CE0dto1q.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CAP9YhHv.js → ChatBubbleRenderer-CZ5-yYiD.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-z5wXOQP1.js → Checkbox-Ck1RVIVo.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DVaDJnxy.js → Codegen-qpoyf6DK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CJmaRDR5.js → Col-DUyPWjIE.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DF29_2bI.js → Community-D_S1BemS.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DtkQkBzn.js → Compact-BjxeK-Gc.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-xoHDxp7I.js → Compliance-Dd3CEJsu.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BUm-R7ht.js → Cowork-B4QG7-ad.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Cgl14ZkB.js → Cron-BtZnids5.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BNcbmoqh.js → Crosschain-CHPfCBFD.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Bwja0Zo9.js → DID-DT8DCEOK.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Bk5XjEdJ.js → Dashboard-COQab5_U.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Ci36Oitq.js → Dropdown-BY1PsQuU.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer--U04ZN4q.js → EmailListRenderer-CsuAuaq3.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-B2gNPR2s.js → FamilyGuardDashboard-BqpinD-j.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-C4KghYn7.js → Federation-D0gkfQFv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-QL7x_Q_0.js → FormItemContext-BqgE-axK.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-F3Ryltwm.js → GenericCardRenderer-D0hJ2KvC.js} +1 -1
- package/src/assets/web-panel/assets/{Git-2HassK5k.js → Git-CmMB0uPY.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DoBIp7oe.js → Governance-BnVEI0Dk.js} +1 -1
- package/src/assets/web-panel/assets/{Inference--W01QhZ-.js → Inference-YTAMihRP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DmvwqGpT.js → KnowledgeGraph-0ax7VHop.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BBJ4yAel.js → Logs-B9TRcokL.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-yySN5YNM.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-D5lNngSI.js → Marketplace-B6CSuD_H.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CkEVOMKH.js → McpTools-CjMXvOVU.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-B1Ow1xVE.js → Memory-CXRF71U3.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-SgYbUfb6.js → MobileBridge-DQgNp_oS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BWdOvQAj.js → MobileProjects-DlPwcCbq.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DcTXSSma.js → Mtc-6hDTRPe1.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Dt3WG4oj.js → MtcAudit-BlVHU1DS.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-B9B38UZx.js → Multisig-DP9pzEbV.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-B0S4GGvG.js → NLProgramming-Djorh8EV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DZ5U58Ng.js → Notes-oLZ-JkUg.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-PjL5ZW34.js → NotificationSettings-Cg288VH3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DrAX2ESt.js → OrderTableRenderer-Bb1O3Ofr.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Cv1GYs-c.js → Organization-CBdo-bHs.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bbkp_PL3.js → Overflow-qZdnVumx.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BD8QUgoZ.js → P2P-D4kNfGNQ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BO-2PeaB.js → PdhVaultBrowser-DnlMemM7.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-D_OPVa02.js → Permissions-DHucHFBJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-mh-pEmaB.js → PersonalDataHub-BovlOIPR.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-6JC2EDdb.js → Pipeline-Deru3CQY.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CUshzUAG.js → Privacy-Bq5oTTbU.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Cs2Aalri.js → ProjectInit-BfzxVWAz.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-1ak49tcj.js → ProjectSettings-CKC6XBtH.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BV4oApsm.js → Projects-CMolGhae.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CXXxvafH.js → Providers-C_jx4TWX.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-PTcDsF58.js → QrScannerModal-BdIHeVGC.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CDwucrJw.js → QuickAsk-BwZwAcPD.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CJlLVRXR.js → Recommend-BNTt0L-m.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DlN-ETsO.js → RemoteSession-Biyn2-Ro.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DI23LR6m.js → Reputation-D6VpG32B.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Cl7UQMrM.js → Row-D_74OvQQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CexHNfh8.js → RssFeed-BTr0a-BQ.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BneBWvRN.js → Search-C3MCFoNU.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DX7Phgrp.js → Security-CqW9aJ6s.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BWXO8Mte.js → Services-Cw5GNaHF.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DCyOjKPJ.js → Skeleton-DZps5qeY.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CXqERsTB.js → Skills-C6NQ1znT.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-bhbrbz8E.js → Sla-DFP8G2Sh.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-3IPU0HBZ.js → SpeechSettings-ESt6MB6v.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DBNTQLbM.js → SyncSettings-BHnN98aM.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-quP0bkN-.js → Tasks-NiSCaOf-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BouWmgho.js → Templates-DsKFhW2C.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-qv0D9s7z.js → Tenant-CQ0E0ze5.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BxvbE8BB.js → Terminal-D_rBu_by.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D-IeLnJ2.js → TimelineRenderer-D3Vm4Po4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CFMKKyVG.js → Tokens-DNTxRH6q.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-L8as3UuJ.js → Trigger-Dgprz1ms.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-MxmA4VZG.js → Trust-B2rUnVBD.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C0VvJYRi.js → UkeySign-DEGQNc6_.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Bf5QZj8N.js → VideoEditing-jlsAx3Eh.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BIJ9riRz.js → Wallet-8jQih1ij.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-B8p9Mhkw.js → WebAuthn-XLeO6xIB.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BVsuaPen.js → WorkflowEditor-De_dgDAG.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BfkjoKpV.js → chat-CI19R_Sk.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DeAlsWK8.js → colors-DsYTOYN3.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cs97bz5j.js → compact-item-CM3-t_Qr.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BqbG60LH.js → createContext-BjxlBRZv.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-S1ld2LwU.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-i_RY0b1X.js → hasIn-RDaWX_4i.js} +1 -1
- package/src/assets/web-panel/assets/{index-U4f5izHz.js → index-9R6BxAr0.js} +1 -1
- package/src/assets/web-panel/assets/{index-0-agYlWL.js → index-B-Z8eFUJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1CbOH1i.js → index-B0PMlbMs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqBgBpgp.js → index-B5REnKt8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUXAB2s.js → index-B7WBt_4T.js} +1 -1
- package/src/assets/web-panel/assets/{index-CIG8fQ2_.js → index-B9O9YyWE.js} +1 -1
- package/src/assets/web-panel/assets/{index-gyFpvL7l.js → index-BCnZyIMA.js} +1 -1
- package/src/assets/web-panel/assets/{index-yObj1O4n.js → index-BGqqJfL0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiSkx4UB.js → index-BHNYPO0b.js} +3 -3
- package/src/assets/web-panel/assets/{index-Bm7lLR4c.js → index-BKJr-TZp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOVkf1K3.js → index-BN6UPD2E.js} +1 -1
- package/src/assets/web-panel/assets/{index-BESLFt4q.js → index-Bi46Xu4u.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2VtTlp.js → index-BlT2VAEK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmbfhJUP.js → index-C0yx-jtQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYUVuwKt.js → index-C4gkzxkw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeWoNHau.js → index-CVQMeiwH.js} +1 -1
- package/src/assets/web-panel/assets/{index-PT_5sBMA.js → index-Cd3euP_4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgsqdnRV.js → index-Cd9OJhHG.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-3tEpTc.js → index-CiECJi-Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkBi5TmC.js → index-CjTrRt13.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2B4VPyP.js → index-CnHH7bnD.js} +1 -1
- package/src/assets/web-panel/assets/{index-dIHr4B_u.js → index-Cpqm1qfU.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5UkyA3c.js → index-CyOGN6a5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk7-v4hP.js → index-D0zcy54x.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZxO_-E8a.js → index-D6sm5-Xq.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-JVTrpr.js → index-DIUnNOjG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk2qouAz.js → index-DX7tuWVJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuLQobzW.js → index-DXXtGMUJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUUrv141.js → index-DcPFB8qJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9evtvg2.js → index-DoQA0-52.js} +1 -1
- package/src/assets/web-panel/assets/{index-COdGA-8n.js → index-Drlnwr8F.js} +1 -1
- package/src/assets/web-panel/assets/index-Drz6yq0m.js +1 -0
- package/src/assets/web-panel/assets/{index-CED2PL_y.js → index-Dsij_KsN.js} +1 -1
- package/src/assets/web-panel/assets/{index-79V_zv1s.js → index-Dxge-xWl.js} +1 -1
- package/src/assets/web-panel/assets/{index-CG3PRyAL.js → index-DyIWssPC.js} +1 -1
- package/src/assets/web-panel/assets/{index-YXgLng0K.js → index-IeiL9RKc.js} +1 -1
- package/src/assets/web-panel/assets/{index-s1opAL-M.js → index-nAH87mQP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf3WCLmy.js → index-pPL66J85.js} +1 -1
- package/src/assets/web-panel/assets/index-uWiUs7S_.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-C9h9yKL3.js → initDefaultProps-BTL88of4.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BvmLsPdu.js → motion-BUB2pXwB.js} +1 -1
- package/src/assets/web-panel/assets/{move-CoL4ekU1.js → move-egCkkOMm.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C4pMy6xV.js → mtc-parser-DAWhZC8c.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DPuJfNxx.js → omit-CvOxe_6d.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D0KEeaK2.js → pickAttrs-DZUSLZ5Z.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BpkyINxt.js → placementArrow-BERxGNMY.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DQI_wOq8.js → responsiveObserve-DZuDfZ9n.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DYfdDiRg.js → slide-DO756sgI.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B5dtvb0D.js → statusUtils-BRQlIz28.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CF310mSj.js → styleChecker-DCSfdybl.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D1Qp4BLp.js → useFlexGapSupport-LD8dWVve.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-C7_iyvJK.js → useFs-BX9uW2LK.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DZUt_fIm.js → usePersonalDataHub-BPSlPuHU.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D3dsi0lZ.js → vnode-BmOILC9V.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-WdbOlvdQ.js → zoom-1iKowKGg.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/automation.js +103 -1
- package/src/commands/team-merge-review.js +937 -0
- package/src/commands/team.js +2 -0
- package/src/data/changelog.json +14 -1
- package/src/lib/agent-schedule-store.js +139 -28
- package/src/lib/agent-team/team-merge-review-store.js +765 -0
- package/src/lib/agent-team/team-merge-review-transaction.js +1223 -0
- package/src/lib/agent-team/team-merge-review.js +1847 -0
- package/src/lib/automation-center-incidents.js +314 -0
- package/src/lib/automation-center-runtime.js +448 -0
- package/src/lib/automation-center.js +182 -38
- package/src/lib/automation-engine.js +41 -0
- package/src/lib/automation-execution-authority.js +241 -12
- package/src/lib/automation-execution-incident.js +737 -0
- package/src/lib/cowork-cron.js +85 -14
- package/src/lib/scheduler-kernel/agenda-adapter.js +17 -7
- package/src/lib/scheduler-kernel/automation-adapter.js +105 -11
- package/src/lib/scheduler-kernel/automation-event-adapter.js +87 -11
- package/src/lib/scheduler-kernel/contract.js +91 -0
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +12 -1
- package/src/lib/scheduler-kernel/loop-adapter.js +7 -1
- package/src/lib/scheduler-kernel/routine-adapter.js +7 -1
- package/src/lib/scheduler-kernel/runtime-control-capabilities.js +31 -0
- package/src/lib/scheduler-kernel/runtime.js +176 -3
- package/src/lib/scheduler-kernel/store.js +1253 -45
- package/src/runtime/agent-core.js +10 -0
- package/src/runtime/mcp-config.js +81 -10
- package/src/assets/web-panel/assets/MarkdownRenderer-BLCZMzhJ.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +0 -1
- package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +0 -1
- package/src/assets/web-panel/assets/index-ChVT0p9u.js +0 -1
package/src/lib/cowork-cron.js
CHANGED
|
@@ -31,12 +31,14 @@
|
|
|
31
31
|
|
|
32
32
|
import crypto from "node:crypto";
|
|
33
33
|
import {
|
|
34
|
-
|
|
34
|
+
closeSync,
|
|
35
|
+
fsyncSync,
|
|
35
36
|
mkdirSync,
|
|
37
|
+
openSync,
|
|
36
38
|
readFileSync,
|
|
37
39
|
renameSync,
|
|
38
40
|
unlinkSync,
|
|
39
|
-
|
|
41
|
+
writeSync,
|
|
40
42
|
} from "node:fs";
|
|
41
43
|
import { join } from "node:path";
|
|
42
44
|
import {
|
|
@@ -47,12 +49,14 @@ import {
|
|
|
47
49
|
import { withFileLock } from "./with-file-lock.js";
|
|
48
50
|
|
|
49
51
|
export const _deps = {
|
|
50
|
-
|
|
52
|
+
closeSync,
|
|
53
|
+
fsyncSync,
|
|
51
54
|
mkdirSync,
|
|
55
|
+
openSync,
|
|
52
56
|
readFileSync,
|
|
53
57
|
renameSync,
|
|
54
58
|
unlinkSync,
|
|
55
|
-
|
|
59
|
+
writeSync,
|
|
56
60
|
withFileLock,
|
|
57
61
|
now: () => new Date(),
|
|
58
62
|
runTask: null, // injected at runtime to avoid circular import
|
|
@@ -543,14 +547,33 @@ function _scheduleFile(cwd) {
|
|
|
543
547
|
/** Load all schedules from disk. Returns [] if the file doesn't exist. */
|
|
544
548
|
export function loadSchedules(cwd, { failOnMalformed = false } = {}) {
|
|
545
549
|
const file = _scheduleFile(cwd);
|
|
546
|
-
|
|
547
|
-
|
|
550
|
+
let raw;
|
|
551
|
+
try {
|
|
552
|
+
raw = _deps.readFileSync(file, "utf-8");
|
|
553
|
+
} catch (cause) {
|
|
554
|
+
if (cause?.code === "ENOENT") return [];
|
|
555
|
+
const error = new Error(`cowork schedule store read failed: ${file}`, {
|
|
556
|
+
cause,
|
|
557
|
+
});
|
|
558
|
+
error.code = "COWORK_SCHEDULE_STORE_READ_FAILED";
|
|
559
|
+
error.filePath = file;
|
|
560
|
+
if (typeof cause?.code === "string") error.fsCode = cause.code;
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
548
563
|
const out = [];
|
|
549
564
|
for (const line of raw.split("\n")) {
|
|
550
565
|
const trimmed = line.trim();
|
|
551
566
|
if (!trimmed) continue;
|
|
552
567
|
try {
|
|
553
|
-
|
|
568
|
+
const schedule = JSON.parse(trimmed);
|
|
569
|
+
if (
|
|
570
|
+
!schedule ||
|
|
571
|
+
typeof schedule !== "object" ||
|
|
572
|
+
Array.isArray(schedule)
|
|
573
|
+
) {
|
|
574
|
+
throw new TypeError("schedule entry must be an object");
|
|
575
|
+
}
|
|
576
|
+
out.push(schedule);
|
|
554
577
|
} catch (cause) {
|
|
555
578
|
if (failOnMalformed) {
|
|
556
579
|
const error = new Error(
|
|
@@ -573,20 +596,68 @@ function _prepareScheduleDir(cwd) {
|
|
|
573
596
|
}
|
|
574
597
|
|
|
575
598
|
function _saveSchedulesUnlocked(cwd, schedules) {
|
|
576
|
-
_prepareScheduleDir(cwd);
|
|
599
|
+
const directory = _prepareScheduleDir(cwd);
|
|
577
600
|
const file = _scheduleFile(cwd);
|
|
578
601
|
const body = schedules.map((s) => JSON.stringify(s)).join("\n");
|
|
602
|
+
const contents = Buffer.from(body ? `${body}\n` : "", "utf8");
|
|
579
603
|
const tmp = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
604
|
+
let descriptor = null;
|
|
605
|
+
let renamed = false;
|
|
580
606
|
try {
|
|
581
|
-
_deps.
|
|
607
|
+
descriptor = _deps.openSync(tmp, "wx", 0o600);
|
|
608
|
+
let offset = 0;
|
|
609
|
+
while (offset < contents.length) {
|
|
610
|
+
const written = _deps.writeSync(
|
|
611
|
+
descriptor,
|
|
612
|
+
contents,
|
|
613
|
+
offset,
|
|
614
|
+
contents.length - offset,
|
|
615
|
+
null,
|
|
616
|
+
);
|
|
617
|
+
if (!Number.isSafeInteger(written) || written <= 0) {
|
|
618
|
+
const cause = new Error("write returned no progress");
|
|
619
|
+
cause.code = "EIO";
|
|
620
|
+
throw cause;
|
|
621
|
+
}
|
|
622
|
+
offset += written;
|
|
623
|
+
}
|
|
624
|
+
_deps.fsyncSync(descriptor);
|
|
625
|
+
_deps.closeSync(descriptor);
|
|
626
|
+
descriptor = null;
|
|
582
627
|
_deps.renameSync(tmp, file);
|
|
628
|
+
renamed = true;
|
|
629
|
+
|
|
630
|
+
if (process.platform !== "win32") {
|
|
631
|
+
const directoryDescriptor = _deps.openSync(directory, "r");
|
|
632
|
+
try {
|
|
633
|
+
_deps.fsyncSync(directoryDescriptor);
|
|
634
|
+
} finally {
|
|
635
|
+
_deps.closeSync(directoryDescriptor);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
583
638
|
} catch (error) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
639
|
+
if (descriptor !== null) {
|
|
640
|
+
try {
|
|
641
|
+
_deps.closeSync(descriptor);
|
|
642
|
+
} catch {
|
|
643
|
+
// Preserve the original persistence error.
|
|
644
|
+
}
|
|
588
645
|
}
|
|
589
|
-
|
|
646
|
+
if (!renamed) {
|
|
647
|
+
try {
|
|
648
|
+
_deps.unlinkSync(tmp);
|
|
649
|
+
} catch {
|
|
650
|
+
// Best-effort orphan cleanup; the authoritative file is unchanged.
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
const persistenceError =
|
|
654
|
+
error && (typeof error === "object" || typeof error === "function")
|
|
655
|
+
? error
|
|
656
|
+
: new Error(`Cowork schedule persistence failed: ${String(error)}`, {
|
|
657
|
+
cause: error,
|
|
658
|
+
});
|
|
659
|
+
persistenceError.commitState = renamed ? "unknown" : "not-committed";
|
|
660
|
+
throw persistenceError;
|
|
590
661
|
}
|
|
591
662
|
}
|
|
592
663
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
normalizeJson,
|
|
16
16
|
} from "./contract.js";
|
|
17
17
|
import { SchedulerRuntime } from "./runtime.js";
|
|
18
|
+
import { CHECKPOINT_V1_RUNTIME_CONTROL } from "./runtime-control-capabilities.js";
|
|
18
19
|
import { schedulerMigrationSourceDigest } from "./store.js";
|
|
19
20
|
import {
|
|
20
21
|
bindSchedulerAuthorityPolicy,
|
|
@@ -677,6 +678,7 @@ export function createAgendaSchedulerAdapter({
|
|
|
677
678
|
}
|
|
678
679
|
return {
|
|
679
680
|
kind: AGENDA_SCHEDULER_KIND,
|
|
681
|
+
runtimeControl: CHECKPOINT_V1_RUNTIME_CONTROL,
|
|
680
682
|
async adjudicate({ occurrence, adjudication }) {
|
|
681
683
|
const payload = occurrence.payload;
|
|
682
684
|
const expected = agendaEntrySnapshot(payload?.entry);
|
|
@@ -711,7 +713,7 @@ export function createAgendaSchedulerAdapter({
|
|
|
711
713
|
}
|
|
712
714
|
return { continue: true };
|
|
713
715
|
},
|
|
714
|
-
async execute({ occurrence }) {
|
|
716
|
+
async execute({ occurrence, checkpoint }) {
|
|
715
717
|
const payload = occurrence.payload;
|
|
716
718
|
const expected = agendaEntrySnapshot(payload?.entry);
|
|
717
719
|
const expectedDigest = agendaEntrySnapshotDigest(expected);
|
|
@@ -757,6 +759,17 @@ export function createAgendaSchedulerAdapter({
|
|
|
757
759
|
`Agenda entry changed after occurrence enqueue: ${expected.id}`,
|
|
758
760
|
);
|
|
759
761
|
}
|
|
762
|
+
if (expected.kind === "monitor" && typeof runMonitor !== "function") {
|
|
763
|
+
throw agendaError(
|
|
764
|
+
"AGENDA_SCHEDULER_MONITOR_RUNNER_REQUIRED",
|
|
765
|
+
"Agenda scheduler monitor execution requires a monitor runner",
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
checkpoint({
|
|
770
|
+
entryId: expected.id,
|
|
771
|
+
phase: "before_execution_bind",
|
|
772
|
+
});
|
|
760
773
|
|
|
761
774
|
current = agendaStore.bindSchedulerExecution(expected.id, {
|
|
762
775
|
occurrenceId: occurrence.id,
|
|
@@ -784,12 +797,6 @@ export function createAgendaSchedulerAdapter({
|
|
|
784
797
|
let action = null;
|
|
785
798
|
try {
|
|
786
799
|
if (expected.kind === "monitor") {
|
|
787
|
-
if (typeof runMonitor !== "function") {
|
|
788
|
-
throw agendaError(
|
|
789
|
-
"AGENDA_SCHEDULER_MONITOR_RUNNER_REQUIRED",
|
|
790
|
-
"Agenda scheduler monitor execution requires a monitor runner",
|
|
791
|
-
);
|
|
792
|
-
}
|
|
793
800
|
observation = normalizeMonitorObservation(
|
|
794
801
|
expected,
|
|
795
802
|
await runMonitor({
|
|
@@ -944,6 +951,9 @@ export function createAgendaSchedulerAdapter({
|
|
|
944
951
|
);
|
|
945
952
|
}
|
|
946
953
|
},
|
|
954
|
+
async resume(context) {
|
|
955
|
+
return this.execute(context);
|
|
956
|
+
},
|
|
947
957
|
classifyError(error) {
|
|
948
958
|
return {
|
|
949
959
|
retryable: error?.retryable === true,
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { nextCronTime } from "../agent-schedule-store.js";
|
|
3
3
|
import {
|
|
4
|
+
assertAutomationRuntimeBoundary,
|
|
4
5
|
automationExecutionAuthorityDigest,
|
|
5
6
|
automationExecutionAuthoritySnapshot,
|
|
6
7
|
automationSchedulerAuthority,
|
|
8
|
+
classifyAutomationBoundaryError,
|
|
7
9
|
normalizeAutomationExecutionAuthoritySnapshot,
|
|
8
10
|
reserveAutomationExecutionAuthority,
|
|
9
11
|
} from "../automation-execution-authority.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveAutomationExecutionIncidentsForSucceededRun,
|
|
14
|
+
upsertAutomationExecutionIncident,
|
|
15
|
+
} from "../automation-execution-incident.js";
|
|
10
16
|
import {
|
|
11
17
|
EXECUTION_STATUS,
|
|
12
18
|
FLOW_STATUS,
|
|
@@ -33,6 +39,7 @@ import {
|
|
|
33
39
|
normalizeJson,
|
|
34
40
|
} from "./contract.js";
|
|
35
41
|
import { SchedulerRuntime } from "./runtime.js";
|
|
42
|
+
import { CHECKPOINT_V1_RUNTIME_CONTROL } from "./runtime-control-capabilities.js";
|
|
36
43
|
import {
|
|
37
44
|
schedulerJobDefinitionDigest,
|
|
38
45
|
schedulerMigrationSourceDigest,
|
|
@@ -178,6 +185,54 @@ export function automationSchedulerExecutionId(occurrenceId) {
|
|
|
178
185
|
return `exec-scheduler-${digest}`;
|
|
179
186
|
}
|
|
180
187
|
|
|
188
|
+
function automationBoundaryIncidentEvidence(error, snapshot) {
|
|
189
|
+
const classified = classifyAutomationBoundaryError(error);
|
|
190
|
+
if (!classified) return null;
|
|
191
|
+
const deniedPermissions = Array.isArray(error?.details?.permissions)
|
|
192
|
+
? error.details.permissions.map(String).slice(0, 32)
|
|
193
|
+
: [];
|
|
194
|
+
return {
|
|
195
|
+
classified,
|
|
196
|
+
boundary: {
|
|
197
|
+
...(deniedPermissions.length > 0 ? { deniedPermissions } : {}),
|
|
198
|
+
...(snapshot?.budget?.revision === undefined
|
|
199
|
+
? {}
|
|
200
|
+
: { budgetRevision: snapshot.budget.revision }),
|
|
201
|
+
...(Array.isArray(snapshot?.connectors)
|
|
202
|
+
? { connectors: snapshot.connectors }
|
|
203
|
+
: {}),
|
|
204
|
+
},
|
|
205
|
+
details: { reasonCode: classified.code, retryable: false },
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function persistAutomationSchedulerIncident(
|
|
210
|
+
db,
|
|
211
|
+
occurrence,
|
|
212
|
+
flow,
|
|
213
|
+
snapshot,
|
|
214
|
+
error,
|
|
215
|
+
now,
|
|
216
|
+
) {
|
|
217
|
+
const evidence = automationBoundaryIncidentEvidence(error, snapshot);
|
|
218
|
+
if (!evidence) return null;
|
|
219
|
+
return upsertAutomationExecutionIncident(
|
|
220
|
+
db,
|
|
221
|
+
{
|
|
222
|
+
runId: automationSchedulerExecutionId(occurrence.id),
|
|
223
|
+
flowId: flow.id,
|
|
224
|
+
occurrenceId: occurrence.id,
|
|
225
|
+
triggerType: TRIGGER_TYPE.SCHEDULE,
|
|
226
|
+
category: evidence.classified.category,
|
|
227
|
+
code: evidence.classified.code,
|
|
228
|
+
authorityDigest: automationExecutionAuthorityDigest(snapshot, flow),
|
|
229
|
+
boundary: evidence.boundary,
|
|
230
|
+
details: evidence.details,
|
|
231
|
+
},
|
|
232
|
+
{ now },
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
181
236
|
export function buildAutomationSchedulerJob(flow, executionAuthority) {
|
|
182
237
|
const snapshot = automationFlowSnapshot(flow);
|
|
183
238
|
const normalizedExecutionAuthority =
|
|
@@ -702,7 +757,7 @@ export function authorizeAutomationOccurrence({ job, occurrence }) {
|
|
|
702
757
|
}
|
|
703
758
|
}
|
|
704
759
|
|
|
705
|
-
function existingExecutionResult(db, occurrence, expected) {
|
|
760
|
+
function existingExecutionResult(db, occurrence, expected, now) {
|
|
706
761
|
const executionId = automationSchedulerExecutionId(occurrence.id);
|
|
707
762
|
const execution = getExecution(db, executionId);
|
|
708
763
|
if (!execution) return null;
|
|
@@ -729,6 +784,9 @@ function existingExecutionResult(db, occurrence, expected) {
|
|
|
729
784
|
{ executionId: execution.id, status: execution.status },
|
|
730
785
|
);
|
|
731
786
|
}
|
|
787
|
+
resolveAutomationExecutionIncidentsForSucceededRun(db, execution.id, {
|
|
788
|
+
now,
|
|
789
|
+
});
|
|
732
790
|
return execution;
|
|
733
791
|
}
|
|
734
792
|
|
|
@@ -741,6 +799,7 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
741
799
|
}
|
|
742
800
|
return {
|
|
743
801
|
kind: AUTOMATION_SCHEDULER_KIND,
|
|
802
|
+
runtimeControl: CHECKPOINT_V1_RUNTIME_CONTROL,
|
|
744
803
|
async adjudicate({ occurrence, adjudication }) {
|
|
745
804
|
const payload = occurrence?.payload;
|
|
746
805
|
const expected = automationFlowSnapshot(payload?.flow);
|
|
@@ -772,7 +831,7 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
772
831
|
}
|
|
773
832
|
return { continue: true };
|
|
774
833
|
},
|
|
775
|
-
async execute({ occurrence }) {
|
|
834
|
+
async execute({ occurrence, checkpoint }) {
|
|
776
835
|
const payload = occurrence?.payload;
|
|
777
836
|
const expected = automationFlowSnapshot(payload?.flow);
|
|
778
837
|
const expectedDigest = automationFlowSnapshotDigest(expected);
|
|
@@ -789,7 +848,7 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
789
848
|
// A prior process may have committed the flow result and died before the
|
|
790
849
|
// scheduler settlement. Durable success is authoritative even if the flow
|
|
791
850
|
// was subsequently paused or edited.
|
|
792
|
-
const recovered = existingExecutionResult(db, occurrence, expected);
|
|
851
|
+
const recovered = existingExecutionResult(db, occurrence, expected, now);
|
|
793
852
|
if (recovered) return recovered;
|
|
794
853
|
|
|
795
854
|
const current = getFlow(db, expected.id);
|
|
@@ -813,14 +872,31 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
813
872
|
);
|
|
814
873
|
}
|
|
815
874
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
875
|
+
checkpoint({
|
|
876
|
+
executionId: automationSchedulerExecutionId(occurrence.id),
|
|
877
|
+
phase: "before_execution_reservation",
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
try {
|
|
881
|
+
reserveAutomationExecutionAuthority(
|
|
882
|
+
db,
|
|
883
|
+
effectiveCurrent,
|
|
884
|
+
occurrence.id,
|
|
885
|
+
payload.executionAuthority,
|
|
886
|
+
payload.executionAuthorityDigest,
|
|
887
|
+
{ now },
|
|
888
|
+
);
|
|
889
|
+
} catch (error) {
|
|
890
|
+
persistAutomationSchedulerIncident(
|
|
891
|
+
db,
|
|
892
|
+
occurrence,
|
|
893
|
+
effectiveCurrent,
|
|
894
|
+
payload.executionAuthority,
|
|
895
|
+
error,
|
|
896
|
+
now,
|
|
897
|
+
);
|
|
898
|
+
throw error;
|
|
899
|
+
}
|
|
824
900
|
|
|
825
901
|
let execution;
|
|
826
902
|
try {
|
|
@@ -828,8 +904,20 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
828
904
|
inputData: {},
|
|
829
905
|
triggerType: TRIGGER_TYPE.SCHEDULE,
|
|
830
906
|
executionId: automationSchedulerExecutionId(occurrence.id),
|
|
907
|
+
executionAuthority: payload.executionAuthority,
|
|
908
|
+
assertRuntimeBoundary: assertAutomationRuntimeBoundary,
|
|
831
909
|
});
|
|
832
910
|
} catch (cause) {
|
|
911
|
+
if (classifyAutomationBoundaryError(cause)) {
|
|
912
|
+
persistAutomationSchedulerIncident(
|
|
913
|
+
db,
|
|
914
|
+
occurrence,
|
|
915
|
+
effectiveCurrent,
|
|
916
|
+
payload.executionAuthority,
|
|
917
|
+
cause,
|
|
918
|
+
now,
|
|
919
|
+
);
|
|
920
|
+
}
|
|
833
921
|
if (cause?.code === "AUTOMATION_EXECUTION_OUTCOME_UNKNOWN") {
|
|
834
922
|
throw automationError(
|
|
835
923
|
"AUTOMATION_SCHEDULER_OUTCOME_UNKNOWN",
|
|
@@ -847,8 +935,14 @@ export function createAutomationSchedulerAdapter({ db, now = Date.now } = {}) {
|
|
|
847
935
|
{ executionId: execution.id, error: execution.error },
|
|
848
936
|
);
|
|
849
937
|
}
|
|
938
|
+
resolveAutomationExecutionIncidentsForSucceededRun(db, execution.id, {
|
|
939
|
+
now,
|
|
940
|
+
});
|
|
850
941
|
return execution;
|
|
851
942
|
},
|
|
943
|
+
async resume(context) {
|
|
944
|
+
return this.execute(context);
|
|
945
|
+
},
|
|
852
946
|
classifyError() {
|
|
853
947
|
return { retryable: false };
|
|
854
948
|
},
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
3
|
import {
|
|
4
|
+
assertAutomationRuntimeBoundary,
|
|
4
5
|
automationExecutionAuthorityDigest,
|
|
5
6
|
automationExecutionAuthoritySnapshot,
|
|
6
7
|
automationSchedulerAuthority,
|
|
8
|
+
classifyAutomationBoundaryError,
|
|
7
9
|
normalizeAutomationExecutionAuthoritySnapshot,
|
|
8
10
|
reserveAutomationExecutionAuthority,
|
|
9
11
|
} from "../automation-execution-authority.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveAutomationExecutionIncidentsForSucceededRun,
|
|
14
|
+
upsertAutomationExecutionIncident,
|
|
15
|
+
} from "../automation-execution-incident.js";
|
|
10
16
|
import {
|
|
11
17
|
EXECUTION_STATUS,
|
|
12
18
|
FLOW_STATUS,
|
|
@@ -26,6 +32,7 @@ import {
|
|
|
26
32
|
normalizeJson,
|
|
27
33
|
} from "./contract.js";
|
|
28
34
|
import { SchedulerRuntime } from "./runtime.js";
|
|
35
|
+
import { CHECKPOINT_V1_RUNTIME_CONTROL } from "./runtime-control-capabilities.js";
|
|
29
36
|
|
|
30
37
|
export const AUTOMATION_EVENT_KIND = "automation-event";
|
|
31
38
|
export const AUTOMATION_EVENT_CAPABILITY = "automation.execute";
|
|
@@ -294,6 +301,39 @@ export function automationEventExecutionId(occurrenceId) {
|
|
|
294
301
|
.digest("hex")}`;
|
|
295
302
|
}
|
|
296
303
|
|
|
304
|
+
function persistAutomationEventIncident(db, occurrence, flow, error, now) {
|
|
305
|
+
const classified = classifyAutomationBoundaryError(error);
|
|
306
|
+
if (!classified) return null;
|
|
307
|
+
const snapshot = occurrence.payload.executionAuthority;
|
|
308
|
+
const deniedPermissions = Array.isArray(error?.details?.permissions)
|
|
309
|
+
? error.details.permissions.map(String).slice(0, 32)
|
|
310
|
+
: [];
|
|
311
|
+
return upsertAutomationExecutionIncident(
|
|
312
|
+
db,
|
|
313
|
+
{
|
|
314
|
+
runId: automationEventExecutionId(occurrence.id),
|
|
315
|
+
flowId: flow.id,
|
|
316
|
+
occurrenceId: occurrence.id,
|
|
317
|
+
triggerType: TRIGGER_TYPE.EVENT,
|
|
318
|
+
triggerId: occurrence.payload?.trigger?.id || null,
|
|
319
|
+
category: classified.category,
|
|
320
|
+
code: classified.code,
|
|
321
|
+
authorityDigest: automationExecutionAuthorityDigest(snapshot, flow),
|
|
322
|
+
boundary: {
|
|
323
|
+
...(deniedPermissions.length > 0 ? { deniedPermissions } : {}),
|
|
324
|
+
...(snapshot?.budget?.revision === undefined
|
|
325
|
+
? {}
|
|
326
|
+
: { budgetRevision: snapshot.budget.revision }),
|
|
327
|
+
...(Array.isArray(snapshot?.connectors)
|
|
328
|
+
? { connectors: snapshot.connectors }
|
|
329
|
+
: {}),
|
|
330
|
+
},
|
|
331
|
+
details: { reasonCode: classified.code, retryable: false },
|
|
332
|
+
},
|
|
333
|
+
{ now },
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
297
337
|
export function automationEventTriggerKey(event) {
|
|
298
338
|
const normalized = normalizeAutomationChannelEvent(event);
|
|
299
339
|
return `channel:${createHash("sha256")
|
|
@@ -514,7 +554,7 @@ export function authorizeAutomationEventOccurrence({ job, occurrence }) {
|
|
|
514
554
|
}
|
|
515
555
|
}
|
|
516
556
|
|
|
517
|
-
function existingExecutionResult(db, occurrence, expectedFlow) {
|
|
557
|
+
function existingExecutionResult(db, occurrence, expectedFlow, now) {
|
|
518
558
|
const executionId = automationEventExecutionId(occurrence.id);
|
|
519
559
|
const execution = getExecution(db, executionId);
|
|
520
560
|
if (!execution) return null;
|
|
@@ -541,6 +581,9 @@ function existingExecutionResult(db, occurrence, expectedFlow) {
|
|
|
541
581
|
{ executionId: execution.id, status: execution.status },
|
|
542
582
|
);
|
|
543
583
|
}
|
|
584
|
+
resolveAutomationExecutionIncidentsForSucceededRun(db, execution.id, {
|
|
585
|
+
now,
|
|
586
|
+
});
|
|
544
587
|
return execution;
|
|
545
588
|
}
|
|
546
589
|
|
|
@@ -553,6 +596,7 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
553
596
|
}
|
|
554
597
|
return {
|
|
555
598
|
kind: AUTOMATION_EVENT_KIND,
|
|
599
|
+
runtimeControl: CHECKPOINT_V1_RUNTIME_CONTROL,
|
|
556
600
|
async adjudicate({ occurrence, adjudication }) {
|
|
557
601
|
const payload = occurrence?.payload;
|
|
558
602
|
const expectedFlow = automationEventFlowSnapshot(payload?.flow);
|
|
@@ -588,7 +632,7 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
588
632
|
}
|
|
589
633
|
return { continue: true };
|
|
590
634
|
},
|
|
591
|
-
async execute({ occurrence }) {
|
|
635
|
+
async execute({ occurrence, checkpoint }) {
|
|
592
636
|
const payload = occurrence?.payload;
|
|
593
637
|
const expectedFlow = automationEventFlowSnapshot(payload?.flow);
|
|
594
638
|
const expectedTrigger = automationEventTriggerSnapshot(payload?.trigger);
|
|
@@ -605,7 +649,12 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
605
649
|
);
|
|
606
650
|
}
|
|
607
651
|
|
|
608
|
-
const recovered = existingExecutionResult(
|
|
652
|
+
const recovered = existingExecutionResult(
|
|
653
|
+
db,
|
|
654
|
+
occurrence,
|
|
655
|
+
expectedFlow,
|
|
656
|
+
now,
|
|
657
|
+
);
|
|
609
658
|
if (recovered) return recovered;
|
|
610
659
|
|
|
611
660
|
const currentFlow = getFlow(db, expectedFlow.id);
|
|
@@ -630,14 +679,24 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
630
679
|
);
|
|
631
680
|
}
|
|
632
681
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
682
|
+
checkpoint({
|
|
683
|
+
executionId: automationEventExecutionId(occurrence.id),
|
|
684
|
+
phase: "before_execution_reservation",
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
try {
|
|
688
|
+
reserveAutomationExecutionAuthority(
|
|
689
|
+
db,
|
|
690
|
+
currentFlow,
|
|
691
|
+
occurrence.id,
|
|
692
|
+
payload.executionAuthority,
|
|
693
|
+
payload.executionAuthorityDigest,
|
|
694
|
+
{ now },
|
|
695
|
+
);
|
|
696
|
+
} catch (error) {
|
|
697
|
+
persistAutomationEventIncident(db, occurrence, currentFlow, error, now);
|
|
698
|
+
throw error;
|
|
699
|
+
}
|
|
641
700
|
|
|
642
701
|
let execution;
|
|
643
702
|
try {
|
|
@@ -645,8 +704,19 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
645
704
|
inputData: { event: expectedEvent },
|
|
646
705
|
triggerType: TRIGGER_TYPE.EVENT,
|
|
647
706
|
executionId: automationEventExecutionId(occurrence.id),
|
|
707
|
+
executionAuthority: payload.executionAuthority,
|
|
708
|
+
assertRuntimeBoundary: assertAutomationRuntimeBoundary,
|
|
648
709
|
});
|
|
649
710
|
} catch (cause) {
|
|
711
|
+
if (classifyAutomationBoundaryError(cause)) {
|
|
712
|
+
persistAutomationEventIncident(
|
|
713
|
+
db,
|
|
714
|
+
occurrence,
|
|
715
|
+
currentFlow,
|
|
716
|
+
cause,
|
|
717
|
+
now,
|
|
718
|
+
);
|
|
719
|
+
}
|
|
650
720
|
if (cause?.code === "AUTOMATION_EXECUTION_OUTCOME_UNKNOWN") {
|
|
651
721
|
throw eventError(
|
|
652
722
|
"AUTOMATION_EVENT_OUTCOME_UNKNOWN",
|
|
@@ -664,8 +734,14 @@ export function createAutomationEventAdapter({ db, now = Date.now } = {}) {
|
|
|
664
734
|
{ executionId: execution.id, error: execution.error },
|
|
665
735
|
);
|
|
666
736
|
}
|
|
737
|
+
resolveAutomationExecutionIncidentsForSucceededRun(db, execution.id, {
|
|
738
|
+
now,
|
|
739
|
+
});
|
|
667
740
|
return execution;
|
|
668
741
|
},
|
|
742
|
+
async resume(context) {
|
|
743
|
+
return this.execute(context);
|
|
744
|
+
},
|
|
669
745
|
classifyError() {
|
|
670
746
|
return { retryable: false };
|
|
671
747
|
},
|
|
@@ -3,6 +3,26 @@ import { Buffer } from "node:buffer";
|
|
|
3
3
|
|
|
4
4
|
export const SCHEDULER_SCHEMA_VERSION = 1;
|
|
5
5
|
export const AUTHORITY_ENVELOPE_VERSION = 1;
|
|
6
|
+
export const RUNTIME_CONTROL_SCHEMA_VERSION = 1;
|
|
7
|
+
|
|
8
|
+
export const RUNTIME_PAUSE_RESUME = Object.freeze({
|
|
9
|
+
NONE: "none",
|
|
10
|
+
CHECKPOINT_V1: "checkpoint_v1",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const RUNTIME_CONTROL_SAFE_POINTS = Object.freeze({
|
|
14
|
+
BEFORE_EXECUTE: "before_execute",
|
|
15
|
+
ADAPTER_CHECKPOINT: "adapter_checkpoint",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const RUNTIME_CONTROL_FIELDS = new Set([
|
|
19
|
+
"schemaVersion",
|
|
20
|
+
"pauseResume",
|
|
21
|
+
"safePoints",
|
|
22
|
+
]);
|
|
23
|
+
const RUNTIME_CONTROL_SAFE_POINT_SET = new Set(
|
|
24
|
+
Object.values(RUNTIME_CONTROL_SAFE_POINTS),
|
|
25
|
+
);
|
|
6
26
|
|
|
7
27
|
export const OCCURRENCE_STATUS = Object.freeze({
|
|
8
28
|
QUEUED: "queued",
|
|
@@ -286,6 +306,77 @@ export function normalizeHistoryLimit(value = DEFAULT_HISTORY_LIMIT) {
|
|
|
286
306
|
return Math.min(value, MAX_HISTORY_LIMIT);
|
|
287
307
|
}
|
|
288
308
|
|
|
309
|
+
/**
|
|
310
|
+
* Normalize the durable runtime-control capability advertised by an adapter.
|
|
311
|
+
*
|
|
312
|
+
* Missing declarations are deliberately treated as unsupported. A checkpoint
|
|
313
|
+
* declaration is evidence, not a hint: only the two kernel-owned safe points
|
|
314
|
+
* are accepted and every persisted pause request is bound to these canonical
|
|
315
|
+
* bytes.
|
|
316
|
+
*/
|
|
317
|
+
export function normalizeRuntimeControlCapability(input) {
|
|
318
|
+
if (input === undefined || input === null) {
|
|
319
|
+
return Object.freeze({
|
|
320
|
+
schemaVersion: RUNTIME_CONTROL_SCHEMA_VERSION,
|
|
321
|
+
pauseResume: RUNTIME_PAUSE_RESUME.NONE,
|
|
322
|
+
safePoints: Object.freeze([]),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
const capability = assertPlainObject(input, "runtimeControl");
|
|
326
|
+
assertKnownFields(capability, RUNTIME_CONTROL_FIELDS, "runtimeControl");
|
|
327
|
+
if (
|
|
328
|
+
capability.schemaVersion !== undefined &&
|
|
329
|
+
capability.schemaVersion !== RUNTIME_CONTROL_SCHEMA_VERSION
|
|
330
|
+
) {
|
|
331
|
+
throw invalidArgument(
|
|
332
|
+
`runtimeControl.schemaVersion must be ${RUNTIME_CONTROL_SCHEMA_VERSION}`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
const pauseResume = capability.pauseResume ?? RUNTIME_PAUSE_RESUME.NONE;
|
|
336
|
+
if (!Object.values(RUNTIME_PAUSE_RESUME).includes(pauseResume)) {
|
|
337
|
+
throw invalidArgument(
|
|
338
|
+
"runtimeControl.pauseResume must be none or checkpoint_v1",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
const inputSafePoints = capability.safePoints ?? [];
|
|
342
|
+
if (!Array.isArray(inputSafePoints)) {
|
|
343
|
+
throw invalidArgument("runtimeControl.safePoints must be an array");
|
|
344
|
+
}
|
|
345
|
+
const safePoints = [
|
|
346
|
+
...new Set(
|
|
347
|
+
inputSafePoints.map((safePoint, index) =>
|
|
348
|
+
normalizeIdentifier(safePoint, `runtimeControl.safePoints[${index}]`, {
|
|
349
|
+
maxLength: 64,
|
|
350
|
+
}),
|
|
351
|
+
),
|
|
352
|
+
),
|
|
353
|
+
].sort();
|
|
354
|
+
const unknownSafePoints = safePoints.filter(
|
|
355
|
+
(safePoint) => !RUNTIME_CONTROL_SAFE_POINT_SET.has(safePoint),
|
|
356
|
+
);
|
|
357
|
+
if (unknownSafePoints.length > 0) {
|
|
358
|
+
throw invalidArgument("runtimeControl.safePoints contains unknown values", {
|
|
359
|
+
safePoints: unknownSafePoints,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
if (
|
|
363
|
+
(pauseResume === RUNTIME_PAUSE_RESUME.NONE && safePoints.length !== 0) ||
|
|
364
|
+
(pauseResume === RUNTIME_PAUSE_RESUME.CHECKPOINT_V1 &&
|
|
365
|
+
safePoints.length === 0)
|
|
366
|
+
) {
|
|
367
|
+
throw invalidArgument(
|
|
368
|
+
pauseResume === RUNTIME_PAUSE_RESUME.NONE
|
|
369
|
+
? "runtimeControl.safePoints must be empty when pauseResume is none"
|
|
370
|
+
: "runtimeControl.safePoints must declare at least one checkpoint",
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return Object.freeze({
|
|
374
|
+
schemaVersion: RUNTIME_CONTROL_SCHEMA_VERSION,
|
|
375
|
+
pauseResume,
|
|
376
|
+
safePoints: Object.freeze(safePoints),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
289
380
|
/**
|
|
290
381
|
* Stable identity for one logical firing. All adapters must supply the same
|
|
291
382
|
* triggerKey for the same logical schedule. Including jobRevision prevents an
|