@tangle-network/agent-runtime 0.44.0 → 0.46.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 +95 -203
- package/dist/agent.d.ts +3 -2
- package/dist/agent.js +5 -7
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop.d.ts +28 -2
- package/dist/analyst-loop.js +4 -1
- package/dist/audit.d.ts +93 -0
- package/dist/audit.js +312 -0
- package/dist/audit.js.map +1 -0
- package/dist/chunk-4B6U4CVQ.js +15 -0
- package/dist/chunk-4B6U4CVQ.js.map +1 -0
- package/dist/chunk-65FQLI4V.js +4089 -0
- package/dist/chunk-65FQLI4V.js.map +1 -0
- package/dist/{chunk-GFKVVRQ7.js → chunk-GN75RGM6.js} +13 -12
- package/dist/chunk-GN75RGM6.js.map +1 -0
- package/dist/chunk-GSUO5QS6.js +146 -0
- package/dist/chunk-GSUO5QS6.js.map +1 -0
- package/dist/chunk-HNUXAZIJ.js +580 -0
- package/dist/chunk-HNUXAZIJ.js.map +1 -0
- package/dist/{chunk-SKUZZCHE.js → chunk-I42NHLKX.js} +5 -5
- package/dist/chunk-I42NHLKX.js.map +1 -0
- package/dist/{chunk-HVYOHJHK.js → chunk-JNPK46YH.js} +2 -2
- package/dist/chunk-JNPK46YH.js.map +1 -0
- package/dist/{chunk-3HMHSN22.js → chunk-KADIJAD4.js} +38 -24
- package/dist/chunk-KADIJAD4.js.map +1 -0
- package/dist/{chunk-KDMRUD2P.js → chunk-KPN7OQ64.js} +296 -8
- package/dist/chunk-KPN7OQ64.js.map +1 -0
- package/dist/{chunk-NRZOXCJK.js → chunk-VR4JIC5H.js} +2 -2
- package/dist/chunk-WIR4HOOJ.js +27 -0
- package/dist/chunk-WIR4HOOJ.js.map +1 -0
- package/dist/coder-DCWFQpmJ.d.ts +114 -0
- package/dist/driver-C-mtBo7h.d.ts +221 -0
- package/dist/improvement.d.ts +0 -1
- package/dist/improvement.js +0 -5
- package/dist/improvement.js.map +1 -1
- package/dist/index.d.ts +122 -9
- package/dist/index.js +398 -10
- package/dist/index.js.map +1 -1
- package/dist/{kb-gate-D0ZIhFOU.d.ts → kb-gate-2Gwpz_27.d.ts} +86 -9
- package/dist/{loop-runner-bin-BLMa8He3.d.ts → loop-runner-bin-D-K6bRp3.d.ts} +17 -13
- package/dist/loop-runner-bin.d.ts +8 -6
- package/dist/loop-runner-bin.js +6 -8
- package/dist/loops.d.ts +7 -393
- package/dist/loops.js +96 -27
- package/dist/mcp/bin.js +7 -7
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +286 -13
- package/dist/mcp/index.js +341 -9
- package/dist/mcp/index.js.map +1 -1
- package/dist/{otel-export-wFDmmurL.d.ts → otel-export-nurzFwuJ.d.ts} +1 -1
- package/dist/profiles.d.ts +385 -86
- package/dist/profiles.js +549 -4
- package/dist/profiles.js.map +1 -1
- package/dist/{run-loop-C4L1Sted.d.ts → run-loop-CU2Y00Si.d.ts} +36 -13
- package/dist/runtime-hooks-C7JwKb9E.d.ts +70 -0
- package/dist/runtime.d.ts +1964 -0
- package/dist/runtime.js +114 -0
- package/dist/runtime.js.map +1 -0
- package/dist/substrate-CUgk7F7s.d.ts +77 -0
- package/dist/topology.d.ts +73 -0
- package/dist/topology.js +111 -0
- package/dist/topology.js.map +1 -0
- package/dist/types-BfoeiQRZ.d.ts +438 -0
- package/dist/{types-DbJzz2uf.d.ts → types-DnYoHvvZ.d.ts} +110 -4
- package/dist/workflow.d.ts +4 -3
- package/dist/workflow.js +4 -5
- package/dist/workflow.js.map +1 -1
- package/package.json +37 -28
- package/skills/agent-runtime-adoption/SKILL.md +32 -29
- package/skills/generate-eval/SKILL.md +60 -0
- package/dist/chunk-3HMHSN22.js.map +0 -1
- package/dist/chunk-GFKVVRQ7.js.map +0 -1
- package/dist/chunk-HVYOHJHK.js.map +0 -1
- package/dist/chunk-KDMRUD2P.js.map +0 -1
- package/dist/chunk-PY6NMZYX.js +0 -52
- package/dist/chunk-PY6NMZYX.js.map +0 -1
- package/dist/chunk-S7JXV32P.js +0 -947
- package/dist/chunk-S7JXV32P.js.map +0 -1
- package/dist/chunk-SKUZZCHE.js.map +0 -1
- package/dist/chunk-SQSCRJ7U.js +0 -65
- package/dist/chunk-SQSCRJ7U.js.map +0 -1
- package/dist/chunk-VOX6Z3II.js +0 -90
- package/dist/chunk-VOX6Z3II.js.map +0 -1
- package/dist/chunk-XBUG326M.js +0 -261
- package/dist/chunk-XBUG326M.js.map +0 -1
- package/dist/dynamic-wUgp6UKs.d.ts +0 -108
- package/dist/optimize-prompt-D-urF2wW.d.ts +0 -129
- /package/dist/{chunk-NRZOXCJK.js.map → chunk-VR4JIC5H.js.map} +0 -0
package/dist/chunk-S7JXV32P.js
DELETED
|
@@ -1,947 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PlannerError,
|
|
3
|
-
ValidationError
|
|
4
|
-
} from "./chunk-SQSCRJ7U.js";
|
|
5
|
-
|
|
6
|
-
// src/loops/drivers/dynamic.ts
|
|
7
|
-
function createDynamicDriver(options) {
|
|
8
|
-
if (typeof options.planner !== "function") {
|
|
9
|
-
throw new ValidationError("createDynamicDriver: planner must be a function");
|
|
10
|
-
}
|
|
11
|
-
const maxIterations = options.maxIterations ?? 8;
|
|
12
|
-
if (!Number.isFinite(maxIterations) || maxIterations <= 0) {
|
|
13
|
-
throw new ValidationError("createDynamicDriver: maxIterations must be > 0");
|
|
14
|
-
}
|
|
15
|
-
const maxFanout = options.maxFanout ?? 4;
|
|
16
|
-
if (!Number.isFinite(maxFanout) || maxFanout < 1) {
|
|
17
|
-
throw new ValidationError("createDynamicDriver: maxFanout must be >= 1");
|
|
18
|
-
}
|
|
19
|
-
let pending;
|
|
20
|
-
return {
|
|
21
|
-
name: options.name ?? "dynamic",
|
|
22
|
-
async plan(task, history) {
|
|
23
|
-
if (history.length >= maxIterations) {
|
|
24
|
-
pending = { kind: "stop", rationale: `maxIterations (${maxIterations}) reached` };
|
|
25
|
-
return [];
|
|
26
|
-
}
|
|
27
|
-
const move = await options.planner({
|
|
28
|
-
task,
|
|
29
|
-
history,
|
|
30
|
-
iterationsSpent: history.length,
|
|
31
|
-
iterationsRemaining: maxIterations - history.length
|
|
32
|
-
});
|
|
33
|
-
pending = validateMove(move, maxFanout);
|
|
34
|
-
switch (pending.kind) {
|
|
35
|
-
case "refine":
|
|
36
|
-
return [pending.task];
|
|
37
|
-
case "fanout":
|
|
38
|
-
return pending.tasks;
|
|
39
|
-
case "stop":
|
|
40
|
-
return [];
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
decide() {
|
|
44
|
-
return pending?.kind === "stop" ? "done" : "continue";
|
|
45
|
-
},
|
|
46
|
-
describePlan() {
|
|
47
|
-
if (!pending) return void 0;
|
|
48
|
-
const out = { kind: pending.kind };
|
|
49
|
-
if (pending.rationale !== void 0) out.rationale = pending.rationale;
|
|
50
|
-
if (pending.kind !== "stop" && pending.parentIndex !== void 0) {
|
|
51
|
-
out.parentIndex = pending.parentIndex;
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function validateMove(move, maxFanout) {
|
|
58
|
-
if (!move || typeof move !== "object" || typeof move.kind !== "string") {
|
|
59
|
-
throw new PlannerError(`dynamic planner returned a non-move value: ${describe(move)}`);
|
|
60
|
-
}
|
|
61
|
-
switch (move.kind) {
|
|
62
|
-
case "refine":
|
|
63
|
-
return move;
|
|
64
|
-
case "stop":
|
|
65
|
-
return move;
|
|
66
|
-
case "fanout": {
|
|
67
|
-
if (!Array.isArray(move.tasks) || move.tasks.length === 0) {
|
|
68
|
-
throw new PlannerError("dynamic planner fanout move must carry a non-empty tasks[]");
|
|
69
|
-
}
|
|
70
|
-
if (move.tasks.length <= maxFanout) return move;
|
|
71
|
-
return {
|
|
72
|
-
kind: "fanout",
|
|
73
|
-
tasks: move.tasks.slice(0, maxFanout),
|
|
74
|
-
rationale: `${move.rationale ?? ""} [clamped ${move.tasks.length}\u2192${maxFanout}]`.trim()
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
default:
|
|
78
|
-
throw new PlannerError(
|
|
79
|
-
`dynamic planner returned unknown move kind: ${describe(move.kind)}`
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function describe(value) {
|
|
84
|
-
try {
|
|
85
|
-
return JSON.stringify(value) ?? String(value);
|
|
86
|
-
} catch {
|
|
87
|
-
return String(value);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
function summarizeHistory(history, opts = {}) {
|
|
91
|
-
const maxOutputChars = opts.maxOutputChars ?? 600;
|
|
92
|
-
return history.map((iter) => {
|
|
93
|
-
const row = { index: iter.index, agentRunName: iter.agentRunName };
|
|
94
|
-
if (iter.verdict) {
|
|
95
|
-
row.valid = iter.verdict.valid;
|
|
96
|
-
if (typeof iter.verdict.score === "number") row.score = iter.verdict.score;
|
|
97
|
-
}
|
|
98
|
-
if (iter.error) row.error = iter.error.message;
|
|
99
|
-
if (iter.output !== void 0) {
|
|
100
|
-
const serialized = describe(iter.output);
|
|
101
|
-
row.output = serialized.length > maxOutputChars ? `${serialized.slice(0, maxOutputChars)}\u2026` : serialized;
|
|
102
|
-
}
|
|
103
|
-
return row;
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// src/loops/drivers/planners.ts
|
|
108
|
-
var blind = ({ task, history }) => history.length === 0 ? { kind: "refine", task, rationale: "blind: single attempt" } : { kind: "stop", rationale: "blind: one attempt only" };
|
|
109
|
-
var PROMPT_PLANNERS = {
|
|
110
|
-
blind
|
|
111
|
-
};
|
|
112
|
-
function resolvePlanner(name) {
|
|
113
|
-
const planner = PROMPT_PLANNERS[name];
|
|
114
|
-
if (!planner) {
|
|
115
|
-
throw new Error(
|
|
116
|
-
`planners: unknown driver '${name}' (have: ${Object.keys(PROMPT_PLANNERS).join(", ")})`
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
return planner;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// src/loops/drivers/refine.ts
|
|
123
|
-
function createRefineDriver(options = {}) {
|
|
124
|
-
const maxIterations = options.maxIterations ?? 5;
|
|
125
|
-
if (!Number.isFinite(maxIterations) || maxIterations <= 0) {
|
|
126
|
-
throw new ValidationError("createRefineDriver: maxIterations must be > 0");
|
|
127
|
-
}
|
|
128
|
-
const refineTask = options.refineTask;
|
|
129
|
-
return {
|
|
130
|
-
name: options.name ?? "refine",
|
|
131
|
-
async plan(task, history) {
|
|
132
|
-
if (history.length >= maxIterations) return [];
|
|
133
|
-
if (history.length === 0) return [task];
|
|
134
|
-
const prior = history.at(-1);
|
|
135
|
-
if (!prior) return [task];
|
|
136
|
-
if (prior.verdict?.valid === true) return [];
|
|
137
|
-
if (!refineTask || !prior.verdict) return [prior.task];
|
|
138
|
-
return [refineTask(prior.task, prior.verdict)];
|
|
139
|
-
},
|
|
140
|
-
decide(history) {
|
|
141
|
-
const last = history.at(-1);
|
|
142
|
-
if (!last) return "continue";
|
|
143
|
-
if (last.verdict?.valid === true) return "stop";
|
|
144
|
-
if (history.length >= maxIterations) return "stop";
|
|
145
|
-
return "continue";
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
function refineWinnerIndex(iterations) {
|
|
150
|
-
for (let i = iterations.length - 1; i >= 0; i -= 1) {
|
|
151
|
-
if (iterations[i]?.verdict?.valid) return i;
|
|
152
|
-
}
|
|
153
|
-
return iterations.length > 0 ? iterations.length - 1 : void 0;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// src/loops/drivers/sandbox-planner.ts
|
|
157
|
-
function createSandboxPlanner(opts) {
|
|
158
|
-
if (!opts.client || typeof opts.client.create !== "function") {
|
|
159
|
-
throw new ValidationError("createSandboxPlanner: client.create is required");
|
|
160
|
-
}
|
|
161
|
-
if (typeof opts.decodeTask !== "function") {
|
|
162
|
-
throw new ValidationError("createSandboxPlanner: decodeTask is required");
|
|
163
|
-
}
|
|
164
|
-
const buildPrompt = opts.buildPrompt ?? defaultBuildPrompt;
|
|
165
|
-
const parseEnvelope = opts.parseEnvelope ?? defaultParseEnvelope;
|
|
166
|
-
return async (ctx) => {
|
|
167
|
-
const reused = opts.reuseBox ? await opts.reuseBox() : void 0;
|
|
168
|
-
const box = reused ?? await opts.client.create(buildSandboxOptions(opts.profile, opts.sandboxOverrides));
|
|
169
|
-
const plannerOwnsBox = reused === void 0;
|
|
170
|
-
try {
|
|
171
|
-
const prompt = await buildPrompt(ctx);
|
|
172
|
-
const events = [];
|
|
173
|
-
for await (const event of box.streamPrompt(prompt, { signal: opts.signal })) {
|
|
174
|
-
events.push(event);
|
|
175
|
-
}
|
|
176
|
-
const envelope = parseEnvelope(events);
|
|
177
|
-
if (!envelope) {
|
|
178
|
-
throw new PlannerError("sandbox planner emitted no parseable topology-move envelope");
|
|
179
|
-
}
|
|
180
|
-
return envelopeToMove(envelope, ctx, opts.decodeTask);
|
|
181
|
-
} finally {
|
|
182
|
-
if (plannerOwnsBox) {
|
|
183
|
-
try {
|
|
184
|
-
if (typeof box.delete === "function") await box.delete();
|
|
185
|
-
} catch {
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function envelopeToMove(envelope, ctx, decodeTask) {
|
|
192
|
-
const kind = String(envelope.kind ?? "").toLowerCase();
|
|
193
|
-
const rationale = typeof envelope.rationale === "string" ? envelope.rationale : void 0;
|
|
194
|
-
if (kind === "stop") {
|
|
195
|
-
return { kind: "stop", rationale };
|
|
196
|
-
}
|
|
197
|
-
if (kind === "refine") {
|
|
198
|
-
const raw = Array.isArray(envelope.tasks) ? envelope.tasks[0] : void 0;
|
|
199
|
-
const task = raw === void 0 ? ctx.task : decodeTaskGuarded(decodeTask, raw, ctx);
|
|
200
|
-
return { kind: "refine", task, rationale };
|
|
201
|
-
}
|
|
202
|
-
if (kind === "fanout") {
|
|
203
|
-
const tasks = resolveFanoutTasks(envelope, ctx, decodeTask);
|
|
204
|
-
return { kind: "fanout", tasks, rationale };
|
|
205
|
-
}
|
|
206
|
-
throw new PlannerError(
|
|
207
|
-
`sandbox planner emitted unknown move kind: ${JSON.stringify(envelope.kind)}`
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
function resolveFanoutTasks(envelope, ctx, decodeTask) {
|
|
211
|
-
if (Array.isArray(envelope.tasks) && envelope.tasks.length > 0) {
|
|
212
|
-
return envelope.tasks.map((raw) => decodeTaskGuarded(decodeTask, raw, ctx));
|
|
213
|
-
}
|
|
214
|
-
if (typeof envelope.n === "number" && Number.isFinite(envelope.n) && envelope.n >= 1) {
|
|
215
|
-
return Array.from({ length: Math.floor(envelope.n) }, () => ctx.task);
|
|
216
|
-
}
|
|
217
|
-
throw new PlannerError("sandbox planner fanout envelope needs a non-empty tasks[] or n >= 1");
|
|
218
|
-
}
|
|
219
|
-
function decodeTaskGuarded(decodeTask, raw, ctx) {
|
|
220
|
-
try {
|
|
221
|
-
return decodeTask(raw, ctx);
|
|
222
|
-
} catch (err) {
|
|
223
|
-
throw new PlannerError(`sandbox planner decodeTask rejected ${JSON.stringify(raw)}`, {
|
|
224
|
-
cause: err
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
function buildSandboxOptions(profile, overrides) {
|
|
229
|
-
const base = overrides ?? {};
|
|
230
|
-
const overrideBackend = base.backend;
|
|
231
|
-
const explicitType = profile.metadata?.backendType;
|
|
232
|
-
return {
|
|
233
|
-
...base,
|
|
234
|
-
backend: {
|
|
235
|
-
type: overrideBackend?.type ?? explicitType ?? "opencode",
|
|
236
|
-
profile,
|
|
237
|
-
...overrideBackend?.model ? { model: overrideBackend.model } : {},
|
|
238
|
-
...overrideBackend?.server ? { server: overrideBackend.server } : {}
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
function defaultBuildPrompt(ctx) {
|
|
243
|
-
const summary = summarizeHistory(ctx.history);
|
|
244
|
-
return [
|
|
245
|
-
"You are the loop planner. You do not do the work \u2014 you decide the topology of the next round.",
|
|
246
|
-
"",
|
|
247
|
-
`Root task:
|
|
248
|
-
${safeJson(ctx.task)}`,
|
|
249
|
-
"",
|
|
250
|
-
`Iterations spent: ${ctx.iterationsSpent}. Remaining before the hard cap: ${ctx.iterationsRemaining}.`,
|
|
251
|
-
"",
|
|
252
|
-
ctx.history.length === 0 ? "No attempts yet." : `Attempts so far (index, agent, verdict, output):
|
|
253
|
-
${safeJson(summary)}`,
|
|
254
|
-
"",
|
|
255
|
-
"Choose ONE move and emit it as a fenced JSON block:",
|
|
256
|
-
' - {"kind":"refine","tasks":[<task>],"rationale":"..."} \u2014 one more attempt; omit tasks to replay the root task.',
|
|
257
|
-
' - {"kind":"fanout","tasks":[<task>,<task>],"rationale":"..."} \u2014 N parallel branches (or "n": N for N copies of the root task).',
|
|
258
|
-
' - {"kind":"stop","rationale":"..."} \u2014 a valid result exists or further attempts will not help.',
|
|
259
|
-
"",
|
|
260
|
-
"Stop as soon as an attempt is valid. Prefer refine when an attempt is close; fan out when attempts disagree or the approach is uncertain.",
|
|
261
|
-
"Emit ONLY the JSON block."
|
|
262
|
-
].join("\n");
|
|
263
|
-
}
|
|
264
|
-
var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set(["result", "final", "planner.move", "done"]);
|
|
265
|
-
function defaultParseEnvelope(events) {
|
|
266
|
-
const moveText = (data) => pickString(data.finalText) ?? pickString(data.text) ?? pickString(data.delta) ?? pickString(data.content) ?? pickString(data.message);
|
|
267
|
-
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
268
|
-
const event = events[i];
|
|
269
|
-
if (!event) continue;
|
|
270
|
-
const data = isRecord(event.data) ? event.data : void 0;
|
|
271
|
-
if (!data) continue;
|
|
272
|
-
if (!TERMINAL_EVENT_TYPES.has(String(event.type ?? ""))) continue;
|
|
273
|
-
const structured = coerceEnvelope(data.result ?? data.output ?? data.move ?? data);
|
|
274
|
-
if (structured) return structured;
|
|
275
|
-
const fromText = coerceEnvelope(extractFencedJson(moveText(data) ?? ""));
|
|
276
|
-
if (fromText) return fromText;
|
|
277
|
-
}
|
|
278
|
-
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
279
|
-
const event = events[i];
|
|
280
|
-
if (!event) continue;
|
|
281
|
-
const data = isRecord(event.data) ? event.data : void 0;
|
|
282
|
-
if (!data) continue;
|
|
283
|
-
const text = moveText(data);
|
|
284
|
-
if (!text) continue;
|
|
285
|
-
const coerced = coerceEnvelope(extractFencedJson(text));
|
|
286
|
-
if (coerced) return coerced;
|
|
287
|
-
}
|
|
288
|
-
return void 0;
|
|
289
|
-
}
|
|
290
|
-
function coerceEnvelope(value) {
|
|
291
|
-
if (!isRecord(value)) return void 0;
|
|
292
|
-
if (typeof value.kind !== "string" || value.kind.length === 0) return void 0;
|
|
293
|
-
const out = { kind: value.kind };
|
|
294
|
-
if (Array.isArray(value.tasks)) out.tasks = value.tasks;
|
|
295
|
-
if (typeof value.n === "number") out.n = value.n;
|
|
296
|
-
if (typeof value.rationale === "string") out.rationale = value.rationale;
|
|
297
|
-
return out;
|
|
298
|
-
}
|
|
299
|
-
function isRecord(value) {
|
|
300
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
301
|
-
}
|
|
302
|
-
function pickString(value) {
|
|
303
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
304
|
-
}
|
|
305
|
-
function extractFencedJson(text) {
|
|
306
|
-
const match = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
307
|
-
const body = (match?.[1] ?? text).trim();
|
|
308
|
-
if (!body) return void 0;
|
|
309
|
-
try {
|
|
310
|
-
return JSON.parse(body);
|
|
311
|
-
} catch {
|
|
312
|
-
return void 0;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
function safeJson(value) {
|
|
316
|
-
try {
|
|
317
|
-
return JSON.stringify(value, null, 2) ?? String(value);
|
|
318
|
-
} catch {
|
|
319
|
-
return String(value);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// src/loops/report-usage.ts
|
|
324
|
-
function reportLoopUsage(cost, result, source = "loop") {
|
|
325
|
-
cost.observe(result.costUsd, source);
|
|
326
|
-
cost.observeTokens({ input: result.tokenUsage.input, output: result.tokenUsage.output });
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// src/loops/sandbox-acquire.ts
|
|
330
|
-
var RETRYABLE_HTTP = /* @__PURE__ */ new Set([502, 503, 504, 522, 524, 408, 425, 429]);
|
|
331
|
-
var TERMINAL_STATUS = /* @__PURE__ */ new Set(["failed", "expired", "stopped"]);
|
|
332
|
-
async function acquireSandbox(client, options, acquire = {}) {
|
|
333
|
-
if (!client || typeof client.create !== "function") {
|
|
334
|
-
throw new ValidationError("acquireSandbox: client.create is required");
|
|
335
|
-
}
|
|
336
|
-
const now = acquire.now ?? Date.now;
|
|
337
|
-
const sleep = acquire.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
338
|
-
const pollMs = acquire.pollIntervalMs ?? 3e3;
|
|
339
|
-
const deadline = now() + (acquire.readyTimeoutMs ?? 6e5);
|
|
340
|
-
const name = options.name ?? acquire.name ?? `loop-sbx-${randomSuffix()}`;
|
|
341
|
-
const createOpts = { ...options, name };
|
|
342
|
-
const c = client;
|
|
343
|
-
let lastErr;
|
|
344
|
-
let attempt = 0;
|
|
345
|
-
while (now() < deadline) {
|
|
346
|
-
throwIfAborted(acquire.signal);
|
|
347
|
-
try {
|
|
348
|
-
const box = await client.create(createOpts);
|
|
349
|
-
return await waitUntilReady(box, deadline, pollMs, acquire.signal, now, sleep);
|
|
350
|
-
} catch (err) {
|
|
351
|
-
throwIfAborted(acquire.signal);
|
|
352
|
-
if (!isRetryable(err)) throw err;
|
|
353
|
-
lastErr = err;
|
|
354
|
-
if (typeof c.list === "function") {
|
|
355
|
-
const found = (await c.list().catch(() => []))?.find((b) => b.name === name);
|
|
356
|
-
if (found) return await waitUntilReady(found, deadline, pollMs, acquire.signal, now, sleep);
|
|
357
|
-
}
|
|
358
|
-
attempt += 1;
|
|
359
|
-
await sleep(Math.min(pollMs * attempt, 15e3));
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
throw new ValidationError(
|
|
363
|
-
`acquireSandbox: could not acquire a running sandbox "${name}" within budget`,
|
|
364
|
-
{ cause: lastErr instanceof Error ? lastErr : void 0 }
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
async function waitUntilReady(box, deadline, pollMs, signal, now, sleep) {
|
|
368
|
-
for (; ; ) {
|
|
369
|
-
throwIfAborted(signal);
|
|
370
|
-
const status = readStatus(box);
|
|
371
|
-
if (status === void 0 || status === "running") return box;
|
|
372
|
-
if (TERMINAL_STATUS.has(status)) {
|
|
373
|
-
throw new ValidationError(
|
|
374
|
-
`acquireSandbox: sandbox ${box.id ?? "(unknown)"} is ${status}${box.error ? `: ${box.error}` : ""}`
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
if (now() >= deadline) {
|
|
378
|
-
throw new ValidationError(
|
|
379
|
-
`acquireSandbox: sandbox ${box.id ?? "(unknown)"} not running within budget (last status: ${status})`
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
await sleep(pollMs);
|
|
383
|
-
if (typeof box.refresh === "function") await box.refresh();
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
function readStatus(box) {
|
|
387
|
-
const s = box.status;
|
|
388
|
-
return typeof s === "string" ? s : void 0;
|
|
389
|
-
}
|
|
390
|
-
function isRetryable(err) {
|
|
391
|
-
if (!err || typeof err !== "object") return false;
|
|
392
|
-
const e = err;
|
|
393
|
-
const status = e.status ?? e.statusCode;
|
|
394
|
-
if (typeof status === "number" && RETRYABLE_HTTP.has(status)) return true;
|
|
395
|
-
const name = e.name ?? "";
|
|
396
|
-
if (name === "TimeoutError" || name === "ServerError" || name === "NetworkError") return true;
|
|
397
|
-
return /\b(timed out|timeout|gateway|temporarily unavailable|ECONNRESET|ETIMEDOUT|EAI_AGAIN)\b/i.test(
|
|
398
|
-
e.message ?? ""
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
function throwIfAborted(signal) {
|
|
402
|
-
if (signal?.aborted) {
|
|
403
|
-
const err = new Error("aborted");
|
|
404
|
-
err.name = "AbortError";
|
|
405
|
-
throw err;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
function randomSuffix(len = 10) {
|
|
409
|
-
return Math.random().toString(36).slice(2, 2 + len);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// src/loops/sandbox-events.ts
|
|
413
|
-
function extractLlmCallEvent(event, agentRunName) {
|
|
414
|
-
if (!event || typeof event !== "object") return void 0;
|
|
415
|
-
const type = String(event.type ?? "");
|
|
416
|
-
const data = event.data && typeof event.data === "object" ? event.data : {};
|
|
417
|
-
if (type === "llm_call" || type === "cost.usage" || type === "usage") {
|
|
418
|
-
return buildLlmCall(data, agentRunName);
|
|
419
|
-
}
|
|
420
|
-
if (type === "message.completed" || type === "result" || type === "final") {
|
|
421
|
-
const usage = data.usage;
|
|
422
|
-
if (!usage || typeof usage !== "object") return void 0;
|
|
423
|
-
return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName);
|
|
424
|
-
}
|
|
425
|
-
if (type === "done") {
|
|
426
|
-
const usage = data.tokenUsage;
|
|
427
|
-
if (!usage || typeof usage !== "object") return void 0;
|
|
428
|
-
const out = pickFiniteNumber(usage, ["outputTokens", "completion_tokens", "tokensOut"]);
|
|
429
|
-
const reasoning = pickFiniteNumber(usage, ["reasoningTokens"]);
|
|
430
|
-
const mergedOut = out !== void 0 || reasoning !== void 0 ? (out ?? 0) + (reasoning ?? 0) : void 0;
|
|
431
|
-
return buildLlmCall(
|
|
432
|
-
{
|
|
433
|
-
inputTokens: usage.inputTokens,
|
|
434
|
-
outputTokens: mergedOut,
|
|
435
|
-
totalCostUsd: data.totalCostUsd,
|
|
436
|
-
model: data.model ?? usage.model
|
|
437
|
-
},
|
|
438
|
-
agentRunName
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
return void 0;
|
|
442
|
-
}
|
|
443
|
-
function buildLlmCall(data, agentRunName) {
|
|
444
|
-
const tokensIn = pickFiniteNumber(data, ["tokensIn", "inputTokens", "prompt_tokens"]);
|
|
445
|
-
const tokensOut = pickFiniteNumber(data, ["tokensOut", "outputTokens", "completion_tokens"]);
|
|
446
|
-
const costUsd = pickFiniteNumber(data, ["costUsd", "totalCostUsd", "cost_usd", "cost"]);
|
|
447
|
-
if (tokensIn === void 0 && tokensOut === void 0 && costUsd === void 0) {
|
|
448
|
-
return void 0;
|
|
449
|
-
}
|
|
450
|
-
const model = typeof data.model === "string" && data.model.length > 0 ? data.model : agentRunName;
|
|
451
|
-
const event = {
|
|
452
|
-
type: "llm_call",
|
|
453
|
-
model
|
|
454
|
-
};
|
|
455
|
-
if (tokensIn !== void 0) event.tokensIn = tokensIn;
|
|
456
|
-
if (tokensOut !== void 0) event.tokensOut = tokensOut;
|
|
457
|
-
if (costUsd !== void 0) event.costUsd = costUsd;
|
|
458
|
-
return event;
|
|
459
|
-
}
|
|
460
|
-
function pickFiniteNumber(data, keys) {
|
|
461
|
-
for (const key of keys) {
|
|
462
|
-
const value = data[key];
|
|
463
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
464
|
-
}
|
|
465
|
-
return void 0;
|
|
466
|
-
}
|
|
467
|
-
function mapSandboxEvent(event, opts = {}) {
|
|
468
|
-
if (!event || typeof event !== "object") return void 0;
|
|
469
|
-
const type = String(event.type ?? "");
|
|
470
|
-
const data = event.data && typeof event.data === "object" ? event.data : {};
|
|
471
|
-
if (type === "message.part.updated") {
|
|
472
|
-
const part = data.part && typeof data.part === "object" ? data.part : {};
|
|
473
|
-
const partType = String(part.type ?? "");
|
|
474
|
-
const delta = typeof data.delta === "string" ? data.delta : void 0;
|
|
475
|
-
const text = delta ?? (typeof part.text === "string" ? part.text : void 0);
|
|
476
|
-
if (text === void 0) return void 0;
|
|
477
|
-
if (partType === "text") return { type: "text_delta", text };
|
|
478
|
-
if (partType === "reasoning" || partType === "thinking")
|
|
479
|
-
return { type: "reasoning_delta", text };
|
|
480
|
-
return void 0;
|
|
481
|
-
}
|
|
482
|
-
return extractLlmCallEvent(event, opts.agentRunName ?? "agent");
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// src/loops/run-loop.ts
|
|
486
|
-
var DEFAULT_MAX_ITERATIONS = 10;
|
|
487
|
-
var DEFAULT_MAX_CONCURRENCY = 4;
|
|
488
|
-
async function runLoop(options) {
|
|
489
|
-
const specs = resolveAgentRuns(options);
|
|
490
|
-
const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
491
|
-
if (!Number.isFinite(maxIterations) || maxIterations <= 0) {
|
|
492
|
-
throw new ValidationError("runLoop: maxIterations must be > 0");
|
|
493
|
-
}
|
|
494
|
-
const maxConcurrency = options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
|
|
495
|
-
if (!Number.isFinite(maxConcurrency) || maxConcurrency <= 0) {
|
|
496
|
-
throw new ValidationError("runLoop: maxConcurrency must be > 0");
|
|
497
|
-
}
|
|
498
|
-
if (!options.ctx?.sandboxClient || typeof options.ctx.sandboxClient.create !== "function") {
|
|
499
|
-
throw new ValidationError("runLoop: ctx.sandboxClient.create is required");
|
|
500
|
-
}
|
|
501
|
-
const now = options.now ?? Date.now;
|
|
502
|
-
const runId = options.runId ?? `loop-${randomSuffix2()}`;
|
|
503
|
-
const loopStart = now();
|
|
504
|
-
const driverName = options.driver.name ?? "driver";
|
|
505
|
-
const iterations = [];
|
|
506
|
-
let round = 0;
|
|
507
|
-
const ownedBoxes = [];
|
|
508
|
-
const collectBox = options.shareWorkerBox ? (box) => {
|
|
509
|
-
ownedBoxes.push(box);
|
|
510
|
-
options.shareWorkerBox?.(box);
|
|
511
|
-
} : void 0;
|
|
512
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
513
|
-
kind: "loop.started",
|
|
514
|
-
runId,
|
|
515
|
-
timestamp: now(),
|
|
516
|
-
payload: {
|
|
517
|
-
driver: driverName,
|
|
518
|
-
agentRunNames: specs.map((spec) => spec.name ?? spec.profile.name ?? "agent"),
|
|
519
|
-
maxIterations,
|
|
520
|
-
maxConcurrency
|
|
521
|
-
}
|
|
522
|
-
});
|
|
523
|
-
const controller = new AbortController();
|
|
524
|
-
const onOuterAbort = () => controller.abort();
|
|
525
|
-
if (options.ctx.signal) {
|
|
526
|
-
if (options.ctx.signal.aborted) controller.abort();
|
|
527
|
-
else options.ctx.signal.addEventListener("abort", onOuterAbort, { once: true });
|
|
528
|
-
}
|
|
529
|
-
try {
|
|
530
|
-
while (iterations.length < maxIterations) {
|
|
531
|
-
if (controller.signal.aborted) throwAbort();
|
|
532
|
-
const planned = await options.driver.plan(options.task, iterations);
|
|
533
|
-
const planDesc = options.driver.describePlan?.();
|
|
534
|
-
const roundIndex = round;
|
|
535
|
-
const baseIndex = iterations.length;
|
|
536
|
-
const remaining = maxIterations - iterations.length;
|
|
537
|
-
const slice = planned.slice(0, remaining);
|
|
538
|
-
const parentIndex = planDesc?.parentIndex ?? (roundIndex === 0 ? void 0 : branchPoint(iterations));
|
|
539
|
-
const childIndices = slice.map((_, i) => baseIndex + i);
|
|
540
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
541
|
-
kind: "loop.plan",
|
|
542
|
-
runId,
|
|
543
|
-
timestamp: now(),
|
|
544
|
-
payload: {
|
|
545
|
-
roundIndex,
|
|
546
|
-
plannedCount: planned.length,
|
|
547
|
-
moveKind: planDesc?.kind ?? (planned.length === 0 ? "stop" : planned.length === 1 ? "refine" : "fanout"),
|
|
548
|
-
rationale: planDesc?.rationale,
|
|
549
|
-
parentIndex,
|
|
550
|
-
childIndices
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
round += 1;
|
|
554
|
-
if (planned.length === 0) break;
|
|
555
|
-
for (let i = 0; i < slice.length; i += 1) {
|
|
556
|
-
const spec = specs[(baseIndex + i) % specs.length];
|
|
557
|
-
iterations.push({
|
|
558
|
-
index: baseIndex + i,
|
|
559
|
-
task: slice[i],
|
|
560
|
-
agentRunName: spec.name ?? spec.profile.name ?? "agent",
|
|
561
|
-
events: [],
|
|
562
|
-
startedAt: now(),
|
|
563
|
-
endedAt: 0,
|
|
564
|
-
costUsd: 0,
|
|
565
|
-
tokenUsage: { input: 0, output: 0 }
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
await runBatch({
|
|
569
|
-
slice,
|
|
570
|
-
baseIndex,
|
|
571
|
-
iterations,
|
|
572
|
-
specs,
|
|
573
|
-
output: options.output,
|
|
574
|
-
validator: options.validator,
|
|
575
|
-
maxConcurrency,
|
|
576
|
-
signal: controller.signal,
|
|
577
|
-
ctx: options.ctx,
|
|
578
|
-
runId,
|
|
579
|
-
now,
|
|
580
|
-
roundIndex,
|
|
581
|
-
parentIndex,
|
|
582
|
-
collectBox
|
|
583
|
-
});
|
|
584
|
-
if (controller.signal.aborted) throwAbort();
|
|
585
|
-
const decision2 = await options.driver.decide(iterations);
|
|
586
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
587
|
-
kind: "loop.decision",
|
|
588
|
-
runId,
|
|
589
|
-
timestamp: now(),
|
|
590
|
-
payload: { decision: serializeDecision(decision2), historyLength: iterations.length }
|
|
591
|
-
});
|
|
592
|
-
if (isTerminalDecision(decision2)) {
|
|
593
|
-
return finalize({
|
|
594
|
-
options,
|
|
595
|
-
decision: decision2,
|
|
596
|
-
iterations,
|
|
597
|
-
startMs: loopStart,
|
|
598
|
-
now,
|
|
599
|
-
runId
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
if (iterations.length >= maxIterations) {
|
|
604
|
-
const decision2 = await options.driver.decide(iterations);
|
|
605
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
606
|
-
kind: "loop.decision",
|
|
607
|
-
runId,
|
|
608
|
-
timestamp: now(),
|
|
609
|
-
payload: { decision: serializeDecision(decision2), historyLength: iterations.length }
|
|
610
|
-
});
|
|
611
|
-
return finalize({ options, decision: decision2, iterations, startMs: loopStart, now, runId });
|
|
612
|
-
}
|
|
613
|
-
const decision = await options.driver.decide(iterations);
|
|
614
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
615
|
-
kind: "loop.decision",
|
|
616
|
-
runId,
|
|
617
|
-
timestamp: now(),
|
|
618
|
-
payload: { decision: serializeDecision(decision), historyLength: iterations.length }
|
|
619
|
-
});
|
|
620
|
-
return finalize({ options, decision, iterations, startMs: loopStart, now, runId });
|
|
621
|
-
} finally {
|
|
622
|
-
if (options.ctx.signal) options.ctx.signal.removeEventListener("abort", onOuterAbort);
|
|
623
|
-
for (const b of ownedBoxes) await destroySandboxSafe(b);
|
|
624
|
-
if (options.shareWorkerBox) options.shareWorkerBox(void 0);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
async function runBatch(args) {
|
|
628
|
-
const queue = args.slice.map((task, offset) => ({ task, index: args.baseIndex + offset }));
|
|
629
|
-
const inflight = /* @__PURE__ */ new Set();
|
|
630
|
-
while (queue.length > 0 || inflight.size > 0) {
|
|
631
|
-
while (inflight.size < args.maxConcurrency && queue.length > 0) {
|
|
632
|
-
const item = queue.shift();
|
|
633
|
-
const p = executeIteration({ ...args, item }).finally(() => inflight.delete(p));
|
|
634
|
-
inflight.add(p);
|
|
635
|
-
}
|
|
636
|
-
if (inflight.size === 0) break;
|
|
637
|
-
await Promise.race(inflight);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
async function executeIteration(args) {
|
|
641
|
-
const slot = args.iterations[args.item.index];
|
|
642
|
-
if (!slot)
|
|
643
|
-
throw new ValidationError(`runLoop: missing iteration slot at index ${args.item.index}`);
|
|
644
|
-
const spec = args.specs[args.item.index % args.specs.length];
|
|
645
|
-
if (!spec) throw new ValidationError("runLoop: no AgentRunSpec available for iteration");
|
|
646
|
-
slot.startedAt = args.now();
|
|
647
|
-
slot.agentRunName = spec.name ?? spec.profile.name ?? "agent";
|
|
648
|
-
await emitTrace(args.ctx.traceEmitter, {
|
|
649
|
-
kind: "loop.iteration.started",
|
|
650
|
-
runId: args.runId,
|
|
651
|
-
timestamp: args.now(),
|
|
652
|
-
payload: {
|
|
653
|
-
iterationIndex: args.item.index,
|
|
654
|
-
agentRunName: slot.agentRunName,
|
|
655
|
-
taskHash: hashJson(args.item.task),
|
|
656
|
-
groupId: args.roundIndex,
|
|
657
|
-
parentIndex: args.parentIndex
|
|
658
|
-
}
|
|
659
|
-
});
|
|
660
|
-
let box;
|
|
661
|
-
try {
|
|
662
|
-
box = await createSandboxForSpec(args.ctx.sandboxClient, spec, args.signal);
|
|
663
|
-
const placement = describeSandboxPlacement(args.ctx.sandboxClient, box);
|
|
664
|
-
await emitTrace(args.ctx.traceEmitter, {
|
|
665
|
-
kind: "loop.iteration.dispatch",
|
|
666
|
-
runId: args.runId,
|
|
667
|
-
timestamp: args.now(),
|
|
668
|
-
payload: {
|
|
669
|
-
iterationIndex: args.item.index,
|
|
670
|
-
agentRunName: slot.agentRunName,
|
|
671
|
-
placement: placement.kind,
|
|
672
|
-
sandboxId: placement.sandboxId,
|
|
673
|
-
fleetId: placement.fleetId,
|
|
674
|
-
machineId: placement.machineId,
|
|
675
|
-
groupId: args.roundIndex,
|
|
676
|
-
parentIndex: args.parentIndex
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
const message = spec.taskToPrompt(args.item.task);
|
|
680
|
-
const events = [];
|
|
681
|
-
for await (const event of box.streamPrompt(message, { signal: args.signal })) {
|
|
682
|
-
events.push(event);
|
|
683
|
-
const llmCall = extractLlmCallEvent(event, slot.agentRunName);
|
|
684
|
-
if (llmCall) {
|
|
685
|
-
slot.costUsd += llmCall.costUsd ?? 0;
|
|
686
|
-
slot.tokenUsage.input += llmCall.tokensIn ?? 0;
|
|
687
|
-
slot.tokenUsage.output += llmCall.tokensOut ?? 0;
|
|
688
|
-
args.ctx.runHandle?.observe(llmCall);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
slot.events = events;
|
|
692
|
-
slot.output = args.output.parse(events);
|
|
693
|
-
if (args.validator) {
|
|
694
|
-
slot.verdict = await args.validator.validate(slot.output, {
|
|
695
|
-
iteration: args.item.index,
|
|
696
|
-
signal: args.signal,
|
|
697
|
-
traceEmitter: args.ctx.traceEmitter
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
} catch (err) {
|
|
701
|
-
slot.error = err instanceof Error ? err : new Error(String(err));
|
|
702
|
-
} finally {
|
|
703
|
-
slot.endedAt = args.now();
|
|
704
|
-
await emitTrace(args.ctx.traceEmitter, {
|
|
705
|
-
kind: "loop.iteration.ended",
|
|
706
|
-
runId: args.runId,
|
|
707
|
-
timestamp: args.now(),
|
|
708
|
-
payload: {
|
|
709
|
-
iterationIndex: args.item.index,
|
|
710
|
-
agentRunName: slot.agentRunName,
|
|
711
|
-
outputHash: slot.output !== void 0 ? hashJson(slot.output) : void 0,
|
|
712
|
-
verdict: slot.verdict,
|
|
713
|
-
error: slot.error?.message,
|
|
714
|
-
costUsd: slot.costUsd,
|
|
715
|
-
durationMs: slot.endedAt - slot.startedAt,
|
|
716
|
-
tokenUsage: slot.tokenUsage.input || slot.tokenUsage.output ? { ...slot.tokenUsage } : void 0,
|
|
717
|
-
groupId: args.roundIndex,
|
|
718
|
-
parentIndex: args.parentIndex,
|
|
719
|
-
outputPreview: slot.output !== void 0 ? previewOutput(slot.output) : void 0
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
if (args.collectBox && box) args.collectBox(box);
|
|
723
|
-
else await destroySandboxSafe(box);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
async function destroySandboxSafe(box) {
|
|
727
|
-
if (!box || typeof box.delete !== "function") return;
|
|
728
|
-
try {
|
|
729
|
-
await box.delete();
|
|
730
|
-
} catch {
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
function branchPoint(iterations) {
|
|
734
|
-
if (iterations.length === 0) return void 0;
|
|
735
|
-
let best = iterations.length - 1;
|
|
736
|
-
let bestScore = -Infinity;
|
|
737
|
-
for (const iter of iterations) {
|
|
738
|
-
if (iter.verdict?.valid !== true) continue;
|
|
739
|
-
const score = iter.verdict.score ?? 0;
|
|
740
|
-
if (score > bestScore) {
|
|
741
|
-
bestScore = score;
|
|
742
|
-
best = iter.index;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
return best;
|
|
746
|
-
}
|
|
747
|
-
function previewOutput(output) {
|
|
748
|
-
let s;
|
|
749
|
-
try {
|
|
750
|
-
s = typeof output === "string" ? output : JSON.stringify(output) ?? String(output);
|
|
751
|
-
} catch {
|
|
752
|
-
s = String(output);
|
|
753
|
-
}
|
|
754
|
-
return s.length > 280 ? `${s.slice(0, 280)}\u2026` : s;
|
|
755
|
-
}
|
|
756
|
-
function describeSandboxPlacement(client, box) {
|
|
757
|
-
if (typeof client.describePlacement === "function") {
|
|
758
|
-
try {
|
|
759
|
-
const result = client.describePlacement(box);
|
|
760
|
-
if (result && typeof result === "object" && (result.kind === "sibling" || result.kind === "fleet")) {
|
|
761
|
-
return {
|
|
762
|
-
kind: result.kind,
|
|
763
|
-
sandboxId: result.sandboxId ?? readSandboxId(box),
|
|
764
|
-
fleetId: result.fleetId,
|
|
765
|
-
machineId: result.machineId
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
} catch {
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
return { kind: "sibling", sandboxId: readSandboxId(box) };
|
|
772
|
-
}
|
|
773
|
-
function readSandboxId(box) {
|
|
774
|
-
const raw = box.id;
|
|
775
|
-
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
776
|
-
}
|
|
777
|
-
async function createSandboxForSpec(client, spec, signal) {
|
|
778
|
-
const overrides = spec.sandboxOverrides ?? {};
|
|
779
|
-
const overrideBackend = overrides.backend;
|
|
780
|
-
const opts = {
|
|
781
|
-
...overrides,
|
|
782
|
-
backend: {
|
|
783
|
-
type: overrideBackend?.type ?? inferBackendType(spec.profile),
|
|
784
|
-
profile: spec.profile,
|
|
785
|
-
...overrideBackend?.model ? { model: overrideBackend.model } : {},
|
|
786
|
-
...overrideBackend?.server ? { server: overrideBackend.server } : {}
|
|
787
|
-
}
|
|
788
|
-
};
|
|
789
|
-
if (signal.aborted) throwAbort();
|
|
790
|
-
return acquireSandbox(client, opts, { signal });
|
|
791
|
-
}
|
|
792
|
-
function inferBackendType(profile) {
|
|
793
|
-
const explicit = profile.metadata?.backendType;
|
|
794
|
-
if (typeof explicit === "string") return explicit;
|
|
795
|
-
return "opencode";
|
|
796
|
-
}
|
|
797
|
-
function finalize(args) {
|
|
798
|
-
const winner = (args.options.selectWinner ?? defaultSelectWinner)(args.iterations);
|
|
799
|
-
const costUsd = args.iterations.reduce((sum, iter) => sum + (iter.costUsd || 0), 0);
|
|
800
|
-
const tokenUsage = args.iterations.reduce(
|
|
801
|
-
(acc, iter) => {
|
|
802
|
-
acc.input += iter.tokenUsage?.input ?? 0;
|
|
803
|
-
acc.output += iter.tokenUsage?.output ?? 0;
|
|
804
|
-
return acc;
|
|
805
|
-
},
|
|
806
|
-
{ input: 0, output: 0 }
|
|
807
|
-
);
|
|
808
|
-
const result = {
|
|
809
|
-
decision: args.decision,
|
|
810
|
-
iterations: args.iterations,
|
|
811
|
-
winner,
|
|
812
|
-
durationMs: args.now() - args.startMs,
|
|
813
|
-
costUsd,
|
|
814
|
-
tokenUsage
|
|
815
|
-
};
|
|
816
|
-
void emitTrace(args.options.ctx.traceEmitter, {
|
|
817
|
-
kind: "loop.ended",
|
|
818
|
-
runId: args.runId,
|
|
819
|
-
timestamp: args.now(),
|
|
820
|
-
payload: {
|
|
821
|
-
winnerIterationIndex: winner?.iterationIndex,
|
|
822
|
-
totalCostUsd: costUsd,
|
|
823
|
-
durationMs: result.durationMs,
|
|
824
|
-
iterations: args.iterations.length
|
|
825
|
-
}
|
|
826
|
-
});
|
|
827
|
-
return result;
|
|
828
|
-
}
|
|
829
|
-
function defaultSelectWinner(iterations) {
|
|
830
|
-
const candidates = iterations.filter((iter) => iter.output !== void 0 && !iter.error);
|
|
831
|
-
if (candidates.length === 0) return void 0;
|
|
832
|
-
const valid = candidates.filter((iter) => iter.verdict?.valid === true);
|
|
833
|
-
const pool = valid.length > 0 ? valid : candidates;
|
|
834
|
-
const sorted = [...pool].sort(
|
|
835
|
-
(a, b) => (b.verdict?.score ?? 0) - (a.verdict?.score ?? 0) || a.index - b.index
|
|
836
|
-
);
|
|
837
|
-
const top = sorted[0];
|
|
838
|
-
if (!top || top.output === void 0) return void 0;
|
|
839
|
-
return {
|
|
840
|
-
task: top.task,
|
|
841
|
-
output: top.output,
|
|
842
|
-
verdict: top.verdict,
|
|
843
|
-
iterationIndex: top.index,
|
|
844
|
-
agentRunName: top.agentRunName
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
function resolveAgentRuns(options) {
|
|
848
|
-
if (options.agentRun && options.agentRuns) {
|
|
849
|
-
throw new ValidationError("runLoop: pass exactly one of `agentRun` or `agentRuns`");
|
|
850
|
-
}
|
|
851
|
-
if (options.agentRun) return [options.agentRun];
|
|
852
|
-
if (options.agentRuns && options.agentRuns.length > 0) return options.agentRuns;
|
|
853
|
-
throw new ValidationError("runLoop: `agentRun` or non-empty `agentRuns` is required");
|
|
854
|
-
}
|
|
855
|
-
function isTerminalDecision(decision) {
|
|
856
|
-
return decision === "stop" || decision === "pick-winner" || decision === "fail" || decision === "done";
|
|
857
|
-
}
|
|
858
|
-
function serializeDecision(decision) {
|
|
859
|
-
if (typeof decision === "string") return decision;
|
|
860
|
-
if (decision === null || decision === void 0) return "null";
|
|
861
|
-
try {
|
|
862
|
-
return JSON.stringify(decision);
|
|
863
|
-
} catch {
|
|
864
|
-
return String(decision);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
async function emitTrace(emitter, event) {
|
|
868
|
-
if (!emitter) return;
|
|
869
|
-
await emitter.emit(event);
|
|
870
|
-
}
|
|
871
|
-
function randomSuffix2(len = 8) {
|
|
872
|
-
return Math.random().toString(36).slice(2, 2 + len);
|
|
873
|
-
}
|
|
874
|
-
function throwAbort() {
|
|
875
|
-
const err = new Error("aborted");
|
|
876
|
-
err.name = "AbortError";
|
|
877
|
-
throw err;
|
|
878
|
-
}
|
|
879
|
-
function hashJson(value) {
|
|
880
|
-
let str;
|
|
881
|
-
try {
|
|
882
|
-
str = JSON.stringify(value) ?? String(value);
|
|
883
|
-
} catch {
|
|
884
|
-
str = String(value);
|
|
885
|
-
}
|
|
886
|
-
let h = 2166136261;
|
|
887
|
-
for (let i = 0; i < str.length; i += 1) {
|
|
888
|
-
h ^= str.charCodeAt(i);
|
|
889
|
-
h = Math.imul(h, 16777619);
|
|
890
|
-
}
|
|
891
|
-
return (h >>> 0).toString(16).padStart(8, "0");
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
// src/loops/loop-dispatch.ts
|
|
895
|
-
function campaignTraceToLoopEmitter(trace) {
|
|
896
|
-
return {
|
|
897
|
-
emit(event) {
|
|
898
|
-
trace.span(event.kind, { runId: event.runId, timestamp: event.timestamp, ...event.payload }).end();
|
|
899
|
-
}
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
async function runLoopForCell(opts, scenario, profile, ctx) {
|
|
903
|
-
const loopOptions = opts.toLoopOptions(scenario, profile);
|
|
904
|
-
const result = await runLoop({
|
|
905
|
-
...loopOptions,
|
|
906
|
-
ctx: {
|
|
907
|
-
sandboxClient: opts.sandboxClient,
|
|
908
|
-
signal: ctx.signal,
|
|
909
|
-
traceEmitter: opts.forwardTrace === false ? void 0 : campaignTraceToLoopEmitter(ctx.trace)
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
reportLoopUsage(ctx.cost, result, opts.costSource ?? "loop");
|
|
913
|
-
const toArtifact = opts.toArtifact ?? ((r) => r.winner?.output);
|
|
914
|
-
return toArtifact(result);
|
|
915
|
-
}
|
|
916
|
-
function loopDispatch(opts) {
|
|
917
|
-
return (profile, scenario, ctx) => runLoopForCell(opts, scenario, profile, ctx);
|
|
918
|
-
}
|
|
919
|
-
function loopCampaignDispatch(opts) {
|
|
920
|
-
const profileSentinel = { id: "loop-campaign", model: "n/a@loop-campaign" };
|
|
921
|
-
const profiled = {
|
|
922
|
-
...opts,
|
|
923
|
-
toLoopOptions: (scenario) => opts.toLoopOptions(scenario)
|
|
924
|
-
};
|
|
925
|
-
return (scenario, ctx) => runLoopForCell(profiled, scenario, profileSentinel, ctx);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
export {
|
|
929
|
-
createDynamicDriver,
|
|
930
|
-
summarizeHistory,
|
|
931
|
-
blind,
|
|
932
|
-
PROMPT_PLANNERS,
|
|
933
|
-
resolvePlanner,
|
|
934
|
-
createRefineDriver,
|
|
935
|
-
refineWinnerIndex,
|
|
936
|
-
createSandboxPlanner,
|
|
937
|
-
reportLoopUsage,
|
|
938
|
-
acquireSandbox,
|
|
939
|
-
extractLlmCallEvent,
|
|
940
|
-
mapSandboxEvent,
|
|
941
|
-
runLoop,
|
|
942
|
-
describeSandboxPlacement,
|
|
943
|
-
createSandboxForSpec,
|
|
944
|
-
loopDispatch,
|
|
945
|
-
loopCampaignDispatch
|
|
946
|
-
};
|
|
947
|
-
//# sourceMappingURL=chunk-S7JXV32P.js.map
|