@tangle-network/agent-runtime 0.19.0 → 0.20.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 +110 -0
- package/dist/agent.d.ts +1 -1
- package/dist/chunk-LPPM7EGS.js +1141 -0
- package/dist/chunk-LPPM7EGS.js.map +1 -0
- package/dist/chunk-VFUEE6DF.js +373 -0
- package/dist/chunk-VFUEE6DF.js.map +1 -0
- package/dist/chunk-Z5LKAYAS.js +248 -0
- package/dist/chunk-Z5LKAYAS.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/loops.d.ts +4 -4
- package/dist/loops.js +6 -366
- package/dist/loops.js.map +1 -1
- package/dist/mcp/bin.d.ts +1 -0
- package/dist/mcp/bin.js +150 -0
- package/dist/mcp/bin.js.map +1 -0
- package/dist/mcp/index.d.ts +827 -0
- package/dist/mcp/index.js +74 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/profiles.d.ts +3 -3
- package/dist/profiles.js +5 -240
- package/dist/profiles.js.map +1 -1
- package/dist/{runtime-run-4pbY3Jq5.d.ts → runtime-run-B2j-hvBj.d.ts} +1 -1
- package/dist/{types-EKcAHfxI.d.ts → types-Bx-tArkc.d.ts} +1 -1
- package/dist/{types-DlyPgeI0.d.ts → types-DvJIha6w.d.ts} +1 -1
- package/package.json +15 -1
package/dist/loops.js
CHANGED
|
@@ -1,374 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRefineDriver,
|
|
3
|
+
refineWinnerIndex,
|
|
4
|
+
runLoop
|
|
5
|
+
} from "./chunk-VFUEE6DF.js";
|
|
1
6
|
import {
|
|
2
7
|
createFanoutVoteDriver,
|
|
3
8
|
scoreFanoutVoteIterations
|
|
4
9
|
} from "./chunk-XLWPTPRP.js";
|
|
5
|
-
import
|
|
6
|
-
ValidationError
|
|
7
|
-
} from "./chunk-RZAOYKCO.js";
|
|
10
|
+
import "./chunk-RZAOYKCO.js";
|
|
8
11
|
import "./chunk-DGUM43GV.js";
|
|
9
|
-
|
|
10
|
-
// src/loops/drivers/refine.ts
|
|
11
|
-
function createRefineDriver(options = {}) {
|
|
12
|
-
const maxIterations = options.maxIterations ?? 5;
|
|
13
|
-
if (!Number.isFinite(maxIterations) || maxIterations <= 0) {
|
|
14
|
-
throw new ValidationError("createRefineDriver: maxIterations must be > 0");
|
|
15
|
-
}
|
|
16
|
-
const refineTask = options.refineTask;
|
|
17
|
-
return {
|
|
18
|
-
name: options.name ?? "refine",
|
|
19
|
-
async plan(task, history) {
|
|
20
|
-
if (history.length >= maxIterations) return [];
|
|
21
|
-
if (history.length === 0) return [task];
|
|
22
|
-
const prior = history.at(-1);
|
|
23
|
-
if (!prior) return [task];
|
|
24
|
-
if (prior.verdict?.valid === true) return [];
|
|
25
|
-
if (!refineTask || !prior.verdict) return [prior.task];
|
|
26
|
-
return [refineTask(prior.task, prior.verdict)];
|
|
27
|
-
},
|
|
28
|
-
decide(history) {
|
|
29
|
-
const last = history.at(-1);
|
|
30
|
-
if (!last) return "continue";
|
|
31
|
-
if (last.verdict?.valid === true) return "stop";
|
|
32
|
-
if (history.length >= maxIterations) return "stop";
|
|
33
|
-
return "continue";
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function refineWinnerIndex(iterations) {
|
|
38
|
-
for (let i = iterations.length - 1; i >= 0; i -= 1) {
|
|
39
|
-
if (iterations[i]?.verdict?.valid) return i;
|
|
40
|
-
}
|
|
41
|
-
return iterations.length > 0 ? iterations.length - 1 : void 0;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// src/loops/run-loop.ts
|
|
45
|
-
var DEFAULT_MAX_ITERATIONS = 10;
|
|
46
|
-
var DEFAULT_MAX_CONCURRENCY = 4;
|
|
47
|
-
async function runLoop(options) {
|
|
48
|
-
const specs = resolveAgentRuns(options);
|
|
49
|
-
const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
50
|
-
if (!Number.isFinite(maxIterations) || maxIterations <= 0) {
|
|
51
|
-
throw new ValidationError("runLoop: maxIterations must be > 0");
|
|
52
|
-
}
|
|
53
|
-
const maxConcurrency = options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
|
|
54
|
-
if (!Number.isFinite(maxConcurrency) || maxConcurrency <= 0) {
|
|
55
|
-
throw new ValidationError("runLoop: maxConcurrency must be > 0");
|
|
56
|
-
}
|
|
57
|
-
if (!options.ctx?.sandboxClient || typeof options.ctx.sandboxClient.create !== "function") {
|
|
58
|
-
throw new ValidationError("runLoop: ctx.sandboxClient.create is required");
|
|
59
|
-
}
|
|
60
|
-
const now = options.now ?? Date.now;
|
|
61
|
-
const runId = options.runId ?? `loop-${randomSuffix()}`;
|
|
62
|
-
const loopStart = now();
|
|
63
|
-
const driverName = options.driver.name ?? "driver";
|
|
64
|
-
const iterations = [];
|
|
65
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
66
|
-
kind: "loop.started",
|
|
67
|
-
runId,
|
|
68
|
-
timestamp: now(),
|
|
69
|
-
payload: {
|
|
70
|
-
driver: driverName,
|
|
71
|
-
agentRunNames: specs.map((spec) => spec.name ?? spec.profile.name ?? "agent"),
|
|
72
|
-
maxIterations,
|
|
73
|
-
maxConcurrency
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
const controller = new AbortController();
|
|
77
|
-
const onOuterAbort = () => controller.abort();
|
|
78
|
-
if (options.ctx.signal) {
|
|
79
|
-
if (options.ctx.signal.aborted) controller.abort();
|
|
80
|
-
else options.ctx.signal.addEventListener("abort", onOuterAbort, { once: true });
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
while (iterations.length < maxIterations) {
|
|
84
|
-
if (controller.signal.aborted) throwAbort();
|
|
85
|
-
const planned = await options.driver.plan(options.task, iterations);
|
|
86
|
-
if (planned.length === 0) break;
|
|
87
|
-
const remaining = maxIterations - iterations.length;
|
|
88
|
-
const slice = planned.slice(0, remaining);
|
|
89
|
-
const baseIndex = iterations.length;
|
|
90
|
-
for (let i = 0; i < slice.length; i += 1) {
|
|
91
|
-
const spec = specs[(baseIndex + i) % specs.length];
|
|
92
|
-
iterations.push({
|
|
93
|
-
index: baseIndex + i,
|
|
94
|
-
task: slice[i],
|
|
95
|
-
agentRunName: spec.name ?? spec.profile.name ?? "agent",
|
|
96
|
-
events: [],
|
|
97
|
-
startedAt: now(),
|
|
98
|
-
endedAt: 0,
|
|
99
|
-
costUsd: 0
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
await runBatch({
|
|
103
|
-
slice,
|
|
104
|
-
baseIndex,
|
|
105
|
-
iterations,
|
|
106
|
-
specs,
|
|
107
|
-
output: options.output,
|
|
108
|
-
validator: options.validator,
|
|
109
|
-
maxConcurrency,
|
|
110
|
-
signal: controller.signal,
|
|
111
|
-
ctx: options.ctx,
|
|
112
|
-
runId,
|
|
113
|
-
now
|
|
114
|
-
});
|
|
115
|
-
if (controller.signal.aborted) throwAbort();
|
|
116
|
-
const decision2 = await options.driver.decide(iterations);
|
|
117
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
118
|
-
kind: "loop.decision",
|
|
119
|
-
runId,
|
|
120
|
-
timestamp: now(),
|
|
121
|
-
payload: { decision: serializeDecision(decision2), historyLength: iterations.length }
|
|
122
|
-
});
|
|
123
|
-
if (isTerminalDecision(decision2)) {
|
|
124
|
-
return finalize({
|
|
125
|
-
options,
|
|
126
|
-
decision: decision2,
|
|
127
|
-
iterations,
|
|
128
|
-
startMs: loopStart,
|
|
129
|
-
now,
|
|
130
|
-
runId
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (iterations.length >= maxIterations) {
|
|
135
|
-
const decision2 = await options.driver.decide(iterations);
|
|
136
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
137
|
-
kind: "loop.decision",
|
|
138
|
-
runId,
|
|
139
|
-
timestamp: now(),
|
|
140
|
-
payload: { decision: serializeDecision(decision2), historyLength: iterations.length }
|
|
141
|
-
});
|
|
142
|
-
return finalize({ options, decision: decision2, iterations, startMs: loopStart, now, runId });
|
|
143
|
-
}
|
|
144
|
-
const decision = await options.driver.decide(iterations);
|
|
145
|
-
await emitTrace(options.ctx.traceEmitter, {
|
|
146
|
-
kind: "loop.decision",
|
|
147
|
-
runId,
|
|
148
|
-
timestamp: now(),
|
|
149
|
-
payload: { decision: serializeDecision(decision), historyLength: iterations.length }
|
|
150
|
-
});
|
|
151
|
-
return finalize({ options, decision, iterations, startMs: loopStart, now, runId });
|
|
152
|
-
} finally {
|
|
153
|
-
if (options.ctx.signal) options.ctx.signal.removeEventListener("abort", onOuterAbort);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
async function runBatch(args) {
|
|
157
|
-
const queue = args.slice.map((task, offset) => ({ task, index: args.baseIndex + offset }));
|
|
158
|
-
const inflight = /* @__PURE__ */ new Set();
|
|
159
|
-
while (queue.length > 0 || inflight.size > 0) {
|
|
160
|
-
while (inflight.size < args.maxConcurrency && queue.length > 0) {
|
|
161
|
-
const item = queue.shift();
|
|
162
|
-
const p = executeIteration({ ...args, item }).finally(() => inflight.delete(p));
|
|
163
|
-
inflight.add(p);
|
|
164
|
-
}
|
|
165
|
-
if (inflight.size === 0) break;
|
|
166
|
-
await Promise.race(inflight);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
async function executeIteration(args) {
|
|
170
|
-
const slot = args.iterations[args.item.index];
|
|
171
|
-
if (!slot)
|
|
172
|
-
throw new ValidationError(`runLoop: missing iteration slot at index ${args.item.index}`);
|
|
173
|
-
const spec = args.specs[args.item.index % args.specs.length];
|
|
174
|
-
if (!spec) throw new ValidationError("runLoop: no AgentRunSpec available for iteration");
|
|
175
|
-
slot.startedAt = args.now();
|
|
176
|
-
slot.agentRunName = spec.name ?? spec.profile.name ?? "agent";
|
|
177
|
-
await emitTrace(args.ctx.traceEmitter, {
|
|
178
|
-
kind: "loop.iteration.started",
|
|
179
|
-
runId: args.runId,
|
|
180
|
-
timestamp: args.now(),
|
|
181
|
-
payload: {
|
|
182
|
-
iterationIndex: args.item.index,
|
|
183
|
-
agentRunName: slot.agentRunName,
|
|
184
|
-
taskHash: hashJson(args.item.task)
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
try {
|
|
188
|
-
const box = await createSandboxForSpec(args.ctx.sandboxClient, spec, args.signal);
|
|
189
|
-
const message = spec.taskToPrompt(args.item.task);
|
|
190
|
-
const events = [];
|
|
191
|
-
for await (const event of box.streamPrompt(message, { signal: args.signal })) {
|
|
192
|
-
events.push(event);
|
|
193
|
-
const llmCall = extractLlmCallEvent(event, slot.agentRunName);
|
|
194
|
-
if (llmCall) {
|
|
195
|
-
slot.costUsd += llmCall.costUsd ?? 0;
|
|
196
|
-
args.ctx.runHandle?.observe(llmCall);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
slot.events = events;
|
|
200
|
-
slot.output = args.output.parse(events);
|
|
201
|
-
if (args.validator) {
|
|
202
|
-
slot.verdict = await args.validator.validate(slot.output, {
|
|
203
|
-
iteration: args.item.index,
|
|
204
|
-
signal: args.signal
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
} catch (err) {
|
|
208
|
-
slot.error = err instanceof Error ? err : new Error(String(err));
|
|
209
|
-
} finally {
|
|
210
|
-
slot.endedAt = args.now();
|
|
211
|
-
await emitTrace(args.ctx.traceEmitter, {
|
|
212
|
-
kind: "loop.iteration.ended",
|
|
213
|
-
runId: args.runId,
|
|
214
|
-
timestamp: args.now(),
|
|
215
|
-
payload: {
|
|
216
|
-
iterationIndex: args.item.index,
|
|
217
|
-
agentRunName: slot.agentRunName,
|
|
218
|
-
outputHash: slot.output !== void 0 ? hashJson(slot.output) : void 0,
|
|
219
|
-
verdict: slot.verdict,
|
|
220
|
-
error: slot.error?.message,
|
|
221
|
-
costUsd: slot.costUsd,
|
|
222
|
-
durationMs: slot.endedAt - slot.startedAt
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
async function createSandboxForSpec(client, spec, signal) {
|
|
228
|
-
const overrides = spec.sandboxOverrides ?? {};
|
|
229
|
-
const overrideBackend = overrides.backend;
|
|
230
|
-
const opts = {
|
|
231
|
-
...overrides,
|
|
232
|
-
backend: {
|
|
233
|
-
type: overrideBackend?.type ?? inferBackendType(spec.profile),
|
|
234
|
-
profile: spec.profile,
|
|
235
|
-
...overrideBackend?.model ? { model: overrideBackend.model } : {},
|
|
236
|
-
...overrideBackend?.server ? { server: overrideBackend.server } : {}
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
if (signal.aborted) throwAbort();
|
|
240
|
-
return client.create(opts);
|
|
241
|
-
}
|
|
242
|
-
function inferBackendType(profile) {
|
|
243
|
-
const explicit = profile.metadata?.backendType;
|
|
244
|
-
if (typeof explicit === "string") return explicit;
|
|
245
|
-
return "opencode";
|
|
246
|
-
}
|
|
247
|
-
function finalize(args) {
|
|
248
|
-
const winner = (args.options.selectWinner ?? defaultSelectWinner)(args.iterations);
|
|
249
|
-
const costUsd = args.iterations.reduce((sum, iter) => sum + (iter.costUsd || 0), 0);
|
|
250
|
-
const result = {
|
|
251
|
-
decision: args.decision,
|
|
252
|
-
iterations: args.iterations,
|
|
253
|
-
winner,
|
|
254
|
-
durationMs: args.now() - args.startMs,
|
|
255
|
-
costUsd
|
|
256
|
-
};
|
|
257
|
-
void emitTrace(args.options.ctx.traceEmitter, {
|
|
258
|
-
kind: "loop.ended",
|
|
259
|
-
runId: args.runId,
|
|
260
|
-
timestamp: args.now(),
|
|
261
|
-
payload: {
|
|
262
|
-
winnerIterationIndex: winner?.iterationIndex,
|
|
263
|
-
totalCostUsd: costUsd,
|
|
264
|
-
durationMs: result.durationMs,
|
|
265
|
-
iterations: args.iterations.length
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
return result;
|
|
269
|
-
}
|
|
270
|
-
function defaultSelectWinner(iterations) {
|
|
271
|
-
const candidates = iterations.filter((iter) => iter.output !== void 0 && !iter.error);
|
|
272
|
-
if (candidates.length === 0) return void 0;
|
|
273
|
-
const valid = candidates.filter((iter) => iter.verdict?.valid === true);
|
|
274
|
-
const pool = valid.length > 0 ? valid : candidates;
|
|
275
|
-
const sorted = [...pool].sort(
|
|
276
|
-
(a, b) => (b.verdict?.score ?? 0) - (a.verdict?.score ?? 0) || a.index - b.index
|
|
277
|
-
);
|
|
278
|
-
const top = sorted[0];
|
|
279
|
-
if (!top || top.output === void 0) return void 0;
|
|
280
|
-
return {
|
|
281
|
-
task: top.task,
|
|
282
|
-
output: top.output,
|
|
283
|
-
verdict: top.verdict,
|
|
284
|
-
iterationIndex: top.index,
|
|
285
|
-
agentRunName: top.agentRunName
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
function resolveAgentRuns(options) {
|
|
289
|
-
if (options.agentRun && options.agentRuns) {
|
|
290
|
-
throw new ValidationError("runLoop: pass exactly one of `agentRun` or `agentRuns`");
|
|
291
|
-
}
|
|
292
|
-
if (options.agentRun) return [options.agentRun];
|
|
293
|
-
if (options.agentRuns && options.agentRuns.length > 0) return options.agentRuns;
|
|
294
|
-
throw new ValidationError("runLoop: `agentRun` or non-empty `agentRuns` is required");
|
|
295
|
-
}
|
|
296
|
-
function isTerminalDecision(decision) {
|
|
297
|
-
return decision === "stop" || decision === "pick-winner" || decision === "fail" || decision === "done";
|
|
298
|
-
}
|
|
299
|
-
function serializeDecision(decision) {
|
|
300
|
-
if (typeof decision === "string") return decision;
|
|
301
|
-
if (decision === null || decision === void 0) return "null";
|
|
302
|
-
try {
|
|
303
|
-
return JSON.stringify(decision);
|
|
304
|
-
} catch {
|
|
305
|
-
return String(decision);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
async function emitTrace(emitter, event) {
|
|
309
|
-
if (!emitter) return;
|
|
310
|
-
await emitter.emit(event);
|
|
311
|
-
}
|
|
312
|
-
function randomSuffix(len = 8) {
|
|
313
|
-
return Math.random().toString(36).slice(2, 2 + len);
|
|
314
|
-
}
|
|
315
|
-
function throwAbort() {
|
|
316
|
-
const err = new Error("aborted");
|
|
317
|
-
err.name = "AbortError";
|
|
318
|
-
throw err;
|
|
319
|
-
}
|
|
320
|
-
function extractLlmCallEvent(event, agentRunName) {
|
|
321
|
-
if (!event || typeof event !== "object") return void 0;
|
|
322
|
-
const type = String(event.type ?? "");
|
|
323
|
-
const data = event.data && typeof event.data === "object" ? event.data : {};
|
|
324
|
-
if (type === "llm_call" || type === "cost.usage" || type === "usage") {
|
|
325
|
-
return buildLlmCall(data, agentRunName);
|
|
326
|
-
}
|
|
327
|
-
if (type === "message.completed" || type === "result" || type === "final") {
|
|
328
|
-
const usage = data.usage;
|
|
329
|
-
if (!usage || typeof usage !== "object") return void 0;
|
|
330
|
-
return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName);
|
|
331
|
-
}
|
|
332
|
-
return void 0;
|
|
333
|
-
}
|
|
334
|
-
function buildLlmCall(data, agentRunName) {
|
|
335
|
-
const tokensIn = pickFiniteNumber(data, ["tokensIn", "inputTokens", "prompt_tokens"]);
|
|
336
|
-
const tokensOut = pickFiniteNumber(data, ["tokensOut", "outputTokens", "completion_tokens"]);
|
|
337
|
-
const costUsd = pickFiniteNumber(data, ["costUsd", "totalCostUsd", "cost_usd", "cost"]);
|
|
338
|
-
if (tokensIn === void 0 && tokensOut === void 0 && costUsd === void 0) {
|
|
339
|
-
return void 0;
|
|
340
|
-
}
|
|
341
|
-
const model = typeof data.model === "string" && data.model.length > 0 ? data.model : agentRunName;
|
|
342
|
-
const event = {
|
|
343
|
-
type: "llm_call",
|
|
344
|
-
model
|
|
345
|
-
};
|
|
346
|
-
if (tokensIn !== void 0) event.tokensIn = tokensIn;
|
|
347
|
-
if (tokensOut !== void 0) event.tokensOut = tokensOut;
|
|
348
|
-
if (costUsd !== void 0) event.costUsd = costUsd;
|
|
349
|
-
return event;
|
|
350
|
-
}
|
|
351
|
-
function pickFiniteNumber(data, keys) {
|
|
352
|
-
for (const key of keys) {
|
|
353
|
-
const value = data[key];
|
|
354
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
355
|
-
}
|
|
356
|
-
return void 0;
|
|
357
|
-
}
|
|
358
|
-
function hashJson(value) {
|
|
359
|
-
let str;
|
|
360
|
-
try {
|
|
361
|
-
str = JSON.stringify(value) ?? String(value);
|
|
362
|
-
} catch {
|
|
363
|
-
str = String(value);
|
|
364
|
-
}
|
|
365
|
-
let h = 2166136261;
|
|
366
|
-
for (let i = 0; i < str.length; i += 1) {
|
|
367
|
-
h ^= str.charCodeAt(i);
|
|
368
|
-
h = Math.imul(h, 16777619);
|
|
369
|
-
}
|
|
370
|
-
return (h >>> 0).toString(16).padStart(8, "0");
|
|
371
|
-
}
|
|
372
12
|
export {
|
|
373
13
|
createFanoutVoteDriver,
|
|
374
14
|
createRefineDriver,
|
package/dist/loops.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loops/drivers/refine.ts","../src/loops/run-loop.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Refine driver — single task per iteration, validator-gated.\n *\n * `plan` returns `[task]` (possibly transformed via `refineTask`) until the\n * prior verdict is valid OR the local cap is hit, then `[]`.\n * `decide` returns `'stop'` once the latest verdict is valid OR the cap is\n * reached. The kernel's `maxIterations` is an orthogonal safety cap;\n * whichever is lower wins.\n */\n\nimport { ValidationError } from '../../errors'\nimport type { DefaultVerdict, Driver, Iteration } from '../types'\n\nexport type RefineDecision = 'continue' | 'stop'\n\n/** @experimental */\nexport interface CreateRefineDriverOptions<Task> {\n /** Hard cap on iterations. Default 5. */\n maxIterations?: number\n /**\n * Optional task transform applied each round based on the prior verdict.\n * When omitted, the same task is replayed and the agent is expected to\n * inspect the sandbox session state for prior attempts.\n */\n refineTask?: (task: Task, prior: DefaultVerdict) => Task\n /** Stable identifier surfaced in trace events. Default `'refine'`. */\n name?: string\n}\n\n/** @experimental */\nexport function createRefineDriver<Task, Output>(\n options: CreateRefineDriverOptions<Task> = {},\n): Driver<Task, Output, RefineDecision> {\n const maxIterations = options.maxIterations ?? 5\n if (!Number.isFinite(maxIterations) || maxIterations <= 0) {\n throw new ValidationError('createRefineDriver: maxIterations must be > 0')\n }\n const refineTask = options.refineTask\n return {\n name: options.name ?? 'refine',\n async plan(task, history) {\n if (history.length >= maxIterations) return []\n if (history.length === 0) return [task]\n const prior = history.at(-1)\n if (!prior) return [task]\n if (prior.verdict?.valid === true) return []\n // Worker error: replay the same task so the agent can self-correct.\n // The driver has no signal beyond `verdict`; only the validator\n // controls \"good enough\".\n if (!refineTask || !prior.verdict) return [prior.task]\n return [refineTask(prior.task, prior.verdict)]\n },\n decide(history) {\n const last = history.at(-1)\n if (!last) return 'continue'\n if (last.verdict?.valid === true) return 'stop'\n if (history.length >= maxIterations) return 'stop'\n return 'continue'\n },\n }\n}\n\n/**\n * Test helper: select the last-valid iteration (or the last attempt if\n * none passed). Mirrors the kernel's default selector ordering for refine\n * topologies — the most recent successful attempt wins.\n *\n * @experimental\n */\nexport function refineWinnerIndex<Task, Output>(\n iterations: ReadonlyArray<Iteration<Task, Output>>,\n): number | undefined {\n for (let i = iterations.length - 1; i >= 0; i -= 1) {\n if (iterations[i]?.verdict?.valid) return i\n }\n return iterations.length > 0 ? iterations.length - 1 : undefined\n}\n","/**\n * @experimental\n *\n * `runLoop` — the topology-agnostic kernel built atop the sandbox SDK.\n *\n * Each iteration:\n * 1. `driver.plan(task, history)` → N tasks (1 = refine, N = fanout, 0 = stop)\n * 2. For each task (parallel, bounded by `maxConcurrency`):\n * a. round-robin an `AgentRunSpec` from `agentRuns`\n * b. `sandboxClient.create({ backend: { profile }, ...overrides })`\n * c. iterate `box.streamPrompt(taskToPrompt(task))` and collect events\n * 3. `output.parse(events)` → typed `Output`\n * 4. `validator?.validate(output)` → `DefaultVerdict`\n * 5. Append `Iteration` to history; emit `loop.iteration.ended`\n * 6. `driver.decide(history)` → if terminal, return result + winner\n *\n * The kernel owns: iteration accounting, per-iteration timing, error\n * capture, abort propagation, concurrency cap, cost aggregation, and trace\n * emission. The kernel does NOT own: what the agent runs (sandbox SDK +\n * profile), how outputs are decoded (output adapter), how outputs are\n * scored (validator), or topology (driver).\n */\n\nimport type {\n AgentProfile,\n CreateSandboxOptions,\n SandboxEvent,\n SandboxInstance,\n} from '@tangle-network/sandbox'\nimport { ValidationError } from '../errors'\nimport type { RuntimeStreamEvent } from '../types'\nimport type {\n AgentRunSpec,\n Driver,\n ExecCtx,\n Iteration,\n LoopResult,\n LoopSandboxClient,\n LoopTraceEmitter,\n LoopTraceEvent,\n LoopWinner,\n OutputAdapter,\n Validator,\n} from './types'\n\nconst DEFAULT_MAX_ITERATIONS = 10\nconst DEFAULT_MAX_CONCURRENCY = 4\n\n/** @experimental */\nexport interface RunLoopOptions<Task, Output, Decision> {\n driver: Driver<Task, Output, Decision>\n /**\n * Single agent spec — every iteration uses this profile. Mutually\n * exclusive with `agentRuns`.\n */\n agentRun?: AgentRunSpec<Task>\n /**\n * Multiple specs for heterogeneous fanout. The kernel round-robins\n * through them when the driver plans N tasks. Mutually exclusive with\n * `agentRun`.\n */\n agentRuns?: AgentRunSpec<Task>[]\n output: OutputAdapter<Output>\n validator?: Validator<Output>\n task: Task\n ctx: ExecCtx\n /** Default 10. Hard cap on total iterations across all `plan()` rounds. */\n maxIterations?: number\n /** Default 4. In-flight worker cap within a single `plan()` batch. */\n maxConcurrency?: number\n /**\n * Pre-allocated id for trace correlation. Default = `loop-${random}`.\n * Surfaces as `runId` on every emitted `LoopTraceEvent`.\n */\n runId?: string\n /**\n * Clock override; default `Date.now`. Deterministic tests pass a\n * monotonic counter to stabilize iteration timing fields.\n */\n now?: () => number\n /**\n * Override the default winner selector (highest-valid-score, ties broken\n * by earliest iteration).\n */\n selectWinner?: (iterations: Iteration<Task, Output>[]) => LoopWinner<Task, Output> | undefined\n}\n\n/** @experimental */\nexport async function runLoop<Task, Output, Decision>(\n options: RunLoopOptions<Task, Output, Decision>,\n): Promise<LoopResult<Task, Output, Decision>> {\n const specs = resolveAgentRuns(options)\n const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS\n if (!Number.isFinite(maxIterations) || maxIterations <= 0) {\n throw new ValidationError('runLoop: maxIterations must be > 0')\n }\n const maxConcurrency = options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY\n if (!Number.isFinite(maxConcurrency) || maxConcurrency <= 0) {\n throw new ValidationError('runLoop: maxConcurrency must be > 0')\n }\n if (!options.ctx?.sandboxClient || typeof options.ctx.sandboxClient.create !== 'function') {\n throw new ValidationError('runLoop: ctx.sandboxClient.create is required')\n }\n const now = options.now ?? Date.now\n const runId = options.runId ?? `loop-${randomSuffix()}`\n const loopStart = now()\n const driverName = options.driver.name ?? 'driver'\n const iterations: Iteration<Task, Output>[] = []\n\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.started',\n runId,\n timestamp: now(),\n payload: {\n driver: driverName,\n agentRunNames: specs.map((spec) => spec.name ?? spec.profile.name ?? 'agent'),\n maxIterations,\n maxConcurrency,\n },\n })\n\n const controller = new AbortController()\n const onOuterAbort = () => controller.abort()\n if (options.ctx.signal) {\n if (options.ctx.signal.aborted) controller.abort()\n else options.ctx.signal.addEventListener('abort', onOuterAbort, { once: true })\n }\n\n try {\n while (iterations.length < maxIterations) {\n if (controller.signal.aborted) throwAbort()\n const planned = await options.driver.plan(options.task, iterations)\n if (planned.length === 0) break\n\n const remaining = maxIterations - iterations.length\n const slice = planned.slice(0, remaining)\n const baseIndex = iterations.length\n // Reserve slots up front so concurrent workers may mutate by index.\n for (let i = 0; i < slice.length; i += 1) {\n const spec = specs[(baseIndex + i) % specs.length]!\n iterations.push({\n index: baseIndex + i,\n task: slice[i] as Task,\n agentRunName: spec.name ?? spec.profile.name ?? 'agent',\n events: [],\n startedAt: now(),\n endedAt: 0,\n costUsd: 0,\n })\n }\n\n await runBatch({\n slice,\n baseIndex,\n iterations,\n specs,\n output: options.output,\n validator: options.validator,\n maxConcurrency,\n signal: controller.signal,\n ctx: options.ctx,\n runId,\n now,\n })\n\n if (controller.signal.aborted) throwAbort()\n\n const decision = await options.driver.decide(iterations)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.decision',\n runId,\n timestamp: now(),\n payload: { decision: serializeDecision(decision), historyLength: iterations.length },\n })\n if (isTerminalDecision(decision)) {\n return finalize({\n options,\n decision,\n iterations,\n startMs: loopStart,\n now,\n runId,\n })\n }\n }\n\n if (iterations.length >= maxIterations) {\n // Cap reached without a terminal decision — ask the driver one more time\n // for its final state, then close out.\n const decision = await options.driver.decide(iterations)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.decision',\n runId,\n timestamp: now(),\n payload: { decision: serializeDecision(decision), historyLength: iterations.length },\n })\n return finalize({ options, decision, iterations, startMs: loopStart, now, runId })\n }\n // `plan()` returned `[]` before `decide()` reached a terminal state.\n const decision = await options.driver.decide(iterations)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.decision',\n runId,\n timestamp: now(),\n payload: { decision: serializeDecision(decision), historyLength: iterations.length },\n })\n return finalize({ options, decision, iterations, startMs: loopStart, now, runId })\n } finally {\n if (options.ctx.signal) options.ctx.signal.removeEventListener('abort', onOuterAbort)\n }\n}\n\ninterface RunBatchArgs<Task, Output> {\n slice: Task[]\n baseIndex: number\n iterations: Iteration<Task, Output>[]\n specs: AgentRunSpec<Task>[]\n output: OutputAdapter<Output>\n validator: Validator<Output> | undefined\n maxConcurrency: number\n signal: AbortSignal\n ctx: ExecCtx\n runId: string\n now: () => number\n}\n\nasync function runBatch<Task, Output>(args: RunBatchArgs<Task, Output>) {\n const queue = args.slice.map((task, offset) => ({ task, index: args.baseIndex + offset }))\n const inflight = new Set<Promise<void>>()\n while (queue.length > 0 || inflight.size > 0) {\n while (inflight.size < args.maxConcurrency && queue.length > 0) {\n const item = queue.shift()!\n const p = executeIteration({ ...args, item }).finally(() => inflight.delete(p))\n inflight.add(p)\n }\n if (inflight.size === 0) break\n await Promise.race(inflight)\n }\n}\n\ninterface ExecuteIterationArgs<Task, Output> extends RunBatchArgs<Task, Output> {\n item: { task: Task; index: number }\n}\n\nasync function executeIteration<Task, Output>(args: ExecuteIterationArgs<Task, Output>) {\n const slot = args.iterations[args.item.index]\n if (!slot)\n throw new ValidationError(`runLoop: missing iteration slot at index ${args.item.index}`)\n const spec = args.specs[args.item.index % args.specs.length]\n if (!spec) throw new ValidationError('runLoop: no AgentRunSpec available for iteration')\n slot.startedAt = args.now()\n slot.agentRunName = spec.name ?? spec.profile.name ?? 'agent'\n\n await emitTrace(args.ctx.traceEmitter, {\n kind: 'loop.iteration.started',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n iterationIndex: args.item.index,\n agentRunName: slot.agentRunName,\n taskHash: hashJson(args.item.task),\n },\n })\n\n try {\n const box = await createSandboxForSpec(args.ctx.sandboxClient, spec, args.signal)\n const message = spec.taskToPrompt(args.item.task)\n const events: SandboxEvent[] = []\n for await (const event of box.streamPrompt(message, { signal: args.signal })) {\n events.push(event)\n const llmCall = extractLlmCallEvent(event, slot.agentRunName)\n if (llmCall) {\n slot.costUsd += llmCall.costUsd ?? 0\n args.ctx.runHandle?.observe(llmCall)\n }\n }\n slot.events = events\n slot.output = args.output.parse(events)\n if (args.validator) {\n slot.verdict = await args.validator.validate(slot.output, {\n iteration: args.item.index,\n signal: args.signal,\n })\n }\n } catch (err) {\n slot.error = err instanceof Error ? err : new Error(String(err))\n } finally {\n slot.endedAt = args.now()\n await emitTrace(args.ctx.traceEmitter, {\n kind: 'loop.iteration.ended',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n iterationIndex: args.item.index,\n agentRunName: slot.agentRunName,\n outputHash: slot.output !== undefined ? hashJson(slot.output) : undefined,\n verdict: slot.verdict,\n error: slot.error?.message,\n costUsd: slot.costUsd,\n durationMs: slot.endedAt - slot.startedAt,\n },\n })\n }\n}\n\nasync function createSandboxForSpec<Task>(\n client: LoopSandboxClient,\n spec: AgentRunSpec<Task>,\n signal: AbortSignal,\n): Promise<SandboxInstance> {\n const overrides = spec.sandboxOverrides ?? {}\n const overrideBackend = overrides.backend\n const opts: CreateSandboxOptions = {\n ...overrides,\n backend: {\n type: overrideBackend?.type ?? inferBackendType(spec.profile),\n profile: spec.profile satisfies AgentProfile,\n ...(overrideBackend?.model ? { model: overrideBackend.model } : {}),\n ...(overrideBackend?.server ? { server: overrideBackend.server } : {}),\n },\n }\n // Cooperative cancellation: if the abort signal fires while .create is\n // pending, the promise itself is not abortable but the inflight prompt is.\n if (signal.aborted) throwAbort()\n return client.create(opts)\n}\n\nfunction inferBackendType(\n profile: AgentProfile,\n): CreateSandboxOptions['backend'] extends infer B\n ? B extends { type: infer T }\n ? T\n : never\n : never {\n // The sandbox SDK accepts profile-driven backend selection by name. When the\n // profile has no explicit hint we fall through to the SDK's default\n // ('opencode' on the platform side). Returning a literal here would lie\n // about provenance — let the SDK pick.\n type BackendType = NonNullable<CreateSandboxOptions['backend']>['type']\n const explicit = profile.metadata?.backendType\n if (typeof explicit === 'string') return explicit as BackendType\n return 'opencode' as BackendType\n}\n\ninterface FinalizeArgs<Task, Output, Decision> {\n options: RunLoopOptions<Task, Output, Decision>\n decision: Decision\n iterations: Iteration<Task, Output>[]\n startMs: number\n now: () => number\n runId: string\n}\n\nfunction finalize<Task, Output, Decision>(\n args: FinalizeArgs<Task, Output, Decision>,\n): LoopResult<Task, Output, Decision> {\n const winner = (args.options.selectWinner ?? defaultSelectWinner)(args.iterations)\n const costUsd = args.iterations.reduce((sum, iter) => sum + (iter.costUsd || 0), 0)\n const result: LoopResult<Task, Output, Decision> = {\n decision: args.decision,\n iterations: args.iterations,\n winner,\n durationMs: args.now() - args.startMs,\n costUsd,\n }\n void emitTrace(args.options.ctx.traceEmitter, {\n kind: 'loop.ended',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n winnerIterationIndex: winner?.iterationIndex,\n totalCostUsd: costUsd,\n durationMs: result.durationMs,\n iterations: args.iterations.length,\n },\n })\n return result\n}\n\nfunction defaultSelectWinner<Task, Output>(\n iterations: Iteration<Task, Output>[],\n): LoopWinner<Task, Output> | undefined {\n const candidates = iterations.filter((iter) => iter.output !== undefined && !iter.error)\n if (candidates.length === 0) return undefined\n const valid = candidates.filter((iter) => iter.verdict?.valid === true)\n const pool = valid.length > 0 ? valid : candidates\n const sorted = [...pool].sort(\n (a, b) => (b.verdict?.score ?? 0) - (a.verdict?.score ?? 0) || a.index - b.index,\n )\n const top = sorted[0]\n if (!top || top.output === undefined) return undefined\n return {\n task: top.task,\n output: top.output,\n verdict: top.verdict,\n iterationIndex: top.index,\n agentRunName: top.agentRunName,\n }\n}\n\nfunction resolveAgentRuns<Task, Output, Decision>(\n options: RunLoopOptions<Task, Output, Decision>,\n): AgentRunSpec<Task>[] {\n if (options.agentRun && options.agentRuns) {\n throw new ValidationError('runLoop: pass exactly one of `agentRun` or `agentRuns`')\n }\n if (options.agentRun) return [options.agentRun]\n if (options.agentRuns && options.agentRuns.length > 0) return options.agentRuns\n throw new ValidationError('runLoop: `agentRun` or non-empty `agentRuns` is required')\n}\n\nfunction isTerminalDecision(decision: unknown): boolean {\n return (\n decision === 'stop' || decision === 'pick-winner' || decision === 'fail' || decision === 'done'\n )\n}\n\nfunction serializeDecision(decision: unknown): string {\n if (typeof decision === 'string') return decision\n if (decision === null || decision === undefined) return 'null'\n try {\n return JSON.stringify(decision)\n } catch {\n return String(decision)\n }\n}\n\nasync function emitTrace(\n emitter: LoopTraceEmitter | undefined,\n event: LoopTraceEvent,\n): Promise<void> {\n if (!emitter) return\n await emitter.emit(event)\n}\n\nfunction randomSuffix(len = 8): string {\n return Math.random()\n .toString(36)\n .slice(2, 2 + len)\n}\n\nfunction throwAbort(): never {\n const err = new Error('aborted')\n err.name = 'AbortError'\n throw err\n}\n\n/**\n * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when\n * the event carries usage/cost data. Returns `undefined` for non-cost events\n * so the kernel can iterate the full stream without branching.\n *\n * Sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`. The\n * canonical cost-carrying types observed in the wild:\n * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`\n * - `message.completed` / `result` — `data: { usage: { inputTokens,\n * outputTokens, totalCostUsd? } }`\n * - `cost.usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator\n * write so a sentinel `NaN` from a misbehaving backend cannot poison the\n * ledger.\n */\nfunction extractLlmCallEvent(\n event: SandboxEvent,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'llm_call' || type === 'cost.usage' || type === 'usage') {\n return buildLlmCall(data, agentRunName)\n }\n if (type === 'message.completed' || type === 'result' || type === 'final') {\n const usage = data.usage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName)\n }\n return undefined\n}\n\nfunction buildLlmCall(\n data: Record<string, unknown>,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n const tokensIn = pickFiniteNumber(data, ['tokensIn', 'inputTokens', 'prompt_tokens'])\n const tokensOut = pickFiniteNumber(data, ['tokensOut', 'outputTokens', 'completion_tokens'])\n const costUsd = pickFiniteNumber(data, ['costUsd', 'totalCostUsd', 'cost_usd', 'cost'])\n if (tokensIn === undefined && tokensOut === undefined && costUsd === undefined) {\n return undefined\n }\n const model = typeof data.model === 'string' && data.model.length > 0 ? data.model : agentRunName\n const event: RuntimeStreamEvent & { type: 'llm_call' } = {\n type: 'llm_call',\n model,\n }\n if (tokensIn !== undefined) event.tokensIn = tokensIn\n if (tokensOut !== undefined) event.tokensOut = tokensOut\n if (costUsd !== undefined) event.costUsd = costUsd\n return event\n}\n\nfunction pickFiniteNumber(data: Record<string, unknown>, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\n/**\n * Stable hash for the trace payload. Not cryptographic — only used so\n * downstream eval pipelines can group iterations whose task / output is the\n * same. Bare structural hash; non-JSON values stringify via their `toString`.\n */\nfunction hashJson(value: unknown): string {\n let str: string\n try {\n str = JSON.stringify(value) ?? String(value)\n } catch {\n str = String(value)\n }\n // FNV-1a 32-bit — branch-free, dependency-free, good enough for grouping.\n let h = 0x811c9dc5\n for (let i = 0; i < str.length; i += 1) {\n h ^= str.charCodeAt(i)\n h = Math.imul(h, 0x01000193)\n }\n return (h >>> 0).toString(16).padStart(8, '0')\n}\n"],"mappings":"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","names":["decision"]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/mcp/bin.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createDefaultCoderDelegate,
|
|
4
|
+
createMcpServer
|
|
5
|
+
} from "../chunk-LPPM7EGS.js";
|
|
6
|
+
import {
|
|
7
|
+
runLoop
|
|
8
|
+
} from "../chunk-VFUEE6DF.js";
|
|
9
|
+
import "../chunk-Z5LKAYAS.js";
|
|
10
|
+
import "../chunk-XLWPTPRP.js";
|
|
11
|
+
import "../chunk-RZAOYKCO.js";
|
|
12
|
+
import "../chunk-DGUM43GV.js";
|
|
13
|
+
|
|
14
|
+
// src/mcp/bin.ts
|
|
15
|
+
async function main() {
|
|
16
|
+
const fanoutHarnesses = parseHarnesses(process.env.MCP_CODER_FANOUT_HARNESSES);
|
|
17
|
+
const maxConcurrency = parseConcurrency(process.env.MCP_MAX_CONCURRENT_SANDBOXES);
|
|
18
|
+
const wantCoder = !process.env.MCP_DISABLE_CODER;
|
|
19
|
+
const wantResearcher = !process.env.MCP_DISABLE_RESEARCHER;
|
|
20
|
+
const needsSandbox = wantCoder || wantResearcher;
|
|
21
|
+
let sandboxClient;
|
|
22
|
+
if (needsSandbox) {
|
|
23
|
+
const apiKey = process.env.SANDBOX_API_KEY;
|
|
24
|
+
if (!apiKey && !process.env.AGENT_RUNTIME_MCP_ALLOW_NO_KEY) {
|
|
25
|
+
process.stderr.write(
|
|
26
|
+
"agent-runtime-mcp: SANDBOX_API_KEY is required (set AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 to run without it for diagnostics, or set MCP_DISABLE_CODER=1 MCP_DISABLE_RESEARCHER=1 to run the queue-only subset)\n"
|
|
27
|
+
);
|
|
28
|
+
process.exit(2);
|
|
29
|
+
}
|
|
30
|
+
sandboxClient = await loadSandboxClient(apiKey);
|
|
31
|
+
}
|
|
32
|
+
const coderDelegate = wantCoder && sandboxClient ? createDefaultCoderDelegate({
|
|
33
|
+
sandboxClient,
|
|
34
|
+
fanoutHarnesses,
|
|
35
|
+
maxConcurrency
|
|
36
|
+
}) : void 0;
|
|
37
|
+
const researcherDelegate = wantResearcher && sandboxClient ? await loadResearcherDelegate(sandboxClient, maxConcurrency) : void 0;
|
|
38
|
+
const server = createMcpServer({ coderDelegate, researcherDelegate });
|
|
39
|
+
process.on("SIGINT", () => {
|
|
40
|
+
server.stop();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
});
|
|
43
|
+
process.on("SIGTERM", () => {
|
|
44
|
+
server.stop();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
});
|
|
47
|
+
await server.serve();
|
|
48
|
+
}
|
|
49
|
+
async function loadSandboxClient(apiKey) {
|
|
50
|
+
const mod = await import("@tangle-network/sandbox").catch((err) => {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
`agent-runtime-mcp: failed to load @tangle-network/sandbox (${err.message}); install the peer dependency
|
|
53
|
+
`
|
|
54
|
+
);
|
|
55
|
+
process.exit(2);
|
|
56
|
+
});
|
|
57
|
+
const SandboxCtor = mod.Sandbox;
|
|
58
|
+
if (!SandboxCtor) {
|
|
59
|
+
process.stderr.write(
|
|
60
|
+
"agent-runtime-mcp: @tangle-network/sandbox does not export Sandbox; cannot construct client\n"
|
|
61
|
+
);
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
const baseUrl = process.env.SANDBOX_BASE_URL;
|
|
65
|
+
return new SandboxCtor({
|
|
66
|
+
apiKey,
|
|
67
|
+
...baseUrl ? { baseUrl } : {}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function loadResearcherDelegate(sandboxClient, maxConcurrency) {
|
|
71
|
+
const profilesSpecifier = "@tangle-network/agent-knowledge/profiles";
|
|
72
|
+
const mod = await import(profilesSpecifier).catch(() => void 0);
|
|
73
|
+
if (!mod) return void 0;
|
|
74
|
+
const fanoutFactory = mod.multiHarnessResearcherFanout;
|
|
75
|
+
const singleFactory = mod.researcherProfile;
|
|
76
|
+
if (!fanoutFactory || !singleFactory) return void 0;
|
|
77
|
+
return async (args, ctx) => {
|
|
78
|
+
const task = {
|
|
79
|
+
question: args.question,
|
|
80
|
+
knowledgeNamespace: args.namespace,
|
|
81
|
+
scope: args.scope,
|
|
82
|
+
sources: args.sources,
|
|
83
|
+
recencyWindow: args.config?.recencyWindow ? {
|
|
84
|
+
since: args.config.recencyWindow.since ? new Date(args.config.recencyWindow.since) : void 0,
|
|
85
|
+
until: args.config.recencyWindow.until ? new Date(args.config.recencyWindow.until) : void 0
|
|
86
|
+
} : void 0,
|
|
87
|
+
maxItems: args.config?.maxItems,
|
|
88
|
+
minConfidence: args.config?.minConfidence
|
|
89
|
+
};
|
|
90
|
+
const variants = Math.max(1, Math.trunc(args.variants ?? 1));
|
|
91
|
+
ctx.report({ iteration: 0, phase: "starting" });
|
|
92
|
+
if (variants <= 1) {
|
|
93
|
+
const preset = singleFactory({ task });
|
|
94
|
+
const result2 = await runLoop({
|
|
95
|
+
driver: {
|
|
96
|
+
name: "mcp-researcher-single",
|
|
97
|
+
async plan(t, history) {
|
|
98
|
+
return history.length === 0 ? [t] : [];
|
|
99
|
+
},
|
|
100
|
+
decide(history) {
|
|
101
|
+
return history.length > 0 ? "pick-winner" : "fail";
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
agentRun: preset.agentRunSpec,
|
|
105
|
+
output: preset.output,
|
|
106
|
+
validator: preset.validator,
|
|
107
|
+
task,
|
|
108
|
+
ctx: { sandboxClient, signal: ctx.signal },
|
|
109
|
+
maxIterations: 1,
|
|
110
|
+
maxConcurrency
|
|
111
|
+
});
|
|
112
|
+
const output2 = result2.winner?.output;
|
|
113
|
+
if (!output2) throw new Error("researcher delegate produced no winner");
|
|
114
|
+
ctx.report({ iteration: 1, phase: "completed" });
|
|
115
|
+
return output2;
|
|
116
|
+
}
|
|
117
|
+
const fanout = fanoutFactory({ task });
|
|
118
|
+
const result = await runLoop({
|
|
119
|
+
driver: fanout.driver,
|
|
120
|
+
agentRuns: fanout.agentRuns.slice(0, variants),
|
|
121
|
+
output: fanout.output,
|
|
122
|
+
validator: fanout.validator,
|
|
123
|
+
task,
|
|
124
|
+
ctx: { sandboxClient, signal: ctx.signal },
|
|
125
|
+
maxIterations: variants,
|
|
126
|
+
maxConcurrency: Math.min(maxConcurrency, variants)
|
|
127
|
+
});
|
|
128
|
+
const output = result.winner?.output;
|
|
129
|
+
if (!output) throw new Error("researcher delegate fanout produced no winner");
|
|
130
|
+
ctx.report({ iteration: result.iterations.length, phase: "completed" });
|
|
131
|
+
return output;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function parseHarnesses(raw) {
|
|
135
|
+
if (!raw) return void 0;
|
|
136
|
+
const list = raw.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
137
|
+
return list.length > 0 ? list : void 0;
|
|
138
|
+
}
|
|
139
|
+
function parseConcurrency(raw) {
|
|
140
|
+
if (!raw) return 4;
|
|
141
|
+
const n = Number(raw);
|
|
142
|
+
if (!Number.isFinite(n) || n < 1) return 4;
|
|
143
|
+
return Math.min(Math.trunc(n), 32);
|
|
144
|
+
}
|
|
145
|
+
main().catch((err) => {
|
|
146
|
+
process.stderr.write(`agent-runtime-mcp: ${err instanceof Error ? err.stack : String(err)}
|
|
147
|
+
`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
150
|
+
//# sourceMappingURL=bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * @experimental\n *\n * `agent-runtime-mcp` — stdio MCP server entry point.\n *\n * Spins up a server with the default coder delegate (wired against the\n * real `@tangle-network/sandbox` client) and, when the optional\n * `@tangle-network/agent-knowledge` peer is installed, a researcher\n * delegate against `multiHarnessResearcherFanout`.\n *\n * Environment variables:\n * SANDBOX_API_KEY required — passed to `new Sandbox({ apiKey })`\n * SANDBOX_BASE_URL optional — sandbox-SDK base URL override\n * MCP_MAX_CONCURRENT_SANDBOXES default 4 — kernel maxConcurrency cap\n * MCP_CODER_FANOUT_HARNESSES comma-separated harness ids to use for variants > 1\n * MCP_DISABLE_CODER set to `1` to omit `delegate_code`\n * MCP_DISABLE_RESEARCHER set to `1` to omit `delegate_research` even when peer is present\n */\n\nimport type { LoopSandboxClient } from '../loops'\nimport { runLoop } from '../loops'\nimport { createDefaultCoderDelegate, type ResearcherDelegate } from './delegates'\nimport { createMcpServer } from './server'\nimport type { ResearchOutputShape } from './types'\n\nasync function main(): Promise<void> {\n const fanoutHarnesses = parseHarnesses(process.env.MCP_CODER_FANOUT_HARNESSES)\n const maxConcurrency = parseConcurrency(process.env.MCP_MAX_CONCURRENT_SANDBOXES)\n const wantCoder = !process.env.MCP_DISABLE_CODER\n const wantResearcher = !process.env.MCP_DISABLE_RESEARCHER\n\n // Skip the sandbox client load entirely when no profile delegate needs it —\n // the feedback + status + history tools are queue-bound and require no\n // sandbox. Useful for tooling that mounts the MCP server purely for\n // self-introspection.\n const needsSandbox = wantCoder || wantResearcher\n let sandboxClient: LoopSandboxClient | undefined\n if (needsSandbox) {\n const apiKey = process.env.SANDBOX_API_KEY\n if (!apiKey && !process.env.AGENT_RUNTIME_MCP_ALLOW_NO_KEY) {\n process.stderr.write(\n 'agent-runtime-mcp: SANDBOX_API_KEY is required (set AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 to run without it for diagnostics, or set MCP_DISABLE_CODER=1 MCP_DISABLE_RESEARCHER=1 to run the queue-only subset)\\n',\n )\n process.exit(2)\n }\n sandboxClient = await loadSandboxClient(apiKey)\n }\n\n const coderDelegate =\n wantCoder && sandboxClient\n ? createDefaultCoderDelegate({\n sandboxClient,\n fanoutHarnesses,\n maxConcurrency,\n })\n : undefined\n\n const researcherDelegate =\n wantResearcher && sandboxClient\n ? await loadResearcherDelegate(sandboxClient, maxConcurrency)\n : undefined\n\n const server = createMcpServer({ coderDelegate, researcherDelegate })\n\n process.on('SIGINT', () => {\n server.stop()\n process.exit(0)\n })\n process.on('SIGTERM', () => {\n server.stop()\n process.exit(0)\n })\n\n await server.serve()\n}\n\nasync function loadSandboxClient(apiKey: string | undefined): Promise<LoopSandboxClient> {\n // Dynamic import keeps the bin importable in environments that haven't\n // installed `@tangle-network/sandbox` yet (the runtime package lists it\n // as a peer dep, not a hard dep).\n const mod = await import('@tangle-network/sandbox').catch((err) => {\n process.stderr.write(\n `agent-runtime-mcp: failed to load @tangle-network/sandbox (${err.message}); install the peer dependency\\n`,\n )\n process.exit(2)\n })\n const SandboxCtor = (mod as { Sandbox?: new (config: unknown) => LoopSandboxClient }).Sandbox\n if (!SandboxCtor) {\n process.stderr.write(\n 'agent-runtime-mcp: @tangle-network/sandbox does not export Sandbox; cannot construct client\\n',\n )\n process.exit(2)\n }\n const baseUrl = process.env.SANDBOX_BASE_URL\n return new SandboxCtor({\n apiKey,\n ...(baseUrl ? { baseUrl } : {}),\n })\n}\n\ninterface ResearcherProfilePreset {\n agentRunSpec: Parameters<typeof runLoop>[0]['agentRun'] extends infer T ? NonNullable<T> : never\n output: Parameters<typeof runLoop>[0]['output']\n validator: Parameters<typeof runLoop>[0]['validator']\n}\n\ninterface ResearcherFanoutPreset {\n agentRuns: NonNullable<Parameters<typeof runLoop>[0]['agentRuns']>\n output: Parameters<typeof runLoop>[0]['output']\n validator: Parameters<typeof runLoop>[0]['validator']\n driver: Parameters<typeof runLoop>[0]['driver']\n}\n\nasync function loadResearcherDelegate(\n sandboxClient: LoopSandboxClient,\n maxConcurrency: number,\n): Promise<ResearcherDelegate | undefined> {\n // Optional peer — when `@tangle-network/agent-knowledge` isn't installed,\n // we silently omit the researcher tool from the advertisement. The\n // dynamic-import path is resolved at runtime; TypeScript cannot see the\n // peer, so we type the module structurally rather than via its own\n // declaration file.\n const profilesSpecifier = '@tangle-network/agent-knowledge/profiles'\n const mod = await import(profilesSpecifier).catch(() => undefined)\n if (!mod) return undefined\n type SingleFactory = (opts: { task: unknown }) => ResearcherProfilePreset\n type FanoutFactory = (opts: { task: unknown }) => ResearcherFanoutPreset\n const fanoutFactory = (mod as { multiHarnessResearcherFanout?: FanoutFactory })\n .multiHarnessResearcherFanout\n const singleFactory = (mod as { researcherProfile?: SingleFactory }).researcherProfile\n if (!fanoutFactory || !singleFactory) return undefined\n\n return async (args, ctx) => {\n const task = {\n question: args.question,\n knowledgeNamespace: args.namespace,\n scope: args.scope,\n sources: args.sources,\n recencyWindow: args.config?.recencyWindow\n ? {\n since: args.config.recencyWindow.since\n ? new Date(args.config.recencyWindow.since)\n : undefined,\n until: args.config.recencyWindow.until\n ? new Date(args.config.recencyWindow.until)\n : undefined,\n }\n : undefined,\n maxItems: args.config?.maxItems,\n minConfidence: args.config?.minConfidence,\n }\n const variants = Math.max(1, Math.trunc(args.variants ?? 1))\n ctx.report({ iteration: 0, phase: 'starting' })\n if (variants <= 1) {\n const preset = singleFactory({ task })\n const result = await runLoop({\n driver: {\n name: 'mcp-researcher-single',\n async plan(t, history) {\n return history.length === 0 ? [t] : []\n },\n decide(history) {\n return history.length > 0 ? 'pick-winner' : 'fail'\n },\n },\n agentRun: preset.agentRunSpec,\n output: preset.output,\n validator: preset.validator,\n task,\n ctx: { sandboxClient, signal: ctx.signal },\n maxIterations: 1,\n maxConcurrency,\n })\n const output = result.winner?.output\n if (!output) throw new Error('researcher delegate produced no winner')\n ctx.report({ iteration: 1, phase: 'completed' })\n return output as ResearchOutputShape\n }\n const fanout = fanoutFactory({ task })\n const result = await runLoop({\n driver: fanout.driver,\n agentRuns: fanout.agentRuns.slice(0, variants),\n output: fanout.output,\n validator: fanout.validator,\n task,\n ctx: { sandboxClient, signal: ctx.signal },\n maxIterations: variants,\n maxConcurrency: Math.min(maxConcurrency, variants),\n })\n const output = result.winner?.output\n if (!output) throw new Error('researcher delegate fanout produced no winner')\n ctx.report({ iteration: result.iterations.length, phase: 'completed' })\n return output as ResearchOutputShape\n }\n}\n\nfunction parseHarnesses(raw: string | undefined): string[] | undefined {\n if (!raw) return undefined\n const list = raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n return list.length > 0 ? list : undefined\n}\n\nfunction parseConcurrency(raw: string | undefined): number {\n if (!raw) return 4\n const n = Number(raw)\n if (!Number.isFinite(n) || n < 1) return 4\n return Math.min(Math.trunc(n), 32)\n}\n\nmain().catch((err) => {\n process.stderr.write(`agent-runtime-mcp: ${err instanceof Error ? err.stack : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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","names":["result","output"]}
|