vigiles 36.0.0 → 37.0.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/cli-commands.d.ts +1 -1
- package/dist/cli-commands.js +1 -0
- package/dist/cli-main.js +13 -0
- package/dist/cli.js +51 -0
- package/dist/core/eval-driver.d.ts +9 -1
- package/dist/core/eval-spec-parse.d.ts +14 -0
- package/dist/core/eval-spec-parse.js +168 -0
- package/dist/core/run-env.d.ts +46 -1
- package/dist/core/run-env.js +59 -0
- package/dist/core/stub-rules.d.ts +199 -0
- package/dist/core/stub-rules.js +397 -0
- package/dist/eval-cache.d.ts +19 -1
- package/dist/eval-cache.js +1 -1
- package/dist/eval-entry.d.ts +28 -0
- package/dist/eval-entry.js +63 -10
- package/dist/eval-surface.d.ts +2 -0
- package/dist/eval.d.ts +80 -32
- package/dist/eval.js +293 -91
- package/dist/harness-test.d.ts +1 -1
- package/dist/services.d.ts +1 -1
- package/dist/services.js +1 -1
- package/dist/stub.d.ts +57 -0
- package/dist/stub.js +103 -0
- package/dist/test.d.ts +4 -1
- package/dist/test.js +8 -4
- package/dist/tool-stub.d.ts +24 -29
- package/dist/tool-stub.js +63 -74
- package/package.json +1 -1
- package/skills/test-harness/references/observing-a-run.md +13 -12
- package/skills/test-harness/references/writing-tests.md +1 -0
package/dist/eval.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RunEnv } from "./core/run-env.js";
|
|
1
2
|
import type { HarnessLiveDriver } from "./core/live-driver.js";
|
|
2
3
|
import type { AgentRunArgs, AgentRunner, EvalDriver, EvalUsage, ModelOutputParser, ParsedModelRun, RunOut } from "./core/eval-driver.js";
|
|
3
4
|
export type { AgentRunArgs, AgentRunner, EvalDriver, EvalUsage, ModelOutputParser, ParsedModelRun, RunOut, } from "./core/eval-driver.js";
|
|
@@ -7,7 +8,7 @@ import { type EvalLockOptions } from "./eval-lock.js";
|
|
|
7
8
|
import type { Check, CheckJSON } from "./check.js";
|
|
8
9
|
import { type Comparison } from "./stats.js";
|
|
9
10
|
import { type ToolIntercept } from "./tool-intercept.js";
|
|
10
|
-
import { type ToolStub } from "./
|
|
11
|
+
import { type StubCall, type ToolStub } from "./core/stub-rules.js";
|
|
11
12
|
/** One arm of the comparison: fixture overrides + settings (hooks) for this arm. */
|
|
12
13
|
export interface EvalArm {
|
|
13
14
|
/** Files written on top of the base fixture for this arm. */
|
|
@@ -156,37 +157,41 @@ export interface EvalSpec<M extends Metrics> {
|
|
|
156
157
|
/** Base backoff ms (doubled each retry). Default 1000. */
|
|
157
158
|
readonly retryBackoffMs?: number;
|
|
158
159
|
/**
|
|
159
|
-
* **
|
|
160
|
-
* throwaway `$HOME` + scrubbed env, re-injecting only the harness's own auth (see
|
|
161
|
-
* `ephemeralRunEnv`). Running a model-driven skill/agent is itself a side
|
|
162
|
-
* effect (the *model*, not the author, chose the actions), so a `git push` /
|
|
163
|
-
* write to `~` should land in a disposable HOME, not the real `~/.gitconfig` /
|
|
164
|
-
* `~/.ssh` / `~/.aws`. This is the cross-platform STATE-protection floor (no
|
|
165
|
-
* kernel features), orthogonal to the bubblewrap host-confinement in
|
|
166
|
-
* `src/sandbox.ts`.
|
|
160
|
+
* **Where each trial runs — required, no default.**
|
|
167
161
|
*
|
|
168
|
-
*
|
|
169
|
-
* the
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* `
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* **Tool stubs on PATH (rung R2).** A list of fake binaries to shadow on PATH
|
|
179
|
-
* for every trial, so a skill/hook/agent that calls a CLI tool (`gh`, `psql`,
|
|
180
|
-
* `redis-cli`, `z3`, …) and works with its RESULT can be tested against a
|
|
181
|
-
* **recorded / author-provided canned output** — no live service. vigiles writes
|
|
182
|
-
* one executable stub per {@link ToolStub} into a bin dir under the trial cwd and
|
|
183
|
-
* PREPENDS that dir to the run's PATH (both the default and the ephemeral env
|
|
184
|
-
* path), so the fake wins over the real binary.
|
|
162
|
+
* - `{ kind: "ephemeral" }` — a throwaway HOME (under the trial's temp dir) and
|
|
163
|
+
* a scrubbed environment: the OS essentials, the harness's own auth
|
|
164
|
+
* (`runEnv.keep`, plus its auth file copied in), the eval's own `VIGILES_*`
|
|
165
|
+
* variables, and nothing else. `home: { kind: "files", files }` seeds that
|
|
166
|
+
* HOME first (HOME-relative paths; the harness's auth file cannot be
|
|
167
|
+
* overwritten). Running a model-driven skill is itself a side effect, so a
|
|
168
|
+
* `git push` or a write to `~` lands in a disposable HOME.
|
|
169
|
+
* - `{ kind: "inherit", reason }` — your real HOME and environment, minus the
|
|
170
|
+
* parent session's identity. `reason` is printed with the report.
|
|
185
171
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
172
|
+
* There is no default because both are wrong silently in one case: inheriting
|
|
173
|
+
* measures the author's machine (their config, their tokens), and an
|
|
174
|
+
* ephemeral run fails if a user's credential is not where the harness says it
|
|
175
|
+
* is — loudly, on trial 1, which is why it is the one to try first. See
|
|
176
|
+
* `docs/safety.md` (what a child run inherits).
|
|
177
|
+
*/
|
|
178
|
+
readonly env: RunEnv;
|
|
179
|
+
/**
|
|
180
|
+
* **Tool stubs on PATH (rung R2)** — fake binaries that answer PER INVOCATION.
|
|
181
|
+
* Build each with {@link experimental_stub}: a list of rules over the argv
|
|
182
|
+
* tokens, first match wins. vigiles writes a shim per stub into a temp dir
|
|
183
|
+
* BESIDE the trial's working dir and puts it first on the run's PATH, so a
|
|
184
|
+
* skill/hook/agent — or a script it runs — that calls `gh`/`psql`/`git` gets
|
|
185
|
+
* the recorded answer instead of the real service.
|
|
186
|
+
*
|
|
187
|
+
* An invocation no rule answers is UNANSWERED: the model sees one neutral
|
|
188
|
+
* stderr line and a non-zero exit, no new trials start, the report lists the
|
|
189
|
+
* call (`ArmReport.unansweredStubCalls`), `vigiles eval` exits 2 and
|
|
190
|
+
* `--update` writes no lock. Every call is recorded in `RunContext.stubCalls`,
|
|
191
|
+
* so `experimental_stub.called(...)` can assert one happened.
|
|
192
|
+
*
|
|
193
|
+
* The answers are author/recorded fixtures, **never** model-synthesized — a
|
|
194
|
+
* synthesized tool output looks plausible but diverges from the real tool.
|
|
190
195
|
*/
|
|
191
196
|
readonly stubs?: readonly ToolStub[];
|
|
192
197
|
/**
|
|
@@ -236,6 +241,12 @@ export interface ArmReport {
|
|
|
236
241
|
readonly stats: Record<string, MetricStat>;
|
|
237
242
|
/** Cost / latency / token totals + means for this arm. */
|
|
238
243
|
readonly usage: ArmUsage;
|
|
244
|
+
/**
|
|
245
|
+
* Stub invocations no rule answered, across this arm's trials. Non-empty means
|
|
246
|
+
* the arm measured at least one answer nobody wrote: `vigiles eval` exits 2 on
|
|
247
|
+
* it and `--update` refuses to record the lock. Empty when the eval has no stubs.
|
|
248
|
+
*/
|
|
249
|
+
readonly unansweredStubCalls: readonly StubCall[];
|
|
239
250
|
}
|
|
240
251
|
export interface EvalReport {
|
|
241
252
|
readonly name: string;
|
|
@@ -245,10 +256,21 @@ export interface EvalReport {
|
|
|
245
256
|
readonly totalCostUsd: number;
|
|
246
257
|
/** True if a `maxCostUsd` budget cap stopped the run before all trials ran. */
|
|
247
258
|
readonly aborted: boolean;
|
|
259
|
+
/**
|
|
260
|
+
* The run environment the trials used, without seeded file contents — so an
|
|
261
|
+
* `inherit` run's reason travels with the report and is printed with it.
|
|
262
|
+
* Absent on a report built before `env` existed.
|
|
263
|
+
*/
|
|
264
|
+
readonly env?: {
|
|
265
|
+
readonly kind: "ephemeral";
|
|
266
|
+
} | {
|
|
267
|
+
readonly kind: "inherit";
|
|
268
|
+
readonly reason: string;
|
|
269
|
+
};
|
|
248
270
|
}
|
|
249
271
|
/**
|
|
250
272
|
* Resolve the environment a trial's subprocess actually runs with — the
|
|
251
|
-
* SECURITY-CRITICAL decision behind `
|
|
273
|
+
* SECURITY-CRITICAL decision behind `env: { kind: "ephemeral" }`. When `replaceEnv` is set, the
|
|
252
274
|
* scrubbed `env` is the COMPLETE environment, so the real `$HOME` and inherited
|
|
253
275
|
* secrets are DROPPED; otherwise `env` is an overlay on `base`. In BOTH, the
|
|
254
276
|
* harness's declared session identity (`runEnv.sessionIdentity`) is removed: a
|
|
@@ -501,8 +523,17 @@ export declare function measureArms(spec: ArmsMeasureSpec): Promise<ArmsCheckRep
|
|
|
501
523
|
* than vanilla" is a p-value, not a vibe. Reuses `welchTTest` from stats.ts.
|
|
502
524
|
*/
|
|
503
525
|
export declare function compareCheck(report: ArmsCheckReport, baseline: string, arm: string, checkIndex: number): Comparison;
|
|
504
|
-
/**
|
|
526
|
+
/**
|
|
527
|
+
* Format a {@link CheckReport}: one line per check with its rate ± se and
|
|
528
|
+
* pass^k, and the line saying `measure` ran with your HOME and environment
|
|
529
|
+
* (it has no `env` field; `INHERITED_BY_MEASURE`).
|
|
530
|
+
*/
|
|
505
531
|
export declare function formatCheckReport(report: CheckReport): string;
|
|
532
|
+
/**
|
|
533
|
+
* Format an {@link ArmsCheckReport}: each arm's checks under `[arm: name]`, and
|
|
534
|
+
* the inherit line ONCE at the end — every arm ran the same way.
|
|
535
|
+
*/
|
|
536
|
+
export declare function formatArmsCheckReport(report: ArmsCheckReport): string;
|
|
506
537
|
/**
|
|
507
538
|
* The scored gate (Phase 4): throw if any check's measured rate is below its
|
|
508
539
|
* threshold — the `measure` counterpart to `assertChecks` (strict). Reads the
|
|
@@ -652,6 +683,23 @@ export declare function seedEphemeralHome(throwawayHome: string, realHome: strin
|
|
|
652
683
|
export declare function isRateLimited(out: RunOut): boolean;
|
|
653
684
|
/** Map `worker` over `items` with at most `concurrency` in flight, order preserved. */
|
|
654
685
|
export declare function runPool<T, R>(items: readonly T[], concurrency: number, worker: (item: T) => Promise<R>): Promise<R[]>;
|
|
686
|
+
/**
|
|
687
|
+
* The author-facing failure for a report whose stubs left a call unanswered, or
|
|
688
|
+
* undefined. One owner for the rule — `vigiles eval`'s exit 2 and the lock's
|
|
689
|
+
* refusal both read it — so a consumer whose `assert` calls no helper still
|
|
690
|
+
* fails. Pure.
|
|
691
|
+
*/
|
|
692
|
+
export declare function unansweredInReport(report: Pick<EvalReport, "arms">, stubs: readonly ToolStub[]): string | undefined;
|
|
693
|
+
/**
|
|
694
|
+
* `runEval` with its author-facing output, over any runner — so the real one and
|
|
695
|
+
* a test's fake share one path. Besides the run it prints, to stderr, what the
|
|
696
|
+
* caller must not miss: a skill arm that never activates, a stub call no rule
|
|
697
|
+
* answered (the same text `vigiles eval` fails with — a script that calls
|
|
698
|
+
* `runEval` itself, as the `withServices` examples do, gets the report back and
|
|
699
|
+
* nothing else, so the library is not silent about it either), and what the
|
|
700
|
+
* run spent.
|
|
701
|
+
*/
|
|
702
|
+
export declare function runEvalReporting<M extends Metrics>(spec: EvalSpec<M>, runner: AgentRunner): Promise<EvalReport>;
|
|
655
703
|
export declare function runEvalWith<M extends Metrics>(input: EvalSpec<M>, runner: AgentRunner): Promise<EvalReport>;
|
|
656
704
|
/** Format an eval report as a compact table for the console (mean ± se, pass^k). */
|
|
657
705
|
export declare function formatEvalReport(report: EvalReport): string;
|