@tangle-network/agent-runtime 0.163.0 → 0.165.0
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/dist/{activation-DxGP0h_p.js → activation-DmUIq51m.js} +3 -3
- package/dist/{activation-DxGP0h_p.js.map → activation-DmUIq51m.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{authoring-kO7vE_c9.js → authoring-Cm122Duf.js} +2 -2
- package/dist/{authoring-kO7vE_c9.js.map → authoring-Cm122Duf.js.map} +1 -1
- package/dist/candidate-execution/index.js +4 -4
- package/dist/{candidate-execution-izJj02pU.js → candidate-execution-DC0T8l2p.js} +4 -4
- package/dist/{candidate-execution-izJj02pU.js.map → candidate-execution-DC0T8l2p.js.map} +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/durable.d.ts +2 -2
- package/dist/durable.js +1 -1
- package/dist/{environment-provider-BXFFK_rZ.d.ts → environment-provider-DWjbGH8W.d.ts} +2 -2
- package/dist/environment-provider.d.ts +1 -1
- package/dist/{graph-COikUkrj.js → graph-DGri_zJm.js} +3 -3
- package/dist/{graph-COikUkrj.js.map → graph-DGri_zJm.js.map} +1 -1
- package/dist/graph.d.ts +155 -53
- package/dist/graph.js +946 -330
- package/dist/graph.js.map +1 -1
- package/dist/{improvement-cycle-dGtgX1vr.js → improvement-cycle-BOd5DFeG.js} +4 -4
- package/dist/{improvement-cycle-dGtgX1vr.js.map → improvement-cycle-BOd5DFeG.js.map} +1 -1
- package/dist/{index-CbyKCjR7.d.ts → index-BGQtpQuJ.d.ts} +5 -5
- package/dist/{index-CykKDPnB.d.ts → index-BgptUCKl.d.ts} +4 -4
- package/dist/{index-OFOvnNc2.d.ts → index-CFMB9ux3.d.ts} +2 -2
- package/dist/index.d.ts +7 -7
- package/dist/index.js +11 -11
- package/dist/intelligence.d.ts +4 -4
- package/dist/intelligence.js +5 -5
- package/dist/kernel.d.ts +5 -5
- package/dist/kernel.js +7 -7
- package/dist/{knowledge-CZT8ahp6.js → knowledge-7_IfyD9R.js} +5 -5
- package/dist/{knowledge-CZT8ahp6.js.map → knowledge-7_IfyD9R.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-DrN092ew.d.ts → loop-runner-bin-C2NXQK7J.d.ts} +3 -3
- package/dist/{loop-runner-bin-CIjmKf7v.js → loop-runner-bin-Dx-5GZQK.js} +3 -3
- package/dist/{loop-runner-bin-CIjmKf7v.js.map → loop-runner-bin-Dx-5GZQK.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +3 -3
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-DG779Z6S.js → openai-tools-CDS7eSww.js} +2 -2
- package/dist/{openai-tools-DG779Z6S.js.map → openai-tools-CDS7eSww.js.map} +1 -1
- package/dist/{prepare-VLGnSyav.js → prepare-BCScSDCv.js} +7 -5
- package/dist/prepare-BCScSDCv.js.map +1 -0
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/{protected-model-port-CcCH0Uji.js → protected-model-port-D-5E3HBe.js} +2 -2
- package/dist/{protected-model-port-CcCH0Uji.js.map → protected-model-port-D-5E3HBe.js.map} +1 -1
- package/dist/{redact-DL_ZtFvR.d.ts → redact-DkObldQP.d.ts} +3 -3
- package/dist/{runtime-CCbeY1KO.d.ts → runtime-B0HZbuEv.d.ts} +4 -4
- package/dist/{runtime-BT00zfIS.js → runtime-DESENUe1.js} +7 -7
- package/dist/{runtime-BT00zfIS.js.map → runtime-DESENUe1.js.map} +1 -1
- package/dist/{spawn-journal-oewq1g2h.js → spawn-journal-Tgvp5VS5.js} +8 -2
- package/dist/{spawn-journal-oewq1g2h.js.map → spawn-journal-Tgvp5VS5.js.map} +1 -1
- package/dist/{stream-agent-turn-Dx2YrEWW.d.ts → stream-agent-turn-DxQ3GOuR.d.ts} +2 -2
- package/dist/{structural-rollout-DAenyNls.js → structural-rollout-DZGDg0yX.js} +3 -3
- package/dist/{structural-rollout-DAenyNls.js.map → structural-rollout-DZGDg0yX.js.map} +1 -1
- package/dist/{supervise-Di6xvnOL.js → supervise-Da20R0PO.js} +3 -3
- package/dist/{supervise-Di6xvnOL.js.map → supervise-Da20R0PO.js.map} +1 -1
- package/dist/{supervisor-DmWq4vkU.js → supervisor-DpIKMlPJ.js} +114 -40
- package/dist/supervisor-DpIKMlPJ.js.map +1 -0
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +12 -12
- package/dist/{types-C2l1jaos.d.ts → types-DjVO-p7R.d.ts} +58 -3
- package/dist/{workspace-archive-BptlGCEU.js → workspace-archive-CHY5HvrF.js} +2 -2
- package/dist/{workspace-archive-BptlGCEU.js.map → workspace-archive-CHY5HvrF.js.map} +1 -1
- package/package.json +1 -1
- package/dist/prepare-VLGnSyav.js.map +0 -1
- package/dist/supervisor-DmWq4vkU.js.map +0 -1
package/dist/graph.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { m as ValidationError } from "./errors-CDZ8XsVj.js";
|
|
2
|
-
import { i as InMemorySpawnJournal, r as InMemoryResultBlobStore, x as contentAddress } from "./spawn-journal-
|
|
3
|
-
import {
|
|
4
|
-
import { i as
|
|
5
|
-
import {
|
|
2
|
+
import { d as replaySpawnTree, i as InMemorySpawnJournal, l as materializeTreeView, o as closesCursorSlot, r as InMemoryResultBlobStore, u as pendingWaits, x as contentAddress } from "./spawn-journal-Tgvp5VS5.js";
|
|
3
|
+
import { t as addSpend } from "./util-D6ZEuBMi.js";
|
|
4
|
+
import { N as createBudgetPool, U as createExecutorRegistry, a as uncertainSpawnBudgets, i as sumMeasuredSpendFromEvents, l as runFinalizer, o as bestDelivered, p as createScope, r as maxSeqOf, s as collectDelivered } from "./supervisor-DpIKMlPJ.js";
|
|
5
|
+
import { i as workerFromBackend, s as supervisorAgent } from "./supervise-Da20R0PO.js";
|
|
6
|
+
import { t as GraphEdgeCapError } from "./graph-DGri_zJm.js";
|
|
6
7
|
//#region src/runtime/graph/condition.ts
|
|
7
8
|
/**
|
|
8
9
|
* The ONE predicate tree: every guard in a graph — an edge `guard`, a filter projection's
|
|
@@ -588,6 +589,211 @@ function createGraphEngine(options) {
|
|
|
588
589
|
return engine;
|
|
589
590
|
}
|
|
590
591
|
//#endregion
|
|
592
|
+
//#region src/runtime/graph/fold.ts
|
|
593
|
+
/**
|
|
594
|
+
* The fold (agent-runtime#974): scheduler state is a pure function of the journal, reconstructed
|
|
595
|
+
* by replaying events through the SAME reducer the live scheduler feeds — fold, never checkpoint.
|
|
596
|
+
* Kernel events (`spawned`, `settled`, `waiting`, `woken`) carry what the kernel owns; the engine's
|
|
597
|
+
* events (`node-inputs-resolved`, `edge-verdict`, `join-state`) carry what the scheduler decided,
|
|
598
|
+
* each journaled BEFORE its effect was visible. `edge` stays observability and is skipped here
|
|
599
|
+
* exactly as the kernel's own replay skips it.
|
|
600
|
+
*
|
|
601
|
+
* Events are consumed in APPEND order (the journal file's line order), not by `seq`: kernel and
|
|
602
|
+
* engine events carry independent ordinal counters, and append order is the one total order both
|
|
603
|
+
* share.
|
|
604
|
+
*/
|
|
605
|
+
/** The reducer's zero: every node unvisited, every edge pending, nothing suspended. */
|
|
606
|
+
function emptyFoldState(compiled) {
|
|
607
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
608
|
+
for (const id of compiled.nodes.keys()) nodes.set(id, {
|
|
609
|
+
visits: 0,
|
|
610
|
+
blocked: false,
|
|
611
|
+
settles: []
|
|
612
|
+
});
|
|
613
|
+
const edges = /* @__PURE__ */ new Map();
|
|
614
|
+
for (const edge of compiled.edges) edges.set(edge.id, {
|
|
615
|
+
state: "pending",
|
|
616
|
+
consumedOnce: false,
|
|
617
|
+
traversals: 0,
|
|
618
|
+
judgedSourceSettles: 0,
|
|
619
|
+
capped: false
|
|
620
|
+
});
|
|
621
|
+
return {
|
|
622
|
+
nodes,
|
|
623
|
+
edges,
|
|
624
|
+
instances: /* @__PURE__ */ new Map(),
|
|
625
|
+
spawnedIds: /* @__PURE__ */ new Map(),
|
|
626
|
+
suspensions: /* @__PURE__ */ new Map(),
|
|
627
|
+
exhaustedEdges: /* @__PURE__ */ new Set()
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function instanceOf(state, label) {
|
|
631
|
+
return state.instances.get(label);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Apply ONE journal event. The live scheduler calls this right after each append; the restart path
|
|
635
|
+
* calls it over the loaded journal. Unknown kernel events are ignored — the engine folds only what
|
|
636
|
+
* it understands, exactly as the kernel's replay skips the engine's events.
|
|
637
|
+
*/
|
|
638
|
+
function applyGraphFoldEvent(state, ev, compiled) {
|
|
639
|
+
switch (ev.kind) {
|
|
640
|
+
case "node-inputs-resolved": {
|
|
641
|
+
const visit = Number(ev.instance.split("#").at(-1));
|
|
642
|
+
if (!Number.isSafeInteger(visit)) throw new ValidationError(`graph fold: instance ${ev.instance} carries no visit ordinal`);
|
|
643
|
+
state.instances.set(ev.instance, {
|
|
644
|
+
node: ev.node,
|
|
645
|
+
instance: ev.instance,
|
|
646
|
+
visit,
|
|
647
|
+
inputRef: ev.inputRef,
|
|
648
|
+
status: "released"
|
|
649
|
+
});
|
|
650
|
+
const node = state.nodes.get(ev.node);
|
|
651
|
+
if (node) node.visits = Math.max(node.visits, visit);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
case "spawned": {
|
|
655
|
+
const instance = instanceOf(state, ev.label);
|
|
656
|
+
if (instance) {
|
|
657
|
+
instance.status = "live";
|
|
658
|
+
state.spawnedIds.set(ev.id, ev.label);
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
case "settled": {
|
|
663
|
+
const label = state.spawnedIds.get(ev.id);
|
|
664
|
+
const instance = label === void 0 ? void 0 : instanceOf(state, label);
|
|
665
|
+
if (!instance) return;
|
|
666
|
+
instance.status = ev.status;
|
|
667
|
+
const settle = {
|
|
668
|
+
node: instance.node,
|
|
669
|
+
visit: instance.visit,
|
|
670
|
+
status: ev.status,
|
|
671
|
+
...ev.outRef !== void 0 ? { outRef: ev.outRef } : {},
|
|
672
|
+
...ev.reason !== void 0 ? { reason: ev.reason } : {}
|
|
673
|
+
};
|
|
674
|
+
if (ev.trace?.status === "available") settle.traceRef = ev.trace.traceRef;
|
|
675
|
+
instance.settle = settle;
|
|
676
|
+
state.nodes.get(instance.node)?.settles.push(settle);
|
|
677
|
+
const node = compiled.nodes.get(instance.node);
|
|
678
|
+
for (const edge of node?.outbound ?? []) {
|
|
679
|
+
const folded = state.edges.get(edge.id);
|
|
680
|
+
if (folded?.consumedOnce && folded.state === "pending") {
|
|
681
|
+
folded.consumedOnce = false;
|
|
682
|
+
folded.judgedSourceSettles += 1;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
case "edge-verdict": {
|
|
688
|
+
const folded = state.edges.get(ev.edge);
|
|
689
|
+
if (!folded) return;
|
|
690
|
+
if (ev.capped) {
|
|
691
|
+
folded.capped = true;
|
|
692
|
+
state.exhaustedEdges.add(ev.edge);
|
|
693
|
+
const target = compiled.edges.find((edge) => edge.id === ev.edge)?.spec.to.node;
|
|
694
|
+
const compiledTarget = target === void 0 ? void 0 : compiled.nodes.get(target);
|
|
695
|
+
if (compiledTarget && (compiledTarget.join === "all" || compiledTarget.join === "all_done")) {
|
|
696
|
+
const node = state.nodes.get(target);
|
|
697
|
+
if (node) node.blocked = true;
|
|
698
|
+
}
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
folded.judgedSourceSettles += 1;
|
|
702
|
+
if (!ev.fired) {
|
|
703
|
+
folded.state = ev.sourceStatus === "done" ? "dead" : "failed";
|
|
704
|
+
folded.payloadRef = void 0;
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
folded.state = "satisfied";
|
|
708
|
+
folded.payloadRef = ev.inputRef;
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
case "join-state": {
|
|
712
|
+
for (const edgeId of ev.satisfiedBy) {
|
|
713
|
+
const folded = state.edges.get(edgeId);
|
|
714
|
+
if (!folded) continue;
|
|
715
|
+
folded.traversals += 1;
|
|
716
|
+
folded.state = "pending";
|
|
717
|
+
folded.payloadRef = void 0;
|
|
718
|
+
}
|
|
719
|
+
const target = compiled.nodes.get(ev.node);
|
|
720
|
+
for (const edge of target?.inbound ?? []) {
|
|
721
|
+
const folded = state.edges.get(edge.id);
|
|
722
|
+
if (!folded) continue;
|
|
723
|
+
if (ev.consumedPending.includes(edge.id)) folded.consumedOnce = true;
|
|
724
|
+
else if (folded.state !== "pending" && !ev.satisfiedBy.includes(edge.id)) {
|
|
725
|
+
folded.state = "pending";
|
|
726
|
+
folded.payloadRef = void 0;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
case "waiting": {
|
|
732
|
+
if (ev.spec.kind !== "token") return;
|
|
733
|
+
const instance = instanceOf(state, ev.label);
|
|
734
|
+
if (instance) {
|
|
735
|
+
instance.status = "suspended";
|
|
736
|
+
if (instance.settle !== void 0) {
|
|
737
|
+
const settles = state.nodes.get(instance.node)?.settles;
|
|
738
|
+
if (settles && settles.at(-1) === instance.settle) settles.pop();
|
|
739
|
+
instance.settle = void 0;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
state.suspensions.set(ev.spec.token, {
|
|
743
|
+
token: ev.spec.token,
|
|
744
|
+
node: instance?.node ?? ev.label.split("#")[0] ?? ev.label,
|
|
745
|
+
instance: ev.label,
|
|
746
|
+
onExpire: ev.spec.onExpire,
|
|
747
|
+
...ev.spec.expiresAtMs !== void 0 ? { expiresAtMs: ev.spec.expiresAtMs } : {},
|
|
748
|
+
...ev.spec.defaultRef !== void 0 ? { defaultRef: ev.spec.defaultRef } : {},
|
|
749
|
+
status: "pending"
|
|
750
|
+
});
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
case "woken": {
|
|
754
|
+
const token = ev.id.startsWith("graphwait:") ? ev.id.slice(10) : void 0;
|
|
755
|
+
const suspension = token === void 0 ? void 0 : state.suspensions.get(token);
|
|
756
|
+
if (!suspension) return;
|
|
757
|
+
const instance = instanceOf(state, suspension.instance);
|
|
758
|
+
if (ev.by === "expired") {
|
|
759
|
+
suspension.status = "expired";
|
|
760
|
+
if (instance) {
|
|
761
|
+
instance.status = "down";
|
|
762
|
+
const settle = {
|
|
763
|
+
node: suspension.node,
|
|
764
|
+
visit: instance.visit,
|
|
765
|
+
status: "down",
|
|
766
|
+
reason: "suspension expired"
|
|
767
|
+
};
|
|
768
|
+
instance.settle = settle;
|
|
769
|
+
state.nodes.get(suspension.node)?.settles.push(settle);
|
|
770
|
+
}
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
suspension.status = "woken";
|
|
774
|
+
if (instance) {
|
|
775
|
+
instance.status = "done";
|
|
776
|
+
const settle = {
|
|
777
|
+
node: suspension.node,
|
|
778
|
+
visit: instance.visit,
|
|
779
|
+
status: "done",
|
|
780
|
+
...ev.outRef !== void 0 ? { outRef: ev.outRef } : {}
|
|
781
|
+
};
|
|
782
|
+
instance.settle = settle;
|
|
783
|
+
state.nodes.get(suspension.node)?.settles.push(settle);
|
|
784
|
+
}
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
default: return;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/** Fold a loaded journal (append order) into scheduler state. */
|
|
791
|
+
function foldGraphJournal(events, compiled) {
|
|
792
|
+
const state = emptyFoldState(compiled);
|
|
793
|
+
for (const ev of events) applyGraphFoldEvent(state, ev, compiled);
|
|
794
|
+
return state;
|
|
795
|
+
}
|
|
796
|
+
//#endregion
|
|
591
797
|
//#region src/runtime/graph/kinds.ts
|
|
592
798
|
/**
|
|
593
799
|
* One profile, one run: the kernel's leaf, exactly as `supervise()` derives it from `backend`.
|
|
@@ -799,18 +1005,43 @@ function asRecord(value, context) {
|
|
|
799
1005
|
//#endregion
|
|
800
1006
|
//#region src/runtime/graph/scheduler.ts
|
|
801
1007
|
/**
|
|
802
|
-
* The engine scheduler (agent-runtime#980): run a compiled graph by hosting
|
|
803
|
-
* one kernel `Scope` — the pool, the journal, the blob store and
|
|
804
|
-
* never re-implemented. The scheduler owns only what a graph adds:
|
|
805
|
-
* traversal and visit caps, directive/payload delivery,
|
|
1008
|
+
* The engine scheduler (agent-runtime#980, durable per #974/#976): run a compiled graph by hosting
|
|
1009
|
+
* every node instance on one kernel `Scope` — the pool, the journal, the blob store and
|
|
1010
|
+
* cancellation are the kernel's, never re-implemented. The scheduler owns only what a graph adds:
|
|
1011
|
+
* joins over guarded edges, traversal and visit caps, directive/payload delivery, suspensions,
|
|
1012
|
+
* terminals and the finalizer reduce.
|
|
806
1013
|
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
1014
|
+
* DURABILITY MODEL — fold, never checkpoint. Every scheduling decision is journaled BEFORE its
|
|
1015
|
+
* effect is visible (blob-then-journal where a ref is minted), then applied to in-memory state
|
|
1016
|
+
* through the SAME pure reducer (`applyGraphFoldEvent`) a restart replays the journal through.
|
|
1017
|
+
* Kill the process at any journal boundary and a restart with the same journal re-enters the exact
|
|
1018
|
+
* state: settled nodes restore from `outRef` and are never re-executed; a released-but-unspawned
|
|
1019
|
+
* instance re-enters from its pinned `inputRef`; an in-flight instance is in-doubt and re-enters
|
|
1020
|
+
* per its kind's `onCrash`.
|
|
1021
|
+
*
|
|
1022
|
+
* Engine `woken` ordinals live in a reserved high band (`ENGINE_WOKEN_SEQ_BASE`+) so they can
|
|
1023
|
+
* never collide with the kernel scope's cursor counter, which the kernel owns and advances
|
|
1024
|
+
* concurrently.
|
|
813
1025
|
*/
|
|
1026
|
+
/** Engine-appended `woken` ordinals start here — far above any kernel cursor counter. */
|
|
1027
|
+
const ENGINE_WOKEN_SEQ_BASE = 1e7;
|
|
1028
|
+
const SUSPEND_MARK = "__graphSuspension";
|
|
1029
|
+
/** What a kind's executor returns to park its node on a host wake (agent-runtime#976). */
|
|
1030
|
+
function suspended(options = {}) {
|
|
1031
|
+
const onExpire = options.onExpire ?? "wait";
|
|
1032
|
+
if (onExpire !== "wait" && options.expiresInMs === void 0) throw new ValidationError(`suspended: onExpire '${onExpire}' requires expiresInMs`);
|
|
1033
|
+
if (onExpire === "wait" && options.expiresInMs !== void 0) throw new ValidationError("suspended: onExpire 'wait' never expires — remove expiresInMs");
|
|
1034
|
+
if (onExpire === "default" && options.default === void 0) throw new ValidationError("suspended: onExpire 'default' requires a default payload");
|
|
1035
|
+
return {
|
|
1036
|
+
[SUSPEND_MARK]: true,
|
|
1037
|
+
onExpire,
|
|
1038
|
+
...options.expiresInMs !== void 0 ? { expiresInMs: options.expiresInMs } : {},
|
|
1039
|
+
...options.default !== void 0 ? { default: options.default } : {}
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function isSuspensionRequest(value) {
|
|
1043
|
+
return typeof value === "object" && value !== null && value[SUSPEND_MARK] === true;
|
|
1044
|
+
}
|
|
814
1045
|
/** Admission for every value crossing an edge (#971): JSON round-trip, `undefined` stripped, a
|
|
815
1046
|
* non-representable value becomes a RECORD of that fact — a degraded record beats a vanished
|
|
816
1047
|
* edge. */
|
|
@@ -824,12 +1055,45 @@ function admitPayload(value) {
|
|
|
824
1055
|
return { nonCanonical: error instanceof Error ? error.message : String(error) };
|
|
825
1056
|
}
|
|
826
1057
|
}
|
|
1058
|
+
/** Run a graph to its result: `createGraphRun` awaited — the one-call form for a run that needs
|
|
1059
|
+
* no live host wakes. */
|
|
1060
|
+
async function runEngineGraph(engine, spec, task, options) {
|
|
1061
|
+
return createGraphRun(engine, spec, task, options).done;
|
|
1062
|
+
}
|
|
827
1063
|
/**
|
|
828
|
-
*
|
|
829
|
-
*
|
|
1064
|
+
* Start (or resume) a graph run and return its handle: await `done` for the result; deliver host
|
|
1065
|
+
* wakes through `resume`/`expire` while it runs (#976).
|
|
830
1066
|
*/
|
|
831
|
-
|
|
1067
|
+
function createGraphRun(engine, spec, task, options) {
|
|
832
1068
|
const compiled = "nodes" in spec && spec.nodes instanceof Map ? spec : compileGraph(engine, spec);
|
|
1069
|
+
const wakes = [];
|
|
1070
|
+
let notifyWake;
|
|
1071
|
+
const handleState = { finished: false };
|
|
1072
|
+
const done = runGraphLoop(engine, compiled, task, options, wakes, () => notifyWake?.(), (fn) => {
|
|
1073
|
+
notifyWake = fn;
|
|
1074
|
+
}).finally(() => {
|
|
1075
|
+
handleState.finished = true;
|
|
1076
|
+
});
|
|
1077
|
+
const queue = (token, payload, expire) => {
|
|
1078
|
+
if (handleState.finished) return Promise.reject(new ValidationError(`graph resume: run completed; start a new run over the same journal to wake '${token}'`));
|
|
1079
|
+
return new Promise((settle, fail) => {
|
|
1080
|
+
wakes.push({
|
|
1081
|
+
token,
|
|
1082
|
+
payload,
|
|
1083
|
+
expire,
|
|
1084
|
+
settle,
|
|
1085
|
+
fail
|
|
1086
|
+
});
|
|
1087
|
+
notifyWake?.();
|
|
1088
|
+
});
|
|
1089
|
+
};
|
|
1090
|
+
return {
|
|
1091
|
+
done,
|
|
1092
|
+
resume: (token, payload) => queue(token, payload, false),
|
|
1093
|
+
expire: (token) => queue(token, void 0, true)
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
async function runGraphLoop(engine, compiled, task, options, wakes, _notify, onWakeSignal) {
|
|
833
1097
|
const context = "runEngineGraph";
|
|
834
1098
|
for (const node of compiled.nodes.values()) {
|
|
835
1099
|
const missing = node.kind.effects.filter((name) => !(name in engine.effects));
|
|
@@ -838,15 +1102,40 @@ async function runEngineGraph(engine, spec, task, options) {
|
|
|
838
1102
|
}
|
|
839
1103
|
for (const edge of compiled.edges) if (edge.spec.directive !== void 0 && options.prompts === void 0) throw new ValidationError(`${context}: edge ${edge.id} carries a directive but options.prompts is absent`);
|
|
840
1104
|
const now = options.now ?? Date.now;
|
|
841
|
-
const started = now();
|
|
842
|
-
const runId = options.runId ?? `graph:${contentAddress({
|
|
843
|
-
task,
|
|
844
|
-
at: started
|
|
845
|
-
}).slice(0, 18)}`;
|
|
846
|
-
const pool = createBudgetPool(options.budget, () => now() - started);
|
|
847
1105
|
const journal = options.journal ?? new InMemorySpawnJournal();
|
|
848
1106
|
const blobs = options.blobs ?? new InMemoryResultBlobStore();
|
|
849
|
-
|
|
1107
|
+
const runId = options.runId ?? `graph:${contentAddress({ task }).slice(0, 18)}`;
|
|
1108
|
+
const prior = await journal.loadTree(runId);
|
|
1109
|
+
if (prior !== void 0 && options.resume !== true) throw new ValidationError(`${context}: runId '${runId}' already exists; pass resume: true to continue it or use a new runId`);
|
|
1110
|
+
const resuming = options.resume === true && prior !== void 0 && prior.length > 0;
|
|
1111
|
+
let runEpochMs = now();
|
|
1112
|
+
let rootBudget = options.budget;
|
|
1113
|
+
if (prior === void 0) await journal.beginTree(runId, new Date(runEpochMs).toISOString());
|
|
1114
|
+
if (!resuming) await journal.appendEvent(runId, {
|
|
1115
|
+
kind: "spawned",
|
|
1116
|
+
id: runId,
|
|
1117
|
+
label: "graph-root",
|
|
1118
|
+
budget: options.budget,
|
|
1119
|
+
runtime: "inline",
|
|
1120
|
+
seq: 0,
|
|
1121
|
+
at: new Date(runEpochMs).toISOString()
|
|
1122
|
+
});
|
|
1123
|
+
if (resuming) {
|
|
1124
|
+
const root = prior.find((ev) => ev.kind === "spawned" && ev.id === runId);
|
|
1125
|
+
if (root?.kind === "spawned") {
|
|
1126
|
+
runEpochMs = Date.parse(root.at);
|
|
1127
|
+
rootBudget = root.budget;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
const elapsed = () => now() - runEpochMs;
|
|
1131
|
+
const pool = resuming ? createBudgetPool(rootBudget, elapsed, {
|
|
1132
|
+
committed: (() => {
|
|
1133
|
+
const measured = sumMeasuredSpendFromEvents(prior);
|
|
1134
|
+
return addSpend(measured.childWork, measured.driverInference);
|
|
1135
|
+
})(),
|
|
1136
|
+
uncertainReservations: uncertainSpawnBudgets(prior),
|
|
1137
|
+
...rootBudget.deadlineMs !== void 0 ? { absoluteDeadlineMs: runEpochMs + rootBudget.deadlineMs } : {}
|
|
1138
|
+
}) : createBudgetPool(rootBudget, elapsed);
|
|
850
1139
|
const abort = new AbortController();
|
|
851
1140
|
const onOuterAbort = () => abort.abort(options.signal?.reason);
|
|
852
1141
|
options.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
@@ -861,343 +1150,670 @@ async function runEngineGraph(engine, spec, task, options) {
|
|
|
861
1150
|
seams: {},
|
|
862
1151
|
depth: 0,
|
|
863
1152
|
signal: abort.signal,
|
|
864
|
-
now
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
});
|
|
876
|
-
const edges = /* @__PURE__ */ new Map();
|
|
877
|
-
for (const edge of compiled.edges) edges.set(edge.id, {
|
|
878
|
-
edge,
|
|
879
|
-
state: "pending",
|
|
880
|
-
consumedOnce: false,
|
|
881
|
-
traversals: 0,
|
|
882
|
-
payload: void 0
|
|
1153
|
+
now,
|
|
1154
|
+
...resuming ? { resumeFrom: {
|
|
1155
|
+
settled: await replaySpawnTree(journal, blobs, runId),
|
|
1156
|
+
view: materializeTreeView(prior),
|
|
1157
|
+
maxSpawnOrdinal: maxSeqOf(prior, (ev) => ev.kind === "spawned"),
|
|
1158
|
+
maxCursorSeq: maxSeqOf(prior, closesCursorSlot),
|
|
1159
|
+
maxWaitOrdinal: maxSeqOf(prior, (ev) => ev.kind === "waiting"),
|
|
1160
|
+
waits: pendingWaits(prior),
|
|
1161
|
+
keys: /* @__PURE__ */ new Map(),
|
|
1162
|
+
priorSpend: sumMeasuredSpendFromEvents(prior)
|
|
1163
|
+
} } : {}
|
|
883
1164
|
});
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
from: spec.from.node,
|
|
899
|
-
to: spec.to.node,
|
|
900
|
-
traversal: runtime.traversals,
|
|
901
|
-
outcome,
|
|
902
|
-
...spec.directive !== void 0 ? { directive: `${spec.directive.surface}/v${spec.directive.version}` } : {},
|
|
903
|
-
...spec.kind === "data" ? { port: runtime.edge.toPort } : {},
|
|
904
|
-
...reason !== void 0 ? { reason } : {}
|
|
1165
|
+
try {
|
|
1166
|
+
return await runToResult();
|
|
1167
|
+
} finally {
|
|
1168
|
+
options.signal?.removeEventListener("abort", onOuterAbort);
|
|
1169
|
+
abort.abort("run complete");
|
|
1170
|
+
}
|
|
1171
|
+
async function runToResult() {
|
|
1172
|
+
const state = emptyFoldState(compiled);
|
|
1173
|
+
if (resuming) for (const ev of prior) applyGraphFoldEvent(state, ev, compiled);
|
|
1174
|
+
let engineSeq = resuming ? maxEngineSeq(prior) + 1 : 0;
|
|
1175
|
+
let engineWokenSeq = ENGINE_WOKEN_SEQ_BASE + (resuming ? prior.filter((ev) => ev.kind === "woken" && ev.seq >= 1e7).length : 0);
|
|
1176
|
+
const appendAndApply = async (ev) => {
|
|
1177
|
+
await journal.appendEvent(runId, ev);
|
|
1178
|
+
applyGraphFoldEvent(state, ev, compiled);
|
|
905
1179
|
};
|
|
906
|
-
ledger
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1180
|
+
const ledger = [];
|
|
1181
|
+
const liveHandles = /* @__PURE__ */ new Map();
|
|
1182
|
+
const waitingForBudget = [];
|
|
1183
|
+
const outCache = /* @__PURE__ */ new Map();
|
|
1184
|
+
let ledgerSeq = resuming ? prior.filter((ev) => ev.kind === "edge").length : 0;
|
|
1185
|
+
let liveCount = 0;
|
|
1186
|
+
let failure;
|
|
1187
|
+
const recordLedger = async (edge, traversal, outcome, reason) => {
|
|
1188
|
+
const spec = edge.spec;
|
|
1189
|
+
const entry = {
|
|
1190
|
+
edge: edge.id,
|
|
911
1191
|
kind: spec.kind,
|
|
912
1192
|
from: spec.from.node,
|
|
913
1193
|
to: spec.to.node,
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1194
|
+
traversal,
|
|
1195
|
+
outcome,
|
|
1196
|
+
...spec.directive !== void 0 ? { directive: `${spec.directive.surface}/v${spec.directive.version}` } : {},
|
|
1197
|
+
...spec.kind === "data" ? { port: edge.toPort } : {},
|
|
1198
|
+
...reason !== void 0 ? { reason } : {}
|
|
1199
|
+
};
|
|
1200
|
+
ledger.push(entry);
|
|
1201
|
+
await journal.appendEvent(runId, {
|
|
1202
|
+
kind: "edge",
|
|
1203
|
+
id: `graph:${spec.to.node}`,
|
|
1204
|
+
edge: {
|
|
1205
|
+
kind: spec.kind,
|
|
1206
|
+
from: spec.from.node,
|
|
1207
|
+
to: spec.to.node,
|
|
1208
|
+
...entry.directive !== void 0 ? { directive: entry.directive } : {},
|
|
1209
|
+
...entry.port !== void 0 ? { port: entry.port } : {}
|
|
1210
|
+
},
|
|
1211
|
+
traversal,
|
|
1212
|
+
outcome: outcome === "delivered" ? "delivered" : outcome,
|
|
1213
|
+
bytes: 0,
|
|
1214
|
+
...reason !== void 0 ? { reason } : {},
|
|
1215
|
+
seq: ledgerSeq++,
|
|
1216
|
+
at: new Date(now()).toISOString()
|
|
1217
|
+
});
|
|
1218
|
+
};
|
|
1219
|
+
/** Spawn one released instance from its journaled envelope — the ONE entry path, used for a
|
|
1220
|
+
* fresh release, a budget-parked retry, and a restart re-entry alike. */
|
|
1221
|
+
const spawnInstance = async (instanceLabel) => {
|
|
1222
|
+
if (failure) return;
|
|
1223
|
+
const instance = state.instances.get(instanceLabel);
|
|
1224
|
+
const node = instance === void 0 ? void 0 : compiled.nodes.get(instance.node);
|
|
1225
|
+
if (!instance || !node || instance.inputRef === void 0) throw new ValidationError(`${context}: instance ${instanceLabel} has no journaled envelope`);
|
|
1226
|
+
const envelope = await blobs.get(instance.inputRef);
|
|
1227
|
+
if (envelope === void 0) throw new ValidationError(`${context}: envelope ${instance.inputRef} is not in the blob store`);
|
|
1228
|
+
const effects = narrowEffects(node.kind.effects, engine.effects, `${context}: node ${node.id}`);
|
|
1229
|
+
const agent = node.kind.run({
|
|
1230
|
+
config: node.config,
|
|
1231
|
+
profile: {
|
|
1232
|
+
name: node.id,
|
|
1233
|
+
...node.spec.profile ?? {}
|
|
1234
|
+
},
|
|
1235
|
+
inputs: envelope.inputs,
|
|
1236
|
+
effects
|
|
1237
|
+
});
|
|
1238
|
+
const budget = node.spec.budget ?? options.perNode;
|
|
1239
|
+
const spawned = scope.spawn(agent, envelope.task, {
|
|
1240
|
+
label: instanceLabel,
|
|
1241
|
+
budget
|
|
1242
|
+
});
|
|
1243
|
+
if (spawned.ok) {
|
|
1244
|
+
liveCount += 1;
|
|
1245
|
+
liveHandles.set(spawned.handle.id, instanceLabel);
|
|
1246
|
+
applyGraphFoldEvent(state, {
|
|
1247
|
+
kind: "spawned",
|
|
1248
|
+
id: spawned.handle.id,
|
|
1249
|
+
label: instanceLabel,
|
|
1250
|
+
budget,
|
|
1251
|
+
runtime: "inline",
|
|
1252
|
+
seq: 0,
|
|
1253
|
+
at: ""
|
|
1254
|
+
}, compiled);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if (spawned.reason === "budget-exhausted" || spawned.reason === "max-live-workers") {
|
|
1258
|
+
waitingForBudget.push(instanceLabel);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
933
1261
|
failure = {
|
|
934
|
-
reason: "
|
|
1262
|
+
reason: "driver-failed",
|
|
935
1263
|
error: {
|
|
936
|
-
name: "
|
|
937
|
-
message: `node ${id}
|
|
1264
|
+
name: "SpawnRefused",
|
|
1265
|
+
message: `node ${node.id}: ${spawned.reason}`
|
|
938
1266
|
}
|
|
939
1267
|
};
|
|
940
|
-
abort.abort(`
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1268
|
+
abort.abort(`spawn refused: ${node.id}`);
|
|
1269
|
+
};
|
|
1270
|
+
/** Release a node: journal the wave (join-state + envelope) then spawn. */
|
|
1271
|
+
const release = async (nodeId, satisfiedBy, consumedPending) => {
|
|
1272
|
+
const node = compiled.nodes.get(nodeId);
|
|
1273
|
+
const folded = state.nodes.get(nodeId);
|
|
1274
|
+
if (!node || !folded) return;
|
|
1275
|
+
const visit = folded.visits + 1;
|
|
1276
|
+
if (visit > node.maxVisits) {
|
|
1277
|
+
failure = {
|
|
1278
|
+
reason: "cycle-budget-exceeded",
|
|
1279
|
+
error: {
|
|
1280
|
+
name: "GraphCycleBudget",
|
|
1281
|
+
message: `node ${nodeId} entered ${visit} times; maxVisits ${node.maxVisits}`
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
abort.abort(`cycle-budget-exceeded: ${nodeId}`);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const instanceLabel = `${nodeId}#${visit}`;
|
|
1288
|
+
const inputs = {};
|
|
1289
|
+
const directives = [];
|
|
1290
|
+
const traces = [];
|
|
1291
|
+
for (const edge of satisfiedBy) {
|
|
1292
|
+
const foldedEdge = state.edges.get(edge.id);
|
|
1293
|
+
const spec = edge.spec;
|
|
1294
|
+
if (spec.kind === "data" && foldedEdge?.state === "satisfied") {
|
|
1295
|
+
let payload = foldedEdge.payloadRef === void 0 ? void 0 : outCache.get(foldedEdge.payloadRef);
|
|
1296
|
+
if (payload === void 0 && foldedEdge.payloadRef !== void 0) payload = admitPayload(await blobs.get(foldedEdge.payloadRef));
|
|
1297
|
+
let outcome = "delivered";
|
|
1298
|
+
let reason;
|
|
1299
|
+
if (spec.projection !== void 0) try {
|
|
1300
|
+
payload = admitPayload(applyProjection(payload, spec.projection, `edge ${edge.id}`));
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
outcome = "empty";
|
|
1303
|
+
reason = error instanceof Error ? error.message : String(error);
|
|
1304
|
+
payload = void 0;
|
|
1305
|
+
}
|
|
1306
|
+
if (payload === void 0 && outcome === "delivered") outcome = "empty";
|
|
1307
|
+
await recordLedger(edge, (foldedEdge?.traversals ?? 0) + 1, outcome, reason);
|
|
1308
|
+
if (payload !== void 0) inputs[edge.toPort] = payload;
|
|
1309
|
+
} else {
|
|
1310
|
+
if (spec.directive !== void 0 && options.prompts !== void 0) directives.push(options.prompts.resolve(spec.directive).text);
|
|
1311
|
+
if (spec.kind === "analyzes" && foldedEdge?.state === "satisfied") traces.push(`trace of ${spec.from.node}: ${foldedEdge.payloadRef ?? "(no traceRef)"}`);
|
|
1312
|
+
await recordLedger(edge, (foldedEdge?.traversals ?? 0) + 1, "delivered");
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
const nodeTask = [
|
|
1316
|
+
nodeId === compiled.root ? task : "",
|
|
1317
|
+
...directives,
|
|
1318
|
+
...traces
|
|
1319
|
+
].filter((part) => part.length > 0).join("\n\n");
|
|
1320
|
+
const envelope = {
|
|
1321
|
+
task: nodeTask.length > 0 ? nodeTask : task,
|
|
1322
|
+
inputs
|
|
1323
|
+
};
|
|
1324
|
+
const inputRef = contentAddress(envelope);
|
|
1325
|
+
await blobs.put(inputRef, envelope);
|
|
1326
|
+
const at = new Date(now()).toISOString();
|
|
1327
|
+
await appendAndApply({
|
|
1328
|
+
kind: "node-inputs-resolved",
|
|
1329
|
+
id: instanceLabel,
|
|
1330
|
+
node: nodeId,
|
|
1331
|
+
instance: instanceLabel,
|
|
1332
|
+
inputRef,
|
|
1333
|
+
seq: engineSeq++,
|
|
1334
|
+
at
|
|
1335
|
+
});
|
|
1336
|
+
await appendAndApply({
|
|
1337
|
+
kind: "join-state",
|
|
1338
|
+
id: instanceLabel,
|
|
1339
|
+
node: nodeId,
|
|
1340
|
+
rule: node.join,
|
|
1341
|
+
satisfiedBy: satisfiedBy.map((edge) => edge.id),
|
|
1342
|
+
consumedPending,
|
|
1343
|
+
instance: instanceLabel,
|
|
1344
|
+
seq: engineSeq++,
|
|
1345
|
+
at
|
|
1346
|
+
});
|
|
1347
|
+
await spawnInstance(instanceLabel);
|
|
1348
|
+
};
|
|
1349
|
+
/** Join evaluation per ADC's rules over the folded edge states. */
|
|
1350
|
+
const tryRelease = async (nodeId) => {
|
|
1351
|
+
const node = compiled.nodes.get(nodeId);
|
|
1352
|
+
const folded = state.nodes.get(nodeId);
|
|
1353
|
+
if (!node || !folded || folded.blocked || failure) return;
|
|
1354
|
+
const gating = node.inbound;
|
|
1355
|
+
if (gating.length === 0) return;
|
|
1356
|
+
const states = gating.map((edge) => ({
|
|
1357
|
+
edge,
|
|
1358
|
+
folded: state.edges.get(edge.id)
|
|
1359
|
+
}));
|
|
1360
|
+
const settled = states.filter((entry) => entry.folded && entry.folded.state !== "pending");
|
|
1361
|
+
const satisfied = states.filter((entry) => entry.folded?.state === "satisfied");
|
|
1362
|
+
const failed = states.filter((entry) => entry.folded?.state === "failed");
|
|
1363
|
+
const allSettled = settled.length === gating.length;
|
|
1364
|
+
let releaseNow = false;
|
|
1365
|
+
switch (node.join) {
|
|
1366
|
+
case "all":
|
|
1367
|
+
releaseNow = satisfied.length === gating.length && !states.some((entry) => entry.folded?.state === "dead" || entry.folded?.state === "failed");
|
|
1368
|
+
break;
|
|
1369
|
+
case "any":
|
|
1370
|
+
releaseNow = satisfied.length > 0;
|
|
1371
|
+
if (!releaseNow && allSettled) folded.blocked = true;
|
|
1372
|
+
break;
|
|
1373
|
+
case "any_failed":
|
|
1374
|
+
releaseNow = failed.length > 0;
|
|
1375
|
+
if (!releaseNow && allSettled) folded.blocked = true;
|
|
1376
|
+
break;
|
|
1377
|
+
case "all_done":
|
|
1378
|
+
releaseNow = allSettled;
|
|
1379
|
+
break;
|
|
1380
|
+
}
|
|
1381
|
+
if (!releaseNow) return;
|
|
1382
|
+
const consuming = node.join === "any" ? [satisfied[0]] : node.join === "any_failed" ? [failed[0]] : settled;
|
|
1383
|
+
for (const entry of consuming) {
|
|
1384
|
+
const cap = entry.edge.spec.maxTraversals;
|
|
1385
|
+
if (cap !== void 0 && (entry.folded?.traversals ?? 0) >= cap) {
|
|
1386
|
+
recordLedger(entry.edge, (entry.folded?.traversals ?? 0) + 1, "unpropagated", `traversal-cap-exhausted (max ${cap})`);
|
|
1387
|
+
await appendAndApply({
|
|
1388
|
+
kind: "edge-verdict",
|
|
1389
|
+
id: `graph:${nodeId}`,
|
|
1390
|
+
edge: entry.edge.id,
|
|
1391
|
+
fired: false,
|
|
1392
|
+
sourceStatus: "done",
|
|
1393
|
+
capped: true,
|
|
1394
|
+
seq: engineSeq++,
|
|
1395
|
+
at: new Date(now()).toISOString()
|
|
1396
|
+
});
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
const consumedPending = states.filter((entry) => entry.folded?.state === "pending" && hasLiveInstance(state, liveHandles, entry.edge.spec.from.node)).map((entry) => entry.edge.id);
|
|
1401
|
+
await release(nodeId, consuming.map((entry) => entry.edge), consumedPending);
|
|
1402
|
+
};
|
|
1403
|
+
/** Judge every outbound edge of a settle: journal the verdict, apply, then try the joins. */
|
|
1404
|
+
const propagate = async (instanceLabel, settle) => {
|
|
1405
|
+
const node = compiled.nodes.get(settle.node);
|
|
1406
|
+
if (!node) return;
|
|
1407
|
+
const succeeded = settle.status === "done" && settle.valid !== false;
|
|
1408
|
+
const sourceSettles = state.nodes.get(settle.node)?.settles.length ?? 0;
|
|
1409
|
+
for (const edge of node.outbound) {
|
|
1410
|
+
const folded = state.edges.get(edge.id);
|
|
1411
|
+
if (!folded) continue;
|
|
1412
|
+
if (folded.judgedSourceSettles >= sourceSettles) continue;
|
|
1413
|
+
let fired = false;
|
|
1414
|
+
let inputRef;
|
|
1415
|
+
if (succeeded) {
|
|
1416
|
+
const guardContext = {
|
|
1417
|
+
node: settle.node,
|
|
1418
|
+
out: settle.out,
|
|
1419
|
+
visits: state.nodes.get(settle.node)?.visits ?? 0,
|
|
1420
|
+
valid: settle.valid ?? true
|
|
1421
|
+
};
|
|
1422
|
+
fired = edge.spec.guard === void 0 || evaluateCondition(edge.spec.guard, guardContext);
|
|
1423
|
+
if (fired) inputRef = edge.spec.kind === "analyzes" ? settle.traceRef ?? void 0 : settle.outRef;
|
|
1424
|
+
}
|
|
1425
|
+
await appendAndApply({
|
|
1426
|
+
kind: "edge-verdict",
|
|
1427
|
+
id: instanceLabel,
|
|
1428
|
+
edge: edge.id,
|
|
1429
|
+
fired,
|
|
1430
|
+
sourceStatus: settle.status === "down" ? "down" : succeeded ? "done" : "invalid",
|
|
1431
|
+
...inputRef !== void 0 ? { inputRef } : {},
|
|
1432
|
+
seq: engineSeq++,
|
|
1433
|
+
at: new Date(now()).toISOString()
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
for (const edge of node.outbound) await tryRelease(edge.spec.to.node);
|
|
1437
|
+
};
|
|
1438
|
+
const mintToken = (instanceLabel) => contentAddress({
|
|
1439
|
+
runId,
|
|
1440
|
+
instance: instanceLabel,
|
|
1441
|
+
kind: "graph-suspension"
|
|
966
1442
|
});
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
waitingForBudget.push(id);
|
|
975
|
-
runtime.visits -= 1;
|
|
976
|
-
runtime.directives.unshift(...directives);
|
|
977
|
-
runtime.traces.unshift(...traces);
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
failure = {
|
|
981
|
-
reason: "driver-failed",
|
|
982
|
-
error: {
|
|
983
|
-
name: "SpawnRefused",
|
|
984
|
-
message: `node ${id}: ${spawned.reason}`
|
|
1443
|
+
const journalSuspension = async (instanceLabel, request) => {
|
|
1444
|
+
const token = mintToken(instanceLabel);
|
|
1445
|
+
let defaultRef;
|
|
1446
|
+
if (request.default !== void 0) {
|
|
1447
|
+
const admitted = admitPayload(request.default);
|
|
1448
|
+
defaultRef = contentAddress(admitted);
|
|
1449
|
+
await blobs.put(defaultRef, admitted);
|
|
985
1450
|
}
|
|
1451
|
+
await appendAndApply({
|
|
1452
|
+
kind: "waiting",
|
|
1453
|
+
id: `graphwait:${token}`,
|
|
1454
|
+
label: instanceLabel,
|
|
1455
|
+
spec: {
|
|
1456
|
+
kind: "token",
|
|
1457
|
+
token,
|
|
1458
|
+
onExpire: request.onExpire,
|
|
1459
|
+
...request.expiresInMs !== void 0 ? { expiresAtMs: now() + request.expiresInMs } : {},
|
|
1460
|
+
...defaultRef !== void 0 ? { defaultRef } : {}
|
|
1461
|
+
},
|
|
1462
|
+
armedAt: now(),
|
|
1463
|
+
seq: engineSeq++,
|
|
1464
|
+
at: new Date(now()).toISOString()
|
|
1465
|
+
});
|
|
986
1466
|
};
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
else release = satisfied.length === gating.length;
|
|
1005
|
-
break;
|
|
1006
|
-
case "any":
|
|
1007
|
-
release = satisfied.length > 0;
|
|
1008
|
-
if (!release && allSettled) runtime.blocked = true;
|
|
1009
|
-
break;
|
|
1010
|
-
case "any_failed":
|
|
1011
|
-
release = failed.length > 0;
|
|
1012
|
-
if (!release && allSettled) runtime.blocked = true;
|
|
1013
|
-
break;
|
|
1014
|
-
case "all_done":
|
|
1015
|
-
release = allSettled;
|
|
1016
|
-
break;
|
|
1017
|
-
}
|
|
1018
|
-
if (!release) return false;
|
|
1019
|
-
const consuming = node.join === "any" ? [satisfied[0]] : node.join === "any_failed" ? [failed[0]] : settled;
|
|
1020
|
-
for (const runtimeEdge of consuming) {
|
|
1021
|
-
const cap = runtimeEdge.edge.spec.maxTraversals;
|
|
1022
|
-
if (cap !== void 0 && runtimeEdge.traversals >= cap) {
|
|
1023
|
-
record(runtimeEdge, "unpropagated", `traversal-cap-exhausted (max ${cap})`);
|
|
1024
|
-
runtimeEdge.traversals -= 1;
|
|
1025
|
-
exhaustedEdges.add(runtimeEdge.edge.id);
|
|
1026
|
-
runtime.blocked = node.join === "any" || node.join === "any_failed" ? runtime.blocked : true;
|
|
1027
|
-
return false;
|
|
1467
|
+
const wake = async (suspension, by, outRef) => {
|
|
1468
|
+
await appendAndApply({
|
|
1469
|
+
kind: "woken",
|
|
1470
|
+
id: `graphwait:${suspension.token}`,
|
|
1471
|
+
by,
|
|
1472
|
+
...outRef !== void 0 ? { outRef } : {},
|
|
1473
|
+
seq: engineWokenSeq++,
|
|
1474
|
+
at: new Date(now()).toISOString()
|
|
1475
|
+
});
|
|
1476
|
+
const settle = state.instances.get(suspension.instance)?.settle;
|
|
1477
|
+
if (settle !== void 0) {
|
|
1478
|
+
const enriched = {
|
|
1479
|
+
...settle,
|
|
1480
|
+
...settle.outRef !== void 0 ? { out: admitPayload(await blobs.get(settle.outRef)) } : {}
|
|
1481
|
+
};
|
|
1482
|
+
if (enriched.outRef !== void 0 && enriched.out !== void 0) outCache.set(enriched.outRef, enriched.out);
|
|
1483
|
+
await propagate(suspension.instance, enriched);
|
|
1028
1484
|
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
if (
|
|
1037
|
-
|
|
1485
|
+
};
|
|
1486
|
+
/** Expiries are engine-clocked: judged on every loop turn and before parking (#976). */
|
|
1487
|
+
const sweepExpiries = async () => {
|
|
1488
|
+
for (const suspension of [...state.suspensions.values()]) {
|
|
1489
|
+
if (suspension.status !== "pending") continue;
|
|
1490
|
+
if (suspension.expiresAtMs === void 0 || now() < suspension.expiresAtMs) continue;
|
|
1491
|
+
if (suspension.onExpire === "fail") await wake(suspension, "expired");
|
|
1492
|
+
else if (suspension.onExpire === "default") await wake(suspension, "fired", suspension.defaultRef);
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
const processWakes = async () => {
|
|
1496
|
+
while (wakes.length > 0) {
|
|
1497
|
+
const request = wakes.shift();
|
|
1498
|
+
if (!request) break;
|
|
1499
|
+
try {
|
|
1500
|
+
const suspension = state.suspensions.get(request.token);
|
|
1501
|
+
if (!suspension) throw new ValidationError(`graph resume: unknown token '${request.token}'`);
|
|
1502
|
+
if (suspension.status !== "pending") throw new ValidationError(`graph resume: token '${request.token}' already woken`);
|
|
1503
|
+
if (request.expire) if (suspension.onExpire === "fail") await wake(suspension, "expired");
|
|
1504
|
+
else if (suspension.onExpire === "default") await wake(suspension, "fired", suspension.defaultRef);
|
|
1505
|
+
else await wake(suspension, "expired");
|
|
1506
|
+
else {
|
|
1507
|
+
const admitted = admitPayload(request.payload);
|
|
1508
|
+
const outRef = contentAddress(admitted);
|
|
1509
|
+
await blobs.put(outRef, admitted);
|
|
1510
|
+
outCache.set(outRef, admitted);
|
|
1511
|
+
await wake(suspension, "fired", outRef);
|
|
1512
|
+
}
|
|
1513
|
+
request.settle();
|
|
1038
1514
|
} catch (error) {
|
|
1039
|
-
|
|
1040
|
-
reason = error instanceof Error ? error.message : String(error);
|
|
1041
|
-
payload = void 0;
|
|
1515
|
+
request.fail(error);
|
|
1042
1516
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
const handleSettle = async (settled) => {
|
|
1520
|
+
const instanceLabel = liveHandles.get(settled.handle.id);
|
|
1521
|
+
if (instanceLabel === void 0) return;
|
|
1522
|
+
liveHandles.delete(settled.handle.id);
|
|
1523
|
+
liveCount -= 1;
|
|
1524
|
+
const instance = state.instances.get(instanceLabel);
|
|
1525
|
+
const node = instance === void 0 ? void 0 : compiled.nodes.get(instance.node);
|
|
1526
|
+
if (!instance || !node) return;
|
|
1527
|
+
if (settled.kind === "done" && isSuspensionRequest(settled.out)) {
|
|
1528
|
+
await journalSuspension(instanceLabel, settled.out);
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
applyGraphFoldEvent(state, {
|
|
1532
|
+
kind: "settled",
|
|
1533
|
+
id: settled.handle.id,
|
|
1534
|
+
status: settled.kind === "done" ? "done" : "down",
|
|
1535
|
+
...settled.kind === "done" ? { outRef: settled.outRef } : {},
|
|
1536
|
+
...settled.kind === "down" ? { reason: settled.reason } : {},
|
|
1537
|
+
...settled.kind === "done" && settled.trace?.status === "available" ? { trace: settled.trace } : {},
|
|
1538
|
+
spent: {
|
|
1539
|
+
iterations: 0,
|
|
1540
|
+
tokens: {
|
|
1541
|
+
input: 0,
|
|
1542
|
+
output: 0
|
|
1543
|
+
},
|
|
1544
|
+
usd: 0,
|
|
1545
|
+
ms: 0
|
|
1546
|
+
},
|
|
1547
|
+
seq: 0,
|
|
1548
|
+
at: ""
|
|
1549
|
+
}, compiled);
|
|
1550
|
+
const folded = state.instances.get(instanceLabel)?.settle;
|
|
1551
|
+
if (!folded) return;
|
|
1552
|
+
let enriched = folded;
|
|
1553
|
+
if (settled.kind === "done") {
|
|
1554
|
+
const admitted = admitPayload(settled.out);
|
|
1555
|
+
if (settled.outRef !== void 0) outCache.set(settled.outRef, admitted);
|
|
1556
|
+
let valid;
|
|
1557
|
+
if (node.deliverable !== void 0) try {
|
|
1558
|
+
valid = await node.deliverable.check(admitted);
|
|
1559
|
+
} catch {
|
|
1560
|
+
valid = false;
|
|
1561
|
+
}
|
|
1562
|
+
enriched = {
|
|
1563
|
+
...folded,
|
|
1564
|
+
out: admitted,
|
|
1565
|
+
...valid !== void 0 ? { valid } : {}
|
|
1566
|
+
};
|
|
1567
|
+
state.instances.get(instanceLabel).settle = enriched;
|
|
1568
|
+
const settles = state.nodes.get(instance.node)?.settles;
|
|
1569
|
+
if (settles) settles[settles.length - 1] = enriched;
|
|
1570
|
+
}
|
|
1571
|
+
if (instance.node === compiled.root && enriched.status === "done" && enriched.valid === true) {
|
|
1572
|
+
abort.abort("root delivered");
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
await propagate(instanceLabel, enriched);
|
|
1576
|
+
};
|
|
1577
|
+
const enterEntryNode = async (nodeId) => {
|
|
1578
|
+
const folded = state.nodes.get(nodeId);
|
|
1579
|
+
if (!folded) return;
|
|
1580
|
+
const visit = folded.visits + 1;
|
|
1581
|
+
const node = compiled.nodes.get(nodeId);
|
|
1582
|
+
if (!node) return;
|
|
1583
|
+
if (visit > node.maxVisits) return;
|
|
1584
|
+
const instanceLabel = `${nodeId}#${visit}`;
|
|
1585
|
+
const envelope = {
|
|
1586
|
+
task,
|
|
1587
|
+
inputs: {}
|
|
1588
|
+
};
|
|
1589
|
+
const inputRef = contentAddress(envelope);
|
|
1590
|
+
await blobs.put(inputRef, envelope);
|
|
1591
|
+
await appendAndApply({
|
|
1592
|
+
kind: "node-inputs-resolved",
|
|
1593
|
+
id: instanceLabel,
|
|
1594
|
+
node: nodeId,
|
|
1595
|
+
instance: instanceLabel,
|
|
1596
|
+
inputRef,
|
|
1597
|
+
seq: engineSeq++,
|
|
1598
|
+
at: new Date(now()).toISOString()
|
|
1599
|
+
});
|
|
1600
|
+
await spawnInstance(instanceLabel);
|
|
1601
|
+
};
|
|
1602
|
+
if (!resuming) for (const id of compiled.entries) await enterEntryNode(id);
|
|
1603
|
+
else {
|
|
1604
|
+
for (const instance of [...state.instances.values()]) if (instance.status === "released") await spawnInstance(instance.instance);
|
|
1605
|
+
else if (instance.status === "live") {
|
|
1606
|
+
const node = compiled.nodes.get(instance.node);
|
|
1607
|
+
const folded = state.nodes.get(instance.node);
|
|
1608
|
+
if (!node || !folded) continue;
|
|
1609
|
+
const visit = folded.visits + 1;
|
|
1610
|
+
if (visit > node.maxVisits) {
|
|
1611
|
+
failure = {
|
|
1612
|
+
reason: "cycle-budget-exceeded",
|
|
1613
|
+
error: {
|
|
1614
|
+
name: "GraphCycleBudget",
|
|
1615
|
+
message: `node ${instance.node} entered ${visit} times; maxVisits ${node.maxVisits}`
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
break;
|
|
1619
|
+
}
|
|
1620
|
+
const restartLabel = `${instance.node}#${visit}`;
|
|
1621
|
+
instance.status = "down";
|
|
1622
|
+
const at = new Date(now()).toISOString();
|
|
1623
|
+
await journal.appendEvent(runId, {
|
|
1624
|
+
kind: "node-inputs-resolved",
|
|
1625
|
+
id: restartLabel,
|
|
1626
|
+
node: instance.node,
|
|
1627
|
+
instance: restartLabel,
|
|
1628
|
+
inputRef: instance.inputRef,
|
|
1629
|
+
seq: engineSeq++,
|
|
1630
|
+
at
|
|
1631
|
+
});
|
|
1632
|
+
applyGraphFoldEvent(state, {
|
|
1633
|
+
kind: "node-inputs-resolved",
|
|
1634
|
+
id: restartLabel,
|
|
1635
|
+
node: instance.node,
|
|
1636
|
+
instance: restartLabel,
|
|
1637
|
+
inputRef: instance.inputRef,
|
|
1638
|
+
seq: 0,
|
|
1639
|
+
at
|
|
1640
|
+
}, compiled);
|
|
1641
|
+
await spawnInstance(restartLabel);
|
|
1642
|
+
}
|
|
1643
|
+
for (const [nodeId, foldedNode] of state.nodes) {
|
|
1644
|
+
const latest = foldedNode.settles.at(-1);
|
|
1645
|
+
if (latest === void 0) continue;
|
|
1646
|
+
const instanceLabel = `${nodeId}#${latest.visit}`;
|
|
1647
|
+
let enriched = latest;
|
|
1648
|
+
if (latest.status === "done" && latest.outRef !== void 0) {
|
|
1649
|
+
const out = admitPayload(await blobs.get(latest.outRef));
|
|
1650
|
+
if (isSuspensionRequest(out)) {
|
|
1651
|
+
foldedNode.settles.pop();
|
|
1652
|
+
const instance = state.instances.get(instanceLabel);
|
|
1653
|
+
if (instance) instance.settle = void 0;
|
|
1654
|
+
await journalSuspension(instanceLabel, out);
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
outCache.set(latest.outRef, out);
|
|
1658
|
+
const nodeSpec = compiled.nodes.get(nodeId);
|
|
1659
|
+
let valid;
|
|
1660
|
+
if (nodeSpec?.deliverable !== void 0) try {
|
|
1661
|
+
valid = await nodeSpec.deliverable.check(out);
|
|
1662
|
+
} catch {
|
|
1663
|
+
valid = false;
|
|
1664
|
+
}
|
|
1665
|
+
enriched = {
|
|
1666
|
+
...latest,
|
|
1667
|
+
out,
|
|
1668
|
+
...valid !== void 0 ? { valid } : {}
|
|
1669
|
+
};
|
|
1054
1670
|
}
|
|
1055
|
-
|
|
1671
|
+
await propagate(instanceLabel, enriched);
|
|
1056
1672
|
}
|
|
1673
|
+
for (const id of compiled.entries) if ((state.nodes.get(id)?.visits ?? 0) === 0) await enterEntryNode(id);
|
|
1674
|
+
for (const id of compiled.nodes.keys()) await tryRelease(id);
|
|
1057
1675
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if (runtime.consumedOnce && runtime.state === "pending") {
|
|
1074
|
-
runtime.consumedOnce = false;
|
|
1676
|
+
const allTerminalsSettled = () => compiled.terminals.every((id) => (state.nodes.get(id)?.settles.length ?? 0) > 0);
|
|
1677
|
+
let wakeSignal = Promise.resolve();
|
|
1678
|
+
let armWake = () => {};
|
|
1679
|
+
const rearmWakeSignal = () => {
|
|
1680
|
+
wakeSignal = new Promise((fire) => {
|
|
1681
|
+
armWake = fire;
|
|
1682
|
+
});
|
|
1683
|
+
onWakeSignal(() => armWake());
|
|
1684
|
+
};
|
|
1685
|
+
rearmWakeSignal();
|
|
1686
|
+
let inFlightNext;
|
|
1687
|
+
while (!failure) {
|
|
1688
|
+
await sweepExpiries();
|
|
1689
|
+
if (wakes.length > 0) {
|
|
1690
|
+
await processWakes();
|
|
1075
1691
|
continue;
|
|
1076
1692
|
}
|
|
1077
|
-
if (
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1693
|
+
if (allTerminalsSettled()) break;
|
|
1694
|
+
if (liveCount === 0) {
|
|
1695
|
+
const pendingSuspensions = [...state.suspensions.values()].filter((suspension) => suspension.status === "pending");
|
|
1696
|
+
if (pendingSuspensions.length > 0) {
|
|
1697
|
+
const nearest = Math.min(...pendingSuspensions.map((suspension) => suspension.expiresAtMs ?? Number.POSITIVE_INFINITY));
|
|
1698
|
+
if (Number.isFinite(nearest) && now() >= nearest) continue;
|
|
1699
|
+
if (options.waitForWakes) {
|
|
1700
|
+
rearmWakeSignal();
|
|
1701
|
+
await wakeSignal;
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
const defaulting = pendingSuspensions.filter((suspension) => suspension.onExpire === "default");
|
|
1705
|
+
if (defaulting.length > 0) {
|
|
1706
|
+
for (const suspension of defaulting) await wake(suspension, "fired", suspension.defaultRef);
|
|
1707
|
+
continue;
|
|
1708
|
+
}
|
|
1709
|
+
break;
|
|
1710
|
+
}
|
|
1711
|
+
break;
|
|
1090
1712
|
}
|
|
1713
|
+
inFlightNext ??= scope.next();
|
|
1714
|
+
const raced = await Promise.race([inFlightNext.then((settle) => ({ settle })), wakeSignal.then(() => "wake")]);
|
|
1715
|
+
if (raced === "wake") {
|
|
1716
|
+
rearmWakeSignal();
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
inFlightNext = void 0;
|
|
1720
|
+
if (raced.settle === null) break;
|
|
1721
|
+
await handleSettle(raced.settle);
|
|
1722
|
+
for (const parked of waitingForBudget.splice(0)) await spawnInstance(parked);
|
|
1091
1723
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
liveCount -= 1;
|
|
1103
|
-
const node = compiled.nodes.get(nodeId);
|
|
1104
|
-
const runtime = nodes.get(nodeId);
|
|
1105
|
-
if (!node || !runtime) continue;
|
|
1106
|
-
runtime.live -= 1;
|
|
1107
|
-
let entry;
|
|
1108
|
-
if (settled.kind === "done") {
|
|
1109
|
-
const admitted = admitPayload(settled.out);
|
|
1110
|
-
let valid;
|
|
1111
|
-
if (node.deliverable !== void 0) try {
|
|
1112
|
-
valid = await node.deliverable.check(admitted);
|
|
1113
|
-
} catch (error) {
|
|
1724
|
+
abort.abort("graph loop complete");
|
|
1725
|
+
while (await (inFlightNext ?? scope.next()) !== null) inFlightNext = void 0;
|
|
1726
|
+
const rehydrate = async (settle) => {
|
|
1727
|
+
if (settle.out !== void 0 || settle.outRef === void 0) return settle;
|
|
1728
|
+
const out = outCache.get(settle.outRef) ?? admitPayload(await blobs.get(settle.outRef));
|
|
1729
|
+
const node = compiled.nodes.get(settle.node);
|
|
1730
|
+
let valid = settle.valid;
|
|
1731
|
+
if (valid === void 0 && node?.deliverable !== void 0 && settle.status === "done") try {
|
|
1732
|
+
valid = await node.deliverable.check(out);
|
|
1733
|
+
} catch {
|
|
1114
1734
|
valid = false;
|
|
1115
1735
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
...valid !== void 0 ? { valid } : {},
|
|
1121
|
-
out: admitted,
|
|
1122
|
-
outRef: settled.outRef
|
|
1736
|
+
return {
|
|
1737
|
+
...settle,
|
|
1738
|
+
out,
|
|
1739
|
+
...valid !== void 0 ? { valid } : {}
|
|
1123
1740
|
};
|
|
1124
|
-
if (settled.trace?.status === "available") entry.traceRef = settled.trace.traceRef;
|
|
1125
|
-
} else if (settled.kind === "down") entry = {
|
|
1126
|
-
node: nodeId,
|
|
1127
|
-
visit: runtime.visits,
|
|
1128
|
-
status: "down",
|
|
1129
|
-
reason: settled.reason
|
|
1130
1741
|
};
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1742
|
+
const settles = await Promise.all([...compiled.nodes.keys()].flatMap((id) => state.nodes.get(id)?.settles ?? []).map(rehydrate));
|
|
1743
|
+
const terminals = settles.filter((settle) => compiled.nodes.get(settle.node)?.terminal);
|
|
1744
|
+
const finish = (result) => {
|
|
1745
|
+
if (result.kind === "no-winner" && state.exhaustedEdges.size > 0) throw new GraphEdgeCapError(Object.freeze([...state.exhaustedEdges]), Object.freeze([...ledger]), result);
|
|
1746
|
+
return result;
|
|
1747
|
+
};
|
|
1748
|
+
if (failure) return finish({
|
|
1749
|
+
kind: "no-winner",
|
|
1750
|
+
reason: failure.reason,
|
|
1751
|
+
...failure.error ? { error: failure.error } : {},
|
|
1752
|
+
terminals,
|
|
1753
|
+
settles,
|
|
1754
|
+
ledger,
|
|
1755
|
+
unreachable: []
|
|
1756
|
+
});
|
|
1757
|
+
const pendingTokens = [...state.suspensions.values()].filter((suspension) => suspension.status === "pending").map((suspension) => suspension.token);
|
|
1758
|
+
if (pendingTokens.length > 0 && !allTerminalsSettled()) return {
|
|
1759
|
+
kind: "suspended",
|
|
1760
|
+
tokens: pendingTokens,
|
|
1761
|
+
terminals,
|
|
1762
|
+
settles,
|
|
1763
|
+
ledger
|
|
1764
|
+
};
|
|
1765
|
+
if (options.signal?.aborted) return finish({
|
|
1766
|
+
kind: "no-winner",
|
|
1767
|
+
reason: "aborted",
|
|
1768
|
+
terminals,
|
|
1769
|
+
settles,
|
|
1770
|
+
ledger,
|
|
1771
|
+
unreachable: []
|
|
1772
|
+
});
|
|
1773
|
+
const out = terminals.some((settle) => settle.status === "done" && settle.valid !== false) ? await runFinalizer(options.finalizer === void 0 || options.finalizer === "bestDelivered" ? bestDelivered : options.finalizer === "collectDelivered" ? collectDelivered : options.finalizer, {
|
|
1774
|
+
settled: terminals.map((settle) => ({
|
|
1775
|
+
id: settle.node,
|
|
1776
|
+
status: settle.status,
|
|
1777
|
+
valid: settle.status === "done" && settle.valid !== false,
|
|
1778
|
+
...settle.outRef !== void 0 ? { outRef: settle.outRef } : {}
|
|
1779
|
+
})),
|
|
1155
1780
|
blobs,
|
|
1156
1781
|
tree: scope.view,
|
|
1157
1782
|
budget: scope.budget
|
|
1783
|
+
}) : void 0;
|
|
1784
|
+
if (out !== void 0) return {
|
|
1785
|
+
kind: "winner",
|
|
1786
|
+
out,
|
|
1787
|
+
terminals,
|
|
1788
|
+
settles,
|
|
1789
|
+
ledger
|
|
1790
|
+
};
|
|
1791
|
+
const unreachable = [...compiled.nodes.keys()].filter((id) => (state.nodes.get(id)?.settles.length ?? 0) === 0);
|
|
1792
|
+
return finish({
|
|
1793
|
+
kind: "no-winner",
|
|
1794
|
+
reason: compiled.terminals.some((id) => unreachable.includes(id)) ? unreachable.length === compiled.nodes.size ? "budget-exhausted" : "unreachable-terminal" : "all-children-down",
|
|
1795
|
+
terminals,
|
|
1796
|
+
settles,
|
|
1797
|
+
ledger,
|
|
1798
|
+
unreachable
|
|
1158
1799
|
});
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
if (options.signal?.aborted) return finish({
|
|
1175
|
-
kind: "no-winner",
|
|
1176
|
-
reason: "aborted",
|
|
1177
|
-
terminals,
|
|
1178
|
-
settles,
|
|
1179
|
-
ledger,
|
|
1180
|
-
unreachable: []
|
|
1181
|
-
});
|
|
1182
|
-
const out = compiled.terminals.some((id) => (nodes.get(id)?.settles ?? []).some((settle) => settle.status === "done" && settle.valid !== false)) ? await finalize() : void 0;
|
|
1183
|
-
if (out !== void 0) return {
|
|
1184
|
-
kind: "winner",
|
|
1185
|
-
out,
|
|
1186
|
-
terminals,
|
|
1187
|
-
settles,
|
|
1188
|
-
ledger
|
|
1189
|
-
};
|
|
1190
|
-
const unreachable = [...compiled.nodes.keys()].filter((id) => (nodes.get(id)?.settles.length ?? 0) === 0);
|
|
1191
|
-
return finish({
|
|
1192
|
-
kind: "no-winner",
|
|
1193
|
-
reason: compiled.terminals.some((id) => unreachable.includes(id)) ? unreachable.length === compiled.nodes.size ? "budget-exhausted" : "unreachable-terminal" : "all-children-down",
|
|
1194
|
-
terminals,
|
|
1195
|
-
settles,
|
|
1196
|
-
ledger,
|
|
1197
|
-
unreachable
|
|
1198
|
-
});
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
function hasLiveInstance(state, liveHandles, node) {
|
|
1803
|
+
for (const label of liveHandles.values()) if (state.instances.get(label)?.node === node) return true;
|
|
1804
|
+
return false;
|
|
1805
|
+
}
|
|
1806
|
+
function maxEngineSeq(events) {
|
|
1807
|
+
let max = -1;
|
|
1808
|
+
for (const ev of events) {
|
|
1809
|
+
if (ev.kind === "node-inputs-resolved" || ev.kind === "edge-verdict" || ev.kind === "join-state") {
|
|
1810
|
+
if (ev.seq > max) max = ev.seq;
|
|
1811
|
+
}
|
|
1812
|
+
if (ev.kind === "waiting" && ev.spec.kind === "token" && ev.seq > max) max = ev.seq;
|
|
1813
|
+
}
|
|
1814
|
+
return max;
|
|
1199
1815
|
}
|
|
1200
1816
|
//#endregion
|
|
1201
|
-
export { CONDITION_OPS, DEFAULT_MAX_NODE_VISITS, JOIN_RULES, MAX_MAX_NODE_VISITS, admitPayload, agentKind, applyProjection, compileGraph, createGraphEngine, createRegistry, evaluateCondition, formatRegistryHandle, kindHandle, narrowEffects, parseRegistryHandle, runEngineGraph, schemaAccepts, scriptKind, subgraphKind, supervisorKind, validateCondition, validateNodeKind, validateProjection };
|
|
1817
|
+
export { CONDITION_OPS, DEFAULT_MAX_NODE_VISITS, ENGINE_WOKEN_SEQ_BASE, JOIN_RULES, MAX_MAX_NODE_VISITS, admitPayload, agentKind, applyGraphFoldEvent, applyProjection, compileGraph, createGraphEngine, createGraphRun, createRegistry, emptyFoldState, evaluateCondition, foldGraphJournal, formatRegistryHandle, kindHandle, narrowEffects, parseRegistryHandle, runEngineGraph, schemaAccepts, scriptKind, subgraphKind, supervisorKind, suspended, validateCondition, validateNodeKind, validateProjection };
|
|
1202
1818
|
|
|
1203
1819
|
//# sourceMappingURL=graph.js.map
|