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,827 @@
1
+ import { t as UsageError } from "./errors-CSxfJ4qu.js";
2
+ import { t as BUILD_ID, u as ownRequire } from "./root-CFDjpWuc.js";
3
+ import { t as RUN_ID } from "./paths-CBvm6pap.js";
4
+ import { c as relativeFile, l as sha256 } from "./ids-Bp6ut7ZD.js";
5
+ import { n as isRuntimeState } from "./context-loi9OCZv.js";
6
+ import { P as effortOf, j as contrastEffort } from "./model-CDL98xue.js";
7
+ import { o as round3, s as wilson, t as liftInterval } from "./stats-BNAHlNMD.js";
8
+ import { f as scratchDir, l as recordNames, o as readRecord, p as warnUnreadable } from "./files-D2-qn_Dl.js";
9
+ import { dirname, join } from "node:path";
10
+ import { mkdir, readFile, stat } from "node:fs/promises";
11
+ /** A failing verdict this close to its threshold points at the judge, not the agent. */
12
+ const JUDGE_MARGIN = .1;
13
+ const DERIVED_TABLES = `
14
+ CREATE TABLE effort_m (
15
+ run_id TEXT, eval_key TEXT, cell_key TEXT, role TEXT, variant TEXT, harness TEXT,
16
+ environment TEXT, n INTEGER, tools_median REAL, tools_q1 REAL, tools_q3 REAL,
17
+ agent_s_median REAL, agent_s_q1 REAL, agent_s_q3 REAL, file_edits_median REAL,
18
+ control_key TEXT, control_n INTEGER, control_tools_median REAL, control_agent_s_median REAL,
19
+ tools_ratio REAL, agent_s_ratio REAL, agent_s_delta REAL, tools_p REAL, agent_s_p REAL,
20
+ separated INTEGER, verdict TEXT, time_verdict TEXT
21
+ );
22
+ CREATE TABLE factor_effects_m (
23
+ factor TEXT, level TEXT, baseline TEXT, context TEXT, tier TEXT, run_id TEXT, eval_key TEXT,
24
+ strata INTEGER, runs INTEGER, variant_pass INTEGER, variant_counted INTEGER,
25
+ control_pass INTEGER, control_counted INTEGER, points REAL, lo REAL, hi REAL,
26
+ within_noise INTEGER, pass_verdict TEXT, tools_ratio REAL, tools_ratios TEXT,
27
+ agent_s_ratio REAL, tools_p REAL, consistent INTEGER, separated INTEGER, effort_verdict TEXT,
28
+ note TEXT
29
+ );
30
+ CREATE TABLE flaky_m (
31
+ eval_key TEXT, cell_key TEXT, fingerprint TEXT, runs INTEGER, flaky_runs INTEGER,
32
+ first_seen TEXT, last_seen TEXT, pass INTEGER, counted INTEGER, infra_error INTEGER,
33
+ rate REAL, lo REAL, hi REAL, verdict TEXT, cause TEXT, min_attempts INTEGER
34
+ );
35
+ CREATE TABLE drift_m (
36
+ run_id TEXT, prev_run_id TEXT, eval_key TEXT, cell_key TEXT, changed TEXT,
37
+ changed_paths TEXT, noise INTEGER, real_changed TEXT
38
+ );
39
+ `;
40
+ const DERIVED = [
41
+ "effort_m",
42
+ "factor_effects_m",
43
+ "flaky_m",
44
+ "drift_m"
45
+ ];
46
+ const PARTS = [
47
+ ["sources", "fp_sources"],
48
+ ["harness", "fp_harness"],
49
+ ["environment", "fp_environment"],
50
+ ["image", "fp_image"],
51
+ ["agent_cli", "fp_agent_cli"],
52
+ ["judge", "fp_judge"],
53
+ ["context", "fp_context"]
54
+ ];
55
+ const r4 = (x) => x === null ? null : Math.round(x * 1e4) / 1e4;
56
+ const s1 = (ms) => Math.round(ms / 100) / 10;
57
+ const cellId = (run, evalKey, cell) => `${run}\0${evalKey}\0${cell}`;
58
+ function passCols(v, c) {
59
+ const lift = liftInterval({
60
+ pass: v.pass,
61
+ n: v.counted
62
+ }, {
63
+ pass: c.pass,
64
+ n: c.counted
65
+ });
66
+ const noise = lift.low <= 0 && lift.high >= 0;
67
+ return {
68
+ variant_pass: v.pass,
69
+ variant_counted: v.counted,
70
+ control_pass: c.pass,
71
+ control_counted: c.counted,
72
+ points: round3(lift.points),
73
+ lo: round3(lift.low),
74
+ hi: round3(lift.high),
75
+ within_noise: noise ? 1 : 0,
76
+ pass_verdict: v.counted && c.counted ? noise ? "noise" : "effect" : "insufficient"
77
+ };
78
+ }
79
+ function effortCols(e) {
80
+ return {
81
+ tools_ratio: r4(e?.toolsRatio ?? null),
82
+ tools_ratios: e?.toolsRatio === null || !e ? null : `×${e.toolsRatio.toFixed(2)}`,
83
+ agent_s_ratio: r4(e?.agentMsRatio ?? null),
84
+ tools_p: r4(e?.toolsP ?? null),
85
+ consistent: e && (e.verdict === "fewer" || e.verdict === "more") ? 1 : 0,
86
+ separated: e?.separated ? 1 : 0,
87
+ effort_verdict: e?.verdict ?? null
88
+ };
89
+ }
90
+ /** Across strata: an effect when ≥ 2/3 of them show it in the same direction at p < 0.05. */
91
+ function pooledEffort(strata) {
92
+ const known = strata.filter((s) => s !== null);
93
+ const k = strata.length;
94
+ const count = (v) => known.filter((s) => s.verdict === v).length;
95
+ const needed = Math.ceil(2 * k / 3);
96
+ let verdict = "noise";
97
+ if (count("fewer") >= needed && count("more") === 0) verdict = "fewer";
98
+ else if (count("more") >= needed && count("fewer") === 0) verdict = "more";
99
+ else if (known.length && count("insufficient") === known.length) verdict = "insufficient";
100
+ const sorted = [...known.map((s) => s.toolsRatio).filter((r) => r !== null)].sort((a, b) => a - b);
101
+ const mid = sorted.length ? sorted.length % 2 ? sorted[(sorted.length - 1) / 2] : (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : null;
102
+ return {
103
+ tools_ratio: r4(mid),
104
+ tools_ratios: known.map((s) => s.toolsRatio === null ? "×–" : `×${s.toolsRatio.toFixed(2)}`).join(" / "),
105
+ agent_s_ratio: null,
106
+ tools_p: r4(known.length ? Math.max(...known.map((s) => s.toolsP ?? 1)) : null),
107
+ consistent: verdict === "fewer" || verdict === "more" ? count(verdict) : 0,
108
+ separated: known.filter((s) => s.separated).length,
109
+ effort_verdict: verdict
110
+ };
111
+ }
112
+ const merge = (sides) => ({
113
+ pass: sides.reduce((n, s) => n + s.pass, 0),
114
+ counted: sides.reduce((n, s) => n + s.counted, 0),
115
+ samples: sides.flatMap((s) => s.samples)
116
+ });
117
+ function insertAll(db, table, rows) {
118
+ if (rows.length === 0) return;
119
+ const cols = db.prepare(`PRAGMA table_info("${table}")`).all().map((c) => c.name);
120
+ const statement = db.prepare(`INSERT INTO ${table} (${cols.join(", ")}) VALUES (${cols.map(() => "?").join(", ")})`);
121
+ for (const row of rows) statement.run(...cols.map((c) => row[c] ?? null));
122
+ }
123
+ /** Recomputes every derived table from the projected rows; run inside the sync's transaction. */
124
+ function deriveFacts(db) {
125
+ for (const table of DERIVED) db.exec(`DELETE FROM ${table}`);
126
+ const cells = db.prepare("SELECT c.*, r.started_at FROM cells c JOIN runs r USING (run_id) ORDER BY c.run_id, c.rowid").all();
127
+ const attempts = db.prepare("SELECT run_id, eval_key, cell_key, key, outcome, error_kind, tools, agent_ms, file_edits FROM attempts ORDER BY run_id, rowid").all();
128
+ const nearThreshold = new Set(db.prepare(`SELECT DISTINCT run_id, attempt_key FROM judgements WHERE verdict = 'fail' AND ABS(score - threshold) <= ${JUDGE_MARGIN}`).all().map((r) => `${r.run_id}\0${r.attempt_key}`));
129
+ const samples = /* @__PURE__ */ new Map();
130
+ const byCell = /* @__PURE__ */ new Map();
131
+ for (const a of attempts) {
132
+ const id = cellId(a.run_id, a.eval_key, a.cell_key);
133
+ byCell.set(id, [...byCell.get(id) ?? [], a]);
134
+ if (a.outcome !== "pass" && a.outcome !== "fail") continue;
135
+ samples.set(id, [...samples.get(id) ?? [], {
136
+ tools: a.tools,
137
+ agentMs: a.agent_ms,
138
+ fileEdits: a.file_edits
139
+ }]);
140
+ }
141
+ const side = (c) => ({
142
+ pass: c.pass,
143
+ counted: c.pass + c.fail,
144
+ samples: samples.get(cellId(c.run_id, c.eval_key, c.key)) ?? []
145
+ });
146
+ const find = (run, evalKey, key) => cells.find((c) => c.run_id === run && c.eval_key === evalKey && c.key === key);
147
+ insertAll(db, "effort_m", cells.map((c) => {
148
+ const own = effortOf(side(c).samples);
149
+ const control = c.lift_vs ? find(c.run_id, c.eval_key, c.lift_vs) : void 0;
150
+ const vs = control ? contrastEffort(side(c).samples, side(control).samples) : null;
151
+ return {
152
+ run_id: c.run_id,
153
+ eval_key: c.eval_key,
154
+ cell_key: c.key,
155
+ role: c.role,
156
+ variant: c.variant,
157
+ harness: c.harness,
158
+ environment: c.environment,
159
+ n: own?.n ?? 0,
160
+ tools_median: own?.tools.median ?? null,
161
+ tools_q1: own?.tools.q1 ?? null,
162
+ tools_q3: own?.tools.q3 ?? null,
163
+ agent_s_median: own ? s1(own.agentMs.median) : null,
164
+ agent_s_q1: own ? s1(own.agentMs.q1) : null,
165
+ agent_s_q3: own ? s1(own.agentMs.q3) : null,
166
+ file_edits_median: own?.fileEdits ?? null,
167
+ control_key: control?.key ?? null,
168
+ control_n: vs?.control.n ?? null,
169
+ control_tools_median: vs?.control.tools.median ?? null,
170
+ control_agent_s_median: vs ? s1(vs.control.agentMs.median) : null,
171
+ tools_ratio: r4(vs?.toolsRatio ?? null),
172
+ agent_s_ratio: r4(vs?.agentMsRatio ?? null),
173
+ agent_s_delta: vs ? s1(vs.agentMsDelta) : null,
174
+ tools_p: r4(vs?.toolsP ?? null),
175
+ agent_s_p: r4(vs?.agentMsP ?? null),
176
+ separated: vs ? vs.separated ? 1 : 0 : null,
177
+ verdict: vs?.verdict ?? null,
178
+ time_verdict: vs?.timeVerdict ?? null
179
+ };
180
+ }));
181
+ const pairs = [];
182
+ const groups = /* @__PURE__ */ new Map();
183
+ for (const c of cells) {
184
+ const id = `${c.run_id}\0${c.eval_key}`;
185
+ groups.set(id, [...groups.get(id) ?? [], c]);
186
+ }
187
+ for (const group of groups.values()) {
188
+ for (const v of group) {
189
+ const control = v.lift_vs ? group.find((c) => c.key === v.lift_vs) : void 0;
190
+ if (!control) continue;
191
+ pairs.push({
192
+ factor: "environment",
193
+ level: v.variant ?? v.environment,
194
+ baseline: "control",
195
+ context: v.harness,
196
+ run: v.run_id,
197
+ evalKey: v.eval_key,
198
+ pool: `${v.comparable}\0${control.comparable}`,
199
+ v: side(v),
200
+ c: side(control)
201
+ });
202
+ }
203
+ const bySetup = /* @__PURE__ */ new Map();
204
+ for (const c of group) {
205
+ const id = `${c.variant ?? c.role}\0${c.sandbox}`;
206
+ bySetup.set(id, [...bySetup.get(id) ?? [], c]);
207
+ }
208
+ for (const same of bySetup.values()) {
209
+ const base = same[0];
210
+ for (const other of same.slice(1)) {
211
+ if (other.harness === base.harness) continue;
212
+ pairs.push({
213
+ factor: other.harness_kind === base.harness_kind ? "model" : "harness",
214
+ level: other.harness,
215
+ baseline: base.harness,
216
+ context: other.variant ?? "control",
217
+ run: other.run_id,
218
+ evalKey: other.eval_key,
219
+ pool: `${other.comparable}\0${base.comparable}`,
220
+ v: side(other),
221
+ c: side(base)
222
+ });
223
+ }
224
+ }
225
+ }
226
+ const factorRows = [];
227
+ const head = (p) => ({
228
+ factor: p.factor,
229
+ level: p.level,
230
+ baseline: p.baseline,
231
+ context: p.context
232
+ });
233
+ for (const p of pairs) factorRows.push({
234
+ ...head(p),
235
+ tier: "controlled",
236
+ run_id: p.run,
237
+ eval_key: p.evalKey,
238
+ strata: 1,
239
+ runs: 1,
240
+ ...passCols(p.v, p.c),
241
+ ...effortCols(contrastEffort(p.v.samples, p.c.samples)),
242
+ note: null
243
+ });
244
+ const pooled = /* @__PURE__ */ new Map();
245
+ for (const p of pairs) {
246
+ const id = [
247
+ p.factor,
248
+ p.level,
249
+ p.baseline,
250
+ p.context,
251
+ p.evalKey,
252
+ p.pool
253
+ ].join("\0");
254
+ pooled.set(id, [...pooled.get(id) ?? [], p]);
255
+ }
256
+ const strata = /* @__PURE__ */ new Map();
257
+ for (const group of pooled.values()) {
258
+ const first = group[0];
259
+ const v = merge(group.map((p) => p.v));
260
+ const c = merge(group.map((p) => p.c));
261
+ if (group.length > 1) factorRows.push({
262
+ ...head(first),
263
+ tier: "pooled",
264
+ run_id: null,
265
+ eval_key: first.evalKey,
266
+ strata: 1,
267
+ runs: group.length,
268
+ ...passCols(v, c),
269
+ ...effortCols(contrastEffort(v.samples, c.samples)),
270
+ note: null
271
+ });
272
+ const id = [
273
+ first.factor,
274
+ first.level,
275
+ first.baseline,
276
+ first.context
277
+ ].join("\0");
278
+ strata.set(id, [...strata.get(id) ?? [], {
279
+ v,
280
+ c
281
+ }]);
282
+ }
283
+ for (const [id, list] of strata) {
284
+ if (list.length < 2) continue;
285
+ const [factor, level, baseline, context] = id.split("\0");
286
+ const v = merge(list.map((s) => s.v));
287
+ const c = merge(list.map((s) => s.c));
288
+ factorRows.push({
289
+ ...head({
290
+ factor,
291
+ level,
292
+ baseline,
293
+ context
294
+ }),
295
+ tier: "pooled",
296
+ run_id: null,
297
+ eval_key: null,
298
+ strata: list.length,
299
+ runs: null,
300
+ ...passCols(v, c),
301
+ ...pooledEffort(list.map((s) => contrastEffort(s.v.samples, s.c.samples))),
302
+ note: "pass rate: summed counts; effort: an effect in at least 2/3 of strata"
303
+ });
304
+ }
305
+ const driftRows = [];
306
+ const series = /* @__PURE__ */ new Map();
307
+ for (const c of cells) {
308
+ const id = `${c.eval_key}\0${c.key}`;
309
+ series.set(id, [...series.get(id) ?? [], c]);
310
+ }
311
+ const contextOf = db.prepare("SELECT path, sha256 FROM attempt_context WHERE run_id = ? AND attempt_key = ?");
312
+ const cellContext = (c) => {
313
+ const files = /* @__PURE__ */ new Map();
314
+ for (const a of byCell.get(cellId(c.run_id, c.eval_key, c.key)) ?? []) for (const f of contextOf.all(c.run_id, a.key)) files.set(f.path, f.sha256);
315
+ return files;
316
+ };
317
+ for (const list of series.values()) {
318
+ list.sort((a, b) => a.run_id < b.run_id ? -1 : 1);
319
+ for (let i = 1; i < list.length; i++) {
320
+ const prev = list[i - 1];
321
+ const cur = list[i];
322
+ if (prev.fingerprint === cur.fingerprint) continue;
323
+ const changed = PARTS.filter(([, col]) => prev[col] !== cur[col]).map(([name]) => name);
324
+ let paths = [];
325
+ if (changed.includes("context")) {
326
+ const a = cellContext(prev);
327
+ const b = cellContext(cur);
328
+ paths = [.../* @__PURE__ */ new Set([...a.keys(), ...b.keys()])].filter((p) => a.get(p) !== b.get(p)).sort();
329
+ }
330
+ const contextNoise = paths.length > 0 && paths.every(isRuntimeState);
331
+ const real = changed.filter((part) => part !== "context" || !contextNoise);
332
+ driftRows.push({
333
+ run_id: cur.run_id,
334
+ prev_run_id: prev.run_id,
335
+ eval_key: cur.eval_key,
336
+ cell_key: cur.key,
337
+ changed: changed.join(" "),
338
+ changed_paths: paths.length ? paths.join(", ") : null,
339
+ noise: real.length === 0 ? 1 : 0,
340
+ real_changed: real.join(" ") || null
341
+ });
342
+ if (real.length === 0) continue;
343
+ const judgeOnly = real.length === 1 && real[0] === "judge";
344
+ factorRows.push({
345
+ factor: real.length === 1 ? real[0] : "confounded",
346
+ level: cur.run_id,
347
+ baseline: prev.run_id,
348
+ context: `${cur.eval_key} # ${cur.key}`,
349
+ tier: "observed",
350
+ run_id: cur.run_id,
351
+ eval_key: cur.eval_key,
352
+ strata: 1,
353
+ runs: 2,
354
+ ...passCols(side(cur), side(prev)),
355
+ ...effortCols(contrastEffort(side(cur).samples, side(prev).samples)),
356
+ note: judgeOnly ? "the judge changed: a measurement change, not an effect of the thing under test" : real.length > 1 ? `confounded: ${real.join(", ")} changed together` : null
357
+ });
358
+ }
359
+ }
360
+ insertAll(db, "drift_m", driftRows);
361
+ const levels = (pick) => [...new Set(cells.map(pick))];
362
+ const single = [
363
+ ["harness", levels((c) => c.harness_kind)],
364
+ ["model", levels((c) => `${c.harness_kind}:${c.model ?? "-"}`)],
365
+ ["sandbox", levels((c) => c.sandbox)]
366
+ ];
367
+ if (cells.length && !cells.some((c) => c.role === "variant")) single.push(["environment", levels((c) => c.environment)]);
368
+ for (const [factor, values] of single) {
369
+ if (values.length !== 1) continue;
370
+ factorRows.push({
371
+ factor,
372
+ level: values[0],
373
+ baseline: null,
374
+ context: null,
375
+ tier: "not_measured",
376
+ note: "one level in every run, so its effect is not measured"
377
+ });
378
+ }
379
+ insertAll(db, "factor_effects_m", factorRows);
380
+ const flaky = /* @__PURE__ */ new Map();
381
+ for (const c of cells) {
382
+ const id = `${c.eval_key}\0${c.key}\0${c.comparable}`;
383
+ flaky.set(id, [...flaky.get(id) ?? [], c]);
384
+ }
385
+ const flakyRows = [];
386
+ for (const list of flaky.values()) {
387
+ const pass = list.reduce((n, c) => n + c.pass, 0);
388
+ const fail = list.reduce((n, c) => n + c.fail, 0);
389
+ if (!(pass > 0 && fail > 0)) continue;
390
+ const first = list[0];
391
+ const infra = list.reduce((n, c) => n + c.infra_error, 0);
392
+ const failing = list.flatMap((c) => (byCell.get(cellId(c.run_id, c.eval_key, c.key)) ?? []).filter((a) => a.outcome === "fail"));
393
+ const w = wilson(pass, pass + fail);
394
+ const cause = infra > 0 || failing.some((a) => a.error_kind === "infra") ? "infra" : failing.some((a) => nearThreshold.has(`${a.run_id}\0${a.key}`)) ? "judge" : "agent";
395
+ const starts = list.map((c) => c.started_at).sort();
396
+ flakyRows.push({
397
+ eval_key: first.eval_key,
398
+ cell_key: first.key,
399
+ fingerprint: first.comparable,
400
+ runs: list.length,
401
+ flaky_runs: list.filter((c) => c.pass > 0 && c.fail > 0).length,
402
+ first_seen: starts[0] ?? null,
403
+ last_seen: starts.at(-1) ?? null,
404
+ pass,
405
+ counted: pass + fail,
406
+ infra_error: infra,
407
+ rate: round3(pass / (pass + fail)),
408
+ lo: round3(w.low),
409
+ hi: round3(w.high),
410
+ verdict: pass + fail >= 6 ? "flaky" : "insufficient",
411
+ cause,
412
+ min_attempts: 6
413
+ });
414
+ }
415
+ flakyRows.sort((a, b) => String(a.first_seen).localeCompare(String(b.first_seen)));
416
+ insertAll(db, "flaky_m", flakyRows);
417
+ }
418
+ //#endregion
419
+ //#region src/history/db.ts
420
+ /** Bump when the tables or views change: a database from another version is rebuilt. */
421
+ const DB_VERSION = 2;
422
+ /** This build of reeval, like the header cache: another reeval re-derives every row. */
423
+ const BUILD = BUILD_ID;
424
+ const historyDbPath = (root) => join(root, ".reeval", "history.db");
425
+ /** `node:sqlite` is in Node from 22.13 (package `engines`); before 24 it warns on load. */
426
+ const SQLITE_NODE = "22.13.0";
427
+ function nodeAtLeast(version, min = SQLITE_NODE) {
428
+ const [a = 0, b = 0, c = 0] = version.replace(/^v/, "").split(".").map(Number);
429
+ const [x = 0, y = 0, z = 0] = min.split(".").map(Number);
430
+ return a !== x ? a > x : b !== y ? b > y : c >= z;
431
+ }
432
+ function loadSqlite() {
433
+ if (!nodeAtLeast(process.versions.node)) throw new UsageError(`reeval query needs Node ${SQLITE_NODE} or later for node:sqlite (this is Node ${process.versions.node}).`);
434
+ const emit = process.emitWarning;
435
+ process.emitWarning = ((warning, ...rest) => {
436
+ const text = typeof warning === "string" ? warning : warning.message;
437
+ if (/SQLite/.test(text)) return;
438
+ return emit.call(process, warning, ...rest);
439
+ });
440
+ try {
441
+ return ownRequire("node:sqlite");
442
+ } finally {
443
+ process.emitWarning = emit;
444
+ }
445
+ }
446
+ const TABLES = `
447
+ CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
448
+ -- The record file each run was read from, and its size and mtime when it was.
449
+ CREATE TABLE files (run_id TEXT PRIMARY KEY, path TEXT NOT NULL, size INTEGER NOT NULL, mtime_ms REAL NOT NULL);
450
+ CREATE TABLE runs (
451
+ run_id TEXT PRIMARY KEY, recorded INTEGER NOT NULL, status TEXT, started_at TEXT, ended_at TEXT,
452
+ duration_ms INTEGER, author TEXT, git_sha TEXT, branch TEXT, dirty INTEGER, runner TEXT,
453
+ ci_provider TEXT, ci_url TEXT, pr_number INTEGER, pr_base TEXT, es TEXT, judge_model TEXT,
454
+ judge_version TEXT, profile TEXT, evals INTEGER, attempts INTEGER, pass INTEGER, fail INTEGER,
455
+ not_gated INTEGER, infra_error INTEGER, skipped INTEGER
456
+ );
457
+ CREATE TABLE evals (
458
+ run_id TEXT NOT NULL, key TEXT NOT NULL, id TEXT, slug TEXT, file TEXT, groups TEXT, name TEXT,
459
+ tags TEXT, fingerprint TEXT, outcome TEXT, pass_rate REAL, sources TEXT,
460
+ PRIMARY KEY (run_id, key)
461
+ );
462
+ CREATE TABLE cells (
463
+ run_id TEXT NOT NULL, eval_key TEXT NOT NULL, key TEXT NOT NULL, role TEXT, variant TEXT,
464
+ harness TEXT, harness_kind TEXT, model TEXT, environment TEXT, environment_digest TEXT,
465
+ sandbox TEXT, gate TEXT, gate_pass_rate REAL, fingerprint TEXT, fp_sources TEXT,
466
+ fp_harness TEXT, fp_environment TEXT, fp_image TEXT, fp_agent_cli TEXT, fp_judge TEXT,
467
+ fp_context TEXT, runs INTEGER, pass INTEGER, fail INTEGER, infra_error INTEGER,
468
+ skipped INTEGER, pass_rate REAL, outcome TEXT, lift_vs TEXT, lift_points REAL, lift_lo REAL,
469
+ lift_hi REAL, within_noise INTEGER, min_lift REAL, comparable TEXT,
470
+ PRIMARY KEY (run_id, eval_key, key)
471
+ );
472
+ CREATE TABLE attempts (
473
+ run_id TEXT NOT NULL, eval_key TEXT NOT NULL, cell_key TEXT NOT NULL, key TEXT NOT NULL,
474
+ id TEXT, n INTEGER, seq INTEGER, started_at TEXT, outcome TEXT, error_kind TEXT,
475
+ error_message TEXT, duration_ms INTEGER, image_id TEXT, turns INTEGER, tools INTEGER,
476
+ file_edits INTEGER, setup_ms INTEGER, agent_ms INTEGER, checks_ms INTEGER, judge_ms INTEGER,
477
+ last_text TEXT, input_tokens INTEGER, output_tokens INTEGER, cache_read_tokens INTEGER,
478
+ cache_write_tokens INTEGER, reasoning_tokens INTEGER, served_model TEXT,
479
+ PRIMARY KEY (run_id, key)
480
+ );
481
+ CREATE TABLE judgements (
482
+ run_id TEXT NOT NULL, attempt_key TEXT NOT NULL, key TEXT NOT NULL, question TEXT,
483
+ verdict TEXT, score REAL, threshold REAL, model TEXT, resolved_model TEXT, input_path TEXT,
484
+ PRIMARY KEY (run_id, attempt_key, key)
485
+ );
486
+ CREATE TABLE environments (
487
+ run_id TEXT NOT NULL, name TEXT NOT NULL, digest TEXT NOT NULL, parent TEXT, overrides TEXT,
488
+ PRIMARY KEY (run_id, digest)
489
+ );
490
+ CREATE TABLE env_skills (
491
+ run_id TEXT NOT NULL, digest TEXT NOT NULL, name TEXT NOT NULL, source TEXT, commit_sha TEXT,
492
+ sha256 TEXT, placement TEXT
493
+ );
494
+ CREATE TABLE artifacts (
495
+ run_id TEXT NOT NULL, attempt_key TEXT NOT NULL, kind TEXT NOT NULL, path TEXT NOT NULL,
496
+ sha256 TEXT, bytes INTEGER
497
+ );
498
+ -- What the agent could see, by content hash; runtime = 1 for an agent CLI's own session state.
499
+ CREATE TABLE attempt_context (
500
+ run_id TEXT NOT NULL, attempt_key TEXT NOT NULL, path TEXT NOT NULL, sha256 TEXT, runtime INTEGER
501
+ );
502
+ -- Every tool call in each attempt's transcript: its line in transcript.jsonl (from 1) and target.
503
+ CREATE TABLE tool_calls (
504
+ run_id TEXT NOT NULL, attempt_key TEXT NOT NULL, line INTEGER NOT NULL, turn INTEGER,
505
+ tool TEXT, target TEXT, ok INTEGER, duration_ms INTEGER
506
+ );
507
+ ${DERIVED_TABLES}
508
+ CREATE INDEX cells_by_eval ON cells (eval_key, key);
509
+ CREATE INDEX tool_calls_by_attempt ON tool_calls (run_id, attempt_key);
510
+ CREATE INDEX context_by_attempt ON attempt_context (run_id, attempt_key);
511
+ CREATE INDEX attempts_by_cell ON attempts (run_id, eval_key, cell_key);
512
+ CREATE INDEX env_skills_by_digest ON env_skills (run_id, digest);
513
+ `;
514
+ /** The canned questions of docs/history-observability-review.md §2, as views to filter. */
515
+ const VIEWS = `
516
+ -- Variant against control, per harness, per run.
517
+ CREATE VIEW lift AS
518
+ SELECT c.run_id, r.started_at, r.branch, c.eval_key, c.harness, c.model, c.variant,
519
+ c.lift_vs AS control, c.pass AS variant_pass, c.pass + c.fail AS variant_counted,
520
+ k.pass AS control_pass, k.pass + k.fail AS control_counted, c.lift_points AS points,
521
+ c.lift_lo AS lo, c.lift_hi AS hi, c.within_noise, c.min_lift, c.outcome, c.fingerprint
522
+ FROM cells c JOIN runs r USING (run_id)
523
+ JOIN cells k ON k.run_id = c.run_id AND k.eval_key = c.eval_key AND k.key = c.lift_vs
524
+ WHERE c.role = 'variant';
525
+
526
+ -- Cells with mixed outcomes, pooled over runs whose fingerprints match once agent runtime state is
527
+ -- left out; 'flaky' needs FLAKY_MIN_ATTEMPTS counted attempts, else 'insufficient'.
528
+ CREATE VIEW flaky_cells AS SELECT * FROM flaky_m;
529
+
530
+ -- Each run where a cell's fingerprint differs from its previous run: which parts, which context
531
+ -- paths, and noise = 1 when only agent runtime state changed (not a real change).
532
+ CREATE VIEW drift AS SELECT * FROM drift_m;
533
+
534
+ -- Tool calls and agent time per cell, and a variant's against its control in the same run.
535
+ CREATE VIEW effort AS SELECT * FROM effort_m;
536
+
537
+ -- What a factor (environment, harness, model) did, by evidence tier: controlled (one run),
538
+ -- pooled (comparable runs, and across evals), observed (over time), not_measured (one level).
539
+ CREATE VIEW factor_effects AS SELECT * FROM factor_effects_m;
540
+
541
+ -- Whether each attempt read each skill its environment placed, and at which transcript line.
542
+ CREATE VIEW skill_reads AS
543
+ SELECT a.run_id, a.eval_key, a.cell_key, a.key AS attempt_key, a.id AS attempt_id, c.variant,
544
+ s.name AS skill, s.placement,
545
+ (SELECT MIN(t.line) FROM tool_calls t WHERE t.run_id = a.run_id AND t.attempt_key = a.key
546
+ AND t.target LIKE '%skills/' || s.name || '/%') AS read_line,
547
+ (SELECT COUNT(*) FROM tool_calls t WHERE t.run_id = a.run_id AND t.attempt_key = a.key
548
+ AND t.target LIKE '%skills/' || s.name || '/%') AS reads
549
+ FROM attempts a
550
+ JOIN cells c ON c.run_id = a.run_id AND c.eval_key = a.eval_key AND c.key = a.cell_key
551
+ JOIN env_skills s ON s.run_id = c.run_id AND s.digest = c.environment_digest;
552
+
553
+ -- Failed attempts with the cell's harness, model and each of its environment's skills.
554
+ CREATE VIEW failing_attempts AS
555
+ SELECT a.run_id, r.started_at, a.eval_key, a.key AS attempt_key, a.cell_key, c.harness,
556
+ c.model, c.environment, s.name AS skill, a.error_kind, a.error_message, a.duration_ms
557
+ FROM attempts a JOIN runs r USING (run_id)
558
+ JOIN cells c ON c.run_id = a.run_id AND c.eval_key = a.eval_key AND c.key = a.cell_key
559
+ LEFT JOIN env_skills s ON s.run_id = c.run_id AND s.digest = c.environment_digest
560
+ WHERE a.outcome = 'fail';
561
+
562
+ -- One eval per run: outcome and pass rate, with the branch and pull request.
563
+ CREATE VIEW eval_trend AS
564
+ SELECT e.eval_key, r.run_id, r.started_at, r.branch, r.pr_number, r.recorded, e.outcome,
565
+ e.pass_rate, e.fingerprint
566
+ FROM (SELECT key AS eval_key, * FROM evals) e JOIN runs r USING (run_id);
567
+
568
+ -- Each judge question's pass rate per cell per run, where it differs from that cell's last run.
569
+ CREATE VIEW verdict_flips AS
570
+ WITH rates AS (
571
+ SELECT j.run_id, a.eval_key, a.cell_key, j.key AS judgement_key, j.question,
572
+ AVG(j.verdict = 'pass') AS pass_rate, COUNT(*) AS n
573
+ FROM judgements j JOIN attempts a ON a.run_id = j.run_id AND a.key = j.attempt_key
574
+ GROUP BY j.run_id, a.eval_key, a.cell_key, j.key
575
+ ), ordered AS (
576
+ SELECT *, LAG(pass_rate) OVER w AS prev_pass_rate, LAG(run_id) OVER w AS prev_run_id
577
+ FROM rates WINDOW w AS (PARTITION BY eval_key, cell_key, judgement_key ORDER BY run_id)
578
+ )
579
+ SELECT run_id, prev_run_id, eval_key, cell_key, judgement_key, question, prev_pass_rate,
580
+ pass_rate, n
581
+ FROM ordered WHERE prev_run_id IS NOT NULL AND pass_rate IS NOT prev_pass_rate;
582
+ `;
583
+ const VIEW_NAMES = [
584
+ "lift",
585
+ "effort",
586
+ "factor_effects",
587
+ "skill_reads",
588
+ "flaky_cells",
589
+ "drift",
590
+ "failing_attempts",
591
+ "eval_trend",
592
+ "verdict_flips"
593
+ ];
594
+ const json = (value) => JSON.stringify(value);
595
+ /** `attempts.last_text` is the agent's final message, capped: most "why" questions start there. */
596
+ const LAST_TEXT_LIMIT = 2048;
597
+ /** A tool call's target: its path, pattern, command or URL, else its input, capped. */
598
+ function toolTarget(input) {
599
+ if (typeof input !== "object" || input === null) return String(input ?? "").slice(0, 300);
600
+ const i = input;
601
+ for (const key of [
602
+ "path",
603
+ "file_path",
604
+ "filePath",
605
+ "pattern",
606
+ "glob_pattern",
607
+ "command",
608
+ "url",
609
+ "query"
610
+ ]) if (typeof i[key] === "string") return i[key].slice(0, 300);
611
+ return JSON.stringify(input).slice(0, 300);
612
+ }
613
+ /** The tool calls in a transcript.jsonl, numbered by line from 1; lines that aren't JSON are skipped. */
614
+ function toolCalls(transcript) {
615
+ const calls = [];
616
+ transcript.split("\n").forEach((line, i) => {
617
+ if (!line.trim()) return;
618
+ let event;
619
+ try {
620
+ event = JSON.parse(line);
621
+ } catch {
622
+ return;
623
+ }
624
+ if (event?.type !== "tool") return;
625
+ calls.push({
626
+ line: i + 1,
627
+ turn: typeof event.turn === "number" ? event.turn : null,
628
+ tool: String(event.tool ?? ""),
629
+ target: toolTarget(event.input),
630
+ ok: event.ok !== false,
631
+ durationMs: typeof event.durationMs === "number" ? event.durationMs : null
632
+ });
633
+ });
634
+ return calls;
635
+ }
636
+ /**
637
+ * What a cell's results can be pooled on across runs: its fingerprint parts, with the context
638
+ * part replaced by the context files that aren't an agent CLI's own runtime state.
639
+ */
640
+ function comparableFingerprint(cell, attempts) {
641
+ const { context: _context, ...parts } = cell.fingerprintParts;
642
+ const files = /* @__PURE__ */ new Map();
643
+ for (const a of attempts) {
644
+ if (a.cell !== cell.key) continue;
645
+ for (const c of a.context) if (!isRuntimeState(c.path)) files.set(c.path, c.sha256);
646
+ }
647
+ return sha256(json({
648
+ parts,
649
+ context: [...files].sort(([a], [b]) => a < b ? -1 : 1)
650
+ }));
651
+ }
652
+ function insertRun(db, record, recorded, transcripts) {
653
+ const run = (sql, ...values) => db.prepare(sql).run(...values);
654
+ const { runner, summary, git } = record;
655
+ run(`INSERT INTO runs VALUES (${Array(26).fill("?").join(", ")})`, record.runId, recorded ? 1 : 0, record.status, record.startedAt, record.endedAt, record.durationMs, record.author, git?.sha ?? null, git?.branch ?? null, git ? git.dirty ? 1 : 0 : null, runner.kind, runner.kind === "ci" ? runner.provider : null, runner.kind === "ci" ? runner.runUrl : null, runner.kind === "ci" ? runner.pr?.number ?? null : null, runner.kind === "ci" ? runner.pr?.baseRef ?? null : null, record.stack.es, record.stack.judge.model, record.stack.judge.version, record.profile?.name ?? null, summary.evals, summary.attempts, summary.pass, summary.fail, summary.notGated, summary.infraError, summary.skipped);
656
+ for (const env of record.environments) {
657
+ run("INSERT OR IGNORE INTO environments VALUES (?, ?, ?, ?, ?)", record.runId, env.name, env.digest, env.parent, json(env.overrides));
658
+ for (const s of env.skills) run("INSERT INTO env_skills VALUES (?, ?, ?, ?, ?, ?, ?)", record.runId, env.digest, s.name, s.source, s.commit, s.sha256, s.placement);
659
+ }
660
+ for (const e of record.evals) {
661
+ run(`INSERT INTO evals VALUES (${Array(12).fill("?").join(", ")})`, record.runId, e.key, e.id, e.slug, e.file, json(e.group), e.name, json(e.tags), e.fingerprint, e.outcome, e.passRate, json(e.sources));
662
+ for (const c of e.cells) {
663
+ const p = c.fingerprintParts;
664
+ run(`INSERT INTO cells VALUES (${Array(35).fill("?").join(", ")})`, record.runId, e.key, c.key, c.role, c.variant ?? null, c.harness, c.harnessKind, c.model, c.environment, c.environmentDigest, c.sandbox, typeof c.gate === "string" ? c.gate : "passRate", typeof c.gate === "string" ? null : c.gate.passRate, c.fingerprint, p.sources, p.harness, p.environment, p.image, p.agentCli, p.judge, p.context, c.runs, c.pass, c.fail, c.infraError, c.skipped, c.passRate, c.outcome, c.lift?.vs ?? null, c.lift?.points ?? null, c.lift?.interval.lo ?? null, c.lift?.interval.hi ?? null, c.lift ? c.lift.withinNoise ? 1 : 0 : null, c.lift?.minLift ?? null, comparableFingerprint(c, e.attempts));
665
+ }
666
+ for (const a of e.attempts) {
667
+ run(`INSERT INTO attempts VALUES (${Array(27).fill("?").join(", ")})`, record.runId, e.key, a.cell, a.key, a.id, a.n, a.seq, a.startedAt, a.outcome, a.error?.kind ?? null, a.error?.message ?? null, a.durationMs, a.imageId, a.turns.length, a.activity.tools, a.activity.fileEdits, a.timings.setupMs, a.timings.agentMs, a.timings.checksMs, a.timings.judgeMs, a.turns.at(-1)?.text.slice(0, 2048) ?? null, a.usage?.input ?? null, a.usage?.output ?? null, a.usage?.cacheRead ?? null, a.usage?.cacheWrite ?? null, a.usage?.reasoning ?? null, a.turns.findLast((t) => t.model)?.model ?? null);
668
+ for (const c of a.context) run("INSERT INTO attempt_context VALUES (?, ?, ?, ?, ?)", record.runId, a.key, c.path, c.sha256, isRuntimeState(c.path) ? 1 : 0);
669
+ const transcript = transcripts.get(a.key);
670
+ for (const t of transcript ? toolCalls(transcript) : []) run("INSERT INTO tool_calls VALUES (?, ?, ?, ?, ?, ?, ?, ?)", record.runId, a.key, t.line, t.turn, t.tool, t.target, t.ok ? 1 : 0, t.durationMs);
671
+ for (const j of a.judgements) run("INSERT OR IGNORE INTO judgements VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", record.runId, a.key, j.key, j.question, j.verdict, j.score, j.threshold, j.model, j.resolvedModel ?? null, a.files.judgeInput?.path ?? null);
672
+ for (const [kind, ref] of Object.entries(a.files)) {
673
+ if (typeof ref?.path !== "string") continue;
674
+ run("INSERT INTO artifacts VALUES (?, ?, ?, ?, ?, ?)", record.runId, a.key, kind, ref.path, ref.sha256, ref.bytes);
675
+ }
676
+ a.checks.forEach((c, i) => {
677
+ if (!c.log) return;
678
+ run("INSERT INTO artifacts VALUES (?, ?, ?, ?, ?, ?)", record.runId, a.key, `check:${i + 1}`, c.log.path, c.log.sha256, c.log.bytes);
679
+ });
680
+ }
681
+ }
682
+ }
683
+ const DATA_TABLES = [
684
+ "runs",
685
+ "evals",
686
+ "cells",
687
+ "attempts",
688
+ "judgements",
689
+ "environments",
690
+ "env_skills",
691
+ "artifacts",
692
+ "attempt_context",
693
+ "tool_calls"
694
+ ];
695
+ function removeRun(db, runId) {
696
+ for (const table of DATA_TABLES) db.prepare(`DELETE FROM ${table} WHERE run_id = ?`).run(runId);
697
+ db.prepare("DELETE FROM files WHERE run_id = ?").run(runId);
698
+ }
699
+ function freshSchema(db) {
700
+ const objects = db.prepare("SELECT type, name FROM sqlite_schema WHERE type IN ('table', 'view')").all();
701
+ for (const o of objects) {
702
+ if (o.name.startsWith("sqlite_")) continue;
703
+ db.exec(`DROP ${o.type === "view" ? "VIEW" : "TABLE"} IF EXISTS "${o.name}"`);
704
+ }
705
+ db.exec(TABLES);
706
+ db.exec(VIEWS);
707
+ db.prepare("INSERT INTO meta VALUES (?, ?), (?, ?)").run("version", String(DB_VERSION), "build", String(BUILD));
708
+ }
709
+ function current(db) {
710
+ try {
711
+ const rows = db.prepare("SELECT key, value FROM meta").all();
712
+ const meta = Object.fromEntries(rows.map((r) => [r.key, r.value]));
713
+ return meta.version === String(DB_VERSION) && meta.build === String(BUILD);
714
+ } catch {
715
+ return false;
716
+ }
717
+ }
718
+ /** Each attempt's transcript.jsonl, by attempt key, where the run kept it. */
719
+ async function readTranscripts(dir, record) {
720
+ const out = /* @__PURE__ */ new Map();
721
+ for (const e of record.evals) for (const a of e.attempts) {
722
+ const ref = a.files.transcript;
723
+ if (!ref) continue;
724
+ const text = await readFile(join(dir, record.runId, ...ref.path.split("/")), "utf8").catch(() => null);
725
+ if (text !== null) out.set(a.key, text);
726
+ }
727
+ return out;
728
+ }
729
+ /**
730
+ * Brings the projection up to date with the record files: recorded runs in `historyDir` win over
731
+ * the same run's scratch copy in `.reeval/runs`. Only new, changed or removed files are touched.
732
+ */
733
+ async function sync(db, root, historyDir) {
734
+ const rebuilt = !current(db);
735
+ if (rebuilt) freshSchema(db);
736
+ const wanted = /* @__PURE__ */ new Map();
737
+ for (const [dir, recorded] of [[historyDir, true], [scratchDir(root), false]]) for (const name of await recordNames(dir) ?? []) {
738
+ const runId = name.slice(0, -5);
739
+ if (!RUN_ID.test(runId) || wanted.has(runId)) continue;
740
+ wanted.set(runId, {
741
+ path: join(dir, name),
742
+ recorded
743
+ });
744
+ }
745
+ const known = new Map(db.prepare("SELECT run_id, path, size, mtime_ms FROM files").all().map((r) => [r.run_id, r]));
746
+ let added = 0;
747
+ let removed = 0;
748
+ db.exec("BEGIN");
749
+ try {
750
+ for (const runId of known.keys()) if (!wanted.has(runId)) {
751
+ removeRun(db, runId);
752
+ removed++;
753
+ }
754
+ for (const [runId, { path, recorded }] of wanted) {
755
+ const info = await stat(path);
756
+ const rel = relativeFile(root, path);
757
+ const had = known.get(runId);
758
+ if (had && had.path === rel && had.size === info.size && had.mtime_ms === info.mtimeMs) continue;
759
+ let record;
760
+ try {
761
+ record = await readRecord(path);
762
+ } catch (error) {
763
+ warnUnreadable(path, error);
764
+ continue;
765
+ }
766
+ if (had) removeRun(db, runId);
767
+ insertRun(db, record, recorded, await readTranscripts(dirname(path), record));
768
+ db.prepare("INSERT INTO files VALUES (?, ?, ?, ?)").run(runId, rel, info.size, info.mtimeMs);
769
+ added++;
770
+ }
771
+ if (added || removed || rebuilt) deriveFacts(db);
772
+ db.exec("COMMIT");
773
+ } catch (error) {
774
+ db.exec("ROLLBACK");
775
+ throw error;
776
+ }
777
+ return {
778
+ runs: wanted.size,
779
+ added,
780
+ removed,
781
+ rebuilt
782
+ };
783
+ }
784
+ /** Opens `.reeval/history.db`, synced with the records; statements after this are read-only. */
785
+ async function openHistoryDb(root, historyDir, options = {}) {
786
+ const { DatabaseSync } = loadSqlite();
787
+ const path = historyDbPath(root);
788
+ await mkdir(dirname(path), { recursive: true });
789
+ const writer = new DatabaseSync(path);
790
+ let synced;
791
+ try {
792
+ writer.exec("PRAGMA busy_timeout = 5000");
793
+ if (options.rebuild) writer.exec("DROP TABLE IF EXISTS meta");
794
+ synced = await sync(writer, root, historyDir);
795
+ } finally {
796
+ writer.close();
797
+ }
798
+ const db = new DatabaseSync(path, { readOnly: true });
799
+ db.exec("PRAGMA busy_timeout = 5000");
800
+ return {
801
+ sync: synced,
802
+ query(sql, params = []) {
803
+ try {
804
+ const statement = db.prepare(sql);
805
+ const rows = statement.all(...params);
806
+ return {
807
+ columns: typeof statement.columns === "function" ? statement.columns().map((c) => c.name) : Object.keys(rows[0] ?? {}),
808
+ rows
809
+ };
810
+ } catch (error) {
811
+ const message = error.message;
812
+ throw new UsageError(/readonly|read-only/i.test(message) ? `reeval query is read-only: .reeval/history.db is a projection of the run records (${message})` : `reeval query: ${message}`);
813
+ }
814
+ },
815
+ schema() {
816
+ return db.prepare(`SELECT name, type FROM sqlite_schema WHERE type IN ('table', 'view') AND name NOT IN ('meta', 'files', ${DERIVED.map((d) => `'${d}'`).join(", ")}) AND name NOT LIKE 'sqlite_%' ORDER BY type, name`).all().map((o) => ({
817
+ ...o,
818
+ columns: db.prepare(`PRAGMA table_info("${o.name}")`).all().map((c) => c.name)
819
+ }));
820
+ },
821
+ close: () => db.close()
822
+ };
823
+ }
824
+ //#endregion
825
+ export { historyDbPath as a, toolCalls as c, comparableFingerprint as i, SQLITE_NODE as n, nodeAtLeast as o, VIEW_NAMES as r, openHistoryDb as s, LAST_TEXT_LIMIT as t };
826
+
827
+ //# sourceMappingURL=db-DxDzMhtB.js.map