@tangle-network/agent-eval 1.0.3 → 1.0.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/analyst/index.js +5 -5
  3. package/dist/{benchmark-command-BeDZB3BQ.js → benchmark-command-C3mZhNh7.js} +5 -5
  4. package/dist/{benchmark-command-BeDZB3BQ.js.map → benchmark-command-C3mZhNh7.js.map} +1 -1
  5. package/dist/benchmarks/index.js +3 -3
  6. package/dist/builder-eval/index.d.ts +1 -1
  7. package/dist/builder-eval/index.js +1 -1
  8. package/dist/campaign/index.d.ts +1 -1
  9. package/dist/campaign/index.js +5 -5
  10. package/dist/{campaign-DKZiYHUq.js → campaign-CqCWhH7v.js} +5 -5
  11. package/dist/{campaign-DKZiYHUq.js.map → campaign-CqCWhH7v.js.map} +1 -1
  12. package/dist/cli.js +1 -1
  13. package/dist/contract/index.js +4 -4
  14. package/dist/{define-agent-eval-BVhGn7VV.js → define-agent-eval-BT1SOjkZ.js} +3 -3
  15. package/dist/{define-agent-eval-BVhGn7VV.js.map → define-agent-eval-BT1SOjkZ.js.map} +1 -1
  16. package/dist/{dspy-rlm-engine-C0zR2xiM.js → dspy-rlm-engine-Xt3znknf.js} +2 -2
  17. package/dist/{dspy-rlm-engine-C0zR2xiM.js.map → dspy-rlm-engine-Xt3znknf.js.map} +1 -1
  18. package/dist/{external-optimizer-process-DKgrrX0J.js → external-optimizer-process-BgitfZ-Q.js} +2 -2
  19. package/dist/{external-optimizer-process-DKgrrX0J.js.map → external-optimizer-process-BgitfZ-Q.js.map} +1 -1
  20. package/dist/{external-optimizer-subprocess-wzMaA0oN.js → external-optimizer-subprocess-CeHuAaMJ.js} +8 -17
  21. package/dist/{external-optimizer-subprocess-wzMaA0oN.js.map → external-optimizer-subprocess-CeHuAaMJ.js.map} +1 -1
  22. package/dist/{findings-store-DNSvmy8U.js → findings-store-Bp-0Guuv.js} +2 -2
  23. package/dist/{findings-store-DNSvmy8U.js.map → findings-store-Bp-0Guuv.js.map} +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +4 -4
  26. package/dist/{llm-judge-BYBLxREr.js → llm-judge-BiUljZDR.js} +3 -3
  27. package/dist/{llm-judge-BYBLxREr.js.map → llm-judge-BiUljZDR.js.map} +1 -1
  28. package/dist/openapi.json +1 -1
  29. package/dist/{proposal-findings-duV5flPv.js → proposal-findings-BclX0PFh.js} +2 -2
  30. package/dist/{proposal-findings-duV5flPv.js.map → proposal-findings-BclX0PFh.js.map} +1 -1
  31. package/dist/{sandbox-harness-BuacGaVn.js → sandbox-harness-BDr1GRm0.js} +32 -21
  32. package/dist/sandbox-harness-BDr1GRm0.js.map +1 -0
  33. package/dist/{sandbox-harness--VUvMji7.d.ts → sandbox-harness-CdO35T2g.d.ts} +15 -5
  34. package/dist/sandbox-harness-CdO35T2g.d.ts.map +1 -0
  35. package/dist/{skillopt-optimization-method-DmfZz-2d.js → skillopt-optimization-method-DpasRfXS.js} +4 -4
  36. package/dist/{skillopt-optimization-method-DmfZz-2d.js.map → skillopt-optimization-method-DpasRfXS.js.map} +1 -1
  37. package/docs/concepts.md +5 -0
  38. package/package.json +1 -1
  39. package/dist/sandbox-harness--VUvMji7.d.ts.map +0 -1
  40. package/dist/sandbox-harness-BuacGaVn.js.map +0 -1
package/dist/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "@tangle-network/agent-eval — wire protocol",
5
- "version": "1.0.3",
5
+ "version": "1.0.4",
6
6
  "description": "HTTP and stdio RPC interface to agent-eval. The TypeScript runtime is the source of truth; this spec is the contract that cross-language clients (Python, Rust, Go) generate from.\n\nWire-protocol version: 1.1.0. Bumps on breaking changes to request/response schemas.",
7
7
  "contact": {
8
8
  "name": "Tangle Network",
@@ -1,4 +1,4 @@
1
- import { S as ProposalFindingSchema } from "./external-optimizer-subprocess-wzMaA0oN.js";
1
+ import { S as ProposalFindingSchema } from "./external-optimizer-subprocess-CeHuAaMJ.js";
2
2
  // src/analyst/proposal-findings.ts
3
3
  /** True when a finding names a source candidate generation may learn from. */
4
4
  function isProposalFinding(finding) {
@@ -22,4 +22,4 @@ function findingLabel(finding, index) {
22
22
  }
23
23
  export { assertProposalFindings as t };
24
24
 
25
- //# sourceMappingURL=proposal-findings-duV5flPv.js.map
25
+ //# sourceMappingURL=proposal-findings-BclX0PFh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"proposal-findings-duV5flPv.js","names":[],"sources":["../src/analyst/proposal-findings.ts"],"sourcesContent":["import { ProposalFindingSchema } from './finding-scope'\nimport type { ProposalFinding } from './types'\n\n/** True when a finding names a source candidate generation may learn from. */\nfunction isProposalFinding(finding: unknown): finding is ProposalFinding {\n return ProposalFindingSchema.safeParse(finding).success\n}\n\n/**\n * Reject findings whose source has not been explicitly admitted for candidate\n * generation. Search feedback and observed production behavior are allowed;\n * final evaluation data has no allowed origin.\n */\nexport function assertProposalFindings(\n findings: unknown,\n context = 'proposal findings',\n): ReadonlyArray<ProposalFinding> {\n if (!Array.isArray(findings)) {\n throw new TypeError(`${context}: expected an array`)\n }\n const rejected = findings.flatMap((finding, index) =>\n isProposalFinding(finding) ? [] : [findingLabel(finding, index)],\n )\n if (rejected.length > 0) {\n throw new Error(\n `${context}: every finding must match AnalystFinding and declare ` +\n `proposal_origin as search or production. ` +\n `Rejected findings: [${rejected.join(', ')}].`,\n )\n }\n return findings as ReadonlyArray<ProposalFinding>\n}\n\nfunction findingLabel(finding: unknown, index: number): string {\n if (typeof finding !== 'object' || finding === null) return `index ${index}`\n const id = (finding as { finding_id?: unknown }).finding_id\n return typeof id === 'string' && id.length > 0 ? id : `index ${index}`\n}\n"],"mappings":";;;AAIA,SAAS,kBAAkB,SAA8C;CACvE,OAAO,sBAAsB,UAAU,OAAO,CAAC,CAAC;AAClD;;;;;;AAOA,SAAgB,uBACd,UACA,UAAU,qBACsB;CAChC,IAAI,CAAC,MAAM,QAAQ,QAAQ,GACzB,MAAM,IAAI,UAAU,GAAG,QAAQ,oBAAoB;CAErD,MAAM,WAAW,SAAS,SAAS,SAAS,UAC1C,kBAAkB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,CAAC,CACjE;CACA,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,MACR,GAAG,QAAQ,qHAEc,SAAS,KAAK,IAAI,EAAE,GAC/C;CAEF,OAAO;AACT;AAEA,SAAS,aAAa,SAAkB,OAAuB;CAC7D,IAAI,OAAO,YAAY,YAAY,YAAY,MAAM,OAAO,SAAS;CACrE,MAAM,KAAM,QAAqC;CACjD,OAAO,OAAO,OAAO,YAAY,GAAG,SAAS,IAAI,KAAK,SAAS;AACjE"}
1
+ {"version":3,"file":"proposal-findings-BclX0PFh.js","names":[],"sources":["../src/analyst/proposal-findings.ts"],"sourcesContent":["import { ProposalFindingSchema } from './finding-scope'\nimport type { ProposalFinding } from './types'\n\n/** True when a finding names a source candidate generation may learn from. */\nfunction isProposalFinding(finding: unknown): finding is ProposalFinding {\n return ProposalFindingSchema.safeParse(finding).success\n}\n\n/**\n * Reject findings whose source has not been explicitly admitted for candidate\n * generation. Search feedback and observed production behavior are allowed;\n * final evaluation data has no allowed origin.\n */\nexport function assertProposalFindings(\n findings: unknown,\n context = 'proposal findings',\n): ReadonlyArray<ProposalFinding> {\n if (!Array.isArray(findings)) {\n throw new TypeError(`${context}: expected an array`)\n }\n const rejected = findings.flatMap((finding, index) =>\n isProposalFinding(finding) ? [] : [findingLabel(finding, index)],\n )\n if (rejected.length > 0) {\n throw new Error(\n `${context}: every finding must match AnalystFinding and declare ` +\n `proposal_origin as search or production. ` +\n `Rejected findings: [${rejected.join(', ')}].`,\n )\n }\n return findings as ReadonlyArray<ProposalFinding>\n}\n\nfunction findingLabel(finding: unknown, index: number): string {\n if (typeof finding !== 'object' || finding === null) return `index ${index}`\n const id = (finding as { finding_id?: unknown }).finding_id\n return typeof id === 'string' && id.length > 0 ? id : `index ${index}`\n}\n"],"mappings":";;;AAIA,SAAS,kBAAkB,SAA8C;CACvE,OAAO,sBAAsB,UAAU,OAAO,CAAC,CAAC;AAClD;;;;;;AAOA,SAAgB,uBACd,UACA,UAAU,qBACsB;CAChC,IAAI,CAAC,MAAM,QAAQ,QAAQ,GACzB,MAAM,IAAI,UAAU,GAAG,QAAQ,oBAAoB;CAErD,MAAM,WAAW,SAAS,SAAS,SAAS,UAC1C,kBAAkB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,CAAC,CACjE;CACA,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,MACR,GAAG,QAAQ,qHAEc,SAAS,KAAK,IAAI,EAAE,GAC/C;CAEF,OAAO;AACT;AAEA,SAAS,aAAa,SAAkB,OAAuB;CAC7D,IAAI,OAAO,YAAY,YAAY,YAAY,MAAM,OAAO,SAAS;CACrE,MAAM,KAAM,QAAqC;CACjD,OAAO,OAAO,OAAO,YAAY,GAAG,SAAS,IAAI,KAAK,SAAS;AACjE"}
@@ -8,16 +8,15 @@ var SubprocessSandboxDriver = class {
8
8
  this.options = options;
9
9
  }
10
10
  async exec(phase, command, config) {
11
- const effectiveEnv = {
12
- ...process.env,
13
- ...this.options.env ?? {},
14
- ...config.env ?? {}
15
- };
16
11
  const result = await runBoundedProcess({
17
12
  command,
18
13
  shell: true,
19
14
  cwd: config.cwd ?? this.options.cwd,
20
- env: effectiveEnv,
15
+ env: {
16
+ ...process.env,
17
+ ...this.options.env,
18
+ ...config.env
19
+ },
21
20
  envMode: "replace",
22
21
  timeoutMs: config.timeoutMs ?? 6e5,
23
22
  maxOutputBytes: config.maxOutputBytes ?? 16777216
@@ -80,12 +79,10 @@ var SandboxHarness = class {
80
79
  result.passed = !test || !test.killedByTimeout && test.exitCode === 0;
81
80
  result.score = test?.testsTotal !== void 0 && test.testsTotal > 0 ? (test.testsPassed ?? 0) / test.testsTotal : result.passed ? 1 : 0;
82
81
  await handle.end({
83
- ...test ? {
84
- exitCode: test.exitCode,
85
- testsTotal: test.testsTotal,
86
- testsPassed: test.testsPassed,
87
- status: result.passed ? "ok" : "error"
88
- } : {},
82
+ exitCode: test?.exitCode,
83
+ testsTotal: test?.testsTotal,
84
+ testsPassed: test?.testsPassed,
85
+ status: result.passed ? "ok" : "error",
89
86
  wallMs: result.totalWallMs
90
87
  });
91
88
  } catch (err) {
@@ -104,15 +101,12 @@ async function runSandboxScenario(spec, options = {}) {
104
101
  assertions: [],
105
102
  timings: []
106
103
  };
104
+ let measurementFailures = 0;
107
105
  const retirements = [];
108
106
  const scope = {
109
107
  signal,
110
- check: (...assertions) => {
111
- report.assertions.push(...assertions);
112
- },
113
- defer: (retire) => {
114
- retirements.push(retire);
115
- },
108
+ check: (...assertions) => report.assertions.push(...assertions),
109
+ defer: (retire) => retirements.push(retire),
116
110
  async measure(name, run) {
117
111
  const start = performance.now();
118
112
  let measured = {
@@ -135,9 +129,24 @@ async function runSandboxScenario(spec, options = {}) {
135
129
  error: error instanceof Error ? error.message : String(error)
136
130
  };
137
131
  }
132
+ if (!measured.success) measurementFailures++;
138
133
  report.timings.push(measured);
139
134
  return measured;
140
135
  },
136
+ async resource(input) {
137
+ let resource;
138
+ const timing = await scope.measure(input.name, async () => {
139
+ const owned = await input.create(signal);
140
+ resource = owned;
141
+ scope.defer(() => input.retire(owned));
142
+ input.onCreate?.(owned);
143
+ await input.ready?.(owned, signal);
144
+ });
145
+ return {
146
+ resource,
147
+ timing
148
+ };
149
+ },
141
150
  async poll(read, ready, budget) {
142
151
  if (!(budget.timeoutMs > 0 && budget.intervalMs > 0)) throw new Error("Polling timeout and interval must be positive");
143
152
  const deadline = performance.now() + budget.timeoutMs;
@@ -152,10 +161,12 @@ async function runSandboxScenario(spec, options = {}) {
152
161
  try {
153
162
  signal.throwIfAborted();
154
163
  const state = await spec.setup(scope);
155
- for (const step of spec.steps) {
164
+ for (const step of spec.steps ?? []) {
156
165
  signal.throwIfAborted();
157
166
  if (step.when && !step.when(state)) continue;
167
+ const failuresBefore = measurementFailures;
158
168
  const timing = await scope.measure(step.name, () => step.run(state, scope));
169
+ if (step.advisory) measurementFailures = failuresBefore;
159
170
  scope.check(...step.expect?.(state, timing) ?? []);
160
171
  }
161
172
  signal.throwIfAborted();
@@ -173,9 +184,9 @@ async function runSandboxScenario(spec, options = {}) {
173
184
  });
174
185
  }
175
186
  }
176
- report.pass = !report.error && report.timings.every((t) => t.success) && report.assertions.every((a) => a.soft || a.status === "pass");
187
+ report.pass = !report.error && measurementFailures === 0 && report.assertions.every((a) => a.soft || a.status === "pass");
177
188
  return report;
178
189
  }
179
190
  export { runSandboxScenario as n, SandboxHarness as t };
180
191
 
181
- //# sourceMappingURL=sandbox-harness-BuacGaVn.js.map
192
+ //# sourceMappingURL=sandbox-harness-BDr1GRm0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-harness-BDr1GRm0.js","names":["delay"],"sources":["../src/sandbox-harness.ts"],"sourcesContent":["import { setTimeout as delay } from 'node:timers/promises'\nimport { runBoundedProcess } from './bounded-process'\n\nimport type { TraceEmitter } from './trace/emitter'\nimport type { SandboxSpan } from './trace/schema'\n\nexport interface HarnessConfig {\n /** Setup command (e.g. \"pnpm install\"). Non-zero exit fails the run. */\n setupCommand?: string\n /** Run command (e.g. \"pnpm build\"). */\n runCommand?: string\n /** Test command (e.g. \"pnpm test --run\"). Drives the test count + pass count. */\n testCommand?: string\n cwd?: string\n /** Max wall-clock per phase in ms. Default 10 minutes. */\n timeoutMs?: number\n /** Captured stdout + stderr cap, default 16 MiB; excess is dropped. */\n maxOutputBytes?: number\n image?: string\n env?: Record<string, string>\n /** Parser for the test output — maps stdout/stderr/exit code → pass count. */\n testParser?: TestOutputParser\n}\n\nexport interface TestOutputParser {\n id: string\n parse(\n stdout: string,\n stderr: string,\n exitCode: number,\n ): { testsTotal: number; testsPassed: number } | undefined\n}\n\nexport interface SandboxResult {\n phase: 'setup' | 'run' | 'test'\n exitCode: number\n stdout: string\n stderr: string\n wallMs: number\n testsTotal?: number\n testsPassed?: number\n /** A timed-out phase always fails, even with exitCode 0. Absent/false means natural exit. */\n killedByTimeout?: boolean\n /** Captured output exceeded maxOutputBytes. */\n outputTruncated?: boolean\n}\n\nexport interface SandboxDriver {\n id: string\n exec(\n phase: SandboxResult['phase'],\n command: string,\n config: HarnessConfig,\n ): Promise<SandboxResult>\n}\n\ninterface SubprocessSandboxDriverOptions {\n cwd?: string\n env?: Record<string, string>\n}\n\nexport class SubprocessSandboxDriver implements SandboxDriver {\n id = 'subprocess'\n constructor(private options: SubprocessSandboxDriverOptions = {}) {}\n\n async exec(\n phase: SandboxResult['phase'],\n command: string,\n config: HarnessConfig,\n ): Promise<SandboxResult> {\n const result = await runBoundedProcess({\n command,\n shell: true,\n cwd: config.cwd ?? this.options.cwd,\n env: { ...process.env, ...this.options.env, ...config.env },\n envMode: 'replace',\n timeoutMs: config.timeoutMs ?? 10 * 60_000,\n maxOutputBytes: config.maxOutputBytes ?? 16 * 1024 * 1024,\n })\n // A timed-out phase never reaches the parser: the output of a killed\n // process is a fragment, and a fragment that happens to parse would\n // report a pass for a run that never finished.\n const parsed =\n !result.killedByTimeout && phase === 'test' && config.testParser\n ? config.testParser.parse(result.stdout, result.stderr, result.exitCode)\n : undefined\n return {\n phase,\n exitCode: result.exitCode,\n stdout: result.stdout,\n stderr: result.runnerError ? result.stderr + result.runnerError : result.stderr,\n wallMs: result.wallMs,\n testsTotal: parsed?.testsTotal,\n testsPassed: parsed?.testsPassed,\n killedByTimeout: result.killedByTimeout || undefined,\n outputTruncated: result.outputTruncated || undefined,\n }\n }\n}\n\n// ── Harness orchestration ────────────────────────────────────────────\n\nexport interface SandboxHarnessResult {\n passed: boolean\n setup?: SandboxResult\n run?: SandboxResult\n test?: SandboxResult\n totalWallMs: number\n /** Final score — 0 when no tests; otherwise testsPassed/testsTotal. */\n score: number\n}\n\nexport class SandboxHarness {\n constructor(private driver: SandboxDriver = new SubprocessSandboxDriver()) {}\n\n async run(config: HarnessConfig, emitter: TraceEmitter): Promise<SandboxHarnessResult> {\n const handle = await emitter.sandbox({\n name: `sandbox(${this.driver.id})`,\n image: config.image,\n command: [config.setupCommand, config.runCommand, config.testCommand]\n .filter(Boolean)\n .join(' && '),\n })\n const result: SandboxHarnessResult = { passed: false, totalWallMs: 0, score: 0 }\n try {\n for (const phase of ['setup', 'run', 'test'] as const) {\n const command = config[`${phase}Command`]\n if (!command) continue\n const measured = await this.driver.exec(phase, command, config)\n result[phase] = measured\n result.totalWallMs += measured.wallMs\n const passed = !measured.killedByTimeout && measured.exitCode === 0\n if (phase !== 'test' && !passed) {\n await handle.fail(\n measured.killedByTimeout\n ? `${phase} timed out after ${measured.wallMs}ms`\n : `${phase} failed (exit ${measured.exitCode})`,\n {\n exitCode: measured.exitCode,\n wallMs: result.totalWallMs,\n } as Partial<SandboxSpan>,\n )\n return result\n }\n }\n const test = result.test\n result.passed = !test || (!test.killedByTimeout && test.exitCode === 0)\n result.score =\n test?.testsTotal !== undefined && test.testsTotal > 0\n ? (test.testsPassed ?? 0) / test.testsTotal\n : result.passed\n ? 1\n : 0\n await handle.end({\n exitCode: test?.exitCode,\n testsTotal: test?.testsTotal,\n testsPassed: test?.testsPassed,\n status: result.passed ? 'ok' : 'error',\n wallMs: result.totalWallMs,\n } as Partial<SandboxSpan>)\n } catch (err) {\n await handle.fail(err instanceof Error ? err : String(err))\n throw err\n }\n return result\n }\n}\n\n/** Portable integration report; products may retain extra timing and assertion fields. */\nexport interface HarnessAssertion {\n name: string\n status: 'pass' | 'fail' | 'skip'\n expected?: string\n actual?: string\n message?: string\n soft?: boolean\n}\nexport interface HarnessTiming {\n name: string\n durationMs: number\n success: boolean\n error?: string\n}\nexport interface HarnessReport {\n pass: boolean\n assertions: HarnessAssertion[]\n timings: HarnessTiming[]\n error?: string\n metadata?: Record<string, unknown>\n}\nexport interface HarnessResource<T> {\n name: string\n create(signal: AbortSignal): Promise<T>\n retire(resource: T): Promise<unknown>\n ready?: (resource: T, signal: AbortSignal) => Promise<unknown>\n onCreate?: (resource: T) => void\n}\nexport interface HarnessScope {\n signal: AbortSignal\n resource<T>(input: HarnessResource<T>): Promise<{ resource?: T; timing: HarnessTiming }>\n check(...assertions: HarnessAssertion[]): void\n measure(name: string, run: () => Promise<unknown>): Promise<HarnessTiming>\n /** Register retirement immediately after allocation; cleanup ignores workload cancellation. */\n defer(retire: () => Promise<unknown>): void\n poll<T>(\n read: () => Promise<T>,\n ready: (value: T) => boolean,\n budget: { timeoutMs: number; intervalMs: number },\n ): Promise<T | undefined>\n}\nexport interface HarnessScenario<T> {\n setup(scope: HarnessScope): Promise<T>\n steps?: ReadonlyArray<{\n name: string\n /** Diagnostic measurements retain failures without gating the scenario. */\n advisory?: boolean\n when?: (state: T) => boolean\n run(state: T, scope: HarnessScope): Promise<unknown>\n expect?: (state: T, timing: HarnessTiming) => HarnessAssertion[]\n }>\n expectations?: (state: T) => HarnessAssertion[]\n metadata?: (state: T) => Record<string, unknown>\n /** Includes setup and steps. Transports must honor scope.signal. */\n budgetMs: number\n}\n\n/** Campaigns own whole-scenario retries; polling only repeats the declared read. */\nexport async function runSandboxScenario<T>(\n spec: HarnessScenario<T>,\n options: {\n signal?: AbortSignal\n measure?: HarnessScope['measure']\n } = {},\n): Promise<HarnessReport> {\n if (!(spec.budgetMs > 0 && Number.isSafeInteger(spec.budgetMs)))\n throw new Error('Scenario budgetMs must be positive and finite')\n const signal = AbortSignal.any([\n AbortSignal.timeout(spec.budgetMs),\n ...(options.signal ? [options.signal] : []),\n ])\n const report: HarnessReport = { pass: false, assertions: [], timings: [] }\n let measurementFailures = 0\n const retirements: Array<() => Promise<unknown>> = []\n const scope: HarnessScope = {\n signal,\n check: (...assertions) => report.assertions.push(...assertions),\n defer: (retire) => retirements.push(retire),\n async measure(name, run) {\n const start = performance.now()\n let measured: HarnessTiming = { name, durationMs: 0, success: true }\n try {\n if (options.measure) measured = await options.measure(name, run)\n else {\n const result = (await run()) as { success?: boolean; error?: string } | undefined\n if (result?.success === false)\n throw new Error(result.error ?? 'measured operation returned success=false')\n measured.durationMs = Math.round(performance.now() - start)\n }\n } catch (error) {\n measured = {\n name,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n if (!measured.success) measurementFailures++\n report.timings.push(measured)\n return measured\n },\n async resource<T>(input: HarnessResource<T>) {\n let resource: T | undefined\n const timing = await scope.measure(input.name, async () => {\n const owned = await input.create(signal)\n resource = owned\n scope.defer(() => input.retire(owned))\n input.onCreate?.(owned)\n await input.ready?.(owned, signal)\n })\n return { resource, timing }\n },\n async poll(read, ready, budget) {\n if (!(budget.timeoutMs > 0 && budget.intervalMs > 0))\n throw new Error('Polling timeout and interval must be positive')\n const deadline = performance.now() + budget.timeoutMs\n while (performance.now() < deadline) {\n signal.throwIfAborted()\n const value = await read()\n if (ready(value)) return value\n await delay(\n Math.min(budget.intervalMs, Math.max(0, deadline - performance.now())),\n undefined,\n { signal },\n )\n }\n return undefined\n },\n }\n try {\n signal.throwIfAborted()\n const state = await spec.setup(scope)\n for (const step of spec.steps ?? []) {\n signal.throwIfAborted()\n if (step.when && !step.when(state)) continue\n const failuresBefore = measurementFailures\n const timing = await scope.measure(step.name, () => step.run(state, scope))\n if (step.advisory) measurementFailures = failuresBefore\n scope.check(...(step.expect?.(state, timing) ?? []))\n }\n signal.throwIfAborted()\n scope.check(...(spec.expectations?.(state) ?? []))\n report.metadata = spec.metadata?.(state)\n } catch (error) {\n report.error = error instanceof Error ? error.message : String(error)\n } finally {\n for (const retire of retirements.reverse()) {\n const measured = await scope.measure('cleanup', retire)\n if (!measured.success)\n scope.check({ name: 'cleanup', status: 'fail', message: measured.error })\n }\n }\n report.pass =\n !report.error &&\n measurementFailures === 0 &&\n report.assertions.every((a) => a.soft || a.status === 'pass')\n return report\n}\n"],"mappings":"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"}
@@ -5,15 +5,12 @@ interface HarnessConfig {
5
5
  runCommand?: string;
6
6
  /** Test command (e.g. "pnpm test --run"). Drives the test count + pass count. */
7
7
  testCommand?: string;
8
- /** Absolute cwd for the subprocess driver. Ignored by docker driver. */
9
8
  cwd?: string;
10
9
  /** Max wall-clock per phase in ms. Default 10 minutes. */
11
10
  timeoutMs?: number;
12
11
  /** Captured stdout + stderr cap, default 16 MiB; excess is dropped. */
13
12
  maxOutputBytes?: number;
14
- /** Image for the docker driver. */
15
13
  image?: string;
16
- /** Extra env vars (validated; shell-escaped). */
17
14
  env?: Record<string, string>;
18
15
  /** Parser for the test output — maps stdout/stderr/exit code → pass count. */
19
16
  testParser?: TestOutputParser;
@@ -73,8 +70,19 @@ interface HarnessReport {
73
70
  error?: string;
74
71
  metadata?: Record<string, unknown>;
75
72
  }
73
+ interface HarnessResource<T> {
74
+ name: string;
75
+ create(signal: AbortSignal): Promise<T>;
76
+ retire(resource: T): Promise<unknown>;
77
+ ready?: (resource: T, signal: AbortSignal) => Promise<unknown>;
78
+ onCreate?: (resource: T) => void;
79
+ }
76
80
  interface HarnessScope {
77
81
  signal: AbortSignal;
82
+ resource<T>(input: HarnessResource<T>): Promise<{
83
+ resource?: T;
84
+ timing: HarnessTiming;
85
+ }>;
78
86
  check(...assertions: HarnessAssertion[]): void;
79
87
  measure(name: string, run: () => Promise<unknown>): Promise<HarnessTiming>;
80
88
  /** Register retirement immediately after allocation; cleanup ignores workload cancellation. */
@@ -86,8 +94,10 @@ interface HarnessScope {
86
94
  }
87
95
  interface HarnessScenario<T> {
88
96
  setup(scope: HarnessScope): Promise<T>;
89
- steps: ReadonlyArray<{
97
+ steps?: ReadonlyArray<{
90
98
  name: string;
99
+ /** Diagnostic measurements retain failures without gating the scenario. */
100
+ advisory?: boolean;
91
101
  when?: (state: T) => boolean;
92
102
  run(state: T, scope: HarnessScope): Promise<unknown>;
93
103
  expect?: (state: T, timing: HarnessTiming) => HarnessAssertion[];
@@ -103,4 +113,4 @@ declare function runSandboxScenario<T>(spec: HarnessScenario<T>, options?: {
103
113
  measure?: HarnessScope['measure'];
104
114
  }): Promise<HarnessReport>;
105
115
  export { HarnessScope as a, SandboxHarnessResult as c, HarnessScenario as i, runSandboxScenario as l, HarnessConfig as n, HarnessTiming as o, HarnessReport as r, SandboxDriver as s, HarnessAssertion as t };
106
- //# sourceMappingURL=sandbox-harness--VUvMji7.d.ts.map
116
+ //# sourceMappingURL=sandbox-harness-CdO35T2g.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-harness-CdO35T2g.d.ts","names":[],"sources":["../src/sandbox-harness.ts"],"mappings":"A,UAMiB;;EAEf;;EAEA;;EAEA;EACA;;EAEA;;EAEA;EACA;EACA,MAAM;;EAEN,aAAa;;UAGE;EACf;EACA,MACE,gBACA,gBACA;IACG;IAAoB;;;UAGV;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;UAGe;EACf;EACA,KACE,OAAO,wBACP,iBACA,QAAQ,gBACP,QAAQ;;UAiDI;EACf;EACA,QAAQ;EACR,MAAM;EACN,OAAO;EACP;;EAEA;;;UA4De;EACf;EACA;EACA;EACA;EACA;EACA;;UAEe;EACf;EACA;EACA;EACA;;UAEe;EACf;EACA,YAAY;EACZ,SAAS;EACT;EACA,WAAW;;UAEI,gBAAgB;EAC/B;EACA,OAAO,QAAQ,cAAc,QAAQ;EACrC,OAAO,UAAU,IAAI;EACrB,SAAS,UAAU,GAAG,QAAQ,gBAAgB;EAC9C,YAAY,UAAU;;UAEP;EACf,QAAQ;EACR,SAAS,GAAG,OAAO,gBAAgB,KAAK;IAAU,WAAW;IAAG,QAAQ;;EACxE,SAAS,YAAY;EACrB,QAAQ,cAAc,WAAW,mBAAmB,QAAQ;;EAE5D,MAAM,cAAc;EACpB,KAAK,GACH,YAAY,QAAQ,IACpB,QAAQ,OAAO,eACf;IAAU;IAAmB;MAC5B,QAAQ;;UAEI,gBAAgB;EAC/B,MAAM,OAAO,eAAe,QAAQ;EACpC,QAAQ;IACN;;IAEA;IACA,QAAQ,OAAO;IACf,IAAI,OAAO,GAAG,OAAO,eAAe;IACpC,UAAU,OAAO,GAAG,QAAQ,kBAAkB;;EAEhD,gBAAgB,OAAO,MAAM;EAC7B,YAAY,OAAO,MAAM;;EAEzB;;;iBAIoB,mBAAmB,GACvC,MAAM,gBAAgB,IACtB;EACE,SAAS;EACT,UAAU;IAEX,QAAQ"}
@@ -4,17 +4,17 @@ import { t as decidePairedPromotion } from "./paired-promotion-decision-5Pl_qJLX
4
4
  import { a as heldoutSignificance, o as pairHoldout } from "./statistical-heldout-DYF5f10K.js";
5
5
  import { d as mapConcurrent } from "./journal-C1uLJi0w.js";
6
6
  import { n as contentHash, t as canonicalJson } from "./verdict-cache-BVH-fl18.js";
7
- import { $ as resolveRunDir, C as surfaceDiff, G as assertIndependentEvaluationSplit, J as exposeFinalEvidence, K as captureFinalEvidencePolicy, R as captureJudge, T as assertSearchHistoryAdmissionOptions, Y as reserveFinalEvidence, Z as runCampaign, b as SearchRecorder, c as readGepaCandidatePopulationArtifact, d as optimizationTokenUsageFromSummary, l as combineComparisonCosts, o as executeOptimizationMethod, q as evaluationUnitMap, s as assertGepaCandidatePopulationSummary, u as costFromLedgerSummary, w as surfaceNode, x as developmentClaim } from "./llm-judge-BYBLxREr.js";
7
+ import { $ as resolveRunDir, C as surfaceDiff, G as assertIndependentEvaluationSplit, J as exposeFinalEvidence, K as captureFinalEvidencePolicy, R as captureJudge, T as assertSearchHistoryAdmissionOptions, Y as reserveFinalEvidence, Z as runCampaign, b as SearchRecorder, c as readGepaCandidatePopulationArtifact, d as optimizationTokenUsageFromSummary, l as combineComparisonCosts, o as executeOptimizationMethod, q as evaluationUnitMap, s as assertGepaCandidatePopulationSummary, u as costFromLedgerSummary, w as surfaceNode, x as developmentClaim } from "./llm-judge-BiUljZDR.js";
8
8
  import { D as assertCompleteCampaign, T as assertCampaignDesign, f as campaignBreakdown, t as createCampaignEvidenceReceipt } from "./campaign-evidence-BICB3a9Z.js";
9
9
  import { t as deepFreezeCanonicalJson } from "./deep-freeze-CK8-2W7p.js";
10
- import { M as fsCampaignStorage, _ as resolveExternalOptimizerCallbackLimits, d as assertExternalOptimizerModelBudget, f as assertJsonValue, g as removeCredentialEnvironment, h as isExternalTextCandidate, i as startExternalOptimizerModelProxy, j as createRunCostLedger, m as isCandidateText, n as closeExternalOptimizerResources, p as assertNoCredentialValues, r as runWithCleanup, t as runExternalOptimizerProcess, v as resolveExternalOptimizerProcessLimits, y as safePathComponent } from "./external-optimizer-subprocess-wzMaA0oN.js";
10
+ import { M as fsCampaignStorage, _ as resolveExternalOptimizerCallbackLimits, d as assertExternalOptimizerModelBudget, f as assertJsonValue, g as removeCredentialEnvironment, h as isExternalTextCandidate, i as startExternalOptimizerModelProxy, j as createRunCostLedger, m as isCandidateText, n as closeExternalOptimizerResources, p as assertNoCredentialValues, r as runWithCleanup, t as runExternalOptimizerProcess, v as resolveExternalOptimizerProcessLimits, y as safePathComponent } from "./external-optimizer-subprocess-CeHuAaMJ.js";
11
11
  import { s as surfaceContentHash } from "./json-recovery-DK4MDm5r.js";
12
12
  import { r as summarizeEvaluationUnits, t as defineEvaluationClaim } from "./claim-Dod_mjk3.js";
13
13
  import { n as openSearchLedger } from "./search-ledger-BsVi-Ngv.js";
14
14
  import { t as acquireSingleRunLock } from "./single-run-lock-BGlW3OSk.js";
15
15
  import { n as isRecord } from "./json-DtvZNAU9.js";
16
16
  import { c as combineAbortSignals, f as assertServedModelPolicy } from "./llm-client-BdSiZ4qz.js";
17
- import { t as startExternalOptimizerCallback } from "./external-optimizer-process-DKgrrX0J.js";
17
+ import { t as startExternalOptimizerCallback } from "./external-optimizer-process-BgitfZ-Q.js";
18
18
  import { createHash, randomBytes } from "node:crypto";
19
19
  import { mkdir } from "node:fs/promises";
20
20
  import { isDeepStrictEqual } from "node:util";
@@ -3223,4 +3223,4 @@ async function finishSkillOptRun(context, result, outputDir, modelProxy) {
3223
3223
  }
3224
3224
  export { importExternalEvaluations as a, composeGate as c, readExternalOptimizerObservationArtifact as d, externalSurface as i, externalTextOptimizationMethod as l, compareOptimizationMethods as n, importGepaPopulation as o, gepaOptimizationMethod as r, heldOutGate as s, skillOptOptimizationMethod as t, decodeExternalTextCandidate as u };
3225
3225
 
3226
- //# sourceMappingURL=skillopt-optimization-method-DmfZz-2d.js.map
3226
+ //# sourceMappingURL=skillopt-optimization-method-DpasRfXS.js.map