chainlesschain 0.166.0 → 0.166.2
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-D5_pwQ7M.js → AIOps-BnZ24WRk.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DgoYO32D.js → ActionButton-ivXY7MGb.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BIpoZ4QH.js → Analytics-BQS4OD-k.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BJ3XrCEe.js → AppLayout-wOBuOKTO.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-DUFs_izZ.js → Artifacts-BYuC4nw-.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BZdPwwfL.js → Audit-BIeVfBt6.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DK9Fn-8B.js → BackgroundAgents-hIuyhmJ7.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Bd6bOhV_.js → Backup-B6ohdgPQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CRnWlXrd.js → BaseInput-BzgYCw6h.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-D1G_yEl6.js → Chat-CwahvZU1.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DDAlEtm-.js → ChatBubbleRenderer-B-1IZ_k8.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CP35SH7R.js → Checkbox-26EDP2x0.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DugeL4pj.js → Codegen-DYLw1Zvd.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DPuJsvKg.js → Col-DH_zAvD-.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CZqNjNNp.js → Community-DURrTXJu.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-eeyIPEAb.js → Compact-CQyBqN0p.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-MGzTw87k.js → Compliance-a0DfgKHD.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D3goVIA5.js → Cowork-C6fY_5Ke.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-9HA8hLLG.js → Cron-DOwmpbTe.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DCSOX3l6.js → Crosschain-DEPOIEUs.js} +1 -1
- package/src/assets/web-panel/assets/{DID-ji3lN5Bc.js → DID-DaKc-CEV.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DlZvF1E5.js → Dashboard-C-0KLXq5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-2FyZcow9.js → Dropdown-CnsDukzr.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-qH2bSpSY.js → EmailListRenderer-Bi_ACD4N.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bfx47wZj.js → FamilyGuardDashboard-BZASpnx3.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DhGcZbIv.js → Federation-43oTtzyp.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BMgi9_YQ.js → FormItemContext-oyVadnkZ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DugMac-4.js → GenericCardRenderer-Cwvfd6TZ.js} +1 -1
- package/src/assets/web-panel/assets/{Git-D943BpH-.js → Git-Cgi2wvU0.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DZ66a8Aa.js → Governance-l8TWDKZ0.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-B00Mp2tN.js → Inference-Dc8j6TaO.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BdwXXO2Q.js → KnowledgeGraph-DgcO-Fwz.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-_MVXyYWc.js → Logs-DjiZpiwa.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-hOTeNyi7.js → MarkdownRenderer-B5MFCi0N.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CxeHRsnO.js → Marketplace-BeDn1hFc.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BOieTxZ4.js → McpTools-PGdVfWK6.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-BQD26CM3.js → Memory-e76lY3a1.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CPJ8ZlvR.js → MobileBridge-DKlQqot_.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D7qRVu0y.js → MobileProjects-C4g6WOEr.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BOyndGuk.js → Mtc-1j3NpRQq.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CXQ_8ZoR.js → MtcAudit-DgwiVd1C.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-32Oh9KbH.js → Multisig-Dzg5lZZY.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CFAd00CX.js → NLProgramming-D7GYhst-.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BBCY_qJA.js → Notes-Blcfvaic.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-uaQ0PdmW.js → NotificationSettings-Dc4Y6vfr.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-dgM87XIn.js +1 -0
- package/src/assets/web-panel/assets/{Organization-CmFn7Oeo.js → Organization-DBXq024L.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-R_avat3a.js → Overflow-CFPbdx1Y.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BIhLFmkY.js → P2P-w7z61NR5.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DTJjk-_i.js → PdhVaultBrowser-B7eGGZOa.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CkzYv8Yb.js → Permissions-Df5Cl50D.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub--0QvN2Nz.js → PersonalDataHub-C-We7RF6.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Bfw5gQCH.js → Pipeline-B6fHPnKy.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DicjKfax.js → Privacy-B85BdiIc.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DUl_ykBp.js → ProjectInit-CcOyazSe.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CPYDvN8a.js → ProjectSettings-BE53KfuU.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DnZ9_fAr.js → Projects-DOvMOoe_.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BX1jP31w.js → Providers-5l7sT9lq.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-COab5qUl.js → QrScannerModal-DE37RXy4.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CdKQ_JZ0.js → QuickAsk-BGmGCo7j.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BPFHg8pK.js → Recommend-BFdx8Nrm.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-CqrfTJvO.js → RemoteSession-C1w-j_hB.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DM-l8Pb3.js → Reputation-BOvn-10y.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CKGBTeCw.js → Row-db-a1YO9.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Wb0YHIkc.js → RssFeed-DeS43ZGM.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DiBjxKv-.js → Search-BaQYNfeb.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BAVEmbWP.js → Security-yt1ESHDg.js} +4 -4
- package/src/assets/web-panel/assets/{Services-A_a3TIRn.js → Services-BcNqPUsS.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CloHJ-I7.js → Skeleton-X3ceUQWf.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CkvV5hKa.js → Skills-DmYeax6C.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BYozxxsV.js → Sla-C_fdfbuJ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CWianiZF.js → SpeechSettings-CDqohXXQ.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BR4G75do.js → SyncSettings-B25maYEY.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D6gyUVrM.js → Tasks-3ILwauYw.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DbfYIRzT.js → Templates-DUH0iuc4.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BHutvukT.js → Tenant-DrvhHf9T.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CYEiy2wJ.js → Terminal-C2lJVYHA.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D9s1AxfE.js → TimelineRenderer-C1Hf_jYn.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-ij9vxnoy.js → Tokens-DniS1NpS.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dq_DCVeQ.js → Trigger-4jrcb8oA.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Bs6wdfoH.js → Trust-DiBte55w.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CLazyr5t.js → UkeySign-CZRtc4VB.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-viWotIHA.js → VideoEditing-BtYGJDmg.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DlZ-A78d.js → Wallet-D1nrTm7H.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DSmOND3Y.js → WebAuthn-DI91acpx.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-BempLOJi.js → WorkflowEditor-BtQjI50d.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CyJzf4JI.js → chat-I4A-f4FC.js} +1 -1
- package/src/assets/web-panel/assets/{colors-C62q7WrL.js → colors-QZkDkFVt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-7dYM1KW3.js → compact-item--FtCdzpr.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CYTy7z2Q.js → createContext-CaIKIKxP.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-D7qVrkrO.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DHoclUqm.js → hasIn-DLYUII6-.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW86F2Zw.js → index-0DYBQ-6e.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2efftJb.js → index-21I79XSf.js} +1 -1
- package/src/assets/web-panel/assets/{index-4gWv-Ubg.js → index-3rVtgqa2.js} +1 -1
- package/src/assets/web-panel/assets/{index-QXtrdrub.js → index-B65QxJaG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMxvWauP.js → index-BGWSmE3G.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dyq9tK5u.js → index-BLGJmgmx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYKwGrAn.js → index-BcgtvJbC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMRmxFU_.js → index-BebTT7iR.js} +1 -1
- package/src/assets/web-panel/assets/{index-XrsAHHQN.js → index-BkJgWW_L.js} +1 -1
- package/src/assets/web-panel/assets/{index-2C8Xx2UO.js → index-Bs0qPPFb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnEZ4ZHj.js → index-BvdwNnHC.js} +3 -3
- package/src/assets/web-panel/assets/{index-DkczsbiP.js → index-BxPoPofq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvEtHpLc.js → index-C3PnIEOH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DK84lAS0.js → index-CBtII-nl.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKdv8cju.js → index-CL4c5D6G.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjeViaU-.js → index-CQ7OBHka.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhJjifZe.js → index-CXaeZCRP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BghdJ87W.js → index-CgMm85BY.js} +1 -1
- package/src/assets/web-panel/assets/{index-lHfNxiB5.js → index-Ch0IFpGy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJzZaPlb.js → index-CisYjy88.js} +1 -1
- package/src/assets/web-panel/assets/{index-DghnbMlY.js → index-D2xETCkj.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5YWYlW9.js → index-D4CXFU-w.js} +1 -1
- package/src/assets/web-panel/assets/index-D9Y_ZwZn.js +1 -0
- package/src/assets/web-panel/assets/{index-LK1MZoBj.js → index-DA_TPdzX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOFAwpUK.js → index-DAuf8r5o.js} +1 -1
- package/src/assets/web-panel/assets/{index-DA-_807C.js → index-DGLHSCHM.js} +1 -1
- package/src/assets/web-panel/assets/{index-9uR7rc9a.js → index-DJrd_CR3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVTnruDt.js → index-DK2r6Bv7.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZXYp76IB.js → index-DVitI3UQ.js} +1 -1
- package/src/assets/web-panel/assets/index-D_cPes4G.js +1 -0
- package/src/assets/web-panel/assets/{index-Bzz0aJ86.js → index-Deg2S9ZJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHgSRimK.js → index-Du2wUavy.js} +1 -1
- package/src/assets/web-panel/assets/{index-94ktimZG.js → index-OQPIS_am.js} +1 -1
- package/src/assets/web-panel/assets/{index-3pI7AfmD.js → index-Ooo_5jHo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNLD3nZY.js → index-Ygi2rYL1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZRBvN6i.js → index-eTqL91aV.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6BC4mmz.js → index-hfiZ-i2b.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAYanuSk.js → index-vfiDxdN2.js} +1 -1
- package/src/assets/web-panel/assets/{index-NuNCstRO.js → index-yayaayEA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CwCl5sFQ.js → initDefaultProps-DDkTvw8k.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BFJFy70N.js → motion-fkr96tN5.js} +1 -1
- package/src/assets/web-panel/assets/{move-ByrtVGq8.js → move-CGBV0SV2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BFiH1mT3.js → mtc-parser-DAsCliFt.js} +1 -1
- package/src/assets/web-panel/assets/{omit-bdvgpivj.js → omit-BM8PMmKN.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BlfuuE3s.js → pickAttrs-C6SdWoTK.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-t2SC3DMy.js → placementArrow-BG0EVu-d.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CoEXv2Li.js → responsiveObserve-L10IvfYC.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BfXCf0Fu.js → slide-TddIkbrn.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DNsdyOuS.js → statusUtils-FlveF2Yn.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-9aHukmoG.js → styleChecker-BrXRyebg.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-dTkPxzHN.js → useFlexGapSupport-B7_oiDXZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DINwfItP.js → useFs-CQYeRYWb.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Bj_RriBV.js → usePersonalDataHub-DspIkhVi.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BzRAZrTc.js → vnode-BCwmwMAW.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CamPjkeK.js → zoom-B0cEGluV.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/team.js +74 -2
- package/src/data/changelog.json +26 -0
- package/src/generated/cc-agent-protocol.schema.json +49 -0
- package/src/lib/agent-team/team-mailbox.js +508 -2
- package/src/lib/agent-team/team-message-bridge.js +644 -0
- package/src/lib/agent-team/team-message-tools.js +198 -0
- package/src/lib/agent-team/team-runner.js +57 -3
- package/src/lib/graph-kernel/compiler.js +548 -16
- package/src/lib/graph-kernel/runtime.js +1158 -30
- package/src/lib/graph-kernel/trace-reducer.js +101 -7
- package/src/lib/subagent-inheritance.js +5 -3
- package/src/runtime/agent-core.js +38 -0
- package/src/runtime/headless-runner.js +36 -3
- package/src/assets/web-panel/assets/OrderTableRenderer-kpBxJ-SG.js +0 -1
- package/src/assets/web-panel/assets/devWarning-BdTg_AMF.js +0 -1
- package/src/assets/web-panel/assets/index-B6WvFV4N.js +0 -1
- package/src/assets/web-panel/assets/index-BKgfptzA.js +0 -1
|
@@ -10,7 +10,10 @@ const graphProtocolSchema = JSON.parse(
|
|
|
10
10
|
);
|
|
11
11
|
|
|
12
12
|
export const GRAPH_DEFINITION_VERSION = 1;
|
|
13
|
-
export const GRAPH_DEFINITION_MIN_VERSION =
|
|
13
|
+
export const GRAPH_DEFINITION_MIN_VERSION = Math.max(
|
|
14
|
+
0,
|
|
15
|
+
GRAPH_DEFINITION_VERSION - 1,
|
|
16
|
+
);
|
|
14
17
|
export const GRAPH_COMPILE_ERROR = "CC_GRAPH_COMPILE_FAILED";
|
|
15
18
|
|
|
16
19
|
const compiledGraphs = new WeakSet();
|
|
@@ -272,7 +275,14 @@ export function writeScopesOverlap(left, right) {
|
|
|
272
275
|
return a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`);
|
|
273
276
|
}
|
|
274
277
|
|
|
275
|
-
function loopMultipliers(
|
|
278
|
+
function loopMultipliers(
|
|
279
|
+
definition,
|
|
280
|
+
diagnostics,
|
|
281
|
+
nodeIds,
|
|
282
|
+
nodes,
|
|
283
|
+
dependencies,
|
|
284
|
+
compensationByNode,
|
|
285
|
+
) {
|
|
276
286
|
const multiplier = new Map([...nodeIds].map((id) => [id, 1]));
|
|
277
287
|
const membership = new Map();
|
|
278
288
|
for (const [index, loop] of definition.loops.entries()) {
|
|
@@ -300,6 +310,27 @@ function loopMultipliers(definition, diagnostics, nodeIds) {
|
|
|
300
310
|
owners.push(loop.id);
|
|
301
311
|
membership.set(nodeId, owners);
|
|
302
312
|
multiplier.set(nodeId, multiplier.get(nodeId) * loop.maxIterations);
|
|
313
|
+
const node = nodes.get(nodeId);
|
|
314
|
+
if (
|
|
315
|
+
node &&
|
|
316
|
+
!["none", "read"].includes(node.effectClass || "none") &&
|
|
317
|
+
!compensationByNode.has(nodeId)
|
|
318
|
+
) {
|
|
319
|
+
diagnostic(
|
|
320
|
+
diagnostics,
|
|
321
|
+
"GRAPH_LOOP_EFFECT_COMPENSATION_REQUIRED",
|
|
322
|
+
`#/loops/${index}/nodeIds`,
|
|
323
|
+
`effectful loop node requires an isolated compensation handler: ${nodeId}`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (node?.kind === "subgraph") {
|
|
327
|
+
diagnostic(
|
|
328
|
+
diagnostics,
|
|
329
|
+
"GRAPH_LOOP_SUBGRAPH_UNSUPPORTED",
|
|
330
|
+
`#/loops/${index}/nodeIds`,
|
|
331
|
+
`subgraph calls cannot be nested inside a loop region: ${nodeId}`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
303
334
|
}
|
|
304
335
|
if (
|
|
305
336
|
!loop.nodeIds.includes(loop.entryNodeId) ||
|
|
@@ -312,6 +343,67 @@ function loopMultipliers(definition, diagnostics, nodeIds) {
|
|
|
312
343
|
"loop entry and exit must be members of the region",
|
|
313
344
|
);
|
|
314
345
|
}
|
|
346
|
+
const regionNodeIds = new Set(loop.nodeIds);
|
|
347
|
+
const regionOutgoing = new Map(
|
|
348
|
+
loop.nodeIds.map((nodeId) => [nodeId, new Set()]),
|
|
349
|
+
);
|
|
350
|
+
for (const nodeId of loop.nodeIds) {
|
|
351
|
+
for (const dependency of dependencies.get(nodeId) || []) {
|
|
352
|
+
if (regionNodeIds.has(dependency)) {
|
|
353
|
+
regionOutgoing.get(dependency).add(nodeId);
|
|
354
|
+
} else if (nodeId !== loop.entryNodeId) {
|
|
355
|
+
diagnostic(
|
|
356
|
+
diagnostics,
|
|
357
|
+
"GRAPH_LOOP_ENTRY_LEAK",
|
|
358
|
+
`#/loops/${index}/entryNodeId`,
|
|
359
|
+
`external dependencies may only enter a loop through its entry node: ${nodeId} depends on ${dependency}`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const canReach = (source, target) => {
|
|
365
|
+
const pending = [source];
|
|
366
|
+
const seen = new Set();
|
|
367
|
+
while (pending.length) {
|
|
368
|
+
const current = pending.pop();
|
|
369
|
+
if (current === target) return true;
|
|
370
|
+
if (seen.has(current)) continue;
|
|
371
|
+
seen.add(current);
|
|
372
|
+
pending.push(...(regionOutgoing.get(current) || []));
|
|
373
|
+
}
|
|
374
|
+
return false;
|
|
375
|
+
};
|
|
376
|
+
for (const nodeId of loop.nodeIds) {
|
|
377
|
+
if (!canReach(loop.entryNodeId, nodeId)) {
|
|
378
|
+
diagnostic(
|
|
379
|
+
diagnostics,
|
|
380
|
+
"GRAPH_LOOP_NODE_UNREACHABLE_FROM_ENTRY",
|
|
381
|
+
`#/loops/${index}/nodeIds`,
|
|
382
|
+
`loop node is unreachable from its entry: ${nodeId}`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (!canReach(nodeId, loop.exitNodeId)) {
|
|
386
|
+
diagnostic(
|
|
387
|
+
diagnostics,
|
|
388
|
+
"GRAPH_LOOP_NODE_CANNOT_REACH_EXIT",
|
|
389
|
+
`#/loops/${index}/nodeIds`,
|
|
390
|
+
`loop node cannot reach its exit: ${nodeId}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
for (const node of definition.nodes) {
|
|
395
|
+
if (regionNodeIds.has(node.id)) continue;
|
|
396
|
+
for (const dependency of dependencies.get(node.id) || []) {
|
|
397
|
+
if (regionNodeIds.has(dependency) && dependency !== loop.exitNodeId) {
|
|
398
|
+
diagnostic(
|
|
399
|
+
diagnostics,
|
|
400
|
+
"GRAPH_LOOP_EXIT_LEAK",
|
|
401
|
+
`#/loops/${index}/exitNodeId`,
|
|
402
|
+
`nodes outside a loop may only depend on its exit node: ${node.id} depends on ${dependency}`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
315
407
|
}
|
|
316
408
|
for (const [nodeId, owners] of membership) {
|
|
317
409
|
if (owners.length > 1) {
|
|
@@ -327,12 +419,19 @@ function loopMultipliers(definition, diagnostics, nodeIds) {
|
|
|
327
419
|
return multiplier;
|
|
328
420
|
}
|
|
329
421
|
|
|
330
|
-
function budgetUpperBound(
|
|
422
|
+
function budgetUpperBound(
|
|
423
|
+
definition,
|
|
424
|
+
multiplier,
|
|
425
|
+
diagnostics,
|
|
426
|
+
subgraphCalls = new Map(),
|
|
427
|
+
) {
|
|
331
428
|
const total = Object.fromEntries(BUDGET_FIELDS.map((field) => [field, 0]));
|
|
332
429
|
for (const node of definition.nodes) {
|
|
430
|
+
const subgraphBudget = subgraphCalls.get(node.id)?.budget;
|
|
333
431
|
for (const field of BUDGET_FIELDS) {
|
|
334
432
|
total[field] +=
|
|
335
|
-
Number(node.budget?.[field]
|
|
433
|
+
Number(subgraphBudget?.[field] ?? node.budget?.[field] ?? 0) *
|
|
434
|
+
multiplier.get(node.id);
|
|
336
435
|
}
|
|
337
436
|
}
|
|
338
437
|
for (const field of BUDGET_FIELDS) {
|
|
@@ -350,9 +449,172 @@ function budgetUpperBound(definition, multiplier, diagnostics) {
|
|
|
350
449
|
return total;
|
|
351
450
|
}
|
|
352
451
|
|
|
452
|
+
function subgraphTarget(registry, definitionId) {
|
|
453
|
+
return registry instanceof Map
|
|
454
|
+
? registry.get(definitionId)
|
|
455
|
+
: registry[definitionId];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function requiredPortNames(ports) {
|
|
459
|
+
return (ports || [])
|
|
460
|
+
.filter((port) => port.required !== false)
|
|
461
|
+
.map((port) => port.name);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function validateSubgraphPortBindings(
|
|
465
|
+
call,
|
|
466
|
+
callNode,
|
|
467
|
+
targetDefinition,
|
|
468
|
+
callIndex,
|
|
469
|
+
diagnostics,
|
|
470
|
+
) {
|
|
471
|
+
const parentInputs = new Map(
|
|
472
|
+
(callNode?.inputs || []).map((port) => [port.name, port]),
|
|
473
|
+
);
|
|
474
|
+
const parentOutputs = new Map(
|
|
475
|
+
(callNode?.outputs || []).map((port) => [port.name, port]),
|
|
476
|
+
);
|
|
477
|
+
const childNodes = new Map(
|
|
478
|
+
(targetDefinition?.nodes || []).map((node) => [node.id, node]),
|
|
479
|
+
);
|
|
480
|
+
const mappedInputs = new Set();
|
|
481
|
+
const mappedChildInputs = new Set();
|
|
482
|
+
const mappedOutputs = new Set();
|
|
483
|
+
|
|
484
|
+
const validateBindings = (direction) => {
|
|
485
|
+
const bindings = call[`${direction}Bindings`] || [];
|
|
486
|
+
for (const [bindingIndex, binding] of bindings.entries()) {
|
|
487
|
+
const path = `#/subgraphCalls/${callIndex}/${direction}Bindings/${bindingIndex}`;
|
|
488
|
+
const parentPorts = direction === "input" ? parentInputs : parentOutputs;
|
|
489
|
+
const parentPort = parentPorts.get(binding.parentPort);
|
|
490
|
+
const childNode = childNodes.get(binding.childNodeId);
|
|
491
|
+
const childPorts = new Map(
|
|
492
|
+
(childNode?.[direction === "input" ? "inputs" : "outputs"] || []).map(
|
|
493
|
+
(port) => [port.name, port],
|
|
494
|
+
),
|
|
495
|
+
);
|
|
496
|
+
const childPort = childPorts.get(binding.childPort);
|
|
497
|
+
const parentSet = direction === "input" ? mappedInputs : mappedOutputs;
|
|
498
|
+
if (parentSet.has(binding.parentPort)) {
|
|
499
|
+
diagnostic(
|
|
500
|
+
diagnostics,
|
|
501
|
+
"GRAPH_SUBGRAPH_DUPLICATE_PARENT_PORT",
|
|
502
|
+
`${path}/parentPort`,
|
|
503
|
+
`subgraph ${direction} port is mapped more than once: ${binding.parentPort}`,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
parentSet.add(binding.parentPort);
|
|
507
|
+
if (!parentPort) {
|
|
508
|
+
diagnostic(
|
|
509
|
+
diagnostics,
|
|
510
|
+
"GRAPH_SUBGRAPH_UNKNOWN_PARENT_PORT",
|
|
511
|
+
`${path}/parentPort`,
|
|
512
|
+
`unknown parent ${direction} port: ${binding.parentPort}`,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
if (!childNode) {
|
|
516
|
+
diagnostic(
|
|
517
|
+
diagnostics,
|
|
518
|
+
"GRAPH_SUBGRAPH_UNKNOWN_CHILD_NODE",
|
|
519
|
+
`${path}/childNodeId`,
|
|
520
|
+
`unknown child node: ${binding.childNodeId}`,
|
|
521
|
+
);
|
|
522
|
+
} else if (!childPort) {
|
|
523
|
+
diagnostic(
|
|
524
|
+
diagnostics,
|
|
525
|
+
"GRAPH_SUBGRAPH_UNKNOWN_CHILD_PORT",
|
|
526
|
+
`${path}/childPort`,
|
|
527
|
+
`unknown child ${direction} port: ${binding.childNodeId}.${binding.childPort}`,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
if (direction === "input") {
|
|
531
|
+
const childKey = `${binding.childNodeId}\0${binding.childPort}`;
|
|
532
|
+
if (mappedChildInputs.has(childKey)) {
|
|
533
|
+
diagnostic(
|
|
534
|
+
diagnostics,
|
|
535
|
+
"GRAPH_SUBGRAPH_DUPLICATE_CHILD_INPUT",
|
|
536
|
+
path,
|
|
537
|
+
`child input is mapped more than once: ${binding.childNodeId}.${binding.childPort}`,
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
mappedChildInputs.add(childKey);
|
|
541
|
+
}
|
|
542
|
+
const producer = direction === "input" ? parentPort : childPort;
|
|
543
|
+
const consumer = direction === "input" ? childPort : parentPort;
|
|
544
|
+
if (
|
|
545
|
+
producer &&
|
|
546
|
+
consumer &&
|
|
547
|
+
!isPortSchemaAssignable(producer.schema, consumer.schema)
|
|
548
|
+
) {
|
|
549
|
+
diagnostic(
|
|
550
|
+
diagnostics,
|
|
551
|
+
"GRAPH_SUBGRAPH_PORT_TYPE_MISMATCH",
|
|
552
|
+
path,
|
|
553
|
+
direction === "input"
|
|
554
|
+
? `${callNode.id}.${binding.parentPort} is not assignable to ${binding.childNodeId}.${binding.childPort}`
|
|
555
|
+
: `${binding.childNodeId}.${binding.childPort} is not assignable to ${callNode.id}.${binding.parentPort}`,
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
validateBindings("input");
|
|
562
|
+
validateBindings("output");
|
|
563
|
+
|
|
564
|
+
for (const portName of requiredPortNames(callNode?.inputs)) {
|
|
565
|
+
if (!mappedInputs.has(portName)) {
|
|
566
|
+
diagnostic(
|
|
567
|
+
diagnostics,
|
|
568
|
+
"GRAPH_SUBGRAPH_REQUIRED_INPUT_UNMAPPED",
|
|
569
|
+
`#/subgraphCalls/${callIndex}/inputBindings`,
|
|
570
|
+
`required parent input is not mapped into the child graph: ${portName}`,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
for (const portName of requiredPortNames(callNode?.outputs)) {
|
|
575
|
+
if (!mappedOutputs.has(portName)) {
|
|
576
|
+
diagnostic(
|
|
577
|
+
diagnostics,
|
|
578
|
+
"GRAPH_SUBGRAPH_REQUIRED_OUTPUT_UNMAPPED",
|
|
579
|
+
`#/subgraphCalls/${callIndex}/outputBindings`,
|
|
580
|
+
`required parent output is not mapped from the child graph: ${portName}`,
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const internallyBoundInputs = new Set(
|
|
586
|
+
(targetDefinition?.edges || [])
|
|
587
|
+
.filter((edge) => edge.kind === "data")
|
|
588
|
+
.map((edge) => `${edge.to}\0${edge.toPort}`),
|
|
589
|
+
);
|
|
590
|
+
for (const childNode of childNodes.values()) {
|
|
591
|
+
for (const portName of requiredPortNames(childNode.inputs)) {
|
|
592
|
+
const key = `${childNode.id}\0${portName}`;
|
|
593
|
+
if (
|
|
594
|
+
!mappedChildInputs.has(key) &&
|
|
595
|
+
!internallyBoundInputs.has(key) &&
|
|
596
|
+
!Object.hasOwn(childNode.inputBindings || {}, portName)
|
|
597
|
+
) {
|
|
598
|
+
diagnostic(
|
|
599
|
+
diagnostics,
|
|
600
|
+
"GRAPH_SUBGRAPH_REQUIRED_CHILD_INPUT_UNBOUND",
|
|
601
|
+
`#/subgraphCalls/${callIndex}/inputBindings`,
|
|
602
|
+
`required child input has no internal or parent binding: ${childNode.id}.${portName}`,
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
353
609
|
function validateSubgraphs(definition, nodeIds, options, diagnostics) {
|
|
354
610
|
const registry = options.subgraphs || new Map();
|
|
355
611
|
const calls = new Map();
|
|
612
|
+
const registryDefinitions = new Map();
|
|
613
|
+
const registryEntries =
|
|
614
|
+
registry instanceof Map ? registry.entries() : Object.entries(registry);
|
|
615
|
+
for (const [id, value] of registryEntries) {
|
|
616
|
+
registryDefinitions.set(id, stableValue(value?.definition || value));
|
|
617
|
+
}
|
|
356
618
|
for (const [index, call] of definition.subgraphCalls.entries()) {
|
|
357
619
|
if (!nodeIds.has(call.nodeId)) {
|
|
358
620
|
diagnostic(
|
|
@@ -362,10 +624,24 @@ function validateSubgraphs(definition, nodeIds, options, diagnostics) {
|
|
|
362
624
|
`subgraph call node does not exist: ${call.nodeId}`,
|
|
363
625
|
);
|
|
364
626
|
}
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
627
|
+
const callNode = definition.nodes.find((node) => node.id === call.nodeId);
|
|
628
|
+
if (callNode && callNode.kind !== "subgraph") {
|
|
629
|
+
diagnostic(
|
|
630
|
+
diagnostics,
|
|
631
|
+
"GRAPH_SUBGRAPH_NODE_KIND_INVALID",
|
|
632
|
+
`#/subgraphCalls/${index}/nodeId`,
|
|
633
|
+
`subgraph call must target a node with kind=subgraph: ${call.nodeId}`,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
const target = subgraphTarget(registry, call.definitionId);
|
|
637
|
+
if (!target && options.allowUnresolvedSubgraphs !== true) {
|
|
638
|
+
diagnostic(
|
|
639
|
+
diagnostics,
|
|
640
|
+
"GRAPH_SUBGRAPH_DEFINITION_REQUIRED",
|
|
641
|
+
`#/subgraphCalls/${index}/definitionId`,
|
|
642
|
+
`subgraph definition must be available during compilation: ${call.definitionId}`,
|
|
643
|
+
);
|
|
644
|
+
}
|
|
369
645
|
if (target && target.revisionDigest !== call.revisionDigest) {
|
|
370
646
|
diagnostic(
|
|
371
647
|
diagnostics,
|
|
@@ -374,7 +650,44 @@ function validateSubgraphs(definition, nodeIds, options, diagnostics) {
|
|
|
374
650
|
`subgraph digest does not match pinned revision: ${call.definitionId}`,
|
|
375
651
|
);
|
|
376
652
|
}
|
|
377
|
-
|
|
653
|
+
const targetDefinition = target?.definition || target;
|
|
654
|
+
if (targetDefinition) {
|
|
655
|
+
validateSubgraphPortBindings(
|
|
656
|
+
call,
|
|
657
|
+
callNode,
|
|
658
|
+
targetDefinition,
|
|
659
|
+
index,
|
|
660
|
+
diagnostics,
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
const targetBudget = target?.budgetUpperBound || targetDefinition?.budget;
|
|
664
|
+
const budget = stableValue(
|
|
665
|
+
call.budget || callNode?.budget || targetDefinition?.budget || {},
|
|
666
|
+
);
|
|
667
|
+
for (const field of BUDGET_FIELDS) {
|
|
668
|
+
const required = Number(targetBudget?.[field] || 0);
|
|
669
|
+
const cap = budget[field];
|
|
670
|
+
if (required > 0 && (cap == null || Number(cap) < required)) {
|
|
671
|
+
diagnostic(
|
|
672
|
+
diagnostics,
|
|
673
|
+
"GRAPH_SUBGRAPH_BUDGET_SLICE_TOO_SMALL",
|
|
674
|
+
`#/subgraphCalls/${index}/budget/${field}`,
|
|
675
|
+
`subgraph ${field} budget slice ${cap ?? "unbounded"} is below the child upper bound ${required}`,
|
|
676
|
+
{ field, required, cap: cap ?? null },
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
calls.set(call.nodeId, { ...call, budget });
|
|
681
|
+
}
|
|
682
|
+
for (const [index, node] of definition.nodes.entries()) {
|
|
683
|
+
if (node.kind === "subgraph" && !calls.has(node.id)) {
|
|
684
|
+
diagnostic(
|
|
685
|
+
diagnostics,
|
|
686
|
+
"GRAPH_SUBGRAPH_CALL_REQUIRED",
|
|
687
|
+
`#/nodes/${index}`,
|
|
688
|
+
`subgraph node requires a pinned SubgraphCall: ${node.id}`,
|
|
689
|
+
);
|
|
690
|
+
}
|
|
378
691
|
}
|
|
379
692
|
|
|
380
693
|
const definitions = new Map();
|
|
@@ -411,15 +724,51 @@ function validateSubgraphs(definition, nodeIds, options, diagnostics) {
|
|
|
411
724
|
visited.add(id);
|
|
412
725
|
};
|
|
413
726
|
visit(definition.id);
|
|
414
|
-
return calls;
|
|
727
|
+
return { calls, definitions: registryDefinitions };
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function upcastGraphDefinitionV0(input) {
|
|
731
|
+
const definition = stableValue(input);
|
|
732
|
+
return {
|
|
733
|
+
schemaVersion: 1,
|
|
734
|
+
id: definition.id,
|
|
735
|
+
revision: definition.revision ?? 1,
|
|
736
|
+
triggers: definition.triggers || [],
|
|
737
|
+
regions: definition.regions || [],
|
|
738
|
+
nodes: (definition.nodes || []).map((node) => ({
|
|
739
|
+
...node,
|
|
740
|
+
kind: node.kind || "task",
|
|
741
|
+
dependsOn: node.dependsOn || [],
|
|
742
|
+
inputs: node.inputs || [],
|
|
743
|
+
outputs: node.outputs || [],
|
|
744
|
+
effectClass: node.effectClass || "none",
|
|
745
|
+
})),
|
|
746
|
+
edges: (definition.edges || []).map((edge, index) => ({
|
|
747
|
+
...edge,
|
|
748
|
+
id: edge.id || `edge-${index + 1}`,
|
|
749
|
+
kind: edge.kind || "control",
|
|
750
|
+
when: edge.when || "success",
|
|
751
|
+
})),
|
|
752
|
+
loops: definition.loops || [],
|
|
753
|
+
subgraphCalls: definition.subgraphCalls || [],
|
|
754
|
+
budget: definition.budget || {},
|
|
755
|
+
allowedCapabilities: definition.allowedCapabilities || [],
|
|
756
|
+
metadata: definition.metadata || {},
|
|
757
|
+
};
|
|
415
758
|
}
|
|
416
759
|
|
|
760
|
+
const BUILTIN_GRAPH_UPCASTERS = Object.freeze({
|
|
761
|
+
0: upcastGraphDefinitionV0,
|
|
762
|
+
});
|
|
763
|
+
|
|
417
764
|
function migrateDefinition(input, options) {
|
|
418
765
|
const sourceVersion = Number(input?.schemaVersion);
|
|
419
766
|
if (sourceVersion === GRAPH_DEFINITION_VERSION) {
|
|
420
767
|
return { definition: stableValue(input), migratedFrom: null };
|
|
421
768
|
}
|
|
422
|
-
const upcaster =
|
|
769
|
+
const upcaster =
|
|
770
|
+
options.upcasters?.[sourceVersion] ||
|
|
771
|
+
BUILTIN_GRAPH_UPCASTERS[sourceVersion];
|
|
423
772
|
if (
|
|
424
773
|
sourceVersion === GRAPH_DEFINITION_VERSION - 1 &&
|
|
425
774
|
typeof upcaster === "function"
|
|
@@ -518,6 +867,118 @@ export function compileGraphDefinition(input, options = {}) {
|
|
|
518
867
|
}
|
|
519
868
|
const nodes = new Map(definition.nodes.map((node) => [node.id, node]));
|
|
520
869
|
const nodeIds = new Set(nodes.keys());
|
|
870
|
+
const compensationTargets = new Map();
|
|
871
|
+
const declareCompensation = (sourceId, targetId) => {
|
|
872
|
+
if (!sourceId || !targetId) return;
|
|
873
|
+
const targets = compensationTargets.get(sourceId) || new Set();
|
|
874
|
+
targets.add(targetId);
|
|
875
|
+
compensationTargets.set(sourceId, targets);
|
|
876
|
+
};
|
|
877
|
+
for (const node of definition.nodes) {
|
|
878
|
+
if (node.compensationNodeId) {
|
|
879
|
+
declareCompensation(node.id, node.compensationNodeId);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
for (const edge of definition.edges) {
|
|
883
|
+
if (edge.kind === "compensation") {
|
|
884
|
+
declareCompensation(edge.from, edge.to);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
const compensationByNode = new Map();
|
|
888
|
+
const compensationOwners = new Map();
|
|
889
|
+
for (const [sourceId, targets] of compensationTargets) {
|
|
890
|
+
if (targets.size !== 1) {
|
|
891
|
+
diagnostic(
|
|
892
|
+
diagnostics,
|
|
893
|
+
"GRAPH_COMPENSATION_TARGET_CONFLICT",
|
|
894
|
+
"#/nodes",
|
|
895
|
+
`effect node declares multiple compensation targets: ${sourceId}`,
|
|
896
|
+
{ sourceId, targetIds: [...targets].sort() },
|
|
897
|
+
);
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
const [targetId] = targets;
|
|
901
|
+
const source = nodes.get(sourceId);
|
|
902
|
+
const target = nodes.get(targetId);
|
|
903
|
+
if (!target) {
|
|
904
|
+
diagnostic(
|
|
905
|
+
diagnostics,
|
|
906
|
+
"GRAPH_UNKNOWN_COMPENSATION_TARGET",
|
|
907
|
+
"#/nodes",
|
|
908
|
+
`unknown compensation target ${targetId} for ${sourceId}`,
|
|
909
|
+
{ sourceId, targetId },
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
if (!source || !target) continue;
|
|
913
|
+
if (sourceId === targetId) {
|
|
914
|
+
diagnostic(
|
|
915
|
+
diagnostics,
|
|
916
|
+
"GRAPH_COMPENSATION_SELF_REFERENCE",
|
|
917
|
+
"#/nodes",
|
|
918
|
+
`node cannot compensate itself: ${sourceId}`,
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
if (!["workspace_write", "external"].includes(source.effectClass)) {
|
|
922
|
+
diagnostic(
|
|
923
|
+
diagnostics,
|
|
924
|
+
"GRAPH_COMPENSATION_SOURCE_NOT_EFFECTFUL",
|
|
925
|
+
"#/nodes",
|
|
926
|
+
`compensation source must be effectful: ${sourceId}`,
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
if (!["workspace_write", "external"].includes(target.effectClass)) {
|
|
930
|
+
diagnostic(
|
|
931
|
+
diagnostics,
|
|
932
|
+
"GRAPH_COMPENSATION_TARGET_NOT_EFFECTFUL",
|
|
933
|
+
"#/nodes",
|
|
934
|
+
`compensation target must be effectful: ${targetId}`,
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
const priorOwner = compensationOwners.get(targetId);
|
|
938
|
+
if (priorOwner && priorOwner !== sourceId) {
|
|
939
|
+
diagnostic(
|
|
940
|
+
diagnostics,
|
|
941
|
+
"GRAPH_COMPENSATION_TARGET_REUSED",
|
|
942
|
+
"#/nodes",
|
|
943
|
+
`compensation target ${targetId} is already owned by ${priorOwner}`,
|
|
944
|
+
);
|
|
945
|
+
} else {
|
|
946
|
+
compensationOwners.set(targetId, sourceId);
|
|
947
|
+
compensationByNode.set(sourceId, targetId);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
const compensationNodeIds = new Set(compensationOwners.keys());
|
|
951
|
+
for (const targetId of compensationNodeIds) {
|
|
952
|
+
const target = nodes.get(targetId);
|
|
953
|
+
const usedAsForwardDependency = definition.nodes.some((node) =>
|
|
954
|
+
node.dependsOn.includes(targetId),
|
|
955
|
+
);
|
|
956
|
+
const usedByForwardEdge = definition.edges.some(
|
|
957
|
+
(edge) =>
|
|
958
|
+
edge.kind !== "compensation" &&
|
|
959
|
+
(edge.from === targetId || edge.to === targetId),
|
|
960
|
+
);
|
|
961
|
+
if (
|
|
962
|
+
target.dependsOn.length > 0 ||
|
|
963
|
+
usedAsForwardDependency ||
|
|
964
|
+
usedByForwardEdge
|
|
965
|
+
) {
|
|
966
|
+
diagnostic(
|
|
967
|
+
diagnostics,
|
|
968
|
+
"GRAPH_COMPENSATION_TARGET_IN_FORWARD_GRAPH",
|
|
969
|
+
"#/nodes",
|
|
970
|
+
`compensation target must be isolated from the forward dependency graph: ${targetId}`,
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
if (compensationTargets.has(targetId)) {
|
|
974
|
+
diagnostic(
|
|
975
|
+
diagnostics,
|
|
976
|
+
"GRAPH_COMPENSATION_RECURSIVE",
|
|
977
|
+
"#/nodes",
|
|
978
|
+
`compensation target cannot declare another compensation target: ${targetId}`,
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
521
982
|
for (const [index, trigger] of (definition.triggers || []).entries()) {
|
|
522
983
|
if (!nodeIds.has(trigger.targetNodeId)) {
|
|
523
984
|
diagnostic(
|
|
@@ -761,6 +1222,7 @@ export function compileGraphDefinition(input, options = {}) {
|
|
|
761
1222
|
|
|
762
1223
|
for (let leftIndex = 0; leftIndex < definition.nodes.length; leftIndex += 1) {
|
|
763
1224
|
const left = definition.nodes[leftIndex];
|
|
1225
|
+
if (compensationNodeIds.has(left.id)) continue;
|
|
764
1226
|
if (!["workspace_write", "external"].includes(left.effectClass)) continue;
|
|
765
1227
|
for (
|
|
766
1228
|
let rightIndex = leftIndex + 1;
|
|
@@ -768,6 +1230,7 @@ export function compileGraphDefinition(input, options = {}) {
|
|
|
768
1230
|
rightIndex += 1
|
|
769
1231
|
) {
|
|
770
1232
|
const right = definition.nodes[rightIndex];
|
|
1233
|
+
if (compensationNodeIds.has(right.id)) continue;
|
|
771
1234
|
if (!["workspace_write", "external"].includes(right.effectClass))
|
|
772
1235
|
continue;
|
|
773
1236
|
if (
|
|
@@ -803,14 +1266,26 @@ export function compileGraphDefinition(input, options = {}) {
|
|
|
803
1266
|
}
|
|
804
1267
|
}
|
|
805
1268
|
|
|
806
|
-
const multiplier = loopMultipliers(
|
|
807
|
-
|
|
808
|
-
|
|
1269
|
+
const multiplier = loopMultipliers(
|
|
1270
|
+
definition,
|
|
1271
|
+
diagnostics,
|
|
1272
|
+
nodeIds,
|
|
1273
|
+
nodes,
|
|
1274
|
+
dependencies,
|
|
1275
|
+
compensationByNode,
|
|
1276
|
+
);
|
|
1277
|
+
const subgraphs = validateSubgraphs(
|
|
809
1278
|
definition,
|
|
810
1279
|
nodeIds,
|
|
811
1280
|
options,
|
|
812
1281
|
diagnostics,
|
|
813
1282
|
);
|
|
1283
|
+
const budget = budgetUpperBound(
|
|
1284
|
+
definition,
|
|
1285
|
+
multiplier,
|
|
1286
|
+
diagnostics,
|
|
1287
|
+
subgraphs.calls,
|
|
1288
|
+
);
|
|
814
1289
|
if (diagnostics.length) throw new GraphCompileError(diagnostics);
|
|
815
1290
|
|
|
816
1291
|
const revisionDigest = graphDigest(
|
|
@@ -829,6 +1304,11 @@ export function compileGraphDefinition(input, options = {}) {
|
|
|
829
1304
|
Object.fromEntries([...nodes].map(([id, value]) => [id, value])),
|
|
830
1305
|
),
|
|
831
1306
|
topologicalOrder: Object.freeze(order),
|
|
1307
|
+
forwardTopologicalOrder: Object.freeze(
|
|
1308
|
+
order.filter((nodeId) => !compensationNodeIds.has(nodeId)),
|
|
1309
|
+
),
|
|
1310
|
+
compensationByNode: deepFreeze(Object.fromEntries(compensationByNode)),
|
|
1311
|
+
compensationNodeIds: Object.freeze([...compensationNodeIds].sort()),
|
|
832
1312
|
dependencies: deepFreeze(
|
|
833
1313
|
Object.fromEntries(
|
|
834
1314
|
[...dependencies].map(([id, values]) => [id, [...values].sort()]),
|
|
@@ -853,8 +1333,24 @@ export function compileGraphDefinition(input, options = {}) {
|
|
|
853
1333
|
),
|
|
854
1334
|
),
|
|
855
1335
|
loopMultipliers: deepFreeze(Object.fromEntries(multiplier)),
|
|
1336
|
+
loops: deepFreeze(
|
|
1337
|
+
Object.fromEntries(definition.loops.map((loop) => [loop.id, loop])),
|
|
1338
|
+
),
|
|
1339
|
+
loopByNode: deepFreeze(
|
|
1340
|
+
Object.fromEntries(
|
|
1341
|
+
definition.loops.flatMap((loop) =>
|
|
1342
|
+
loop.nodeIds.map((nodeId) => [nodeId, loop.id]),
|
|
1343
|
+
),
|
|
1344
|
+
),
|
|
1345
|
+
),
|
|
1346
|
+
loopByExitNode: deepFreeze(
|
|
1347
|
+
Object.fromEntries(
|
|
1348
|
+
definition.loops.map((loop) => [loop.exitNodeId, loop.id]),
|
|
1349
|
+
),
|
|
1350
|
+
),
|
|
856
1351
|
budgetUpperBound: deepFreeze(budget),
|
|
857
|
-
subgraphCalls: deepFreeze(Object.fromEntries(
|
|
1352
|
+
subgraphCalls: deepFreeze(Object.fromEntries(subgraphs.calls)),
|
|
1353
|
+
subgraphDefinitions: deepFreeze(Object.fromEntries(subgraphs.definitions)),
|
|
858
1354
|
triggers: deepFreeze([...(definition.triggers || [])]),
|
|
859
1355
|
regions: deepFreeze([...(definition.regions || [])]),
|
|
860
1356
|
};
|
|
@@ -882,6 +1378,8 @@ export function assertCompiledGraph(value) {
|
|
|
882
1378
|
export function migrateGraphDefinition(input, options = {}) {
|
|
883
1379
|
const { definition, migratedFrom } = migrateDefinition(input, options);
|
|
884
1380
|
const compiled = compileGraphDefinition(definition, options);
|
|
1381
|
+
const backupDefinition =
|
|
1382
|
+
migratedFrom == null ? null : deepFreeze(stableValue(input));
|
|
885
1383
|
return Object.freeze({
|
|
886
1384
|
dryRun: options.dryRun !== false,
|
|
887
1385
|
fromVersion: migratedFrom ?? definition.schemaVersion,
|
|
@@ -889,9 +1387,39 @@ export function migrateGraphDefinition(input, options = {}) {
|
|
|
889
1387
|
definition: compiled.definition,
|
|
890
1388
|
revisionDigest: compiled.revisionDigest,
|
|
891
1389
|
backupRequired: migratedFrom != null,
|
|
1390
|
+
backupDefinition,
|
|
1391
|
+
rollbackDigest:
|
|
1392
|
+
backupDefinition == null
|
|
1393
|
+
? null
|
|
1394
|
+
: graphDigest(
|
|
1395
|
+
backupDefinition,
|
|
1396
|
+
`cc.graph.definition-backup/${migratedFrom}`,
|
|
1397
|
+
),
|
|
892
1398
|
});
|
|
893
1399
|
}
|
|
894
1400
|
|
|
1401
|
+
export function restoreGraphDefinitionBackup(
|
|
1402
|
+
backupDefinition,
|
|
1403
|
+
expectedRollbackDigest,
|
|
1404
|
+
) {
|
|
1405
|
+
const backup = stableValue(backupDefinition);
|
|
1406
|
+
const version = Number(backup.schemaVersion);
|
|
1407
|
+
if (version !== GRAPH_DEFINITION_VERSION - 1) {
|
|
1408
|
+
const error = new TypeError(
|
|
1409
|
+
`backup must use GraphDefinition version ${GRAPH_DEFINITION_VERSION - 1}`,
|
|
1410
|
+
);
|
|
1411
|
+
error.code = "CC_GRAPH_MIGRATION_BACKUP_INVALID";
|
|
1412
|
+
throw error;
|
|
1413
|
+
}
|
|
1414
|
+
const digest = graphDigest(backup, `cc.graph.definition-backup/${version}`);
|
|
1415
|
+
if (digest !== expectedRollbackDigest) {
|
|
1416
|
+
const error = new TypeError("graph migration backup digest does not match");
|
|
1417
|
+
error.code = "CC_GRAPH_MIGRATION_BACKUP_TAMPERED";
|
|
1418
|
+
throw error;
|
|
1419
|
+
}
|
|
1420
|
+
return deepFreeze(backup);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
895
1423
|
export function executionAttemptId(nodeId, iterationPath = [], attempt = 1) {
|
|
896
1424
|
if (!Number.isSafeInteger(attempt) || attempt < 1) {
|
|
897
1425
|
throw new TypeError("attempt must be a positive integer");
|
|
@@ -907,5 +1435,9 @@ export function executionAttemptId(nodeId, iterationPath = [], attempt = 1) {
|
|
|
907
1435
|
return number;
|
|
908
1436
|
})
|
|
909
1437
|
: [];
|
|
910
|
-
|
|
1438
|
+
const digest = graphDigest(
|
|
1439
|
+
{ nodeId: String(nodeId), iterationPath: path, attempt },
|
|
1440
|
+
"cc.graph.execution-attempt/v1",
|
|
1441
|
+
);
|
|
1442
|
+
return `attempt-${digest.slice(7, 47)}`;
|
|
911
1443
|
}
|