@tangle-network/agent-runtime 0.79.2 → 0.79.4
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 +50 -327
- package/dist/agent.js +5 -5
- package/dist/analyst-loop.js +2 -2
- package/dist/{chunk-TSDKBFZP.js → chunk-44CX5JU6.js} +414 -113
- package/dist/chunk-44CX5JU6.js.map +1 -0
- package/dist/{chunk-T2HVQVB4.js → chunk-4J6RBI3K.js} +15 -1
- package/dist/chunk-4J6RBI3K.js.map +1 -0
- package/dist/{chunk-75V2XXYJ.js → chunk-AHZ3YBL6.js} +524 -12
- package/dist/chunk-AHZ3YBL6.js.map +1 -0
- package/dist/{chunk-IODKUOBA.js → chunk-C2FZ6GR6.js} +2 -2
- package/dist/{chunk-Z3RRRPRB.js → chunk-H7IBHAFT.js} +23 -14
- package/dist/chunk-H7IBHAFT.js.map +1 -0
- package/dist/{chunk-VMNEQHJR.js → chunk-IPEQ3ERC.js} +17 -2
- package/dist/chunk-IPEQ3ERC.js.map +1 -0
- package/dist/{chunk-PBE35ULD.js → chunk-NXZEVWKP.js} +2 -2
- package/dist/{chunk-SONQUREI.js → chunk-RUJZK6VH.js} +2 -2
- package/dist/{chunk-2DS6T46I.js → chunk-U6AP535M.js} +4 -4
- package/dist/{coordination-BoEPhGas.d.ts → coordination-BFVtgRax.d.ts} +30 -4
- package/dist/environment-provider.d.ts +1 -1
- package/dist/environment-provider.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -8
- package/dist/intelligence.d.ts +30 -2
- package/dist/intelligence.js +33 -23
- package/dist/intelligence.js.map +1 -1
- package/dist/{loop-runner-bin-DCr5OMe5.d.ts → loop-runner-bin-CTVja8e0.d.ts} +2 -2
- package/dist/loop-runner-bin.d.ts +4 -4
- package/dist/loop-runner-bin.js +7 -7
- package/dist/loops.d.ts +283 -13
- package/dist/loops.js +26 -6
- package/dist/mcp/bin.js +5 -5
- package/dist/mcp/index.d.ts +6 -6
- package/dist/mcp/index.js +7 -7
- package/dist/{router-client-CMAWGv1h.d.ts → router-client-Ak2IGuXq.d.ts} +33 -1
- package/dist/{types-C1sozrte.d.ts → types-DYW0tloU.d.ts} +2 -2
- package/dist/{worktree-fanout-CtQrRDME.d.ts → worktree-fanout-DaUDwCA_.d.ts} +5 -74
- package/dist/{worktree-CpptK3oF.d.ts → worktree-harness-Dt6s_m3z.d.ts} +74 -2
- package/package.json +1 -1
- package/skills/build-with-agent-runtime/SKILL.md +53 -23
- package/dist/chunk-75V2XXYJ.js.map +0 -1
- package/dist/chunk-T2HVQVB4.js.map +0 -1
- package/dist/chunk-TSDKBFZP.js.map +0 -1
- package/dist/chunk-VMNEQHJR.js.map +0 -1
- package/dist/chunk-Z3RRRPRB.js.map +0 -1
- /package/dist/{chunk-IODKUOBA.js.map → chunk-C2FZ6GR6.js.map} +0 -0
- /package/dist/{chunk-PBE35ULD.js.map → chunk-NXZEVWKP.js.map} +0 -0
- /package/dist/{chunk-SONQUREI.js.map → chunk-RUJZK6VH.js.map} +0 -0
- /package/dist/{chunk-2DS6T46I.js.map → chunk-U6AP535M.js.map} +0 -0
|
@@ -29,6 +29,19 @@ function extractLlmCallEvent(event, agentRunName) {
|
|
|
29
29
|
}
|
|
30
30
|
return void 0;
|
|
31
31
|
}
|
|
32
|
+
function sumSandboxUsage(events, agentRunName = "agent") {
|
|
33
|
+
let input = 0;
|
|
34
|
+
let output = 0;
|
|
35
|
+
let costUsd = 0;
|
|
36
|
+
for (const ev of events) {
|
|
37
|
+
const call = extractLlmCallEvent(ev, agentRunName);
|
|
38
|
+
if (!call) continue;
|
|
39
|
+
input += call.tokensIn ?? 0;
|
|
40
|
+
output += call.tokensOut ?? 0;
|
|
41
|
+
costUsd += call.costUsd ?? 0;
|
|
42
|
+
}
|
|
43
|
+
return { input, output, costUsd };
|
|
44
|
+
}
|
|
32
45
|
function buildLlmCall(data, agentRunName) {
|
|
33
46
|
const tokensIn = pickFiniteNumber(data, ["tokensIn", "inputTokens", "prompt_tokens"]);
|
|
34
47
|
const tokensOut = pickFiniteNumber(data, ["tokensOut", "outputTokens", "completion_tokens"]);
|
|
@@ -73,6 +86,7 @@ function mapSandboxEvent(event, opts = {}) {
|
|
|
73
86
|
|
|
74
87
|
export {
|
|
75
88
|
extractLlmCallEvent,
|
|
89
|
+
sumSandboxUsage,
|
|
76
90
|
mapSandboxEvent
|
|
77
91
|
};
|
|
78
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-4J6RBI3K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/sandbox-events.ts"],"sourcesContent":["/**\n * Sandbox-event → runtime-event mapping.\n *\n * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`\n * whose `type` vocabulary is backend-determined (opencode, etc.) rather than\n * enumerated by the SDK. Two consumers project it:\n * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off\n * every cost-bearing event, regardless of stream shape;\n * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects\n * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.\n *\n * Both live here so the empirically-observed `type` vocabulary has one home.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport type { RuntimeStreamEvent } from '../types'\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 * 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` / `usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator write\n * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.\n */\nexport function 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 // sandbox 0.4.0 terminal event: `data = { tokenUsage: { inputTokens, outputTokens,\n // reasoningTokens, cacheReadInputTokens }, totalCostUsd }`. Usage lives under\n // `tokenUsage` (not `usage`) and the cost is top-level — neither matched the\n // branches above, so an in-process loopDispatch run reported {0,0} and the\n // backend-integrity guard misread a real run as a stub. Reasoning tokens are\n // billed output (reasoning models), so they fold into the output count.\n if (type === 'done') {\n const usage = data.tokenUsage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n const out = pickFiniteNumber(usage, ['outputTokens', 'completion_tokens', 'tokensOut'])\n const reasoning = pickFiniteNumber(usage, ['reasoningTokens'])\n const mergedOut =\n out !== undefined || reasoning !== undefined ? (out ?? 0) + (reasoning ?? 0) : undefined\n return buildLlmCall(\n {\n inputTokens: usage.inputTokens,\n outputTokens: mergedOut,\n totalCostUsd: data.totalCostUsd,\n model: data.model ?? usage.model,\n },\n agentRunName,\n )\n }\n return undefined\n}\n\n/**\n * Sum the token usage + USD cost of a sandbox turn's events — the one honest way to meter an\n * `openSandboxRun` cell. Folds `extractLlmCallEvent` over the stream (which reads usage off EVERY backend\n * event shape), so a `runProfileMatrix` dispatch can report it to `ctx.cost`:\n *\n * const turn = await run.start(prompt)\n * const u = sumSandboxUsage(turn.events)\n * if (u.input || u.output) ctx.cost.observeTokens({ input: u.input, output: u.output })\n * if (u.costUsd) ctx.cost.observe(u.costUsd, 'sandbox-cell')\n *\n * Without this a cell reads `{tokens:0, cost:0}` and the backend-integrity guard correctly aborts the\n * matrix as a stub. `agentRunName` is the fallback model label for cost-only events (default `'agent'`).\n */\nexport function sumSandboxUsage(\n events: readonly SandboxEvent[],\n agentRunName = 'agent',\n): { input: number; output: number; costUsd: number } {\n let input = 0\n let output = 0\n let costUsd = 0\n for (const ev of events) {\n const call = extractLlmCallEvent(ev, agentRunName)\n if (!call) continue\n input += call.tokensIn ?? 0\n output += call.tokensOut ?? 0\n costUsd += call.costUsd ?? 0\n }\n return { input, output, costUsd }\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 * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,\n * for runtimes that bridge a sandbox `streamPrompt` into the\n * `AgentRuntime.act` streaming contract. Returns `undefined` for events that\n * have no faithful projection — the raw stream is preserved separately for the\n * `OutputAdapter`, so an unmapped event never loses data.\n *\n * Mapped (the task-optional incremental variants — no synthesized task\n * lifecycle, no guessed tool-part shapes):\n * - `message.part.updated` text part → `text_delta`\n * - `message.part.updated` reasoning/thinking part → `reasoning_delta`\n * - cost-bearing events → `llm_call` (shared with the ledger extractor)\n *\n * The opencode backend emits incremental text as\n * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;\n * `delta` is the increment, `part.text` the running accumulation.\n */\nexport function mapSandboxEvent(\n event: SandboxEvent,\n opts: { agentRunName?: string } = {},\n): RuntimeStreamEvent | 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 === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n const partType = String(part.type ?? '')\n const delta = typeof data.delta === 'string' ? data.delta : undefined\n const text = delta ?? (typeof part.text === 'string' ? part.text : undefined)\n if (text === undefined) return undefined\n if (partType === 'text') return { type: 'text_delta', text }\n if (partType === 'reasoning' || partType === 'thinking')\n return { type: 'reasoning_delta', text }\n return undefined\n }\n\n return extractLlmCallEvent(event, opts.agentRunName ?? 'agent')\n}\n"],"mappings":";AA+BO,SAAS,oBACd,OACA,cACyD;AACzD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,OAAO,OAAO,MAAM,QAAQ,EAAE;AACpC,QAAM,OACJ,MAAM,QAAQ,OAAO,MAAM,SAAS,WAC/B,MAAM,OACN,CAAC;AAER,MAAI,SAAS,cAAc,SAAS,gBAAgB,SAAS,SAAS;AACpE,WAAO,aAAa,MAAM,YAAY;AAAA,EACxC;AACA,MAAI,SAAS,uBAAuB,SAAS,YAAY,SAAS,SAAS;AACzE,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,WAAO,aAAa,EAAE,GAAG,OAAO,OAAO,KAAK,SAAS,MAAM,MAAM,GAAG,YAAY;AAAA,EAClF;AAOA,MAAI,SAAS,QAAQ;AACnB,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,UAAM,MAAM,iBAAiB,OAAO,CAAC,gBAAgB,qBAAqB,WAAW,CAAC;AACtF,UAAM,YAAY,iBAAiB,OAAO,CAAC,iBAAiB,CAAC;AAC7D,UAAM,YACJ,QAAQ,UAAa,cAAc,UAAa,OAAO,MAAM,aAAa,KAAK;AACjF,WAAO;AAAA,MACL;AAAA,QACE,aAAa,MAAM;AAAA,QACnB,cAAc;AAAA,QACd,cAAc,KAAK;AAAA,QACnB,OAAO,KAAK,SAAS,MAAM;AAAA,MAC7B;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAeO,SAAS,gBACd,QACA,eAAe,SACqC;AACpD,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,MAAI,UAAU;AACd,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,oBAAoB,IAAI,YAAY;AACjD,QAAI,CAAC,KAAM;AACX,aAAS,KAAK,YAAY;AAC1B,cAAU,KAAK,aAAa;AAC5B,eAAW,KAAK,WAAW;AAAA,EAC7B;AACA,SAAO,EAAE,OAAO,QAAQ,QAAQ;AAClC;AAEA,SAAS,aACP,MACA,cACyD;AACzD,QAAM,WAAW,iBAAiB,MAAM,CAAC,YAAY,eAAe,eAAe,CAAC;AACpF,QAAM,YAAY,iBAAiB,MAAM,CAAC,aAAa,gBAAgB,mBAAmB,CAAC;AAC3F,QAAM,UAAU,iBAAiB,MAAM,CAAC,WAAW,gBAAgB,YAAY,MAAM,CAAC;AACtF,MAAI,aAAa,UAAa,cAAc,UAAa,YAAY,QAAW;AAC9E,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,SAAS,IAAI,KAAK,QAAQ;AACrF,QAAM,QAAmD;AAAA,IACvD,MAAM;AAAA,IACN;AAAA,EACF;AACA,MAAI,aAAa,OAAW,OAAM,WAAW;AAC7C,MAAI,cAAc,OAAW,OAAM,YAAY;AAC/C,MAAI,YAAY,OAAW,OAAM,UAAU;AAC3C,SAAO;AACT;AAEA,SAAS,iBAAiB,MAA+B,MAAoC;AAC3F,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,KAAK,GAAG;AACtB,QAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAAA,EAClE;AACA,SAAO;AACT;AAmBO,SAAS,gBACd,OACA,OAAkC,CAAC,GACH;AAChC,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,OAAO,OAAO,MAAM,QAAQ,EAAE;AACpC,QAAM,OACJ,MAAM,QAAQ,OAAO,MAAM,SAAS,WAC/B,MAAM,OACN,CAAC;AAER,MAAI,SAAS,wBAAwB;AACnC,UAAM,OACJ,KAAK,QAAQ,OAAO,KAAK,SAAS,WAAY,KAAK,OAAmC,CAAC;AACzF,UAAM,WAAW,OAAO,KAAK,QAAQ,EAAE;AACvC,UAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,UAAM,OAAO,UAAU,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACnE,QAAI,SAAS,OAAW,QAAO;AAC/B,QAAI,aAAa,OAAQ,QAAO,EAAE,MAAM,cAAc,KAAK;AAC3D,QAAI,aAAa,eAAe,aAAa;AAC3C,aAAO,EAAE,MAAM,mBAAmB,KAAK;AACzC,WAAO;AAAA,EACT;AAEA,SAAO,oBAAoB,OAAO,KAAK,gBAAgB,OAAO;AAChE;","names":[]}
|
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
routerToolLoop,
|
|
13
13
|
runLoop,
|
|
14
14
|
settledToIteration,
|
|
15
|
+
supervise,
|
|
15
16
|
withDriverExecutor
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-44CX5JU6.js";
|
|
17
18
|
import {
|
|
18
19
|
addTokenUsage,
|
|
19
20
|
isAbortError,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
sleep,
|
|
22
23
|
stringifySafe,
|
|
23
24
|
zeroTokenUsage
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-H7IBHAFT.js";
|
|
25
26
|
import {
|
|
26
27
|
AnalystError,
|
|
27
28
|
PlannerError,
|
|
@@ -205,6 +206,355 @@ Audit the route: revealed intent, verdict, evidence, one recommendation.`
|
|
|
205
206
|
};
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
// src/runtime/benchmark-report.ts
|
|
210
|
+
import {
|
|
211
|
+
benjaminiHochberg,
|
|
212
|
+
confidenceInterval,
|
|
213
|
+
pairedBootstrap,
|
|
214
|
+
pairedTTest,
|
|
215
|
+
wilson
|
|
216
|
+
} from "@tangle-network/agent-eval";
|
|
217
|
+
var defaultScoreOf = (r) => {
|
|
218
|
+
const o = r.outcome;
|
|
219
|
+
if (typeof o.holdoutScore === "number") return o.holdoutScore;
|
|
220
|
+
if (typeof o.searchScore === "number") return o.searchScore;
|
|
221
|
+
const raw = o.raw ?? {};
|
|
222
|
+
for (const k of ["composite", "score", "passed", "resolved"]) {
|
|
223
|
+
if (typeof raw[k] === "number") return raw[k];
|
|
224
|
+
}
|
|
225
|
+
return void 0;
|
|
226
|
+
};
|
|
227
|
+
var defaultProfileKeyOf = (r) => {
|
|
228
|
+
const cell = r.agentProfile;
|
|
229
|
+
const harness = cell?.harness;
|
|
230
|
+
return harness ? `${harness}\xB7${r.model}` : r.model;
|
|
231
|
+
};
|
|
232
|
+
var defaultGroupOf = (r) => r.scenarioId ?? r.experimentId;
|
|
233
|
+
function quantile(sorted, q) {
|
|
234
|
+
if (sorted.length === 0) return 0;
|
|
235
|
+
const idx = Math.min(sorted.length - 1, Math.floor(q * (sorted.length - 1)));
|
|
236
|
+
return sorted[idx] ?? 0;
|
|
237
|
+
}
|
|
238
|
+
function mean(xs) {
|
|
239
|
+
return xs.length === 0 ? 0 : xs.reduce((a, b) => a + b, 0) / xs.length;
|
|
240
|
+
}
|
|
241
|
+
function meanByScenario(records, scoreOf) {
|
|
242
|
+
const sums = /* @__PURE__ */ new Map();
|
|
243
|
+
for (const r of records) {
|
|
244
|
+
const s = scoreOf(r);
|
|
245
|
+
if (typeof s !== "number") continue;
|
|
246
|
+
const id = r.scenarioId;
|
|
247
|
+
if (!id) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
`benchmark-report: RunRecord (candidate ${r.candidateId ?? "unknown"}) is missing scenarioId \u2014 cannot pair or interval it honestly. Pass opts.stats only on a scenario-tagged corpus.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
const acc = sums.get(id) ?? { total: 0, n: 0 };
|
|
253
|
+
acc.total += s;
|
|
254
|
+
acc.n += 1;
|
|
255
|
+
sums.set(id, acc);
|
|
256
|
+
}
|
|
257
|
+
const out = /* @__PURE__ */ new Map();
|
|
258
|
+
for (const [id, acc] of sums) out.set(id, acc.n ? acc.total / acc.n : 0);
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
function leaderboard(records, opts = {}) {
|
|
262
|
+
const scoreOf = opts.scoreOf ?? defaultScoreOf;
|
|
263
|
+
const profileKeyOf = opts.profileKeyOf ?? defaultProfileKeyOf;
|
|
264
|
+
const groupOf = opts.groupOf ?? defaultGroupOf;
|
|
265
|
+
const labelOf = opts.labelOf ?? ((k) => k);
|
|
266
|
+
const axisSet = /* @__PURE__ */ new Set();
|
|
267
|
+
if (opts.axisScoresOf) {
|
|
268
|
+
for (const r of records) for (const k of Object.keys(opts.axisScoresOf(r))) axisSet.add(k);
|
|
269
|
+
} else {
|
|
270
|
+
for (const r of records) axisSet.add(groupOf(r));
|
|
271
|
+
}
|
|
272
|
+
const axes = [...axisSet].sort();
|
|
273
|
+
const byProfile = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const r of records) {
|
|
275
|
+
const key = profileKeyOf(r);
|
|
276
|
+
const bucket = byProfile.get(key);
|
|
277
|
+
if (bucket) bucket.push(r);
|
|
278
|
+
else byProfile.set(key, [r]);
|
|
279
|
+
}
|
|
280
|
+
const rows = [];
|
|
281
|
+
for (const [profileKey, recs] of byProfile) {
|
|
282
|
+
const scores = recs.map(scoreOf).filter((s) => typeof s === "number");
|
|
283
|
+
const axisBuckets = /* @__PURE__ */ new Map();
|
|
284
|
+
for (const r of recs) {
|
|
285
|
+
if (opts.axisScoresOf) {
|
|
286
|
+
for (const [axis, s] of Object.entries(opts.axisScoresOf(r))) {
|
|
287
|
+
const b = axisBuckets.get(axis) ?? [];
|
|
288
|
+
b.push(s);
|
|
289
|
+
axisBuckets.set(axis, b);
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
const s = scoreOf(r);
|
|
293
|
+
if (typeof s === "number") {
|
|
294
|
+
const axis = groupOf(r);
|
|
295
|
+
const b = axisBuckets.get(axis) ?? [];
|
|
296
|
+
b.push(s);
|
|
297
|
+
axisBuckets.set(axis, b);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const perAxis = {};
|
|
302
|
+
for (const [axis, b] of axisBuckets) perAxis[axis] = mean(b);
|
|
303
|
+
const latencies = recs.map((r) => r.wallMs).sort((a, b) => a - b);
|
|
304
|
+
const pass = opts.passThreshold ?? 0.999;
|
|
305
|
+
let scoreCi;
|
|
306
|
+
let passCi;
|
|
307
|
+
if (opts.stats) {
|
|
308
|
+
const collapsed = [...meanByScenario(recs, scoreOf).values()];
|
|
309
|
+
if (collapsed.length > 0) {
|
|
310
|
+
const ci2 = confidenceInterval(collapsed, 0.95, { seed: 7 });
|
|
311
|
+
scoreCi = { lower: ci2.lower, upper: ci2.upper };
|
|
312
|
+
const w = wilson(collapsed.filter((s) => s >= pass).length, collapsed.length, 0.95);
|
|
313
|
+
passCi = { lower: w.lower, upper: w.upper };
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
rows.push({
|
|
317
|
+
profileKey,
|
|
318
|
+
label: labelOf(profileKey),
|
|
319
|
+
model: recs[0]?.model ?? profileKey,
|
|
320
|
+
n: recs.length,
|
|
321
|
+
meanScore: mean(scores),
|
|
322
|
+
solveRate: scores.length === 0 ? 0 : scores.filter((s) => s >= pass).length / scores.length,
|
|
323
|
+
perAxis,
|
|
324
|
+
costUsd: recs.reduce((a, r) => a + r.costUsd, 0),
|
|
325
|
+
tokensIn: recs.reduce((a, r) => a + (r.tokenUsage?.input ?? 0), 0),
|
|
326
|
+
tokensOut: recs.reduce((a, r) => a + (r.tokenUsage?.output ?? 0), 0),
|
|
327
|
+
latencyP50Ms: quantile(latencies, 0.5),
|
|
328
|
+
latencyP90Ms: quantile(latencies, 0.9),
|
|
329
|
+
...scoreCi ? { scoreCi } : {},
|
|
330
|
+
...passCi ? { passCi } : {}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
rows.sort(
|
|
334
|
+
(a, b) => b.meanScore - a.meanScore || a.costUsd - b.costUsd || a.label.localeCompare(b.label)
|
|
335
|
+
);
|
|
336
|
+
const models = [...new Set(records.map((r) => r.model))].sort();
|
|
337
|
+
return {
|
|
338
|
+
title: opts.title ?? "Benchmark report",
|
|
339
|
+
axes,
|
|
340
|
+
profiles: rows,
|
|
341
|
+
meta: opts.meta ?? {},
|
|
342
|
+
provenance: {
|
|
343
|
+
records: records.length,
|
|
344
|
+
profiles: rows.length,
|
|
345
|
+
axes: axes.length,
|
|
346
|
+
models,
|
|
347
|
+
totalCostUsd: records.reduce((a, r) => a + r.costUsd, 0)
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function pairwiseSignificance(records, opts = {}) {
|
|
352
|
+
const scoreOf = opts.scoreOf ?? defaultScoreOf;
|
|
353
|
+
const profileKeyOf = opts.profileKeyOf ?? defaultProfileKeyOf;
|
|
354
|
+
const labelOf = opts.labelOf ?? ((k) => k);
|
|
355
|
+
const minPairs = opts.minPairs ?? 12;
|
|
356
|
+
const byProfile = /* @__PURE__ */ new Map();
|
|
357
|
+
for (const r of records) {
|
|
358
|
+
const k = profileKeyOf(r);
|
|
359
|
+
const b = byProfile.get(k);
|
|
360
|
+
if (b) b.push(r);
|
|
361
|
+
else byProfile.set(k, [r]);
|
|
362
|
+
}
|
|
363
|
+
const keys = [...byProfile.keys()].sort();
|
|
364
|
+
const collapsed = new Map(keys.map((k) => [k, meanByScenario(byProfile.get(k) ?? [], scoreOf)]));
|
|
365
|
+
const raw = [];
|
|
366
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
367
|
+
for (let j = i + 1; j < keys.length; j += 1) {
|
|
368
|
+
const ka = keys[i];
|
|
369
|
+
const kb = keys[j];
|
|
370
|
+
const am = collapsed.get(ka);
|
|
371
|
+
const bm = collapsed.get(kb);
|
|
372
|
+
const aScores = [];
|
|
373
|
+
const bScores = [];
|
|
374
|
+
for (const sid of [...am.keys()].sort()) {
|
|
375
|
+
const bv = bm.get(sid);
|
|
376
|
+
if (bv !== void 0) {
|
|
377
|
+
aScores.push(am.get(sid));
|
|
378
|
+
bScores.push(bv);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (aScores.length === 0) continue;
|
|
382
|
+
const boot = pairedBootstrap(aScores, bScores, { seed: 7, statistic: "median" });
|
|
383
|
+
const p = pairedTTest(aScores, bScores).p;
|
|
384
|
+
raw.push({
|
|
385
|
+
a: labelOf(ka),
|
|
386
|
+
b: labelOf(kb),
|
|
387
|
+
pairs: aScores.length,
|
|
388
|
+
delta: boot.median,
|
|
389
|
+
ciLow: boot.low,
|
|
390
|
+
ciHigh: boot.high,
|
|
391
|
+
p
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const { significant } = benjaminiHochberg(
|
|
396
|
+
raw.map((r) => r.p),
|
|
397
|
+
opts.fdr ?? 0.05
|
|
398
|
+
);
|
|
399
|
+
return raw.map((r, i) => ({
|
|
400
|
+
...r,
|
|
401
|
+
significant: (significant[i] ?? false) && r.pairs >= minPairs
|
|
402
|
+
}));
|
|
403
|
+
}
|
|
404
|
+
var pct = (x) => `${(100 * x).toFixed(1)}%`;
|
|
405
|
+
var ci = (iv) => iv ? ` [${pct(iv.lower)}, ${pct(iv.upper)}]` : "";
|
|
406
|
+
function renderLeaderboardMarkdown(report) {
|
|
407
|
+
const lines = [];
|
|
408
|
+
lines.push(`# ${report.title}`, "");
|
|
409
|
+
const p = report.provenance;
|
|
410
|
+
lines.push(
|
|
411
|
+
`**${p.profiles} profiles \xD7 ${p.axes} axes**, ${p.records} runs, ${p.models.length} models \xB7 total $${p.totalCostUsd.toFixed(2)}`,
|
|
412
|
+
""
|
|
413
|
+
);
|
|
414
|
+
for (const [k, v] of Object.entries(report.meta)) lines.push(`- **${k}:** ${v}`);
|
|
415
|
+
if (Object.keys(report.meta).length) lines.push("");
|
|
416
|
+
lines.push("## Leaderboard", "");
|
|
417
|
+
lines.push(
|
|
418
|
+
"| # | Profile | Score (95% CI) | Solved (95% CI) | Runs | Cost | Tok in/out | p50 | p90 |"
|
|
419
|
+
);
|
|
420
|
+
lines.push("|---|---|--:|--:|--:|--:|--:|--:|--:|");
|
|
421
|
+
report.profiles.forEach((r, i) => {
|
|
422
|
+
lines.push(
|
|
423
|
+
`| ${i + 1} | ${r.label} | ${pct(r.meanScore)}${ci(r.scoreCi)} | ${pct(r.solveRate)}${ci(r.passCi)} | ${r.n} | $${r.costUsd.toFixed(3)} | ${r.tokensIn}/${r.tokensOut} | ${(r.latencyP50Ms / 1e3).toFixed(1)}s | ${(r.latencyP90Ms / 1e3).toFixed(1)}s |`
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
lines.push("");
|
|
427
|
+
lines.push("## Score matrix \u2014 profile \xD7 axis", "");
|
|
428
|
+
lines.push(`| Profile | ${report.axes.join(" | ")} |`);
|
|
429
|
+
lines.push(`|---|${report.axes.map(() => "--:").join("|")}|`);
|
|
430
|
+
for (const r of report.profiles) {
|
|
431
|
+
const cells = report.axes.map((a) => {
|
|
432
|
+
const v = r.perAxis[a];
|
|
433
|
+
return v === void 0 ? "\xB7" : pct(v);
|
|
434
|
+
});
|
|
435
|
+
lines.push(`| ${r.label} | ${cells.join(" | ")} |`);
|
|
436
|
+
}
|
|
437
|
+
lines.push("");
|
|
438
|
+
lines.push("> `\xB7` = the profile never ran that axis (blank, never zero).");
|
|
439
|
+
return lines.join("\n");
|
|
440
|
+
}
|
|
441
|
+
function renderPairwiseMarkdown(verdicts, title = "Pairwise significance (paired, BH-corrected)") {
|
|
442
|
+
const lines = [`## ${title}`, ""];
|
|
443
|
+
if (verdicts.length === 0) return lines.concat("_no comparable pairs_").join("\n");
|
|
444
|
+
lines.push("| A vs B | \u0394(b\u2212a) median | 95% CI | pairs | p | verdict |");
|
|
445
|
+
lines.push("|---|--:|--:|--:|--:|---|");
|
|
446
|
+
for (const v of verdicts) {
|
|
447
|
+
const verdict = v.significant ? `**${v.delta >= 0 ? v.b : v.a} wins**` : "ns";
|
|
448
|
+
lines.push(
|
|
449
|
+
`| ${v.a} vs ${v.b} | ${v.delta >= 0 ? "+" : ""}${pct(v.delta)} | [${pct(v.ciLow)}, ${pct(v.ciHigh)}] | ${v.pairs} | ${v.p.toFixed(3)} | ${verdict} |`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
lines.push(
|
|
453
|
+
"",
|
|
454
|
+
"> `ns` = not significant after Benjamini\u2013Hochberg (or below the paired-count floor)."
|
|
455
|
+
);
|
|
456
|
+
return lines.join("\n");
|
|
457
|
+
}
|
|
458
|
+
function esc(s) {
|
|
459
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
460
|
+
}
|
|
461
|
+
function ramp(score) {
|
|
462
|
+
const x = Math.max(0, Math.min(1, score));
|
|
463
|
+
const r = Math.round(x < 0.5 ? 220 : 220 - (x - 0.5) * 2 * 160);
|
|
464
|
+
const g = Math.round(x < 0.5 ? x * 2 * 170 : 170);
|
|
465
|
+
return `rgb(${r},${g},60)`;
|
|
466
|
+
}
|
|
467
|
+
function renderLeaderboardSvg(report) {
|
|
468
|
+
const rowH = 26;
|
|
469
|
+
const labelW = 220;
|
|
470
|
+
const cellW = 54;
|
|
471
|
+
const pad = 16;
|
|
472
|
+
const barAreaW = 360;
|
|
473
|
+
const profiles = report.profiles;
|
|
474
|
+
const barsH = profiles.length * rowH + pad;
|
|
475
|
+
const heatTop = barsH + 48;
|
|
476
|
+
const heatW = labelW + report.axes.length * cellW + pad;
|
|
477
|
+
const heatH = profiles.length * rowH + pad;
|
|
478
|
+
const width = Math.max(labelW + barAreaW + pad * 2, heatW);
|
|
479
|
+
const height = heatTop + heatH + pad;
|
|
480
|
+
const out = [];
|
|
481
|
+
out.push(
|
|
482
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" font-family="ui-sans-serif,system-ui,sans-serif" font-size="12">`
|
|
483
|
+
);
|
|
484
|
+
out.push(`<rect width="${width}" height="${height}" fill="white"/>`);
|
|
485
|
+
out.push(
|
|
486
|
+
`<text x="${pad}" y="${pad + 4}" font-weight="700" font-size="14">${esc(report.title)} \u2014 score</text>`
|
|
487
|
+
);
|
|
488
|
+
profiles.forEach((r, i) => {
|
|
489
|
+
const y = pad + 12 + i * rowH;
|
|
490
|
+
out.push(`<text x="${pad}" y="${y + 14}">${esc(r.label)}</text>`);
|
|
491
|
+
const w = Math.round(r.meanScore * barAreaW);
|
|
492
|
+
out.push(`<rect x="${labelW}" y="${y + 4}" width="${barAreaW}" height="16" fill="#eef0f2"/>`);
|
|
493
|
+
out.push(
|
|
494
|
+
`<rect x="${labelW}" y="${y + 4}" width="${w}" height="16" fill="${ramp(r.meanScore)}"/>`
|
|
495
|
+
);
|
|
496
|
+
out.push(`<text x="${labelW + barAreaW + 6}" y="${y + 16}">${pct(r.meanScore)}</text>`);
|
|
497
|
+
});
|
|
498
|
+
out.push(
|
|
499
|
+
`<text x="${pad}" y="${heatTop - 12}" font-weight="700" font-size="14">profile \xD7 axis</text>`
|
|
500
|
+
);
|
|
501
|
+
report.axes.forEach((a, c) => {
|
|
502
|
+
const x = labelW + c * cellW;
|
|
503
|
+
out.push(
|
|
504
|
+
`<text x="${x + cellW / 2}" y="${heatTop}" text-anchor="middle" fill="#555">${esc(a.length > 8 ? `${a.slice(0, 7)}\u2026` : a)}</text>`
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
profiles.forEach((r, i) => {
|
|
508
|
+
const y = heatTop + 8 + i * rowH;
|
|
509
|
+
out.push(`<text x="${pad}" y="${y + 16}">${esc(r.label)}</text>`);
|
|
510
|
+
report.axes.forEach((a, c) => {
|
|
511
|
+
const x = labelW + c * cellW;
|
|
512
|
+
const s = r.perAxis[a];
|
|
513
|
+
if (s !== void 0) {
|
|
514
|
+
out.push(
|
|
515
|
+
`<rect x="${x}" y="${y}" width="${cellW - 3}" height="${rowH - 4}" fill="${ramp(s)}"/>`
|
|
516
|
+
);
|
|
517
|
+
out.push(
|
|
518
|
+
`<text x="${x + (cellW - 3) / 2}" y="${y + 15}" text-anchor="middle" fill="white" font-size="10">${Math.round(100 * s)}</text>`
|
|
519
|
+
);
|
|
520
|
+
} else {
|
|
521
|
+
out.push(
|
|
522
|
+
`<rect x="${x}" y="${y}" width="${cellW - 3}" height="${rowH - 4}" fill="#f3f4f6"/>`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
out.push("</svg>");
|
|
528
|
+
return out.join("\n");
|
|
529
|
+
}
|
|
530
|
+
function renderLeaderboardHtml(report) {
|
|
531
|
+
const svg = renderLeaderboardSvg(report);
|
|
532
|
+
const p = report.provenance;
|
|
533
|
+
const rows = report.profiles.map(
|
|
534
|
+
(r, i) => `<tr><td>${i + 1}</td><td>${esc(r.label)}</td><td class="n">${pct(r.meanScore)}</td><td class="n">${pct(r.solveRate)}</td><td class="n">${r.n}</td><td class="n">$${r.costUsd.toFixed(3)}</td>${report.axes.map((a) => {
|
|
535
|
+
const v = r.perAxis[a];
|
|
536
|
+
return `<td class="n">${v === void 0 ? "\xB7" : pct(v)}</td>`;
|
|
537
|
+
}).join("")}</tr>`
|
|
538
|
+
).join("\n");
|
|
539
|
+
const axisHead = report.axes.map((a) => `<th>${esc(a)}</th>`).join("");
|
|
540
|
+
return `<!doctype html><html><head><meta charset="utf-8"><title>${esc(report.title)}</title>
|
|
541
|
+
<style>
|
|
542
|
+
body{font:14px ui-sans-serif,system-ui,sans-serif;margin:2rem;color:#111}
|
|
543
|
+
h1{font-size:1.4rem}.sub{color:#555;margin:.2rem 0 1.2rem}
|
|
544
|
+
table{border-collapse:collapse;margin-top:1rem}td,th{border:1px solid #e5e7eb;padding:.35rem .6rem}
|
|
545
|
+
th{background:#f9fafb;text-align:left}.n{text-align:right;font-variant-numeric:tabular-nums}
|
|
546
|
+
tr:first-child td{font-weight:600}
|
|
547
|
+
</style></head><body>
|
|
548
|
+
<h1>${esc(report.title)}</h1>
|
|
549
|
+
<div class="sub">${p.profiles} profiles \xD7 ${p.axes} axes \xB7 ${p.records} runs \xB7 ${p.models.length} models \xB7 total $${p.totalCostUsd.toFixed(2)}</div>
|
|
550
|
+
${svg}
|
|
551
|
+
<table><thead><tr><th>#</th><th>Profile</th><th>Score</th><th>Solved</th><th>Runs</th><th>Cost</th>${axisHead}</tr></thead>
|
|
552
|
+
<tbody>
|
|
553
|
+
${rows}
|
|
554
|
+
</tbody></table>
|
|
555
|
+
</body></html>`;
|
|
556
|
+
}
|
|
557
|
+
|
|
208
558
|
// src/runtime/completion.ts
|
|
209
559
|
function completionAuthorizes(v, policy) {
|
|
210
560
|
if (!v?.done) return false;
|
|
@@ -608,6 +958,9 @@ function campaignTraceToLoopEmitter(trace) {
|
|
|
608
958
|
}
|
|
609
959
|
async function runLoopForCell(opts, scenario, profile, ctx) {
|
|
610
960
|
const loopOptions = opts.toLoopOptions(scenario, profile);
|
|
961
|
+
return runLoopWithCampaignContext(opts, loopOptions, ctx);
|
|
962
|
+
}
|
|
963
|
+
async function runLoopWithCampaignContext(opts, loopOptions, ctx) {
|
|
611
964
|
const result = await runLoop({
|
|
612
965
|
...loopOptions,
|
|
613
966
|
ctx: {
|
|
@@ -620,6 +973,9 @@ async function runLoopForCell(opts, scenario, profile, ctx) {
|
|
|
620
973
|
const toArtifact = opts.toArtifact ?? ((r) => r.winner?.output);
|
|
621
974
|
return toArtifact(result);
|
|
622
975
|
}
|
|
976
|
+
function loopCampaignDispatch(opts) {
|
|
977
|
+
return (scenario, ctx) => runLoopWithCampaignContext(opts, opts.toLoopOptions(scenario), ctx);
|
|
978
|
+
}
|
|
623
979
|
function loopDispatch(opts) {
|
|
624
980
|
return (profile, scenario, ctx) => runLoopForCell(opts, scenario, profile, ctx);
|
|
625
981
|
}
|
|
@@ -1863,7 +2219,7 @@ function promotionGate(opts) {
|
|
|
1863
2219
|
}
|
|
1864
2220
|
|
|
1865
2221
|
// src/runtime/run-benchmark.ts
|
|
1866
|
-
import { pairedBootstrap, paretoFrontier } from "@tangle-network/agent-eval";
|
|
2222
|
+
import { pairedBootstrap as pairedBootstrap2, paretoFrontier } from "@tangle-network/agent-eval";
|
|
1867
2223
|
|
|
1868
2224
|
// src/runtime/strategy.ts
|
|
1869
2225
|
import { createChatClient, estimateCost, isModelPriced } from "@tangle-network/agent-eval";
|
|
@@ -2560,15 +2916,15 @@ async function runBenchmark(cfg) {
|
|
|
2560
2916
|
const ok = perTask.filter(
|
|
2561
2917
|
(r) => !!r.cells
|
|
2562
2918
|
);
|
|
2563
|
-
const
|
|
2919
|
+
const mean2 = (xs) => xs.length ? xs.reduce((s, x) => s + x, 0) / xs.length : 0;
|
|
2564
2920
|
const perStrategy = {};
|
|
2565
2921
|
for (const s of strategies) {
|
|
2566
2922
|
const cells = ok.map((r) => r.cells[s.name]).filter((c) => !!c);
|
|
2567
2923
|
perStrategy[s.name] = {
|
|
2568
|
-
score:
|
|
2569
|
-
resolved:
|
|
2570
|
-
usd:
|
|
2571
|
-
ms:
|
|
2924
|
+
score: mean2(cells.map((c) => c.score)),
|
|
2925
|
+
resolved: mean2(cells.map((c) => c.resolved ? 1 : 0)),
|
|
2926
|
+
usd: mean2(cells.map((c) => c.usd)),
|
|
2927
|
+
ms: mean2(cells.map((c) => c.ms))
|
|
2572
2928
|
};
|
|
2573
2929
|
}
|
|
2574
2930
|
const frontier = paretoFrontier(
|
|
@@ -2587,7 +2943,7 @@ async function runBenchmark(cfg) {
|
|
|
2587
2943
|
};
|
|
2588
2944
|
const names = strategies.map((s) => s.name);
|
|
2589
2945
|
if (names.includes("refine") && names.includes("sample") && ok.length >= 2) {
|
|
2590
|
-
const b =
|
|
2946
|
+
const b = pairedBootstrap2(
|
|
2591
2947
|
ok.map((r) => r.cells.sample?.score ?? 0),
|
|
2592
2948
|
ok.map((r) => r.cells.refine?.score ?? 0)
|
|
2593
2949
|
);
|
|
@@ -2596,7 +2952,7 @@ async function runBenchmark(cfg) {
|
|
|
2596
2952
|
return report;
|
|
2597
2953
|
}
|
|
2598
2954
|
function printBenchmarkReport(report) {
|
|
2599
|
-
const
|
|
2955
|
+
const pct2 = (x) => `${(x * 100).toFixed(1)}%`;
|
|
2600
2956
|
const pp = (x) => `${x >= 0 ? "+" : ""}${(x * 100).toFixed(1)}pp`;
|
|
2601
2957
|
console.log(
|
|
2602
2958
|
`
|
|
@@ -2607,7 +2963,7 @@ function printBenchmarkReport(report) {
|
|
|
2607
2963
|
);
|
|
2608
2964
|
for (const [s, v] of Object.entries(report.perStrategy))
|
|
2609
2965
|
console.log(
|
|
2610
|
-
` ${(report.pareto.includes(s) ? `${s} *` : s).padEnd(16)} ${
|
|
2966
|
+
` ${(report.pareto.includes(s) ? `${s} *` : s).padEnd(16)} ${pct2(v.score).padStart(7)} ${pct2(v.resolved).padStart(9)} ${`$${v.usd.toFixed(3)}`.padStart(8)} ${(v.ms / 1e3).toFixed(0).padStart(6)}s`
|
|
2611
2967
|
);
|
|
2612
2968
|
if (report.pareto.length) console.log(` * = on the (score, $) Pareto frontier`);
|
|
2613
2969
|
for (const row of report.perTask)
|
|
@@ -3824,6 +4180,153 @@ function worktreeFanout(options) {
|
|
|
3824
4180
|
});
|
|
3825
4181
|
}
|
|
3826
4182
|
|
|
4183
|
+
// src/runtime/supervise-surface.ts
|
|
4184
|
+
function captureFailures(base) {
|
|
4185
|
+
let lastReport = "";
|
|
4186
|
+
const surface = {
|
|
4187
|
+
name: base.name,
|
|
4188
|
+
open: (t) => base.open(t),
|
|
4189
|
+
tools: (t, h) => base.tools(t, h),
|
|
4190
|
+
async call(h, name, args) {
|
|
4191
|
+
const out = await base.call(h, name, args);
|
|
4192
|
+
if (name === "run_tests") lastReport = out;
|
|
4193
|
+
return out;
|
|
4194
|
+
},
|
|
4195
|
+
score: (t, h) => base.score(t, h),
|
|
4196
|
+
close: (h) => base.close(h)
|
|
4197
|
+
};
|
|
4198
|
+
const failing = () => {
|
|
4199
|
+
const body = /FAILING:\s*(.+)/i.exec(lastReport)?.[1];
|
|
4200
|
+
return body ? body.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
4201
|
+
};
|
|
4202
|
+
return { surface, failing };
|
|
4203
|
+
}
|
|
4204
|
+
function failuresAnalyst() {
|
|
4205
|
+
return {
|
|
4206
|
+
kinds: [
|
|
4207
|
+
{
|
|
4208
|
+
id: "failures",
|
|
4209
|
+
description: "Surface the worker's still-failing tests so the driver targets them next.",
|
|
4210
|
+
area: "progress"
|
|
4211
|
+
}
|
|
4212
|
+
],
|
|
4213
|
+
run: async (_kindId, trace) => {
|
|
4214
|
+
const w = trace ?? {};
|
|
4215
|
+
if (!(typeof w === "object" && w !== null && "resolved" in w))
|
|
4216
|
+
return { summary: `worker produced: ${JSON.stringify(trace).slice(0, 300)}` };
|
|
4217
|
+
if (w.resolved) return { summary: "worker RESOLVED \u2014 every check passed; stop." };
|
|
4218
|
+
const failing = w.failing ?? [];
|
|
4219
|
+
const head = `worker did NOT resolve \u2014 score ${(100 * (w.score ?? 0)).toFixed(0)}%, ${w.shots ?? "?"} shot(s)`;
|
|
4220
|
+
return {
|
|
4221
|
+
summary: failing.length ? `${head}. STILL FAILING (${failing.length}): ${failing.slice(0, 12).join(", ")}. Spawn the next worker to fix exactly these; if a test keeps failing across workers, give it concrete guidance about that case.` : `${head}. (no failing-test list available this round)`
|
|
4222
|
+
};
|
|
4223
|
+
}
|
|
4224
|
+
};
|
|
4225
|
+
}
|
|
4226
|
+
function surfaceWorkerExecutor(surface, task, worker, strategy) {
|
|
4227
|
+
let artifact;
|
|
4228
|
+
return {
|
|
4229
|
+
runtime: "surface-worker",
|
|
4230
|
+
async execute(brief) {
|
|
4231
|
+
const guidance = typeof brief === "string" ? brief.trim() : brief ? JSON.stringify(brief) : "";
|
|
4232
|
+
const attemptTask = guidance ? {
|
|
4233
|
+
...task,
|
|
4234
|
+
systemPrompt: `${task.systemPrompt ?? ""}
|
|
4235
|
+
|
|
4236
|
+
\u2014 Supervisor guidance for THIS attempt (incorporate it; do not just repeat a prior approach) \u2014
|
|
4237
|
+
${guidance}`
|
|
4238
|
+
} : task;
|
|
4239
|
+
const cap = captureFailures(surface);
|
|
4240
|
+
const r = await runAgentic({
|
|
4241
|
+
surface: cap.surface,
|
|
4242
|
+
task: attemptTask,
|
|
4243
|
+
strategy,
|
|
4244
|
+
budget: worker.budget ?? 1,
|
|
4245
|
+
routerBaseUrl: worker.routerBaseUrl,
|
|
4246
|
+
routerKey: worker.routerKey,
|
|
4247
|
+
model: worker.model,
|
|
4248
|
+
...worker.maxTokens !== void 0 ? { maxTokens: worker.maxTokens } : {},
|
|
4249
|
+
...worker.innerTurns !== void 0 ? { innerTurns: worker.innerTurns } : {}
|
|
4250
|
+
});
|
|
4251
|
+
const out = {
|
|
4252
|
+
resolved: r.resolved,
|
|
4253
|
+
score: r.score,
|
|
4254
|
+
shots: r.shots,
|
|
4255
|
+
summary: `${strategy.name} ${r.shots} shot(s) \u2192 ${(100 * r.score).toFixed(0)}% (${r.resolved ? "resolved" : "unresolved"})`,
|
|
4256
|
+
failing: r.resolved ? [] : cap.failing()
|
|
4257
|
+
};
|
|
4258
|
+
const spent = { iterations: r.completions, tokens: r.tokens, usd: r.usd, ms: r.ms };
|
|
4259
|
+
artifact = {
|
|
4260
|
+
outRef: `surface-worker:${task.id}:${r.shots}:${r.resolved ? "ok" : "no"}`,
|
|
4261
|
+
out,
|
|
4262
|
+
verdict: { valid: r.resolved, score: r.score },
|
|
4263
|
+
spent
|
|
4264
|
+
};
|
|
4265
|
+
return artifact;
|
|
4266
|
+
},
|
|
4267
|
+
teardown: () => Promise.resolve({ destroyed: true }),
|
|
4268
|
+
resultArtifact() {
|
|
4269
|
+
if (!artifact) throw new Error("surfaceWorkerExecutor: resultArtifact before execute");
|
|
4270
|
+
return artifact;
|
|
4271
|
+
}
|
|
4272
|
+
};
|
|
4273
|
+
}
|
|
4274
|
+
async function superviseSurface(profile, task, opts) {
|
|
4275
|
+
const strategy = opts.strategy ?? refine;
|
|
4276
|
+
const innerTurns = opts.worker.innerTurns ?? 6;
|
|
4277
|
+
const router = opts.router ?? {
|
|
4278
|
+
routerBaseUrl: opts.worker.routerBaseUrl,
|
|
4279
|
+
routerKey: opts.worker.routerKey,
|
|
4280
|
+
model: opts.worker.model
|
|
4281
|
+
};
|
|
4282
|
+
const budget = opts.budget ?? {
|
|
4283
|
+
maxIterations: (innerTurns + 2) * 5 + 16,
|
|
4284
|
+
maxTokens: (opts.worker.maxTokens ?? 4e3) * 8
|
|
4285
|
+
};
|
|
4286
|
+
const makeWorkerAgent = (rawProfile) => {
|
|
4287
|
+
const p = rawProfile ?? {};
|
|
4288
|
+
const name = typeof p.name === "string" && p.name.length > 0 ? p.name : "surface-worker";
|
|
4289
|
+
const spec = {
|
|
4290
|
+
profile: rawProfile,
|
|
4291
|
+
harness: null,
|
|
4292
|
+
executor: surfaceWorkerExecutor(
|
|
4293
|
+
opts.surface,
|
|
4294
|
+
task,
|
|
4295
|
+
opts.worker,
|
|
4296
|
+
strategy
|
|
4297
|
+
)
|
|
4298
|
+
};
|
|
4299
|
+
return { name, act: async () => "", executorSpec: spec };
|
|
4300
|
+
};
|
|
4301
|
+
const deliverable = {
|
|
4302
|
+
describe: `resolve the surface task ${task.id} (every required check passes)`,
|
|
4303
|
+
check: (out2) => out2?.resolved === true
|
|
4304
|
+
};
|
|
4305
|
+
const analysts = opts.analysts === null ? void 0 : opts.analysts ?? failuresAnalyst();
|
|
4306
|
+
const result = await supervise(profile, task, {
|
|
4307
|
+
makeWorkerAgent,
|
|
4308
|
+
deliverable,
|
|
4309
|
+
budget,
|
|
4310
|
+
maxLiveWorkers: opts.maxLiveWorkers ?? 1,
|
|
4311
|
+
// A SMALL per-worker reservation so MULTIPLE workers fit the pool (the default reserves the whole pool
|
|
4312
|
+
// per worker → only one ever spawns, defeating the spawn-a-targeted-worker steering).
|
|
4313
|
+
perWorker: { maxIterations: innerTurns + 2, maxTokens: opts.worker.maxTokens ?? 4e3 },
|
|
4314
|
+
router,
|
|
4315
|
+
...analysts ? { analysts, analyzeOnSettle: analysts.kinds.map((k) => k.id) } : {}
|
|
4316
|
+
});
|
|
4317
|
+
const out = result.kind === "winner" ? result.out : void 0;
|
|
4318
|
+
const sp = result.spentTotal;
|
|
4319
|
+
return {
|
|
4320
|
+
resolved: out?.resolved ?? false,
|
|
4321
|
+
score: out?.score ?? 0,
|
|
4322
|
+
usd: sp.usd,
|
|
4323
|
+
tokensIn: sp.tokens.input,
|
|
4324
|
+
tokensOut: sp.tokens.output,
|
|
4325
|
+
ms: sp.ms,
|
|
4326
|
+
completions: sp.iterations
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
|
|
3827
4330
|
// src/runtime/verifier-environment.ts
|
|
3828
4331
|
var submitTool = {
|
|
3829
4332
|
type: "function",
|
|
@@ -4116,6 +4619,12 @@ export {
|
|
|
4116
4619
|
renderAnytimeTable,
|
|
4117
4620
|
defaultAuditorInstruction,
|
|
4118
4621
|
auditIntent,
|
|
4622
|
+
leaderboard,
|
|
4623
|
+
pairwiseSignificance,
|
|
4624
|
+
renderLeaderboardMarkdown,
|
|
4625
|
+
renderPairwiseMarkdown,
|
|
4626
|
+
renderLeaderboardSvg,
|
|
4627
|
+
renderLeaderboardHtml,
|
|
4119
4628
|
completionAuthorizes,
|
|
4120
4629
|
stopSentinel,
|
|
4121
4630
|
sentinelCompletion,
|
|
@@ -4127,6 +4636,7 @@ export {
|
|
|
4127
4636
|
inProcessSandboxClient,
|
|
4128
4637
|
inlineSandboxClient,
|
|
4129
4638
|
reportLoopUsage,
|
|
4639
|
+
loopCampaignDispatch,
|
|
4130
4640
|
loopDispatch,
|
|
4131
4641
|
createMcpEnvironment,
|
|
4132
4642
|
assertTraceDerivedFindings,
|
|
@@ -4182,6 +4692,8 @@ export {
|
|
|
4182
4692
|
sandboxSessionTraceSource,
|
|
4183
4693
|
analyzeTrace,
|
|
4184
4694
|
worktreeFanout,
|
|
4695
|
+
failuresAnalyst,
|
|
4696
|
+
superviseSurface,
|
|
4185
4697
|
createVerifierEnvironment,
|
|
4186
4698
|
createWaterfallCollector,
|
|
4187
4699
|
localShell,
|
|
@@ -4191,4 +4703,4 @@ export {
|
|
|
4191
4703
|
computeFindingId,
|
|
4192
4704
|
makeFinding2 as makeFinding
|
|
4193
4705
|
};
|
|
4194
|
-
//# sourceMappingURL=chunk-
|
|
4706
|
+
//# sourceMappingURL=chunk-AHZ3YBL6.js.map
|