@tangle-network/agent-runtime 0.165.0 → 0.166.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-DmUIq51m.js → activation-BkO0p4I1.js} +2 -2
- package/dist/{activation-DmUIq51m.js.map → activation-BkO0p4I1.js.map} +1 -1
- package/dist/agent.js +2 -2
- package/dist/{authoring-Cm122Duf.js → authoring-Bt17KOML.js} +2 -2
- package/dist/{authoring-Cm122Duf.js.map → authoring-Bt17KOML.js.map} +1 -1
- package/dist/durable.js +1 -1
- package/dist/graph-E0U0c2YP.js +2668 -0
- package/dist/graph-E0U0c2YP.js.map +1 -0
- package/dist/graph.d.ts +179 -15
- package/dist/graph.js +2 -1819
- package/dist/{improvement-cycle-BOd5DFeG.js → improvement-cycle-BAWOyeTA.js} +3 -3
- package/dist/{improvement-cycle-BOd5DFeG.js.map → improvement-cycle-BAWOyeTA.js.map} +1 -1
- package/dist/index.js +7 -7
- package/dist/intelligence.js +3 -3
- package/dist/kernel.js +6 -6
- package/dist/{knowledge-7_IfyD9R.js → knowledge-Bhhz6lvH.js} +3 -3
- package/dist/{knowledge-7_IfyD9R.js.map → knowledge-Bhhz6lvH.js.map} +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-Dx-5GZQK.js → loop-runner-bin-BQnpSn0h.js} +3 -3
- package/dist/{loop-runner-bin-Dx-5GZQK.js.map → loop-runner-bin-BQnpSn0h.js.map} +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-CDS7eSww.js → openai-tools-t6spVYKL.js} +2 -2
- package/dist/{openai-tools-CDS7eSww.js.map → openai-tools-t6spVYKL.js.map} +1 -1
- package/dist/{runtime-DESENUe1.js → runtime-BJoNta1Y.js} +6 -6
- package/dist/{runtime-DESENUe1.js.map → runtime-BJoNta1Y.js.map} +1 -1
- package/dist/{structural-rollout-DZGDg0yX.js → structural-rollout-B5E0mWCH.js} +2 -2
- package/dist/{structural-rollout-DZGDg0yX.js.map → structural-rollout-B5E0mWCH.js.map} +1 -1
- package/dist/{supervise-Da20R0PO.js → supervise-CnxqHx1P.js} +2 -2
- package/dist/{supervise-Da20R0PO.js.map → supervise-CnxqHx1P.js.map} +1 -1
- package/dist/{supervisor-DpIKMlPJ.js → supervisor-C8kgh8mf.js} +21 -2
- package/dist/{supervisor-DpIKMlPJ.js.map → supervisor-C8kgh8mf.js.map} +1 -1
- package/dist/testing.js +11 -11
- package/package.json +1 -1
- package/dist/graph-DGri_zJm.js +0 -532
- package/dist/graph-DGri_zJm.js.map +0 -1
- package/dist/graph.js.map +0 -1
package/dist/testing.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ct as immutableCandidateValue, et as parseExactAgentProfile, it as canonicalCandidateDigest$1, q as applyExactAgentProfileDiff } from "./prepare-BCScSDCv.js";
|
|
2
|
-
import { f as verifyAgentImprovementProposal } from "./improvement-cycle-
|
|
3
|
-
import { c as supervisorAgentWithTestBrain, ft as driverAgent, r as superviseWithTestBrain } from "./supervise-
|
|
4
|
-
import { i as runGraphWithTestBrain } from "./graph-
|
|
2
|
+
import { f as verifyAgentImprovementProposal } from "./improvement-cycle-BAWOyeTA.js";
|
|
3
|
+
import { c as supervisorAgentWithTestBrain, ft as driverAgent, r as superviseWithTestBrain } from "./supervise-CnxqHx1P.js";
|
|
4
|
+
import { i as runGraphWithTestBrain } from "./graph-E0U0c2YP.js";
|
|
5
5
|
import { SANDBOX_SIZE_PRESET_NAMES } from "@tangle-network/agent-interface";
|
|
6
6
|
//#region src/testing/fixtures/agent-improvement-proposal.json
|
|
7
7
|
var agent_improvement_proposal_default = {
|
|
8
8
|
changedSurfaces: ["prompt"],
|
|
9
|
-
digest: "sha256:
|
|
9
|
+
digest: "sha256:139b4e63472943844aeeec1e79c3b010f6bd8330d220803a25555f0f5ab9d625",
|
|
10
10
|
evaluation: {
|
|
11
11
|
"decision": {
|
|
12
12
|
"contributingChecks": [
|
|
@@ -4577,7 +4577,7 @@ var agent_improvement_proposal_default = {
|
|
|
4577
4577
|
],
|
|
4578
4578
|
"metadata": {
|
|
4579
4579
|
"fixture": "agent-improvement-proposal",
|
|
4580
|
-
"runtimeVersion": "0.
|
|
4580
|
+
"runtimeVersion": "0.166.0"
|
|
4581
4581
|
},
|
|
4582
4582
|
"objectives": [
|
|
4583
4583
|
{
|
|
@@ -4688,8 +4688,8 @@ var agent_improvement_proposal_default = {
|
|
|
4688
4688
|
"baselineContentHash": "sha256:5c21ee53e513fc604cb09754e21c392b24a424da0ef37dbf8f1ee4a8a0b08f09",
|
|
4689
4689
|
"candidateContentHash": "sha256:60fcbb1c728194bd51d7d19cb732d1c3f1881dce7e0a6266b41c8b98cfd65693",
|
|
4690
4690
|
"kind": "agent-eval-loop",
|
|
4691
|
-
"recordDigest": "sha256:
|
|
4692
|
-
"runId": "agent-runtime-0.
|
|
4691
|
+
"recordDigest": "sha256:81fe3b3e866f49786232541eb7da42bf213399f00c6a558ffd8c2b76b02e5ee1",
|
|
4692
|
+
"runId": "agent-runtime-0.166.0-proposal-fixture",
|
|
4693
4693
|
"schema": "agent-candidate-experiment"
|
|
4694
4694
|
}
|
|
4695
4695
|
},
|
|
@@ -4712,13 +4712,13 @@ var agent_improvement_proposal_default = {
|
|
|
4712
4712
|
}],
|
|
4713
4713
|
kind: "agent-improvement-proposal",
|
|
4714
4714
|
proposedAt: "2026-07-10T01:00:00.000Z",
|
|
4715
|
-
runId: "agent-runtime-0.
|
|
4715
|
+
runId: "agent-runtime-0.166.0-proposal-fixture"
|
|
4716
4716
|
};
|
|
4717
4717
|
//#endregion
|
|
4718
4718
|
//#region src/testing/fixtures/agent-profile-improvement-proposal.json
|
|
4719
4719
|
var agent_profile_improvement_proposal_default = {
|
|
4720
4720
|
changedSurfaces: ["prompt", "skills"],
|
|
4721
|
-
digest: "sha256:
|
|
4721
|
+
digest: "sha256:ba63ca7bd3bfd70f3a23698db348ddd5d859c3e2180a072d5ceb6af6b67b7f83",
|
|
4722
4722
|
evaluation: {
|
|
4723
4723
|
"decision": {
|
|
4724
4724
|
"contributingChecks": [
|
|
@@ -6352,7 +6352,7 @@ var agent_profile_improvement_proposal_default = {
|
|
|
6352
6352
|
],
|
|
6353
6353
|
"metadata": {
|
|
6354
6354
|
"fixture": "agent-profile-improvement-proposal",
|
|
6355
|
-
"runtimeVersion": "0.
|
|
6355
|
+
"runtimeVersion": "0.166.0"
|
|
6356
6356
|
},
|
|
6357
6357
|
"objectives": [
|
|
6358
6358
|
{
|
|
@@ -6463,7 +6463,7 @@ var agent_profile_improvement_proposal_default = {
|
|
|
6463
6463
|
"baselineContentHash": "sha256:21c495a37c418c10bde64fbaa188beddeed31f1f051ea60a6a6582a9ee0db704",
|
|
6464
6464
|
"candidateContentHash": "sha256:103f77bc8481601eef1ad5fe6ba84a40dffabc3a44f421f8c8559121edab84e9",
|
|
6465
6465
|
"kind": "agent-eval-loop",
|
|
6466
|
-
"recordDigest": "sha256:
|
|
6466
|
+
"recordDigest": "sha256:88b11a3cfbf0302f9e70cd2722324f1185606a005e0b57cc82755c127dd20239",
|
|
6467
6467
|
"runId": "profile-improvement-1",
|
|
6468
6468
|
"schema": "agent-profile-improvement-experiment"
|
|
6469
6469
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.166.0",
|
|
4
4
|
"description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
|
|
5
5
|
"homepage": "https://github.com/tangle-network/agent-runtime#readme",
|
|
6
6
|
"repository": {
|
package/dist/graph-DGri_zJm.js
DELETED
|
@@ -1,532 +0,0 @@
|
|
|
1
|
-
import { m as ValidationError } from "./errors-CDZ8XsVj.js";
|
|
2
|
-
import { i as InMemorySpawnJournal, r as InMemoryResultBlobStore } from "./spawn-journal-Tgvp5VS5.js";
|
|
3
|
-
import { f as harnessRunsAgent } from "./model-policy-Sw4ywhtL.js";
|
|
4
|
-
import { t as composeRuntimeHooks } from "./runtime-hooks-tXpAarhW.js";
|
|
5
|
-
import { h as kernelPromptRegistry, m as formatPromptHandle, n as supervise, r as superviseWithTestBrain } from "./supervise-Da20R0PO.js";
|
|
6
|
-
import { agentProfileSchema, canonicalCandidateDigest } from "@tangle-network/agent-interface";
|
|
7
|
-
//#region src/runtime/supervise/graph.ts
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
10
|
-
* `runGraph` — agent graphs: profiles as nodes, registry-backed prompt directives as edges.
|
|
11
|
-
*
|
|
12
|
-
* A topology is PLAIN DATA an agent can author in a few lines: nodes are canonical
|
|
13
|
-
* `AgentProfile`s (the ONLY way a node is described — no role-builder functions), edges are typed
|
|
14
|
-
* values carrying versioned {@link PromptHandle} directives, `deliverable` (termination) and
|
|
15
|
-
* `budget` (one conserved pool) are mandatory. Driver↔worker is the two-node cyclic instance;
|
|
16
|
-
* "agent 3 analyzes 1 and 2 and reports to 1" is ONE edge, not a framework.
|
|
17
|
-
*
|
|
18
|
-
* NOT A SECOND SCHEDULER. `runGraph` is an interpretation layer over what already runs:
|
|
19
|
-
* `supervise()` is the execution core — the same `supervisorAgent`/`driverAgent` machinery,
|
|
20
|
-
* backend-derived worker path (authorized, classified, recursive), conserved-pool budget, and
|
|
21
|
-
* deliverable-gated settlement every
|
|
22
|
-
* supervised run uses. (`runAgentRounds` is deliberately NOT the substrate here.) What the graph
|
|
23
|
-
* layer ADDS is exactly what a bespoke driver loop never
|
|
24
|
-
* had:
|
|
25
|
-
*
|
|
26
|
-
* 1. **Node pinning** — a spawn names a node (`profile.name` = node id) and the node's canonical
|
|
27
|
-
* profile is what runs; a driver cannot smuggle capabilities into a worker it did not define.
|
|
28
|
-
* 2. **Observable edges** — every delegates/analyzes traversal lands in an EDGE LEDGER
|
|
29
|
-
* (`delivered | stripped | empty | unpropagated`, with byte counts), in memory on
|
|
30
|
-
* the result AND as `edge` events in the run journal. The motivating incident: a filter
|
|
31
|
-
* silently replaced 1,700-char steering with 241 chars of boilerplate for three rounds and
|
|
32
|
-
* NO artifact said so — an unobservable edge cannot be trusted and its directive cannot be
|
|
33
|
-
* optimized.
|
|
34
|
-
* 3. **Directives as data** — edge text lives in the prompt registry (`<surface>/v<n>`), so every
|
|
35
|
-
* edge is a versioned optimization target, never prose hardcoded in a builder function.
|
|
36
|
-
* 4. **Per-edge traversal caps** — the cyclic-graph backstop. A delegates edge whose cap is
|
|
37
|
-
* exhausted REFUSES further traversals (fail loud), so a cycle cannot spin the pool dry.
|
|
38
|
-
* 5. **Continuity as data** — a delegates edge may declare `continuity: 'resume'`, so each spawn
|
|
39
|
-
* after the node's first re-attaches to its latest SETTLED session (the spawn context hands
|
|
40
|
-
* the executor seam `resume: { ofWorker, sequence }`; the kernel keeps identity, ordering,
|
|
41
|
-
* ledger truth, and the one conserved pool). Every ledger row states how its hop continued:
|
|
42
|
-
* `'fresh' | 'resume'` for spawns, `'steer'` for mid-run deliveries — fresh respawns, session
|
|
43
|
-
* resumes, and live steers are all plain data, each a ledgered fact.
|
|
44
|
-
*
|
|
45
|
-
* ORACLES ARE ENVIRONMENT, NEVER WORKERS. Graders/verifiers must not be spawnable in the graph —
|
|
46
|
-
* a delegates edge to them leaks the rubric. An `analyzes` edge names its analyst in one of two
|
|
47
|
-
* forms: a LENS id from the environment's registry (a pure function over trace evidence), or the
|
|
48
|
-
* id of a graph NODE — a tool-equipped analyst AGENT spawned on each matching settle with the
|
|
49
|
-
* node's pinned profile, whose settle output IS the findings. Either way the oracle doctrine
|
|
50
|
-
* holds: an analyst node can never be a delegates target (refused loudly), so no driver can hand
|
|
51
|
-
* it work, and an id living in both the registry and the nodes is refused as ambiguous.
|
|
52
|
-
*
|
|
53
|
-
* @experimental
|
|
54
|
-
*/
|
|
55
|
-
/** Default per-edge traversal cap — the cyclic-graph backstop when an edge names none. */
|
|
56
|
-
const defaultEdgeTraversalCap = 32;
|
|
57
|
-
/** A delegates edge exhausted its traversal cap and the run produced no winner: the cap, not the
|
|
58
|
-
* task, ended it. Carries the full evidence so failing loud loses nothing. */
|
|
59
|
-
var GraphEdgeCapError = class extends Error {
|
|
60
|
-
exhaustedEdges;
|
|
61
|
-
ledger;
|
|
62
|
-
result;
|
|
63
|
-
constructor(exhaustedEdges, ledger, result) {
|
|
64
|
-
super(`runGraph: edge traversal cap exhausted on ${exhaustedEdges.join(", ")} and the run delivered no winner — the cap (the cyclic-graph backstop), not the task, ended this run. Raise maxTraversals on the edge or fix the cycle; the full edge ledger and the supervised result ride on this error.`);
|
|
65
|
-
this.name = "GraphEdgeCapError";
|
|
66
|
-
this.exhaustedEdges = exhaustedEdges;
|
|
67
|
-
this.ledger = ledger;
|
|
68
|
-
this.result = result;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Forwarded to the root `supervise()` VERBATIM. Everything not owned, transformed, or refused
|
|
73
|
-
* above belongs here — the default is "a graph honors it", not "someone remembered to add it".
|
|
74
|
-
*/
|
|
75
|
-
const GRAPH_FORWARDED_SUPERVISE_OPTIONS = [
|
|
76
|
-
"backend",
|
|
77
|
-
"rootHandle",
|
|
78
|
-
"signal",
|
|
79
|
-
"execution",
|
|
80
|
-
"resolveDeliverable",
|
|
81
|
-
"coordination",
|
|
82
|
-
"peerMail",
|
|
83
|
-
"driverBackend",
|
|
84
|
-
"profileSecurity",
|
|
85
|
-
"authorizeSpawn",
|
|
86
|
-
"isDriverProfile",
|
|
87
|
-
"router",
|
|
88
|
-
"driveHarness",
|
|
89
|
-
"driverRetry",
|
|
90
|
-
"onDriverAttempt",
|
|
91
|
-
"childSettleGraceMs",
|
|
92
|
-
"resolveDriveHarness",
|
|
93
|
-
"driveHarnessMaterialization",
|
|
94
|
-
"resolveSupervisorTools",
|
|
95
|
-
"extraTools",
|
|
96
|
-
"executeExtraTool",
|
|
97
|
-
"perWorker",
|
|
98
|
-
"maxLiveWorkers",
|
|
99
|
-
"watchWorkers",
|
|
100
|
-
"stallAfterMs",
|
|
101
|
-
"runDir",
|
|
102
|
-
"probes",
|
|
103
|
-
"stopRule",
|
|
104
|
-
"onProgressStop",
|
|
105
|
-
"maxDepth",
|
|
106
|
-
"maxTurns",
|
|
107
|
-
"compaction",
|
|
108
|
-
"now",
|
|
109
|
-
"allowedModels",
|
|
110
|
-
"finalizer",
|
|
111
|
-
"otel"
|
|
112
|
-
];
|
|
113
|
-
/** Copy every forwarded option the caller actually set. Absent stays absent: `supervise()` and the
|
|
114
|
-
* graph must not disagree about what "unset" means. */
|
|
115
|
-
function forwardedSuperviseOptions(opts) {
|
|
116
|
-
const forwarded = {};
|
|
117
|
-
for (const key of GRAPH_FORWARDED_SUPERVISE_OPTIONS) {
|
|
118
|
-
const value = opts[key];
|
|
119
|
-
if (value !== void 0) forwarded[key] = value;
|
|
120
|
-
}
|
|
121
|
-
return forwarded;
|
|
122
|
-
}
|
|
123
|
-
function edgeId(edge) {
|
|
124
|
-
return edge.kind === "delegates" ? `delegates:${edge.from}->${edge.to}` : `analyzes:${edge.analyst}:${edge.over.join("+")}->${edge.to}`;
|
|
125
|
-
}
|
|
126
|
-
/** Validate the graph and resolve every directive BEFORE any compute is spent — an invalid
|
|
127
|
-
* topology or an unknown directive is a configuration fault, never a mid-run surprise. */
|
|
128
|
-
function validateGraph(graph, registry, analysts) {
|
|
129
|
-
if (!Array.isArray(graph.nodes) || graph.nodes.length === 0) throw new ValidationError("runGraph: graph.nodes must be a non-empty array");
|
|
130
|
-
if (!Array.isArray(graph.edges) || graph.edges.length === 0) throw new ValidationError("runGraph: graph.edges must be a non-empty array");
|
|
131
|
-
if (typeof graph.deliverable?.check !== "function") throw new ValidationError("runGraph: graph.deliverable is mandatory (termination oracle)");
|
|
132
|
-
if (typeof graph.budget !== "object" || graph.budget === null) throw new ValidationError("runGraph: graph.budget is mandatory (the conserved pool)");
|
|
133
|
-
const byId = /* @__PURE__ */ new Map();
|
|
134
|
-
for (const node of graph.nodes) {
|
|
135
|
-
if (typeof node.id !== "string" || node.id.length === 0) throw new ValidationError("runGraph: every node needs a non-empty string id");
|
|
136
|
-
if (byId.has(node.id)) throw new ValidationError(`runGraph: duplicate node id '${node.id}'`);
|
|
137
|
-
const parsed = agentProfileSchema.safeParse(node.profile);
|
|
138
|
-
if (!parsed.success) throw new ValidationError(`runGraph: node '${node.id}' has an invalid AgentProfile: ${parsed.error.message}`);
|
|
139
|
-
if (node.profile.name !== node.id) throw new ValidationError(`runGraph: node '${node.id}' has profile.name ${JSON.stringify(node.profile.name)} — profile.name IS the node identity (node pinning and analyst routing match on it) and must equal the node id`);
|
|
140
|
-
byId.set(node.id, node);
|
|
141
|
-
}
|
|
142
|
-
const requireNode = (id, where) => {
|
|
143
|
-
const node = byId.get(id);
|
|
144
|
-
if (!node) throw new ValidationError(`runGraph: ${where} references unknown node '${id}'`);
|
|
145
|
-
return node;
|
|
146
|
-
};
|
|
147
|
-
const delegates = graph.edges.filter((edge) => edge.kind === "delegates");
|
|
148
|
-
const analyzes = graph.edges.filter((edge) => edge.kind === "analyzes");
|
|
149
|
-
if (delegates.length === 0) throw new ValidationError("runGraph: at least one delegates edge is required (who spawns whom)");
|
|
150
|
-
for (const edge of graph.edges) registry.resolve(edge.directive);
|
|
151
|
-
for (const edge of delegates) {
|
|
152
|
-
requireNode(edge.from, edgeId(edge));
|
|
153
|
-
requireNode(edge.to, edgeId(edge));
|
|
154
|
-
if (edge.from === edge.to) throw new ValidationError(`runGraph: ${edgeId(edge)} delegates to itself — the driver↔worker cycle is the settle-return loop, not a self-edge`);
|
|
155
|
-
if (edge.continuity !== void 0 && edge.continuity !== "fresh" && edge.continuity !== "resume") throw new ValidationError(`runGraph: ${edgeId(edge)} has invalid continuity ${JSON.stringify(edge.continuity)} — a delegates edge's continuity is 'fresh' or 'resume'`);
|
|
156
|
-
}
|
|
157
|
-
const delegatedTo = new Set(delegates.map((edge) => edge.to));
|
|
158
|
-
const roots = [...new Set(delegates.map((edge) => edge.from))].filter((id) => !delegatedTo.has(id));
|
|
159
|
-
if (roots.length !== 1) throw new ValidationError(`runGraph: expected exactly ONE root (a node that delegates and is never delegated to), found ${roots.length === 0 ? "none — delegates edges form a cycle with no entry" : roots.join(", ")}. P0 executes driver↔worker(s); nested driver graphs are P3.`);
|
|
160
|
-
const root = requireNode(roots[0], "root resolution");
|
|
161
|
-
for (const edge of delegates) if (edge.from !== root.id) throw new ValidationError(`runGraph: ${edgeId(edge)} delegates from a non-root node — P0 executes one driver over its workers (the 2-node cyclic case, star-generalized); deeper delegation is P3`);
|
|
162
|
-
const analystIds = /* @__PURE__ */ new Set();
|
|
163
|
-
const analystNodes = /* @__PURE__ */ new Map();
|
|
164
|
-
for (const edge of analyzes) {
|
|
165
|
-
if (edge.continuity !== void 0) throw new ValidationError(`runGraph: ${edgeId(edge)} carries continuity — analysts are spawned by the analyst machinery (every analyst run is a fresh session over settled evidence), so continuity is a delegates-edge axis only`);
|
|
166
|
-
if (analystIds.has(edge.analyst)) throw new ValidationError(`runGraph: two analyzes edges share analyst '${edge.analyst}' — one analyzes edge per analyst lens (traversals are ledgered by analyst id; a second edge would silently absorb the first's). Register the lens under a second id for a second edge.`);
|
|
167
|
-
analystIds.add(edge.analyst);
|
|
168
|
-
const analystNode = byId.get(edge.analyst);
|
|
169
|
-
const inRegistry = analysts?.kinds.some((kind) => kind.id === edge.analyst) === true;
|
|
170
|
-
if (analystNode !== void 0 && inRegistry) throw new ValidationError(`runGraph: ${edgeId(edge)} analyst '${edge.analyst}' is BOTH a graph node and a lens in the analysts registry — the id alone distinguishes the two analyst forms, so this is ambiguous; rename the node or register the lens under another id`);
|
|
171
|
-
if (analystNode !== void 0) {
|
|
172
|
-
if (analystNode.id === root.id) throw new ValidationError(`runGraph: ${edgeId(edge)} names the ROOT as its analyst — the root is the driver; give the analyst its own node with no delegates edge pointing at it`);
|
|
173
|
-
if (delegatedTo.has(analystNode.id)) throw new ValidationError(`runGraph: ${edgeId(edge)} names node '${edge.analyst}' as its analyst, but that node is a delegates target — oracle doctrine: an analyst is never delegated to. An analyst NODE is legal only with NO delegates edge pointing at it; give the analyst its own delegates-free node or pass a lens id from RunGraphOptions.analysts.`);
|
|
174
|
-
analystNodes.set(analystNode.id, analystNode);
|
|
175
|
-
} else if (!analysts) throw new ValidationError(`runGraph: ${edgeId(edge)} analyst '${edge.analyst}' is not a graph node, and no RunGraphOptions.analysts registry was provided to resolve it as a lens`);
|
|
176
|
-
else if (!inRegistry) throw new ValidationError(`runGraph: ${edgeId(edge)} analyst '${edge.analyst}' is neither a graph node nor in the analysts registry (known lenses: ${analysts.kinds.map((kind) => kind.id).join(", ") || "none"})`);
|
|
177
|
-
if (edge.over.length === 0) throw new ValidationError(`runGraph: ${edgeId(edge)} must analyze at least one node`);
|
|
178
|
-
for (const over of edge.over) {
|
|
179
|
-
requireNode(over, edgeId(edge));
|
|
180
|
-
if (over === root.id) throw new ValidationError(`runGraph: ${edgeId(edge)} analyzes the ROOT — analysts observe settled workers, and the root never settles as one, so this edge would silently never fire; list delegates-target nodes only`);
|
|
181
|
-
}
|
|
182
|
-
requireNode(edge.to, edgeId(edge));
|
|
183
|
-
}
|
|
184
|
-
for (const edge of analyzes) for (const over of edge.over) if (analystNodes.has(over)) throw new ValidationError(`runGraph: ${edgeId(edge)} analyzes '${over}', which is an analyst node — an analyst run settles as a finding, never as a worker, so this edge would silently never fire; analyst nodes are not analyzable`);
|
|
185
|
-
const workers = /* @__PURE__ */ new Map();
|
|
186
|
-
const delegatesByWorker = /* @__PURE__ */ new Map();
|
|
187
|
-
for (const edge of delegates) {
|
|
188
|
-
if (delegatesByWorker.has(edge.to)) throw new ValidationError(`runGraph: node '${edge.to}' is the target of two delegates edges — one delegation directive per worker node (version the directive instead of forking the edge)`);
|
|
189
|
-
delegatesByWorker.set(edge.to, edge);
|
|
190
|
-
workers.set(edge.to, requireNode(edge.to, edgeId(edge)));
|
|
191
|
-
}
|
|
192
|
-
for (const node of graph.nodes) if (node.id !== root.id && !workers.has(node.id) && !analystNodes.has(node.id)) throw new ValidationError(`runGraph: node '${node.id}' has no delegates edge to it — an unreachable node never runs`);
|
|
193
|
-
return {
|
|
194
|
-
root,
|
|
195
|
-
workers,
|
|
196
|
-
delegatesByWorker,
|
|
197
|
-
analyzes,
|
|
198
|
-
analystNodes
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
const byteLength = (text) => Buffer.byteLength(text, "utf8");
|
|
202
|
-
function stringifyPayload(payload) {
|
|
203
|
-
if (typeof payload === "string") return payload;
|
|
204
|
-
try {
|
|
205
|
-
return JSON.stringify(payload) ?? String(payload);
|
|
206
|
-
} catch {
|
|
207
|
-
return String(payload);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Execute an {@link AgentGraph}. The root node becomes the supervisor (`supervise()` — the
|
|
212
|
-
* execution core), each worker node is spawnable BY NODE ID (`spawn_agent` with
|
|
213
|
-
* `profile: { name: '<node id>' }`; the node's canonical profile is pinned by the graph), each
|
|
214
|
-
* delegates directive is appended to the worker profile's `prompt.instructions` per traversal,
|
|
215
|
-
* and each analyzes edge becomes an analyst-on-settle route with a real DESTINATION. Every
|
|
216
|
-
* traversal is ledgered and journaled.
|
|
217
|
-
*/
|
|
218
|
-
function runGraph(graph, opts) {
|
|
219
|
-
const { brain, ...runtimeOptions } = opts;
|
|
220
|
-
return runGraphInternal(graph, runtimeOptions, brain);
|
|
221
|
-
}
|
|
222
|
-
/** Alias for graph tests written before `RunGraphOptions.brain` was production. The production
|
|
223
|
-
* entry accepts the same shape; this wrapper only keeps the `/testing` import path working. */
|
|
224
|
-
function runGraphWithTestBrain(graph, opts) {
|
|
225
|
-
const { brain, ...runtimeOptions } = opts;
|
|
226
|
-
return runGraphInternal(graph, runtimeOptions, brain);
|
|
227
|
-
}
|
|
228
|
-
function runGraphInternal(graph, opts, brain) {
|
|
229
|
-
const registry = opts.registry ?? kernelPromptRegistry();
|
|
230
|
-
const { root, workers, delegatesByWorker, analyzes, analystNodes } = validateGraph(graph, registry, opts.analysts);
|
|
231
|
-
if (brain && opts.driverBackend) throw new ValidationError("runGraph: brain and driverBackend are mutually exclusive — a caller brain makes the root model calls, a driverBackend places a harness that makes its own");
|
|
232
|
-
if (brain && harnessRunsAgent(root.profile.harness)) throw new ValidationError(`runGraph: root node '${root.id}' declares harness '${root.profile.harness}', so the harness drives it — a caller brain applies only to a router-brained root (profile.harness omitted or 'cli-base')`);
|
|
233
|
-
if (!opts.backend && !opts.makeLeafAgent) throw new ValidationError("runGraph: provide opts.backend (where nodes run) or opts.makeLeafAgent");
|
|
234
|
-
const journal = opts.journal ?? new InMemorySpawnJournal();
|
|
235
|
-
const blobs = opts.blobs ?? new InMemoryResultBlobStore();
|
|
236
|
-
const runId = opts.runId ?? `graph-${canonicalCandidateDigest(graph.nodes.map((n) => n.id)).slice(7, 19)}`;
|
|
237
|
-
const now = opts.now ?? Date.now;
|
|
238
|
-
const ledger = [];
|
|
239
|
-
const journaled = /* @__PURE__ */ new Set();
|
|
240
|
-
const traversalCounts = /* @__PURE__ */ new Map();
|
|
241
|
-
const exhausted = /* @__PURE__ */ new Set();
|
|
242
|
-
const exhaustedDelegates = /* @__PURE__ */ new Set();
|
|
243
|
-
const journalWrites = [];
|
|
244
|
-
let ledgerSeq = 0;
|
|
245
|
-
const appendJournal = (entry, nodeIdForEvent) => {
|
|
246
|
-
if (journaled.has(entry)) return Promise.resolve();
|
|
247
|
-
journaled.add(entry);
|
|
248
|
-
const write = journal.appendEvent(runId, {
|
|
249
|
-
kind: "edge",
|
|
250
|
-
id: nodeIdForEvent,
|
|
251
|
-
edge: {
|
|
252
|
-
kind: entry.kind,
|
|
253
|
-
from: entry.from,
|
|
254
|
-
to: entry.to,
|
|
255
|
-
directive: entry.directive
|
|
256
|
-
},
|
|
257
|
-
traversal: entry.traversal,
|
|
258
|
-
outcome: entry.outcome,
|
|
259
|
-
continuity: entry.continuity,
|
|
260
|
-
bytes: entry.bytes,
|
|
261
|
-
...entry.reason !== void 0 ? { reason: entry.reason } : {},
|
|
262
|
-
seq: ledgerSeq++,
|
|
263
|
-
at: new Date(now()).toISOString()
|
|
264
|
-
});
|
|
265
|
-
journalWrites.push(write);
|
|
266
|
-
return write;
|
|
267
|
-
};
|
|
268
|
-
const record = (entry, journalNow) => {
|
|
269
|
-
const count = (traversalCounts.get(entry.edge) ?? 0) + 1;
|
|
270
|
-
traversalCounts.set(entry.edge, count);
|
|
271
|
-
const row = {
|
|
272
|
-
...entry,
|
|
273
|
-
traversal: count
|
|
274
|
-
};
|
|
275
|
-
ledger.push(row);
|
|
276
|
-
if (journalNow) appendJournal(row, row.workerId ?? `graph:${row.to}`);
|
|
277
|
-
return row;
|
|
278
|
-
};
|
|
279
|
-
const nodeByWorkerId = /* @__PURE__ */ new Map();
|
|
280
|
-
const pendingByAssignment = /* @__PURE__ */ new Map();
|
|
281
|
-
const pinNode = (input) => {
|
|
282
|
-
const requested = typeof input.profile?.name === "string" ? input.profile.name : void 0;
|
|
283
|
-
if (input.analyst !== void 0) {
|
|
284
|
-
const analystNode = analystNodes.get(input.analyst);
|
|
285
|
-
if (!analystNode || requested !== analystNode.id) throw new ValidationError(`runGraph: analyst run for ${JSON.stringify(input.analyst)} does not name an analyst node of this graph (analyst nodes: ${[...analystNodes.keys()].join(", ") || "none"})`);
|
|
286
|
-
return analystNode.profile;
|
|
287
|
-
}
|
|
288
|
-
const node = requested !== void 0 ? workers.get(requested) : void 0;
|
|
289
|
-
if (!node) throw new ValidationError(`runGraph: spawn_agent named profile ${JSON.stringify(requested)} which is not a worker node of this graph (nodes: ${[...workers.keys()].join(", ")}). Spawn by node id: profile.name selects the node; the node profile itself is pinned by the graph.`);
|
|
290
|
-
const edge = delegatesByWorker.get(node.id);
|
|
291
|
-
const id = edgeId(edge);
|
|
292
|
-
const cap = edge.maxTraversals ?? 32;
|
|
293
|
-
const used = traversalCounts.get(id) ?? 0;
|
|
294
|
-
const spawnContinuity = input.continuity ?? "fresh";
|
|
295
|
-
if (used >= cap) {
|
|
296
|
-
exhausted.add(id);
|
|
297
|
-
exhaustedDelegates.add(id);
|
|
298
|
-
record({
|
|
299
|
-
edge: id,
|
|
300
|
-
kind: "delegates",
|
|
301
|
-
from: edge.from,
|
|
302
|
-
to: edge.to,
|
|
303
|
-
directive: formatPromptHandle(edge.directive),
|
|
304
|
-
outcome: "unpropagated",
|
|
305
|
-
continuity: spawnContinuity,
|
|
306
|
-
bytes: 0,
|
|
307
|
-
reason: `traversal-cap-exhausted (max ${cap})`
|
|
308
|
-
}, true);
|
|
309
|
-
throw new ValidationError(`runGraph: delegates edge ${id} exhausted its traversal cap (${cap}) — the cyclic-graph backstop refused this spawn`);
|
|
310
|
-
}
|
|
311
|
-
const directiveText = registry.resolve(edge.directive).text;
|
|
312
|
-
const taskText = stringifyPayload(input.task);
|
|
313
|
-
const bytes = byteLength(directiveText) + byteLength(taskText);
|
|
314
|
-
const row = record({
|
|
315
|
-
edge: id,
|
|
316
|
-
kind: "delegates",
|
|
317
|
-
from: edge.from,
|
|
318
|
-
to: edge.to,
|
|
319
|
-
directive: formatPromptHandle(edge.directive),
|
|
320
|
-
outcome: bytes === 0 ? "empty" : "delivered",
|
|
321
|
-
continuity: spawnContinuity,
|
|
322
|
-
bytes,
|
|
323
|
-
...bytes === 0 ? { reason: "no directive text and no task payload" } : {}
|
|
324
|
-
}, false);
|
|
325
|
-
pendingByAssignment.set(input.assignmentId, row);
|
|
326
|
-
return directiveText.length === 0 ? node.profile : {
|
|
327
|
-
...node.profile,
|
|
328
|
-
prompt: {
|
|
329
|
-
...node.profile.prompt ?? {},
|
|
330
|
-
instructions: [...node.profile.prompt?.instructions ?? [], directiveText]
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
};
|
|
334
|
-
const resolveSpawnProfile = (authored) => {
|
|
335
|
-
const requested = typeof authored.name === "string" ? authored.name : void 0;
|
|
336
|
-
return ((requested !== void 0 ? workers.get(requested) : void 0) ?? (requested !== void 0 ? analystNodes.get(requested) : void 0))?.profile ?? authored;
|
|
337
|
-
};
|
|
338
|
-
const graphAuthorizeSpawn = (input) => {
|
|
339
|
-
const pinned = pinNode(input);
|
|
340
|
-
if (!opts.authorizeSpawn) return { profile: pinned };
|
|
341
|
-
return opts.authorizeSpawn({
|
|
342
|
-
...input,
|
|
343
|
-
profile: pinned
|
|
344
|
-
});
|
|
345
|
-
};
|
|
346
|
-
const routes = analyzes.map((edge) => {
|
|
347
|
-
const analystNode = analystNodes.get(edge.analyst);
|
|
348
|
-
if (analystNode) return {
|
|
349
|
-
kind: edge.analyst,
|
|
350
|
-
over: edge.over,
|
|
351
|
-
agent: analystNode.profile,
|
|
352
|
-
directive: registry.resolve(edge.directive).text,
|
|
353
|
-
...edge.to === root.id ? {} : { to: edge.to }
|
|
354
|
-
};
|
|
355
|
-
return edge.to === root.id ? {
|
|
356
|
-
kind: edge.analyst,
|
|
357
|
-
over: edge.over
|
|
358
|
-
} : {
|
|
359
|
-
kind: edge.analyst,
|
|
360
|
-
over: edge.over,
|
|
361
|
-
to: edge.to,
|
|
362
|
-
directive: registry.resolve(edge.directive).text
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
const driverAnalyzesBriefs = analyzes.filter((edge) => edge.to === root.id).map((edge) => analystNodes.has(edge.analyst) ? `Findings from analyst '${edge.analyst}' (a tool-equipped analyst agent node, over: ${edge.over.join(", ")}) will arrive as finding events.` : `Findings from analyst '${edge.analyst}' (over: ${edge.over.join(", ")}) will arrive as finding events.\n${registry.resolve(edge.directive).text}`);
|
|
366
|
-
const continuityByProfile = {};
|
|
367
|
-
for (const [nodeId, edge] of delegatesByWorker) if (edge.continuity !== void 0) continuityByProfile[nodeId] = edge.continuity;
|
|
368
|
-
const graphBrief = [
|
|
369
|
-
"AGENT GRAPH: you are the driver node of a fixed topology. You may spawn ONLY these worker",
|
|
370
|
-
"nodes, by EXACT name (spawn_agent with profile: { name: '<node id>' }; the node's full",
|
|
371
|
-
"profile is pinned by the graph — any other profile fields you author are ignored):",
|
|
372
|
-
...[...workers.values()].map((node) => {
|
|
373
|
-
const edge = delegatesByWorker.get(node.id);
|
|
374
|
-
const cap = edge.maxTraversals ?? 32;
|
|
375
|
-
const description = typeof node.profile.description === "string" && node.profile.description.length > 0 ? ` — ${node.profile.description}` : "";
|
|
376
|
-
const continuityNote = edge.continuity === "resume" ? "; continuity: resume — each spawn after the first re-attaches to this node's latest settled session (spawn again to continue it; steer while it is live)" : "";
|
|
377
|
-
return `- '${node.id}'${description} (delegation cap: ${cap} traversals${continuityNote})`;
|
|
378
|
-
}),
|
|
379
|
-
...driverAnalyzesBriefs.length > 0 ? ["", ...driverAnalyzesBriefs] : []
|
|
380
|
-
].join("\n");
|
|
381
|
-
const rootProfile = {
|
|
382
|
-
...root.profile,
|
|
383
|
-
prompt: {
|
|
384
|
-
...root.profile.prompt ?? {},
|
|
385
|
-
instructions: [...root.profile.prompt?.instructions ?? [], graphBrief]
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
const strippedByDigest = /* @__PURE__ */ new Map();
|
|
389
|
-
const authorizeMessage = (input) => {
|
|
390
|
-
if (!opts.authorizeMessage) return { instruction: input.instruction };
|
|
391
|
-
const decision = opts.authorizeMessage(input);
|
|
392
|
-
if (decision.instruction !== input.instruction) strippedByDigest.set(canonicalCandidateDigest(decision.instruction), { composedBytes: byteLength(input.instruction) });
|
|
393
|
-
return decision;
|
|
394
|
-
};
|
|
395
|
-
const routedAnalyzesByAnalyst = /* @__PURE__ */ new Map();
|
|
396
|
-
const driverAnalyzesByAnalyst = /* @__PURE__ */ new Map();
|
|
397
|
-
for (const edge of analyzes) (edge.to === root.id ? driverAnalyzesByAnalyst : routedAnalyzesByAnalyst).set(edge.analyst, edge);
|
|
398
|
-
const analyzesCapReached = (edge) => {
|
|
399
|
-
const cap = edge.maxTraversals ?? 32;
|
|
400
|
-
if ((traversalCounts.get(edgeId(edge)) ?? 0) < cap) return false;
|
|
401
|
-
exhausted.add(edgeId(edge));
|
|
402
|
-
return true;
|
|
403
|
-
};
|
|
404
|
-
const ledgerAnalyzes = (edge, outcome, bytes, reason, workerId) => {
|
|
405
|
-
const capped = analyzesCapReached(edge);
|
|
406
|
-
record({
|
|
407
|
-
edge: edgeId(edge),
|
|
408
|
-
kind: "analyzes",
|
|
409
|
-
from: edge.over.join("+"),
|
|
410
|
-
to: edge.to,
|
|
411
|
-
directive: formatPromptHandle(edge.directive),
|
|
412
|
-
outcome: capped ? "unpropagated" : outcome,
|
|
413
|
-
continuity: "steer",
|
|
414
|
-
bytes,
|
|
415
|
-
...capped ? { reason: `traversal-cap-exhausted (max ${edge.maxTraversals ?? 32})` } : reason !== void 0 ? { reason } : {},
|
|
416
|
-
...workerId !== void 0 ? { workerId } : {}
|
|
417
|
-
}, true);
|
|
418
|
-
};
|
|
419
|
-
const onCoordinationEvent = async (_context, _eventId, recordEnvelope) => {
|
|
420
|
-
const event = recordEnvelope.event;
|
|
421
|
-
if (event.type === "finding") {
|
|
422
|
-
const edge = driverAnalyzesByAnalyst.get(event.finding.analyst);
|
|
423
|
-
if (!edge) return;
|
|
424
|
-
const sourceNode = nodeByWorkerId.get(event.finding.fromWorker);
|
|
425
|
-
if (sourceNode === void 0 || !edge.over.includes(sourceNode)) return;
|
|
426
|
-
const findingsText = event.finding.findings === void 0 ? "" : stringifyPayload(event.finding.findings);
|
|
427
|
-
const directiveBytes = byteLength(registry.resolve(edge.directive).text);
|
|
428
|
-
const empty = findingsText.length === 0;
|
|
429
|
-
ledgerAnalyzes(edge, empty ? "empty" : "delivered", directiveBytes + byteLength(findingsText), empty ? "analyst returned no findings" : void 0, event.finding.fromWorker);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
if (event.type === "steer") {
|
|
433
|
-
const down = event.down;
|
|
434
|
-
if (event.analyst !== void 0) {
|
|
435
|
-
const edge = routedAnalyzesByAnalyst.get(event.analyst);
|
|
436
|
-
if (!edge) return;
|
|
437
|
-
ledgerAnalyzes(edge, down.delivered ? "delivered" : "unpropagated", byteLength(down.instruction), down.delivered ? void 0 : down.outcome, down.toWorker);
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
const nodeId = nodeByWorkerId.get(down.toWorker);
|
|
441
|
-
if (nodeId === void 0) return;
|
|
442
|
-
const edge = delegatesByWorker.get(nodeId);
|
|
443
|
-
if (!edge) return;
|
|
444
|
-
const stripped = strippedByDigest.get(down.instructionDigest);
|
|
445
|
-
record({
|
|
446
|
-
edge: edgeId(edge),
|
|
447
|
-
kind: "delegates",
|
|
448
|
-
from: edge.from,
|
|
449
|
-
to: edge.to,
|
|
450
|
-
directive: formatPromptHandle(edge.directive),
|
|
451
|
-
outcome: !down.delivered ? "unpropagated" : stripped ? "stripped" : "delivered",
|
|
452
|
-
continuity: "steer",
|
|
453
|
-
bytes: byteLength(down.instruction),
|
|
454
|
-
...!down.delivered ? { reason: down.outcome } : stripped ? { reason: `authorization narrowed ${stripped.composedBytes} composed bytes` } : {},
|
|
455
|
-
workerId: down.toWorker
|
|
456
|
-
}, true);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
const hooks = composeRuntimeHooks({ onEvent: (event) => {
|
|
460
|
-
if (event.target !== "agent.spawn" || event.phase !== "after") return;
|
|
461
|
-
const payload = event.payload;
|
|
462
|
-
if (typeof payload?.childId !== "string" || typeof payload.assignmentId !== "string") return;
|
|
463
|
-
const pending = pendingByAssignment.get(payload.assignmentId);
|
|
464
|
-
if (!pending) return;
|
|
465
|
-
pendingByAssignment.delete(payload.assignmentId);
|
|
466
|
-
const bound = {
|
|
467
|
-
...pending,
|
|
468
|
-
workerId: payload.childId
|
|
469
|
-
};
|
|
470
|
-
ledger[ledger.indexOf(pending)] = bound;
|
|
471
|
-
nodeByWorkerId.set(payload.childId, bound.to);
|
|
472
|
-
return appendJournal(bound, payload.childId);
|
|
473
|
-
} }, opts.hooks);
|
|
474
|
-
const start = async () => {
|
|
475
|
-
const superviseOptions = {
|
|
476
|
-
...forwardedSuperviseOptions(opts),
|
|
477
|
-
rootDriverFromBackend: false,
|
|
478
|
-
budget: graph.budget,
|
|
479
|
-
deliverable: graph.deliverable,
|
|
480
|
-
authorizeSpawn: graphAuthorizeSpawn,
|
|
481
|
-
resolveSpawnProfile,
|
|
482
|
-
...opts.makeLeafAgent ? { makeLeafAgent: opts.makeLeafAgent } : {},
|
|
483
|
-
journal,
|
|
484
|
-
blobs,
|
|
485
|
-
runId,
|
|
486
|
-
hooks,
|
|
487
|
-
onCoordinationEvent,
|
|
488
|
-
...routes.length > 0 ? {
|
|
489
|
-
analyzeOnSettle: routes,
|
|
490
|
-
...opts.analysts ? { analysts: opts.analysts } : {}
|
|
491
|
-
} : {},
|
|
492
|
-
...Object.keys(continuityByProfile).length > 0 ? { continuityByProfile } : {},
|
|
493
|
-
authorizeMessage
|
|
494
|
-
};
|
|
495
|
-
const result = brain === void 0 ? await supervise(rootProfile, graphTask(graph, root), superviseOptions) : await superviseWithTestBrain(rootProfile, graphTask(graph, root), {
|
|
496
|
-
...superviseOptions,
|
|
497
|
-
brain
|
|
498
|
-
});
|
|
499
|
-
for (const pending of pendingByAssignment.values()) {
|
|
500
|
-
const refused = {
|
|
501
|
-
...pending,
|
|
502
|
-
outcome: "unpropagated",
|
|
503
|
-
bytes: 0,
|
|
504
|
-
reason: `no-live-worker-bound (spawn refused after the factory, or a keyed re-spawn deduplicated to a completed result; ${pending.bytes} composed bytes never crossed)`
|
|
505
|
-
};
|
|
506
|
-
ledger[ledger.indexOf(pending)] = refused;
|
|
507
|
-
await appendJournal(refused, `graph:${refused.to}`);
|
|
508
|
-
}
|
|
509
|
-
pendingByAssignment.clear();
|
|
510
|
-
await Promise.all(journalWrites);
|
|
511
|
-
const exhaustedEdges = Object.freeze([...exhausted]);
|
|
512
|
-
const frozenLedger = Object.freeze(ledger.map((row) => Object.freeze({ ...row })));
|
|
513
|
-
const lifecycleEnded = result.kind === "no-winner" && (result.reason === "aborted" || result.reason === "budget-exhausted");
|
|
514
|
-
if (result.kind !== "winner" && !lifecycleEnded && exhaustedDelegates.size > 0) throw new GraphEdgeCapError(Object.freeze([...exhaustedDelegates]), frozenLedger, result);
|
|
515
|
-
return {
|
|
516
|
-
result,
|
|
517
|
-
ledger: frozenLedger,
|
|
518
|
-
exhaustedEdges,
|
|
519
|
-
runId
|
|
520
|
-
};
|
|
521
|
-
};
|
|
522
|
-
return start();
|
|
523
|
-
}
|
|
524
|
-
/** The root task: the graph's own framing. The deliverable (mandatory) is the termination; the
|
|
525
|
-
* task names what the topology exists to produce. */
|
|
526
|
-
function graphTask(graph, root) {
|
|
527
|
-
return graph.deliverable.describe ?? `Deliver the graph's deliverable by driving your worker nodes (root: '${root.id}').`;
|
|
528
|
-
}
|
|
529
|
-
//#endregion
|
|
530
|
-
export { runGraphWithTestBrain as i, defaultEdgeTraversalCap as n, runGraph as r, GraphEdgeCapError as t };
|
|
531
|
-
|
|
532
|
-
//# sourceMappingURL=graph-DGri_zJm.js.map
|