@tangle-network/agent-runtime 0.48.0 → 0.50.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/README.md +79 -15
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/analyst-loop.d.ts +1 -1
- package/dist/{chunk-656G2XCL.js → chunk-BKAIVNFA.js} +3 -3
- package/dist/{chunk-IW2LMLK6.js → chunk-CM2IK7VS.js} +913 -152
- package/dist/chunk-CM2IK7VS.js.map +1 -0
- package/dist/{chunk-VR4JIC5H.js → chunk-ML4IXGTV.js} +2 -2
- package/dist/{chunk-TJS7S3HJ.js → chunk-NDM5VXZW.js} +19 -8
- package/dist/chunk-NDM5VXZW.js.map +1 -0
- package/dist/chunk-OM3YNZIW.js +978 -0
- package/dist/chunk-OM3YNZIW.js.map +1 -0
- package/dist/{chunk-JNPK46YH.js → chunk-RHW75JW5.js} +498 -350
- package/dist/chunk-RHW75JW5.js.map +1 -0
- package/dist/{coder-CVZNGbyg.d.ts → coder-_YCf3BAK.d.ts} +2 -2
- package/dist/{driver-DYU2sgHr.d.ts → driver-DLI1io57.d.ts} +1 -1
- package/dist/index.d.ts +34 -9
- package/dist/index.js +117 -27
- package/dist/index.js.map +1 -1
- package/dist/kb-gate-CHAyt4aI.d.ts +1571 -0
- package/dist/{loop-runner-bin-DEm4roYF.d.ts → loop-runner-bin-DFUNgpeK.d.ts} +4 -4
- package/dist/loop-runner-bin.d.ts +5 -5
- package/dist/loop-runner-bin.js +3 -3
- package/dist/loops.d.ts +6 -6
- package/dist/loops.js +17 -1
- package/dist/mcp/bin.js +206 -29
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +41 -177
- package/dist/mcp/index.js +40 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/openai-tools-D4HLDWgw.d.ts +45 -0
- package/dist/platform.js +2 -2
- package/dist/platform.js.map +1 -1
- package/dist/profiles.d.ts +2 -2
- package/dist/{run-loop-DvD4aGiE.d.ts → run-loop-BIineL1T.d.ts} +1 -1
- package/dist/runtime.d.ts +403 -24
- package/dist/runtime.js +17 -1
- package/dist/{types-BpDfCPUp.d.ts → types-5MGt5KTY.d.ts} +1 -1
- package/dist/{types-nBMuollC.d.ts → types-BEQsBhOE.d.ts} +1 -1
- package/dist/workflow.d.ts +2 -2
- package/dist/workflow.js +1 -1
- package/package.json +6 -5
- package/dist/chunk-IW2LMLK6.js.map +0 -1
- package/dist/chunk-JNPK46YH.js.map +0 -1
- package/dist/chunk-LX66I3SC.js +0 -218
- package/dist/chunk-LX66I3SC.js.map +0 -1
- package/dist/chunk-TJS7S3HJ.js.map +0 -1
- package/dist/kb-gate-51BlLlVM.d.ts +0 -529
- package/dist/otel-export-EzfsVUhh.d.ts +0 -191
- /package/dist/{chunk-656G2XCL.js.map → chunk-BKAIVNFA.js.map} +0 -0
- /package/dist/{chunk-VR4JIC5H.js.map → chunk-ML4IXGTV.js.map} +0 -0
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
import {
|
|
2
|
+
coderProfile,
|
|
3
|
+
multiHarnessCoderFanout
|
|
4
|
+
} from "./chunk-KADIJAD4.js";
|
|
5
|
+
import {
|
|
6
|
+
createSandboxForSpec,
|
|
7
|
+
deleteBoxSafe,
|
|
8
|
+
runLoop,
|
|
9
|
+
sleep,
|
|
10
|
+
throwAbort,
|
|
11
|
+
throwIfAborted
|
|
12
|
+
} from "./chunk-CM2IK7VS.js";
|
|
13
|
+
import {
|
|
14
|
+
ValidationError
|
|
15
|
+
} from "./chunk-GSUO5QS6.js";
|
|
16
|
+
|
|
17
|
+
// src/otel-export.ts
|
|
18
|
+
var SCOPE = { name: "@tangle-network/agent-runtime", version: "0.33.0" };
|
|
19
|
+
var GEN_AI = {
|
|
20
|
+
operation: "gen_ai.operation.name",
|
|
21
|
+
agentName: "gen_ai.agent.name",
|
|
22
|
+
conversationId: "gen_ai.conversation.id",
|
|
23
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
24
|
+
outputTokens: "gen_ai.usage.output_tokens"
|
|
25
|
+
};
|
|
26
|
+
function createOtelExporter(config) {
|
|
27
|
+
const resolvedEndpoint = config?.endpoint ?? (typeof process !== "undefined" ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : void 0);
|
|
28
|
+
if (!resolvedEndpoint) return void 0;
|
|
29
|
+
const endpoint = resolvedEndpoint;
|
|
30
|
+
const headers = config?.headers ?? parseHeadersFromEnv();
|
|
31
|
+
const batchSize = config?.batchSize ?? 64;
|
|
32
|
+
const flushIntervalMs = config?.flushIntervalMs ?? 5e3;
|
|
33
|
+
const serviceName = config?.serviceName ?? "agent-runtime";
|
|
34
|
+
const resourceAttrs = config?.resourceAttributes ?? {};
|
|
35
|
+
const pending = [];
|
|
36
|
+
let timer;
|
|
37
|
+
let stopped = false;
|
|
38
|
+
const exporter = {
|
|
39
|
+
exportSpan(span) {
|
|
40
|
+
if (stopped) return;
|
|
41
|
+
pending.push(span);
|
|
42
|
+
if (pending.length >= batchSize) {
|
|
43
|
+
void doFlush();
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
async flush() {
|
|
47
|
+
await doFlush();
|
|
48
|
+
},
|
|
49
|
+
async shutdown() {
|
|
50
|
+
stopped = true;
|
|
51
|
+
if (timer !== void 0) {
|
|
52
|
+
clearInterval(timer);
|
|
53
|
+
timer = void 0;
|
|
54
|
+
}
|
|
55
|
+
await doFlush();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
timer = setInterval(() => {
|
|
59
|
+
if (pending.length > 0) void doFlush();
|
|
60
|
+
}, flushIntervalMs);
|
|
61
|
+
if (typeof timer === "object" && "unref" in timer) {
|
|
62
|
+
;
|
|
63
|
+
timer.unref();
|
|
64
|
+
}
|
|
65
|
+
async function doFlush() {
|
|
66
|
+
if (pending.length === 0) return;
|
|
67
|
+
const batch = pending.splice(0);
|
|
68
|
+
const body = {
|
|
69
|
+
resourceSpans: [
|
|
70
|
+
{
|
|
71
|
+
resource: {
|
|
72
|
+
attributes: toAttributes({
|
|
73
|
+
"service.name": serviceName,
|
|
74
|
+
...resourceAttrs
|
|
75
|
+
})
|
|
76
|
+
},
|
|
77
|
+
scopeSpans: [{ scope: SCOPE, spans: batch }]
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
const url = `${endpoint.replace(/\/+$/, "")}/v1/traces`;
|
|
82
|
+
try {
|
|
83
|
+
await fetch(url, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: { "content-type": "application/json", ...headers },
|
|
86
|
+
body: JSON.stringify(body)
|
|
87
|
+
});
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return exporter;
|
|
92
|
+
}
|
|
93
|
+
function loopEventToOtelSpan(event, traceId, parentSpanId) {
|
|
94
|
+
const spanId = generateSpanId();
|
|
95
|
+
const attrs = {
|
|
96
|
+
"loop.event_kind": event.kind,
|
|
97
|
+
"loop.run_id": event.runId
|
|
98
|
+
};
|
|
99
|
+
for (const [k, v] of Object.entries(event.payload)) {
|
|
100
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
101
|
+
attrs[`loop.${k}`] = v;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const ts = msToNs(event.timestamp);
|
|
105
|
+
return {
|
|
106
|
+
traceId: padTraceId(traceId),
|
|
107
|
+
spanId,
|
|
108
|
+
parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
|
|
109
|
+
name: event.kind,
|
|
110
|
+
kind: 1,
|
|
111
|
+
startTimeUnixNano: ts,
|
|
112
|
+
endTimeUnixNano: ts,
|
|
113
|
+
attributes: toAttributes(attrs),
|
|
114
|
+
status: { code: 1 }
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function buildLoopOtelSpans(events, traceId, rootParentSpanId) {
|
|
118
|
+
const tid = padTraceId(traceId);
|
|
119
|
+
return buildLoopSpanNodes(events).map((node) => ({
|
|
120
|
+
traceId: tid,
|
|
121
|
+
spanId: node.spanId,
|
|
122
|
+
parentSpanId: node.parentSpanId ? padSpanId(node.parentSpanId) : rootParentSpanId ? padSpanId(rootParentSpanId) : void 0,
|
|
123
|
+
name: node.name,
|
|
124
|
+
kind: 1,
|
|
125
|
+
startTimeUnixNano: msToNs(node.startMs),
|
|
126
|
+
endTimeUnixNano: msToNs(node.endMs),
|
|
127
|
+
attributes: toAttributes(node.attrs),
|
|
128
|
+
status: { code: node.error ? 2 : 1 }
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
function buildLoopSpanNodes(events) {
|
|
132
|
+
if (events.length === 0) return [];
|
|
133
|
+
const out = [];
|
|
134
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
135
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
136
|
+
const rec = (v) => v && typeof v === "object" ? v : {};
|
|
137
|
+
const started = events.find((e) => e.kind === "loop.started");
|
|
138
|
+
const ended = events.find((e) => e.kind === "loop.ended");
|
|
139
|
+
const runId = events[0]?.runId ?? "";
|
|
140
|
+
const rootStart = started?.timestamp ?? events[0].timestamp;
|
|
141
|
+
const rootEnd = ended?.timestamp ?? events[events.length - 1].timestamp;
|
|
142
|
+
const rootId = generateSpanId();
|
|
143
|
+
const make = (spanId, parentSpanId, name, kind, startMs, endMs, attrs, error = false) => ({
|
|
144
|
+
spanId,
|
|
145
|
+
parentSpanId,
|
|
146
|
+
name,
|
|
147
|
+
kind,
|
|
148
|
+
startMs,
|
|
149
|
+
endMs,
|
|
150
|
+
attrs,
|
|
151
|
+
error
|
|
152
|
+
});
|
|
153
|
+
const sp = rec(started?.payload);
|
|
154
|
+
const rootAttrs = {
|
|
155
|
+
[GEN_AI.operation]: "invoke_workflow",
|
|
156
|
+
[GEN_AI.conversationId]: runId,
|
|
157
|
+
"tangle.loop.driver": str(sp.driver) ?? "driver"
|
|
158
|
+
};
|
|
159
|
+
if (Array.isArray(sp.agentRunNames) && sp.agentRunNames.length > 0) {
|
|
160
|
+
rootAttrs["tangle.loop.agents"] = sp.agentRunNames.map(String).join(",");
|
|
161
|
+
}
|
|
162
|
+
if (ended) {
|
|
163
|
+
const ep = rec(ended.payload);
|
|
164
|
+
const win = num(ep.winnerIterationIndex);
|
|
165
|
+
if (win !== void 0) rootAttrs["tangle.loop.winner.iteration_index"] = win;
|
|
166
|
+
const cost = num(ep.totalCostUsd);
|
|
167
|
+
if (cost !== void 0) rootAttrs["tangle.cost.usd"] = cost;
|
|
168
|
+
const dur = num(ep.durationMs);
|
|
169
|
+
if (dur !== void 0) rootAttrs["tangle.loop.duration_ms"] = dur;
|
|
170
|
+
const iters = num(ep.iterations);
|
|
171
|
+
if (iters !== void 0) rootAttrs["tangle.loop.iterations"] = iters;
|
|
172
|
+
}
|
|
173
|
+
out.push(make(rootId, void 0, "loop", "loop", rootStart, rootEnd, rootAttrs));
|
|
174
|
+
const iterStartTs = /* @__PURE__ */ new Map();
|
|
175
|
+
const placementByIdx = /* @__PURE__ */ new Map();
|
|
176
|
+
let currentRoundId;
|
|
177
|
+
let pendingRound;
|
|
178
|
+
const flushRound = (endMs) => {
|
|
179
|
+
if (!pendingRound) return;
|
|
180
|
+
out.push(
|
|
181
|
+
make(
|
|
182
|
+
pendingRound.id,
|
|
183
|
+
rootId,
|
|
184
|
+
"loop.round",
|
|
185
|
+
"round",
|
|
186
|
+
pendingRound.start,
|
|
187
|
+
endMs,
|
|
188
|
+
pendingRound.attrs
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
pendingRound = void 0;
|
|
192
|
+
};
|
|
193
|
+
for (const e of events) {
|
|
194
|
+
const p = rec(e.payload);
|
|
195
|
+
switch (e.kind) {
|
|
196
|
+
case "loop.plan": {
|
|
197
|
+
flushRound(e.timestamp);
|
|
198
|
+
const id = generateSpanId();
|
|
199
|
+
const roundIdx = num(p.roundIndex) ?? 0;
|
|
200
|
+
const attrs = {
|
|
201
|
+
[GEN_AI.operation]: "invoke_workflow",
|
|
202
|
+
"tangle.loop.round.index": roundIdx,
|
|
203
|
+
"tangle.loop.move.kind": str(p.moveKind) ?? "unknown",
|
|
204
|
+
"tangle.loop.move.round": roundIdx,
|
|
205
|
+
"tangle.loop.move.width": num(p.plannedCount) ?? 0
|
|
206
|
+
};
|
|
207
|
+
const r = str(p.rationale);
|
|
208
|
+
if (r) attrs["tangle.loop.move.rationale"] = r;
|
|
209
|
+
const parent = num(p.parentIndex);
|
|
210
|
+
if (parent !== void 0) attrs["tangle.loop.move.parent_index"] = parent;
|
|
211
|
+
if (Array.isArray(p.childIndices) && p.childIndices.length > 0) {
|
|
212
|
+
attrs["tangle.loop.move.child_indices"] = p.childIndices.map(String).join(",");
|
|
213
|
+
}
|
|
214
|
+
pendingRound = { id, start: e.timestamp, attrs };
|
|
215
|
+
currentRoundId = id;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "loop.iteration.started": {
|
|
219
|
+
const idx = num(p.iterationIndex);
|
|
220
|
+
if (idx !== void 0) iterStartTs.set(idx, e.timestamp);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case "loop.iteration.dispatch": {
|
|
224
|
+
const idx = num(p.iterationIndex);
|
|
225
|
+
if (idx === void 0) break;
|
|
226
|
+
const place = {};
|
|
227
|
+
const kind = str(p.placement);
|
|
228
|
+
if (kind) place["tangle.loop.placement.kind"] = kind;
|
|
229
|
+
const sid = str(p.sandboxId);
|
|
230
|
+
if (sid) place["tangle.sandbox.id"] = sid;
|
|
231
|
+
const fid = str(p.fleetId);
|
|
232
|
+
if (fid) place["tangle.fleet.id"] = fid;
|
|
233
|
+
const mid = str(p.machineId);
|
|
234
|
+
if (mid) place["tangle.machine.id"] = mid;
|
|
235
|
+
placementByIdx.set(idx, place);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case "loop.iteration.ended": {
|
|
239
|
+
const idx = num(p.iterationIndex) ?? 0;
|
|
240
|
+
const start = iterStartTs.get(idx) ?? e.timestamp;
|
|
241
|
+
const err = str(p.error);
|
|
242
|
+
const attrs = {
|
|
243
|
+
[GEN_AI.operation]: "invoke_agent",
|
|
244
|
+
"tangle.loop.iteration.index": idx
|
|
245
|
+
};
|
|
246
|
+
const agent = str(p.agentRunName);
|
|
247
|
+
if (agent) attrs[GEN_AI.agentName] = agent;
|
|
248
|
+
const tu = rec(p.tokenUsage);
|
|
249
|
+
const inTok = num(tu.input);
|
|
250
|
+
if (inTok !== void 0) attrs[GEN_AI.inputTokens] = inTok;
|
|
251
|
+
const outTok = num(tu.output);
|
|
252
|
+
if (outTok !== void 0) attrs[GEN_AI.outputTokens] = outTok;
|
|
253
|
+
const cost = num(p.costUsd);
|
|
254
|
+
if (cost !== void 0) attrs["tangle.cost.usd"] = cost;
|
|
255
|
+
const verdict = rec(p.verdict);
|
|
256
|
+
if (typeof verdict.valid === "boolean") attrs["tangle.loop.verdict.valid"] = verdict.valid;
|
|
257
|
+
const score = num(verdict.score);
|
|
258
|
+
if (score !== void 0) attrs["tangle.loop.verdict.score"] = score;
|
|
259
|
+
if (err) attrs["tangle.loop.error"] = err;
|
|
260
|
+
const gid = num(p.groupId);
|
|
261
|
+
if (gid !== void 0) attrs["tangle.loop.iteration.group_id"] = gid;
|
|
262
|
+
const par = num(p.parentIndex);
|
|
263
|
+
if (par !== void 0) attrs["tangle.loop.iteration.parent_index"] = par;
|
|
264
|
+
const dur = num(p.durationMs);
|
|
265
|
+
if (dur !== void 0) attrs["tangle.loop.iteration.duration_ms"] = dur;
|
|
266
|
+
const preview = str(p.outputPreview);
|
|
267
|
+
if (preview) attrs["tangle.loop.iteration.output_preview"] = preview;
|
|
268
|
+
Object.assign(attrs, placementByIdx.get(idx) ?? {});
|
|
269
|
+
out.push(
|
|
270
|
+
make(
|
|
271
|
+
generateSpanId(),
|
|
272
|
+
currentRoundId ?? rootId,
|
|
273
|
+
"loop.iteration",
|
|
274
|
+
"branch",
|
|
275
|
+
start,
|
|
276
|
+
e.timestamp,
|
|
277
|
+
attrs,
|
|
278
|
+
err !== void 0
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
case "loop.decision": {
|
|
284
|
+
if (pendingRound) {
|
|
285
|
+
const dec = str(p.decision);
|
|
286
|
+
if (dec) pendingRound.attrs["tangle.loop.decision"] = dec;
|
|
287
|
+
flushRound(e.timestamp);
|
|
288
|
+
}
|
|
289
|
+
currentRoundId = void 0;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
flushRound(rootEnd);
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
function parseHeadersFromEnv() {
|
|
298
|
+
if (typeof process === "undefined") return {};
|
|
299
|
+
const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS;
|
|
300
|
+
if (!raw) return {};
|
|
301
|
+
const out = {};
|
|
302
|
+
for (const pair of raw.split(",")) {
|
|
303
|
+
const eq = pair.indexOf("=");
|
|
304
|
+
if (eq < 0) continue;
|
|
305
|
+
const key = pair.slice(0, eq).trim();
|
|
306
|
+
const value = pair.slice(eq + 1).trim();
|
|
307
|
+
if (key) out[key] = value;
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
function toAttributes(record) {
|
|
312
|
+
return Object.entries(record).map(([key, value]) => ({
|
|
313
|
+
key,
|
|
314
|
+
value: typeof value === "number" ? Number.isInteger(value) ? { intValue: value.toString() } : { doubleValue: value } : typeof value === "boolean" ? { boolValue: value } : { stringValue: value }
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
function msToNs(ms) {
|
|
318
|
+
return (BigInt(Math.floor(ms)) * 1000000n).toString();
|
|
319
|
+
}
|
|
320
|
+
function padSpanId(id) {
|
|
321
|
+
const cleaned = id.replace(/-/g, "");
|
|
322
|
+
return cleaned.slice(0, 16).padEnd(16, "0");
|
|
323
|
+
}
|
|
324
|
+
function padTraceId(id) {
|
|
325
|
+
const cleaned = id.replace(/-/g, "");
|
|
326
|
+
return cleaned.slice(0, 32).padEnd(32, "0");
|
|
327
|
+
}
|
|
328
|
+
function generateSpanId() {
|
|
329
|
+
const bytes = new Uint8Array(8);
|
|
330
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
331
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
332
|
+
} else {
|
|
333
|
+
for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
334
|
+
}
|
|
335
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
336
|
+
}
|
|
337
|
+
var INTELLIGENCE_WIRE_VERSION = "2026-05-26.v1";
|
|
338
|
+
var DEFAULT_INTELLIGENCE_BASE = "https://intelligence.tangle.tools";
|
|
339
|
+
async function exportEvalRuns(events, config) {
|
|
340
|
+
if (events.length === 0) return { ok: true, status: 0, accepted: 0, rejected: [] };
|
|
341
|
+
const apiKey = config?.apiKey ?? (typeof process !== "undefined" ? process.env.TANGLE_API_KEY : void 0);
|
|
342
|
+
if (!apiKey)
|
|
343
|
+
throw new Error("exportEvalRuns: apiKey required (pass config.apiKey or set TANGLE_API_KEY)");
|
|
344
|
+
const base = config?.base ?? (typeof process !== "undefined" ? process.env.INTELLIGENCE_BASE : void 0) ?? DEFAULT_INTELLIGENCE_BASE;
|
|
345
|
+
const url = `${base.replace(/\/+$/, "")}/v1/ingest/eval-runs`;
|
|
346
|
+
const res = await fetch(url, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
headers: {
|
|
349
|
+
"content-type": "application/json",
|
|
350
|
+
authorization: `Bearer ${apiKey}`,
|
|
351
|
+
"X-Tangle-Wire-Version": INTELLIGENCE_WIRE_VERSION,
|
|
352
|
+
...config?.idempotencyKey ? { "Idempotency-Key": config.idempotencyKey } : {}
|
|
353
|
+
},
|
|
354
|
+
body: JSON.stringify({ wireVersion: INTELLIGENCE_WIRE_VERSION, events })
|
|
355
|
+
});
|
|
356
|
+
let parsed = {};
|
|
357
|
+
try {
|
|
358
|
+
parsed = await res.json();
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
ok: res.ok,
|
|
363
|
+
status: res.status,
|
|
364
|
+
accepted: parsed.accepted ?? (res.ok ? events.length : 0),
|
|
365
|
+
rejected: parsed.rejected ?? []
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/mcp/delegation-trace.ts
|
|
370
|
+
var DELEGATION_TRACE_MAX_SPANS = 512;
|
|
371
|
+
var DELEGATION_TRACE_MAX_BYTES = 256 * 1024;
|
|
372
|
+
function buildDelegationTraceSpans(events) {
|
|
373
|
+
return buildLoopSpanNodes(events).map((node) => ({
|
|
374
|
+
spanId: node.spanId,
|
|
375
|
+
...node.parentSpanId !== void 0 ? { parentSpanId: node.parentSpanId } : {},
|
|
376
|
+
name: node.name,
|
|
377
|
+
kind: node.kind,
|
|
378
|
+
startMs: node.startMs,
|
|
379
|
+
endMs: node.endMs,
|
|
380
|
+
...Object.keys(node.attrs).length > 0 ? { meta: node.attrs } : {}
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
function capDelegationTrace(spans, caps) {
|
|
384
|
+
const maxSpans = caps?.maxSpans ?? DELEGATION_TRACE_MAX_SPANS;
|
|
385
|
+
const maxBytes = caps?.maxBytes ?? DELEGATION_TRACE_MAX_BYTES;
|
|
386
|
+
let start = Math.max(0, spans.length - maxSpans);
|
|
387
|
+
const sizes = spans.map((span) => JSON.stringify(span).length + 1);
|
|
388
|
+
let total = 0;
|
|
389
|
+
for (let i = start; i < sizes.length; i += 1) total += sizes[i];
|
|
390
|
+
while (start < spans.length - 1 && total > maxBytes) {
|
|
391
|
+
total -= sizes[start];
|
|
392
|
+
start += 1;
|
|
393
|
+
}
|
|
394
|
+
return { trace: spans.slice(start), truncated: start > 0 };
|
|
395
|
+
}
|
|
396
|
+
function createDelegationTraceCollector(onSpans) {
|
|
397
|
+
const buffers = /* @__PURE__ */ new Map();
|
|
398
|
+
const flush = (events) => {
|
|
399
|
+
const spans = buildDelegationTraceSpans(events);
|
|
400
|
+
if (spans.length > 0) onSpans(spans);
|
|
401
|
+
};
|
|
402
|
+
return {
|
|
403
|
+
emitter: {
|
|
404
|
+
emit(event) {
|
|
405
|
+
const buf = buffers.get(event.runId);
|
|
406
|
+
if (buf) buf.push(event);
|
|
407
|
+
else buffers.set(event.runId, [event]);
|
|
408
|
+
if (event.kind === "loop.ended") {
|
|
409
|
+
const events = buffers.get(event.runId) ?? [event];
|
|
410
|
+
buffers.delete(event.runId);
|
|
411
|
+
flush(events);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
settle() {
|
|
416
|
+
for (const events of buffers.values()) flush(events);
|
|
417
|
+
buffers.clear();
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function generateDelegationSpanId() {
|
|
422
|
+
const bytes = new Uint8Array(8);
|
|
423
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
424
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
425
|
+
} else {
|
|
426
|
+
for (let i = 0; i < 8; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
427
|
+
}
|
|
428
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
429
|
+
}
|
|
430
|
+
function composeLoopTraceEmitters(...emitters) {
|
|
431
|
+
const live = emitters.filter((e) => e !== void 0);
|
|
432
|
+
if (live.length === 0) return void 0;
|
|
433
|
+
if (live.length === 1) return live[0];
|
|
434
|
+
return {
|
|
435
|
+
emit(event) {
|
|
436
|
+
const pending = [];
|
|
437
|
+
for (const emitter of live) {
|
|
438
|
+
const result = emitter.emit(event);
|
|
439
|
+
if (result) pending.push(result);
|
|
440
|
+
}
|
|
441
|
+
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/mcp/detached-turn.ts
|
|
447
|
+
var DEFAULT_TICK_INTERVAL_MS = 5e3;
|
|
448
|
+
function formatDetachedSessionRef(parts) {
|
|
449
|
+
assertRefComponent("sessionId", parts.sessionId);
|
|
450
|
+
if (parts.sandboxId === void 0) return `session=${parts.sessionId}`;
|
|
451
|
+
assertRefComponent("sandboxId", parts.sandboxId);
|
|
452
|
+
return `sandbox=${parts.sandboxId};session=${parts.sessionId}`;
|
|
453
|
+
}
|
|
454
|
+
function parseDetachedSessionRef(raw) {
|
|
455
|
+
const fields = /* @__PURE__ */ new Map();
|
|
456
|
+
for (const pair of raw.split(";")) {
|
|
457
|
+
const eq = pair.indexOf("=");
|
|
458
|
+
const key = eq === -1 ? "" : pair.slice(0, eq);
|
|
459
|
+
const value = eq === -1 ? "" : pair.slice(eq + 1);
|
|
460
|
+
if (key !== "session" && key !== "sandbox" || value.length === 0 || fields.has(key)) {
|
|
461
|
+
throw new ValidationError(
|
|
462
|
+
`parseDetachedSessionRef: malformed detachedSessionRef ${JSON.stringify(raw)} \u2014 expected "session=<id>" or "sandbox=<id>;session=<id>"`
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
fields.set(key, value);
|
|
466
|
+
}
|
|
467
|
+
const sessionId = fields.get("session");
|
|
468
|
+
if (!sessionId) {
|
|
469
|
+
throw new ValidationError(
|
|
470
|
+
`parseDetachedSessionRef: detachedSessionRef ${JSON.stringify(raw)} carries no session id`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
const sandboxId = fields.get("sandbox");
|
|
474
|
+
return { sessionId, ...sandboxId !== void 0 ? { sandboxId } : {} };
|
|
475
|
+
}
|
|
476
|
+
function assertRefComponent(name, value) {
|
|
477
|
+
if (value.length === 0 || value.includes(";") || value.includes("=")) {
|
|
478
|
+
throw new ValidationError(
|
|
479
|
+
`formatDetachedSessionRef: ${name} ${JSON.stringify(value)} must be non-empty and free of ";" / "="`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function detachedTurnEvents(sessionId, turn) {
|
|
484
|
+
return [
|
|
485
|
+
{
|
|
486
|
+
type: "result",
|
|
487
|
+
id: sessionId,
|
|
488
|
+
data: {
|
|
489
|
+
text: turn.text,
|
|
490
|
+
finalText: turn.text,
|
|
491
|
+
success: true,
|
|
492
|
+
result: turn.result
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
];
|
|
496
|
+
}
|
|
497
|
+
async function runDetachedTurn(options) {
|
|
498
|
+
const intervalMs = options.tickIntervalMs ?? DEFAULT_TICK_INTERVAL_MS;
|
|
499
|
+
const trace = createDetachedTurnTrace(options);
|
|
500
|
+
trace.started();
|
|
501
|
+
const box = await createSandboxForSpec(options.client, options.spec, options.signal).catch(
|
|
502
|
+
(err) => {
|
|
503
|
+
trace.ended(err instanceof Error ? err.message : String(err));
|
|
504
|
+
throw err;
|
|
505
|
+
}
|
|
506
|
+
);
|
|
507
|
+
const drive = box;
|
|
508
|
+
const onAbort = () => {
|
|
509
|
+
void drive._sessionCancel?.(options.sessionId).catch(() => {
|
|
510
|
+
});
|
|
511
|
+
};
|
|
512
|
+
try {
|
|
513
|
+
if (typeof drive.driveTurn !== "function") {
|
|
514
|
+
throw new ValidationError(
|
|
515
|
+
"runDetachedTurn: the acquired sandbox exposes no driveTurn(message, { sessionId }) \u2014 detached dispatch requires @tangle-network/sandbox >= 0.6 and a session-backed placement (sibling/fleet); disable detached dispatch for this executor."
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
const sandboxId = box.id;
|
|
519
|
+
if (typeof sandboxId !== "string" || sandboxId.length === 0) {
|
|
520
|
+
throw new ValidationError(
|
|
521
|
+
"runDetachedTurn: the acquired sandbox carries no id \u2014 without it the detached run cannot be resumed after a restart, so refusing to dispatch detached."
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
options.bindSandbox(sandboxId);
|
|
525
|
+
trace.dispatched(sandboxId);
|
|
526
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
527
|
+
for (; ; ) {
|
|
528
|
+
throwIfAborted(options.signal);
|
|
529
|
+
const tick = await drive.driveTurn(options.prompt, {
|
|
530
|
+
sessionId: options.sessionId,
|
|
531
|
+
turnId: options.sessionId,
|
|
532
|
+
...options.wallCapMs !== void 0 ? { wallCapMs: options.wallCapMs } : {}
|
|
533
|
+
});
|
|
534
|
+
throwIfAborted(options.signal);
|
|
535
|
+
if (tick.state === "completed") {
|
|
536
|
+
trace.ended();
|
|
537
|
+
return { text: tick.text, result: tick.result };
|
|
538
|
+
}
|
|
539
|
+
if (tick.state === "failed") {
|
|
540
|
+
throw new Error(`detached turn ${options.sessionId} failed: ${tick.error}`);
|
|
541
|
+
}
|
|
542
|
+
options.report({ iteration: 0, phase: detachedRunningPhase(tick.elapsedMs) });
|
|
543
|
+
await sleep(intervalMs, options.signal);
|
|
544
|
+
}
|
|
545
|
+
} catch (err) {
|
|
546
|
+
trace.ended(err instanceof Error ? err.message : String(err));
|
|
547
|
+
throw err;
|
|
548
|
+
} finally {
|
|
549
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
550
|
+
if (options.signal.aborted) onAbort();
|
|
551
|
+
await deleteBoxSafe(box);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function createDetachedTurnTrace(options) {
|
|
555
|
+
const emitter = options.traceEmitter;
|
|
556
|
+
if (!emitter) {
|
|
557
|
+
return { started() {
|
|
558
|
+
}, dispatched() {
|
|
559
|
+
}, ended() {
|
|
560
|
+
} };
|
|
561
|
+
}
|
|
562
|
+
const runId = options.sessionId;
|
|
563
|
+
const agentRunName = options.spec.name ?? options.spec.profile.name ?? "detached-turn";
|
|
564
|
+
const startMs = Date.now();
|
|
565
|
+
let done = false;
|
|
566
|
+
const emit = (event) => {
|
|
567
|
+
void emitter.emit(event);
|
|
568
|
+
};
|
|
569
|
+
return {
|
|
570
|
+
started() {
|
|
571
|
+
emit({
|
|
572
|
+
kind: "loop.started",
|
|
573
|
+
runId,
|
|
574
|
+
timestamp: startMs,
|
|
575
|
+
payload: {
|
|
576
|
+
driver: "detached-turn",
|
|
577
|
+
agentRunNames: [agentRunName],
|
|
578
|
+
maxIterations: 1,
|
|
579
|
+
maxConcurrency: 1
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
emit({
|
|
583
|
+
kind: "loop.iteration.started",
|
|
584
|
+
runId,
|
|
585
|
+
timestamp: startMs,
|
|
586
|
+
payload: { iterationIndex: 0, agentRunName, taskHash: options.sessionId }
|
|
587
|
+
});
|
|
588
|
+
},
|
|
589
|
+
dispatched(sandboxId) {
|
|
590
|
+
emit({
|
|
591
|
+
kind: "loop.iteration.dispatch",
|
|
592
|
+
runId,
|
|
593
|
+
timestamp: Date.now(),
|
|
594
|
+
payload: {
|
|
595
|
+
iterationIndex: 0,
|
|
596
|
+
agentRunName,
|
|
597
|
+
placement: options.placement ?? "sibling",
|
|
598
|
+
sandboxId
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
},
|
|
602
|
+
ended(error) {
|
|
603
|
+
if (done) return;
|
|
604
|
+
done = true;
|
|
605
|
+
const endMs = Date.now();
|
|
606
|
+
emit({
|
|
607
|
+
kind: "loop.iteration.ended",
|
|
608
|
+
runId,
|
|
609
|
+
timestamp: endMs,
|
|
610
|
+
payload: {
|
|
611
|
+
iterationIndex: 0,
|
|
612
|
+
agentRunName,
|
|
613
|
+
costUsd: 0,
|
|
614
|
+
durationMs: endMs - startMs,
|
|
615
|
+
...error !== void 0 ? { error } : {}
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
emit({
|
|
619
|
+
kind: "loop.ended",
|
|
620
|
+
runId,
|
|
621
|
+
timestamp: endMs,
|
|
622
|
+
payload: {
|
|
623
|
+
...error === void 0 ? { winnerIterationIndex: 0 } : {},
|
|
624
|
+
totalCostUsd: 0,
|
|
625
|
+
durationMs: endMs - startMs,
|
|
626
|
+
iterations: 1
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function detachedRunningPhase(elapsedMs) {
|
|
633
|
+
return elapsedMs === void 0 ? "detached-running" : `detached-running ${Math.round(elapsedMs / 1e3)}s`;
|
|
634
|
+
}
|
|
635
|
+
function createDriveTurnResumeDriver(options) {
|
|
636
|
+
const cancelHooked = /* @__PURE__ */ new Set();
|
|
637
|
+
return {
|
|
638
|
+
intervalMs: options.intervalMs ?? DEFAULT_TICK_INTERVAL_MS,
|
|
639
|
+
async tick({ record, detachedSessionRef }, ctx) {
|
|
640
|
+
const ref = parseDetachedSessionRef(detachedSessionRef);
|
|
641
|
+
if (ref.sandboxId === void 0) {
|
|
642
|
+
return {
|
|
643
|
+
state: "failed",
|
|
644
|
+
error: {
|
|
645
|
+
message: `detached session "${ref.sessionId}" was never bound to a sandbox \u2014 the previous process died before the box was acquired, so the turn was never dispatched and cannot be resumed`,
|
|
646
|
+
kind: "DetachedSessionUnboundError"
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
const box = await options.resolveSandbox(ref.sandboxId);
|
|
651
|
+
if (!cancelHooked.has(record.taskId)) {
|
|
652
|
+
cancelHooked.add(record.taskId);
|
|
653
|
+
ctx.signal.addEventListener(
|
|
654
|
+
"abort",
|
|
655
|
+
() => {
|
|
656
|
+
void box._sessionCancel?.(ref.sessionId).catch(() => {
|
|
657
|
+
});
|
|
658
|
+
},
|
|
659
|
+
{ once: true }
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
if (ctx.signal.aborted) throwAbort();
|
|
663
|
+
const tick = await box.driveTurn(options.buildMessage(record), {
|
|
664
|
+
sessionId: ref.sessionId,
|
|
665
|
+
turnId: ref.sessionId,
|
|
666
|
+
...options.wallCapMs !== void 0 ? { wallCapMs: options.wallCapMs } : {}
|
|
667
|
+
});
|
|
668
|
+
if (tick.state === "completed") {
|
|
669
|
+
const output = await options.settleOutput(
|
|
670
|
+
{ text: tick.text, result: tick.result },
|
|
671
|
+
record,
|
|
672
|
+
{
|
|
673
|
+
signal: ctx.signal
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
return { state: "completed", output };
|
|
677
|
+
}
|
|
678
|
+
if (tick.state === "failed") {
|
|
679
|
+
return {
|
|
680
|
+
state: "failed",
|
|
681
|
+
error: {
|
|
682
|
+
message: `detached turn ${ref.sessionId} failed: ${tick.error}`,
|
|
683
|
+
kind: "DetachedTurnFailedError"
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
ctx.report({ iteration: 0, phase: detachedRunningPhase(tick.elapsedMs) });
|
|
688
|
+
return { state: "running" };
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/mcp/executor.ts
|
|
694
|
+
function createSiblingSandboxExecutor(options) {
|
|
695
|
+
const underlying = options.client;
|
|
696
|
+
const client = {
|
|
697
|
+
create(opts) {
|
|
698
|
+
return underlying.create(opts);
|
|
699
|
+
},
|
|
700
|
+
describePlacement(box) {
|
|
701
|
+
return { kind: "sibling", sandboxId: readId(box) };
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
return {
|
|
705
|
+
client,
|
|
706
|
+
placement: "sibling",
|
|
707
|
+
describe() {
|
|
708
|
+
return "sibling-sandbox (each delegation = fresh sandbox via client.create)";
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function createFleetWorkspaceExecutor(options) {
|
|
713
|
+
const fleet = options.fleet;
|
|
714
|
+
const exclude = new Set(options.excludeMachineIds ?? []);
|
|
715
|
+
let callIndex = 0;
|
|
716
|
+
const placementBySandboxId = /* @__PURE__ */ new Map();
|
|
717
|
+
const client = {
|
|
718
|
+
async create() {
|
|
719
|
+
const ids = fleet.ids.filter((id) => !exclude.has(id));
|
|
720
|
+
if (ids.length === 0) {
|
|
721
|
+
throw new Error(
|
|
722
|
+
`agent-runtime: fleet ${fleet.fleetId} has no eligible worker machines (ids=[${fleet.ids.join(",")}], excluded=[${[...exclude].join(",")}])`
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
const selector = options.selectMachine;
|
|
726
|
+
const machineId = selector ? selector({ callIndex, ids }) : ids[callIndex % ids.length];
|
|
727
|
+
callIndex += 1;
|
|
728
|
+
if (typeof machineId !== "string" || machineId.length === 0) {
|
|
729
|
+
throw new Error("agent-runtime: fleet executor selectMachine returned an empty machine id");
|
|
730
|
+
}
|
|
731
|
+
const box = await fleet.sandbox(machineId);
|
|
732
|
+
const sandboxId = readId(box);
|
|
733
|
+
if (sandboxId) placementBySandboxId.set(sandboxId, { machineId });
|
|
734
|
+
return box;
|
|
735
|
+
},
|
|
736
|
+
describePlacement(box) {
|
|
737
|
+
const sandboxId = readId(box);
|
|
738
|
+
const recorded = sandboxId ? placementBySandboxId.get(sandboxId) : void 0;
|
|
739
|
+
return {
|
|
740
|
+
kind: "fleet",
|
|
741
|
+
sandboxId,
|
|
742
|
+
fleetId: fleet.fleetId,
|
|
743
|
+
machineId: recorded?.machineId
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
return {
|
|
748
|
+
client,
|
|
749
|
+
placement: "fleet",
|
|
750
|
+
describe() {
|
|
751
|
+
const excluded = exclude.size > 0 ? ` (excluded=[${[...exclude].join(",")}])` : "";
|
|
752
|
+
return `fleet-workspace (fleetId=${fleet.fleetId}, machines=[${fleet.ids.join(",")}]${excluded})`;
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function readId(box) {
|
|
757
|
+
const raw = box.id;
|
|
758
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/mcp/delegates.ts
|
|
762
|
+
function createDefaultCoderDelegate(options) {
|
|
763
|
+
const executor = resolveExecutor(options);
|
|
764
|
+
const sandboxClient = executor.client;
|
|
765
|
+
const fanoutHarnesses = options.fanoutHarnesses;
|
|
766
|
+
const maxConcurrency = options.maxConcurrency ?? 4;
|
|
767
|
+
const traceEmitter = options.traceEmitter;
|
|
768
|
+
return async (args, ctx) => {
|
|
769
|
+
const task = coderTaskFromArgs(args);
|
|
770
|
+
const variants = Math.max(1, Math.trunc(args.variants ?? 1));
|
|
771
|
+
const loopEmitter = composeLoopTraceEmitters(traceEmitter, ctx.traceEmitter);
|
|
772
|
+
ctx.report({ iteration: 0, phase: "starting" });
|
|
773
|
+
if (variants <= 1) {
|
|
774
|
+
const { agentRunSpec, output, validator } = coderProfile({
|
|
775
|
+
task,
|
|
776
|
+
...options.harness ? { harness: options.harness } : {},
|
|
777
|
+
...options.model ? { model: options.model } : {}
|
|
778
|
+
});
|
|
779
|
+
if (ctx.detachedSessionRef !== void 0 && ctx.updateDetachedSessionRef) {
|
|
780
|
+
const { sessionId } = parseDetachedSessionRef(ctx.detachedSessionRef);
|
|
781
|
+
const rebind = ctx.updateDetachedSessionRef;
|
|
782
|
+
const turn = await runDetachedTurn({
|
|
783
|
+
client: sandboxClient,
|
|
784
|
+
spec: agentRunSpec,
|
|
785
|
+
prompt: agentRunSpec.taskToPrompt(task),
|
|
786
|
+
sessionId,
|
|
787
|
+
bindSandbox: (sandboxId) => rebind(formatDetachedSessionRef({ sandboxId, sessionId })),
|
|
788
|
+
signal: ctx.signal,
|
|
789
|
+
report: ctx.report,
|
|
790
|
+
...loopEmitter ? { traceEmitter: loopEmitter } : {},
|
|
791
|
+
...executor.placement === "fleet" ? { placement: "fleet" } : {},
|
|
792
|
+
...options.detachedTickIntervalMs !== void 0 ? { tickIntervalMs: options.detachedTickIntervalMs } : {},
|
|
793
|
+
...options.detachedWallCapMs !== void 0 ? { wallCapMs: options.detachedWallCapMs } : {}
|
|
794
|
+
});
|
|
795
|
+
const chosen3 = await settleDetachedCoderTurn(turn, {
|
|
796
|
+
task,
|
|
797
|
+
sessionId,
|
|
798
|
+
signal: ctx.signal,
|
|
799
|
+
...options.harness ? { harness: options.harness } : {},
|
|
800
|
+
...options.model ? { model: options.model } : {},
|
|
801
|
+
...options.reviewer ? { reviewer: options.reviewer } : {}
|
|
802
|
+
});
|
|
803
|
+
ctx.report({ iteration: 1, phase: "completed" });
|
|
804
|
+
return chosen3;
|
|
805
|
+
}
|
|
806
|
+
const result2 = await runLoop({
|
|
807
|
+
driver: singleShotDriver,
|
|
808
|
+
agentRun: agentRunSpec,
|
|
809
|
+
output,
|
|
810
|
+
validator,
|
|
811
|
+
task,
|
|
812
|
+
ctx: {
|
|
813
|
+
sandboxClient,
|
|
814
|
+
signal: ctx.signal,
|
|
815
|
+
...loopEmitter ? { traceEmitter: loopEmitter } : {}
|
|
816
|
+
},
|
|
817
|
+
maxIterations: 1,
|
|
818
|
+
maxConcurrency
|
|
819
|
+
});
|
|
820
|
+
const chosen2 = await pickCoderWinner({
|
|
821
|
+
iterations: result2.iterations,
|
|
822
|
+
reviewer: options.reviewer,
|
|
823
|
+
selection: options.winnerSelection ?? "highest-score",
|
|
824
|
+
task,
|
|
825
|
+
signal: ctx.signal
|
|
826
|
+
});
|
|
827
|
+
if (!chosen2) throw new Error(noWinnerMessage(options.reviewer));
|
|
828
|
+
ctx.report({ iteration: 1, phase: "completed" });
|
|
829
|
+
return chosen2;
|
|
830
|
+
}
|
|
831
|
+
const fanout = multiHarnessCoderFanout({
|
|
832
|
+
...fanoutHarnesses && fanoutHarnesses.length > 0 ? { harnesses: fanoutHarnesses.slice(0, variants) } : {},
|
|
833
|
+
...options.fanoutModels ? { models: options.fanoutModels.slice(0, variants) } : {}
|
|
834
|
+
});
|
|
835
|
+
const agentRuns = fanout.agentRuns.slice(0, variants);
|
|
836
|
+
const result = await runLoop({
|
|
837
|
+
driver: fanout.driver,
|
|
838
|
+
agentRuns,
|
|
839
|
+
output: fanout.output,
|
|
840
|
+
validator: fanout.validator,
|
|
841
|
+
task,
|
|
842
|
+
ctx: {
|
|
843
|
+
sandboxClient,
|
|
844
|
+
signal: ctx.signal,
|
|
845
|
+
...loopEmitter ? { traceEmitter: loopEmitter } : {}
|
|
846
|
+
},
|
|
847
|
+
maxIterations: variants,
|
|
848
|
+
maxConcurrency: Math.min(maxConcurrency, variants)
|
|
849
|
+
});
|
|
850
|
+
const chosen = await pickCoderWinner({
|
|
851
|
+
iterations: result.iterations,
|
|
852
|
+
reviewer: options.reviewer,
|
|
853
|
+
selection: options.winnerSelection ?? "highest-score",
|
|
854
|
+
task,
|
|
855
|
+
signal: ctx.signal
|
|
856
|
+
});
|
|
857
|
+
if (!chosen) throw new Error(noWinnerMessage(options.reviewer));
|
|
858
|
+
ctx.report({ iteration: agentRuns.length, phase: "completed" });
|
|
859
|
+
return chosen;
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
async function pickCoderWinner(args) {
|
|
863
|
+
const valid = [];
|
|
864
|
+
for (const iter of args.iterations) {
|
|
865
|
+
if (iter.output === void 0 || iter.error || iter.verdict?.valid !== true) continue;
|
|
866
|
+
valid.push({
|
|
867
|
+
index: iter.index,
|
|
868
|
+
output: iter.output,
|
|
869
|
+
score: iter.verdict.score ?? 0,
|
|
870
|
+
readiness: iter.verdict.score ?? 0
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
if (valid.length === 0) return void 0;
|
|
874
|
+
let eligible = valid;
|
|
875
|
+
if (args.reviewer) {
|
|
876
|
+
eligible = [];
|
|
877
|
+
for (const c of valid) {
|
|
878
|
+
const review = await args.reviewer(c.output, args.task, { signal: args.signal });
|
|
879
|
+
if (review.approved) eligible.push({ ...c, readiness: review.readiness });
|
|
880
|
+
}
|
|
881
|
+
if (eligible.length === 0) return void 0;
|
|
882
|
+
}
|
|
883
|
+
return selectCoderCandidate(eligible, args.selection).output;
|
|
884
|
+
}
|
|
885
|
+
function selectCoderCandidate(candidates, selection) {
|
|
886
|
+
const diffLines = (c) => c.output.diffStats.insertions + c.output.diffStats.deletions;
|
|
887
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
888
|
+
switch (selection) {
|
|
889
|
+
case "smallest-diff":
|
|
890
|
+
return diffLines(a) - diffLines(b) || a.index - b.index;
|
|
891
|
+
case "highest-readiness":
|
|
892
|
+
return b.readiness - a.readiness || a.index - b.index;
|
|
893
|
+
case "first-approved":
|
|
894
|
+
return a.index - b.index;
|
|
895
|
+
default:
|
|
896
|
+
return b.score - a.score || a.index - b.index;
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
return sorted[0];
|
|
900
|
+
}
|
|
901
|
+
function noWinnerMessage(reviewer) {
|
|
902
|
+
return reviewer ? "coder delegate: no candidate passed validation + review" : "coder delegate: no candidate passed validation";
|
|
903
|
+
}
|
|
904
|
+
function coderTaskFromArgs(args) {
|
|
905
|
+
return {
|
|
906
|
+
goal: buildCoderGoal(args),
|
|
907
|
+
repoRoot: args.repoRoot,
|
|
908
|
+
testCmd: args.config?.testCmd,
|
|
909
|
+
typecheckCmd: args.config?.typecheckCmd,
|
|
910
|
+
forbiddenPaths: args.config?.forbiddenPaths,
|
|
911
|
+
maxDiffLines: args.config?.maxDiffLines
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
async function settleDetachedCoderTurn(turn, options) {
|
|
915
|
+
const { output, validator } = coderProfile({
|
|
916
|
+
task: options.task,
|
|
917
|
+
...options.harness ? { harness: options.harness } : {},
|
|
918
|
+
...options.model ? { model: options.model } : {}
|
|
919
|
+
});
|
|
920
|
+
const parsed = output.parse(detachedTurnEvents(options.sessionId, turn));
|
|
921
|
+
const verdict = await validator.validate(parsed, { iteration: 0, signal: options.signal });
|
|
922
|
+
if (verdict.valid !== true) throw new Error(noWinnerMessage(options.reviewer));
|
|
923
|
+
if (options.reviewer) {
|
|
924
|
+
const review = await options.reviewer(parsed, options.task, { signal: options.signal });
|
|
925
|
+
if (!review.approved) throw new Error(noWinnerMessage(options.reviewer));
|
|
926
|
+
}
|
|
927
|
+
return parsed;
|
|
928
|
+
}
|
|
929
|
+
function buildCoderGoal(args) {
|
|
930
|
+
if (!args.contextHint) return args.goal;
|
|
931
|
+
return [args.goal, "", "## Context", args.contextHint].join("\n");
|
|
932
|
+
}
|
|
933
|
+
function resolveExecutor(options) {
|
|
934
|
+
if (options.executor && options.sandboxClient) {
|
|
935
|
+
throw new Error("createDefaultCoderDelegate: pass exactly one of `executor` or `sandboxClient`");
|
|
936
|
+
}
|
|
937
|
+
if (options.executor) return options.executor;
|
|
938
|
+
if (options.sandboxClient) {
|
|
939
|
+
return createSiblingSandboxExecutor({ client: options.sandboxClient });
|
|
940
|
+
}
|
|
941
|
+
throw new Error("createDefaultCoderDelegate: `executor` or `sandboxClient` is required");
|
|
942
|
+
}
|
|
943
|
+
var singleShotDriver = {
|
|
944
|
+
name: "mcp-single-shot",
|
|
945
|
+
async plan(task, history) {
|
|
946
|
+
return history.length === 0 ? [task] : [];
|
|
947
|
+
},
|
|
948
|
+
decide(history) {
|
|
949
|
+
return history.length > 0 ? "pick-winner" : "fail";
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
export {
|
|
954
|
+
createOtelExporter,
|
|
955
|
+
loopEventToOtelSpan,
|
|
956
|
+
buildLoopOtelSpans,
|
|
957
|
+
buildLoopSpanNodes,
|
|
958
|
+
INTELLIGENCE_WIRE_VERSION,
|
|
959
|
+
exportEvalRuns,
|
|
960
|
+
DELEGATION_TRACE_MAX_SPANS,
|
|
961
|
+
DELEGATION_TRACE_MAX_BYTES,
|
|
962
|
+
buildDelegationTraceSpans,
|
|
963
|
+
capDelegationTrace,
|
|
964
|
+
createDelegationTraceCollector,
|
|
965
|
+
generateDelegationSpanId,
|
|
966
|
+
composeLoopTraceEmitters,
|
|
967
|
+
formatDetachedSessionRef,
|
|
968
|
+
parseDetachedSessionRef,
|
|
969
|
+
detachedTurnEvents,
|
|
970
|
+
runDetachedTurn,
|
|
971
|
+
createDriveTurnResumeDriver,
|
|
972
|
+
createSiblingSandboxExecutor,
|
|
973
|
+
createFleetWorkspaceExecutor,
|
|
974
|
+
createDefaultCoderDelegate,
|
|
975
|
+
coderTaskFromArgs,
|
|
976
|
+
settleDetachedCoderTurn
|
|
977
|
+
};
|
|
978
|
+
//# sourceMappingURL=chunk-OM3YNZIW.js.map
|