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.
Files changed (213) hide show
  1. package/LICENSE +202 -0
  2. package/THIRD_PARTY_NOTICES.md +2160 -0
  3. package/assets/package.json +9 -0
  4. package/assets/pnpm-lock.yaml +1336 -0
  5. package/dist/agent-DVHkx8aT.js +532 -0
  6. package/dist/agent-DVHkx8aT.js.map +1 -0
  7. package/dist/analysis-CxSCt1YY.js +16 -0
  8. package/dist/analysis-CxSCt1YY.js.map +1 -0
  9. package/dist/assemble-B-nmz8oH.js +695 -0
  10. package/dist/assemble-B-nmz8oH.js.map +1 -0
  11. package/dist/assemble-CmRVDZ2Q.js +2 -0
  12. package/dist/attempt-BdRQ9jKX.js +176 -0
  13. package/dist/attempt-BdRQ9jKX.js.map +1 -0
  14. package/dist/bridge/index.mjs +1911 -0
  15. package/dist/bridge/package.json +13 -0
  16. package/dist/bridge/pnpm-lock.yaml +1041 -0
  17. package/dist/bridge/pnpm-workspace.yaml +2 -0
  18. package/dist/budget-DBUrH4jX.js +8 -0
  19. package/dist/budget-DBUrH4jX.js.map +1 -0
  20. package/dist/cli--CQ3Iy9q.js +1865 -0
  21. package/dist/cli--CQ3Iy9q.js.map +1 -0
  22. package/dist/cli-spec-DKAu09Gi.js +478 -0
  23. package/dist/cli-spec-DKAu09Gi.js.map +1 -0
  24. package/dist/cli.d.ts +1 -0
  25. package/dist/cli.js +8 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/colors-QV9dFVhN.js +34 -0
  28. package/dist/colors-QV9dFVhN.js.map +1 -0
  29. package/dist/command-BSJzHMtP.js +280 -0
  30. package/dist/command-BSJzHMtP.js.map +1 -0
  31. package/dist/command-CEm8PZ1N.js +1043 -0
  32. package/dist/command-CEm8PZ1N.js.map +1 -0
  33. package/dist/command-D-jnQWVG.js +673 -0
  34. package/dist/command-D-jnQWVG.js.map +1 -0
  35. package/dist/command-DQAvbG7d.js +432 -0
  36. package/dist/command-DQAvbG7d.js.map +1 -0
  37. package/dist/commands-A4udfyKZ.js +2 -0
  38. package/dist/commands-CF8mOfiu.js +207 -0
  39. package/dist/commands-CF8mOfiu.js.map +1 -0
  40. package/dist/config-Cuj0kVYj.js +415 -0
  41. package/dist/config-Cuj0kVYj.js.map +1 -0
  42. package/dist/context-loi9OCZv.js +106 -0
  43. package/dist/context-loi9OCZv.js.map +1 -0
  44. package/dist/core-tLN-z1Be.js +96 -0
  45. package/dist/core-tLN-z1Be.js.map +1 -0
  46. package/dist/db-Cjj2ZsfQ.js +2 -0
  47. package/dist/db-DxDzMhtB.js +827 -0
  48. package/dist/db-DxDzMhtB.js.map +1 -0
  49. package/dist/debug-D24NOQH1.js +89 -0
  50. package/dist/debug-D24NOQH1.js.map +1 -0
  51. package/dist/debug-SEmW4w8f.js +2 -0
  52. package/dist/dist-BZXYHN2X.js +1673 -0
  53. package/dist/dist-BZXYHN2X.js.map +1 -0
  54. package/dist/docker-DQDZWXF9.js +20 -0
  55. package/dist/docker-DQDZWXF9.js.map +1 -0
  56. package/dist/docker-DnWAVdEm.js +2 -0
  57. package/dist/docker-oSbCExHL.js +153 -0
  58. package/dist/docker-oSbCExHL.js.map +1 -0
  59. package/dist/doctor-CxlH1pDM.js +525 -0
  60. package/dist/doctor-CxlH1pDM.js.map +1 -0
  61. package/dist/egress-73SQfKdx.js +619 -0
  62. package/dist/egress-73SQfKdx.js.map +1 -0
  63. package/dist/env-BQqj93gu.js +21 -0
  64. package/dist/env-BQqj93gu.js.map +1 -0
  65. package/dist/environment-Mz7wvrtD.js +552 -0
  66. package/dist/environment-Mz7wvrtD.js.map +1 -0
  67. package/dist/errors-C9w_ud7g.js +160 -0
  68. package/dist/errors-C9w_ud7g.js.map +1 -0
  69. package/dist/errors-CSxfJ4qu.js +11 -0
  70. package/dist/errors-CSxfJ4qu.js.map +1 -0
  71. package/dist/files--6CEUXm7.js +2 -0
  72. package/dist/files-BLlrhU4-.js +62 -0
  73. package/dist/files-BLlrhU4-.js.map +1 -0
  74. package/dist/files-Bd25OAQH.js +2 -0
  75. package/dist/files-BvHCFWgD.js +2 -0
  76. package/dist/files-D2-qn_Dl.js +275 -0
  77. package/dist/files-D2-qn_Dl.js.map +1 -0
  78. package/dist/files-bF2fPuT9.js +99 -0
  79. package/dist/files-bF2fPuT9.js.map +1 -0
  80. package/dist/git-D6hMeieS.js +44 -0
  81. package/dist/git-D6hMeieS.js.map +1 -0
  82. package/dist/harness-ls5aX1UO.js +131 -0
  83. package/dist/harness-ls5aX1UO.js.map +1 -0
  84. package/dist/harness.d.ts +74 -0
  85. package/dist/harness.js +2 -0
  86. package/dist/header-DzFDoMCI.js +78 -0
  87. package/dist/header-DzFDoMCI.js.map +1 -0
  88. package/dist/ids-Bp6ut7ZD.js +49 -0
  89. package/dist/ids-Bp6ut7ZD.js.map +1 -0
  90. package/dist/images-Cc1-coFc.js +286 -0
  91. package/dist/images-Cc1-coFc.js.map +1 -0
  92. package/dist/index.d.ts +97 -0
  93. package/dist/index.js +1959 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/init-CFljS8BJ.js +344 -0
  96. package/dist/init-CFljS8BJ.js.map +1 -0
  97. package/dist/init-z2ZPn9Tj.js +2 -0
  98. package/dist/invocations-rXktgSAn.js +157 -0
  99. package/dist/invocations-rXktgSAn.js.map +1 -0
  100. package/dist/jiti-BOLXpKyG.js +46 -0
  101. package/dist/jiti-BOLXpKyG.js.map +1 -0
  102. package/dist/judge-D8Uf4dA9.js +146 -0
  103. package/dist/judge-D8Uf4dA9.js.map +1 -0
  104. package/dist/judge.d.ts +21 -0
  105. package/dist/judge.js +19 -0
  106. package/dist/judge.js.map +1 -0
  107. package/dist/live-Bs3LReMV.js +98 -0
  108. package/dist/live-Bs3LReMV.js.map +1 -0
  109. package/dist/matchers-DyLvm7EG.js +374 -0
  110. package/dist/matchers-DyLvm7EG.js.map +1 -0
  111. package/dist/model-BdO79gWz.js +44 -0
  112. package/dist/model-BdO79gWz.js.map +1 -0
  113. package/dist/model-CDL98xue.js +725 -0
  114. package/dist/model-CDL98xue.js.map +1 -0
  115. package/dist/model-v-0_Ik7S.js +2 -0
  116. package/dist/node-kG64XxdE.js +506 -0
  117. package/dist/node-kG64XxdE.js.map +1 -0
  118. package/dist/opencode-providers-DZuD_oYt.js +3149 -0
  119. package/dist/opencode-providers-DZuD_oYt.js.map +1 -0
  120. package/dist/options-CAQhu--C.js +452 -0
  121. package/dist/options-CAQhu--C.js.map +1 -0
  122. package/dist/patch-BkKje4cp.js +69 -0
  123. package/dist/patch-BkKje4cp.js.map +1 -0
  124. package/dist/paths-CBvm6pap.js +72 -0
  125. package/dist/paths-CBvm6pap.js.map +1 -0
  126. package/dist/plan-gwk6dJ8c.js +237 -0
  127. package/dist/plan-gwk6dJ8c.js.map +1 -0
  128. package/dist/preflight-BtubXKQ8.js +2 -0
  129. package/dist/preflight-BypZMNBG.js +185 -0
  130. package/dist/preflight-BypZMNBG.js.map +1 -0
  131. package/dist/prepare-Cwv0bHs_.js +3 -0
  132. package/dist/prepare-D6Uzcubb.js +213 -0
  133. package/dist/prepare-D6Uzcubb.js.map +1 -0
  134. package/dist/provenance-Dfb8YY5Y.js +129 -0
  135. package/dist/provenance-Dfb8YY5Y.js.map +1 -0
  136. package/dist/provider-BDbNOQhQ.js +2 -0
  137. package/dist/provider-CmAYTQQs.js +14 -0
  138. package/dist/provider-CmAYTQQs.js.map +1 -0
  139. package/dist/providers-2p3ylMHI.js +107 -0
  140. package/dist/providers-2p3ylMHI.js.map +1 -0
  141. package/dist/providers-BUOxitcT.js +2 -0
  142. package/dist/query-CFATHZmV.js +91 -0
  143. package/dist/query-CFATHZmV.js.map +1 -0
  144. package/dist/query-DR4m6CXf.js +2 -0
  145. package/dist/record-DQ8wzyco.js +99 -0
  146. package/dist/record-DQ8wzyco.js.map +1 -0
  147. package/dist/redact-C4fL_bZP.js +167 -0
  148. package/dist/redact-C4fL_bZP.js.map +1 -0
  149. package/dist/results-CeXb8Wde.js +37 -0
  150. package/dist/results-CeXb8Wde.js.map +1 -0
  151. package/dist/root-CFDjpWuc.js +110 -0
  152. package/dist/root-CFDjpWuc.js.map +1 -0
  153. package/dist/sandbox-By2gkdkC.js +133 -0
  154. package/dist/sandbox-By2gkdkC.js.map +1 -0
  155. package/dist/sandbox.d.ts +41 -0
  156. package/dist/sandbox.js +2 -0
  157. package/dist/server-Bi61uiY0.js +350 -0
  158. package/dist/server-Bi61uiY0.js.map +1 -0
  159. package/dist/server-CsSBL8NC.js +2 -0
  160. package/dist/source-BXzrmof5.js +2 -0
  161. package/dist/source-Bsfa3LZH.js +40 -0
  162. package/dist/source-Bsfa3LZH.js.map +1 -0
  163. package/dist/src-55j1zNHH.js +3 -0
  164. package/dist/src-AEwxd1UP.js +602 -0
  165. package/dist/src-AEwxd1UP.js.map +1 -0
  166. package/dist/src-BDwSuyIo.js +3 -0
  167. package/dist/src-BQyMiWri.js +490 -0
  168. package/dist/src-BQyMiWri.js.map +1 -0
  169. package/dist/src-C-LHyTSV.js +3 -0
  170. package/dist/src-Cmp-7YrD.js +1136 -0
  171. package/dist/src-Cmp-7YrD.js.map +1 -0
  172. package/dist/src-D0RdqpNb.js +252 -0
  173. package/dist/src-D0RdqpNb.js.map +1 -0
  174. package/dist/src-D8KYzxBX.js +793 -0
  175. package/dist/src-D8KYzxBX.js.map +1 -0
  176. package/dist/src-DEgff9Z7.js +4 -0
  177. package/dist/src-DXx_29pq.js +567 -0
  178. package/dist/src-DXx_29pq.js.map +1 -0
  179. package/dist/src-DwwiVLqv.js +2937 -0
  180. package/dist/src-DwwiVLqv.js.map +1 -0
  181. package/dist/src-N7E_mfcZ.js +2 -0
  182. package/dist/src-W9g-y5X5.js +3 -0
  183. package/dist/src-daS0wKH2.js +3 -0
  184. package/dist/state-Cyw3QTsN.js +2 -0
  185. package/dist/state-DZ7eqb1F.js +167 -0
  186. package/dist/state-DZ7eqb1F.js.map +1 -0
  187. package/dist/stats-BNAHlNMD.js +114 -0
  188. package/dist/stats-BNAHlNMD.js.map +1 -0
  189. package/dist/studio-app/assets/index-D6XyfFSH.js +58 -0
  190. package/dist/studio-app/assets/index-xwzWdcE_.css +2 -0
  191. package/dist/studio-app/favicon.svg +1 -0
  192. package/dist/studio-app/index.html +15 -0
  193. package/dist/summary-D7Ttky0z.js +166 -0
  194. package/dist/summary-D7Ttky0z.js.map +1 -0
  195. package/dist/text-gEcKjAyp.js +29 -0
  196. package/dist/text-gEcKjAyp.js.map +1 -0
  197. package/dist/types-C5vI3g1C.js +9 -0
  198. package/dist/types-C5vI3g1C.js.map +1 -0
  199. package/dist/types-DLec-6Cx.d.ts +1535 -0
  200. package/dist/usage-DsYUqIUM.js +24 -0
  201. package/dist/usage-DsYUqIUM.js.map +1 -0
  202. package/dist/view/index.d.ts +645 -0
  203. package/dist/view/index.js +8 -0
  204. package/dist/views-BrHOv6wg.js +220 -0
  205. package/dist/views-BrHOv6wg.js.map +1 -0
  206. package/dist/views-CwCWLWqo.js +3 -0
  207. package/dist/worker-entry-DnYkANPG.js +506 -0
  208. package/dist/worker-entry-DnYkANPG.js.map +1 -0
  209. package/dist/workspace-C2S0Z6gl.js +327 -0
  210. package/dist/workspace-C2S0Z6gl.js.map +1 -0
  211. package/dist/workspace-CyoXG6CY.js +2 -0
  212. package/docker/Dockerfile +19 -0
  213. package/package.json +79 -0
@@ -0,0 +1,725 @@
1
+ import { c as shardPath, i as manifestPath, s as shardOf } from "./paths-CBvm6pap.js";
2
+ import { a as quantile, i as minimumP, n as mannWhitney, r as median } from "./stats-BNAHlNMD.js";
3
+ import { s as readableTranscriptLine } from "./record-DQ8wzyco.js";
4
+ import { n as promptTokens } from "./usage-DsYUqIUM.js";
5
+ //#region src/view/compare.ts
6
+ const STACK_FIELDS = [
7
+ {
8
+ field: "imageIds",
9
+ label: "image ids",
10
+ outside: false
11
+ },
12
+ {
13
+ field: "agentClis",
14
+ label: "agent CLI",
15
+ outside: false
16
+ },
17
+ {
18
+ field: "harnesses",
19
+ label: "harnesses",
20
+ outside: false
21
+ },
22
+ {
23
+ field: "environments",
24
+ label: "environments",
25
+ outside: false
26
+ },
27
+ {
28
+ field: "judgeModel",
29
+ label: "judge model",
30
+ outside: false
31
+ },
32
+ {
33
+ field: "threshold",
34
+ label: "judge threshold",
35
+ outside: false
36
+ },
37
+ {
38
+ field: "judgeServed",
39
+ label: "judge model served",
40
+ outside: true
41
+ },
42
+ {
43
+ field: "judgeVersion",
44
+ label: "judge version",
45
+ outside: false
46
+ },
47
+ {
48
+ field: "fingerprintScope",
49
+ label: "fingerprint scope",
50
+ outside: true
51
+ }
52
+ ];
53
+ const show = (value) => Array.isArray(value) ? value.join(", ") || "none" : value === null ? "none" : String(value).replace(/^sha256:/, "").slice(0, 24);
54
+ /** The stack fields that differ by which cells ran, read off each cell. */
55
+ const PER_CELL = {
56
+ imageIds: (c) => c.fingerprintParts.image,
57
+ agentClis: (c) => c.fingerprintParts.agentCli,
58
+ harnesses: (c) => c.harness,
59
+ environments: (c) => `${c.environment}@${(c.environmentDigest ?? "").replace(/^sha256:/, "").slice(0, 12)}`
60
+ };
61
+ /** The cells both runs have (same eval, same cell key), as `[base, head]` pairs. */
62
+ function sharedCells(base, head) {
63
+ if (![base, head].every((r) => r.evals?.every((e) => Array.isArray(e.cells)))) return null;
64
+ if (!base.evals || !head.evals) return null;
65
+ const before = new Map(base.evals.map((e) => [e.id, new Map(e.cells.map((c) => [c.key, c]))]));
66
+ return head.evals.flatMap((e) => e.cells.flatMap((c) => {
67
+ const b = before.get(e.id)?.get(c.key);
68
+ return b ? [[b, c]] : [];
69
+ }));
70
+ }
71
+ /**
72
+ * What changed in the stack between two runs. With both runs' cells, images, agent CLIs,
73
+ * harnesses and environments compare over the cells both have, so a run over fewer cells isn't
74
+ * drift. Reported served-judge lists only compare when both have one.
75
+ */
76
+ function stackChanges(base, head) {
77
+ const shared = sharedCells(base, head);
78
+ const changes = [];
79
+ for (const { field, label, outside } of STACK_FIELDS) {
80
+ const pick = PER_CELL[field];
81
+ const differ = pick && shared ? shared.filter(([x, y]) => pick(x) !== pick(y)) : [];
82
+ const over = (side) => [...new Set(differ.flatMap((pair) => pick?.(pair[side]) ?? []))].sort();
83
+ const b = pick && shared ? over(0) : base.stack[field];
84
+ const h = pick && shared ? over(1) : head.stack[field];
85
+ if (field === "judgeServed" && (!b.length || !h.length)) continue;
86
+ if (JSON.stringify(b) === JSON.stringify(h)) continue;
87
+ changes.push({
88
+ field,
89
+ label,
90
+ base: show(b),
91
+ head: show(h),
92
+ outsideFingerprint: outside
93
+ });
94
+ }
95
+ return changes;
96
+ }
97
+ /**
98
+ * Whether any of these cells changed fingerprint since `seen` last had it, then records them.
99
+ * A cell `seen` doesn't have yet is new coverage, not a change.
100
+ */
101
+ function cellsChanged(seen, evalId, cells) {
102
+ let changed = false;
103
+ for (const c of cells) {
104
+ const key = `${evalId}\0${c.key}`;
105
+ const was = seen.get(key);
106
+ if (was !== void 0 && was !== c.fingerprint) changed = true;
107
+ seen.set(key, c.fingerprint);
108
+ }
109
+ return changed;
110
+ }
111
+ /** Which source files were added, removed or changed. */
112
+ function sourceChanges(before, after) {
113
+ const a = new Map(before.map((s) => [s.path, s.sha256]));
114
+ const b = new Map(after.map((s) => [s.path, s.sha256]));
115
+ return [.../* @__PURE__ */ new Set([...a.keys(), ...b.keys()])].sort().flatMap((path) => {
116
+ if (!a.has(path)) return [`${path} added`];
117
+ if (!b.has(path)) return [`${path} removed`];
118
+ return a.get(path) === b.get(path) ? [] : [`${path} changed`];
119
+ });
120
+ }
121
+ const PART_LABELS = {
122
+ harness: "harness config",
123
+ environment: "environment",
124
+ image: "image",
125
+ agentCli: "agent CLI",
126
+ judge: "judge",
127
+ context: "agent context"
128
+ };
129
+ /** Why two runs of an eval aren't comparable: the source files and fingerprint parts that differ. */
130
+ function whyChanged(base, head) {
131
+ const out = sourceChanges(base.sources, head.sources);
132
+ const keys = [...new Set([...base.cells, ...head.cells].map((c) => c.key))];
133
+ const parts = /* @__PURE__ */ new Set();
134
+ for (const key of keys) {
135
+ const b = base.cells.find((c) => c.key === key);
136
+ const h = head.cells.find((c) => c.key === key);
137
+ if (!b || !h) {
138
+ out.push(`cell ${key} ${b ? "removed" : "added"}`);
139
+ continue;
140
+ }
141
+ for (const part of Object.keys(PART_LABELS)) if (b.fingerprintParts[part] !== h.fingerprintParts[part]) parts.add(PART_LABELS[part]);
142
+ }
143
+ return [...out, ...[...parts].map((p) => `${p} changed`)];
144
+ }
145
+ /** An eval's outcome from some of its cells, as the run rolls it up: ungated failures pass. */
146
+ function outcomeOf(cells) {
147
+ const outcomes = cells.map((c) => c.gate === "none" && c.outcome === "fail" ? "pass" : c.outcome);
148
+ if (outcomes.includes("fail")) return "fail";
149
+ if (outcomes.includes("infra_error")) return "infra_error";
150
+ return outcomes.every((o) => o === "skipped") ? "skipped" : "pass";
151
+ }
152
+ /**
153
+ * When two runs of an eval differ only in which cells ran (a filtered run, a harness added), its
154
+ * outcome on the cells both have, and the cells only one has. Null when no cell is shared or a
155
+ * shared cell changed.
156
+ */
157
+ function sharedScope(b, h) {
158
+ const before = new Map(b.cells.map((c) => [c.key, c]));
159
+ const shared = h.cells.filter((c) => before.has(c.key));
160
+ if (!shared.length || shared.some((c) => before.get(c.key)?.fingerprint !== c.fingerprint)) return null;
161
+ const keys = new Set(h.cells.map((c) => c.key));
162
+ return {
163
+ base: outcomeOf(shared.map((c) => before.get(c.key) ?? c)),
164
+ head: outcomeOf(shared),
165
+ cells: {
166
+ added: h.cells.filter((c) => !before.has(c.key)).map((c) => c.key),
167
+ removed: b.cells.filter((c) => !keys.has(c.key)).map((c) => c.key)
168
+ }
169
+ };
170
+ }
171
+ /** Cells in both runs that have something to compare: a lift or effort on either side. */
172
+ function compareCells(base, head) {
173
+ const pick = (c) => ({
174
+ ...c.lift ? { lift: c.lift } : {},
175
+ effort: c.effort ?? null,
176
+ pass: c.pass,
177
+ fail: c.fail
178
+ });
179
+ return (head.cells ?? []).flatMap((h) => {
180
+ const b = base.cells?.find((c) => c.key === h.key);
181
+ if (!b || !(b.lift || h.lift || b.effort && h.effort)) return [];
182
+ return [{
183
+ key: h.key,
184
+ base: pick(b),
185
+ head: pick(h)
186
+ }];
187
+ });
188
+ }
189
+ function compare(base, head) {
190
+ const graderChanged = stackChanges(base, head).some((c) => c.outsideFingerprint);
191
+ return [.../* @__PURE__ */ new Set([...head.evals.map((e) => e.id), ...base.evals.map((e) => e.id)])].map((id) => {
192
+ const b = base.evals.find((e) => e.id === id);
193
+ const h = head.evals.find((e) => e.id === id);
194
+ if (!b) return {
195
+ id,
196
+ base: null,
197
+ head: h?.outcome ?? null,
198
+ status: "added"
199
+ };
200
+ if (!h) return {
201
+ id,
202
+ base: b.outcome,
203
+ head: null,
204
+ status: "removed"
205
+ };
206
+ const scoped = b.fingerprint === h.fingerprint ? null : sharedScope(b, h);
207
+ const row = scoped ? {
208
+ id,
209
+ ...scoped
210
+ } : {
211
+ id,
212
+ base: b.outcome,
213
+ head: h.outcome
214
+ };
215
+ const perCell = compareCells(b, h);
216
+ if (perCell.length) row.perCell = perCell;
217
+ if (row.base === "infra_error" || row.head === "infra_error") return {
218
+ ...row,
219
+ status: "infra"
220
+ };
221
+ if (b.fingerprint !== h.fingerprint && !scoped) return {
222
+ ...row,
223
+ status: "drift",
224
+ changes: whyChanged(b, h)
225
+ };
226
+ if (graderChanged && row.base !== row.head) return {
227
+ ...row,
228
+ status: "drift"
229
+ };
230
+ if (row.base === "pass" && row.head === "fail") return {
231
+ ...row,
232
+ status: "regressed"
233
+ };
234
+ if (row.base === "fail" && row.head === "pass") return {
235
+ ...row,
236
+ status: "fixed"
237
+ };
238
+ return {
239
+ ...row,
240
+ status: "same"
241
+ };
242
+ });
243
+ }
244
+ //#endregion
245
+ //#region src/view/effort.ts
246
+ /** Below this two-sided p, an effort difference is an effect. */
247
+ const EFFORT_ALPHA = .05;
248
+ const effortSample = (a) => ({
249
+ tools: a.activity.tools,
250
+ agentMs: a.timings.agentMs,
251
+ fileEdits: a.activity.fileEdits
252
+ });
253
+ const counted = (a) => a.outcome === "pass" || a.outcome === "fail";
254
+ const spread = (values) => ({
255
+ median: median(values) ?? 0,
256
+ q1: quantile(values, .25) ?? 0,
257
+ q3: quantile(values, .75) ?? 0
258
+ });
259
+ function effortOf(samples) {
260
+ if (samples.length === 0) return null;
261
+ return {
262
+ n: samples.length,
263
+ tools: spread(samples.map((s) => s.tools)),
264
+ agentMs: spread(samples.map((s) => s.agentMs)),
265
+ fileEdits: median(samples.map((s) => s.fileEdits)) ?? 0
266
+ };
267
+ }
268
+ /** The verdict of an exact rank test of `xs` (variant) against `ys` (control). */
269
+ function effortVerdict(xs, ys) {
270
+ const p = mannWhitney(xs, ys);
271
+ if (minimumP(xs.length, ys.length) >= .05) return {
272
+ p,
273
+ verdict: "insufficient"
274
+ };
275
+ if (p === null || p >= .05) return {
276
+ p,
277
+ verdict: "noise"
278
+ };
279
+ return {
280
+ p,
281
+ verdict: (median(xs) ?? 0) < (median(ys) ?? 0) ? "fewer" : "more"
282
+ };
283
+ }
284
+ const ratio = (a, b) => b === 0 ? null : a / b;
285
+ function contrastEffort(variant, control) {
286
+ const v = effortOf(variant);
287
+ const c = effortOf(control);
288
+ if (!v || !c) return null;
289
+ const tools = effortVerdict(variant.map((s) => s.tools), control.map((s) => s.tools));
290
+ const time = effortVerdict(variant.map((s) => s.agentMs), control.map((s) => s.agentMs));
291
+ const vt = variant.map((s) => s.tools);
292
+ const ct = control.map((s) => s.tools);
293
+ return {
294
+ variant: v,
295
+ control: c,
296
+ toolsRatio: ratio(v.tools.median, c.tools.median),
297
+ agentMsRatio: ratio(v.agentMs.median, c.agentMs.median),
298
+ agentMsDelta: v.agentMs.median - c.agentMs.median,
299
+ toolsP: tools.p,
300
+ agentMsP: time.p,
301
+ separated: Math.max(...vt) < Math.min(...ct) || Math.min(...vt) > Math.max(...ct),
302
+ verdict: tools.verdict,
303
+ timeVerdict: time.verdict
304
+ };
305
+ }
306
+ /** The counted attempts of one cell of an eval, as effort samples. */
307
+ const cellSamples = (e, cellKey) => e.attempts.filter((a) => a.cell === cellKey && counted(a)).map(effortSample);
308
+ const cellEffort = (e, cellKey) => effortOf(cellSamples(e, cellKey));
309
+ /** A variant cell's effort against its control's, in the same run; null for controls. */
310
+ function cellContrast(e, cellKey) {
311
+ const cell = e.cells.find((c) => c.key === cellKey);
312
+ if (!cell?.lift) return null;
313
+ return contrastEffort(cellSamples(e, cellKey), cellSamples(e, cell.lift.vs));
314
+ }
315
+ /** `4`, `4.5`: medians of counts are whole or halves. */
316
+ const formatCount = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
317
+ const formatRatio = (r) => r === null ? "×–" : `×${r.toFixed(2)}`;
318
+ /** Seconds for agent time: `21s`, `1m 05s`. */
319
+ function formatSeconds(ms) {
320
+ if (ms < 9950) return ms < 50 ? "0s" : `${(ms / 1e3).toFixed(1)}s`;
321
+ const s = Math.round(ms / 1e3);
322
+ return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${String(s % 60).padStart(2, "0")}s`;
323
+ }
324
+ const formatP = (p) => p === null ? "" : ` (p = ${p < .001 ? "<0.001" : p.toFixed(3)})`;
325
+ const VERDICT_TEXT = {
326
+ fewer: (what) => `fewer ${what}`,
327
+ more: (what) => `more ${what}`,
328
+ noise: () => "within noise",
329
+ insufficient: () => "too few attempts to test"
330
+ };
331
+ /**
332
+ * `tools 5 vs 43 (×0.11), agent 21s vs 46s (×0.47): too few attempts to test` for a variant
333
+ * against its control; with enough attempts, `fewer tool calls (p = 0.029)`.
334
+ */
335
+ function effortLabel(c) {
336
+ return `${`tools ${formatCount(c.variant.tools.median)} vs ${formatCount(c.control.tools.median)} (${formatRatio(c.toolsRatio)})`}, ${`agent ${formatSeconds(c.variant.agentMs.median)} vs ${formatSeconds(c.control.agentMs.median)} (${formatRatio(c.agentMsRatio)})`}: ${c.verdict === "fewer" || c.verdict === "more" ? `${VERDICT_TEXT[c.verdict]("tool calls")}${formatP(c.toolsP)}` : VERDICT_TEXT[c.verdict]("")}`;
337
+ }
338
+ /** One cell's effort on its own: `tools 43 · agent 46s`. */
339
+ const effortSummary = (e) => `tools ${formatCount(e.tools.median)} · agent ${formatSeconds(e.agentMs.median)}`;
340
+ /** The same cell between two runs: `tools 44 → 10 (×0.23) · agent 45s → 21s (×0.47)`. */
341
+ function effortChangeLabel(base, head) {
342
+ const t = ratio(head.tools.median, base.tools.median);
343
+ const a = ratio(head.agentMs.median, base.agentMs.median);
344
+ return `tools ${formatCount(base.tools.median)} → ${formatCount(head.tools.median)} (${formatRatio(t)}) · agent ${formatSeconds(base.agentMs.median)} → ${formatSeconds(head.agentMs.median)} (${formatRatio(a)})`;
345
+ }
346
+ //#endregion
347
+ //#region src/view/model.ts
348
+ /** Splits `file › group › … › name` back into its parts. */
349
+ function splitEvalId(id) {
350
+ const [file = id, ...path] = id.split(" › ");
351
+ return {
352
+ file,
353
+ path: path.length ? path : [file]
354
+ };
355
+ }
356
+ /** Month shards (newest run first) and the manifest over them, from every run's header. */
357
+ function buildIndex(entries, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
358
+ const sorted = [...entries].sort((a, b) => a.runId < b.runId ? 1 : -1);
359
+ const shards = /* @__PURE__ */ new Map();
360
+ for (const entry of sorted) {
361
+ const id = shardOf(entry.startedAt);
362
+ const list = shards.get(id) ?? [];
363
+ list.push(entry);
364
+ shards.set(id, list);
365
+ }
366
+ const evals = /* @__PURE__ */ new Map();
367
+ for (const run of sorted) for (const e of run.evals) {
368
+ const known = evals.get(e.slug);
369
+ if (known) {
370
+ known.runs++;
371
+ continue;
372
+ }
373
+ evals.set(e.slug, {
374
+ slug: e.slug,
375
+ id: e.id,
376
+ ...splitEvalId(e.id),
377
+ runs: 1,
378
+ latest: {
379
+ runId: run.runId,
380
+ outcome: e.outcome
381
+ }
382
+ });
383
+ }
384
+ return {
385
+ manifest: {
386
+ generatedAt,
387
+ shards: [...shards].map(([id, runs]) => ({
388
+ id,
389
+ runs: runs.length
390
+ })),
391
+ latest: sorted[0]?.runId ?? null,
392
+ evals: [...evals.values()].sort((a, b) => a.id < b.id ? -1 : 1)
393
+ },
394
+ shards
395
+ };
396
+ }
397
+ /** Every file the index consists of, by relative path. */
398
+ function indexFiles(entries, generatedAt) {
399
+ const { manifest, shards } = buildIndex(entries, generatedAt);
400
+ const files = /* @__PURE__ */ new Map([[manifestPath, manifest]]);
401
+ for (const [id, list] of shards) files.set(shardPath(id), list);
402
+ return files;
403
+ }
404
+ function formatDuration(ms) {
405
+ if (ms === null || ms === void 0) return "–";
406
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
407
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(ms < 1e4 ? 1 : 0)}s`;
408
+ const m = Math.floor(ms / 6e4);
409
+ const s = Math.round(ms % 6e4 / 1e3);
410
+ if (m < 60) return s ? `${m}m ${s}s` : `${m}m`;
411
+ return `${Math.floor(m / 60)}h ${m % 60}m`;
412
+ }
413
+ const formatPercent = (rate) => rate === null ? "–" : `${Math.round(rate * 100)}%`;
414
+ /**
415
+ * A harness of the run's stack as `cursor (model composer-2.5, served composer-2.5[fast=true])`:
416
+ * the configured model, then the variant the harness says it resolved.
417
+ */
418
+ function harnessLabel(h) {
419
+ const parts = [...h.model ? [`model ${h.model}`] : [], ...h.resolvedModels.length ? [`served ${h.resolvedModels.join(", ")}`] : []];
420
+ return parts.length ? `${h.id} (${parts.join(", ")})` : h.id;
421
+ }
422
+ /** Harness-reported tokens as `127k in (98k cached) · 776 out`; null when none were reported. */
423
+ function formatUsage(usage) {
424
+ if (!usage) return null;
425
+ const k = (n) => n >= 1e4 ? `${Math.round(n / 1e3)}k` : String(n);
426
+ const cached = usage.cacheRead ? ` (${k(usage.cacheRead)} cached)` : "";
427
+ return `${k(promptTokens(usage))} in${cached} · ${k(usage.output)} out`;
428
+ }
429
+ const shortSha = (sha) => sha ? sha.slice(0, 7) : "no git";
430
+ /**
431
+ * An attempt's effort, counted as the CLI's turn lines count it: tool calls, then file edits.
432
+ * Edit calls become one file edit per file they change, so they're counted apart, never summed.
433
+ */
434
+ function activityLabel(a) {
435
+ const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
436
+ return `${plural(a.tools, "tool call")} · ${plural(a.fileEdits, "file edit")}`;
437
+ }
438
+ /**
439
+ * A run's outcome, as `reeval run`'s exit code has it: a failed gated cell (or a budget stop) fails
440
+ * the run, while failures in ungated cells (controls, by default) don't.
441
+ */
442
+ function runOutcome(run) {
443
+ if (run.status === "interrupted") return "skipped";
444
+ const outcomes = run.evals.map((e) => e.outcome);
445
+ if (outcomes.includes("fail") || run.status === "budget") return "fail";
446
+ if (outcomes.includes("infra_error")) return "infra_error";
447
+ return "pass";
448
+ }
449
+ /**
450
+ * Newest first. Drift is measured per cell, against the last run that had it: a run over fewer
451
+ * cells (a filtered run) is no drift, and neither is the full run after it. The judge's settings
452
+ * compare with the next older complete run.
453
+ */
454
+ function runRows(entries) {
455
+ const oldest = [...entries].sort((a, b) => a.runId < b.runId ? -1 : 1);
456
+ const seen = /* @__PURE__ */ new Map();
457
+ const lastCells = /* @__PURE__ */ new Map();
458
+ let previous = null;
459
+ const rows = [];
460
+ for (const entry of oldest) {
461
+ const { pass, fail, infraError } = entry.summary;
462
+ const base = previous && {
463
+ stack: previous.stack,
464
+ evals: [...lastCells].map(([id, cells]) => ({
465
+ id,
466
+ cells: [...cells.values()]
467
+ }))
468
+ };
469
+ let changedEvals = 0;
470
+ for (const e of entry.evals) {
471
+ if (cellsChanged(seen, e.id, e.cells)) changedEvals++;
472
+ const cells = lastCells.get(e.id) ?? /* @__PURE__ */ new Map();
473
+ for (const c of e.cells) cells.set(c.key, c);
474
+ lastCells.set(e.id, cells);
475
+ }
476
+ rows.push({
477
+ entry,
478
+ passed: pass,
479
+ total: pass + fail + infraError,
480
+ drift: base ? stackChanges(base, entry) : [],
481
+ changedEvals
482
+ });
483
+ if (entry.status === "complete") previous = entry;
484
+ }
485
+ return rows.reverse();
486
+ }
487
+ const tallyOf = (e) => ({
488
+ pass: e.attempts.filter((a) => a.outcome === "pass").length,
489
+ fail: e.attempts.filter((a) => a.outcome === "fail" && !isUngatedMiss(e, a)).length,
490
+ missed: e.attempts.filter((a) => isUngatedMiss(e, a)).length,
491
+ infra: e.attempts.filter((a) => a.outcome === "infra_error").length,
492
+ skipped: e.attempts.filter((a) => a.outcome === "skipped").length
493
+ });
494
+ const addTally = (a, b) => ({
495
+ pass: a.pass + b.pass,
496
+ fail: a.fail + b.fail,
497
+ missed: a.missed + b.missed,
498
+ infra: a.infra + b.infra,
499
+ skipped: a.skipped + b.skipped
500
+ });
501
+ const ZERO = {
502
+ pass: 0,
503
+ fail: 0,
504
+ missed: 0,
505
+ infra: 0,
506
+ skipped: 0
507
+ };
508
+ /** Files, then groups, then evals, each with attempt counts; failing branches sort first. */
509
+ function evalTree(record) {
510
+ const root = {
511
+ kind: "group",
512
+ key: "",
513
+ name: "",
514
+ tally: ZERO,
515
+ children: []
516
+ };
517
+ for (const e of record.evals) {
518
+ const trail = [e.file, ...e.group];
519
+ let node = root;
520
+ for (const [i, name] of trail.entries()) {
521
+ const key = trail.slice(0, i + 1).join(" › ");
522
+ let child = node.children.find((c) => c.kind === "group" && c.key === key);
523
+ if (!child) {
524
+ child = {
525
+ kind: "group",
526
+ key,
527
+ name,
528
+ tally: ZERO,
529
+ children: []
530
+ };
531
+ node.children.push(child);
532
+ }
533
+ node = child;
534
+ }
535
+ node.children.push({
536
+ kind: "eval",
537
+ key: e.id,
538
+ name: e.name,
539
+ tally: tallyOf(e),
540
+ eval: e
541
+ });
542
+ }
543
+ const finish = (n) => {
544
+ if (n.kind === "eval") return n;
545
+ const children = n.children.map(finish);
546
+ children.sort((a, b) => rank(a.tally) - rank(b.tally));
547
+ return {
548
+ ...n,
549
+ children,
550
+ tally: children.reduce((t, c) => addTally(t, c.tally), ZERO)
551
+ };
552
+ };
553
+ return finish(root).children;
554
+ }
555
+ const rank = (t) => t.fail ? 0 : t.infra ? 1 : t.pass ? 2 : 3;
556
+ /** One row per cell: controls and variants, with each variant's recorded lift. */
557
+ function cellRows(e) {
558
+ return e.cells.map((cell) => ({
559
+ key: cell.key,
560
+ harness: cell.harness,
561
+ environment: cell.environment,
562
+ sandbox: cell.sandbox,
563
+ role: cell.role,
564
+ gated: cell.gate !== "none",
565
+ gate: cell.gate,
566
+ overrides: cell.overrides,
567
+ runs: cell.runs,
568
+ pass: cell.pass,
569
+ fail: cell.fail,
570
+ infraError: cell.infraError,
571
+ skipped: cell.skipped,
572
+ passRate: cell.passRate,
573
+ outcome: cell.outcome,
574
+ lift: cell.lift ? Math.round(cell.lift.points * 100) : null,
575
+ liftInterval: cell.lift ? {
576
+ lo: Math.round(cell.lift.interval.lo * 100),
577
+ hi: Math.round(cell.lift.interval.hi * 100),
578
+ withinNoise: cell.lift.withinNoise
579
+ } : null,
580
+ belowMinLift: cell.lift?.minLift !== void 0 && cell.lift.interval.lo < cell.lift.minLift,
581
+ effort: cellEffort(e, cell.key),
582
+ effortVsControl: cellContrast(e, cell.key),
583
+ attempts: e.attempts.filter((a) => a.cell === cell.key)
584
+ }));
585
+ }
586
+ /** Whether an eval needs the cells table: several cells, or several runs of one. */
587
+ const isMatrix = (e) => e.cells.length > 1 || e.cells.some((c) => c.runs > 1);
588
+ /** One eval across runs, oldest first. */
589
+ function evalHistory(entries, slug) {
590
+ const oldest = [...entries].sort((a, b) => a.runId < b.runId ? -1 : 1);
591
+ const points = [];
592
+ const seen = /* @__PURE__ */ new Map();
593
+ for (const run of oldest) {
594
+ const e = run.evals.find((x) => x.slug === slug);
595
+ if (!e) continue;
596
+ points.push({
597
+ run,
598
+ outcome: e.outcome,
599
+ passRate: e.passRate,
600
+ cells: e.cells,
601
+ changed: cellsChanged(seen, e.id, e.cells)
602
+ });
603
+ }
604
+ return points;
605
+ }
606
+ const meanScore = (e) => {
607
+ const scores = e?.attempts.flatMap((a) => a.judgements.map((j) => j.score)) ?? [];
608
+ return scores.length ? scores.reduce((a, b) => a + b, 0) / scores.length : null;
609
+ };
610
+ const COMPARE_ORDER = [
611
+ "regressed",
612
+ "fixed",
613
+ "drift",
614
+ "infra",
615
+ "added",
616
+ "removed",
617
+ "same"
618
+ ];
619
+ /**
620
+ * Whether some of an eval's cells aren't gated (`gate: 'none'`, e.g. controls under
621
+ * `compare.gate: 'variants'`). Its pass rate then covers fewer attempts than its pass count, so
622
+ * the eval line labels the rate `gated`: `2/4 · 100% gated`.
623
+ */
624
+ const hasUngatedCells = (cells) => cells.some((c) => c.gate === "none");
625
+ /**
626
+ * An expected miss: a failed attempt in an ungated cell (a control, by default). It's reported
627
+ * apart from failures, since it fails neither its eval nor the run.
628
+ */
629
+ const isUngatedMiss = (e, a) => a.outcome === "fail" && a.error?.kind !== "aborted" && e.cells.find((c) => c.key === a.cell)?.gate === "none";
630
+ /** Passes over passes and fails in the gated cells, as an eval's `passRate` counts them. */
631
+ function gatedPassRate(cells) {
632
+ const gated = cells.filter((c) => c.gate !== "none");
633
+ const pass = gated.reduce((n, c) => n + c.pass, 0);
634
+ const counted = pass + gated.reduce((n, c) => n + c.fail, 0);
635
+ return counted ? pass / counted : null;
636
+ }
637
+ /** `reeval compare`'s classification, plus pass rates and judge-score deltas from the records. */
638
+ function compareView(base, head, records = {}) {
639
+ const rows = compare(base, head).map((row) => {
640
+ const b = base.evals.find((e) => e.id === row.id);
641
+ const h = head.evals.find((e) => e.id === row.id);
642
+ const baseKeys = new Set(b?.cells.map((c) => c.key));
643
+ const shared = row.cells && new Set(h?.cells.filter((c) => baseKeys.has(c.key)).map((c) => c.key));
644
+ const passRate = (e) => {
645
+ if (!e) return null;
646
+ return shared ? gatedPassRate(e.cells.filter((c) => shared.has(c.key))) : e.passRate;
647
+ };
648
+ return {
649
+ ...row,
650
+ slug: h?.slug ?? b?.slug ?? null,
651
+ basePassRate: passRate(b),
652
+ headPassRate: passRate(h),
653
+ baseScore: meanScore(records.base?.evals.find((e) => e.id === row.id)),
654
+ headScore: meanScore(records.head?.evals.find((e) => e.id === row.id)),
655
+ perCell: row.perCell ?? []
656
+ };
657
+ });
658
+ rows.sort((a, b) => COMPARE_ORDER.indexOf(a.status) - COMPARE_ORDER.indexOf(b.status));
659
+ return {
660
+ rows,
661
+ counts: Object.fromEntries(COMPARE_ORDER.map((s) => [s, rows.filter((r) => r.status === s).length])),
662
+ changes: stackChanges(base, head)
663
+ };
664
+ }
665
+ /** Parses `transcript.jsonl`, skipping lines that aren't JSON (a truncated tail, say). */
666
+ function parseTranscript(text) {
667
+ const lines = [];
668
+ for (const line of text.split("\n")) {
669
+ if (!line.trim()) continue;
670
+ try {
671
+ const value = JSON.parse(line);
672
+ if (value && typeof value.type === "string") lines.push(readableTranscriptLine(value));
673
+ } catch {}
674
+ }
675
+ return lines;
676
+ }
677
+ function transcriptItems(lines) {
678
+ return lines.map((line) => {
679
+ if (line.type === "prompt") return {
680
+ kind: "turn",
681
+ turn: line.turn,
682
+ prompt: line.text,
683
+ ts: line.ts
684
+ };
685
+ if (line.type === "turn-end") return {
686
+ kind: "turn-end",
687
+ turn: line.turn,
688
+ status: line.status,
689
+ durationMs: line.durationMs
690
+ };
691
+ return {
692
+ kind: "event",
693
+ turn: line.turn,
694
+ event: line
695
+ };
696
+ });
697
+ }
698
+ const HEADER = /^diff --git a\/(.+?) b\/(.+)$/m;
699
+ /** Splits a git patch into one entry per file, with line counts. */
700
+ function splitPatch(patch) {
701
+ if (!patch.trim()) return [];
702
+ return patch.split(/(?=^diff --git )/m).filter((part) => part.startsWith("diff --git ")).map((text) => {
703
+ const header = HEADER.exec(text);
704
+ const path = header?.[2] ?? header?.[1] ?? "unknown";
705
+ let added = 0;
706
+ let removed = 0;
707
+ for (const line of text.split("\n")) if (line.startsWith("+") && !line.startsWith("+++ ")) added++;
708
+ else if (line.startsWith("-") && !line.startsWith("--- ")) removed++;
709
+ return {
710
+ path,
711
+ status: /^new file mode/m.test(text) ? "added" : /^deleted file mode/m.test(text) ? "deleted" : /^rename from/m.test(text) ? "renamed" : /^(Binary files|GIT binary patch)/m.test(text) ? "binary" : "modified",
712
+ added,
713
+ removed,
714
+ text
715
+ };
716
+ });
717
+ }
718
+ const findEval = (record, slug) => record.evals.find((e) => e.slug === slug);
719
+ const findAttempt = (e, attemptId) => e.attempts.find((a) => a.id === attemptId);
720
+ /** The attempt to open by default: the first failure, else the first attempt. */
721
+ const defaultAttempt = (e) => e.attempts.find((a) => a.outcome === "fail" || a.outcome === "infra_error") ?? e.attempts[0];
722
+ //#endregion
723
+ export { cellSamples as A, formatSeconds as B, splitEvalId as C, VERDICT_TEXT as D, EFFORT_ALPHA as E, effortSample as F, whyChanged as G, compare as H, effortSummary as I, effortVerdict as L, effortChangeLabel as M, effortLabel as N, cellContrast as O, effortOf as P, formatCount as R, shortSha as S, transcriptItems as T, sourceChanges as U, cellsChanged as V, stackChanges as W, isMatrix as _, compareView as a, runOutcome as b, evalTree as c, formatDuration as d, formatPercent as f, indexFiles as g, hasUngatedCells as h, cellRows as i, contrastEffort as j, cellEffort as k, findAttempt as l, harnessLabel as m, activityLabel as n, defaultAttempt as o, formatUsage as p, buildIndex as r, evalHistory as s, COMPARE_ORDER as t, findEval as u, isUngatedMiss as v, splitPatch as w, runRows as x, parseTranscript as y, formatRatio as z };
724
+
725
+ //# sourceMappingURL=model-CDL98xue.js.map