@tangle-network/agent-runtime 0.337.2 → 0.339.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-DRHM55Oo.d.ts → activation-CJIIl_Ey.d.ts} +90 -22
- package/dist/activation-CVSFxis9.js +876 -0
- package/dist/activation-CVSFxis9.js.map +1 -0
- package/dist/agent.d.ts +4 -4
- package/dist/agent.js +4 -7
- package/dist/agent.js.map +1 -1
- package/dist/{bundle-BPGjESee.js → bundle-DVOhBTic.js} +2 -2
- package/dist/{bundle-BPGjESee.js.map → bundle-DVOhBTic.js.map} +1 -1
- package/dist/candidate-execution/index.d.ts +4 -63
- package/dist/candidate-execution/index.js +6 -146
- package/dist/candidate-execution/index.js.map +1 -1
- package/dist/content-digest-sBTLujlK.js +9 -0
- package/dist/content-digest-sBTLujlK.js.map +1 -0
- package/dist/{delegate-B-T_ZB50.js → delegate-CCWE_AkU.js} +2 -2
- package/dist/{delegate-B-T_ZB50.js.map → delegate-CCWE_AkU.js.map} +1 -1
- package/dist/{coordination-driver-BHkFVqIq.js → driver-control-acknowledgers-C53ggdJ6.js} +4520 -4442
- package/dist/driver-control-acknowledgers-C53ggdJ6.js.map +1 -0
- package/dist/{durable-file-B_eJr6Mb.js → durable-file-gNI9ILAn.js} +1 -3
- package/dist/durable-file-gNI9ILAn.js.map +1 -0
- package/dist/durable.d.ts +3 -35
- package/dist/durable.js +225 -229
- package/dist/durable.js.map +1 -1
- package/dist/{graph-C4RFlWAJ.js → graph-CI9JXKqO.js} +65 -18
- package/dist/graph-CI9JXKqO.js.map +1 -0
- package/dist/{graph-registry-DAI6KNXB.js → graph-registry-BnO64cn8.js} +18 -17
- package/dist/graph-registry-BnO64cn8.js.map +1 -0
- package/dist/{improvement-cycle-D1I4hQvf.js → improve-BOXTLWJ3.js} +76 -1248
- package/dist/improve-BOXTLWJ3.js.map +1 -0
- package/dist/{improvement-adapter-D5gwwoXQ.d.ts → improvement-adapter-HUt_-cWA.d.ts} +2 -20
- package/dist/index.d.ts +10 -207
- package/dist/index.js +91 -305
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +10 -229
- package/dist/intelligence.js +506 -88
- package/dist/intelligence.js.map +1 -1
- package/dist/{jsonl-file-DBv31uPi.js → jsonl-file-C9Kde_0Z.js} +2 -2
- package/dist/{jsonl-file-DBv31uPi.js.map → jsonl-file-C9Kde_0Z.js.map} +1 -1
- package/dist/kernel.d.ts +4 -3
- package/dist/kernel.js +12 -13
- package/dist/loop-runner-CWzG1bH4.d.ts +32 -0
- package/dist/loop-runner-bin.d.ts +32 -2
- package/dist/loop-runner-bin.js +93 -1
- package/dist/loop-runner-bin.js.map +1 -0
- package/dist/loop-runner-pmPIHFVY.js +59 -0
- package/dist/loop-runner-pmPIHFVY.js.map +1 -0
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +101 -416
- package/dist/mcp/index.js +122 -206
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-bin.js +231 -1
- package/dist/mcp/memory-bin.js.map +1 -1
- package/dist/model-policy-CyCUwHed.js.map +1 -1
- package/dist/placement.d.ts +2 -9
- package/dist/placement.js +1 -1
- package/dist/placement.js.map +1 -1
- package/dist/platform.d.ts +194 -2
- package/dist/platform.js +145 -9
- package/dist/platform.js.map +1 -1
- package/dist/{prepare-CER-ZkPp.js → prepare-bXkvPMJA.js} +1213 -710
- package/dist/prepare-bXkvPMJA.js.map +1 -0
- package/dist/{profile-gNUsHNIJ.js → profile-Dpei_Sq0.js} +89 -3
- package/dist/profile-Dpei_Sq0.js.map +1 -0
- package/dist/{profile-materialization-BnoEf1co.d.ts → profile-materialization-B1wm2SPT.d.ts} +3 -5
- package/dist/profiles.d.ts +2 -2
- package/dist/profiles.js +4 -43
- package/dist/profiles.js.map +1 -1
- package/dist/{protected-model-port-BhpQRTw-.d.ts → protected-model-port-BGgpy2r9.d.ts} +4 -30
- package/dist/protected-redaction-DFCeo4vg.js.map +1 -1
- package/dist/{provision-supervisor-DGegCUH4.js → provision-supervisor-CJ6Zz6dz.js} +13 -10
- package/dist/provision-supervisor-CJ6Zz6dz.js.map +1 -0
- package/dist/record-value-B6bwJxLa.js +12 -0
- package/dist/record-value-B6bwJxLa.js.map +1 -0
- package/dist/{run-layout-Cu8H7aeh.js → run-cancellation-C9oL62sW.js} +175 -100
- package/dist/run-cancellation-C9oL62sW.js.map +1 -0
- package/dist/{runtime-BOGpotnH.js → runtime-B2fjL9Oi.js} +2639 -3187
- package/dist/runtime-B2fjL9Oi.js.map +1 -0
- package/dist/{runtime-hooks-Bj6wJHlH.d.ts → runtime-hooks-nsqxSUFL.d.ts} +2 -6
- package/dist/{sandbox-profile-compat-QTkLe1Z6.js → sandbox-profile-compat-BzVBFqrz.js} +690 -699
- package/dist/sandbox-profile-compat-BzVBFqrz.js.map +1 -0
- package/dist/search-executor-zMumiak_.js +1916 -0
- package/dist/search-executor-zMumiak_.js.map +1 -0
- package/dist/{server-hZWM-G8w.js → server-_4voDNrl.js} +362 -67
- package/dist/server-_4voDNrl.js.map +1 -0
- package/dist/{search-executor-Cm1WZmL-.js → structural-rollout-DpLCXzaL.js} +1680 -3403
- package/dist/structural-rollout-DpLCXzaL.js.map +1 -0
- package/dist/{substrate-B0TYNrXn.js → substrate-CGDFTSRh.js} +2 -9
- package/dist/substrate-CGDFTSRh.js.map +1 -0
- package/dist/{supervise-CvAKVfue.js → supervise-qAwXmPtD.js} +7768 -5360
- package/dist/supervise-qAwXmPtD.js.map +1 -0
- package/dist/{supervisor-Rz8AGpYb.js → supervisor-DHrUJhEZ.js} +26301 -26964
- package/dist/supervisor-DHrUJhEZ.js.map +1 -0
- package/dist/testing.d.ts +227 -2
- package/dist/testing.js +13 -14
- package/dist/testing.js.map +1 -1
- package/dist/tool-loop.d.ts +1 -1
- package/dist/{tool-server-Dqer2M4_.js → tool-server-XV4aJwoQ.js} +2 -2
- package/dist/tool-server-XV4aJwoQ.js.map +1 -0
- package/dist/top-snapshot-CbZE1JsU.js +502 -0
- package/dist/top-snapshot-CbZE1JsU.js.map +1 -0
- package/dist/tui/bin.js +711 -1
- package/dist/tui/bin.js.map +1 -1
- package/dist/tui/index.d.ts +5 -71
- package/dist/tui/index.js +3 -3
- package/dist/{types-Dl5L4r8Z.d.ts → types-CaOXqRTq.d.ts} +16230 -17376
- package/dist/workspace-store/index.d.ts +2 -27
- package/dist/workspace-store/index.js +2 -2
- package/dist/workspace-store/index.js.map +1 -1
- package/dist/{workspace-streams-JLjh5DC6.d.ts → workspace-streams-BiS4ABah.d.ts} +19 -32
- package/package.json +2 -2
- package/skills/agent-graphs/SKILL.md +10 -3
- package/skills/agent-graphs/references/authoring.md +18 -0
- package/skills/profile-authoring/SKILL.md +3 -3
- package/skills/supervise/SKILL.md +2 -2
- package/dist/activation-BDP6Eivv.js +0 -234
- package/dist/activation-BDP6Eivv.js.map +0 -1
- package/dist/coder-yhVWbdWc.js +0 -40
- package/dist/coder-yhVWbdWc.js.map +0 -1
- package/dist/coordination-driver-BHkFVqIq.js.map +0 -1
- package/dist/delegation-status-CF4D0NZ_.js +0 -358
- package/dist/delegation-status-CF4D0NZ_.js.map +0 -1
- package/dist/durable-file-B_eJr6Mb.js.map +0 -1
- package/dist/graph-C4RFlWAJ.js.map +0 -1
- package/dist/graph-registry-DAI6KNXB.js.map +0 -1
- package/dist/improvement-cycle-D1I4hQvf.js.map +0 -1
- package/dist/integrations-DopfCC2a.d.ts +0 -190
- package/dist/integrations-dFi-ALk7.js +0 -132
- package/dist/integrations-dFi-ALk7.js.map +0 -1
- package/dist/kb-gate-C8z2juK8.d.ts +0 -80
- package/dist/kb-gate-DpaSwXVx.js +0 -80
- package/dist/kb-gate-DpaSwXVx.js.map +0 -1
- package/dist/loop-runner-bin-DOuBIJ2O.d.ts +0 -164
- package/dist/loop-runner-bin-Dv5QvYDH.js +0 -246
- package/dist/loop-runner-bin-Dv5QvYDH.js.map +0 -1
- package/dist/memory-server-DXQ8IxGR.js +0 -234
- package/dist/memory-server-DXQ8IxGR.js.map +0 -1
- package/dist/openai-tools-DoQ32vpe.js +0 -44
- package/dist/openai-tools-DoQ32vpe.js.map +0 -1
- package/dist/openai-tools-Oxk4KQrY.d.ts +0 -24
- package/dist/prepare-CER-ZkPp.js.map +0 -1
- package/dist/profile-gNUsHNIJ.js.map +0 -1
- package/dist/protected-model-port-BuRsviSL.js +0 -766
- package/dist/protected-model-port-BuRsviSL.js.map +0 -1
- package/dist/provision-supervisor-DGegCUH4.js.map +0 -1
- package/dist/run-layout-Cu8H7aeh.js.map +0 -1
- package/dist/runtime-BOGpotnH.js.map +0 -1
- package/dist/sandbox-profile-compat-QTkLe1Z6.js.map +0 -1
- package/dist/search-executor-Cm1WZmL-.js.map +0 -1
- package/dist/server-hZWM-G8w.js.map +0 -1
- package/dist/substrate-B0TYNrXn.js.map +0 -1
- package/dist/supervise-CvAKVfue.js.map +0 -1
- package/dist/supervisor-Rz8AGpYb.js.map +0 -1
- package/dist/tool-server-Dqer2M4_.js.map +0 -1
- package/dist/top-app-BaMw1W2z.js +0 -1212
- package/dist/top-app-BaMw1W2z.js.map +0 -1
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
import { o as NotFoundError } from "./errors-DodWX-cb.js";
|
|
2
|
-
//#region src/mcp/feedback-store.ts
|
|
3
|
-
/** In-memory `FeedbackStore` — suitable for single-process use and tests. @stable */
|
|
4
|
-
var InMemoryFeedbackStore = class {
|
|
5
|
-
events = [];
|
|
6
|
-
async put(event) {
|
|
7
|
-
this.events.push({ ...event });
|
|
8
|
-
}
|
|
9
|
-
async list(filter = {}) {
|
|
10
|
-
let out = this.events;
|
|
11
|
-
if (filter.namespace !== void 0) out = out.filter((event) => event.namespace === filter.namespace);
|
|
12
|
-
if (filter.refersToRef !== void 0) out = out.filter((event) => event.refersTo.ref === filter.refersToRef);
|
|
13
|
-
return out.map((event) => ({ ...event }));
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Project a `FeedbackEvent` down to the snapshot shape carried on
|
|
18
|
-
* `delegation_history` entries.
|
|
19
|
-
*
|
|
20
|
-
* @stable
|
|
21
|
-
*/
|
|
22
|
-
function eventToSnapshot(event) {
|
|
23
|
-
const snap = {
|
|
24
|
-
id: event.id,
|
|
25
|
-
score: event.rating.score,
|
|
26
|
-
by: event.by,
|
|
27
|
-
notes: event.rating.notes,
|
|
28
|
-
capturedAt: event.capturedAt
|
|
29
|
-
};
|
|
30
|
-
if (event.rating.label) snap.label = event.rating.label;
|
|
31
|
-
return snap;
|
|
32
|
-
}
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/mcp/tools/delegate-feedback.ts
|
|
35
|
-
/** MCP tool name for the `delegate_feedback` feedback-recording tool. @stable */
|
|
36
|
-
const DELEGATE_FEEDBACK_TOOL_NAME = "delegate_feedback";
|
|
37
|
-
/** Human-readable description of the `delegate_feedback` MCP tool, injected into the tool manifest. @stable */
|
|
38
|
-
const DELEGATE_FEEDBACK_DESCRIPTION = [
|
|
39
|
-
"Record feedback on a delegation, artifact, or outcome. Synchronous — the",
|
|
40
|
-
"event is durably stored when this call returns.",
|
|
41
|
-
"",
|
|
42
|
-
"Use when: you (the agent), the user, or a downstream judge has formed an",
|
|
43
|
-
"opinion about a piece of work and want it persisted for calibration,",
|
|
44
|
-
"pricing, or future routing. Every call is a new event — multiple ratings",
|
|
45
|
-
"on the same target are expected and never deduped.",
|
|
46
|
-
"",
|
|
47
|
-
"`refersTo.kind`:",
|
|
48
|
-
" - \"delegation\": ref is a taskId returned by delegate_ui_audit",
|
|
49
|
-
" - \"artifact\": ref is a URI/path/git-sha — anything you can dereference",
|
|
50
|
-
" - \"outcome\": ref is a free-form description of a downstream result",
|
|
51
|
-
"",
|
|
52
|
-
"`by`:",
|
|
53
|
-
" - \"agent\": the agent itself rated the work",
|
|
54
|
-
" - \"user\": the human user rated it",
|
|
55
|
-
" - \"downstream-judge\": an automated evaluator emitted the rating",
|
|
56
|
-
"",
|
|
57
|
-
"When ref names a known taskId, the rating is also attached to the",
|
|
58
|
-
"delegation record so delegation_history surfaces it inline."
|
|
59
|
-
].join("\n");
|
|
60
|
-
/** JSON Schema for `delegate_feedback` tool arguments (`refersTo`, `rating`, `by`, optional fields). @stable */
|
|
61
|
-
const DELEGATE_FEEDBACK_INPUT_SCHEMA = {
|
|
62
|
-
type: "object",
|
|
63
|
-
properties: {
|
|
64
|
-
refersTo: {
|
|
65
|
-
type: "object",
|
|
66
|
-
properties: {
|
|
67
|
-
kind: {
|
|
68
|
-
type: "string",
|
|
69
|
-
enum: [
|
|
70
|
-
"delegation",
|
|
71
|
-
"artifact",
|
|
72
|
-
"outcome"
|
|
73
|
-
]
|
|
74
|
-
},
|
|
75
|
-
ref: { type: "string" }
|
|
76
|
-
},
|
|
77
|
-
required: ["kind", "ref"],
|
|
78
|
-
additionalProperties: false
|
|
79
|
-
},
|
|
80
|
-
rating: {
|
|
81
|
-
type: "object",
|
|
82
|
-
properties: {
|
|
83
|
-
score: {
|
|
84
|
-
type: "number",
|
|
85
|
-
minimum: 0,
|
|
86
|
-
maximum: 1
|
|
87
|
-
},
|
|
88
|
-
label: {
|
|
89
|
-
type: "string",
|
|
90
|
-
enum: [
|
|
91
|
-
"good",
|
|
92
|
-
"bad",
|
|
93
|
-
"neutral",
|
|
94
|
-
"mixed"
|
|
95
|
-
]
|
|
96
|
-
},
|
|
97
|
-
notes: { type: "string" }
|
|
98
|
-
},
|
|
99
|
-
required: ["score", "notes"],
|
|
100
|
-
additionalProperties: false
|
|
101
|
-
},
|
|
102
|
-
by: {
|
|
103
|
-
type: "string",
|
|
104
|
-
enum: [
|
|
105
|
-
"agent",
|
|
106
|
-
"user",
|
|
107
|
-
"downstream-judge"
|
|
108
|
-
]
|
|
109
|
-
},
|
|
110
|
-
capturedAt: { type: "string" },
|
|
111
|
-
namespace: { type: "string" }
|
|
112
|
-
},
|
|
113
|
-
required: [
|
|
114
|
-
"refersTo",
|
|
115
|
-
"rating",
|
|
116
|
-
"by"
|
|
117
|
-
],
|
|
118
|
-
additionalProperties: false
|
|
119
|
-
};
|
|
120
|
-
/** Parse and validate raw MCP tool input into typed `DelegateFeedbackArgs`; throws `TypeError` on bad input. @stable */
|
|
121
|
-
function validateDelegateFeedbackArgs(raw) {
|
|
122
|
-
if (raw === null || typeof raw !== "object") throw new TypeError("delegate_feedback: arguments must be an object");
|
|
123
|
-
const value = raw;
|
|
124
|
-
const refersTo = validateRefersTo(value.refersTo);
|
|
125
|
-
const rating = validateRating(value.rating);
|
|
126
|
-
const by = value.by;
|
|
127
|
-
if (by !== "agent" && by !== "user" && by !== "downstream-judge") throw new TypeError("delegate_feedback: `by` must be one of \"agent\" | \"user\" | \"downstream-judge\"");
|
|
128
|
-
const args = {
|
|
129
|
-
refersTo,
|
|
130
|
-
rating,
|
|
131
|
-
by
|
|
132
|
-
};
|
|
133
|
-
if (value.capturedAt !== void 0) {
|
|
134
|
-
if (typeof value.capturedAt !== "string" || Number.isNaN(Date.parse(value.capturedAt))) throw new TypeError("delegate_feedback: `capturedAt` must be an ISO datetime");
|
|
135
|
-
args.capturedAt = value.capturedAt;
|
|
136
|
-
}
|
|
137
|
-
if (typeof value.namespace === "string") args.namespace = value.namespace;
|
|
138
|
-
return args;
|
|
139
|
-
}
|
|
140
|
-
function validateRefersTo(raw) {
|
|
141
|
-
if (raw === null || typeof raw !== "object") throw new TypeError("delegate_feedback: `refersTo` must be an object");
|
|
142
|
-
const value = raw;
|
|
143
|
-
const kind = value.kind;
|
|
144
|
-
if (kind !== "delegation" && kind !== "artifact" && kind !== "outcome") throw new TypeError("delegate_feedback: `refersTo.kind` must be one of \"delegation\" | \"artifact\" | \"outcome\"");
|
|
145
|
-
const ref = value.ref;
|
|
146
|
-
if (typeof ref !== "string" || ref.trim().length === 0) throw new TypeError("delegate_feedback: `refersTo.ref` must be a non-empty string");
|
|
147
|
-
return {
|
|
148
|
-
kind,
|
|
149
|
-
ref: ref.trim()
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function validateRating(raw) {
|
|
153
|
-
if (raw === null || typeof raw !== "object") throw new TypeError("delegate_feedback: `rating` must be an object");
|
|
154
|
-
const value = raw;
|
|
155
|
-
const score = Number(value.score);
|
|
156
|
-
if (!Number.isFinite(score) || score < 0 || score > 1) throw new RangeError("delegate_feedback: `rating.score` must be a number in [0, 1]");
|
|
157
|
-
const notes = value.notes;
|
|
158
|
-
if (typeof notes !== "string") throw new TypeError("delegate_feedback: `rating.notes` must be a string");
|
|
159
|
-
const rating = {
|
|
160
|
-
score,
|
|
161
|
-
notes
|
|
162
|
-
};
|
|
163
|
-
const label = value.label;
|
|
164
|
-
if (label !== void 0) {
|
|
165
|
-
if (label !== "good" && label !== "bad" && label !== "neutral" && label !== "mixed") throw new TypeError("delegate_feedback: `rating.label` must be one of \"good\" | \"bad\" | \"neutral\" | \"mixed\"");
|
|
166
|
-
rating.label = label;
|
|
167
|
-
}
|
|
168
|
-
return rating;
|
|
169
|
-
}
|
|
170
|
-
/** Build the MCP tool handler that persists feedback events and attaches them to delegation records. @stable */
|
|
171
|
-
function createDelegateFeedbackHandler(options) {
|
|
172
|
-
const generateId = options.generateId ?? randomFeedbackId;
|
|
173
|
-
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
174
|
-
return async (raw) => {
|
|
175
|
-
const args = validateDelegateFeedbackArgs(raw);
|
|
176
|
-
const id = generateId();
|
|
177
|
-
const event = {
|
|
178
|
-
id,
|
|
179
|
-
refersTo: args.refersTo,
|
|
180
|
-
rating: args.rating,
|
|
181
|
-
by: args.by,
|
|
182
|
-
capturedAt: args.capturedAt ?? now(),
|
|
183
|
-
namespace: args.namespace
|
|
184
|
-
};
|
|
185
|
-
await options.store.put(event);
|
|
186
|
-
if (args.refersTo.kind === "delegation") options.queue.attachFeedback(args.refersTo.ref, eventToSnapshot(event));
|
|
187
|
-
return {
|
|
188
|
-
recorded: true,
|
|
189
|
-
id
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
function randomFeedbackId() {
|
|
194
|
-
return `fbk-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
195
|
-
}
|
|
196
|
-
//#endregion
|
|
197
|
-
//#region src/mcp/types.ts
|
|
198
|
-
/**
|
|
199
|
-
* Every delegation profile a queued record can carry. One owner: the tool schemas and validators
|
|
200
|
-
* that filter on a profile read this list, so a profile added here cannot be one a tool refuses.
|
|
201
|
-
* @experimental
|
|
202
|
-
*/
|
|
203
|
-
const delegationProfiles = [
|
|
204
|
-
"coder",
|
|
205
|
-
"researcher",
|
|
206
|
-
"ui-auditor"
|
|
207
|
-
];
|
|
208
|
-
//#endregion
|
|
209
|
-
//#region src/mcp/tools/delegation-history.ts
|
|
210
|
-
/** MCP tool name for the `delegation_history` read-past-delegations tool. @stable */
|
|
211
|
-
const DELEGATION_HISTORY_TOOL_NAME = "delegation_history";
|
|
212
|
-
/** Human-readable description of the `delegation_history` MCP tool, injected into the tool manifest. @stable */
|
|
213
|
-
const DELEGATION_HISTORY_DESCRIPTION = [
|
|
214
|
-
"Read past delegations newest-first. Each entry carries the original",
|
|
215
|
-
"arguments, current status, cost, and any feedback attached via",
|
|
216
|
-
"delegate_feedback.",
|
|
217
|
-
"",
|
|
218
|
-
"Use when: you want to introspect prior decisions — \"have I asked this",
|
|
219
|
-
"question before?",
|
|
220
|
-
"did the last patch land?",
|
|
221
|
-
"what's the historical",
|
|
222
|
-
"success rate of coder delegations on this repo?\". Feed the results back",
|
|
223
|
-
"into your own routing and calibration.",
|
|
224
|
-
"",
|
|
225
|
-
"Each entry carries `hasTrace` — when true, the full loop-trace span tree",
|
|
226
|
-
"is retrievable via delegation_status { taskId, includeTrace: true }.",
|
|
227
|
-
"",
|
|
228
|
-
`Filters: \`namespace\` (multi-tenant scope), \`profile\` (${delegationProfiles.map((profile) => `"${profile}"`).join(" | ")}),`,
|
|
229
|
-
"`since` (ISO date — only delegations started at-or-after). `limit` defaults",
|
|
230
|
-
"to 50, capped at 500."
|
|
231
|
-
].join("\n");
|
|
232
|
-
/** JSON Schema for `delegation_history` tool arguments (optional `namespace`, `profile`, `since`, `limit`). @stable */
|
|
233
|
-
const DELEGATION_HISTORY_INPUT_SCHEMA = {
|
|
234
|
-
type: "object",
|
|
235
|
-
properties: {
|
|
236
|
-
namespace: { type: "string" },
|
|
237
|
-
profile: {
|
|
238
|
-
type: "string",
|
|
239
|
-
enum: delegationProfiles
|
|
240
|
-
},
|
|
241
|
-
since: {
|
|
242
|
-
type: "string",
|
|
243
|
-
description: "ISO datetime — earliest startedAt to include."
|
|
244
|
-
},
|
|
245
|
-
limit: {
|
|
246
|
-
type: "integer",
|
|
247
|
-
minimum: 1,
|
|
248
|
-
maximum: 500
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
additionalProperties: false
|
|
252
|
-
};
|
|
253
|
-
/** Parse and validate raw MCP tool input into typed `DelegationHistoryArgs`; throws `TypeError` on bad input. @stable */
|
|
254
|
-
function validateDelegationHistoryArgs(raw) {
|
|
255
|
-
if (raw === void 0 || raw === null) return {};
|
|
256
|
-
if (typeof raw !== "object") throw new TypeError("delegation_history: arguments must be an object");
|
|
257
|
-
const value = raw;
|
|
258
|
-
const out = {};
|
|
259
|
-
if (value.namespace !== void 0) {
|
|
260
|
-
if (typeof value.namespace !== "string") throw new TypeError("delegation_history: `namespace` must be a string");
|
|
261
|
-
out.namespace = value.namespace;
|
|
262
|
-
}
|
|
263
|
-
if (value.profile !== void 0) {
|
|
264
|
-
if (!delegationProfiles.includes(value.profile)) throw new TypeError(`delegation_history: \`profile\` must be one of ${delegationProfiles.join(", ")}`);
|
|
265
|
-
out.profile = value.profile;
|
|
266
|
-
}
|
|
267
|
-
if (value.since !== void 0) {
|
|
268
|
-
if (typeof value.since !== "string" || Number.isNaN(Date.parse(value.since))) throw new TypeError("delegation_history: `since` must be an ISO datetime");
|
|
269
|
-
out.since = value.since;
|
|
270
|
-
}
|
|
271
|
-
if (value.limit !== void 0) {
|
|
272
|
-
const n = Number(value.limit);
|
|
273
|
-
if (!Number.isFinite(n) || n < 1 || n > 500) throw new RangeError("delegation_history: `limit` must be an integer in [1, 500]");
|
|
274
|
-
out.limit = Math.trunc(n);
|
|
275
|
-
}
|
|
276
|
-
return out;
|
|
277
|
-
}
|
|
278
|
-
/** Build the MCP tool handler that reads filtered past delegations from a `DelegationTaskQueue`. @stable */
|
|
279
|
-
function createDelegationHistoryHandler(options) {
|
|
280
|
-
return async (raw) => {
|
|
281
|
-
const args = validateDelegationHistoryArgs(raw);
|
|
282
|
-
return { delegations: options.queue.history(args) };
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
//#endregion
|
|
286
|
-
//#region src/mcp/tools/delegation-status.ts
|
|
287
|
-
/**
|
|
288
|
-
*
|
|
289
|
-
* `delegation_status` MCP tool — synchronous poll. Returns the current
|
|
290
|
-
* state machine + optional progress + final result (when terminal).
|
|
291
|
-
*
|
|
292
|
-
* @stable
|
|
293
|
-
*/
|
|
294
|
-
/** MCP tool name for the `delegation_status` synchronous-poll tool. @stable */
|
|
295
|
-
const DELEGATION_STATUS_TOOL_NAME = "delegation_status";
|
|
296
|
-
/** Human-readable description of the `delegation_status` MCP tool, injected into the tool manifest. @stable */
|
|
297
|
-
const DELEGATION_STATUS_DESCRIPTION = [
|
|
298
|
-
"Poll the status of an async delegation. Returns the current state",
|
|
299
|
-
"(pending | running | completed | failed | cancelled), optional progress,",
|
|
300
|
-
"and the final result when status === \"completed\".",
|
|
301
|
-
"",
|
|
302
|
-
"Use when: you previously kicked off an async delegation (delegate_ui_audit)",
|
|
303
|
-
"and need to know whether the work is done. The agent's right rhythm is to",
|
|
304
|
-
"call this every minute or two while waiting; do not busy-poll.",
|
|
305
|
-
"",
|
|
306
|
-
"For a completed delegate_ui_audit run, `result.output` is the array of UI",
|
|
307
|
-
"findings — one self-contained Markdown finding per issue, each with an",
|
|
308
|
-
"embedded screenshot and a suggested fix.",
|
|
309
|
-
"",
|
|
310
|
-
"Pass includeTrace: true to also receive the journaled loop-trace span",
|
|
311
|
-
"tree (loop → round → iteration, with placement/cost/verdict metadata).",
|
|
312
|
-
"Default false — keep routine polls light.",
|
|
313
|
-
"",
|
|
314
|
-
"Throws NotFoundError when taskId is unknown — never silently returns",
|
|
315
|
-
"`pending` for a typo."
|
|
316
|
-
].join("\n");
|
|
317
|
-
/** JSON Schema for `delegation_status` tool arguments (`taskId` + optional `includeTrace`). @stable */
|
|
318
|
-
const DELEGATION_STATUS_INPUT_SCHEMA = {
|
|
319
|
-
type: "object",
|
|
320
|
-
properties: {
|
|
321
|
-
taskId: {
|
|
322
|
-
type: "string",
|
|
323
|
-
description: "Returned by delegate_ui_audit."
|
|
324
|
-
},
|
|
325
|
-
includeTrace: {
|
|
326
|
-
type: "boolean",
|
|
327
|
-
description: "Also return the journaled loop-trace span tree for this delegation. Default false."
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
required: ["taskId"],
|
|
331
|
-
additionalProperties: false
|
|
332
|
-
};
|
|
333
|
-
/** Parse and validate raw MCP tool input into typed `DelegationStatusArgs`; throws `TypeError` on bad input. @stable */
|
|
334
|
-
function validateDelegationStatusArgs(raw) {
|
|
335
|
-
if (raw === null || typeof raw !== "object") throw new TypeError("delegation_status: arguments must be an object");
|
|
336
|
-
const value = raw;
|
|
337
|
-
const taskId = value.taskId;
|
|
338
|
-
if (typeof taskId !== "string" || taskId.trim().length === 0) throw new TypeError("delegation_status: `taskId` must be a non-empty string");
|
|
339
|
-
const out = { taskId: taskId.trim() };
|
|
340
|
-
if (value.includeTrace !== void 0) {
|
|
341
|
-
if (typeof value.includeTrace !== "boolean") throw new TypeError("delegation_status: `includeTrace` must be a boolean");
|
|
342
|
-
out.includeTrace = value.includeTrace;
|
|
343
|
-
}
|
|
344
|
-
return out;
|
|
345
|
-
}
|
|
346
|
-
/** Build the MCP tool handler that polls a `DelegationTaskQueue` for task status. @stable */
|
|
347
|
-
function createDelegationStatusHandler(options) {
|
|
348
|
-
return async (raw) => {
|
|
349
|
-
const args = validateDelegationStatusArgs(raw);
|
|
350
|
-
const status = options.queue.status(args.taskId, args.includeTrace !== void 0 ? { includeTrace: args.includeTrace } : void 0);
|
|
351
|
-
if (!status) throw new NotFoundError(`delegation_status: unknown taskId "${args.taskId}"`);
|
|
352
|
-
return status;
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
//#endregion
|
|
356
|
-
export { InMemoryFeedbackStore as _, validateDelegationStatusArgs as a, DELEGATION_HISTORY_TOOL_NAME as c, delegationProfiles as d, DELEGATE_FEEDBACK_DESCRIPTION as f, validateDelegateFeedbackArgs as g, createDelegateFeedbackHandler as h, createDelegationStatusHandler as i, createDelegationHistoryHandler as l, DELEGATE_FEEDBACK_TOOL_NAME as m, DELEGATION_STATUS_INPUT_SCHEMA as n, DELEGATION_HISTORY_DESCRIPTION as o, DELEGATE_FEEDBACK_INPUT_SCHEMA as p, DELEGATION_STATUS_TOOL_NAME as r, DELEGATION_HISTORY_INPUT_SCHEMA as s, DELEGATION_STATUS_DESCRIPTION as t, validateDelegationHistoryArgs as u, eventToSnapshot as v };
|
|
357
|
-
|
|
358
|
-
//# sourceMappingURL=delegation-status-CF4D0NZ_.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delegation-status-CF4D0NZ_.js","names":[],"sources":["../src/mcp/feedback-store.ts","../src/mcp/tools/delegate-feedback.ts","../src/mcp/types.ts","../src/mcp/tools/delegation-history.ts","../src/mcp/tools/delegation-status.ts"],"sourcesContent":["/**\n *\n * Feedback persistence surface for the MCP layer.\n *\n * Feedback storage is product policy, so the MCP layer depends on this narrow\n * interface instead of choosing a knowledge store. The default implementation\n * is in-memory; consumers wire their own durable adapter via\n * `createMcpServer({ feedbackStore })`.\n *\n * Feedback events are append-only: every rating is a new event with a\n * fresh id, even when the same delegation is rated multiple times. The\n * caller decides how to roll up scores downstream.\n *\n * @stable\n */\n\nimport type { DelegateFeedbackArgs, DelegationFeedbackSnapshot } from './types'\n\n/** @stable */\nexport interface FeedbackEvent {\n id: string\n refersTo: DelegateFeedbackArgs['refersTo']\n rating: DelegateFeedbackArgs['rating']\n by: DelegateFeedbackArgs['by']\n capturedAt: string\n namespace?: string\n}\n\n/** @stable */\nexport interface FeedbackStore {\n /** Append a new event. Never dedupes — every rating is its own event. */\n put(event: FeedbackEvent): Promise<void>\n /**\n * List events filtered by `namespace`. When `namespace` is omitted, list\n * across all namespaces. Returns events in insertion order.\n */\n list(filter?: { namespace?: string; refersToRef?: string }): Promise<FeedbackEvent[]>\n}\n\n/** In-memory `FeedbackStore` — suitable for single-process use and tests. @stable */\nexport class InMemoryFeedbackStore implements FeedbackStore {\n private readonly events: FeedbackEvent[] = []\n\n async put(event: FeedbackEvent): Promise<void> {\n this.events.push({ ...event })\n }\n\n async list(filter: { namespace?: string; refersToRef?: string } = {}): Promise<FeedbackEvent[]> {\n let out = this.events\n if (filter.namespace !== undefined) {\n out = out.filter((event) => event.namespace === filter.namespace)\n }\n if (filter.refersToRef !== undefined) {\n out = out.filter((event) => event.refersTo.ref === filter.refersToRef)\n }\n return out.map((event) => ({ ...event }))\n }\n}\n\n/**\n * Project a `FeedbackEvent` down to the snapshot shape carried on\n * `delegation_history` entries.\n *\n * @stable\n */\nexport function eventToSnapshot(event: FeedbackEvent): DelegationFeedbackSnapshot {\n const snap: DelegationFeedbackSnapshot = {\n id: event.id,\n score: event.rating.score,\n by: event.by,\n notes: event.rating.notes,\n capturedAt: event.capturedAt,\n }\n if (event.rating.label) snap.label = event.rating.label\n return snap\n}\n","/**\n *\n * `delegate_feedback` MCP tool — synchronous record of agent / user /\n * downstream-judge feedback on a delegation, artifact, or outcome.\n *\n * The store is append-only. Every rating is its own event; no dedupe.\n * When `refersTo.kind === 'delegation'`, the snapshot is also attached\n * to the matching queue record so `delegation_history` surfaces it\n * inline without a join.\n *\n * @stable\n */\n\nimport type { FeedbackStore } from '../feedback-store'\nimport { eventToSnapshot } from '../feedback-store'\nimport type { DelegationTaskQueue } from '../task-queue'\nimport type {\n DelegateFeedbackArgs,\n DelegateFeedbackResult,\n FeedbackRating,\n FeedbackRefersTo,\n} from '../types'\n\n/** MCP tool name for the `delegate_feedback` feedback-recording tool. @stable */\nexport const DELEGATE_FEEDBACK_TOOL_NAME = 'delegate_feedback'\n\n/** Human-readable description of the `delegate_feedback` MCP tool, injected into the tool manifest. @stable */\nexport const DELEGATE_FEEDBACK_DESCRIPTION = [\n 'Record feedback on a delegation, artifact, or outcome. Synchronous — the',\n 'event is durably stored when this call returns.',\n '',\n 'Use when: you (the agent), the user, or a downstream judge has formed an',\n 'opinion about a piece of work and want it persisted for calibration,',\n 'pricing, or future routing. Every call is a new event — multiple ratings',\n 'on the same target are expected and never deduped.',\n '',\n '`refersTo.kind`:',\n ' - \"delegation\": ref is a taskId returned by delegate_ui_audit',\n ' - \"artifact\": ref is a URI/path/git-sha — anything you can dereference',\n ' - \"outcome\": ref is a free-form description of a downstream result',\n '',\n '`by`:',\n ' - \"agent\": the agent itself rated the work',\n ' - \"user\": the human user rated it',\n ' - \"downstream-judge\": an automated evaluator emitted the rating',\n '',\n 'When ref names a known taskId, the rating is also attached to the',\n 'delegation record so delegation_history surfaces it inline.',\n].join('\\n')\n\n/** JSON Schema for `delegate_feedback` tool arguments (`refersTo`, `rating`, `by`, optional fields). @stable */\nexport const DELEGATE_FEEDBACK_INPUT_SCHEMA = {\n type: 'object',\n properties: {\n refersTo: {\n type: 'object',\n properties: {\n kind: { type: 'string', enum: ['delegation', 'artifact', 'outcome'] },\n ref: { type: 'string' },\n },\n required: ['kind', 'ref'],\n additionalProperties: false,\n },\n rating: {\n type: 'object',\n properties: {\n score: { type: 'number', minimum: 0, maximum: 1 },\n label: { type: 'string', enum: ['good', 'bad', 'neutral', 'mixed'] },\n notes: { type: 'string' },\n },\n required: ['score', 'notes'],\n additionalProperties: false,\n },\n by: { type: 'string', enum: ['agent', 'user', 'downstream-judge'] },\n capturedAt: { type: 'string' },\n namespace: { type: 'string' },\n },\n required: ['refersTo', 'rating', 'by'],\n additionalProperties: false,\n} as const\n\n/** Parse and validate raw MCP tool input into typed `DelegateFeedbackArgs`; throws `TypeError` on bad input. @stable */\nexport function validateDelegateFeedbackArgs(raw: unknown): DelegateFeedbackArgs {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError('delegate_feedback: arguments must be an object')\n }\n const value = raw as Record<string, unknown>\n const refersTo = validateRefersTo(value.refersTo)\n const rating = validateRating(value.rating)\n const by = value.by\n if (by !== 'agent' && by !== 'user' && by !== 'downstream-judge') {\n throw new TypeError(\n 'delegate_feedback: `by` must be one of \"agent\" | \"user\" | \"downstream-judge\"',\n )\n }\n const args: DelegateFeedbackArgs = { refersTo, rating, by }\n if (value.capturedAt !== undefined) {\n if (typeof value.capturedAt !== 'string' || Number.isNaN(Date.parse(value.capturedAt))) {\n throw new TypeError('delegate_feedback: `capturedAt` must be an ISO datetime')\n }\n args.capturedAt = value.capturedAt\n }\n if (typeof value.namespace === 'string') args.namespace = value.namespace\n return args\n}\n\nfunction validateRefersTo(raw: unknown): FeedbackRefersTo {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError('delegate_feedback: `refersTo` must be an object')\n }\n const value = raw as Record<string, unknown>\n const kind = value.kind\n if (kind !== 'delegation' && kind !== 'artifact' && kind !== 'outcome') {\n throw new TypeError(\n 'delegate_feedback: `refersTo.kind` must be one of \"delegation\" | \"artifact\" | \"outcome\"',\n )\n }\n const ref = value.ref\n if (typeof ref !== 'string' || ref.trim().length === 0) {\n throw new TypeError('delegate_feedback: `refersTo.ref` must be a non-empty string')\n }\n return { kind, ref: ref.trim() }\n}\n\nfunction validateRating(raw: unknown): FeedbackRating {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError('delegate_feedback: `rating` must be an object')\n }\n const value = raw as Record<string, unknown>\n const score = Number(value.score)\n if (!Number.isFinite(score) || score < 0 || score > 1) {\n throw new RangeError('delegate_feedback: `rating.score` must be a number in [0, 1]')\n }\n const notes = value.notes\n if (typeof notes !== 'string') {\n throw new TypeError('delegate_feedback: `rating.notes` must be a string')\n }\n const rating: FeedbackRating = { score, notes }\n const label = value.label\n if (label !== undefined) {\n if (label !== 'good' && label !== 'bad' && label !== 'neutral' && label !== 'mixed') {\n throw new TypeError(\n 'delegate_feedback: `rating.label` must be one of \"good\" | \"bad\" | \"neutral\" | \"mixed\"',\n )\n }\n rating.label = label\n }\n return rating\n}\n\n/** @stable */\nexport interface DelegateFeedbackHandlerOptions {\n queue: DelegationTaskQueue\n store: FeedbackStore\n generateId?: () => string\n now?: () => string\n}\n\n/** Build the MCP tool handler that persists feedback events and attaches them to delegation records. @stable */\nexport function createDelegateFeedbackHandler(\n options: DelegateFeedbackHandlerOptions,\n): (raw: unknown) => Promise<DelegateFeedbackResult> {\n const generateId = options.generateId ?? randomFeedbackId\n const now = options.now ?? (() => new Date().toISOString())\n return async (raw) => {\n const args = validateDelegateFeedbackArgs(raw)\n const id = generateId()\n const event = {\n id,\n refersTo: args.refersTo,\n rating: args.rating,\n by: args.by,\n capturedAt: args.capturedAt ?? now(),\n namespace: args.namespace,\n }\n await options.store.put(event)\n if (args.refersTo.kind === 'delegation') {\n options.queue.attachFeedback(args.refersTo.ref, eventToSnapshot(event))\n }\n return { recorded: true, id }\n }\n}\n\nfunction randomFeedbackId(): string {\n const t = Date.now().toString(36)\n const r = Math.random().toString(36).slice(2, 10)\n return `fbk-${t}-${r}`\n}\n","/**\n *\n * MCP delegation tool surface — the typed inputs/outputs the product agent\n * sees over the wire. These types are the contract; the JSON schemas under\n * `tools/*` mirror them for the MCP `tools/list` advertisement.\n *\n * Async semantics: `delegate_ui_audit` returns a `taskId` immediately. The\n * product agent polls `delegation_status` until the task transitions to\n * `completed` | `failed` | `cancelled`. `delegate_feedback` +\n * `delegation_history` are synchronous reads / writes against the local\n * task queue + feedback store.\n *\n * @experimental\n */\n\nimport type { CoderTask } from '../profiles/coder'\nimport type { ResearchSource } from '../profiles/researcher'\nimport type { UiFinding, UiLens } from '../profiles/ui-auditor/substrate'\nimport type { DelegationTraceSpan } from './delegation-trace'\nimport type { CoderOutput } from './detached-coder'\n\n/**\n * Every delegation profile a queued record can carry. One owner: the tool schemas and validators\n * that filter on a profile read this list, so a profile added here cannot be one a tool refuses.\n * @experimental\n */\nexport const delegationProfiles = ['coder', 'researcher', 'ui-auditor'] as const\n\n/** @experimental */\nexport type DelegationProfile = (typeof delegationProfiles)[number]\n\n/** @experimental */\nexport type DelegationStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n\n/**\n * Minimal `CoderTask` overrides exposed over the MCP wire. The full\n * `CoderTask` carries fields the kernel synthesizes from `goal` +\n * `repoRoot` — the agent only edits the few that materially gate\n * validator behavior.\n *\n * @experimental\n */\nexport interface DelegateCodeConfig {\n testCmd?: string\n typecheckCmd?: string\n forbiddenPaths?: string[]\n maxDiffLines?: number\n}\n\n/** @experimental */\nexport interface DelegateCodeArgs {\n /** Natural-language description of what the coder must accomplish. */\n goal: string\n /** Absolute path inside the sandbox where the repo lives. */\n repoRoot: string\n /** Optional free-form context the agent surfaces in the prompt prelude. */\n contextHint?: string\n /**\n * When > 1, dispatches `multiHarnessCoderFanout` across the delegate's configured exact profiles\n * and picks the highest-scoring passing patch. Default 1.\n */\n variants?: number\n /** Validator + prompt overrides the agent knows for this repo. */\n config?: DelegateCodeConfig\n /** Multi-tenant scope (customer-id, workspace-id). */\n namespace?: string\n}\n\n/** @experimental */\nexport interface DelegateCodeResult {\n taskId: string\n /** Best-effort hint — coder loops can take minutes-to-hours. */\n estimatedDurationMs?: number\n}\n\nexport type { ResearchSource }\n\n/** @experimental */\nexport interface DelegateResearchConfig {\n recencyWindow?: { since?: string; until?: string }\n maxItems?: number\n minConfidence?: number\n}\n\n/** @experimental */\nexport interface DelegateResearchArgs {\n question: string\n namespace: string\n scope?: string\n sources?: ResearchSource[]\n variants?: number\n config?: DelegateResearchConfig\n}\n\n/** @experimental */\nexport interface DelegateResearchResult {\n taskId: string\n estimatedDurationMs?: number\n}\n\n/** @experimental */\nexport interface FeedbackRefersTo {\n kind: 'delegation' | 'artifact' | 'outcome'\n /** For `'delegation'`, this is the taskId. */\n ref: string\n}\n\n/** @experimental */\nexport interface FeedbackRating {\n /** [0, 1]. */\n score: number\n label?: 'good' | 'bad' | 'neutral' | 'mixed'\n notes: string\n}\n\n/** @experimental */\nexport interface DelegateFeedbackArgs {\n refersTo: FeedbackRefersTo\n rating: FeedbackRating\n by: 'agent' | 'user' | 'downstream-judge'\n /** ISO timestamp; defaults to server clock when omitted. */\n capturedAt?: string\n namespace?: string\n}\n\n/** @experimental */\nexport interface DelegateFeedbackResult {\n recorded: true\n id: string\n}\n\n/** @experimental */\nexport interface DelegationStatusArgs {\n taskId: string\n /**\n * Return the delegation's compact loop-trace span tree alongside the\n * status. Default false — status polls stay light; opt in when you need\n * the topology (which iterations ran, where they were placed, what each\n * cost) rather than just the state machine.\n */\n includeTrace?: boolean\n}\n\n/** @experimental */\nexport interface DelegationProgress {\n iteration: number\n phase: string\n}\n\n/** @experimental */\nexport interface DelegationError {\n message: string\n kind: string\n}\n\n/**\n * Polymorphic `result` field: `CoderOutput` when the underlying profile\n * is `'coder'`, a structurally-typed research output when `'researcher'`.\n * The MCP wire carries it as JSON either way.\n *\n * @experimental\n */\nexport type DelegationResultPayload =\n | { profile: 'coder'; output: CoderOutput }\n | { profile: 'researcher'; output: ResearchOutputShape }\n | { profile: 'ui-auditor'; output: UiAuditorDelegationOutput }\n\n/**\n * Wire-shape of a completed UI-audit delegation. The `findings` array\n * contains every finding persisted to the workspace during the run,\n * already enriched with `id` and `createdAt` by the writer. `workspaceDir`\n * is the absolute path to the workspace; `indexFile` is the workspace-\n * relative path to the regenerated index.md.\n *\n * @experimental\n */\nexport interface UiAuditorDelegationOutput {\n workspaceDir: string\n indexFile: string\n findings: UiFinding[]\n /** Total iterations the loop ran for this delegation. */\n iterations: number\n}\n\n/** @experimental */\nexport type UiAuditLensFilter = readonly UiLens[]\n\n/** Optional per-route capture spec the agent surfaces over the wire. */\nexport interface DelegateUiAuditRoute {\n /** Stable route name (used in screenshot filenames + finding metadata). */\n name: string\n /** Fully-qualified URL. */\n url: string\n /** Viewports to capture at. Defaults to `[{ width: 1280, height: 800 }]`. */\n viewports?: readonly { width: number; height: number }[]\n /** Default false. Full-page captures for the broad lenses. */\n fullPage?: boolean\n /** Selector to wait for before capture. */\n waitFor?: string\n}\n\n/** @experimental */\nexport interface DelegateUiAuditConfig {\n /**\n * Lenses to iterate. Default: every lens except `'other'`. Order is\n * preserved — the driver iterates lens-by-lens.\n */\n lenses?: UiAuditLensFilter\n /** Maximum total iterations across all (lens × route) pairs. Default 33 (11 lenses × 3 routes). */\n maxIterations?: number\n /** Maximum concurrent iterations within a single plan() round. Default 2. */\n maxConcurrency?: number\n /** Free-form product context surfaced to the judge. */\n productContext?: string\n}\n\n/** @experimental */\nexport interface DelegateUiAuditArgs {\n /** Workspace root for the audit (absolute path). */\n workspaceDir: string\n /** Routes to audit. Must be non-empty. */\n routes: readonly DelegateUiAuditRoute[]\n /** Multi-tenant scope. */\n namespace?: string\n config?: DelegateUiAuditConfig\n}\n\n/** @experimental */\nexport interface DelegateUiAuditResult {\n taskId: string\n estimatedDurationMs?: number\n}\n\n/**\n * Provider-neutral research output carried over the MCP boundary. The MCP\n * layer accepts this structural shape instead of coupling its wire contract to\n * one research implementation.\n *\n * @experimental\n */\nexport interface ResearchOutputShape {\n items: unknown[]\n citations: unknown[]\n proposedWrites: unknown[]\n gaps?: string[]\n notes?: string\n [key: string]: unknown\n}\n\n/** @experimental */\nexport interface DelegationStatusResult {\n taskId: string\n profile: DelegationProfile\n status: DelegationStatus\n progress?: DelegationProgress\n result?: DelegationResultPayload\n error?: DelegationError\n costUsd?: number\n startedAt: string\n completedAt?: string\n /** Compact loop-trace span tree; present only when `includeTrace: true` was passed and spans were recorded. */\n trace?: DelegationTraceSpan[]\n /** Present when oldest trace spans were dropped to honor the trace caps. */\n traceTruncated?: true\n /** Inherited trace identity recorded at submit — join key into the caller's trace. */\n traceId?: string\n /** Caller span that dispatched the delegation, when one was inherited. */\n parentSpanId?: string\n}\n\n/** @experimental */\nexport interface DelegationHistoryArgs {\n namespace?: string\n profile?: DelegationProfile\n /** ISO date — only delegations started at-or-after `since` are returned. */\n since?: string\n /** Default 50. Hard cap 500. */\n limit?: number\n}\n\n/** @experimental */\nexport interface DelegationFeedbackSnapshot {\n id: string\n score: number\n label?: FeedbackRating['label']\n by: DelegateFeedbackArgs['by']\n notes: string\n capturedAt: string\n}\n\n/** @experimental */\nexport interface DelegationHistoryEntry {\n taskId: string\n profile: DelegationProfile\n namespace?: string\n args: DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs\n status: DelegationStatus\n feedback?: DelegationFeedbackSnapshot[]\n costUsd?: number\n startedAt: string\n completedAt?: string\n /**\n * True when the record carries a journaled loop trace. History stays\n * light by design — fetch the spans via\n * `delegation_status { taskId, includeTrace: true }`.\n */\n hasTrace: boolean\n /** Inherited trace identity recorded at submit — join key into the caller's trace. */\n traceId?: string\n}\n\n/** @experimental */\nexport interface DelegationHistoryResult {\n delegations: DelegationHistoryEntry[]\n}\n\n/**\n * Re-export `CoderTask` so callers wiring custom delegates don't have to\n * import from `./profiles` themselves.\n *\n * @experimental\n */\nexport type { CoderOutput, CoderTask }\n","/**\n *\n * `delegation_history` MCP tool — synchronous read of past delegations.\n * The agent uses this for self-introspection — \"have I delegated this\n * kind of task before? did it work?\" — and calibration.\n *\n * @stable\n */\n\nimport type {\n DelegationHistoryArgs,\n DelegationHistoryResult,\n DelegationProfile,\n DelegationTaskQueue,\n} from '../task-queue'\nimport { delegationProfiles } from '../types'\n\n/** MCP tool name for the `delegation_history` read-past-delegations tool. @stable */\nexport const DELEGATION_HISTORY_TOOL_NAME = 'delegation_history'\n\n/** Human-readable description of the `delegation_history` MCP tool, injected into the tool manifest. @stable */\nexport const DELEGATION_HISTORY_DESCRIPTION = [\n 'Read past delegations newest-first. Each entry carries the original',\n 'arguments, current status, cost, and any feedback attached via',\n 'delegate_feedback.',\n '',\n 'Use when: you want to introspect prior decisions — \"have I asked this',\n 'question before?',\n 'did the last patch land?',\n \"what's the historical\",\n 'success rate of coder delegations on this repo?\". Feed the results back',\n 'into your own routing and calibration.',\n '',\n 'Each entry carries `hasTrace` — when true, the full loop-trace span tree',\n 'is retrievable via delegation_status { taskId, includeTrace: true }.',\n '',\n `Filters: \\`namespace\\` (multi-tenant scope), \\`profile\\` (${delegationProfiles\n .map((profile) => `\"${profile}\"`)\n .join(' | ')}),`,\n '`since` (ISO date — only delegations started at-or-after). `limit` defaults',\n 'to 50, capped at 500.',\n].join('\\n')\n\n/** JSON Schema for `delegation_history` tool arguments (optional `namespace`, `profile`, `since`, `limit`). @stable */\nexport const DELEGATION_HISTORY_INPUT_SCHEMA = {\n type: 'object',\n properties: {\n namespace: { type: 'string' },\n profile: { type: 'string', enum: delegationProfiles },\n since: { type: 'string', description: 'ISO datetime — earliest startedAt to include.' },\n limit: { type: 'integer', minimum: 1, maximum: 500 },\n },\n additionalProperties: false,\n} as const\n\n/** Parse and validate raw MCP tool input into typed `DelegationHistoryArgs`; throws `TypeError` on bad input. @stable */\nexport function validateDelegationHistoryArgs(raw: unknown): DelegationHistoryArgs {\n if (raw === undefined || raw === null) return {}\n if (typeof raw !== 'object') {\n throw new TypeError('delegation_history: arguments must be an object')\n }\n const value = raw as Record<string, unknown>\n const out: DelegationHistoryArgs = {}\n if (value.namespace !== undefined) {\n if (typeof value.namespace !== 'string') {\n throw new TypeError('delegation_history: `namespace` must be a string')\n }\n out.namespace = value.namespace\n }\n if (value.profile !== undefined) {\n if (!delegationProfiles.includes(value.profile as DelegationProfile)) {\n throw new TypeError(\n `delegation_history: \\`profile\\` must be one of ${delegationProfiles.join(', ')}`,\n )\n }\n out.profile = value.profile as DelegationProfile\n }\n if (value.since !== undefined) {\n if (typeof value.since !== 'string' || Number.isNaN(Date.parse(value.since))) {\n throw new TypeError('delegation_history: `since` must be an ISO datetime')\n }\n out.since = value.since\n }\n if (value.limit !== undefined) {\n const n = Number(value.limit)\n if (!Number.isFinite(n) || n < 1 || n > 500) {\n throw new RangeError('delegation_history: `limit` must be an integer in [1, 500]')\n }\n out.limit = Math.trunc(n)\n }\n return out\n}\n\n/** @stable */\nexport interface DelegationHistoryHandlerOptions {\n queue: DelegationTaskQueue\n}\n\n/** Build the MCP tool handler that reads filtered past delegations from a `DelegationTaskQueue`. @stable */\nexport function createDelegationHistoryHandler(\n options: DelegationHistoryHandlerOptions,\n): (raw: unknown) => Promise<DelegationHistoryResult> {\n return async (raw) => {\n const args = validateDelegationHistoryArgs(raw)\n return { delegations: options.queue.history(args) }\n }\n}\n","/**\n *\n * `delegation_status` MCP tool — synchronous poll. Returns the current\n * state machine + optional progress + final result (when terminal).\n *\n * @stable\n */\n\nimport { NotFoundError } from '../../errors'\nimport type {\n DelegationStatusArgs,\n DelegationStatusResult,\n DelegationTaskQueue,\n} from '../task-queue'\n\n/** MCP tool name for the `delegation_status` synchronous-poll tool. @stable */\nexport const DELEGATION_STATUS_TOOL_NAME = 'delegation_status'\n\n/** Human-readable description of the `delegation_status` MCP tool, injected into the tool manifest. @stable */\nexport const DELEGATION_STATUS_DESCRIPTION = [\n 'Poll the status of an async delegation. Returns the current state',\n '(pending | running | completed | failed | cancelled), optional progress,',\n 'and the final result when status === \"completed\".',\n '',\n 'Use when: you previously kicked off an async delegation (delegate_ui_audit)',\n \"and need to know whether the work is done. The agent's right rhythm is to\",\n 'call this every minute or two while waiting; do not busy-poll.',\n '',\n 'For a completed delegate_ui_audit run, `result.output` is the array of UI',\n 'findings — one self-contained Markdown finding per issue, each with an',\n 'embedded screenshot and a suggested fix.',\n '',\n 'Pass includeTrace: true to also receive the journaled loop-trace span',\n 'tree (loop → round → iteration, with placement/cost/verdict metadata).',\n 'Default false — keep routine polls light.',\n '',\n 'Throws NotFoundError when taskId is unknown — never silently returns',\n '`pending` for a typo.',\n].join('\\n')\n\n/** JSON Schema for `delegation_status` tool arguments (`taskId` + optional `includeTrace`). @stable */\nexport const DELEGATION_STATUS_INPUT_SCHEMA = {\n type: 'object',\n properties: {\n taskId: { type: 'string', description: 'Returned by delegate_ui_audit.' },\n includeTrace: {\n type: 'boolean',\n description:\n 'Also return the journaled loop-trace span tree for this delegation. Default false.',\n },\n },\n required: ['taskId'],\n additionalProperties: false,\n} as const\n\n/** Parse and validate raw MCP tool input into typed `DelegationStatusArgs`; throws `TypeError` on bad input. @stable */\nexport function validateDelegationStatusArgs(raw: unknown): DelegationStatusArgs {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError('delegation_status: arguments must be an object')\n }\n const value = raw as Record<string, unknown>\n const taskId = value.taskId\n if (typeof taskId !== 'string' || taskId.trim().length === 0) {\n throw new TypeError('delegation_status: `taskId` must be a non-empty string')\n }\n const out: DelegationStatusArgs = { taskId: taskId.trim() }\n if (value.includeTrace !== undefined) {\n if (typeof value.includeTrace !== 'boolean') {\n throw new TypeError('delegation_status: `includeTrace` must be a boolean')\n }\n out.includeTrace = value.includeTrace\n }\n return out\n}\n\n/** @stable */\nexport interface DelegationStatusHandlerOptions {\n queue: DelegationTaskQueue\n}\n\n/** Build the MCP tool handler that polls a `DelegationTaskQueue` for task status. @stable */\nexport function createDelegationStatusHandler(\n options: DelegationStatusHandlerOptions,\n): (raw: unknown) => Promise<DelegationStatusResult> {\n return async (raw) => {\n const args = validateDelegationStatusArgs(raw)\n const status = options.queue.status(\n args.taskId,\n args.includeTrace !== undefined ? { includeTrace: args.includeTrace } : undefined,\n )\n if (!status) {\n throw new NotFoundError(`delegation_status: unknown taskId \"${args.taskId}\"`)\n }\n return status\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"durable-file-B_eJr6Mb.js","names":["fsConstants"],"sources":["../src/runtime/supervise/durable-file.ts"],"sourcesContent":["/**\n * Small filesystem durability primitives for Runtime-owned state.\n *\n * Callers must validate their path and parent directory before invoking these functions.\n * The exclusive writer protects a no-clobber publication race; the atomic writer replaces one\n * state file after its temporary contents reach stable storage.\n *\n * @internal\n */\n\nimport { randomUUID } from 'node:crypto'\nimport {\n closeSync,\n existsSync,\n fchmodSync,\n constants as fsConstants,\n fsyncSync,\n linkSync,\n lstatSync,\n openSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n writeSync,\n} from 'node:fs'\nimport { dirname, join, relative, resolve } from 'node:path'\n\nexport interface DurableFileOptions {\n readonly mode?: number\n}\n\n/** Publish a new file without replacing a concurrent winner. */\nexport function publishExclusiveDurableFile(\n filePath: string,\n content: string,\n options: DurableFileOptions = {},\n): boolean {\n const temporaryPath = `${filePath}.${randomUUID()}.tmp`\n let operationFailed = false\n let operationError: unknown\n let published = false\n try {\n writeExclusiveDurableFile(temporaryPath, content, options)\n try {\n linkSync(temporaryPath, filePath)\n syncDurableDirectory(dirname(filePath))\n published = true\n } catch (error) {\n if (!isAlreadyExistsError(error)) throw error\n }\n } catch (error) {\n operationFailed = true\n operationError = error\n }\n let cleanupError: unknown\n try {\n unlinkSync(temporaryPath)\n } catch (error) {\n if (!isNoEntryError(error)) cleanupError = error\n }\n if (operationFailed) throw operationError\n if (cleanupError !== undefined) throw cleanupError\n return published\n}\n\n/** Fsync a directory after publishing a durable file entry. */\nexport function syncDurableDirectory(directoryPath: string): void {\n const fd = openSync(directoryPath, 'r')\n try {\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n}\n\n/** Fsync one durable file after its contents reach stable storage. */\nexport function syncDurableFile(filePath: string, exactMode?: number): void {\n const fd = openSync(filePath, 'r')\n try {\n if (exactMode !== undefined && process.platform !== 'win32') fchmodSync(fd, exactMode)\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n}\n\n/** Append one record with O_APPEND, then fsync the file and containing directory. */\nexport function appendDurableFile(\n filePath: string,\n content: string,\n options: DurableFileOptions = {},\n): void {\n const flags =\n fsConstants.O_APPEND |\n fsConstants.O_CREAT |\n fsConstants.O_WRONLY |\n (typeof fsConstants.O_NOFOLLOW === 'number' ? fsConstants.O_NOFOLLOW : 0)\n const fd = openSync(filePath, flags, options.mode ?? 0o600)\n try {\n if (process.platform !== 'win32') fchmodSync(fd, options.mode ?? 0o600)\n const expectedBytes = Buffer.byteLength(content, 'utf8')\n const writtenBytes = writeSync(fd, content, undefined, 'utf8')\n if (writtenBytes !== expectedBytes) {\n throw new Error(\n `durable append wrote ${writtenBytes} of ${expectedBytes} bytes to '${filePath}'`,\n )\n }\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n syncDurableDirectory(dirname(filePath))\n}\n\n/** Reject a path that escapes its run directory or traverses a symbolic link. */\nexport function assertNoSymlinkDescendant(root: string, target: string, label = 'path'): void {\n const base = resolve(root)\n const exact = resolve(target)\n const rel = relative(base, exact)\n if (rel === '..' || rel.startsWith(`..${process.platform === 'win32' ? '\\\\' : '/'}`)) {\n throw new Error(`${label} path escapes its run directory`)\n }\n if (existsSync(base) && lstatSync(base).isSymbolicLink()) {\n throw new Error(`${label} path contains a symbolic link: ${base}`)\n }\n let current = base\n for (const part of rel.split(/[\\\\/]/u).filter(Boolean)) {\n current = join(current, part)\n if (!existsSync(current)) continue\n if (lstatSync(current).isSymbolicLink()) {\n throw new Error(`${label} path contains a symbolic link: ${current}`)\n }\n }\n}\n\n/** Write, fsync, atomically replace, and fsync one Runtime-owned state file.\n * preserveMode retains ordinary access permissions despite the current umask; a new\n * file keeps normal creation semantics. Callers still own path validation and write exclusion. */\nexport function writeAtomicDurableFile(\n filePath: string,\n content: string,\n options: DurableFileOptions & { readonly preserveMode?: boolean } = {},\n): void {\n const temporaryPath = `${filePath}.${randomUUID()}.tmp`\n let operationFailed = false\n let operationError: unknown\n try {\n let mode = options.mode\n let exactMode = false\n if (options.preserveMode) {\n try {\n mode = statSync(filePath).mode & 0o777\n exactMode = true\n } catch (error) {\n if (!isNoEntryError(error)) throw error\n }\n }\n writeExclusiveDurableFile(temporaryPath, content, { mode }, exactMode)\n renameSync(temporaryPath, filePath)\n syncDurableDirectory(dirname(filePath))\n } catch (error) {\n operationFailed = true\n operationError = error\n }\n let cleanupError: unknown\n try {\n unlinkSync(temporaryPath)\n } catch (error) {\n if (!isNoEntryError(error)) cleanupError = error\n // A successful rename removes the temporary directory entry.\n }\n if (operationFailed) throw operationError\n if (cleanupError !== undefined) throw cleanupError\n}\n\nfunction writeExclusiveDurableFile(\n filePath: string,\n content: string,\n options: DurableFileOptions,\n exactMode = false,\n): void {\n if (options.mode === undefined) {\n writeFileSync(filePath, content, { encoding: 'utf8', flag: 'wx' })\n } else {\n writeFileSync(filePath, content, { encoding: 'utf8', flag: 'wx', mode: options.mode })\n }\n syncDurableFile(filePath, exactMode ? options.mode : undefined)\n}\n\nexport function isAlreadyExistsError(error: unknown): boolean {\n return (\n error !== null &&\n typeof error === 'object' &&\n 'code' in error &&\n (error as { code?: unknown }).code === 'EEXIST'\n )\n}\n\nfunction isNoEntryError(error: unknown): boolean {\n return (\n error !== null &&\n typeof error === 'object' &&\n 'code' in error &&\n (error as { code?: unknown }).code === 'ENOENT'\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graph-C4RFlWAJ.js","names":[],"sources":["../src/runtime/supervise/graph.ts"],"sourcesContent":["/**\n *\n * `runGraph` — agent graphs: profiles as nodes, registry-backed prompt directives as edges.\n *\n * A topology is PLAIN DATA an agent can author in a few lines: nodes are canonical\n * `AgentProfile`s (the ONLY way a node is described — no role-builder functions), edges are typed\n * values carrying versioned {@link PromptHandle} directives, `deliverable` (termination) and\n * `budget` (one conserved pool) are mandatory. Driver↔worker is the two-node cyclic instance;\n * \"agent 3 analyzes 1 and 2 and reports to 1\" is ONE edge, not a framework.\n *\n * NOT A SECOND SCHEDULER. `runGraph` is an interpretation layer over what already runs:\n * `supervise()` is the execution core — the same `supervisorAgent`/`driverAgent` machinery,\n * backend-derived worker path (authorized, classified, recursive), conserved-pool budget, and\n * deliverable-gated settlement every\n * supervised run uses. (`runAgentRounds` is deliberately NOT the substrate here.) What the graph\n * layer ADDS is exactly what a bespoke driver loop never\n * had:\n *\n * 1. **Node pinning** — a spawn names a node (`profile.name` = node id) and the node's canonical\n * profile is what runs; a driver cannot smuggle capabilities into a worker it did not define.\n * 2. **Observable edges** — every delegates/analyzes/maps traversal lands in an EDGE LEDGER\n * (`delivered | stripped | empty | unpropagated | failed`, with byte counts), in memory on\n * the result AND as `edge` events in the run journal. The motivating incident: a filter\n * silently replaced 1,700-char steering with 241 chars of boilerplate for three rounds and\n * NO artifact said so — an unobservable edge cannot be trusted and its directive cannot be\n * optimized.\n * 3. **Directives as data** — edge text lives in the prompt registry (`<surface>/v<n>`), so every\n * edge is a versioned optimization target, never prose hardcoded in a builder function.\n * 4. **Per-edge traversal caps** — the cyclic-graph backstop. A delegates edge whose cap is\n * exhausted REFUSES further traversals (fail loud), so a cycle cannot spin the pool dry.\n * 5. **Continuity as data** — a delegates edge may declare `continuity: 'resume'`, so each spawn\n * after the node's first re-attaches to its latest SETTLED session (the spawn context hands\n * the executor seam `resume: { ofWorker, sequence }`; the kernel keeps identity, ordering,\n * ledger truth, and the one conserved pool). Every ledger row states how its hop continued:\n * `'fresh' | 'resume'` for spawns, `'steer'` for mid-run deliveries — fresh respawns, session\n * resumes, and live steers are all plain data, each a ledgered fact.\n * 6. **Fan-out over a run-time list** — a `maps` edge takes the array a JSON pointer selects in a\n * node's settled result and runs one item per element, then hands the ordered results to a\n * join node. The runtime spawns every item and the join when the source settles, so the\n * driver spends no model turn on them; each item is a spawn through the same authorized path,\n * reserving from the same conserved pool, and every expansion, item and join is a ledger row.\n *\n * ORACLES ARE ENVIRONMENT, NEVER WORKERS. Graders/verifiers must not be spawnable in the graph —\n * a delegates edge to them leaks the rubric. An `analyzes` edge names its analyst in one of two\n * forms: a LENS id from the environment's registry (a pure function over trace evidence), or the\n * id of a graph NODE — a tool-equipped analyst AGENT spawned on each matching settle with the\n * node's pinned profile, whose settle output IS the findings. Either way the oracle doctrine\n * holds: an analyst node can never be a delegates target (refused loudly), so no driver can hand\n * it work, and an id living in both the registry and the nodes is refused as ambiguous.\n *\n * @experimental\n */\n\nimport { resolve } from 'node:path'\nimport { type AgentProfile, canonicalCandidateDigest } from '@tangle-network/agent-interface'\nimport {\n FileResultBlobStore,\n FileSpawnJournal,\n InMemoryResultBlobStore,\n InMemorySpawnJournal,\n} from '../../durable/spawn-journal'\nimport { ConfigError, ValidationError } from '../../errors'\nimport type {\n AnalystRegistry,\n ContinuityMode,\n MakeWorkerAgent,\n MapItemFailurePolicy,\n} from '../../mcp/tools/coordination'\nimport { composeRuntimeHooks, type RuntimeHooks } from '../../runtime-hooks'\nimport { harnessRunsAgent } from '../harness-role'\nimport type { ToolLoopChat } from '../tool-loop'\nimport type { DeliverableSpec } from './completion-gate'\nimport {\n createGraphOverlay,\n graphRootProfile,\n type ValidatedGraph,\n validateGraphTopology,\n} from './graph-overlay'\nimport { kernelPromptRegistry, type PromptHandle, type PromptRegistry } from './prompt-registry'\nimport { withRunContext } from './run-context'\nimport {\n profileGuidanceComposer,\n type SuperviseOptions,\n supervise,\n superviseWithTestBrain,\n} from './supervise'\nimport type { Budget, NodeId, ResultBlobStore, SpawnJournal, SupervisedResult } from './types'\n\nexport type { MapItemFailurePolicy } from '../../mcp/tools/coordination'\nexport { defaultEdgeTraversalCap } from './graph-overlay'\n\n// ── The algebra ────────────────────────────────────────────────────────────────\n\n/** A graph node: an id and a canonical `AgentProfile`. The profile is the ONLY way a node is\n * described — its `prompt.systemPrompt` is the standing role (the 0.117 canonical resolution;\n * never a legacy top-level-only reduction), its tools/mcp/resources are its capabilities. */\nexport interface GraphNode {\n readonly id: NodeId\n readonly profile: AgentProfile\n}\n\nexport type GraphEdge =\n /** Work flows down. The delegation directive is DATA → versionable, sweepable, optimizable.\n * Each spawn of `to` by `from` — and each mid-run steer from `from` to a live `to` worker —\n * is one traversal. */\n | {\n readonly kind: 'delegates'\n readonly from: NodeId\n readonly to: NodeId\n readonly directive: PromptHandle\n /** Cyclic-graph backstop: traversals beyond this REFUSE (fail loud). Default\n * {@link defaultEdgeTraversalCap}. */\n readonly maxTraversals?: number\n /** Default continuity for this edge's SPAWN traversals. `'resume'` makes every spawn after\n * the node's first re-attach to its most recent SETTLED worker: a NEW live worker whose\n * spawn context carries `resume: { ofWorker, sequence }` for the executor seam, spending\n * from the same conserved pool — the node's first spawn is effectively `'fresh'`, and a\n * spawn while a prior worker is still live refuses loudly (steer is the live channel).\n * The driver's per-call `spawn_worker` `continuity` argument overrides either way. Omit =\n * `'fresh'` (today's behavior, byte-identical). Caps count resumes exactly like fresh\n * spawns. */\n readonly continuity?: ContinuityMode\n }\n /** Findings flow anywhere: an analyst over N nodes' settled traces, delivered to ONE node.\n * With a LENS analyst the directive wraps the findings for the recipient; with a NODE analyst\n * the directive is the analyst agent's task and the findings are its settle output. */\n | {\n readonly kind: 'analyzes'\n /** The analyst REFERENCE, in one of two forms: a lens id resolved against\n * `RunGraphOptions.analysts` (environment), or the id of a graph NODE with no delegates\n * edge pointing at it — then each matching settle spawns that node's pinned profile as a\n * tool-equipped analyst WORKER (same spawn machinery, conserved budget, trace join) whose\n * task is this edge's directive plus the settled worker's trace evidence and whose settle\n * output is the findings. An id that is both a node and a registry lens is refused. */\n readonly analyst: string\n readonly over: ReadonlyArray<NodeId>\n readonly to: NodeId\n readonly directive: PromptHandle\n /** Observability cap: traversals beyond this are LEDGERED as exhausted (`unpropagated`).\n * Only delegates caps refuse traversal — they are what close the spawn cycle. */\n readonly maxTraversals?: number\n }\n /**\n * Fan-out over a list that exists only at run time, then one join. When `from` settles `done`,\n * the array at `pointer` in its result is expanded: each element becomes the TASK of one item\n * run, at most `concurrency` at a time, and once every item settled the `join` node runs with\n * `{ map, source, items, results: [{ index, item, result }], failures: [{ index, item, outcome,\n * reason }] }`, results in item order. The runtime spawns the items and the join (`from`'s\n * driver spends no turn on them) through the same authorized spawn path a delegates spawn\n * takes: each item reserves its budget from the one conserved pool and is pinned to the edge's\n * profile, so an item's content can never choose what runs.\n *\n * An item is either the node `to` (its pinned profile plus this edge's directive) or the nested\n * `graph`, started per item exactly as `run_graph` starts a graph. Refusals are loud and\n * ledgered, and the driver is woken with them: a pointer that does not resolve to an array, more\n * elements than `maxItems`, or a `concurrency` the run's worker-slot bound can never honor.\n * `concurrency` otherwise THROTTLES (items wait for a free place); it never refuses an item.\n *\n * An edge rather than a node-level spec, because every kernel invariant a map must keep already\n * attaches to edge traversal: pinning admits only the edge's item and join nodes (the spawn\n * context carries a runtime-authored `map` marker), each item is admitted like a spawn and\n * reserves from the conserved pool, each item settles under its own deliverable check, and each\n * expansion, item and join is a ledger row with its journal twin. Topology stays static; only the\n * traversal count is dynamic, so validation and the one-node-one-profile rule are unchanged.\n */\n | {\n readonly kind: 'maps'\n /** The source node: a delegates target, or the join of another maps edge. Each of its `done`\n * settles expands the map once. */\n readonly from: NodeId\n /** RFC 6901 JSON pointer into `from`'s settled result (`''` is the whole result). */\n readonly pointer: string\n /** The item node, run once per element with the element as its task. Exactly one of `to`\n * and `graph`. Reachable only through this edge: never a delegates target. */\n readonly to?: NodeId\n /** A graph run per element as one subtask, through the `run_graph` path. */\n readonly graph?: GraphTopology\n /** Receives the ordered results once every item settled. Reachable only through this edge. */\n readonly join: NodeId\n /** The item directive, appended to the item node's instructions per traversal. */\n readonly directive: PromptHandle\n /** Lists longer than this are refused before any item runs. */\n readonly maxItems: number\n /** How many items run at once. */\n readonly concurrency: number\n readonly onItemFailure: MapItemFailurePolicy\n /** Each item's budget, merged over the per-worker default as `spawn_worker`'s `budget` is. */\n readonly itemBudget?: Partial<Budget>\n }\n\n/** A topology without the run's own budget and deliverable: the form a `maps` edge runs per item,\n * as `run_graph` runs it. */\nexport interface GraphTopology {\n readonly nodes: ReadonlyArray<GraphNode>\n readonly edges: ReadonlyArray<GraphEdge>\n}\n\nexport interface AgentGraph {\n readonly nodes: ReadonlyArray<GraphNode>\n readonly edges: ReadonlyArray<GraphEdge>\n /** Termination is mandatory, not optional: the independent completion oracle. */\n readonly deliverable: DeliverableSpec<unknown>\n /** One conserved pool across the whole graph — cycles without conservation never terminate. */\n readonly budget: Budget\n}\n\n// ── The edge ledger ────────────────────────────────────────────────────────────\n\n/** `failed` is a maps item or join that ran and settled without a usable result (down, or\n * rejected by its deliverable check); a traversal that never reached a worker is `unpropagated`. */\nexport type EdgeDeliveryOutcome = 'delivered' | 'stripped' | 'empty' | 'unpropagated' | 'failed'\n\n/** How one ledgered hop CONTINUED: a spawn traversal stamps its effective spawn mode\n * (`'fresh'` | `'resume'`), and every mid-run delivery into an already-live recipient — a\n * driver steer leg and every analyzes delivery (routed steer or driver-destined finding) —\n * stamps `'steer'`. Zero ambiguity: every row carries exactly one of the three. */\nexport type TraversalContinuity = ContinuityMode | 'steer'\n\n/** One recorded edge traversal — the in-memory row; the journal twin is the `edge` SpawnEvent. */\nexport interface EdgeTraversal {\n /** Stable edge id: `delegates:<from>-><to>`, `analyzes:<analyst>:<over…>-><to>`, or\n * `maps:<from>-><to | graph>-><join>`. */\n readonly edge: string\n readonly kind: 'delegates' | 'analyzes' | 'maps'\n readonly from: string\n readonly to: string\n /** The resolved directive reference (`<surface>/v<n>`). */\n readonly directive: string\n /** 1-based per-edge ordinal. */\n readonly traversal: number\n readonly outcome: EdgeDeliveryOutcome\n /** How this hop continued — see {@link TraversalContinuity}. */\n readonly continuity: TraversalContinuity\n /** Bytes of directive + payload that actually crossed the edge. */\n readonly bytes: number\n readonly reason?: string\n /** The concrete worker node id, once known. A maps expansion names the source worker. */\n readonly workerId?: string\n /** Present on maps rows: the expansion of the source's list, one item by its index, or the\n * join. */\n readonly map?: { readonly phase: 'expand' | 'item' | 'join'; readonly index?: number }\n}\n\n/** A delegates edge exhausted its traversal cap and the run produced no winner: the cap, not the\n * task, ended it. Carries the full evidence so failing loud loses nothing. */\nexport class GraphEdgeCapError extends Error {\n readonly exhaustedEdges: ReadonlyArray<string>\n readonly ledger: ReadonlyArray<EdgeTraversal>\n readonly result: SupervisedResult<unknown>\n constructor(\n exhaustedEdges: ReadonlyArray<string>,\n ledger: ReadonlyArray<EdgeTraversal>,\n result: SupervisedResult<unknown>,\n ) {\n super(\n `runGraph: edge traversal cap exhausted on ${exhaustedEdges.join(', ')} and the run ` +\n 'delivered no winner — the cap (the cyclic-graph backstop), not the task, ended this run. ' +\n 'Raise maxTraversals on the edge or fix the cycle; the full edge ledger and the ' +\n 'supervised result ride on this error.',\n )\n this.name = 'GraphEdgeCapError'\n this.exhaustedEdges = exhaustedEdges\n this.ledger = ledger\n this.result = result\n }\n}\n\n// ── Options / result ───────────────────────────────────────────────────────────\n\n/**\n * Every `SuperviseOptions` key, partitioned by what `runGraph` does with it.\n *\n * `runGraph` starts one `supervise()` run, so every option that run honors is a graph option too\n * unless the graph itself owns the value. This used to be an opt-in list written by hand, and the\n * hand lost: 25 of 49 keys never reached `supervise()` from a graph, including `childSettleGraceMs`\n * (a root-driver failure tore down children that had ALREADY computed the deliverable) and\n * `driverRetry` (agent-runtime#963). Each absence was discovered by losing a run.\n *\n * The four lists below must cover `keyof SuperviseOptions` exactly. `everySuperviseOptionIsClassified`\n * below fails to COMPILE, naming the offender, when a key belongs to none of them — so the next\n * option added to `supervise()` cannot go missing here silently. It has to be classified, and\n * classifying it as forwarded is one word.\n */\n\n/** The graph derives these from the `AgentGraph` itself; a caller value would be overwritten. */\nconst GRAPH_OWNED_SUPERVISE_OPTIONS = [\n 'rootDriverFromBackend',\n 'resolveSpawnProfile',\n 'budget',\n 'deliverable',\n 'makeWorkerAgent',\n 'onCoordinationEvent',\n 'analyzeOnSettle',\n 'mapOnSettle',\n 'continuityByProfile',\n // A graph pins every node and its delegation edges; a node never gains spawn rights it lacks.\n 'inheritSpawnRights',\n] as const\n\n/** Caller-facing on `RunGraphOptions`, but the graph wraps or defaults the value before it goes in:\n * `hooks` composes with the graph's own spawn-binding hook, `authorizeMessage` is wrapped so a\n * narrowed instruction ledgers `stripped`, `authorizeSpawn` runs AFTER the graph has pinned the\n * node (so a product sees the canonical profile, never the driver's stub), `makeLeafAgent` is the\n * caller's leaf override slotted under the graph's pinning, `analysts` rides only with analyze\n * routes, and `journal`/`blobs`/`runId` get graph defaults. */\nconst GRAPH_TRANSFORMED_SUPERVISE_OPTIONS = [\n 'hooks',\n 'authorizeMessage',\n 'authorizeSpawn',\n 'makeLeafAgent',\n 'analysts',\n 'journal',\n 'blobs',\n 'runId',\n] as const\n\n/** Not reachable from a graph, with the reason. `registry` is a NAME COLLISION, not a policy:\n * `RunGraphOptions.registry` is the directive `PromptRegistry` and `SuperviseOptions.registry` is\n * the `SuperviseRegistry` name→value table. Two different types under one name; the graph's wins.\n * Giving the supervise one a graph channel means renaming a public option, so it is recorded here\n * rather than silently dropped. */\nconst GRAPH_REFUSED_SUPERVISE_OPTIONS = ['registry'] as const\n\n/**\n * Forwarded to the root `supervise()` VERBATIM. Everything not owned, transformed, or refused\n * above belongs here — the default is \"a graph honors it\", not \"someone remembered to add it\".\n */\nconst GRAPH_FORWARDED_SUPERVISE_OPTIONS = [\n 'backend',\n // Forwarded: composes guidance into the root and into profiles a manager authors. The graph also\n // composes each PINNED node profile, because pinning replaces the composed `{ name }` stub.\n 'profileGuidance',\n // Forwarded, not graph-owned: a graph pins WHICH nodes run, never who answers a question one of\n // them raises. Without it a graph run's `ask_parent` would always report `no-parent` even when\n // the caller wired an inbox.\n 'escalateQuestion',\n 'rootHandle',\n 'signal',\n 'execution',\n 'resolveDeliverable',\n 'coordination',\n 'peerMail',\n 'peerAudit',\n 'traceTools',\n 'driverBackend',\n 'profileSecurity',\n 'authorizeSpawn',\n 'router',\n 'driveHarness',\n 'selectSeat',\n 'driverRetry',\n 'onDriverAttempt',\n 'workerRetry',\n 'onWorkerRetry',\n 'continuation',\n 'childSettleGraceMs',\n 'teardownConfirmMs',\n 'retainedAtSettlement',\n 'resolveDriveHarness',\n 'driveHarnessMaterialization',\n 'resolveSupervisorTools',\n 'extraTools',\n 'executeExtraTool',\n 'recoverExecutor',\n 'perWorker',\n 'reservationPolicy',\n 'deadlineCheckIn',\n 'budgetStopHoldMs',\n 'workerSlots',\n 'watchWorkers',\n 'stallAfterMs',\n 'wake',\n 'runDir',\n 'resume',\n 'segment',\n 'runContext',\n 'steerDir',\n 'stopRule',\n 'onProgressStop',\n 'maxDepth',\n 'maxTurns',\n 'compaction',\n 'now',\n 'allowedModels',\n 'finalizer',\n 'otel',\n // Forwarded: the registry a manager's `run_graph` resolves stored graphs against. A node of this\n // graph cannot start another graph (pinning refuses it), so it matters to no node here.\n 'graphRegistry',\n] as const\n\ntype ClassifiedSuperviseOption =\n | (typeof GRAPH_OWNED_SUPERVISE_OPTIONS)[number]\n | (typeof GRAPH_TRANSFORMED_SUPERVISE_OPTIONS)[number]\n | (typeof GRAPH_REFUSED_SUPERVISE_OPTIONS)[number]\n | (typeof GRAPH_FORWARDED_SUPERVISE_OPTIONS)[number]\n\n/**\n * Every key `RunGraphOptions` accepts, as data: the forwarded and transformed `SuperviseOptions`\n * keys, the name-colliding `registry`, and the graph's own `brain`. `GRAPH_OWNED_SUPERVISE_OPTIONS`\n * is deliberately absent — a caller value for one of those is overwritten by the graph, so it is\n * refused by name rather than discarded.\n *\n * The assignment below fails to COMPILE, naming the key, when `RunGraphOptions` declares something\n * this list does not carry.\n */\nconst runGraphOptionKeys = [\n ...GRAPH_FORWARDED_SUPERVISE_OPTIONS,\n ...GRAPH_TRANSFORMED_SUPERVISE_OPTIONS,\n ...GRAPH_REFUSED_SUPERVISE_OPTIONS,\n 'brain',\n] as const\n\ntype UnlistedRunGraphOption = Exclude<keyof RunGraphOptions, (typeof runGraphOptionKeys)[number]>\nconst everyRunGraphOptionIsListed: UnlistedRunGraphOption extends never\n ? true\n : UnlistedRunGraphOption = true\nvoid everyRunGraphOptionIsListed\n\nconst runGraphOptionKeySet: ReadonlySet<string> = new Set<string>(runGraphOptionKeys)\nconst graphOwnedOptionKeySet: ReadonlySet<string> = new Set<string>(GRAPH_OWNED_SUPERVISE_OPTIONS)\n\n/**\n * Refuse an option key the graph would not read, naming it and saying which kind it is.\n *\n * A graph-owned key is the sharper case: it type-checks against a widened `SuperviseOptions`\n * value, the graph derives its own value and overwrites the caller's, the run succeeds, and the\n * capability the caller asked for is not there. The rename of the leaf seam from `makeWorkerAgent`\n * to `makeLeafAgent` is the shape of the failure — the old name stayed a valid `SuperviseOptions`\n * key AND a graph-owned one, so a caller that kept passing it got a run with no leaf override and\n * no complaint.\n */\nfunction assertRunGraphOptionKeys(opts: RunGraphOptions): void {\n const graphOwned: string[] = []\n const unknown: string[] = []\n for (const key of Object.keys(opts)) {\n if (runGraphOptionKeySet.has(key)) continue\n if (graphOwnedOptionKeySet.has(key)) graphOwned.push(key)\n else unknown.push(key)\n }\n if (graphOwned.length > 0) {\n throw new ConfigError(\n `runGraph: ${graphOwned.sort().join(', ')} — the graph derives this from the AgentGraph ` +\n 'itself, so a caller value is overwritten and the capability it asks for would silently ' +\n 'be absent',\n )\n }\n if (unknown.length > 0) {\n throw new ConfigError(\n `runGraph: unknown option ${unknown.sort().join(', ')} — no reader consults that name, so ` +\n 'the value would be discarded and the capability it asks for would silently be absent',\n )\n }\n}\n\n/** A `SuperviseOptions` key in none of the four lists makes this assignment fail, and the compiler\n * error names the key. Classify it — `GRAPH_FORWARDED_SUPERVISE_OPTIONS` is usually the answer. */\ntype UnclassifiedSuperviseOption = Exclude<keyof SuperviseOptions, ClassifiedSuperviseOption>\nconst everySuperviseOptionIsClassified: UnclassifiedSuperviseOption extends never\n ? true\n : UnclassifiedSuperviseOption = true\nvoid everySuperviseOptionIsClassified\n\n/** `backend` and `driverBackend` keep graph-specific documentation below.\n *\n * Do not inherit and redeclare them through an indexed access type.\n * TypeScript 7 correctly treats `SuperviseOptions['backend']` as including `undefined`, which makes\n * that redeclaration wider than the exact optional property inherited from `SuperviseOptions`.\n */\ntype GraphInheritedSuperviseOption = Exclude<\n (typeof GRAPH_FORWARDED_SUPERVISE_OPTIONS)[number],\n 'backend' | 'driverBackend'\n>\n\n/** Copy every forwarded option the caller actually set. Absent stays absent: `supervise()` and the\n * graph must not disagree about what \"unset\" means. */\nfunction forwardedSuperviseOptions(\n opts: RunGraphOptions,\n): Pick<SuperviseOptions, (typeof GRAPH_FORWARDED_SUPERVISE_OPTIONS)[number]> {\n const forwarded: Record<string, unknown> = {}\n for (const key of GRAPH_FORWARDED_SUPERVISE_OPTIONS) {\n const value = (opts as Record<string, unknown>)[key]\n if (value !== undefined) forwarded[key] = value\n }\n return forwarded as Pick<SuperviseOptions, (typeof GRAPH_FORWARDED_SUPERVISE_OPTIONS)[number]>\n}\n\n/**\n * Options for one `runGraph` run.\n *\n * Extends every forwarded `SuperviseOptions` key, so a graph honors what a `supervise()` run\n * honors WITHOUT anyone restating it here. Only the graph-specific members and the ones whose\n * graph semantics differ are declared below; everything else inherits its type AND its\n * documentation from `SuperviseOptions`, which is the one owner of both.\n */\nexport interface RunGraphOptions extends Pick<SuperviseOptions, GraphInheritedSuperviseOption> {\n /** WHERE worker nodes run — the executor backend. Provide this OR `makeLeafAgent`. Forwarded to\n * `supervise()`, which derives every authorized leaf from it. A node that declares\n * `agent_runtime_coordination_spawn_worker` becomes a nested supervisor instead, whose own\n * leaves are derived the same way. */\n readonly backend?: Exclude<SuperviseOptions['backend'], undefined>\n /** WHERE the ROOT node's harness brain runs — forwarded to `supervise()` verbatim (see\n * `SuperviseOptions.driverBackend`). Needed when the root node's profile declares an external\n * harness (`codex`, `claude-code`, `opencode`): that root is driven by the harness, not by the\n * router brain, and automatic execution supports a local `bridge`. Unlike `supervise()`, this\n * does NOT default to `backend`: a graph's `backend` places WORKER nodes, so the root driver\n * is selected only by this field. Omit = no harness driver, which is correct for a root whose\n * `profile.harness` is omitted or `cli-base` (that root runs on the router brain). */\n readonly driverBackend?: Exclude<SuperviseOptions['driverBackend'], undefined>\n /** Leaf-execution override (offline tests / advanced). `runGraph` still owns node pinning,\n * directive delivery, and the edge ledger AROUND this seam — only the leaf `act` is yours.\n * Slots INSIDE the kernel's authorized path (`SuperviseOptions.makeLeafAgent`), so a node that\n * declares the spawn tool still becomes a nested supervisor even under an offline leaf. */\n readonly makeLeafAgent?: MakeWorkerAgent\n /** The ROOT driver's inference seam — a caller-owned `ToolLoopChat` that makes every root\n * model call. Use it when the root's decisions must be caller-owned orchestration (a\n * deterministic conversation driver, a persona loop with its own LLM calls) rather than a\n * router-derived model call. The graph machinery around the seam is unchanged: node pinning,\n * directive delivery, the edge ledger, and the journal twin all run the same shipped path,\n * and the root profile keeps prompt control (`prompt-control-execution` materialization —\n * `systemPrompt`/`instructions` still apply). What moves to the caller with the brain:\n * model selection and provider-identity validation (`expectedModel` cannot be enforced on a\n * call the runtime did not place) and per-turn usage reporting (a brain that reports no\n * usage meters nothing into the pool). Omit = the router brain derived from the root\n * profile — the unchanged default. Mutually exclusive with `driverBackend`, and refused\n * when the root profile declares an external harness (that root is driven BY the harness). */\n readonly brain?: ToolLoopChat\n /** Caller-side runtime hooks (telemetry, policy, product extensions). Composed AFTER the\n * graph's own spawn-binding hook on the SAME event stream — the graph never swallows the\n * seam supervise() exposes. */\n readonly hooks?: RuntimeHooks\n /** The analyst lens registry `analyzes` edges resolve against. ENVIRONMENT — needed only for\n * lens analysts; an analyzes edge naming a graph NODE as its analyst needs no registry. */\n readonly analysts?: AnalystRegistry\n /** Directive registry. Default: the seeded kernel registry (`kernelPromptRegistry()`).\n *\n * NOT `SuperviseOptions.registry`, which is the `SuperviseRegistry` name→value table for\n * code-valued options. The two share a name and nothing else, and this one wins here — see\n * `GRAPH_REFUSED_SUPERVISE_OPTIONS`. */\n readonly registry?: PromptRegistry\n /** The run journal the edge ledger and every spawn/settle ride. Default: in-memory. */\n readonly journal?: SpawnJournal\n readonly blobs?: ResultBlobStore\n readonly runId?: string\n /** Product authority over every steer/answer instruction (the filter seam). `runGraph` observes\n * what it CHANGES: a narrowed instruction ledgers its steer traversal as `stripped`. */\n readonly authorizeMessage?: SuperviseOptions['authorizeMessage']\n}\n\nexport interface GraphResult<Out = unknown> {\n readonly result: SupervisedResult<Out>\n /** Every edge traversal, in occurrence order — the observable-edge contract. */\n readonly ledger: ReadonlyArray<EdgeTraversal>\n /** Edge ids whose traversal cap was hit — analyzes exhaustion included (observable here, never\n * a refusal). A DELEGATES cap paired with a `no-winner` result THROWS\n * ({@link GraphEdgeCapError}) instead of returning: only delegates caps refuse spawns, so only\n * they can have ended the run. A LIFECYCLE no-winner (`aborted` / `budget-exhausted`) returns\n * normally even with an exhausted delegates cap — the abort or the pool, not the cap, ended\n * that run, and the exhaustion stays observable here. */\n readonly exhaustedEdges: ReadonlyArray<string>\n readonly runId: string\n}\n\n/** `RunGraphOptions` with the brain REQUIRED — the shape the `/testing` entry's\n * `runGraphWithTestBrain` keeps accepting now that `brain` is a production option. */\nexport interface RunGraphTestOptions extends RunGraphOptions {\n readonly brain: ToolLoopChat\n}\n\n// ── Validation ─────────────────────────────────────────────────────────────────\n\n/** Validate the graph and resolve every directive BEFORE any compute is spent — an invalid\n * topology or an unknown directive is a configuration fault, never a mid-run surprise. */\nfunction validateGraph(\n graph: AgentGraph,\n registry: PromptRegistry,\n analysts: AnalystRegistry | undefined,\n): ValidatedGraph {\n if (!Array.isArray(graph.nodes) || graph.nodes.length === 0) {\n throw new ValidationError('runGraph: graph.nodes must be a non-empty array')\n }\n if (!Array.isArray(graph.edges) || graph.edges.length === 0) {\n throw new ValidationError('runGraph: graph.edges must be a non-empty array')\n }\n if (typeof graph.deliverable?.check !== 'function') {\n throw new ValidationError('runGraph: graph.deliverable is mandatory (termination oracle)')\n }\n if (typeof graph.budget !== 'object' || graph.budget === null) {\n throw new ValidationError('runGraph: graph.budget is mandatory (the conserved pool)')\n }\n return validateGraphTopology(graph.nodes, graph.edges, registry, analysts)\n}\n\n// ── The runner ─────────────────────────────────────────────────────────────────\n\n/**\n * Execute an {@link AgentGraph}. The root node becomes the supervisor (`supervise()` — the\n * execution core), each worker node is spawnable BY NODE ID (`spawn_worker` with\n * `profile: { name: '<node id>' }`; the node's canonical profile is pinned by the graph), each\n * delegates directive is appended to the worker profile's `prompt.instructions` per traversal,\n * and each analyzes edge becomes an analyst-on-settle route with a real DESTINATION. Every\n * traversal is ledgered and journaled.\n */\nexport function runGraph(graph: AgentGraph, opts: RunGraphOptions): Promise<GraphResult> {\n const { brain, ...runtimeOptions } = opts\n return superviseAgentGraph(graph, runtimeOptions, brain)\n}\n\n/** Alias for graph tests written before `RunGraphOptions.brain` was production. The production\n * entry accepts the same shape; this wrapper only keeps the `/testing` import path working. */\nexport function runGraphWithTestBrain(\n graph: AgentGraph,\n opts: RunGraphTestOptions,\n): Promise<GraphResult> {\n const { brain, ...runtimeOptions } = opts\n return superviseAgentGraph(graph, runtimeOptions, brain)\n}\n\n/**\n * The graph supervise run, as its own entry: the engine's `run-graph` preset node executes exactly\n * this, so the preset and `runGraph` are the same code path by construction (agent-runtime#982).\n */\n/**\n * Every refusal a graph earns before any compute, in one place: the authoring contract. `runGraph`\n * calls it FIRST so a malformed graph throws synchronously, exactly as it did before the engine\n * preset (agent-runtime#982) moved execution behind a promise.\n */\nexport function assertRunGraphAuthoring(\n graph: AgentGraph,\n opts: RunGraphOptions,\n brain?: ToolLoopChat,\n): ReturnType<typeof validateGraph> {\n assertRunGraphOptionKeys(opts)\n const registry = opts.registry ?? kernelPromptRegistry()\n const validated = validateGraph(graph, registry, opts.analysts)\n const { root } = validated\n // A caller brain and a harness driver are two answers to WHO makes the root's calls: refuse the\n // contradiction before any compute, and refuse a harness-driven root outright — the harness IS\n // that root's brain, so a supplied one would be silently ignored downstream.\n if (brain && opts.driverBackend) {\n throw new ValidationError(\n 'runGraph: brain and driverBackend are mutually exclusive — a caller brain makes the root model calls, a driverBackend places a harness that makes its own',\n )\n }\n if (brain && harnessRunsAgent(root.profile.harness)) {\n throw new ValidationError(\n `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')`,\n )\n }\n if (!opts.backend && !opts.makeLeafAgent) {\n throw new ValidationError(\n 'runGraph: provide opts.backend (where nodes run) or opts.makeLeafAgent',\n )\n }\n return validated\n}\n\nexport function superviseAgentGraph(\n graph: AgentGraph,\n opts: RunGraphOptions,\n brain?: ToolLoopChat,\n): Promise<GraphResult> {\n const registry = opts.registry ?? kernelPromptRegistry()\n if (opts.runContext?.acquire !== undefined) {\n return withRunContext(opts.runContext, opts.signal, (runContext, signal) =>\n superviseAgentGraph(\n graph,\n {\n ...opts,\n runContext,\n ...(signal === undefined ? {} : { signal }),\n },\n brain,\n ),\n )\n }\n const validated = assertRunGraphAuthoring(graph, opts, brain)\n const { root } = validated\n // A durable graph needs its journal AND blob store on disk. `supervise()` already resolves this\n // for its own context (`options.journal ?? createFileRunContext(runDir).journal`), but the graph\n // passes an explicit `journal`/`blobs` — the edge ledger's twin writes ride on it — so the graph\n // must do the same resolution itself or its in-memory default SHADOWS the file stores supervise\n // would have built: `runGraph({ runDir })` journaled only to the process, a second process\n // silently restarted the run from scratch, and `runDir/spawn-journal.jsonl` never existed.\n // Layout owner: `createFileRunContext` (run-context.ts); keep these paths byte-identical to it.\n const journal =\n opts.journal ??\n opts.runContext?.journal ??\n (opts.runDir !== undefined\n ? new FileSpawnJournal(`${resolve(opts.runDir)}/spawn-journal.jsonl`)\n : new InMemorySpawnJournal())\n const blobs =\n opts.blobs ??\n opts.runContext?.blobs ??\n (opts.runDir !== undefined\n ? new FileResultBlobStore(`${resolve(opts.runDir)}/blobs`)\n : new InMemoryResultBlobStore())\n const runId =\n opts.runId ??\n opts.runContext?.runId ??\n `graph-${canonicalCandidateDigest(graph.nodes.map((n) => n.id)).slice('sha256:'.length, 'sha256:'.length + 12)}`\n const now = opts.now ?? Date.now\n\n // The graph layer: node pinning, analyst routes, continuity defaults and the edge ledger. A\n // `run_graph` subtask builds the same overlay around its nested manager.\n // `profileGuidance` composes the profile a manager authors, but a graph then swaps that\n // `{ name }` stub for the pinned node profile. Compose the pinned profile too, so every node\n // that runs carries its harness and model guidance. Composition is idempotent.\n const guidance = profileGuidanceComposer(opts.profileGuidance)\n const overlay = createGraphOverlay(validated, {\n registry,\n journal,\n runId,\n now,\n ...(guidance ? { guidance } : {}),\n ...(opts.authorizeSpawn ? { authorizeSpawn: opts.authorizeSpawn } : {}),\n })\n // Always present: the kernel refuses steer/answer whenever spawn authorization is on (the\n // graph's pinning IS spawn authorization), so a graph with no caller filter passes instructions\n // through unchanged rather than losing its steer channel. Only a caller filter can strip.\n const callerAuthorizeMessage = opts.authorizeMessage\n const authorizeMessage = overlay.observeMessages<\n Parameters<NonNullable<SuperviseOptions['authorizeMessage']>>[0]\n >((input) =>\n callerAuthorizeMessage ? callerAuthorizeMessage(input) : { instruction: input.instruction },\n )\n\n // ── Spawn-hook: bind ledger rows to concrete worker ids, then journal them ──\n const graphHooks: RuntimeHooks = {\n onEvent: (event) => {\n if (event.target !== 'agent.spawn' || event.phase !== 'after') return\n const payload = event.payload as { childId?: unknown; assignmentId?: unknown } | undefined\n if (typeof payload?.childId !== 'string' || typeof payload.assignmentId !== 'string') return\n return overlay.bindSpawn(event.parentId, payload.assignmentId, payload.childId)\n },\n }\n const hooks = composeRuntimeHooks(graphHooks, opts.hooks)\n const rootProfile = graphRootProfile(validated, registry)\n\n // Every configuration fault above throws SYNCHRONOUSLY (matching `supervise()`'s own\n // contract); only the run itself is asynchronous.\n const start = async (): Promise<GraphResult> => {\n const superviseOptions = {\n // Every forwarded option the caller set, including the ones nobody thought to list here.\n // `backend` rides along: the kernel derives every authorized LEAF from it, under the graph's\n // pinning. The root driver stays an explicit `driverBackend` choice: `rootDriverFromBackend:\n // false` stops supervise() defaulting one from the other, so where workers run never selects\n // where the root runs (the axis split every graph caller relies on).\n ...forwardedSuperviseOptions(opts),\n rootDriverFromBackend: false,\n // Graph-owned values come after the spread: the graph, not the caller, decides these.\n budget: graph.budget,\n deliverable: graph.deliverable,\n authorizeSpawn: overlay.authorizeSpawn,\n resolveSpawnProfile: overlay.resolveSpawnProfile,\n inheritSpawnRights: false,\n ...(opts.makeLeafAgent ? { makeLeafAgent: opts.makeLeafAgent } : {}),\n journal,\n blobs,\n runId,\n hooks,\n onCoordinationEvent: async (\n _context: unknown,\n _eventId: unknown,\n record: { readonly event: Parameters<typeof overlay.onCoordinationEvent>[0] },\n ) => overlay.onCoordinationEvent(record.event),\n // Lens routes resolve against the registry; agent routes carry their own analyst profile,\n // so a graph whose only analysts are nodes needs no registry at all.\n ...(overlay.routes.length > 0\n ? {\n analyzeOnSettle: overlay.routes,\n ...(opts.analysts ? { analysts: opts.analysts } : {}),\n }\n : {}),\n ...(overlay.maps.length > 0 ? { mapOnSettle: overlay.maps } : {}),\n ...(Object.keys(overlay.continuityByProfile).length > 0\n ? { continuityByProfile: overlay.continuityByProfile }\n : {}),\n authorizeMessage,\n } satisfies SuperviseOptions\n const result =\n brain === undefined\n ? await supervise(rootProfile, graphTask(graph, root), superviseOptions)\n : await superviseWithTestBrain(rootProfile, graphTask(graph, root), {\n ...superviseOptions,\n brain,\n })\n\n const { ledger, exhaustedEdges, exhaustedDelegates } = await overlay.settle()\n // A LIFECYCLE ending (caller abort, exhausted budget) is its own complete explanation: the\n // cap did not end that run even when it was exhausted along the way, and blaming it would\n // misattribute the ending (and turn a caller-initiated abort into a throw). The exhaustion\n // stays observable in `exhaustedEdges` either way.\n const lifecycleEnded =\n result.kind === 'no-winner' &&\n (result.reason === 'aborted' ||\n result.reason === 'cancelled' ||\n result.reason === 'budget-exhausted')\n if (result.kind !== 'winner' && !lifecycleEnded && exhaustedDelegates.length > 0) {\n // Fail LOUD: the backstop, not the task, ended this run. The evidence rides on the error.\n // Only DELEGATES caps refuse spawns, so only they can be the cause named here.\n throw new GraphEdgeCapError(exhaustedDelegates, ledger, result)\n }\n return { result, ledger, exhaustedEdges, runId }\n }\n\n return start()\n}\n\n/** The root task: the graph's own framing. The deliverable (mandatory) is the termination; the\n * task names what the topology exists to produce. */\nfunction graphTask(graph: AgentGraph, root: GraphNode): string {\n const describe = graph.deliverable.describe\n return (\n describe ?? `Deliver the graph's deliverable by driving your worker nodes (root: '${root.id}').`\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graph-registry-DAI6KNXB.js","names":[],"sources":["../src/runtime/supervise/graph-registry.ts"],"sourcesContent":["/**\n * `graph-registry` — the designer's memory: graphs and their outcomes as files.\n *\n * The AgentGraph algebra makes a graph PLAIN DATA (nodes with pinned profiles,\n * edges with registry-versioned directives, a budget, a deliverable\n * description), which is the property that lets a graph be stored, replayed\n * byte-identical, and mutated like code. This module is the thin durable layer\n * on top: one JSONL record per run, the exact graph content-addressed beside\n * it. The natural home is a SANDBOX WORKSPACE — the platform's snapshot story\n * (Firecracker restore, warm leases, `continuity: 'resume'`) then makes the\n * registry the agent's compounding execution memory across runs: a rerun over\n * the same sandbox finds every graph the designer ever ran, what each cost,\n * and whether it delivered.\n *\n * ORACLES STAY ENVIRONMENT-OWNED. `DeliverableSpec.check` is a function and is\n * deliberately NOT stored: a stored graph keeps the deliverable's `describe`\n * and an optional `checkRef`, and replay re-binds the check from the caller's\n * environment. This is the graph contract's own doctrine — graders are\n * environment, never workers — applied to persistence.\n *\n * Scoring is a documented heuristic, not a learned model: delivered beats\n * failed, then cheaper (tokens, then iterations), then most recent. A designer\n * that needs better selection replaces `pickBest`, not the records.\n */\n\nimport { createHash } from 'node:crypto'\nimport { mkdir, readdir, readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport {\n readCommittedJsonFile,\n upsertCommittedJsonLine,\n withJsonlFileLock,\n} from '../../durable/jsonl-file'\nimport { ValidationError } from '../../errors'\nimport { writeAtomicDurableFile } from './durable-file'\nimport type { AgentGraph, GraphEdge } from './graph'\n\n/** A graph as stored: every axis serializable; the check re-bound at replay. */\nexport interface StoredGraph {\n readonly digest: string\n readonly nodes: ReadonlyArray<{ readonly id: string; readonly profile: AgentProfile }>\n readonly edges: ReadonlyArray<GraphEdge>\n readonly budget: AgentGraph['budget']\n readonly deliverable: { readonly describe?: string; readonly checkRef?: string }\n}\n\n/** One completed run of one graph, for one task shape. */\nexport interface GraphRunRecord {\n /** Content-address of the exact graph that ran. */\n readonly graphDigest: string\n /** Digest of the normalized task shape the designer computed for lookup. */\n readonly taskDigest: string\n readonly startedAt: string\n readonly finishedAt: string\n readonly outcome:\n | { readonly kind: 'delivered'; readonly summary?: string }\n | { readonly kind: 'no-winner'; readonly reason?: string }\n /** Cost from the run's terminal accounting. Unknown channels omitted. */\n readonly cost: {\n readonly iterations?: number\n readonly tokens?: number\n readonly usd?: number\n readonly ms?: number\n }\n /** Ledger size + exhausted edges: the trace's shape, for the designer's read. */\n readonly traversals: number\n readonly exhaustedEdges?: ReadonlyArray<string>\n}\n\nexport interface GraphRegistryDir {\n readonly root: string\n}\n\nconst RECORDS_FILE = 'records.jsonl'\nconst GRAPHS_DIR = 'graphs'\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value).digest('hex')\n}\n\n/** Canonical serialization for content-addressing: stable key order, sorted. */\nfunction canonicalJson(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`\n }\n return JSON.stringify(value)\n}\n\n/** Digest of the exact graph (nodes + edges + budget + deliverable describe). */\nexport function graphDigest(graph: Omit<StoredGraph, 'digest'>): string {\n return sha256(canonicalJson(graph))\n}\n\n/** Digest of the normalized task shape a designer looks records up by. */\nexport function taskShapeDigest(taskShape: Record<string, unknown>): string {\n return sha256(canonicalJson(taskShape))\n}\n\n/** Digest of a runnable graph exactly as the registry stores it — the value\n * `recordGraphRun` demands. Callers recording real runs hash through this,\n * never hand-rolled, so a record always matches its graph. */\nexport function digestOfGraph(graph: AgentGraph): string {\n return storeGraph(graph).digest\n}\n\nfunction storeGraph(graph: AgentGraph): StoredGraph {\n return {\n digest: graphDigest({\n nodes: graph.nodes,\n edges: graph.edges,\n budget: graph.budget,\n deliverable: {\n ...(graph.deliverable.describe ? { describe: graph.deliverable.describe } : {}),\n },\n }),\n nodes: graph.nodes,\n edges: graph.edges,\n budget: graph.budget,\n deliverable: {\n ...(graph.deliverable.describe ? { describe: graph.deliverable.describe } : {}),\n },\n }\n}\n\nfunction assertRecord(record: GraphRunRecord): void {\n if (typeof record.graphDigest !== 'string' || record.graphDigest.length === 0)\n throw new ValidationError('graph-registry: record.graphDigest is required')\n if (typeof record.taskDigest !== 'string' || record.taskDigest.length === 0)\n throw new ValidationError('graph-registry: record.taskDigest is required')\n if (record.outcome.kind !== 'delivered' && record.outcome.kind !== 'no-winner')\n throw new ValidationError(\n \"graph-registry: record.outcome.kind must be 'delivered' | 'no-winner'\",\n )\n}\n\nfunction recordKey(record: GraphRunRecord): string {\n assertRecord(record)\n return JSON.stringify([record.taskDigest, record.graphDigest, record.startedAt])\n}\n\n/** Open (or lazily create) a registry rooted at a workspace directory. The\n * directory is the sandbox workspace; snapshots carry it across runs. */\nexport function graphRegistry(root: string): GraphRegistryDir {\n if (!root || root.trim().length === 0)\n throw new ValidationError('graphRegistry: root directory is required')\n return { root }\n}\n\n/** Append one run record; the exact graph is content-addressed beside it.\n * Idempotent per (taskDigest, graphDigest, startedAt): a replayed append with\n * the same key replaces the prior row rather than duplicating it. */\nexport async function recordGraphRun(\n reg: GraphRegistryDir,\n graph: AgentGraph,\n record: GraphRunRecord,\n): Promise<StoredGraph> {\n assertRecord(record)\n const stored = storeGraph(graph)\n if (stored.digest !== record.graphDigest) {\n throw new ValidationError(\n `graph-registry: record.graphDigest ${record.graphDigest} does not match the supplied graph (${stored.digest}) — record the graph that actually ran`,\n )\n }\n await mkdir(join(reg.root, GRAPHS_DIR), { recursive: true })\n const file = join(reg.root, RECORDS_FILE)\n return withJsonlFileLock(file, async (canonicalPath) => {\n const revisionPath = join(reg.root, GRAPHS_DIR, `${stored.digest}.json`)\n writeAtomicDurableFile(revisionPath, JSON.stringify(stored), { preserveMode: true })\n await upsertCommittedJsonLine(canonicalPath, record, recordKey)\n return stored\n })\n}\n\n/** All records, newest last. */\nexport async function listGraphRuns(reg: GraphRegistryDir): Promise<GraphRunRecord[]> {\n return readCommittedJsonFile<GraphRunRecord>(join(reg.root, RECORDS_FILE))\n}\n\n/** Records for one task shape, newest last. */\nexport async function graphRunsFor(\n reg: GraphRegistryDir,\n taskDigest: string,\n): Promise<GraphRunRecord[]> {\n return (await listGraphRuns(reg)).filter((row) => row.taskDigest === taskDigest)\n}\n\nexport type GraphSelection =\n | { readonly kind: 'replay'; readonly graph: StoredGraph; readonly record: GraphRunRecord }\n | { readonly kind: 'author'; readonly reason: string }\n\n/** The documented heuristic: delivered beats failed; among delivered, cheaper\n * (tokens, then iterations) wins; ties go to the most recent. */\nexport function pickBestGraph(records: ReadonlyArray<GraphRunRecord>): GraphRunRecord | undefined {\n const delivered = records.filter((row) => row.outcome.kind === 'delivered')\n if (delivered.length === 0) return undefined\n const better = (a: GraphRunRecord, b: GraphRunRecord): GraphRunRecord => {\n const aTokens = a.cost.tokens ?? Number.POSITIVE_INFINITY\n const bTokens = b.cost.tokens ?? Number.POSITIVE_INFINITY\n if (aTokens !== bTokens) return aTokens < bTokens ? a : b\n const aIter = a.cost.iterations ?? Number.POSITIVE_INFINITY\n const bIter = b.cost.iterations ?? Number.POSITIVE_INFINITY\n if (aIter !== bIter) return aIter < bIter ? a : b\n return a.finishedAt >= b.finishedAt ? a : b\n }\n return delivered.reduce((best, row) => (better(best, row) === best ? best : row))\n}\n\n/** Load a stored graph by digest. */\nexport async function loadGraph(\n reg: GraphRegistryDir,\n digest: string,\n): Promise<StoredGraph | undefined> {\n try {\n return JSON.parse(\n await readFile(join(reg.root, GRAPHS_DIR, `${digest}.json`), 'utf8'),\n ) as StoredGraph\n } catch {\n return undefined\n }\n}\n\n/** The designer's lookup: replay the known-good graph for this task shape, or\n * author a new one — with the reason stated either way (the decision is data). */\nexport async function lookupGraph(\n reg: GraphRegistryDir,\n taskDigest: string,\n): Promise<GraphSelection> {\n const records = await graphRunsFor(reg, taskDigest)\n const best = pickBestGraph(records)\n if (best) {\n const graph = await loadGraph(reg, best.graphDigest)\n if (graph) return { kind: 'replay', graph, record: best }\n return {\n kind: 'author',\n reason: `best record's graph ${best.graphDigest.slice(0, 12)} is missing from the registry`,\n }\n }\n return {\n kind: 'author',\n reason:\n records.length === 0\n ? 'first run for this task shape'\n : 'no graph delivered for this task shape yet',\n }\n}\n\n/** Rehydrate a stored graph into a runnable one. The check comes from the\n * CALLER's environment (oracles are environment, never stored). */\nexport function graphFromStored(\n stored: StoredGraph,\n check: AgentGraph['deliverable']['check'],\n): AgentGraph {\n return {\n nodes: stored.nodes,\n edges: stored.edges,\n budget: stored.budget,\n deliverable: {\n check,\n ...(stored.deliverable.describe ? { describe: stored.deliverable.describe } : {}),\n },\n }\n}\n\n/** Digests present on disk (integrity probe for tests and audits). */\nexport async function storedGraphDigests(reg: GraphRegistryDir): Promise<string[]> {\n try {\n return (await readdir(join(reg.root, GRAPHS_DIR)))\n .filter((name) => name.endsWith('.json'))\n .map((name) => name.slice(0, -'.json'.length))\n .sort()\n } catch {\n return []\n }\n}\n"],"mappings":"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"}
|