@tangle-network/agent-eval 0.83.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,6 +6,10 @@ import {
6
6
  parseCorrectnessResponse,
7
7
  verifyCompletion
8
8
  } from "./chunk-YGYXHNAQ.js";
9
+ import {
10
+ parseRuntimeTrajectoryHookEvent,
11
+ projectRuntimeTrajectoryEvidence
12
+ } from "./chunk-T4SQEITX.js";
9
13
  import {
10
14
  HoldoutAuditor,
11
15
  canaryLeakView,
@@ -1747,6 +1751,107 @@ function canonicalize2(value) {
1747
1751
  return out;
1748
1752
  }
1749
1753
 
1754
+ // src/integrity/preflight.ts
1755
+ function stripSlash(url) {
1756
+ return url.replace(/\/+$/, "");
1757
+ }
1758
+ function errorMessage(body) {
1759
+ if (body == null || typeof body !== "object") return null;
1760
+ const b = body;
1761
+ if (b.error && typeof b.error.message === "string") return b.error.message;
1762
+ if (typeof b.message === "string") return b.message;
1763
+ return null;
1764
+ }
1765
+ async function preflightModels(opts) {
1766
+ const fetchImpl = opts.fetchImpl ?? fetch;
1767
+ const baseUrl = stripSlash(opts.baseUrl);
1768
+ const authHeaders = { authorization: `Bearer ${opts.apiKey}` };
1769
+ let served;
1770
+ try {
1771
+ const res = await fetchImpl(`${baseUrl}/models`, { method: "GET", headers: authHeaders });
1772
+ if (!res.ok) {
1773
+ const text = await res.text().catch(() => "");
1774
+ return {
1775
+ succeeded: false,
1776
+ value: null,
1777
+ error: `preflightModels: GET ${baseUrl}/models \u2192 ${res.status} ${text.slice(0, 400)}`
1778
+ };
1779
+ }
1780
+ const body = await res.json();
1781
+ const ids = Array.isArray(body.data) ? body.data : [];
1782
+ served = new Set(ids.map((m) => m.id).filter((id) => typeof id === "string"));
1783
+ } catch (err) {
1784
+ return {
1785
+ succeeded: false,
1786
+ value: null,
1787
+ error: `preflightModels: GET ${baseUrl}/models failed \u2014 ${err instanceof Error ? err.message : String(err)}`
1788
+ };
1789
+ }
1790
+ const results = [];
1791
+ for (const model of opts.models) {
1792
+ const listed = served.has(model);
1793
+ if (!opts.probe) {
1794
+ results.push({ model, listed, served: null, status: null, detail: null });
1795
+ continue;
1796
+ }
1797
+ try {
1798
+ const res = await fetchImpl(`${baseUrl}/chat/completions`, {
1799
+ method: "POST",
1800
+ headers: { ...authHeaders, "content-type": "application/json" },
1801
+ body: JSON.stringify({
1802
+ model,
1803
+ messages: [{ role: "user", content: "ping" }],
1804
+ max_tokens: 5
1805
+ })
1806
+ });
1807
+ let detail = null;
1808
+ if (!res.ok) {
1809
+ const body = await res.json().catch(() => null);
1810
+ detail = errorMessage(body);
1811
+ }
1812
+ results.push({ model, listed, served: res.ok, status: res.status, detail });
1813
+ } catch (err) {
1814
+ return {
1815
+ succeeded: false,
1816
+ value: null,
1817
+ error: `preflightModels: probe POST ${baseUrl}/chat/completions (model ${model}) failed \u2014 ${err instanceof Error ? err.message : String(err)}`
1818
+ };
1819
+ }
1820
+ }
1821
+ return { succeeded: true, value: results, error: null };
1822
+ }
1823
+ var ModelsUnreachableError = class extends AgentEvalError {
1824
+ constructor(message, results) {
1825
+ super("config", message);
1826
+ this.results = results;
1827
+ this.name = "ModelsUnreachableError";
1828
+ }
1829
+ results;
1830
+ };
1831
+ function describeFailure(r) {
1832
+ if (!r.listed) {
1833
+ const probeNote = r.served === false ? ` (probe ${r.status}${r.detail ? `: ${r.detail}` : ""})` : "";
1834
+ return `${r.model}: not in /models${probeNote}`;
1835
+ }
1836
+ return `${r.model}: listed but probe ${r.status}${r.detail ? ` \u2014 ${r.detail}` : ""}`;
1837
+ }
1838
+ async function assertModelsServed(opts) {
1839
+ const outcome = await preflightModels(opts);
1840
+ if (!outcome.succeeded || outcome.value === null) {
1841
+ throw new ConfigError(
1842
+ outcome.error ?? "assertModelsServed: preflight failed without an error message"
1843
+ );
1844
+ }
1845
+ const dead = outcome.value.filter((r) => !r.listed || r.served === false);
1846
+ if (dead.length > 0) {
1847
+ throw new ModelsUnreachableError(
1848
+ `assertModelsServed: ${dead.length}/${outcome.value.length} model(s) unreachable on the router \u2014 ${dead.map(describeFailure).join("; ")}`,
1849
+ outcome.value
1850
+ );
1851
+ }
1852
+ return outcome.value;
1853
+ }
1854
+
1750
1855
  // src/integrity/single-backend.ts
1751
1856
  var SingleBackendError = class extends AgentEvalError {
1752
1857
  constructor(message, report) {
@@ -1756,7 +1861,7 @@ var SingleBackendError = class extends AgentEvalError {
1756
1861
  }
1757
1862
  report;
1758
1863
  };
1759
- function stripSlash(url) {
1864
+ function stripSlash2(url) {
1760
1865
  return url.replace(/\/+$/, "");
1761
1866
  }
1762
1867
  function assertSingleBackend(agent, judge, opts = {}) {
@@ -1764,7 +1869,7 @@ function assertSingleBackend(agent, judge, opts = {}) {
1764
1869
  if (agent.kind !== judge.kind) {
1765
1870
  divergences.push({ field: "kind", agent: agent.kind, judge: judge.kind });
1766
1871
  }
1767
- if (stripSlash(agent.baseUrl) !== stripSlash(judge.baseUrl)) {
1872
+ if (stripSlash2(agent.baseUrl) !== stripSlash2(judge.baseUrl)) {
1768
1873
  divergences.push({ field: "baseUrl", agent: agent.baseUrl, judge: judge.baseUrl });
1769
1874
  }
1770
1875
  if (agent.model !== judge.model) {
@@ -2911,14 +3016,14 @@ async function runHarnessExperiment(config) {
2911
3016
  const score = config.score ?? ((trace) => critic.scoreTrace(trace));
2912
3017
  const results = await mapLimit(jobs, config.parallelism ?? 1, async (request) => {
2913
3018
  const trace = await config.adapter.run(request);
2914
- const runScore2 = await score(trace, request);
3019
+ const runScore3 = await score(trace, request);
2915
3020
  const result = {
2916
3021
  variant: request.variant,
2917
3022
  scenario: request.scenario,
2918
3023
  trialIndex: request.trialIndex,
2919
3024
  trace,
2920
- score: runScore2,
2921
- aggregate: aggregateRunScore(runScore2, config.weights)
3025
+ score: runScore3,
3026
+ aggregate: aggregateRunScore(runScore3, config.weights)
2922
3027
  };
2923
3028
  await config.onResult?.(result);
2924
3029
  return result;
@@ -7089,6 +7194,121 @@ function createDefaultReviewer(options) {
7089
7194
  };
7090
7195
  }
7091
7196
 
7197
+ // src/description-length-gate.ts
7198
+ import { gzipSync } from "zlib";
7199
+ function runScore2(run) {
7200
+ const o = run.outcome;
7201
+ const s = o.holdoutScore ?? o.searchScore ?? o.raw?.score;
7202
+ return typeof s === "number" && Number.isFinite(s) ? s : void 0;
7203
+ }
7204
+ function taskKey(run) {
7205
+ return run.scenarioId ?? run.experimentId;
7206
+ }
7207
+ function perTaskMeanScore(runs) {
7208
+ const acc = /* @__PURE__ */ new Map();
7209
+ for (const run of runs) {
7210
+ const s = runScore2(run);
7211
+ if (s === void 0) continue;
7212
+ const key = taskKey(run);
7213
+ const cur = acc.get(key) ?? { sum: 0, n: 0 };
7214
+ cur.sum += s;
7215
+ cur.n += 1;
7216
+ acc.set(key, cur);
7217
+ }
7218
+ return new Map([...acc].map(([k, v]) => [k, v.sum / v.n]));
7219
+ }
7220
+ function modelDescriptionBits(content) {
7221
+ return gzipSync(Buffer.from(content, "utf8")).byteLength * 8;
7222
+ }
7223
+ function dataDescriptionBits(scoreByTask, keys, scoreFloor) {
7224
+ let bits = 0;
7225
+ for (const key of keys) {
7226
+ const s = scoreByTask.get(key);
7227
+ if (s === void 0) continue;
7228
+ bits += -Math.log2(Math.max(s, scoreFloor));
7229
+ }
7230
+ return bits;
7231
+ }
7232
+ var DescriptionLengthGate = class {
7233
+ baselineKey;
7234
+ lambda;
7235
+ marginBits;
7236
+ scoreFloor;
7237
+ minTasks;
7238
+ constructor(config) {
7239
+ if (!config.baselineKey) throw new Error("DescriptionLengthGate: baselineKey is required");
7240
+ this.baselineKey = config.baselineKey;
7241
+ this.lambda = config.lambda ?? 1;
7242
+ this.marginBits = config.marginBits ?? 0;
7243
+ this.scoreFloor = config.scoreFloor ?? 2 ** -10;
7244
+ this.minTasks = config.minTasks ?? 3;
7245
+ if (!(this.lambda >= 0)) throw new Error("DescriptionLengthGate: lambda must be \u2265 0");
7246
+ if (!(this.scoreFloor > 0 && this.scoreFloor < 1))
7247
+ throw new Error("DescriptionLengthGate: scoreFloor must be in (0,1)");
7248
+ }
7249
+ /** Decide whether `candidate` should replace `baseline`. Both are scored on
7250
+ * the shared task set (the enlarged evidence); the candidate promotes only
7251
+ * if λ·L_model + L_data is strictly lower by at least `marginBits`. */
7252
+ evaluate(candidate, baseline) {
7253
+ const candidateId = inferCandidateId(candidate.runs, this.baselineKey);
7254
+ const candScores = perTaskMeanScore(candidate.runs);
7255
+ const baseScores = perTaskMeanScore(baseline.runs);
7256
+ const shared = [...candScores.keys()].filter((k) => baseScores.has(k));
7257
+ const modelBits = {
7258
+ candidate: this.lambda * modelDescriptionBits(candidate.content),
7259
+ baseline: this.lambda * modelDescriptionBits(baseline.content)
7260
+ };
7261
+ const dataBits = {
7262
+ candidate: dataDescriptionBits(candScores, shared, this.scoreFloor),
7263
+ baseline: dataDescriptionBits(baseScores, shared, this.scoreFloor)
7264
+ };
7265
+ const totalBits = {
7266
+ candidate: modelBits.candidate + dataBits.candidate,
7267
+ baseline: modelBits.baseline + dataBits.baseline
7268
+ };
7269
+ const evidence = {
7270
+ tasks: shared.length,
7271
+ modelBits,
7272
+ dataBits,
7273
+ totalBits,
7274
+ deltaBits: totalBits.candidate - totalBits.baseline,
7275
+ modelBitsDelta: modelBits.candidate - modelBits.baseline,
7276
+ dataBitsDelta: dataBits.candidate - dataBits.baseline
7277
+ };
7278
+ const base = { candidateId, baselineId: this.baselineKey, evidence };
7279
+ const fmt2 = (n) => n.toFixed(1);
7280
+ if (shared.length < this.minTasks) {
7281
+ return {
7282
+ ...base,
7283
+ promote: false,
7284
+ reason: `few_tasks: ${shared.length} shared task(s) < min ${this.minTasks}`,
7285
+ rejectionCode: "few_tasks"
7286
+ };
7287
+ }
7288
+ if (!(evidence.deltaBits < -this.marginBits)) {
7289
+ const code = evidence.dataBitsDelta < 0 ? "model_bloat" : "no_total_gain";
7290
+ const why = code === "model_bloat" ? `model grew ${fmt2(evidence.modelBitsDelta)} bits, outpacing a ${fmt2(-evidence.dataBitsDelta)}-bit data gain` : `outcomes did not improve (data \u0394=${fmt2(evidence.dataBitsDelta)} bits)`;
7291
+ return {
7292
+ ...base,
7293
+ promote: false,
7294
+ reason: `${code}: total \u0394=${fmt2(evidence.deltaBits)} bits does not clear the ${fmt2(this.marginBits)}-bit margin \u2014 ${why}`,
7295
+ rejectionCode: code
7296
+ };
7297
+ }
7298
+ return {
7299
+ ...base,
7300
+ promote: true,
7301
+ reason: `promote: total \u0394=${fmt2(evidence.deltaBits)} bits (model \u0394=${fmt2(evidence.modelBitsDelta)}, data \u0394=${fmt2(evidence.dataBitsDelta)}) over ${shared.length} tasks`,
7302
+ rejectionCode: null
7303
+ };
7304
+ }
7305
+ };
7306
+ function inferCandidateId(runs, baselineKey) {
7307
+ for (const run of runs)
7308
+ if (run.candidateId && run.candidateId !== baselineKey) return run.candidateId;
7309
+ return runs[0]?.candidateId ?? "(unknown candidate)";
7310
+ }
7311
+
7092
7312
  // src/discover-personas.ts
7093
7313
  import { promises as fs } from "fs";
7094
7314
  import { basename, extname, join as join3 } from "path";
@@ -7240,7 +7460,7 @@ var HeldOutGate = class {
7240
7460
  * the candidate run with the matching baseline run. Pairs without
7241
7461
  * a holdout score on both sides are dropped. */
7242
7462
  evaluate(candidate, baseline) {
7243
- const candidateId = inferCandidateId(candidate, this.baselineKey);
7463
+ const candidateId = inferCandidateId2(candidate, this.baselineKey);
7244
7464
  const baselineId = this.baselineKey;
7245
7465
  const baselineHoldoutByKey = indexHoldoutByKey(baseline);
7246
7466
  const beforeHoldout = [];
@@ -7343,7 +7563,7 @@ var HeldOutGate = class {
7343
7563
  };
7344
7564
  }
7345
7565
  };
7346
- function inferCandidateId(candidate, baselineKey) {
7566
+ function inferCandidateId2(candidate, baselineKey) {
7347
7567
  for (const run of candidate) {
7348
7568
  if (run.candidateId && run.candidateId !== baselineKey) return run.candidateId;
7349
7569
  }
@@ -8314,6 +8534,7 @@ export {
8314
8534
  DEFAULT_TRACE_ANALYST_BUDGETS,
8315
8535
  DEFAULT_TRACE_ANALYST_KINDS,
8316
8536
  Dataset,
8537
+ DescriptionLengthGate,
8317
8538
  DockerSandboxDriver,
8318
8539
  DualAgentBench,
8319
8540
  ERROR_COUNT_PATTERNS,
@@ -8342,6 +8563,7 @@ export {
8342
8563
  LockedJsonlAppender,
8343
8564
  MODEL_PRICING,
8344
8565
  MetricsCollector,
8566
+ ModelsUnreachableError,
8345
8567
  MultiLayerVerifier,
8346
8568
  Mutex,
8347
8569
  NoopRawProviderSink,
@@ -8401,6 +8623,7 @@ export {
8401
8623
  asString,
8402
8624
  assertCrossFamily,
8403
8625
  assertLlmRoute,
8626
+ assertModelsServed,
8404
8627
  assertRealBackend,
8405
8628
  assertReleaseConfidence,
8406
8629
  assertRunAgentProfileCell,
@@ -8485,6 +8708,7 @@ export {
8485
8708
  createTraceAnalystKind,
8486
8709
  crossTraceDiff,
8487
8710
  crowdingDistance,
8711
+ dataDescriptionBits,
8488
8712
  decideNextUserTurn,
8489
8713
  decideReferenceReplayPromotion,
8490
8714
  decideReferenceReplayRunPromotion,
@@ -8591,6 +8815,7 @@ export {
8591
8815
  matchGoldens,
8592
8816
  mergeLayerResults,
8593
8817
  mergeSteeringBundle,
8818
+ modelDescriptionBits,
8594
8819
  multiToolchainLayer,
8595
8820
  nistAiRmfReport,
8596
8821
  normalizeScores,
@@ -8613,16 +8838,19 @@ export {
8613
8838
  parseGoldJsonl,
8614
8839
  parseReflectionResponse,
8615
8840
  parseRunRecordSafe,
8841
+ parseRuntimeTrajectoryHookEvent,
8616
8842
  partialCredit,
8617
8843
  passOrthogonality,
8618
8844
  pixelDeltaRatio,
8619
8845
  planTraceInsightQuestions,
8620
8846
  politenessPrefixMutator,
8621
8847
  positionalBias,
8848
+ preflightModels,
8622
8849
  printDriverSummary,
8623
8850
  probeLlm,
8624
8851
  profile_exports as profile,
8625
8852
  projectOtlpFlatLine,
8853
+ projectRuntimeTrajectoryEvidence,
8626
8854
  promptBisect,
8627
8855
  proposeAutomatedPullRequest,
8628
8856
  proposeSynthesisTargets,