reeval 0.0.1
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/LICENSE +202 -0
- package/THIRD_PARTY_NOTICES.md +2160 -0
- package/assets/package.json +9 -0
- package/assets/pnpm-lock.yaml +1336 -0
- package/dist/agent-DVHkx8aT.js +532 -0
- package/dist/agent-DVHkx8aT.js.map +1 -0
- package/dist/analysis-CxSCt1YY.js +16 -0
- package/dist/analysis-CxSCt1YY.js.map +1 -0
- package/dist/assemble-B-nmz8oH.js +695 -0
- package/dist/assemble-B-nmz8oH.js.map +1 -0
- package/dist/assemble-CmRVDZ2Q.js +2 -0
- package/dist/attempt-BdRQ9jKX.js +176 -0
- package/dist/attempt-BdRQ9jKX.js.map +1 -0
- package/dist/bridge/index.mjs +1911 -0
- package/dist/bridge/package.json +13 -0
- package/dist/bridge/pnpm-lock.yaml +1041 -0
- package/dist/bridge/pnpm-workspace.yaml +2 -0
- package/dist/budget-DBUrH4jX.js +8 -0
- package/dist/budget-DBUrH4jX.js.map +1 -0
- package/dist/cli--CQ3Iy9q.js +1865 -0
- package/dist/cli--CQ3Iy9q.js.map +1 -0
- package/dist/cli-spec-DKAu09Gi.js +478 -0
- package/dist/cli-spec-DKAu09Gi.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -0
- package/dist/colors-QV9dFVhN.js +34 -0
- package/dist/colors-QV9dFVhN.js.map +1 -0
- package/dist/command-BSJzHMtP.js +280 -0
- package/dist/command-BSJzHMtP.js.map +1 -0
- package/dist/command-CEm8PZ1N.js +1043 -0
- package/dist/command-CEm8PZ1N.js.map +1 -0
- package/dist/command-D-jnQWVG.js +673 -0
- package/dist/command-D-jnQWVG.js.map +1 -0
- package/dist/command-DQAvbG7d.js +432 -0
- package/dist/command-DQAvbG7d.js.map +1 -0
- package/dist/commands-A4udfyKZ.js +2 -0
- package/dist/commands-CF8mOfiu.js +207 -0
- package/dist/commands-CF8mOfiu.js.map +1 -0
- package/dist/config-Cuj0kVYj.js +415 -0
- package/dist/config-Cuj0kVYj.js.map +1 -0
- package/dist/context-loi9OCZv.js +106 -0
- package/dist/context-loi9OCZv.js.map +1 -0
- package/dist/core-tLN-z1Be.js +96 -0
- package/dist/core-tLN-z1Be.js.map +1 -0
- package/dist/db-Cjj2ZsfQ.js +2 -0
- package/dist/db-DxDzMhtB.js +827 -0
- package/dist/db-DxDzMhtB.js.map +1 -0
- package/dist/debug-D24NOQH1.js +89 -0
- package/dist/debug-D24NOQH1.js.map +1 -0
- package/dist/debug-SEmW4w8f.js +2 -0
- package/dist/dist-BZXYHN2X.js +1673 -0
- package/dist/dist-BZXYHN2X.js.map +1 -0
- package/dist/docker-DQDZWXF9.js +20 -0
- package/dist/docker-DQDZWXF9.js.map +1 -0
- package/dist/docker-DnWAVdEm.js +2 -0
- package/dist/docker-oSbCExHL.js +153 -0
- package/dist/docker-oSbCExHL.js.map +1 -0
- package/dist/doctor-CxlH1pDM.js +525 -0
- package/dist/doctor-CxlH1pDM.js.map +1 -0
- package/dist/egress-73SQfKdx.js +619 -0
- package/dist/egress-73SQfKdx.js.map +1 -0
- package/dist/env-BQqj93gu.js +21 -0
- package/dist/env-BQqj93gu.js.map +1 -0
- package/dist/environment-Mz7wvrtD.js +552 -0
- package/dist/environment-Mz7wvrtD.js.map +1 -0
- package/dist/errors-C9w_ud7g.js +160 -0
- package/dist/errors-C9w_ud7g.js.map +1 -0
- package/dist/errors-CSxfJ4qu.js +11 -0
- package/dist/errors-CSxfJ4qu.js.map +1 -0
- package/dist/files--6CEUXm7.js +2 -0
- package/dist/files-BLlrhU4-.js +62 -0
- package/dist/files-BLlrhU4-.js.map +1 -0
- package/dist/files-Bd25OAQH.js +2 -0
- package/dist/files-BvHCFWgD.js +2 -0
- package/dist/files-D2-qn_Dl.js +275 -0
- package/dist/files-D2-qn_Dl.js.map +1 -0
- package/dist/files-bF2fPuT9.js +99 -0
- package/dist/files-bF2fPuT9.js.map +1 -0
- package/dist/git-D6hMeieS.js +44 -0
- package/dist/git-D6hMeieS.js.map +1 -0
- package/dist/harness-ls5aX1UO.js +131 -0
- package/dist/harness-ls5aX1UO.js.map +1 -0
- package/dist/harness.d.ts +74 -0
- package/dist/harness.js +2 -0
- package/dist/header-DzFDoMCI.js +78 -0
- package/dist/header-DzFDoMCI.js.map +1 -0
- package/dist/ids-Bp6ut7ZD.js +49 -0
- package/dist/ids-Bp6ut7ZD.js.map +1 -0
- package/dist/images-Cc1-coFc.js +286 -0
- package/dist/images-Cc1-coFc.js.map +1 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.js +1959 -0
- package/dist/index.js.map +1 -0
- package/dist/init-CFljS8BJ.js +344 -0
- package/dist/init-CFljS8BJ.js.map +1 -0
- package/dist/init-z2ZPn9Tj.js +2 -0
- package/dist/invocations-rXktgSAn.js +157 -0
- package/dist/invocations-rXktgSAn.js.map +1 -0
- package/dist/jiti-BOLXpKyG.js +46 -0
- package/dist/jiti-BOLXpKyG.js.map +1 -0
- package/dist/judge-D8Uf4dA9.js +146 -0
- package/dist/judge-D8Uf4dA9.js.map +1 -0
- package/dist/judge.d.ts +21 -0
- package/dist/judge.js +19 -0
- package/dist/judge.js.map +1 -0
- package/dist/live-Bs3LReMV.js +98 -0
- package/dist/live-Bs3LReMV.js.map +1 -0
- package/dist/matchers-DyLvm7EG.js +374 -0
- package/dist/matchers-DyLvm7EG.js.map +1 -0
- package/dist/model-BdO79gWz.js +44 -0
- package/dist/model-BdO79gWz.js.map +1 -0
- package/dist/model-CDL98xue.js +725 -0
- package/dist/model-CDL98xue.js.map +1 -0
- package/dist/model-v-0_Ik7S.js +2 -0
- package/dist/node-kG64XxdE.js +506 -0
- package/dist/node-kG64XxdE.js.map +1 -0
- package/dist/opencode-providers-DZuD_oYt.js +3149 -0
- package/dist/opencode-providers-DZuD_oYt.js.map +1 -0
- package/dist/options-CAQhu--C.js +452 -0
- package/dist/options-CAQhu--C.js.map +1 -0
- package/dist/patch-BkKje4cp.js +69 -0
- package/dist/patch-BkKje4cp.js.map +1 -0
- package/dist/paths-CBvm6pap.js +72 -0
- package/dist/paths-CBvm6pap.js.map +1 -0
- package/dist/plan-gwk6dJ8c.js +237 -0
- package/dist/plan-gwk6dJ8c.js.map +1 -0
- package/dist/preflight-BtubXKQ8.js +2 -0
- package/dist/preflight-BypZMNBG.js +185 -0
- package/dist/preflight-BypZMNBG.js.map +1 -0
- package/dist/prepare-Cwv0bHs_.js +3 -0
- package/dist/prepare-D6Uzcubb.js +213 -0
- package/dist/prepare-D6Uzcubb.js.map +1 -0
- package/dist/provenance-Dfb8YY5Y.js +129 -0
- package/dist/provenance-Dfb8YY5Y.js.map +1 -0
- package/dist/provider-BDbNOQhQ.js +2 -0
- package/dist/provider-CmAYTQQs.js +14 -0
- package/dist/provider-CmAYTQQs.js.map +1 -0
- package/dist/providers-2p3ylMHI.js +107 -0
- package/dist/providers-2p3ylMHI.js.map +1 -0
- package/dist/providers-BUOxitcT.js +2 -0
- package/dist/query-CFATHZmV.js +91 -0
- package/dist/query-CFATHZmV.js.map +1 -0
- package/dist/query-DR4m6CXf.js +2 -0
- package/dist/record-DQ8wzyco.js +99 -0
- package/dist/record-DQ8wzyco.js.map +1 -0
- package/dist/redact-C4fL_bZP.js +167 -0
- package/dist/redact-C4fL_bZP.js.map +1 -0
- package/dist/results-CeXb8Wde.js +37 -0
- package/dist/results-CeXb8Wde.js.map +1 -0
- package/dist/root-CFDjpWuc.js +110 -0
- package/dist/root-CFDjpWuc.js.map +1 -0
- package/dist/sandbox-By2gkdkC.js +133 -0
- package/dist/sandbox-By2gkdkC.js.map +1 -0
- package/dist/sandbox.d.ts +41 -0
- package/dist/sandbox.js +2 -0
- package/dist/server-Bi61uiY0.js +350 -0
- package/dist/server-Bi61uiY0.js.map +1 -0
- package/dist/server-CsSBL8NC.js +2 -0
- package/dist/source-BXzrmof5.js +2 -0
- package/dist/source-Bsfa3LZH.js +40 -0
- package/dist/source-Bsfa3LZH.js.map +1 -0
- package/dist/src-55j1zNHH.js +3 -0
- package/dist/src-AEwxd1UP.js +602 -0
- package/dist/src-AEwxd1UP.js.map +1 -0
- package/dist/src-BDwSuyIo.js +3 -0
- package/dist/src-BQyMiWri.js +490 -0
- package/dist/src-BQyMiWri.js.map +1 -0
- package/dist/src-C-LHyTSV.js +3 -0
- package/dist/src-Cmp-7YrD.js +1136 -0
- package/dist/src-Cmp-7YrD.js.map +1 -0
- package/dist/src-D0RdqpNb.js +252 -0
- package/dist/src-D0RdqpNb.js.map +1 -0
- package/dist/src-D8KYzxBX.js +793 -0
- package/dist/src-D8KYzxBX.js.map +1 -0
- package/dist/src-DEgff9Z7.js +4 -0
- package/dist/src-DXx_29pq.js +567 -0
- package/dist/src-DXx_29pq.js.map +1 -0
- package/dist/src-DwwiVLqv.js +2937 -0
- package/dist/src-DwwiVLqv.js.map +1 -0
- package/dist/src-N7E_mfcZ.js +2 -0
- package/dist/src-W9g-y5X5.js +3 -0
- package/dist/src-daS0wKH2.js +3 -0
- package/dist/state-Cyw3QTsN.js +2 -0
- package/dist/state-DZ7eqb1F.js +167 -0
- package/dist/state-DZ7eqb1F.js.map +1 -0
- package/dist/stats-BNAHlNMD.js +114 -0
- package/dist/stats-BNAHlNMD.js.map +1 -0
- package/dist/studio-app/assets/index-D6XyfFSH.js +58 -0
- package/dist/studio-app/assets/index-xwzWdcE_.css +2 -0
- package/dist/studio-app/favicon.svg +1 -0
- package/dist/studio-app/index.html +15 -0
- package/dist/summary-D7Ttky0z.js +166 -0
- package/dist/summary-D7Ttky0z.js.map +1 -0
- package/dist/text-gEcKjAyp.js +29 -0
- package/dist/text-gEcKjAyp.js.map +1 -0
- package/dist/types-C5vI3g1C.js +9 -0
- package/dist/types-C5vI3g1C.js.map +1 -0
- package/dist/types-DLec-6Cx.d.ts +1535 -0
- package/dist/usage-DsYUqIUM.js +24 -0
- package/dist/usage-DsYUqIUM.js.map +1 -0
- package/dist/view/index.d.ts +645 -0
- package/dist/view/index.js +8 -0
- package/dist/views-BrHOv6wg.js +220 -0
- package/dist/views-BrHOv6wg.js.map +1 -0
- package/dist/views-CwCWLWqo.js +3 -0
- package/dist/worker-entry-DnYkANPG.js +506 -0
- package/dist/worker-entry-DnYkANPG.js.map +1 -0
- package/dist/workspace-C2S0Z6gl.js +327 -0
- package/dist/workspace-C2S0Z6gl.js.map +1 -0
- package/dist/workspace-CyoXG6CY.js +2 -0
- package/docker/Dockerfile +19 -0
- package/package.json +79 -0
|
@@ -0,0 +1,1535 @@
|
|
|
1
|
+
//#region src/view/effort.d.ts
|
|
2
|
+
/** Below this two-sided p, an effort difference is an effect. */
|
|
3
|
+
declare const EFFORT_ALPHA = 0.05;
|
|
4
|
+
type EffortSample = {
|
|
5
|
+
tools: number;
|
|
6
|
+
agentMs: number;
|
|
7
|
+
fileEdits: number;
|
|
8
|
+
};
|
|
9
|
+
type Spread = {
|
|
10
|
+
median: number;
|
|
11
|
+
q1: number;
|
|
12
|
+
q3: number;
|
|
13
|
+
};
|
|
14
|
+
/** One cell's effort over its counted attempts (passes and fails; infra errors and skips aren't). */
|
|
15
|
+
type CellEffort = {
|
|
16
|
+
n: number;
|
|
17
|
+
tools: Spread;
|
|
18
|
+
agentMs: Spread;
|
|
19
|
+
fileEdits: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* `fewer` / `more`: the exact rank test's p is below `EFFORT_ALPHA`. `noise`: it isn't.
|
|
23
|
+
* `insufficient`: too few attempts for the test to ever reach it (2 against 2 can't).
|
|
24
|
+
*/
|
|
25
|
+
type EffortVerdict = 'fewer' | 'more' | 'noise' | 'insufficient';
|
|
26
|
+
type EffortContrast = {
|
|
27
|
+
variant: CellEffort;
|
|
28
|
+
control: CellEffort;
|
|
29
|
+
/** Variant median over control median; null when the control's is 0. */
|
|
30
|
+
toolsRatio: number | null;
|
|
31
|
+
agentMsRatio: number | null;
|
|
32
|
+
agentMsDelta: number;
|
|
33
|
+
toolsP: number | null;
|
|
34
|
+
agentMsP: number | null;
|
|
35
|
+
/** The variant's and control's tool-call ranges don't overlap. */
|
|
36
|
+
separated: boolean;
|
|
37
|
+
/** On tool calls, the steadier measure; `timeVerdict` on agent time. */
|
|
38
|
+
verdict: EffortVerdict;
|
|
39
|
+
timeVerdict: EffortVerdict;
|
|
40
|
+
};
|
|
41
|
+
declare const effortSample: (a: Pick<AttemptRecord, 'activity' | 'timings'>) => EffortSample;
|
|
42
|
+
declare function effortOf(samples: EffortSample[]): CellEffort | null;
|
|
43
|
+
/** The verdict of an exact rank test of `xs` (variant) against `ys` (control). */
|
|
44
|
+
declare function effortVerdict(xs: number[], ys: number[]): {
|
|
45
|
+
p: number | null;
|
|
46
|
+
verdict: EffortVerdict;
|
|
47
|
+
};
|
|
48
|
+
declare function contrastEffort(variant: EffortSample[], control: EffortSample[]): EffortContrast | null;
|
|
49
|
+
/** The counted attempts of one cell of an eval, as effort samples. */
|
|
50
|
+
declare const cellSamples: (e: Pick<EvalEntry, 'attempts'>, cellKey: string) => EffortSample[];
|
|
51
|
+
declare const cellEffort: (e: Pick<EvalEntry, 'attempts'>, cellKey: string) => CellEffort | null;
|
|
52
|
+
/** A variant cell's effort against its control's, in the same run; null for controls. */
|
|
53
|
+
declare function cellContrast(e: Pick<EvalEntry, 'attempts' | 'cells'>, cellKey: string): EffortContrast | null;
|
|
54
|
+
/** `4`, `4.5`: medians of counts are whole or halves. */
|
|
55
|
+
declare const formatCount: (n: number) => string;
|
|
56
|
+
declare const formatRatio: (r: number | null) => string;
|
|
57
|
+
/** Seconds for agent time: `21s`, `1m 05s`. */
|
|
58
|
+
declare function formatSeconds(ms: number): string;
|
|
59
|
+
declare const VERDICT_TEXT: Record<EffortVerdict, (what: string) => string>;
|
|
60
|
+
/**
|
|
61
|
+
* `tools 5 vs 43 (×0.11), agent 21s vs 46s (×0.47): too few attempts to test` for a variant
|
|
62
|
+
* against its control; with enough attempts, `fewer tool calls (p = 0.029)`.
|
|
63
|
+
*/
|
|
64
|
+
declare function effortLabel(c: EffortContrast): string;
|
|
65
|
+
/** One cell's effort on its own: `tools 43 · agent 46s`. */
|
|
66
|
+
declare const effortSummary: (e: Pick<CellEffort, 'tools' | 'agentMs'>) => string;
|
|
67
|
+
/** The same cell between two runs: `tools 44 → 10 (×0.23) · agent 45s → 21s (×0.47)`. */
|
|
68
|
+
declare function effortChangeLabel(base: Pick<CellEffort, 'tools' | 'agentMs'>, head: Pick<CellEffort, 'tools' | 'agentMs'>): string;
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/history/types.d.ts
|
|
71
|
+
/** Stays 1 while reeval is pre-release: the record changes in place. */
|
|
72
|
+
declare const SCHEMA_VERSION = 1;
|
|
73
|
+
type ArtifactLevel = 'all' | 'failures' | 'none';
|
|
74
|
+
type RecordMode = 'manual' | 'ci' | 'always';
|
|
75
|
+
/** `path` is relative to the run's artifact directory; `sha256` is of the bytes on disk. */
|
|
76
|
+
type FileRef = {
|
|
77
|
+
path: string;
|
|
78
|
+
sha256: string;
|
|
79
|
+
bytes: number;
|
|
80
|
+
};
|
|
81
|
+
type GitInfo = {
|
|
82
|
+
/**
|
|
83
|
+
* The commit the run executed at: HEAD when `reeval run` started. The later commit that adds the
|
|
84
|
+
* history file is a different, descendant commit.
|
|
85
|
+
*/
|
|
86
|
+
sha: string;
|
|
87
|
+
branch: string | null;
|
|
88
|
+
/** Uncommitted changes, outside `.reeval/` and the history folder, when the run started. */
|
|
89
|
+
dirty: boolean;
|
|
90
|
+
/** sha256 of the uncommitted changes (tracked and untracked, as a binary patch), when dirty. */
|
|
91
|
+
diffHash: string | null;
|
|
92
|
+
/** Those changes as `worktree.patch` in the artifact dir; apply on `sha` to reproduce. */
|
|
93
|
+
worktreePatch: FileRef | null;
|
|
94
|
+
/** Why a dirty run has no `worktree.patch`, e.g. too large or artifacts: 'none'. */
|
|
95
|
+
worktreePatchNote: string | null;
|
|
96
|
+
commitTime: string;
|
|
97
|
+
shallow: boolean;
|
|
98
|
+
/** The `origin` URL with any credentials removed. */
|
|
99
|
+
remote: string | null;
|
|
100
|
+
};
|
|
101
|
+
type RunnerInfo = {
|
|
102
|
+
kind: 'ci';
|
|
103
|
+
provider: string;
|
|
104
|
+
runUrl: string | null;
|
|
105
|
+
attempt: number | null;
|
|
106
|
+
/** The pull request a CI run built, when the provider says (GitHub Actions). */
|
|
107
|
+
pr: {
|
|
108
|
+
number: number | null;
|
|
109
|
+
baseRef: string | null;
|
|
110
|
+
} | null;
|
|
111
|
+
} | {
|
|
112
|
+
kind: 'local';
|
|
113
|
+
hostHash: string;
|
|
114
|
+
};
|
|
115
|
+
/** One failed judge try that was retried, and the backoff before the next. */
|
|
116
|
+
type JudgeRetry = {
|
|
117
|
+
error: string;
|
|
118
|
+
delayMs: number;
|
|
119
|
+
};
|
|
120
|
+
type JudgementRecord = {
|
|
121
|
+
question: string;
|
|
122
|
+
verdict: 'pass' | 'fail';
|
|
123
|
+
score: number;
|
|
124
|
+
threshold: number;
|
|
125
|
+
model: string;
|
|
126
|
+
/** The model id the provider says served the call, when it reports one. */
|
|
127
|
+
resolvedModel?: string;
|
|
128
|
+
/**
|
|
129
|
+
* `<index>:<sha256(question) prefix>`: the same question at the same place in the eval, so a
|
|
130
|
+
* verdict can be followed across runs (and an edited question starts a new series).
|
|
131
|
+
*/
|
|
132
|
+
key: string;
|
|
133
|
+
/** Transient provider errors retried before this verdict, with the backoff before each retry. */
|
|
134
|
+
retries?: JudgeRetry[];
|
|
135
|
+
};
|
|
136
|
+
type AttemptFiles = {
|
|
137
|
+
/** Each environment setup step's command, exit code, duration and output. */
|
|
138
|
+
setupLog?: FileRef;
|
|
139
|
+
transcript?: FileRef;
|
|
140
|
+
/** Unnormalised harness stream parts, only with `REEVAL_RAW_TRANSCRIPT=1`. */
|
|
141
|
+
rawTranscript?: FileRef;
|
|
142
|
+
diff?: FileRef;
|
|
143
|
+
judgeInput?: FileRef;
|
|
144
|
+
};
|
|
145
|
+
type ContextFile = {
|
|
146
|
+
path: string;
|
|
147
|
+
sha256: string;
|
|
148
|
+
};
|
|
149
|
+
type AttemptRecord = {
|
|
150
|
+
/** `<eval key> # <cell key>#<n>`: unique in the run. */
|
|
151
|
+
key: string;
|
|
152
|
+
/** `<cell key>#<n>`: unique in its eval. */
|
|
153
|
+
id: string;
|
|
154
|
+
/** The cell's key; see `EvalEntry.cells`. */
|
|
155
|
+
cell: string;
|
|
156
|
+
/** Run number within the cell, from 1. */
|
|
157
|
+
n: number;
|
|
158
|
+
/** Start order across the run, from 1; null for attempts that never started. */
|
|
159
|
+
seq: number | null;
|
|
160
|
+
startedAt: string | null;
|
|
161
|
+
/** The image the container started from, in `stack.images`; null for the fake sandbox. */
|
|
162
|
+
imageId: string | null;
|
|
163
|
+
/** Names of the variables and secrets set in the sandbox, and the brokered ones; never values. */
|
|
164
|
+
env: {
|
|
165
|
+
vars: string[];
|
|
166
|
+
secrets: string[];
|
|
167
|
+
brokered: string[];
|
|
168
|
+
};
|
|
169
|
+
/** Skills, rules and MCP config visible to the agent in its sandbox, by content hash. */
|
|
170
|
+
context: ContextFile[];
|
|
171
|
+
/** The egress policy and, when the sandbox records it, the hosts contacted and refused; null if it never ran. */
|
|
172
|
+
egress: EgressRecord | null;
|
|
173
|
+
/**
|
|
174
|
+
* The models the harness's own API calls were served by, as the egress gateway saw them: named
|
|
175
|
+
* in the request (e.g. `gemini-3.8-flash` for the id `gemini-3.8-flash-low`, plus any the agent
|
|
176
|
+
* uses on the side) or, for Codex, in the response, with its effort
|
|
177
|
+
* (`gpt-5.4-mini-2026-03-17[effort=medium]`); null when neither names one (Cursor) or nothing
|
|
178
|
+
* recorded them.
|
|
179
|
+
*/
|
|
180
|
+
servedModels: string[] | null;
|
|
181
|
+
outcome: Outcome;
|
|
182
|
+
error: AttemptError | null;
|
|
183
|
+
durationMs: number;
|
|
184
|
+
/** `text` is the agent's final message, cut at 8 KB; the whole of it is in `transcript.jsonl`. */
|
|
185
|
+
turns: Array<{
|
|
186
|
+
prompt: string;
|
|
187
|
+
status: TurnStatus;
|
|
188
|
+
startedAt: string;
|
|
189
|
+
durationMs: number;
|
|
190
|
+
text: string;
|
|
191
|
+
/** Tokens the turn used; null when the harness reports none. */
|
|
192
|
+
usage: TokenUsage | null;
|
|
193
|
+
/** The model the harness says served the turn; null when it reports none. */
|
|
194
|
+
model: string | null;
|
|
195
|
+
/** Event types the harness can't report; see `Turn.unreported`. */
|
|
196
|
+
unreported: WebEventType[];
|
|
197
|
+
/** The turn's first error, such as a provider's billing or auth error, cut at 1 KB; else null. */
|
|
198
|
+
error: string | null;
|
|
199
|
+
}>;
|
|
200
|
+
/** Tokens across the attempt's turns; null when the harness reported none. */
|
|
201
|
+
usage: TokenUsage | null;
|
|
202
|
+
timings: PhaseTimings;
|
|
203
|
+
/** Counts from the transcript, so a run's shape is visible without reading it. */
|
|
204
|
+
activity: {
|
|
205
|
+
tools: number;
|
|
206
|
+
fileEdits: number;
|
|
207
|
+
/** Null when a turn with none couldn't report them (`unreported`), not a count of 0 that isn't one. */
|
|
208
|
+
webSearches: number | null;
|
|
209
|
+
webFetches: number | null;
|
|
210
|
+
reasoningMs: number;
|
|
211
|
+
};
|
|
212
|
+
judgements: JudgementRecord[];
|
|
213
|
+
/** Harness session starts that didn't become ready within `startTimeout` and were started again (at most one). */
|
|
214
|
+
startRetries?: StartRetry[];
|
|
215
|
+
checks: Array<{
|
|
216
|
+
command: string;
|
|
217
|
+
exitCode: number;
|
|
218
|
+
durationMs: number;
|
|
219
|
+
log?: FileRef;
|
|
220
|
+
}>;
|
|
221
|
+
/** Files under a dependency directory (node_modules/, .venv/, …) collapse to one entry with `count`. */
|
|
222
|
+
changedFiles: Array<FileChange & {
|
|
223
|
+
count?: number;
|
|
224
|
+
}>;
|
|
225
|
+
files: AttemptFiles;
|
|
226
|
+
};
|
|
227
|
+
type EvalEntry = {
|
|
228
|
+
/** `file › group › … › name`, as shown. */
|
|
229
|
+
id: string;
|
|
230
|
+
/** What the eval is matched on across runs; its `id` for now. */
|
|
231
|
+
key: string;
|
|
232
|
+
/** Directory name inside the artifact directory, from `key`. */
|
|
233
|
+
slug: string;
|
|
234
|
+
file: string;
|
|
235
|
+
group: string[];
|
|
236
|
+
name: string;
|
|
237
|
+
/** Results are comparable only when fingerprints match. */
|
|
238
|
+
fingerprint: string;
|
|
239
|
+
/** The eval file and every local file it imports, directly or not, by content hash. */
|
|
240
|
+
sources: Array<{
|
|
241
|
+
path: string;
|
|
242
|
+
sha256: string;
|
|
243
|
+
}>;
|
|
244
|
+
tags: string[];
|
|
245
|
+
/**
|
|
246
|
+
* Over gated cells: `fail` if any failed, else `infra_error` if any errored, else `skipped` if
|
|
247
|
+
* all were, else `pass`. An ungated cell's failures don't fail the eval.
|
|
248
|
+
*/
|
|
249
|
+
outcome: Outcome;
|
|
250
|
+
/** A placeholder, `evaluate.todo` or `group.todo`: it never runs, and its outcome is `skipped`. */
|
|
251
|
+
todo: boolean;
|
|
252
|
+
/** pass / (pass + fail) over the gated cells' attempts; infra errors and skips excluded. */
|
|
253
|
+
passRate: number | null;
|
|
254
|
+
cells: CellRecord[];
|
|
255
|
+
attempts: AttemptRecord[];
|
|
256
|
+
};
|
|
257
|
+
/** A variant cell's pass rate against the control's, on the same harness and sandbox. */
|
|
258
|
+
type Lift = {
|
|
259
|
+
/** The control cell's key. */
|
|
260
|
+
vs: string;
|
|
261
|
+
/** Passes out of counted attempts (passes and fails) on each side. */
|
|
262
|
+
control: {
|
|
263
|
+
pass: number;
|
|
264
|
+
counted: number;
|
|
265
|
+
};
|
|
266
|
+
variant: {
|
|
267
|
+
pass: number;
|
|
268
|
+
counted: number;
|
|
269
|
+
};
|
|
270
|
+
/** Variant pass rate minus control pass rate, from -1 to 1. */
|
|
271
|
+
points: number;
|
|
272
|
+
/** 95% interval on the difference, by Newcombe's hybrid score method from two Wilson intervals. */
|
|
273
|
+
interval: {
|
|
274
|
+
lo: number;
|
|
275
|
+
hi: number;
|
|
276
|
+
level: 0.95;
|
|
277
|
+
method: 'newcombe-wilson';
|
|
278
|
+
};
|
|
279
|
+
/** The interval spans 0: the attempts can't tell the variant from the control. */
|
|
280
|
+
withinNoise: boolean;
|
|
281
|
+
/** Set when the compare has a `minLift`; the cell fails when `interval.lo` is below it. */
|
|
282
|
+
minLift?: number;
|
|
283
|
+
};
|
|
284
|
+
/** Each input of a cell fingerprint, hashed on its own, so a change can be named. */
|
|
285
|
+
type FingerprintParts = {
|
|
286
|
+
/** Over `EvalEntry.sources`. */
|
|
287
|
+
sources: string;
|
|
288
|
+
/** Over the harness descriptor, less concurrency caps. */
|
|
289
|
+
harness: string;
|
|
290
|
+
environment: string | null;
|
|
291
|
+
image: string | null;
|
|
292
|
+
agentCli: string | null;
|
|
293
|
+
/** Over the judge's model, threshold and version. */
|
|
294
|
+
judge: string;
|
|
295
|
+
/** Over the attempt's context files. */
|
|
296
|
+
context: string;
|
|
297
|
+
};
|
|
298
|
+
/** One harness and environment of an eval, and how its attempts went. */
|
|
299
|
+
type CellRecord = Cell & {
|
|
300
|
+
/** `sha256` of `fingerprintParts`; results are comparable only when it matches. */
|
|
301
|
+
fingerprint: string;
|
|
302
|
+
fingerprintParts: FingerprintParts;
|
|
303
|
+
/** The environment's digest: the join key into `RunRecord.environments`; null for none. */
|
|
304
|
+
environmentDigest: string | null;
|
|
305
|
+
harnessKind: string;
|
|
306
|
+
/** The configured harness model, when it has one. */
|
|
307
|
+
model: string | null;
|
|
308
|
+
/** How the attempts decide the cell's outcome; `'none'` when it doesn't fail the eval. */
|
|
309
|
+
gate: CellGate;
|
|
310
|
+
/** Environment keys the variant replaced on the control's (or a child on its parent's). */
|
|
311
|
+
overrides: string[];
|
|
312
|
+
runs: number;
|
|
313
|
+
pass: number;
|
|
314
|
+
fail: number;
|
|
315
|
+
infraError: number;
|
|
316
|
+
skipped: number;
|
|
317
|
+
passRate: number | null;
|
|
318
|
+
/** By `gate` (`'none'` reports the worst outcome); a variant below its `minLift` fails too. */
|
|
319
|
+
outcome: Outcome;
|
|
320
|
+
lift?: Lift;
|
|
321
|
+
};
|
|
322
|
+
/** Counts attempts, except `evals`. */
|
|
323
|
+
type RunSummary = {
|
|
324
|
+
evals: number;
|
|
325
|
+
attempts: number;
|
|
326
|
+
pass: number;
|
|
327
|
+
/** Every failed attempt, including `notGated` ones. */
|
|
328
|
+
fail: number;
|
|
329
|
+
/** Failed attempts in ungated cells (controls, by default), which don't fail the run. */
|
|
330
|
+
notGated: number;
|
|
331
|
+
infraError: number;
|
|
332
|
+
skipped: number;
|
|
333
|
+
};
|
|
334
|
+
/** The tokens the run used, as the harnesses count them; reeval prices nothing. */
|
|
335
|
+
type Cost = {
|
|
336
|
+
/** Prompt tokens, cache reads and writes included. */
|
|
337
|
+
inputTokens: number | null;
|
|
338
|
+
outputTokens: number | null;
|
|
339
|
+
/** The same totals split as each turn reports them. */
|
|
340
|
+
usage: TokenUsage | null;
|
|
341
|
+
source: 'reported' | 'unreported';
|
|
342
|
+
};
|
|
343
|
+
type RunRecord = {
|
|
344
|
+
kind: 'es-run';
|
|
345
|
+
schemaVersion: typeof SCHEMA_VERSION;
|
|
346
|
+
/** Also the file name: `<UTC start>_<short sha>_<author or ci>`. */
|
|
347
|
+
runId: string;
|
|
348
|
+
/** `budget` when `budget.duration` stopped new attempts from starting. */
|
|
349
|
+
status: 'complete' | 'interrupted' | 'budget';
|
|
350
|
+
startedAt: string;
|
|
351
|
+
endedAt: string;
|
|
352
|
+
durationMs: number;
|
|
353
|
+
author: string;
|
|
354
|
+
git: GitInfo | null;
|
|
355
|
+
runner: RunnerInfo;
|
|
356
|
+
dependencies: {
|
|
357
|
+
/** Lockfiles in the project root (and the repo root, if different), by content hash. */
|
|
358
|
+
lockfiles: Array<{
|
|
359
|
+
path: string;
|
|
360
|
+
sha256: string;
|
|
361
|
+
}>;
|
|
362
|
+
};
|
|
363
|
+
stack: Stack;
|
|
364
|
+
/** Each environment the run applied, fingerprinted; secret values are never included. */
|
|
365
|
+
environments: EnvironmentRecord[];
|
|
366
|
+
/** The profile the run was sized with, and why it was chosen. */
|
|
367
|
+
profile: {
|
|
368
|
+
name: string;
|
|
369
|
+
reason: string;
|
|
370
|
+
defaults: {
|
|
371
|
+
harness?: string[];
|
|
372
|
+
environment?: string;
|
|
373
|
+
sandbox?: string;
|
|
374
|
+
runs?: number;
|
|
375
|
+
timeout?: string | number;
|
|
376
|
+
env?: string[];
|
|
377
|
+
};
|
|
378
|
+
limit: {
|
|
379
|
+
runs?: number;
|
|
380
|
+
compare?: false | {
|
|
381
|
+
maxVariants: number;
|
|
382
|
+
};
|
|
383
|
+
harness?: string[];
|
|
384
|
+
};
|
|
385
|
+
} | null;
|
|
386
|
+
/**
|
|
387
|
+
* What the run planned before it started, and each level whose sandbox ran as root or with
|
|
388
|
+
* capabilities added back.
|
|
389
|
+
*/
|
|
390
|
+
plan: {
|
|
391
|
+
evals: number;
|
|
392
|
+
cells: number;
|
|
393
|
+
attempts: number;
|
|
394
|
+
estimateMs: number;
|
|
395
|
+
sandboxWarnings: SandboxWarning[];
|
|
396
|
+
};
|
|
397
|
+
budget: {
|
|
398
|
+
attempts?: number;
|
|
399
|
+
duration?: string | number;
|
|
400
|
+
} | null;
|
|
401
|
+
config: {
|
|
402
|
+
sandbox: string;
|
|
403
|
+
environment: string | null;
|
|
404
|
+
runs: number;
|
|
405
|
+
judgeModel: string | null;
|
|
406
|
+
threshold: number;
|
|
407
|
+
timeoutMs: number;
|
|
408
|
+
/** Attempts that ran at once; not part of `configHash`, since it doesn't change results. */
|
|
409
|
+
concurrency: number;
|
|
410
|
+
configHash: string;
|
|
411
|
+
};
|
|
412
|
+
cost: Cost;
|
|
413
|
+
/** Which artifacts sit next to this record. Refs to files that weren't kept are removed. */
|
|
414
|
+
artifacts: ArtifactLevel;
|
|
415
|
+
redaction: {
|
|
416
|
+
version: 1;
|
|
417
|
+
};
|
|
418
|
+
summary: RunSummary;
|
|
419
|
+
evals: EvalEntry[];
|
|
420
|
+
};
|
|
421
|
+
/** What ran and graded the agents: the machine, images, harnesses and judge. */
|
|
422
|
+
type Stack = {
|
|
423
|
+
es: string;
|
|
424
|
+
node: string;
|
|
425
|
+
os: string;
|
|
426
|
+
arch: string;
|
|
427
|
+
/** Every image an attempt started from. `id` is content-addressed; `repoDigests` only exist once pushed. */
|
|
428
|
+
images: Array<AttemptImage & {
|
|
429
|
+
repoDigests: string[];
|
|
430
|
+
}>;
|
|
431
|
+
/** Exact versions of the packages that run and grade the agent. */
|
|
432
|
+
packages: Record<string, string>;
|
|
433
|
+
/**
|
|
434
|
+
* Each harness: its configured `model`, and `resolvedModels`, what it reported serving across
|
|
435
|
+
* the run's turns with the variant it resolved (`composer-2.5[fast=true]` for `composer-2.5`
|
|
436
|
+
* without `params`); empty when it reports none.
|
|
437
|
+
*/
|
|
438
|
+
harnesses: Array<{
|
|
439
|
+
id: string;
|
|
440
|
+
kind: string;
|
|
441
|
+
/** The configured model, when the harness has one. */
|
|
442
|
+
model: string | null;
|
|
443
|
+
/**
|
|
444
|
+
* The models the harness reported serving, across every turn in the run, with the variant it
|
|
445
|
+
* resolved (`composer-2.5[fast=true]` for `composer-2.5` with no `params`); empty when it
|
|
446
|
+
* reports none.
|
|
447
|
+
*/
|
|
448
|
+
resolvedModels: string[];
|
|
449
|
+
}>;
|
|
450
|
+
judge: {
|
|
451
|
+
/** The config's default model; groups, evals and calls may use others (see each verdict). */
|
|
452
|
+
model: string | null;
|
|
453
|
+
/** Model ids the provider reported serving, across every judge call in the run. */
|
|
454
|
+
resolvedModels: string[];
|
|
455
|
+
threshold: number;
|
|
456
|
+
/** Changes when reeval, the AI SDK or the provider adapter (and so the judge prompt) change. */
|
|
457
|
+
version: string;
|
|
458
|
+
};
|
|
459
|
+
/** Union of every attempt's context files. */
|
|
460
|
+
context: ContextFile[];
|
|
461
|
+
/** What eval fingerprints hash; fingerprints from different scopes aren't comparable. */
|
|
462
|
+
fingerprintScope: string[];
|
|
463
|
+
};
|
|
464
|
+
/** The parts of a run's stack that `reeval compare` checks for drift. */
|
|
465
|
+
type StackSummary = {
|
|
466
|
+
imageIds: string[];
|
|
467
|
+
agentClis: string[];
|
|
468
|
+
harnesses: string[];
|
|
469
|
+
/** `name@digest` per environment. */
|
|
470
|
+
environments: string[];
|
|
471
|
+
judgeModel: string | null;
|
|
472
|
+
/** What the judge provider reported serving; differs from `judgeModel` on silent updates. */
|
|
473
|
+
judgeServed: string[];
|
|
474
|
+
judgeVersion: string;
|
|
475
|
+
threshold: number;
|
|
476
|
+
fingerprintScope: string[];
|
|
477
|
+
};
|
|
478
|
+
/** A cell in a `HistoryEntry`: enough to follow cells, variants and lift across runs. */
|
|
479
|
+
type HistoryCell = Pick<CellRecord, 'key' | 'role' | 'variant' | 'harness' | 'model' | 'environment' | 'environmentDigest' | 'gate' | 'fingerprint' | 'fingerprintParts' | 'runs' | 'pass' | 'fail' | 'infraError' | 'skipped' | 'passRate' | 'outcome'> & {
|
|
480
|
+
lift?: {
|
|
481
|
+
vs: string;
|
|
482
|
+
points: number;
|
|
483
|
+
lo: number;
|
|
484
|
+
hi: number;
|
|
485
|
+
withinNoise: boolean;
|
|
486
|
+
};
|
|
487
|
+
/** Median tool calls and agent time over the counted attempts; null when none counted. */
|
|
488
|
+
effort: CellEffort | null;
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* One line of the derived history listing: cached locally by the CLI, and the run header in the
|
|
492
|
+
* studio's index shards.
|
|
493
|
+
*/
|
|
494
|
+
type HistoryEntry = {
|
|
495
|
+
runId: string;
|
|
496
|
+
recorded: boolean;
|
|
497
|
+
status: RunRecord['status'];
|
|
498
|
+
startedAt: string;
|
|
499
|
+
durationMs: number;
|
|
500
|
+
author: string;
|
|
501
|
+
git: Pick<GitInfo, 'sha' | 'branch' | 'dirty'> | null;
|
|
502
|
+
runner: RunnerInfo['kind'];
|
|
503
|
+
/** The CI provider, its run page and pull request, for CI runs. */
|
|
504
|
+
ci: {
|
|
505
|
+
provider: string;
|
|
506
|
+
runUrl: string | null;
|
|
507
|
+
pr: {
|
|
508
|
+
number: number | null;
|
|
509
|
+
baseRef: string | null;
|
|
510
|
+
} | null;
|
|
511
|
+
} | null;
|
|
512
|
+
harnesses: string[];
|
|
513
|
+
summary: RunSummary;
|
|
514
|
+
/** The run's tokens, summed from the turns that reported them. */
|
|
515
|
+
usage: TokenUsage | null;
|
|
516
|
+
stack: StackSummary;
|
|
517
|
+
evals: Array<{
|
|
518
|
+
id: string;
|
|
519
|
+
key: string;
|
|
520
|
+
slug: string;
|
|
521
|
+
fingerprint: string;
|
|
522
|
+
sources: EvalEntry['sources'];
|
|
523
|
+
outcome: Outcome;
|
|
524
|
+
passRate: number | null;
|
|
525
|
+
cells: HistoryCell[];
|
|
526
|
+
}>;
|
|
527
|
+
};
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/types.d.ts
|
|
530
|
+
type Duration = number | `${number}${'ms' | 's' | 'm' | 'h'}`;
|
|
531
|
+
/** When an event started: wall clock, and ms since its turn started. */
|
|
532
|
+
type EventTime = {
|
|
533
|
+
ts: string;
|
|
534
|
+
atMs: number;
|
|
535
|
+
};
|
|
536
|
+
type FileEditEvent = {
|
|
537
|
+
type: 'file-edit';
|
|
538
|
+
toolCallId: string;
|
|
539
|
+
path: string;
|
|
540
|
+
kind: 'create' | 'modify' | 'delete';
|
|
541
|
+
/** Line counts, or null when the harness didn't send the content (e.g. deletes). */
|
|
542
|
+
added: number | null;
|
|
543
|
+
removed: number | null;
|
|
544
|
+
};
|
|
545
|
+
/**
|
|
546
|
+
* Normalised agent events. Each tool call is one event, merged with its result; `durationMs`
|
|
547
|
+
* is null when the harness never reported the end.
|
|
548
|
+
*/
|
|
549
|
+
type TranscriptEvent = EventTime & ({
|
|
550
|
+
type: 'text' | 'reasoning';
|
|
551
|
+
text: string;
|
|
552
|
+
durationMs: number | null;
|
|
553
|
+
} | (FileEditEvent & {
|
|
554
|
+
durationMs: number | null;
|
|
555
|
+
}) | {
|
|
556
|
+
type: 'web-search';
|
|
557
|
+
toolCallId: string;
|
|
558
|
+
query: string;
|
|
559
|
+
/** How many sources the harness says it used; Cursor sends no URLs or titles. */
|
|
560
|
+
references: number | null;
|
|
561
|
+
durationMs: number | null;
|
|
562
|
+
} | {
|
|
563
|
+
type: 'web-fetch';
|
|
564
|
+
toolCallId: string;
|
|
565
|
+
url: string;
|
|
566
|
+
ok: boolean;
|
|
567
|
+
durationMs: number | null;
|
|
568
|
+
} | {
|
|
569
|
+
type: 'tool';
|
|
570
|
+
toolCallId: string;
|
|
571
|
+
tool: string;
|
|
572
|
+
input: unknown;
|
|
573
|
+
output: unknown;
|
|
574
|
+
ok: boolean;
|
|
575
|
+
durationMs: number | null;
|
|
576
|
+
} | {
|
|
577
|
+
type: 'error';
|
|
578
|
+
message: string;
|
|
579
|
+
} |
|
|
580
|
+
/** A step that failed while the turn went on: a model call the harness retried. */
|
|
581
|
+
{
|
|
582
|
+
type: 'retry';
|
|
583
|
+
message: string;
|
|
584
|
+
});
|
|
585
|
+
/** The transcript events a harness may be unable to report. */
|
|
586
|
+
type WebEventType = 'web-search' | 'web-fetch';
|
|
587
|
+
type ExecResult = {
|
|
588
|
+
exitCode: number;
|
|
589
|
+
stdout: string;
|
|
590
|
+
stderr: string;
|
|
591
|
+
};
|
|
592
|
+
type FileChange = {
|
|
593
|
+
path: string;
|
|
594
|
+
status: 'added' | 'modified' | 'deleted';
|
|
595
|
+
};
|
|
596
|
+
type Diff = {
|
|
597
|
+
patch: string;
|
|
598
|
+
files: FileChange[];
|
|
599
|
+
};
|
|
600
|
+
type TurnStatus = 'completed' | 'aborted' | 'error';
|
|
601
|
+
/**
|
|
602
|
+
* The tokens one turn used, as its harness reported them. `input` excludes cache reads and
|
|
603
|
+
* writes (so the prompt was `input + cacheRead + cacheWrite`); `reasoning` is part of `output`.
|
|
604
|
+
*/
|
|
605
|
+
type TokenUsage = {
|
|
606
|
+
input: number;
|
|
607
|
+
output: number;
|
|
608
|
+
cacheRead: number;
|
|
609
|
+
cacheWrite: number;
|
|
610
|
+
reasoning: number | null;
|
|
611
|
+
};
|
|
612
|
+
/** One agent turn. Immutable once `t.send()` resolves. */
|
|
613
|
+
/** A turn as data, as a record or another language's runner carries it: `Turn` without its methods. */
|
|
614
|
+
type TurnData = {
|
|
615
|
+
prompt: string;
|
|
616
|
+
/** The agent's final message for this turn. */
|
|
617
|
+
text: string;
|
|
618
|
+
status: TurnStatus;
|
|
619
|
+
startedAt: string;
|
|
620
|
+
durationMs: number;
|
|
621
|
+
transcript: TranscriptEvent[];
|
|
622
|
+
/** Tokens the turn used; null when the harness reports none. */
|
|
623
|
+
usage: TokenUsage | null;
|
|
624
|
+
/**
|
|
625
|
+
* The model the harness says served the turn, e.g. `composer-2.5[fast=false]`; null when it
|
|
626
|
+
* reports none. It can differ from the requested one (an alias, or parameters it filled in).
|
|
627
|
+
*/
|
|
628
|
+
model: string | null;
|
|
629
|
+
/**
|
|
630
|
+
* Event types the harness couldn't report for this turn: the agent may have searched or
|
|
631
|
+
* fetched with no event in `transcript`. `toHaveSearched()` and `toHaveFetched()` refuse such a
|
|
632
|
+
* turn. Usually `[]`; a Cursor turn whose web calls the adapter couldn't recover (its SDK drops
|
|
633
|
+
* them from the stream) is `['web-search', 'web-fetch']`.
|
|
634
|
+
*/
|
|
635
|
+
unreported: WebEventType[];
|
|
636
|
+
/** Workspace changes since the eval started, as of the end of this turn. */
|
|
637
|
+
diff: Diff;
|
|
638
|
+
};
|
|
639
|
+
type Turn = TurnData & {
|
|
640
|
+
/** Runs in the eval's sandbox. Same as `t.exec`. */
|
|
641
|
+
exec(command: string): Promise<ExecResult>;
|
|
642
|
+
readFile(path: string): Promise<string | null>;
|
|
643
|
+
};
|
|
644
|
+
type JSONValue = null | boolean | number | string | JSONValue[] | {
|
|
645
|
+
[key: string]: JSONValue;
|
|
646
|
+
};
|
|
647
|
+
/** A canned judge for tests and dry runs; see `fakeJudge()` in `reeval/judge`. */
|
|
648
|
+
type FakeJudgeModel = {
|
|
649
|
+
kind: 'fake-judge';
|
|
650
|
+
score: number;
|
|
651
|
+
};
|
|
652
|
+
/**
|
|
653
|
+
* `'provider/model'` (e.g. `'typesafe-ai/jev-1.13.0'`, `'google/gemini-3.8-flash'`), an AI SDK
|
|
654
|
+
* evaluation model object (per group, eval or call only), or a fake judge.
|
|
655
|
+
*/
|
|
656
|
+
type JudgeModel = string | FakeJudgeModel | {
|
|
657
|
+
readonly specificationVersion: string;
|
|
658
|
+
};
|
|
659
|
+
/** Merged field by field down the cascade, so per-model settings can join as new fields. */
|
|
660
|
+
type JudgeOptions = {
|
|
661
|
+
model?: JudgeModel;
|
|
662
|
+
/** Minimum score (0–1) for a `pass` verdict. */
|
|
663
|
+
threshold?: number;
|
|
664
|
+
providerOptions?: Record<string, Record<string, JSONValue>>;
|
|
665
|
+
/** The most judge calls to this model's provider at once, across the run. */
|
|
666
|
+
maxConcurrent?: number;
|
|
667
|
+
};
|
|
668
|
+
type JudgeCallOptions = JudgeOptions & {
|
|
669
|
+
/** What to grade instead of the latest turn: another turn, or a value used as the output. */
|
|
670
|
+
on?: Turn | JSONValue;
|
|
671
|
+
};
|
|
672
|
+
type Verdict = {
|
|
673
|
+
verdict: 'pass' | 'fail';
|
|
674
|
+
/** The judge's probability that the answer is yes, from 0 to 1. */
|
|
675
|
+
score: number;
|
|
676
|
+
threshold: number;
|
|
677
|
+
question: string;
|
|
678
|
+
model: string;
|
|
679
|
+
/** The model id the provider says served the call, when it reports one. */
|
|
680
|
+
resolvedModel?: string;
|
|
681
|
+
};
|
|
682
|
+
type EvalContext = {
|
|
683
|
+
/** Sends the next turn of this eval's agent session, starting the sandbox on first use. */
|
|
684
|
+
send(prompt: string): Promise<Turn>;
|
|
685
|
+
/** Asks the judge a yes/no question about the latest turn (or `options.on`). */
|
|
686
|
+
judge(question: string, options?: JudgeCallOptions): Promise<Verdict>;
|
|
687
|
+
exec(command: string): Promise<ExecResult>;
|
|
688
|
+
/** Workspace-relative, or `~/` for the home directory; `null` when there's no such file. */
|
|
689
|
+
readFile(path: string): Promise<string | null>;
|
|
690
|
+
/**
|
|
691
|
+
* Starts the sandbox and the agent session without a turn, for checks on what the environment
|
|
692
|
+
* set up. `t.send()` starts them too; calling both starts them once.
|
|
693
|
+
*/
|
|
694
|
+
sandbox(): Promise<Pick<EvalContext, 'exec' | 'readFile'>>;
|
|
695
|
+
/** Every turn so far, oldest first. */
|
|
696
|
+
readonly turns: readonly Turn[];
|
|
697
|
+
/** Aborted on timeout or Ctrl-C. */
|
|
698
|
+
readonly signal: AbortSignal;
|
|
699
|
+
/** Which cell of the matrix this attempt is, and its run number (from 1). */
|
|
700
|
+
readonly attempt: {
|
|
701
|
+
id: string;
|
|
702
|
+
cell: Cell;
|
|
703
|
+
n: number;
|
|
704
|
+
};
|
|
705
|
+
};
|
|
706
|
+
type EvalFn = (t: EvalContext) => Promise<void>;
|
|
707
|
+
type FakeHarnessDescriptor = {
|
|
708
|
+
kind: 'fake';
|
|
709
|
+
/** Cell id; defaults to `fake`. */
|
|
710
|
+
id?: string;
|
|
711
|
+
/** Files the fake agent writes into the sandbox on every turn. */
|
|
712
|
+
files?: Record<string, string>;
|
|
713
|
+
text?: string;
|
|
714
|
+
/** Simulated turn length, honours the abort signal. */
|
|
715
|
+
delayMs?: number;
|
|
716
|
+
/** Sits out `delayMs` even after an abort, like an agent that doesn't stop when it's told to. */
|
|
717
|
+
ignoreAbort?: boolean;
|
|
718
|
+
/** Throw before the first turn starts, to simulate an infra failure. */
|
|
719
|
+
failWith?: string;
|
|
720
|
+
/** The first this many session starts of an attempt never become ready, even when aborted (`true`: every one). */
|
|
721
|
+
startHangs?: number | true;
|
|
722
|
+
/** A reasoning block at the start of every turn. */
|
|
723
|
+
reasoning?: string;
|
|
724
|
+
/**
|
|
725
|
+
* The model the fake agent says served each turn, as a real harness reports its resolved model
|
|
726
|
+
* and variant (`composer-2.5[fast=true]`); unset, it reports none.
|
|
727
|
+
*/
|
|
728
|
+
servedModel?: string;
|
|
729
|
+
/** Tool calls the fake agent reports on every turn, before writing files. */
|
|
730
|
+
tools?: Array<{
|
|
731
|
+
tool: string;
|
|
732
|
+
input: unknown;
|
|
733
|
+
output: unknown;
|
|
734
|
+
ok?: boolean;
|
|
735
|
+
}>;
|
|
736
|
+
/** Files and message used instead when the environment gives the agent a skill. */
|
|
737
|
+
withSkill?: {
|
|
738
|
+
files?: Record<string, string>;
|
|
739
|
+
text?: string;
|
|
740
|
+
};
|
|
741
|
+
/** Chance (0–1) that an attempt writes no files and says so, for flaky pass rates. */
|
|
742
|
+
flakeRate?: number;
|
|
743
|
+
/**
|
|
744
|
+
* Real sandbox actions, one list per `t.send` (the last list repeats), run in order before
|
|
745
|
+
* `files`. `exec` runs as the agent: with the sandbox's own variables, none of the checks'.
|
|
746
|
+
*/
|
|
747
|
+
script?: FakeScriptStep[][];
|
|
748
|
+
/** The most attempts on this harness kind at once, across the run. */
|
|
749
|
+
maxConcurrent?: number;
|
|
750
|
+
/**
|
|
751
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
752
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
753
|
+
*/
|
|
754
|
+
startTimeout?: Duration;
|
|
755
|
+
/**
|
|
756
|
+
* Behave like this agent: its image, and where it reads rules and skills (`CLAUDE.md` and
|
|
757
|
+
* `.claude/skills` for `claude-code`, `~/.gemini/config/skills` for `antigravity`,
|
|
758
|
+
* `~/.config/opencode/skills` for `opencode`, Goose's private home for `goose`,
|
|
759
|
+
* `~/.pi/agent/skills` for `pi`, `~/.agents/skills` for `codex`). Defaults to the
|
|
760
|
+
* fake's own (`AGENTS.md`, `.agents/skills`) in Cursor's image.
|
|
761
|
+
*/
|
|
762
|
+
layout?: 'cursor' | 'claude-code' | 'antigravity' | 'opencode' | 'goose' | 'pi' | 'codex';
|
|
763
|
+
};
|
|
764
|
+
/**
|
|
765
|
+
* One action of a scripted fake agent. `exec` is reported as a `shell` tool call whose output is
|
|
766
|
+
* the command's stdout, `write` as file edits, and `reply` is the turn's final message. `mcp`
|
|
767
|
+
* (`'<server>/<tool>'`) starts the environment's stdio MCP server in the sandbox, as an agent
|
|
768
|
+
* would, and calls the tool with no arguments; it's reported as `mcp__<server>__<tool>`.
|
|
769
|
+
* `error` ends the turn there with status `error` and that message, as a provider error would.
|
|
770
|
+
*/
|
|
771
|
+
type FakeScriptStep = {
|
|
772
|
+
exec: string;
|
|
773
|
+
} | {
|
|
774
|
+
write: Record<string, string>;
|
|
775
|
+
} | {
|
|
776
|
+
reply: string;
|
|
777
|
+
} | {
|
|
778
|
+
mcp: string;
|
|
779
|
+
} | {
|
|
780
|
+
error: string;
|
|
781
|
+
};
|
|
782
|
+
/**
|
|
783
|
+
* Cursor's agent through the Cursor TypeScript SDK's local runtime
|
|
784
|
+
* (`@reeval/harness-cursor-sdk`), made by `cursor()`. It reports each turn's tokens.
|
|
785
|
+
*/
|
|
786
|
+
type CursorHarnessDescriptor = {
|
|
787
|
+
kind: 'cursor';
|
|
788
|
+
/** A model id `Cursor.models.list()` returns for the key, e.g. `composer-2.5`. */
|
|
789
|
+
model: string;
|
|
790
|
+
/**
|
|
791
|
+
* Model parameters, e.g. `{ fast: 'false' }`. Unset ones take the default variant in Cursor's
|
|
792
|
+
* model catalog (`fast=true` for `composer-2.5`), sent explicitly; pass the ones that change cost.
|
|
793
|
+
*/
|
|
794
|
+
params?: Record<string, string>;
|
|
795
|
+
/** Cell id; defaults to `cursor:<model>`. */
|
|
796
|
+
id?: string;
|
|
797
|
+
/** The most Cursor attempts at once, across the run, e.g. for the account's rate limits. */
|
|
798
|
+
maxConcurrent?: number;
|
|
799
|
+
/**
|
|
800
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
801
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
802
|
+
*/
|
|
803
|
+
startTimeout?: Duration;
|
|
804
|
+
};
|
|
805
|
+
/** Claude Code through `@ai-sdk/harness-claude-code`, made by `claudeCode()`. */
|
|
806
|
+
type ClaudeCodeHarnessDescriptor = {
|
|
807
|
+
kind: 'claude-code';
|
|
808
|
+
/** A model id or alias the `claude` CLI accepts, e.g. `claude-sonnet-5` or `sonnet`. */
|
|
809
|
+
model: string;
|
|
810
|
+
/** Cell id; defaults to `claude-code:<model>`. */
|
|
811
|
+
id?: string;
|
|
812
|
+
/** The most Claude Code attempts at once, across the run, e.g. for the account's rate limits. */
|
|
813
|
+
maxConcurrent?: number;
|
|
814
|
+
/**
|
|
815
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
816
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
817
|
+
*/
|
|
818
|
+
startTimeout?: Duration;
|
|
819
|
+
};
|
|
820
|
+
/** The Antigravity CLI through `@reeval/harness-antigravity`, made by `antigravity()`. */
|
|
821
|
+
type AntigravityHarnessDescriptor = {
|
|
822
|
+
kind: 'antigravity';
|
|
823
|
+
/** A model slug `agy models` lists for a Gemini API key, e.g. `gemini-3.8-flash-medium`. */
|
|
824
|
+
model: string;
|
|
825
|
+
/** Cell id; defaults to `antigravity:<model>`. */
|
|
826
|
+
id?: string;
|
|
827
|
+
/** The most Antigravity attempts at once, across the run, e.g. for the key's rate limits. */
|
|
828
|
+
maxConcurrent?: number;
|
|
829
|
+
/**
|
|
830
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
831
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
832
|
+
*/
|
|
833
|
+
startTimeout?: Duration;
|
|
834
|
+
};
|
|
835
|
+
/**
|
|
836
|
+
* OpenCode through `@reeval/harness-opencode` (`opencode acp`), made by `opencode()`. It runs
|
|
837
|
+
* on any provider whose key OpenCode sends in a header on known hosts, with that provider's key,
|
|
838
|
+
* and reports no usage (its ACP server counts one call).
|
|
839
|
+
*/
|
|
840
|
+
type OpenCodeHarnessDescriptor = {
|
|
841
|
+
kind: 'opencode';
|
|
842
|
+
/**
|
|
843
|
+
* OpenCode's model id, `<provider>/<model>`, e.g. `google/gemini-3.8-flash` or
|
|
844
|
+
* `anthropic/claude-haiku-4-5`; the provider picks the key.
|
|
845
|
+
*/
|
|
846
|
+
model: string;
|
|
847
|
+
/** Cell id; defaults to `opencode:<model>`. */
|
|
848
|
+
id?: string;
|
|
849
|
+
/** The most OpenCode attempts at once, across the run, e.g. for the key's rate limits. */
|
|
850
|
+
maxConcurrent?: number;
|
|
851
|
+
/**
|
|
852
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
853
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
854
|
+
*/
|
|
855
|
+
startTimeout?: Duration;
|
|
856
|
+
};
|
|
857
|
+
/**
|
|
858
|
+
* Goose through `@reeval/harness-goose` (`goose acp`), made by `goose()`. It runs Google models
|
|
859
|
+
* on a Gemini API key, and reports no usage (its ACP server counts one call).
|
|
860
|
+
*/
|
|
861
|
+
type GooseHarnessDescriptor = {
|
|
862
|
+
kind: 'goose';
|
|
863
|
+
/** A Gemini model id, run on Goose's `google` provider, e.g. `gemini-3.8-flash`. */
|
|
864
|
+
model: string;
|
|
865
|
+
/** Cell id; defaults to `goose:<model>`. */
|
|
866
|
+
id?: string;
|
|
867
|
+
/** The most Goose attempts at once, across the run, e.g. for the key's rate limits. */
|
|
868
|
+
maxConcurrent?: number;
|
|
869
|
+
/**
|
|
870
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
871
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
872
|
+
*/
|
|
873
|
+
startTimeout?: Duration;
|
|
874
|
+
};
|
|
875
|
+
/**
|
|
876
|
+
* pi through `@reeval/harness-pi` (`pi --mode json` in the sandbox), made by `pi()`. It runs
|
|
877
|
+
* Google models on a Gemini API key and reports each model call's tokens. It has no MCP client.
|
|
878
|
+
*/
|
|
879
|
+
type PiHarnessDescriptor = {
|
|
880
|
+
kind: 'pi';
|
|
881
|
+
/** A Gemini model id, run on pi's `google` provider, e.g. `gemini-3.8-flash`. */
|
|
882
|
+
model: string;
|
|
883
|
+
/** Cell id; defaults to `pi:<model>`. */
|
|
884
|
+
id?: string;
|
|
885
|
+
/** The most pi attempts at once, across the run, e.g. for the key's rate limits. */
|
|
886
|
+
maxConcurrent?: number;
|
|
887
|
+
/**
|
|
888
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
889
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
890
|
+
*/
|
|
891
|
+
startTimeout?: Duration;
|
|
892
|
+
};
|
|
893
|
+
/**
|
|
894
|
+
* OpenAI's Codex CLI through `@reeval/harness-codex` (`codex exec --json`), made by `codex()`.
|
|
895
|
+
* It runs OpenAI models on an OpenAI API key and reports each turn's tokens, reasoning included.
|
|
896
|
+
*/
|
|
897
|
+
type CodexHarnessDescriptor = {
|
|
898
|
+
kind: 'codex';
|
|
899
|
+
/** An OpenAI model id Codex runs on the Responses API, e.g. `gpt-5.4-mini`. */
|
|
900
|
+
model: string;
|
|
901
|
+
/** Cell id; defaults to `codex:<model>`. */
|
|
902
|
+
id?: string;
|
|
903
|
+
/** The most Codex attempts at once, across the run, e.g. for the key's rate limits. */
|
|
904
|
+
maxConcurrent?: number;
|
|
905
|
+
/**
|
|
906
|
+
* How long the harness's session may take to become ready (its bootstrap and first session)
|
|
907
|
+
* before the attempt is an infra error, after one retry; default 90s.
|
|
908
|
+
*/
|
|
909
|
+
startTimeout?: Duration;
|
|
910
|
+
};
|
|
911
|
+
/** A real coding agent, driven through its AI SDK harness adapter. */
|
|
912
|
+
type AgentHarnessDescriptor = CursorHarnessDescriptor | ClaudeCodeHarnessDescriptor | AntigravityHarnessDescriptor | OpenCodeHarnessDescriptor | GooseHarnessDescriptor | PiHarnessDescriptor | CodexHarnessDescriptor;
|
|
913
|
+
type HarnessDescriptor = FakeHarnessDescriptor | AgentHarnessDescriptor;
|
|
914
|
+
type FakeSandboxDescriptor = {
|
|
915
|
+
kind: 'fake';
|
|
916
|
+
id?: string;
|
|
917
|
+
/** Parent directory for the per-eval workspace (defaults to the OS temp dir). */
|
|
918
|
+
dir?: string;
|
|
919
|
+
/** The most attempts on this sandbox kind at once, across the run. */
|
|
920
|
+
maxConcurrent?: number;
|
|
921
|
+
};
|
|
922
|
+
type DockerSandboxDescriptor = {
|
|
923
|
+
kind: 'docker';
|
|
924
|
+
/** Cell id; defaults to `docker`, or `docker:<image>` for other images. */
|
|
925
|
+
id?: string;
|
|
926
|
+
/** An image that already has the harness baked in, used as is. */
|
|
927
|
+
image?: string;
|
|
928
|
+
/** A Debian or Ubuntu based toolchain image; reeval bakes Node and the harness on top, cached. */
|
|
929
|
+
from?: string;
|
|
930
|
+
/**
|
|
931
|
+
* An absolute path to a Dockerfile (`docker()` resolves it against the calling file), built from
|
|
932
|
+
* its directory with `REEVAL_BASE` set to reeval's sandbox image and cached by content hash.
|
|
933
|
+
*/
|
|
934
|
+
dockerfile?: string;
|
|
935
|
+
/** Baked onto the sandbox image in one build container: in order, no secrets, no workspace. */
|
|
936
|
+
install?: InstallStep[];
|
|
937
|
+
/** The most Docker sandboxes at once, across the run, whatever their image. */
|
|
938
|
+
maxConcurrent?: number;
|
|
939
|
+
/**
|
|
940
|
+
* Run the agent, setup steps and checks as root, with the capabilities `apt-get` needs, instead
|
|
941
|
+
* of the unprivileged `es` user. For evals that install system packages at run time.
|
|
942
|
+
*/
|
|
943
|
+
root?: boolean;
|
|
944
|
+
/** Linux capabilities to add back, e.g. `['NET_RAW']`; every other one is dropped. */
|
|
945
|
+
capabilities?: string[];
|
|
946
|
+
/** The container's memory, with no swap on top: `'4g'` (default), `'512m'`, or bytes. */
|
|
947
|
+
memory?: string | number;
|
|
948
|
+
/** CPUs the container may use, fractions allowed. Default 2, capped at the Docker host's. */
|
|
949
|
+
cpus?: number;
|
|
950
|
+
/** Processes and threads at once. Default 1024. */
|
|
951
|
+
pids?: number;
|
|
952
|
+
};
|
|
953
|
+
type SandboxDescriptor = FakeSandboxDescriptor | DockerSandboxDescriptor;
|
|
954
|
+
/**
|
|
955
|
+
* A host environment variable, redacted everywhere reeval writes and recorded by name only. Made by
|
|
956
|
+
* the `secret` helper of `defineEnvironment` and `defineConfig` callbacks.
|
|
957
|
+
*/
|
|
958
|
+
type Secret = {
|
|
959
|
+
kind: 'secret';
|
|
960
|
+
/** The host variable; defaults to the key it's set under. */
|
|
961
|
+
from?: string;
|
|
962
|
+
/** When the host variable is unset, skip it (the record says so) instead of failing. */
|
|
963
|
+
optional?: boolean;
|
|
964
|
+
/** `agent` (default): the container's environment. `checks`: only `t.exec` and `setup` steps. */
|
|
965
|
+
to?: 'agent' | 'checks';
|
|
966
|
+
/**
|
|
967
|
+
* Brokers the secret: the sandbox gets a per-attempt placeholder, and the Docker egress gateway
|
|
968
|
+
* swaps in the real value only on HTTPS requests to these hosts (`*.example.com` for subdomains).
|
|
969
|
+
*/
|
|
970
|
+
hosts?: string[];
|
|
971
|
+
/** Where in a request to those hosts the gateway substitutes; defaults to `['header']`. */
|
|
972
|
+
in?: SubstituteIn[];
|
|
973
|
+
};
|
|
974
|
+
/** `header` values, the `query` string, or the request `body`. */
|
|
975
|
+
type SubstituteIn = 'header' | 'query' | 'body';
|
|
976
|
+
type SecretOptions = {
|
|
977
|
+
optional?: boolean;
|
|
978
|
+
to?: 'agent' | 'checks';
|
|
979
|
+
hosts?: readonly string[];
|
|
980
|
+
in?: readonly SubstituteIn[];
|
|
981
|
+
};
|
|
982
|
+
/** `secret.template('Bearer ${TOKEN}')`: host variables substituted into a string, for MCP headers. */
|
|
983
|
+
type SecretTemplate = {
|
|
984
|
+
kind: 'secret-template';
|
|
985
|
+
template: string;
|
|
986
|
+
};
|
|
987
|
+
type EnvValue = string | Secret;
|
|
988
|
+
type EnvVars = Record<string, EnvValue>;
|
|
989
|
+
/** A file on the host, read when an attempt starts. Made by the `file` helper. */
|
|
990
|
+
type HostFile = {
|
|
991
|
+
kind: 'file';
|
|
992
|
+
path: string;
|
|
993
|
+
};
|
|
994
|
+
type McpServer = {
|
|
995
|
+
kind: 'mcp';
|
|
996
|
+
transport: 'stdio';
|
|
997
|
+
command: string;
|
|
998
|
+
args: string[];
|
|
999
|
+
env: EnvVars;
|
|
1000
|
+
} | {
|
|
1001
|
+
kind: 'mcp';
|
|
1002
|
+
transport: 'http' | 'sse';
|
|
1003
|
+
url: string;
|
|
1004
|
+
headers: Record<string, string | Secret | SecretTemplate>;
|
|
1005
|
+
};
|
|
1006
|
+
/** `harness` (default): the agent's own skills, via its adapter. `workspace`: its repo skill dir. */
|
|
1007
|
+
type SkillPlacement = 'harness' | 'workspace';
|
|
1008
|
+
type SkillSource = {
|
|
1009
|
+
kind: 'skill';
|
|
1010
|
+
placement: SkillPlacement;
|
|
1011
|
+
} & ({
|
|
1012
|
+
source: 'local';
|
|
1013
|
+
path: string;
|
|
1014
|
+
only?: string[];
|
|
1015
|
+
exclude?: string[];
|
|
1016
|
+
} | {
|
|
1017
|
+
source: 'git';
|
|
1018
|
+
repo: string;
|
|
1019
|
+
ref?: string;
|
|
1020
|
+
path?: string;
|
|
1021
|
+
only?: string[];
|
|
1022
|
+
exclude?: string[];
|
|
1023
|
+
} | {
|
|
1024
|
+
source: 'registry';
|
|
1025
|
+
id: string;
|
|
1026
|
+
version?: string;
|
|
1027
|
+
} | {
|
|
1028
|
+
source: 'npm';
|
|
1029
|
+
spec: string;
|
|
1030
|
+
path: string;
|
|
1031
|
+
only?: string[];
|
|
1032
|
+
exclude?: string[];
|
|
1033
|
+
} | {
|
|
1034
|
+
source: 'inline';
|
|
1035
|
+
name: string;
|
|
1036
|
+
description: string;
|
|
1037
|
+
content: string;
|
|
1038
|
+
files: Record<string, string>;
|
|
1039
|
+
});
|
|
1040
|
+
/** A `docker({ install })` command, baked into a cached image layer: no secrets, root, cwd `/`. */
|
|
1041
|
+
type InstallStep = string | {
|
|
1042
|
+
run: string;
|
|
1043
|
+
timeout?: Duration;
|
|
1044
|
+
};
|
|
1045
|
+
/** Runs in every attempt before the agent starts, with secrets, outside the eval timeout. */
|
|
1046
|
+
/** A shell command, or `{ run, timeout?, cwd? }`; `cwd` is relative to the workspace or absolute. */
|
|
1047
|
+
type SetupStep = string | {
|
|
1048
|
+
run: string;
|
|
1049
|
+
timeout?: Duration;
|
|
1050
|
+
cwd?: string;
|
|
1051
|
+
};
|
|
1052
|
+
/**
|
|
1053
|
+
* Where the sandbox may connect: anywhere (`'allow-all'`, the default), nowhere (`'deny-all'`), or
|
|
1054
|
+
* only the listed hosts (`'*.example.com'` matches subdomains). Under a restricted policy the
|
|
1055
|
+
* harness's own hosts, brokered secrets' `hosts` and MCP servers' hosts are allowed too; any
|
|
1056
|
+
* other request gets a refusal, and the attempt goes on.
|
|
1057
|
+
*/
|
|
1058
|
+
type EgressPolicy = 'allow-all' | 'deny-all' | {
|
|
1059
|
+
allow: readonly string[];
|
|
1060
|
+
};
|
|
1061
|
+
/**
|
|
1062
|
+
* A git repository (or one directory of it) the workspace starts as, from the `git` helper. `url`
|
|
1063
|
+
* is what reeval fetches: the clone URL, or an absolute path for a local repository.
|
|
1064
|
+
*/
|
|
1065
|
+
type GitWorkspace = {
|
|
1066
|
+
kind: 'git';
|
|
1067
|
+
/** As written: a URL, `github:owner/repo`, or a local path. */
|
|
1068
|
+
repo: string;
|
|
1069
|
+
url: string;
|
|
1070
|
+
/** A branch, tag or commit SHA; unset is the remote's `HEAD`. */
|
|
1071
|
+
ref?: string;
|
|
1072
|
+
/** A directory in the repository whose contents become the workspace. */
|
|
1073
|
+
path?: string;
|
|
1074
|
+
/** Give the workspace a `.git`: one commit of the fetched tree, with no remote. */
|
|
1075
|
+
init?: boolean;
|
|
1076
|
+
/** The host variable holding a token for the fetch; read on the host only. */
|
|
1077
|
+
token?: string;
|
|
1078
|
+
};
|
|
1079
|
+
type GitHelper = (repo: string | URL, options?: {
|
|
1080
|
+
ref?: string;
|
|
1081
|
+
path?: string;
|
|
1082
|
+
init?: boolean;
|
|
1083
|
+
token?: string;
|
|
1084
|
+
}) => GitWorkspace;
|
|
1085
|
+
/** What an eval runs in: the declarative, phased spec of one environment. */
|
|
1086
|
+
type EnvironmentSpec = {
|
|
1087
|
+
/** The code the workspace starts as, before `files`, `rules` and `setup`; a child's replaces its parent's. */
|
|
1088
|
+
workspace?: GitWorkspace;
|
|
1089
|
+
env?: EnvVars;
|
|
1090
|
+
/** Replaces the inherited policy; an `extend` child's replaces its parent's. */
|
|
1091
|
+
egress?: EgressPolicy;
|
|
1092
|
+
/** Workspace-relative, or `~/` for home. */
|
|
1093
|
+
files?: Record<string, string | HostFile>;
|
|
1094
|
+
/** Project rules, written where the harness's agent reads them: `AGENTS.md`, or `CLAUDE.md` for Claude Code. */
|
|
1095
|
+
rules?: string | HostFile;
|
|
1096
|
+
setup?: SetupStep[];
|
|
1097
|
+
skills?: SkillSource[];
|
|
1098
|
+
mcp?: Record<string, McpServer>;
|
|
1099
|
+
};
|
|
1100
|
+
/** One environment in an `extend` chain. */
|
|
1101
|
+
type EnvironmentLayer = {
|
|
1102
|
+
name: string;
|
|
1103
|
+
spec: EnvironmentSpec;
|
|
1104
|
+
};
|
|
1105
|
+
/**
|
|
1106
|
+
* What an eval runs in: skills, MCP servers, files, rules, variables, secrets and setup. Plain
|
|
1107
|
+
* data (`layers` root first), plus a non-enumerable `extend`; see `defineEnvironment`.
|
|
1108
|
+
*/
|
|
1109
|
+
type Environment = {
|
|
1110
|
+
readonly kind: 'environment';
|
|
1111
|
+
readonly name: string;
|
|
1112
|
+
readonly layers: readonly EnvironmentLayer[];
|
|
1113
|
+
/** A child environment layered on this one; wherever it sets a key this one has, it wins. */
|
|
1114
|
+
extend(name: string, spec: EnvironmentInput): Environment;
|
|
1115
|
+
};
|
|
1116
|
+
/** Typed helpers for environment specs, passed to `defineEnvironment` and `extend` callbacks. */
|
|
1117
|
+
type EnvironmentHelpers = {
|
|
1118
|
+
secret: SecretHelper;
|
|
1119
|
+
skill: SkillHelper;
|
|
1120
|
+
mcp: McpHelper;
|
|
1121
|
+
/** A host file, relative to the file that calls it (or an absolute path or `URL`). */
|
|
1122
|
+
file: (path: string | URL) => HostFile;
|
|
1123
|
+
/** A git repository for `workspace`: a URL, `github:owner/repo`, or a local path. */
|
|
1124
|
+
git: GitHelper;
|
|
1125
|
+
};
|
|
1126
|
+
type SecretHelper = {
|
|
1127
|
+
/** The host variable of the same name as the key it's set under. */
|
|
1128
|
+
(options?: SecretOptions): Secret;
|
|
1129
|
+
fromEnv(host: string, options?: SecretOptions): Secret;
|
|
1130
|
+
template(template: string): SecretTemplate;
|
|
1131
|
+
/** The hosts well-known APIs take their keys on, for `hosts`. */
|
|
1132
|
+
hosts: {
|
|
1133
|
+
readonly gemini: readonly string[];
|
|
1134
|
+
readonly openai: readonly string[];
|
|
1135
|
+
readonly anthropic: readonly string[];
|
|
1136
|
+
readonly github: readonly string[];
|
|
1137
|
+
};
|
|
1138
|
+
};
|
|
1139
|
+
type SkillCommon = {
|
|
1140
|
+
placement?: SkillPlacement;
|
|
1141
|
+
};
|
|
1142
|
+
type SkillDirOptions = SkillCommon & {
|
|
1143
|
+
only?: string[];
|
|
1144
|
+
/**
|
|
1145
|
+
* Paths in each skill directory to leave out of the sandbox, gitignore-style: `*` matches within
|
|
1146
|
+
* a name, a leading or inner `/` anchors to the skill root, a trailing `/` matches directories
|
|
1147
|
+
* only, and anything else matches a name at any depth. Replaces `DEFAULT_SKILL_EXCLUDE`
|
|
1148
|
+
* (`/evals/`, `.history/`, `*.eval.ts`); spread it to add to it. `.git` is always left out.
|
|
1149
|
+
*/
|
|
1150
|
+
exclude?: readonly string[];
|
|
1151
|
+
};
|
|
1152
|
+
type SkillHelper = {
|
|
1153
|
+
/** A directory with a `SKILL.md`, or a directory of them, relative to the calling file. */
|
|
1154
|
+
local(path: string | URL, options?: SkillDirOptions): SkillSource;
|
|
1155
|
+
/** `https://…`, `git@…`, `github:owner/repo` or a GitHub `/tree/<ref>/<path>` URL. */
|
|
1156
|
+
git(repo: string, options?: SkillDirOptions & {
|
|
1157
|
+
ref?: string;
|
|
1158
|
+
path?: string;
|
|
1159
|
+
}): SkillSource;
|
|
1160
|
+
/** A skills.sh id, `owner/repo@skill`; `version` is a git ref. */
|
|
1161
|
+
registry(id: string, options?: SkillCommon & {
|
|
1162
|
+
version?: string;
|
|
1163
|
+
}): SkillSource;
|
|
1164
|
+
/** A pinned npm package spec; skills are read from `path` inside it (default `skills`). */
|
|
1165
|
+
npm(spec: string, options?: SkillDirOptions & {
|
|
1166
|
+
path?: string;
|
|
1167
|
+
}): SkillSource;
|
|
1168
|
+
inline(skill: {
|
|
1169
|
+
name: string;
|
|
1170
|
+
description: string;
|
|
1171
|
+
content: string;
|
|
1172
|
+
files?: Record<string, string>;
|
|
1173
|
+
}, options?: SkillCommon): SkillSource;
|
|
1174
|
+
};
|
|
1175
|
+
type McpHelper = {
|
|
1176
|
+
stdio(command: string, args?: string[], options?: {
|
|
1177
|
+
env?: EnvVars;
|
|
1178
|
+
}): McpServer;
|
|
1179
|
+
http(url: string, options?: {
|
|
1180
|
+
headers?: Record<string, string | Secret | SecretTemplate>;
|
|
1181
|
+
}): McpServer;
|
|
1182
|
+
sse(url: string, options?: {
|
|
1183
|
+
headers?: Record<string, string | Secret | SecretTemplate>;
|
|
1184
|
+
}): McpServer;
|
|
1185
|
+
};
|
|
1186
|
+
/** A spec, or a callback that gets the typed helpers and returns one (synchronously). */
|
|
1187
|
+
type EnvironmentInput = EnvironmentSpec | ((helpers: EnvironmentHelpers) => EnvironmentSpec);
|
|
1188
|
+
/**
|
|
1189
|
+
* An edit of the inherited harness list, as data: `add` appends harnesses, `only` keeps the
|
|
1190
|
+
* inherited ones with these ids, in their order.
|
|
1191
|
+
*/
|
|
1192
|
+
type HarnessEdit = {
|
|
1193
|
+
add: HarnessDescriptor | HarnessDescriptor[];
|
|
1194
|
+
} | {
|
|
1195
|
+
only: string | string[];
|
|
1196
|
+
};
|
|
1197
|
+
/** One value, a list (each is its own cell), or an edit of the inherited list, as data. */
|
|
1198
|
+
type HarnessData = HarnessDescriptor | HarnessDescriptor[] | HarnessEdit;
|
|
1199
|
+
/** `HarnessData`, or a function that edits the inherited list (TypeScript only). */
|
|
1200
|
+
type HarnessOption = HarnessData | ((inherited: HarnessDescriptor[]) => HarnessDescriptor | HarnessDescriptor[]);
|
|
1201
|
+
type CompareGate = 'variants' | 'all' | 'none';
|
|
1202
|
+
/** Variants run next to the inherited environment (the control), with the same prompt and checks. */
|
|
1203
|
+
type CompareOptions<E = Environment> = E[] | {
|
|
1204
|
+
variants: E[];
|
|
1205
|
+
/** Which cells decide pass/fail: `variants` (default), `all`, or `none` (report only). */
|
|
1206
|
+
gate?: CompareGate;
|
|
1207
|
+
/** Lift in points from -1 to 1; a variant cell below it fails. */
|
|
1208
|
+
minLift?: number;
|
|
1209
|
+
/** Harnesses (ids or descriptors) to compare on; the others run the control only. */
|
|
1210
|
+
harness?: Array<string | HarnessDescriptor>;
|
|
1211
|
+
} | false;
|
|
1212
|
+
type PinnableKey = 'harness' | 'runs' | 'compare';
|
|
1213
|
+
/**
|
|
1214
|
+
* How a cell's attempts decide its outcome: `'all'` (the default) passes only when every attempt
|
|
1215
|
+
* passes; `{ passRate }` passes when at least that share of its passes and fails are passes.
|
|
1216
|
+
*/
|
|
1217
|
+
type GatePolicy = 'all' | {
|
|
1218
|
+
passRate: number;
|
|
1219
|
+
};
|
|
1220
|
+
/** A cell's gate: its policy, or `'none'` when its failures don't fail the eval (a control's). */
|
|
1221
|
+
type CellGate = GatePolicy | 'none';
|
|
1222
|
+
type EvalOptions = {
|
|
1223
|
+
/** The only list: each harness is its own cell. */
|
|
1224
|
+
harness?: HarnessOption;
|
|
1225
|
+
sandbox?: SandboxDescriptor;
|
|
1226
|
+
/** What the eval runs in; the control of `compare`. */
|
|
1227
|
+
environment?: Environment;
|
|
1228
|
+
compare?: CompareOptions;
|
|
1229
|
+
/** Attempts per cell. */
|
|
1230
|
+
runs?: number;
|
|
1231
|
+
/** How each cell's attempts decide its outcome; default `'all'`. */
|
|
1232
|
+
gate?: GatePolicy;
|
|
1233
|
+
/** Variables and secrets for the sandbox, merged by name down the cascade. */
|
|
1234
|
+
env?: EnvVars;
|
|
1235
|
+
/**
|
|
1236
|
+
* Where the sandbox may connect. Replaces the inherited policy: config, profile defaults, the
|
|
1237
|
+
* environment, then groups and the eval.
|
|
1238
|
+
*/
|
|
1239
|
+
egress?: EgressPolicy;
|
|
1240
|
+
judge?: JudgeOptions;
|
|
1241
|
+
/** Per attempt. */
|
|
1242
|
+
timeout?: Duration;
|
|
1243
|
+
tags?: string[];
|
|
1244
|
+
/** Keys a profile's `limit` may not narrow for this group or eval. */
|
|
1245
|
+
pin?: PinnableKey[];
|
|
1246
|
+
};
|
|
1247
|
+
type GroupOptions = EvalOptions;
|
|
1248
|
+
/** The config file is plain data sent to workers, so its judge model can't be an object. */
|
|
1249
|
+
type ConfigJudgeOptions = Omit<JudgeOptions, 'model'> & {
|
|
1250
|
+
model?: string | FakeJudgeModel;
|
|
1251
|
+
};
|
|
1252
|
+
type HistoryConfig = {
|
|
1253
|
+
/** Committed history folder (default: `.history` in the directory the evals live in). */
|
|
1254
|
+
dir?: string;
|
|
1255
|
+
/** Artifacts recorded next to each run: every eval's, failing evals' only, or none. */
|
|
1256
|
+
artifacts?: ArtifactLevel;
|
|
1257
|
+
};
|
|
1258
|
+
/** Run-level caps. `attempts` is checked against the plan; `duration` while running. */
|
|
1259
|
+
type Budget = {
|
|
1260
|
+
attempts?: number;
|
|
1261
|
+
duration?: Duration;
|
|
1262
|
+
};
|
|
1263
|
+
/** What evals get when they don't say otherwise: under groups and evals. */
|
|
1264
|
+
type ProfileDefaults = {
|
|
1265
|
+
harness?: HarnessDescriptor | HarnessDescriptor[];
|
|
1266
|
+
sandbox?: SandboxDescriptor;
|
|
1267
|
+
environment?: Environment;
|
|
1268
|
+
runs?: number;
|
|
1269
|
+
gate?: GatePolicy;
|
|
1270
|
+
env?: EnvVars;
|
|
1271
|
+
egress?: EgressPolicy;
|
|
1272
|
+
judge?: ConfigJudgeOptions;
|
|
1273
|
+
timeout?: Duration;
|
|
1274
|
+
};
|
|
1275
|
+
/** Over every eval, and it can only narrow; `pin` on a group or eval skips it. */
|
|
1276
|
+
type ProfileLimit = {
|
|
1277
|
+
/** The most runs per cell. */
|
|
1278
|
+
runs?: number;
|
|
1279
|
+
/** `false` runs the control only; `{ maxVariants }` keeps the first n variants. */
|
|
1280
|
+
compare?: false | {
|
|
1281
|
+
maxVariants: number;
|
|
1282
|
+
};
|
|
1283
|
+
/** Harness ids to allow; evals left with none are skipped. */
|
|
1284
|
+
harness?: string[];
|
|
1285
|
+
};
|
|
1286
|
+
/** How a run is sized: chosen with `--profile`, `REEVAL_PROFILE` or `defaultProfile`. */
|
|
1287
|
+
type Profile = {
|
|
1288
|
+
defaults?: ProfileDefaults;
|
|
1289
|
+
limit?: ProfileLimit;
|
|
1290
|
+
judge?: ConfigJudgeOptions;
|
|
1291
|
+
budget?: Budget;
|
|
1292
|
+
record?: RecordMode;
|
|
1293
|
+
/** Tag filters for this profile, like `--tag` (`'!slow'` excludes). */
|
|
1294
|
+
tags?: string[];
|
|
1295
|
+
};
|
|
1296
|
+
/** A canned analysis for tests; see `fakeAnalysis()` in `reeval/judge`. */
|
|
1297
|
+
type FakeAnalysisModel = {
|
|
1298
|
+
kind: 'fake-analysis';
|
|
1299
|
+
/** The model's structured answer, or a function of the digest it was given. */
|
|
1300
|
+
output: unknown | ((digest: unknown) => unknown);
|
|
1301
|
+
};
|
|
1302
|
+
/** `reeval analyze`: a language model (not an evaluation model) and what matters in this project. */
|
|
1303
|
+
type AnalysisConfig = {
|
|
1304
|
+
/** `'provider/model'`, e.g. `'google/gemini-3.8-flash'`; default: the judge's, if it's one. */
|
|
1305
|
+
model?: string | FakeAnalysisModel;
|
|
1306
|
+
/** Added to the prompt, e.g. "Fewer tool calls count as a win. The skill under test is reeval-good." */
|
|
1307
|
+
instructions?: string;
|
|
1308
|
+
/** The digest's cap; evidence is dropped, lowest priority first, to fit (default 20000). */
|
|
1309
|
+
maxInputTokens?: number;
|
|
1310
|
+
/** The most tokens the model may answer with (default 8000). reeval has no prices, so tokens are the cap. */
|
|
1311
|
+
maxOutputTokens?: number;
|
|
1312
|
+
};
|
|
1313
|
+
type EsConfig = {
|
|
1314
|
+
include?: string[];
|
|
1315
|
+
harness?: HarnessDescriptor | HarnessDescriptor[];
|
|
1316
|
+
sandbox?: SandboxDescriptor;
|
|
1317
|
+
environment?: Environment;
|
|
1318
|
+
runs?: number;
|
|
1319
|
+
/** How each cell's attempts decide its outcome; default `'all'`. */
|
|
1320
|
+
gate?: GatePolicy;
|
|
1321
|
+
env?: EnvVars;
|
|
1322
|
+
egress?: EgressPolicy;
|
|
1323
|
+
judge?: ConfigJudgeOptions;
|
|
1324
|
+
timeout?: Duration;
|
|
1325
|
+
/** How many attempts run at once. `--concurrency` and `REEVAL_CONCURRENCY` override it. */
|
|
1326
|
+
concurrency?: number;
|
|
1327
|
+
/** When runs are copied into the history folder: `reeval record` only, in CI, or on every run. */
|
|
1328
|
+
record?: RecordMode;
|
|
1329
|
+
history?: HistoryConfig;
|
|
1330
|
+
budget?: Budget;
|
|
1331
|
+
profiles?: Record<string, Profile>;
|
|
1332
|
+
/** The profile when neither `--profile` nor `REEVAL_PROFILE` names one: `ci` when `CI` is set. */
|
|
1333
|
+
defaultProfile?: {
|
|
1334
|
+
ci?: string;
|
|
1335
|
+
local?: string;
|
|
1336
|
+
};
|
|
1337
|
+
/** `unbrokered: 'error'` refuses Docker secrets without `hosts` instead of warning (default `'warn'`). */
|
|
1338
|
+
secrets?: {
|
|
1339
|
+
unbrokered?: 'warn' | 'error';
|
|
1340
|
+
};
|
|
1341
|
+
/**
|
|
1342
|
+
* Local debug data in `.reeval/debug`: how many runs to keep (default 20), and how many MB of raw
|
|
1343
|
+
* trace each attempt buffers for trace.ndjson (default 8; the oldest entries go first).
|
|
1344
|
+
*/
|
|
1345
|
+
debug?: {
|
|
1346
|
+
keep?: number;
|
|
1347
|
+
traceBufferMb?: number;
|
|
1348
|
+
};
|
|
1349
|
+
analysis?: AnalysisConfig;
|
|
1350
|
+
};
|
|
1351
|
+
/** What a `defineConfig` callback gets: the environment helpers plus the run's context. */
|
|
1352
|
+
type ConfigHelpers = EnvironmentHelpers & {
|
|
1353
|
+
/** True when `CI` is set and isn't `false`. */
|
|
1354
|
+
ci: boolean;
|
|
1355
|
+
/** The `reeval` command that's running, e.g. `run`. */
|
|
1356
|
+
command: string;
|
|
1357
|
+
/** `process.env`. */
|
|
1358
|
+
hostEnv: NodeJS.ProcessEnv;
|
|
1359
|
+
/** The profile named by `--profile` or `REEVAL_PROFILE` (not `defaultProfile`), else undefined. */
|
|
1360
|
+
profile: string | undefined;
|
|
1361
|
+
};
|
|
1362
|
+
type Outcome = 'pass' | 'fail' | 'infra_error' | 'skipped';
|
|
1363
|
+
type ErrorKind = 'assertion' | 'timeout' | 'aborted' | 'infra' | 'judge';
|
|
1364
|
+
/**
|
|
1365
|
+
* The sandbox an attempt needed that couldn't be prepared before the run, on each attempt that
|
|
1366
|
+
* needed it: its provider's stage (Docker's `image`, `from`, `dockerfile`, `harness` or
|
|
1367
|
+
* `install`, or `workspace` for a git workspace, whose `ref` is `repo#ref:path`), what was being
|
|
1368
|
+
* made, the class (`config`, `transient` after the retries, or `resource`), a named cause
|
|
1369
|
+
* (`enospc`, `oom`), the build log, and the levels that set it.
|
|
1370
|
+
*/
|
|
1371
|
+
type PrepareFailureRecord = {
|
|
1372
|
+
class: 'config' | 'transient' | 'resource';
|
|
1373
|
+
stage: string;
|
|
1374
|
+
ref: string | null;
|
|
1375
|
+
cause: 'enospc' | 'oom' | null;
|
|
1376
|
+
log: string | null;
|
|
1377
|
+
setBy: string[];
|
|
1378
|
+
};
|
|
1379
|
+
/** Why an attempt failed; `prepare` when its sandbox couldn't be prepared. */
|
|
1380
|
+
type AttemptError = {
|
|
1381
|
+
kind: ErrorKind;
|
|
1382
|
+
message: string;
|
|
1383
|
+
prepare?: PrepareFailureRecord;
|
|
1384
|
+
};
|
|
1385
|
+
/** Where an eval's time went, in ms. `agentMs` is the harness turns; `diffMs` reading changes. */
|
|
1386
|
+
type PhaseTimings = {
|
|
1387
|
+
setupMs: number;
|
|
1388
|
+
agentMs: number;
|
|
1389
|
+
diffMs: number;
|
|
1390
|
+
checksMs: number;
|
|
1391
|
+
judgeMs: number;
|
|
1392
|
+
};
|
|
1393
|
+
type CellRole = 'control' | 'variant';
|
|
1394
|
+
/**
|
|
1395
|
+
* One cell of an eval: a harness and an environment (`-` for none) on the eval's sandbox. With
|
|
1396
|
+
* `compare`, the control runs the inherited environment and each variant runs it extended.
|
|
1397
|
+
*/
|
|
1398
|
+
type Cell = {
|
|
1399
|
+
key: string;
|
|
1400
|
+
harness: string;
|
|
1401
|
+
environment: string;
|
|
1402
|
+
sandbox: string;
|
|
1403
|
+
role: CellRole;
|
|
1404
|
+
/** The variant's name, for variant cells. */
|
|
1405
|
+
variant?: string;
|
|
1406
|
+
};
|
|
1407
|
+
/** An environment as it was applied, without secret values. */
|
|
1408
|
+
type EnvironmentRecord = {
|
|
1409
|
+
name: string;
|
|
1410
|
+
/** The environment this one extends (for a variant cell, the control), else null. */
|
|
1411
|
+
parent: string | null;
|
|
1412
|
+
/** Keys a later layer replaced, e.g. `env.KEY`, `skills.genkit`, `mcp.docs`, `rules`. */
|
|
1413
|
+
overrides: string[];
|
|
1414
|
+
/** Covers every field but the rules file's path; secret names are included, values never are. The image is the sandbox's. */
|
|
1415
|
+
digest: string;
|
|
1416
|
+
setup: string[];
|
|
1417
|
+
skills: Array<{
|
|
1418
|
+
name: string;
|
|
1419
|
+
source: string;
|
|
1420
|
+
commit: string | null;
|
|
1421
|
+
sha256: string;
|
|
1422
|
+
placement: SkillPlacement;
|
|
1423
|
+
/** Paths left out of the sandbox by `exclude` (directories end in `/`); `.git` isn't listed. */
|
|
1424
|
+
excluded: string[];
|
|
1425
|
+
}>;
|
|
1426
|
+
mcp: string[];
|
|
1427
|
+
files: Array<{
|
|
1428
|
+
path: string;
|
|
1429
|
+
sha256: string;
|
|
1430
|
+
}>;
|
|
1431
|
+
vars: string[];
|
|
1432
|
+
secrets: string[];
|
|
1433
|
+
/** Secrets the sandbox only had placeholders for, swapped in by the egress gateway. */
|
|
1434
|
+
brokered: string[];
|
|
1435
|
+
/** Optional secrets whose host variable was unset, so they weren't injected. */
|
|
1436
|
+
unset: string[];
|
|
1437
|
+
/** The policy the attempt ran under, from the options and the environment; in the digest unless `allow-all`. */
|
|
1438
|
+
egress: EgressPolicy;
|
|
1439
|
+
/**
|
|
1440
|
+
* The git repository the workspace started as: the repo as written (a local one relative to the
|
|
1441
|
+
* project root), the ref and path asked for (null when unset), the commit the ref resolved to
|
|
1442
|
+
* and the hash of the tree that was copied in, which is in the digest.
|
|
1443
|
+
*/
|
|
1444
|
+
workspace?: {
|
|
1445
|
+
source: string;
|
|
1446
|
+
ref: string | null;
|
|
1447
|
+
path: string | null;
|
|
1448
|
+
commit: string;
|
|
1449
|
+
tree: string;
|
|
1450
|
+
/** `init: true`: the workspace had a `.git` with one commit of the tree. */
|
|
1451
|
+
init?: boolean;
|
|
1452
|
+
};
|
|
1453
|
+
};
|
|
1454
|
+
/**
|
|
1455
|
+
* An attempt's network traffic. `allowed` is every host a restricted policy let through, its own
|
|
1456
|
+
* and the automatic ones; null under `allow-all`. `recorded` is false when the sandbox can't see
|
|
1457
|
+
* the traffic, and then `contacted` and `blocked` are empty. Bytes are counted between the
|
|
1458
|
+
* sandbox and the egress gateway, TLS included. `expectedBlocked` are refused hosts the harness
|
|
1459
|
+
* always tries (its CLI's telemetry, say), kept out of `blocked` so a real block stands out.
|
|
1460
|
+
*/
|
|
1461
|
+
type EgressRecord = {
|
|
1462
|
+
policy: EgressPolicy;
|
|
1463
|
+
allowed: string[] | null;
|
|
1464
|
+
recorded: boolean;
|
|
1465
|
+
contacted: Array<{
|
|
1466
|
+
host: string;
|
|
1467
|
+
port: number;
|
|
1468
|
+
connections: number;
|
|
1469
|
+
sent: number;
|
|
1470
|
+
received: number;
|
|
1471
|
+
}>;
|
|
1472
|
+
blocked: Array<{
|
|
1473
|
+
host: string;
|
|
1474
|
+
port: number;
|
|
1475
|
+
connections: number;
|
|
1476
|
+
reason: string;
|
|
1477
|
+
}>;
|
|
1478
|
+
expectedBlocked: Array<{
|
|
1479
|
+
host: string;
|
|
1480
|
+
port: number;
|
|
1481
|
+
connections: number;
|
|
1482
|
+
reason: string;
|
|
1483
|
+
}>;
|
|
1484
|
+
};
|
|
1485
|
+
/** The image an attempt's container started from, and how reeval made it. */
|
|
1486
|
+
type AttemptImage = {
|
|
1487
|
+
ref: string;
|
|
1488
|
+
/** Content-addressed image id (`sha256:…`). */
|
|
1489
|
+
id: string;
|
|
1490
|
+
agentCli: string | null;
|
|
1491
|
+
/** The harness image `install` was baked onto: `ref` itself without `install`. */
|
|
1492
|
+
base: string;
|
|
1493
|
+
/** The `docker({ install })` commands, in order. */
|
|
1494
|
+
install: string[];
|
|
1495
|
+
/** `docker({ dockerfile })`: the path from the project root and the build context's hash. */
|
|
1496
|
+
dockerfile: {
|
|
1497
|
+
path: string;
|
|
1498
|
+
sha256: string;
|
|
1499
|
+
} | null;
|
|
1500
|
+
/**
|
|
1501
|
+
* `docker({ from })`: the base as written and the digest the image was built on (a local-only
|
|
1502
|
+
* base's image id); `reeval image build` rebuilds it when the tag has moved since.
|
|
1503
|
+
*/
|
|
1504
|
+
from?: {
|
|
1505
|
+
ref: string;
|
|
1506
|
+
digest: string | null;
|
|
1507
|
+
};
|
|
1508
|
+
};
|
|
1509
|
+
/** A harness session start that timed out and was started again. */
|
|
1510
|
+
type StartRetry = {
|
|
1511
|
+
/** `<harness kind>: session did not become ready within <startTimeout>`. */
|
|
1512
|
+
error: string;
|
|
1513
|
+
/** How long it waited before giving up on that start. */
|
|
1514
|
+
waitedMs: number;
|
|
1515
|
+
};
|
|
1516
|
+
/**
|
|
1517
|
+
* A sandbox with more privilege than the default `es` user: `docker({ root: true })`, or
|
|
1518
|
+
* `docker({ capabilities })`. One per level that set such a sandbox, however many evals inherit it.
|
|
1519
|
+
*/
|
|
1520
|
+
type SandboxWarning = {
|
|
1521
|
+
kind: 'root' | 'capabilities';
|
|
1522
|
+
/** Which level set the sandbox. */
|
|
1523
|
+
level: 'config' | 'profile' | 'group' | 'eval';
|
|
1524
|
+
/** That level as reeval names it: `reeval.config.ts`, `profiles.pr.defaults`, `group("net")` or `evaluate("installs deps")`. */
|
|
1525
|
+
where: string;
|
|
1526
|
+
/** The eval file that sets it, for a group or eval; null for the config or a profile. */
|
|
1527
|
+
file: string | null;
|
|
1528
|
+
/** The capabilities added back, for `capabilities`. */
|
|
1529
|
+
capabilities?: string[];
|
|
1530
|
+
/** The line reeval prints, after `WARNING: ` (root) or `warning: ` (capabilities). */
|
|
1531
|
+
message: string;
|
|
1532
|
+
};
|
|
1533
|
+
//#endregion
|
|
1534
|
+
export { SandboxWarning as $, FakeJudgeModel as A, contrastEffort as At, HostFile as B, EsConfig as C, EffortSample as Ct, ExecResult as D, cellContrast as Dt, EvalOptions as E, VERDICT_TEXT as Et, HarnessData as F, effortSummary as Ft, OpenCodeHarnessDescriptor as G, JudgeModel as H, HarnessDescriptor as I, effortVerdict as It, PinnableKey as J, Outcome as K, HarnessEdit as L, formatCount as Lt, FakeScriptStep as M, effortLabel as Mt, GooseHarnessDescriptor as N, effortOf as Nt, FakeAnalysisModel as O, cellEffort as Ot, GroupOptions as P, effortSample as Pt, SandboxDescriptor as Q, HarnessOption as R, formatRatio as Rt, EnvironmentSpec as S, EffortContrast as St, EvalFn as T, Spread as Tt, JudgeOptions as U, JudgeCallOptions as V, McpServer as W, ProfileDefaults as X, Profile as Y, ProfileLimit as Z, EgressRecord as _, JudgementRecord as _t, Cell as a, TranscriptEvent as at, EnvironmentHelpers as b, CellEffort as bt, CodexHarnessDescriptor as c, TurnStatus as ct, ConfigJudgeOptions as d, AttemptRecord as dt, Secret as et, CursorHarnessDescriptor as f, CellRecord as ft, EgressPolicy as g, HistoryEntry as gt, Duration as h, HistoryCell as ht, Budget as i, TokenUsage as it, FakeSandboxDescriptor as j, effortChangeLabel as jt, FakeHarnessDescriptor as k, cellSamples as kt, CompareOptions as l, Verdict as lt, DockerSandboxDescriptor as m, FileRef as mt, AnalysisConfig as n, SetupStep as nt, CellRole as o, Turn as ot, Diff as p, EvalEntry as pt, PiHarnessDescriptor as q, AntigravityHarnessDescriptor as r, SkillSource as rt, ClaudeCodeHarnessDescriptor as s, TurnData as st, AgentHarnessDescriptor as t, SecretTemplate as tt, ConfigHelpers as u, WebEventType as ut, EnvVars as v, RunRecord as vt, EvalContext as w, EffortVerdict as wt, EnvironmentInput as x, EFFORT_ALPHA as xt, Environment as y, StackSummary as yt, HistoryConfig as z, formatSeconds as zt };
|
|
1535
|
+
//# sourceMappingURL=types-DLec-6Cx.d.ts.map
|