reeval 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/README.md +99 -64
  2. package/THIRD_PARTY_NOTICES.md +288 -112
  3. package/assets/package.json +1 -1
  4. package/assets/pnpm-lock.yaml +30 -30
  5. package/dist/adapter-Bwy3QxAK.js +279 -0
  6. package/dist/adapter-Bwy3QxAK.js.map +1 -0
  7. package/dist/{agent-6FefIhX7.js → agent-D483ru7D.js} +6 -6
  8. package/dist/{agent-6FefIhX7.js.map → agent-D483ru7D.js.map} +1 -1
  9. package/dist/{agent-WSmTaesf.js → agent-DpE80WPz.js} +46 -15
  10. package/dist/agent-DpE80WPz.js.map +1 -0
  11. package/dist/{analysis-DlGBDjTj.js → analysis-Bxub9LMT.js} +4 -4
  12. package/dist/{analysis-DlGBDjTj.js.map → analysis-Bxub9LMT.js.map} +1 -1
  13. package/dist/assemble-D5KGrwnJ.js +1 -0
  14. package/dist/{assemble-BCZzTyR7.js → assemble-D9GWr-HS.js} +69 -41
  15. package/dist/assemble-D9GWr-HS.js.map +1 -0
  16. package/dist/{client-Bbo9SPlK.js → assert-EUIoRdIt.js} +10 -113
  17. package/dist/assert-EUIoRdIt.js.map +1 -0
  18. package/dist/bridge/index.mjs +260 -60
  19. package/dist/{capture-Bp74Z6Fr.js → capture-BQlGHiMC.js} +146 -32
  20. package/dist/capture-BQlGHiMC.js.map +1 -0
  21. package/dist/capture-CYGLpWxR.js +1 -0
  22. package/dist/{cli-CVrMh_qz.js → cli-ClpPT8kn.js} +229 -537
  23. package/dist/cli-ClpPT8kn.js.map +1 -0
  24. package/dist/{cli-spec-CKEYa8V2.js → cli-spec-qmEnQ25_.js} +50 -22
  25. package/dist/cli-spec-qmEnQ25_.js.map +1 -0
  26. package/dist/cli.js +4 -2
  27. package/dist/cli.js.map +1 -1
  28. package/dist/client-fcwgwOAD.js +106 -0
  29. package/dist/client-fcwgwOAD.js.map +1 -0
  30. package/dist/{command-DUT5l13I.js → command-Bj39y-Wm.js} +329 -87
  31. package/dist/command-Bj39y-Wm.js.map +1 -0
  32. package/dist/{command-BFP0hKEb.js → command-BlM0y8jN.js} +14 -14
  33. package/dist/{command-BFP0hKEb.js.map → command-BlM0y8jN.js.map} +1 -1
  34. package/dist/command-C-cVjlny.js +1 -0
  35. package/dist/{command-CzIFp1JQ.js → command-DbAe1Rzb.js} +19 -19
  36. package/dist/command-DbAe1Rzb.js.map +1 -0
  37. package/dist/{command-C5xFPKHM.js → command-TgbzPizb.js} +378 -74
  38. package/dist/command-TgbzPizb.js.map +1 -0
  39. package/dist/commands-CthqwkAn.js +1 -0
  40. package/dist/{commands-CLmPBrTS.js → commands-YUwlyTbr.js} +14 -11
  41. package/dist/commands-YUwlyTbr.js.map +1 -0
  42. package/dist/config-DrVSEVN0.js +774 -0
  43. package/dist/config-DrVSEVN0.js.map +1 -0
  44. package/dist/{context-DbSMEVKs.js → context-C1mxXh80.js} +11 -3
  45. package/dist/context-C1mxXh80.js.map +1 -0
  46. package/dist/contrasts-5zsj32gk.js +101 -0
  47. package/dist/contrasts-5zsj32gk.js.map +1 -0
  48. package/dist/db-CKg5NtiW.js +2 -0
  49. package/dist/{db-CfQFwJtq.js → db-DfhipHEF.js} +467 -38
  50. package/dist/db-DfhipHEF.js.map +1 -0
  51. package/dist/debug-D7bEv_UV.js +1 -0
  52. package/dist/{dist-CTzThfu_.js → dist-CuY4ehEk.js} +95 -15
  53. package/dist/dist-CuY4ehEk.js.map +1 -0
  54. package/dist/{docker-CRQGQbIt.js → docker-D1HZDS6q.js} +22 -21
  55. package/dist/docker-D1HZDS6q.js.map +1 -0
  56. package/dist/docker-DNmMPOBb.js +1 -0
  57. package/dist/{docker-DV2GNgle.js → docker-DOvjji9I.js} +3 -3
  58. package/dist/docker-DOvjji9I.js.map +1 -0
  59. package/dist/{doctor-DlKycx5H.js → doctor-D2MbNO8L.js} +262 -84
  60. package/dist/doctor-D2MbNO8L.js.map +1 -0
  61. package/dist/{egress-BhzNh7Yi.js → egress-2-qzPvGi.js} +146 -15
  62. package/dist/egress-2-qzPvGi.js.map +1 -0
  63. package/dist/environment-Cca6a4wB.js +1132 -0
  64. package/dist/environment-Cca6a4wB.js.map +1 -0
  65. package/dist/{errors-yDAEwmZo.js → errors-B6BSMs03.js} +2 -2
  66. package/dist/errors-B6BSMs03.js.map +1 -0
  67. package/dist/{errors-C9w_ud7g.js → errors-r025B0Zb.js} +4 -4
  68. package/dist/errors-r025B0Zb.js.map +1 -0
  69. package/dist/evaluate-DKIl54Hb.js +2348 -0
  70. package/dist/evaluate-DKIl54Hb.js.map +1 -0
  71. package/dist/evaluate-DMyaGQEj.d.ts +92 -0
  72. package/dist/files-BAt0i00X.js +1 -0
  73. package/dist/{files-DJUFg-4e.js → files-BXHZFWmx.js} +5 -5
  74. package/dist/{files-DJUFg-4e.js.map → files-BXHZFWmx.js.map} +1 -1
  75. package/dist/files-Bk1mC3AN.js +43 -0
  76. package/dist/files-Bk1mC3AN.js.map +1 -0
  77. package/dist/{files-CSzVADHn.js → files-BpUYBwGu.js} +11 -10
  78. package/dist/{files-CSzVADHn.js.map → files-BpUYBwGu.js.map} +1 -1
  79. package/dist/files-Bw8lWEjg.js +1 -0
  80. package/dist/files-D6J0iSdx.js +1 -0
  81. package/dist/{git-DfG-YJ1u.js → git-Crl3b87o.js} +2 -2
  82. package/dist/{git-DfG-YJ1u.js.map → git-Crl3b87o.js.map} +1 -1
  83. package/dist/harness-CEuntrla.js +323 -0
  84. package/dist/harness-CEuntrla.js.map +1 -0
  85. package/dist/harness.d.ts +16 -30
  86. package/dist/harness.js +1 -2
  87. package/dist/{header-CALhmafp.js → header-DHQv_ZrF.js} +2 -2
  88. package/dist/{header-CALhmafp.js.map → header-DHQv_ZrF.js.map} +1 -1
  89. package/dist/{images-BMgdpMAX.js → images-Bi3aGxb9.js} +114 -45
  90. package/dist/images-Bi3aGxb9.js.map +1 -0
  91. package/dist/index.d.ts +9 -37
  92. package/dist/index.js +6 -2138
  93. package/dist/{init-BIdLF0F9.js → init-Cl3f3NBT.js} +26 -8
  94. package/dist/init-Cl3f3NBT.js.map +1 -0
  95. package/dist/init-fhOXED8F.js +1 -0
  96. package/dist/{invocations-CLaL8AuW.js → invocations-BYDPe3fV.js} +4 -4
  97. package/dist/{invocations-CLaL8AuW.js.map → invocations-BYDPe3fV.js.map} +1 -1
  98. package/dist/{judge-CeYPN8P_.js → judge-DW_GDOmB.js} +11 -19
  99. package/dist/{judge-CeYPN8P_.js.map → judge-DW_GDOmB.js.map} +1 -1
  100. package/dist/judge.d.ts +2 -2
  101. package/dist/judge.js +1 -1
  102. package/dist/{live-S0qODb6o.js → live-jlfszAbb.js} +9 -20
  103. package/dist/live-jlfszAbb.js.map +1 -0
  104. package/dist/matchers-Ddmc2z7H.js +118 -0
  105. package/dist/matchers-Ddmc2z7H.js.map +1 -0
  106. package/dist/model-CXhrziBe.js +1 -0
  107. package/dist/{model-B1BZoZ9b.js → model-D0IcOQao.js} +22 -20
  108. package/dist/model-D0IcOQao.js.map +1 -0
  109. package/dist/{model-DEQo3FkZ.js → model-DaVCY3rb.js} +2 -2
  110. package/dist/{model-DEQo3FkZ.js.map → model-DaVCY3rb.js.map} +1 -1
  111. package/dist/{model-BOnlAPzs.js → model-DaX4bNLZ.js} +123 -22
  112. package/dist/model-DaX4bNLZ.js.map +1 -0
  113. package/dist/model-_bxNrk2h.js +1 -0
  114. package/dist/{node-Co7EXge0.js → node-B3ydHo6t.js} +10 -47
  115. package/dist/node-B3ydHo6t.js.map +1 -0
  116. package/dist/object-CLf2-tNq.js +9 -0
  117. package/dist/object-CLf2-tNq.js.map +1 -0
  118. package/dist/{opencode-providers-ByD85Q0l.js → opencode-providers-J9colP0-.js} +96 -21
  119. package/dist/opencode-providers-J9colP0-.js.map +1 -0
  120. package/dist/options-C0d0AzeR.js +1118 -0
  121. package/dist/options-C0d0AzeR.js.map +1 -0
  122. package/dist/{pairs-DDsjgm5F.js → pairs-CnInRu-G.js} +76 -2
  123. package/dist/{pairs-DDsjgm5F.js.map → pairs-CnInRu-G.js.map} +1 -1
  124. package/dist/{patch-bDV8YFW0.js → patch-B1bnfhN-.js} +6 -6
  125. package/dist/{patch-bDV8YFW0.js.map → patch-B1bnfhN-.js.map} +1 -1
  126. package/dist/{plan-DUydFc_1.js → plan-DgQxp9yy.js} +34 -13
  127. package/dist/plan-DgQxp9yy.js.map +1 -0
  128. package/dist/{preflight-BkoSxf4C.js → preflight-BRzD30L5.js} +8 -7
  129. package/dist/preflight-BRzD30L5.js.map +1 -0
  130. package/dist/preflight-DlQvbySb.js +1 -0
  131. package/dist/{prepare-CUAWHebF.js → prepare-B_y-AFMv.js} +38 -30
  132. package/dist/prepare-B_y-AFMv.js.map +1 -0
  133. package/dist/prepare-DCKTLHN3.js +2 -0
  134. package/dist/{provenance-D7HK3vzt.js → provenance-DQ0AXn6c.js} +5 -5
  135. package/dist/{provenance-D7HK3vzt.js.map → provenance-DQ0AXn6c.js.map} +1 -1
  136. package/dist/provider-DehR4CMN.js +1 -0
  137. package/dist/providers-B9yTdIqF.js +1 -0
  138. package/dist/providers-DoTq1jHW.js +165 -0
  139. package/dist/providers-DoTq1jHW.js.map +1 -0
  140. package/dist/query-BO-ux7RB.js +139 -0
  141. package/dist/query-BO-ux7RB.js.map +1 -0
  142. package/dist/query-CRQfsw0e.js +1 -0
  143. package/dist/questions-C2dym2ce.js +524 -0
  144. package/dist/questions-C2dym2ce.js.map +1 -0
  145. package/dist/{record-BRP9vZrj.js → record-BKmMvZ6O.js} +9 -1
  146. package/dist/{record-BRP9vZrj.js.map → record-BKmMvZ6O.js.map} +1 -1
  147. package/dist/remote-CPPW86Q9.js +378 -0
  148. package/dist/remote-CPPW86Q9.js.map +1 -0
  149. package/dist/{results-Di7m8dQ3.js → results-BE2f-1XS.js} +6 -5
  150. package/dist/results-BE2f-1XS.js.map +1 -0
  151. package/dist/{resume-SUz1GskV.js → resume-Ca1c8vix.js} +10 -10
  152. package/dist/{resume-SUz1GskV.js.map → resume-Ca1c8vix.js.map} +1 -1
  153. package/dist/{root-CvdRlRZX.js → root-DICDHCtp.js} +4 -4
  154. package/dist/{root-CvdRlRZX.js.map → root-DICDHCtp.js.map} +1 -1
  155. package/dist/{runtime-DdsfnRz_.js → runtime-PX_oXiCS.js} +23 -71
  156. package/dist/runtime-PX_oXiCS.js.map +1 -0
  157. package/dist/sandbox-DLrsvu7U.js +70 -0
  158. package/dist/sandbox-DLrsvu7U.js.map +1 -0
  159. package/dist/sandbox.d.ts +19 -6
  160. package/dist/sandbox.js +1 -2
  161. package/dist/server-Dp8eOeK5.js +1 -0
  162. package/dist/{server-BNnXtmS8.js → server-TUY5xA_A.js} +11 -11
  163. package/dist/server-TUY5xA_A.js.map +1 -0
  164. package/dist/shared-CA61BuPJ.js +188 -0
  165. package/dist/shared-CA61BuPJ.js.map +1 -0
  166. package/dist/{source-Dof0NQBm.js → source-DJGS0a0u.js} +2 -2
  167. package/dist/{source-Dof0NQBm.js.map → source-DJGS0a0u.js.map} +1 -1
  168. package/dist/source-DdbLGR1M.js +1 -0
  169. package/dist/src-BxfCKA3_.js +2 -0
  170. package/dist/{src-C1E9HDWO.js → src-C7_1SqDP.js} +3 -3
  171. package/dist/{src-C1E9HDWO.js.map → src-C7_1SqDP.js.map} +1 -1
  172. package/dist/{src-DDNXsD9H.js → src-CF17Ug08.js} +5 -4
  173. package/dist/src-CF17Ug08.js.map +1 -0
  174. package/dist/{src-C_4Umgh2.js → src-CFp86GtV.js} +52 -51
  175. package/dist/src-CFp86GtV.js.map +1 -0
  176. package/dist/src-CLoOMVWI.js +3 -0
  177. package/dist/src-CcrQbpG8.js +2 -0
  178. package/dist/src-DCFtJRp0.js +2 -0
  179. package/dist/src-DIL0jST0.js +368 -0
  180. package/dist/src-DIL0jST0.js.map +1 -0
  181. package/dist/src-DSYYPEOj.js +2 -0
  182. package/dist/src-DYO3DdnQ.js +1 -0
  183. package/dist/src-DvCg0NBs.js +324 -0
  184. package/dist/src-DvCg0NBs.js.map +1 -0
  185. package/dist/{src-COgE7991.js → src-DvK0EIZx.js} +5 -5
  186. package/dist/{src-COgE7991.js.map → src-DvK0EIZx.js.map} +1 -1
  187. package/dist/{src-DVy58-zZ.js → src-FGXhkEkH.js} +5 -5
  188. package/dist/{src-DVy58-zZ.js.map → src-FGXhkEkH.js.map} +1 -1
  189. package/dist/src-WD-zgQYZ.js +2 -0
  190. package/dist/src-_oI9AePb.js +2 -0
  191. package/dist/{src-8rRXy4u9.js → src-ftu5wPSt.js} +1652 -182
  192. package/dist/src-ftu5wPSt.js.map +1 -0
  193. package/dist/src-qsKCqv7a.js +2 -0
  194. package/dist/{src-D95O-Smv.js → src-u8_ixn_F.js} +15 -11
  195. package/dist/src-u8_ixn_F.js.map +1 -0
  196. package/dist/state-CeR6oz0k.js +66 -0
  197. package/dist/state-CeR6oz0k.js.map +1 -0
  198. package/dist/{state-CiXyLqg0.js → state-DfpHox6b.js} +36 -24
  199. package/dist/state-DfpHox6b.js.map +1 -0
  200. package/dist/state-DpAxYyED.js +1 -0
  201. package/dist/studio-app/assets/index-CBkhNAv8.js +58 -0
  202. package/dist/studio-app/assets/index-CwQN3f_h.css +2 -0
  203. package/dist/studio-app/index.html +2 -2
  204. package/dist/suite-Cw_7G2RM.js +429 -0
  205. package/dist/suite-Cw_7G2RM.js.map +1 -0
  206. package/dist/suite-DamAEnNF.js +1 -0
  207. package/dist/summary-BUKfU-9l.js +611 -0
  208. package/dist/summary-BUKfU-9l.js.map +1 -0
  209. package/dist/{summary-2OAWF2N7.js → summary-Baa4rTHS.js} +7 -7
  210. package/dist/{summary-2OAWF2N7.js.map → summary-Baa4rTHS.js.map} +1 -1
  211. package/dist/testing.d.ts +3 -2
  212. package/dist/testing.js +3 -2
  213. package/dist/{tool-target-BU1JnSE9.js → tool-target-D2mcYlSQ.js} +14 -7
  214. package/dist/tool-target-D2mcYlSQ.js.map +1 -0
  215. package/dist/{tools-B10Uj7OI.js → tools-DLHgetpx.js} +6 -6
  216. package/dist/tools-DLHgetpx.js.map +1 -0
  217. package/dist/transcript-BNNANU8A.js +64 -0
  218. package/dist/transcript-BNNANU8A.js.map +1 -0
  219. package/dist/{types-D8RLfkFP.d.ts → types-BYdlwMwo.d.ts} +398 -46
  220. package/dist/{usage-DDF_6tkV.js → usage-CleNKkly.js} +4 -2
  221. package/dist/{usage-DDF_6tkV.js.map → usage-CleNKkly.js.map} +1 -1
  222. package/dist/view/index.d.ts +290 -8
  223. package/dist/view/index.js +7 -7
  224. package/dist/{views-Dqa5ojfZ.js → views-B8w2J-gD.js} +19 -24
  225. package/dist/views-B8w2J-gD.js.map +1 -0
  226. package/dist/views-DblnI3kA.js +2 -0
  227. package/dist/{worker-entry-BAikHl9F.js → worker-entry-DNnveLqx.js} +16 -229
  228. package/dist/worker-entry-DNnveLqx.js.map +1 -0
  229. package/dist/{workspace-C-TrNIUc.js → workspace-C4RA3srV.js} +7 -7
  230. package/dist/{workspace-C-TrNIUc.js.map → workspace-C4RA3srV.js.map} +1 -1
  231. package/dist/workspace-C_X0tdv6.js +1 -0
  232. package/package.json +12 -12
  233. package/dist/agent-WSmTaesf.js.map +0 -1
  234. package/dist/assemble-BCZzTyR7.js.map +0 -1
  235. package/dist/assemble-C5qq5yT2.js +0 -2
  236. package/dist/capture-Bp74Z6Fr.js.map +0 -1
  237. package/dist/capture-DS-cPyuE.js +0 -2
  238. package/dist/cli-CVrMh_qz.js.map +0 -1
  239. package/dist/cli-spec-CKEYa8V2.js.map +0 -1
  240. package/dist/client-Bbo9SPlK.js.map +0 -1
  241. package/dist/command-BrmXUC-W.js +0 -2
  242. package/dist/command-C5xFPKHM.js.map +0 -1
  243. package/dist/command-CzIFp1JQ.js.map +0 -1
  244. package/dist/command-DUT5l13I.js.map +0 -1
  245. package/dist/commands-CLmPBrTS.js.map +0 -1
  246. package/dist/commands-fI3G2Wnh.js +0 -2
  247. package/dist/config-CGvBeewH.js +0 -457
  248. package/dist/config-CGvBeewH.js.map +0 -1
  249. package/dist/context-DbSMEVKs.js.map +0 -1
  250. package/dist/db-CSSFtGcN.js +0 -3
  251. package/dist/db-CfQFwJtq.js.map +0 -1
  252. package/dist/debug-SEmW4w8f.js +0 -2
  253. package/dist/dist-CTzThfu_.js.map +0 -1
  254. package/dist/docker-CRQGQbIt.js.map +0 -1
  255. package/dist/docker-DV2GNgle.js.map +0 -1
  256. package/dist/docker-Df_YOxxB.js +0 -2
  257. package/dist/doctor-DlKycx5H.js.map +0 -1
  258. package/dist/duration-JsJrIWXq.js +0 -26
  259. package/dist/duration-JsJrIWXq.js.map +0 -1
  260. package/dist/egress-BhzNh7Yi.js.map +0 -1
  261. package/dist/env-BQqj93gu.js +0 -21
  262. package/dist/env-BQqj93gu.js.map +0 -1
  263. package/dist/environment-1jE-HhO-.js +0 -657
  264. package/dist/environment-1jE-HhO-.js.map +0 -1
  265. package/dist/errors-C9w_ud7g.js.map +0 -1
  266. package/dist/errors-yDAEwmZo.js.map +0 -1
  267. package/dist/fake-instructions-CUI33I_F.js +0 -32
  268. package/dist/fake-instructions-CUI33I_F.js.map +0 -1
  269. package/dist/family-D2b7WD10.js +0 -67
  270. package/dist/family-D2b7WD10.js.map +0 -1
  271. package/dist/files-AiEKuEZe.js +0 -2
  272. package/dist/files-BM78_nnF.js +0 -2
  273. package/dist/files-BMCvfB4X.js +0 -2
  274. package/dist/harness-CeCp2GSW.js +0 -171
  275. package/dist/harness-CeCp2GSW.js.map +0 -1
  276. package/dist/images-BMgdpMAX.js.map +0 -1
  277. package/dist/index.js.map +0 -1
  278. package/dist/init-B6vegaP_.js +0 -2
  279. package/dist/init-BIdLF0F9.js.map +0 -1
  280. package/dist/jiti-BcJHCtrb.js +0 -46
  281. package/dist/jiti-BcJHCtrb.js.map +0 -1
  282. package/dist/live-S0qODb6o.js.map +0 -1
  283. package/dist/model-B1BZoZ9b.js.map +0 -1
  284. package/dist/model-BOnlAPzs.js.map +0 -1
  285. package/dist/model-DWeg5J7z.js +0 -2
  286. package/dist/model-FXko2kl7.js +0 -2
  287. package/dist/node-Co7EXge0.js.map +0 -1
  288. package/dist/opencode-providers-ByD85Q0l.js.map +0 -1
  289. package/dist/options-CKqyPGxX.js +0 -739
  290. package/dist/options-CKqyPGxX.js.map +0 -1
  291. package/dist/plan-DUydFc_1.js.map +0 -1
  292. package/dist/preflight-BkoSxf4C.js.map +0 -1
  293. package/dist/preflight-D5LW7Oqi.js +0 -2
  294. package/dist/prepare-CUAWHebF.js.map +0 -1
  295. package/dist/prepare-oji2oO8W.js +0 -3
  296. package/dist/process-CSVO_jsc.js +0 -119
  297. package/dist/process-CSVO_jsc.js.map +0 -1
  298. package/dist/provider-BDbNOQhQ.js +0 -2
  299. package/dist/providers-B1DzMvmH.js +0 -107
  300. package/dist/providers-B1DzMvmH.js.map +0 -1
  301. package/dist/providers-CpEfNU1F.js +0 -2
  302. package/dist/query-BjBMLa-C.js +0 -91
  303. package/dist/query-BjBMLa-C.js.map +0 -1
  304. package/dist/query-oPO_XrTy.js +0 -2
  305. package/dist/questions-B8by4Bbt.js +0 -234
  306. package/dist/questions-B8by4Bbt.js.map +0 -1
  307. package/dist/results-Di7m8dQ3.js.map +0 -1
  308. package/dist/runtime-DdsfnRz_.js.map +0 -1
  309. package/dist/sandbox-D5v-of6d.js +0 -139
  310. package/dist/sandbox-D5v-of6d.js.map +0 -1
  311. package/dist/server-BNnXtmS8.js.map +0 -1
  312. package/dist/server-BNwoKzlm.js +0 -2
  313. package/dist/source-Bk_0ZaEY.js +0 -2
  314. package/dist/src-8rRXy4u9.js.map +0 -1
  315. package/dist/src-BGWuWp6I.js +0 -3
  316. package/dist/src-BRt0O02i.js +0 -3
  317. package/dist/src-Bx2x1ekD.js +0 -3
  318. package/dist/src-C_4Umgh2.js.map +0 -1
  319. package/dist/src-D95O-Smv.js.map +0 -1
  320. package/dist/src-DDNXsD9H.js.map +0 -1
  321. package/dist/src-DLa-ESW0.js +0 -4
  322. package/dist/src-cN0aB-WR.js +0 -3
  323. package/dist/src-cdIVjC5b.js +0 -2
  324. package/dist/src-sIsDHWYq.js +0 -3
  325. package/dist/state-Bo5zg4da.js +0 -2
  326. package/dist/state-CiXyLqg0.js.map +0 -1
  327. package/dist/studio-app/assets/index-B9MCTfuu.css +0 -2
  328. package/dist/studio-app/assets/index-CROH2NB3.js +0 -58
  329. package/dist/summary-BTksrIxn.js +0 -254
  330. package/dist/summary-BTksrIxn.js.map +0 -1
  331. package/dist/tool-target-BU1JnSE9.js.map +0 -1
  332. package/dist/tools-B10Uj7OI.js.map +0 -1
  333. package/dist/views-Cs2Oqayd.js +0 -3
  334. package/dist/views-Dqa5ojfZ.js.map +0 -1
  335. package/dist/worker-entry-BAikHl9F.js.map +0 -1
  336. package/dist/workspace-Do_SiWBs.js +0 -2
@@ -0,0 +1,2348 @@
1
+ import { t as isObjectLike } from "./object-CLf2-tNq.js";
2
+ import { n as UsageError, r as isTimeout, t as TimeoutError } from "./errors-B6BSMs03.js";
3
+ import { n as dataProblem, p as parseDuration } from "./shared-CA61BuPJ.js";
4
+ import { D as decideState, E as badJudgeArtifacts, O as filePartsOf, T as artifactRefsOf, _ as unbrokeredError, a as evalPlan, d as refuseIllegalCompare, f as sandboxId, g as unbrokered, h as secretVarsIn, i as checkOptions, k as resolveJudgeArtifacts, l as matchesTags, m as sandboxSetBy, n as cascade, o as expand, p as sandboxProvider, s as harnessId, t as brokeredIn, v as mergeJudge, w as judgeProblem, y as resolveStateOptions } from "./options-C0d0AzeR.js";
5
+ import { A as workspaceLabel, C as parseGitRepo, D as stepCommand, E as sandboxPaths, S as mcpHosts, a as callerDir, b as isSecret, d as environmentFrom, f as environmentPaths, h as globFilter, j as workspaceOf, m as filesOf, n as DEFAULT_SKILL_EXCLUDE, v as isHostFile, x as isSecretTemplate } from "./environment-Cca6a4wB.js";
6
+ import { c as relativeFile, i as evalSlug, l as sha256, n as attemptKey, r as evalId, s as recordFileName, t as attemptId } from "./ids-Bp6ut7ZD.js";
7
+ import { l as runJudge, s as judgeModelName, u as isApiError } from "./judge-DW_GDOmB.js";
8
+ import { r as badCharacterAt, v as inCI } from "./config-DrVSEVN0.js";
9
+ import { o as redactSecrets, s as registerSecret } from "./redact-DJ9JCShu.js";
10
+ import { t as InfraError } from "./types-C5vI3g1C.js";
11
+ import { _ as layoutOf, a as unenforceable, h as expectedBlockedFor, n as allowedHosts, o as createFakeSandbox, p as brokeringFor, r as egressLabel, v as refusedMcp } from "./egress-2-qzPvGi.js";
12
+ import { S as placeholder, nt as shellQuote, tt as run } from "./src-ftu5wPSt.js";
13
+ import { i as registerTest, n as getRunContext, o as SUBJECT, r as registerGroup, t as collectingFile } from "./runtime-PX_oXiCS.js";
14
+ import { a as withDebug, i as debugTrace, n as attemptDebug, r as debugLog, t as AttemptDebug } from "./attempt-CGPV3BZY.js";
15
+ import { t as attemptDebugDir } from "./files-Ca0d6Uq3.js";
16
+ import { n as clip, r as formatDuration, t as capMiddle } from "./text-gEcKjAyp.js";
17
+ import { a as writeCaptureSidecars, c as sandboxFor, d as workspaceFiles, l as diffSinceSnapshot, o as asCheck, r as serveCommand, s as createChecks, t as capturePage, u as snapshot } from "./capture-BQlGHiMC.js";
18
+ import { t as contextTaker } from "./context-C1mxXh80.js";
19
+ import { t as PrepareError } from "./provider-CmAYTQQs.js";
20
+ import { i as preparedWorkspace } from "./workspace-C4RA3srV.js";
21
+ import { n as judgeDiff } from "./patch-B1bnfhN-.js";
22
+ import { t as transcriptForJudge } from "./transcript-BNNANU8A.js";
23
+ import { r as withLease } from "./client-fcwgwOAD.js";
24
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
25
+ import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
26
+ import { parse } from "yaml";
27
+ import { z } from "zod";
28
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
29
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
30
+ //#region src/attempt/assertions.ts
31
+ const MESSAGE_LIMIT = 1024;
32
+ const LABEL_LIMIT = 60;
33
+ const clipped = (s, limit) => s.length > limit ? `${s.slice(0, limit)}…` : s;
34
+ const text = (error) => error instanceof Error ? error.message : String(error);
35
+ const shown = (value) => {
36
+ if (typeof value === "string") return value;
37
+ if (value instanceof RegExp) return String(value);
38
+ try {
39
+ return JSON.stringify(value) ?? String(value);
40
+ } catch {
41
+ return String(value);
42
+ }
43
+ };
44
+ const shownSubject = (subject) => typeof subject === "string" && subject ? subject.replace(/\s+/g, " ").trim() : void 0;
45
+ /**
46
+ * The records of an attempt's matcher calls, in call order. Without a label, one is
47
+ * `<matcher> <first argument>` (or what the subject names); repeats get ` #2`, ` #3`.
48
+ */
49
+ function assertionRecords(raw) {
50
+ const seen = /* @__PURE__ */ new Map();
51
+ return raw.map((r) => {
52
+ const named = r.args.length ? shown(r.args[0]) : shownSubject(r.subject);
53
+ const base = r.label ?? clipped(named === void 0 ? r.matcher : `${r.matcher} ${named}`, LABEL_LIMIT);
54
+ const n = (seen.get(base) ?? 0) + 1;
55
+ seen.set(base, n);
56
+ return {
57
+ label: n > 1 ? `${base} #${n}` : base,
58
+ labelled: r.label !== void 0,
59
+ matcher: r.matcher,
60
+ status: r.status,
61
+ soft: r.soft,
62
+ ...r.status === "pass" ? {} : { message: clipped(text(r.error), MESSAGE_LIMIT) },
63
+ ...r.durationMs === void 0 ? {} : { durationMs: r.durationMs }
64
+ };
65
+ });
66
+ }
67
+ /** What fails the attempt at its end for its `expect.soft` calls: the first that didn't pass. */
68
+ function softFailure(raw) {
69
+ const failed = raw.find((r) => r.soft && r.status !== "pass");
70
+ if (!failed) return void 0;
71
+ return failed.error instanceof Error ? failed.error : new Error(text(failed.error));
72
+ }
73
+ //#endregion
74
+ //#region src/attempt/debug.ts
75
+ /** Which config and profile the attempt runs under, and where each resolved option came from. */
76
+ function tracePlan(debug, spec, ctx) {
77
+ const config = ctx.configFile ?? "none (no config file found)";
78
+ const from = (key, fallback = ctx.configFile ? `${config} or its default` : "default") => `(${spec.sources[key] ?? fallback})`;
79
+ const profile = ctx.profile ? `profile ${ctx.profile.name} (${ctx.profile.reason})` : "no profile";
80
+ debug.log("plan", `config ${config} · ${profile} · run ${ctx.runId} · attempt ${spec.cell.key}#${spec.n}`);
81
+ debug.log("plan", `harness ${harnessId(spec.harness)} ${from("harness")}`);
82
+ debug.log("plan", `sandbox ${sandboxId(spec.sandbox)} ${from("sandbox")}`);
83
+ const env = spec.environment;
84
+ const role = spec.cell.role === "variant" ? ` · variant ${spec.cell.variant}` : "";
85
+ const envFrom = spec.cell.role === "variant" ? from("compare") : from("environment", "none set");
86
+ debug.log("plan", `environment ${env.name}${role} ${envFrom}`);
87
+ debug.log("plan", `egress ${egressLabel(spec.egress) || "allow-all"} ${from("egress")}`);
88
+ const gate = typeof spec.gate === "string" ? spec.gate : `passRate ${spec.gate.passRate}`;
89
+ debug.log("plan", `run ${spec.n} · runs ${ctx.runs !== void 0 ? "(--runs)" : from("runs")} · gate ${gate} · timeout ${formatDuration(spec.timeoutMs)} ${from("timeout", "config.timeout or 15m")}`);
90
+ const judge = spec.judge;
91
+ const model = typeof judge.model === "string" ? judge.model : judge.model && "kind" in judge.model ? "fake judge" : judge.model ? "a model object" : "none";
92
+ debug.log("plan", `judge ${model}${judge.threshold !== void 0 ? ` · threshold ${judge.threshold}` : ""} ${from("judge")}`);
93
+ for (const note of spec.notes) debug.log("plan", `profile: ${note}`);
94
+ debug.trace("plan", "config", {
95
+ config: ctx.config,
96
+ profile: ctx.profile
97
+ }, "resolved config");
98
+ debug.trace("plan", "attempt", {
99
+ cell: spec.cell,
100
+ n: spec.n,
101
+ harness: spec.harness,
102
+ sandbox: spec.sandbox,
103
+ egress: spec.egress,
104
+ gate: spec.gate,
105
+ timeoutMs: spec.timeoutMs,
106
+ judge: spec.judge,
107
+ sources: spec.sources,
108
+ env: Object.keys(spec.env).sort()
109
+ }, "resolved attempt");
110
+ }
111
+ /** `POST api2.cursor.sh/auth/exchange`, `GET host/v1/*` or `POST host (path ~ /re/)`. */
112
+ function endpointLabel({ method, host, path }) {
113
+ const where = !path ? host : "exact" in path ? `${host}${path.exact}` : "startsWith" in path ? `${host}${path.startsWith}*` : `${host} (path ~ /${path.regex}/)`;
114
+ return `${method?.join("|") ?? "*"} ${where}`;
115
+ }
116
+ /** The resolved environment and what the gateway brokers, by name. */
117
+ function traceEnvironment(debug, spec, env) {
118
+ const r = env.record;
119
+ debug.log("env", `environment ${r.name} (digest ${r.digest.replace(/^sha256:/, "").slice(0, 12)})${r.parent ? ` · extends ${r.parent}` : ""}${r.overrides.length ? ` · overrides ${r.overrides.join(", ")}` : ""}`);
120
+ const unset = r.unset.length ? ` · unset (optional) ${r.unset.join(", ")}` : "";
121
+ debug.log("env", `vars ${r.vars.join(", ") || "none"} · secrets ${r.secrets.join(", ") || "none"}${unset}`);
122
+ for (const s of env.substitutions) debug.log("broker", `${s.name} is a placeholder in the sandbox · swapped in on HTTPS to ${s.hosts.join(", ")} in the ${s.in.join(", ")} · for ${s.phase === "checks" ? "checks and setup only" : "the agent and checks"}`);
123
+ const brokering = brokeringFor(spec.harness);
124
+ if (brokering) {
125
+ const endpoints = brokering.endpoints.map(endpointLabel).join(", ");
126
+ debug.log("broker", `${brokering.credential} stays on the host · the gateway swaps it in on ${endpoints}`);
127
+ }
128
+ debug.trace("env", "environment", {
129
+ record: r,
130
+ vars: Object.keys(env.vars).sort(),
131
+ checkVars: Object.keys(env.checkVars).sort(),
132
+ files: env.files.map((f) => f.path),
133
+ setup: env.setup,
134
+ skills: env.skills.map((s) => s.name),
135
+ workspaceSkills: env.workspaceSkills.map((s) => s.name),
136
+ mcp: Object.fromEntries(Object.entries(env.mcp).map(([name, m]) => [name, m.transport === "stdio" ? {
137
+ transport: m.transport,
138
+ command: m.command,
139
+ args: m.args,
140
+ env: Object.keys(m.env)
141
+ } : {
142
+ transport: m.transport,
143
+ url: m.url,
144
+ headers: Object.keys(m.headers)
145
+ }])),
146
+ brokered: env.substitutions.map(({ name, hosts, in: where, phase }) => ({
147
+ name,
148
+ hosts,
149
+ in: where,
150
+ phase
151
+ }))
152
+ }, "resolved environment");
153
+ }
154
+ //#endregion
155
+ //#region src/environment/resolve.ts
156
+ const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
157
+ const DEFAULT_SETUP_TIMEOUT = "5m";
158
+ function broker(secrets, name, value, hosts, where = ["header"], phase = "agent") {
159
+ const at = badCharacterAt(value);
160
+ if (at && where.includes("header")) throw new InfraError(`${name} has whitespace or a line break in it (character ${at}), so it can't go in a request header. A key copied from a wrapped terminal line picks these up; set it again as one line.`);
161
+ const sub = {
162
+ name,
163
+ placeholder: placeholder(),
164
+ value,
165
+ hosts: [...hosts],
166
+ in: [...where],
167
+ phase
168
+ };
169
+ secrets.brokered.push(sub);
170
+ return sub.placeholder;
171
+ }
172
+ /** The host value, or its placeholder when it's brokered to `secret.hosts` (else `hosts`). */
173
+ function hostValue(name, secret, where, secrets, hosts = secret.hosts) {
174
+ const from = secret.from ?? name;
175
+ const value = process.env[from];
176
+ if (!value) {
177
+ if (secret.optional) {
178
+ secrets.unset.push(name);
179
+ return;
180
+ }
181
+ const source = from === name ? name : `${name} (from ${from})`;
182
+ throw new InfraError(`Secret ${source} for ${where} isn't set. Set it in the environment or .env.local.`);
183
+ }
184
+ registerSecret(value);
185
+ if (secret.to === "checks") secrets.checks.push(name);
186
+ else secrets.names.push(name);
187
+ if (!hosts) return value;
188
+ return broker(secrets, name, value, hosts, secret.in, secret.to === "checks" ? "checks" : "agent");
189
+ }
190
+ /** Each host variable in the template, or with `hosts` one placeholder per variable. */
191
+ function template(value, where, secrets, hosts) {
192
+ const placeholders = /* @__PURE__ */ new Map();
193
+ return value.template.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => {
194
+ const v = process.env[name];
195
+ if (!v) throw new InfraError(`secret.template for ${where} needs ${name}, which isn't set. Set it in the environment or .env.local.`);
196
+ registerSecret(v);
197
+ if (!hosts) return v;
198
+ const p = placeholders.get(name) ?? broker(secrets, name, v, hosts);
199
+ placeholders.set(name, p);
200
+ return p;
201
+ });
202
+ }
203
+ /** A `SKILL.md` split into its YAML frontmatter's `name` and `description` and its body. */
204
+ function parseSkillMd(text, where) {
205
+ const match = /^---\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)([\s\S]*)$/.exec(text);
206
+ if (!match) return { body: text };
207
+ let front;
208
+ try {
209
+ front = parse(match[1] ?? "");
210
+ } catch (error) {
211
+ const reason = error.message.split("\n")[0];
212
+ throw new InfraError(`${where}: the SKILL.md frontmatter isn't valid YAML (${reason}). Quote values that contain ": ".`);
213
+ }
214
+ if (front != null && (typeof front !== "object" || Array.isArray(front))) throw new InfraError(`${where}: the SKILL.md frontmatter must be a YAML mapping, e.g. name: my-skill and description: … on their own lines`);
215
+ const field = (key) => {
216
+ const value = front?.[key];
217
+ if (value == null) return void 0;
218
+ if (typeof value !== "string") throw new InfraError(`${where}: the SKILL.md ${key} must be a string (got ${typeof value})`);
219
+ return value.trim() || void 0;
220
+ };
221
+ return {
222
+ name: field("name"),
223
+ description: field("description"),
224
+ body: match[2] ?? ""
225
+ };
226
+ }
227
+ /** A gitignore-style test for `DEFAULT_SKILL_EXCLUDE`-shaped patterns against a skill-relative path. */
228
+ function excludeMatcher(patterns) {
229
+ const rules = patterns.map((pattern) => {
230
+ const dirOnly = pattern.endsWith("/");
231
+ const body = pattern.replace(/\/+$/, "");
232
+ const anchored = body.includes("/");
233
+ const glob = body.replace(/^\/+/, "").split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*");
234
+ return {
235
+ dirOnly,
236
+ re: new RegExp(`${anchored ? "^" : "(?:^|/)"}${glob}$`)
237
+ };
238
+ });
239
+ return (path, isDir) => rules.some((r) => (isDir || !r.dirOnly) && r.re.test(path));
240
+ }
241
+ function parseSkill(dir, source, exclude) {
242
+ const front = parseSkillMd(readFileSync(join(dir, "SKILL.md"), "utf8"), `Skill ${basename(dir)} in ${source}`);
243
+ const name = front.name || basename(dir);
244
+ checkSkill(name, front.description, source);
245
+ const skip = excludeMatcher(exclude);
246
+ const paths = [];
247
+ const excluded = [];
248
+ const walk = (rel) => {
249
+ for (const entry of readdirSync(join(dir, rel), { withFileTypes: true })) {
250
+ const path = rel ? `${rel}/${entry.name}` : entry.name;
251
+ const isDir = entry.isDirectory();
252
+ if (entry.name === ".git") continue;
253
+ if (skip(path, isDir)) excluded.push(isDir ? `${path}/` : path);
254
+ else if (isDir) walk(path);
255
+ else if (entry.isFile() && path !== "SKILL.md") paths.push(path);
256
+ }
257
+ };
258
+ walk("");
259
+ const files = paths.sort().map((path) => ({
260
+ path,
261
+ content: readFileSync(join(dir, path), "utf8")
262
+ }));
263
+ return {
264
+ skill: {
265
+ name,
266
+ description: front.description ?? "",
267
+ content: front.body,
268
+ files
269
+ },
270
+ excluded: excluded.sort()
271
+ };
272
+ }
273
+ function checkSkill(name, description, source) {
274
+ if (!SKILL_NAME.test(name)) throw new InfraError(`Skill "${name}" in ${source}: the name must be kebab-case, e.g. my-skill (lower-case letters, digits and -)`);
275
+ if (!description) throw new InfraError(`Skill "${name}" in ${source} has no description in its SKILL.md frontmatter. Add a description: line saying when to use it.`);
276
+ }
277
+ function loadSkills(dir, source, only, exclude = DEFAULT_SKILL_EXCLUDE) {
278
+ if (!existsSync(dir) || !statSync(dir).isDirectory()) throw new InfraError(`Skill source ${source} isn't a directory. Point it at a skill's directory, or at a directory of skills.`);
279
+ const dirs = existsSync(join(dir, "SKILL.md")) ? [dir] : readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync(join(dir, e.name, "SKILL.md"))).map((e) => join(dir, e.name)).sort();
280
+ if (dirs.length === 0) throw new InfraError(`Skill source ${source} has no SKILL.md (and no subdirectories with one)`);
281
+ const skills = dirs.map((d) => parseSkill(d, source, exclude));
282
+ if (!only) return skills;
283
+ const missing = only.filter((n) => !skills.some((s) => s.skill.name === n));
284
+ if (missing.length) throw new InfraError(`only: ${JSON.stringify(missing)} not found in ${source} (found: ${skills.map((s) => s.skill.name).join(", ")})`);
285
+ return skills.filter((s) => only.includes(s.skill.name));
286
+ }
287
+ async function git(args, cwd, what) {
288
+ const r = await run("git", args, { cwd });
289
+ if (r.exitCode !== 0) throw new InfraError(`${what} failed: ${r.stderr.trim()}`);
290
+ return r.stdout.trim();
291
+ }
292
+ /** One clone per source and ref per worker process; the commit is recorded. */
293
+ const clones = /* @__PURE__ */ new Map();
294
+ function cloneSkills(root, url, ref) {
295
+ const key = `${url}#${ref ?? ""}`;
296
+ let clone = clones.get(key);
297
+ if (!clone) {
298
+ clone = (async () => {
299
+ const dir = join(root, ".reeval", "cache", "skills", `${sha256(key).slice(0, 16)}-${process.pid}`);
300
+ await rm(dir, {
301
+ recursive: true,
302
+ force: true
303
+ });
304
+ await mkdir(dir, { recursive: true });
305
+ const what = `Cloning ${url}${ref ? ` at ${ref}` : ""}`;
306
+ await git(["init", "-q"], dir, what);
307
+ await git([
308
+ "fetch",
309
+ "-q",
310
+ "--depth",
311
+ "1",
312
+ url,
313
+ ref ?? "HEAD"
314
+ ], dir, what);
315
+ await git([
316
+ "-c",
317
+ "advice.detachedHead=false",
318
+ "checkout",
319
+ "-q",
320
+ "FETCH_HEAD"
321
+ ], dir, what);
322
+ return {
323
+ dir,
324
+ commit: await git(["rev-parse", "HEAD"], dir, what)
325
+ };
326
+ })();
327
+ clones.set(key, clone);
328
+ clone.catch(() => clones.delete(key));
329
+ }
330
+ return clone;
331
+ }
332
+ const packs = /* @__PURE__ */ new Map();
333
+ /** `npm pack` into the cache, unpacked; returns the package directory. */
334
+ function unpackNpm(root, spec) {
335
+ let pack = packs.get(spec);
336
+ if (!pack) {
337
+ pack = (async () => {
338
+ const dir = join(root, ".reeval", "cache", "skills", `npm-${sha256(spec).slice(0, 16)}-${process.pid}`);
339
+ await rm(dir, {
340
+ recursive: true,
341
+ force: true
342
+ });
343
+ await mkdir(dir, { recursive: true });
344
+ const packed = await run("npm", [
345
+ "pack",
346
+ spec,
347
+ "--silent",
348
+ "--pack-destination",
349
+ dir
350
+ ], { cwd: dir });
351
+ const tarball = packed.stdout.trim().split("\n").at(-1);
352
+ if (packed.exitCode !== 0 || !tarball) throw new InfraError(`npm pack ${spec} failed: ${packed.stderr.trim()}`);
353
+ const untar = await run("tar", ["-xzf", tarball], { cwd: dir });
354
+ if (untar.exitCode !== 0) throw new InfraError(`Unpacking ${spec} failed: ${untar.stderr.trim()}`);
355
+ return join(dir, "package");
356
+ })();
357
+ packs.set(spec, pack);
358
+ pack.catch(() => packs.delete(spec));
359
+ }
360
+ return pack;
361
+ }
362
+ async function resolveSkills(root, s) {
363
+ switch (s.source) {
364
+ case "local": {
365
+ const label = `local:${relative(root, s.path).split(sep).join("/") || "."}`;
366
+ return {
367
+ skills: loadSkills(s.path, label, s.only, s.exclude),
368
+ source: label,
369
+ commit: null
370
+ };
371
+ }
372
+ case "git": {
373
+ const { url } = parseGitRepo(s.repo);
374
+ const { dir, commit } = await cloneSkills(root, url, s.ref);
375
+ const path = s.path ?? (!existsSync(join(dir, "SKILL.md")) && existsSync(join(dir, "skills")) ? "skills" : "");
376
+ const label = `${s.repo}${s.ref ? `#${s.ref}` : ""}${path ? `:${path}` : ""}`;
377
+ return {
378
+ skills: loadSkills(join(dir, path), label, s.only, s.exclude),
379
+ source: label,
380
+ commit
381
+ };
382
+ }
383
+ case "registry": {
384
+ const [repo = "", name = ""] = s.id.split("@");
385
+ const { dir, commit } = await cloneSkills(root, `https://github.com/${repo}.git`, s.version);
386
+ const label = `registry:${s.id}${s.version ? `#${s.version}` : ""}`;
387
+ return {
388
+ skills: loadSkills(existsSync(join(dir, "skills")) ? join(dir, "skills") : dir, label, [name]),
389
+ source: label,
390
+ commit
391
+ };
392
+ }
393
+ case "npm": {
394
+ const dir = await unpackNpm(root, s.spec);
395
+ const label = `npm:${isAbsolute(s.spec) ? relative(root, s.spec).split(sep).join("/") : s.spec}:${s.path}`;
396
+ return {
397
+ skills: loadSkills(join(dir, s.path), label, s.only, s.exclude),
398
+ source: label,
399
+ commit: null
400
+ };
401
+ }
402
+ case "inline": {
403
+ checkSkill(s.name, s.description, "skill.inline");
404
+ const files = Object.entries(s.files).sort(([a], [b]) => a < b ? -1 : 1).map(([path, content]) => ({
405
+ path,
406
+ content
407
+ }));
408
+ return {
409
+ skills: [{
410
+ skill: {
411
+ name: s.name,
412
+ description: s.description,
413
+ content: s.content,
414
+ files
415
+ },
416
+ excluded: []
417
+ }],
418
+ source: "inline",
419
+ commit: null
420
+ };
421
+ }
422
+ }
423
+ }
424
+ const skillHash = (s) => sha256(JSON.stringify(s));
425
+ const readHostFile = (f, where) => {
426
+ if (!existsSync(f.path)) throw new InfraError(`${f.path} for ${where} doesn't exist. file() paths resolve against the file that calls it, or reeval.yaml's directory.`);
427
+ return readFileSync(f.path);
428
+ };
429
+ /**
430
+ * Layers the environment (root first; a later layer wins on shared keys, and each such key is
431
+ * an override), over the options `env` merged down the cascade. `where` names the attempt, and
432
+ * `rulesFile` is the workspace file the harness's agent reads `rules` from.
433
+ */
434
+ async function resolveEnvironment(environment, optionsEnv, root, where, rulesFile, egress = "allow-all", prepared = () => void 0) {
435
+ const envWhere = `environment ${environment.name} (${where})`;
436
+ const overrides = /* @__PURE__ */ new Set();
437
+ const env = { ...optionsEnv };
438
+ const files = /* @__PURE__ */ new Map();
439
+ const mcp = /* @__PURE__ */ new Map();
440
+ const skills = /* @__PURE__ */ new Map();
441
+ let rules;
442
+ const setup = [];
443
+ const seen = {
444
+ env: /* @__PURE__ */ new Set(),
445
+ files: /* @__PURE__ */ new Set(),
446
+ mcp: /* @__PURE__ */ new Set()
447
+ };
448
+ let egressSet = false;
449
+ let workspace;
450
+ let sandboxSet = false;
451
+ for (const { spec } of environment.layers) {
452
+ if (spec.sandbox !== void 0) {
453
+ if (sandboxSet) overrides.add("sandbox");
454
+ sandboxSet = true;
455
+ }
456
+ if (spec.workspace !== void 0) {
457
+ if (workspace) overrides.add("workspace");
458
+ workspace = spec.workspace;
459
+ }
460
+ if (spec.egress !== void 0) {
461
+ if (egressSet) overrides.add("egress");
462
+ egressSet = true;
463
+ }
464
+ for (const [k, v] of Object.entries(spec.env ?? {})) {
465
+ if (seen.env.has(k)) overrides.add(`env.${k}`);
466
+ seen.env.add(k);
467
+ env[k] = v;
468
+ }
469
+ for (const [path, v] of Object.entries(spec.files ?? {})) {
470
+ if (seen.files.has(path)) overrides.add(`files.${path}`);
471
+ seen.files.add(path);
472
+ files.set(path, v);
473
+ }
474
+ if (spec.rules !== void 0) {
475
+ if (rules !== void 0) overrides.add("rules");
476
+ rules = spec.rules;
477
+ }
478
+ for (const [name, server] of Object.entries(spec.mcp ?? {})) {
479
+ if (seen.mcp.has(name)) overrides.add(`mcp.${name}`);
480
+ seen.mcp.add(name);
481
+ mcp.set(name, server);
482
+ }
483
+ for (const step of spec.setup ?? []) {
484
+ const s = typeof step === "string" ? { run: step } : step;
485
+ setup.push({
486
+ run: s.run,
487
+ timeoutMs: parseDuration(s.timeout ?? DEFAULT_SETUP_TIMEOUT),
488
+ ...s.cwd ? { cwd: s.cwd } : {}
489
+ });
490
+ }
491
+ const layerSkills = /* @__PURE__ */ new Set();
492
+ for (const source of spec.skills ?? []) {
493
+ const found = await resolveSkills(root, source);
494
+ for (const loaded of found.skills) {
495
+ const name = loaded.skill.name;
496
+ if (skills.has(name) && !layerSkills.has(name)) overrides.add(`skills.${name}`);
497
+ layerSkills.add(name);
498
+ skills.delete(name);
499
+ skills.set(name, {
500
+ ...loaded,
501
+ placement: source.placement,
502
+ source: found.source,
503
+ commit: found.commit
504
+ });
505
+ }
506
+ }
507
+ }
508
+ const secrets = {
509
+ names: [],
510
+ checks: [],
511
+ unset: [],
512
+ brokered: []
513
+ };
514
+ const vars = {};
515
+ const checkVars = {};
516
+ for (const [name, value] of Object.entries(env)) {
517
+ if (!isSecret(value)) {
518
+ vars[name] = value;
519
+ continue;
520
+ }
521
+ const resolved = hostValue(name, value, envWhere, secrets);
522
+ if (resolved === void 0) continue;
523
+ if (value.to === "checks") checkVars[name] = resolved;
524
+ else vars[name] = resolved;
525
+ }
526
+ const secretOf = (value, key, hosts) => {
527
+ if (typeof value === "string") return value;
528
+ if (isSecretTemplate(value)) return template(value, envWhere, secrets, hosts);
529
+ return hostValue(key, value, envWhere, secrets, value.hosts ?? hosts);
530
+ };
531
+ const resolvedMcp = {};
532
+ for (const [name, s] of mcp) if (s.transport === "stdio") {
533
+ const e = {};
534
+ for (const [k, v] of Object.entries(s.env)) {
535
+ const r = secretOf(v, k);
536
+ if (r !== void 0) e[k] = r;
537
+ }
538
+ resolvedMcp[name] = {
539
+ transport: "stdio",
540
+ command: s.command,
541
+ args: s.args,
542
+ env: e
543
+ };
544
+ } else {
545
+ const h = {};
546
+ const hosts = mcpHosts(s.url);
547
+ for (const [k, v] of Object.entries(s.headers)) {
548
+ const r = secretOf(v, k, hosts);
549
+ if (r !== void 0) h[k] = r;
550
+ }
551
+ resolvedMcp[name] = {
552
+ transport: s.transport,
553
+ url: s.url,
554
+ headers: h
555
+ };
556
+ }
557
+ const fileList = [...files].map(([path, v]) => ({
558
+ path,
559
+ content: isHostFile(v) ? readHostFile(v, envWhere) : v,
560
+ ...isHostFile(v) && statSync(v.path).mode & 64 ? { executable: true } : {}
561
+ }));
562
+ const rulesContent = rules === void 0 ? void 0 : isHostFile(rules) ? Buffer.from(readHostFile(rules, envWhere)).toString("utf8") : rules;
563
+ const skillList = [...skills.values()];
564
+ const skillRecords = skillList.map(({ skill, placement, source, commit, excluded }) => ({
565
+ name: skill.name,
566
+ source,
567
+ commit,
568
+ sha256: skillHash(skill),
569
+ placement,
570
+ excluded
571
+ }));
572
+ const fileRecords = fileList.map((f) => ({
573
+ path: f.path,
574
+ sha256: sha256(f.content)
575
+ }));
576
+ const executable = fileList.flatMap((f) => f.executable ? [f.path] : []);
577
+ const rulesRecord = rulesContent === void 0 ? void 0 : {
578
+ path: rulesFile,
579
+ sha256: sha256(rulesContent)
580
+ };
581
+ if (rulesContent !== void 0) fileList.push({
582
+ path: rulesFile,
583
+ content: rulesContent
584
+ });
585
+ const secretNames = [.../* @__PURE__ */ new Set([...secrets.names, ...secrets.checks])].sort();
586
+ const brokered = secrets.brokered.map((b) => JSON.stringify([
587
+ b.name,
588
+ b.hosts,
589
+ b.in,
590
+ b.phase
591
+ ])).sort();
592
+ const brokeredNames = [...new Set(secrets.brokered.map((b) => b.name))].sort();
593
+ const varNames = Object.keys(vars).filter((n) => !secrets.names.includes(n)).sort();
594
+ const literalVars = Object.fromEntries(varNames.map((n) => [n, vars[n]]));
595
+ const mcpShape = Object.fromEntries(Object.entries(resolvedMcp).map(([name, s]) => s.transport === "stdio" ? [name, {
596
+ command: s.command,
597
+ args: s.args,
598
+ env: Object.keys(s.env).sort()
599
+ }] : [name, {
600
+ transport: s.transport,
601
+ url: s.url,
602
+ headers: Object.keys(s.headers).sort()
603
+ }]));
604
+ const tree = workspace && prepared(workspace);
605
+ if (workspace && !tree) throw new InfraError(`The workspace git(${JSON.stringify(workspaceLabel(workspace))}) for ${envWhere} wasn't prepared before the run started`);
606
+ const workspaceRecord = workspace && tree ? {
607
+ source: isAbsolute(workspace.url) ? relative(root, workspace.url).split(sep).join("/") || "." : workspace.repo,
608
+ ref: workspace.ref ?? null,
609
+ path: workspace.path ?? null,
610
+ commit: tree.commit,
611
+ tree: tree.tree,
612
+ ...workspace.init ? { init: true } : {}
613
+ } : void 0;
614
+ const digest = `sha256:${sha256(JSON.stringify({
615
+ setup,
616
+ skills: skillRecords.map((s) => [
617
+ s.name,
618
+ s.sha256,
619
+ s.placement
620
+ ]),
621
+ mcp: mcpShape,
622
+ files: fileRecords,
623
+ ...executable.length ? { executable } : {},
624
+ rules: rulesRecord?.sha256,
625
+ vars: literalVars,
626
+ secrets: secretNames,
627
+ checks: [...secrets.checks].sort(),
628
+ unset: [...secrets.unset].sort(),
629
+ brokered,
630
+ ...egress === "allow-all" ? {} : { egress: typeof egress === "string" ? egress : [...egress.allow].sort() },
631
+ ...workspaceRecord ? { workspace: workspaceRecord.init ? [workspaceRecord.tree, "init"] : workspaceRecord.tree } : {}
632
+ }))}`;
633
+ return {
634
+ record: {
635
+ name: environment.name,
636
+ parent: environment.parent,
637
+ overrides: [...overrides],
638
+ digest,
639
+ setup: setup.map(stepCommand),
640
+ skills: skillRecords,
641
+ mcp: Object.keys(resolvedMcp).sort(),
642
+ files: rulesRecord ? [...fileRecords, rulesRecord] : fileRecords,
643
+ vars: varNames,
644
+ secrets: secretNames,
645
+ brokered: brokeredNames,
646
+ unset: [...secrets.unset].sort(),
647
+ egress,
648
+ ...workspaceRecord ? { workspace: workspaceRecord } : {}
649
+ },
650
+ setup,
651
+ skills: skillList.filter((s) => s.placement === "harness").map((s) => s.skill),
652
+ workspaceSkills: skillList.filter((s) => s.placement === "workspace").map((s) => s.skill),
653
+ mcp: resolvedMcp,
654
+ files: fileList,
655
+ vars,
656
+ checkVars,
657
+ substitutions: secrets.brokered,
658
+ ...workspace && tree ? { workspace: {
659
+ tar: tree.tar,
660
+ label: workspaceLabel(workspace),
661
+ commit: tree.commit,
662
+ ...workspace.init ? { init: true } : {}
663
+ } } : {}
664
+ };
665
+ }
666
+ /** A skill as a `SKILL.md` file plus its other files, for writing into a directory. */
667
+ function skillFiles(skill) {
668
+ return [{
669
+ path: "SKILL.md",
670
+ content: `${`---\nname: ${skill.name}\ndescription: ${JSON.stringify(skill.description)}\n---\n`}${skill.content}`
671
+ }, ...skill.files];
672
+ }
673
+ //#endregion
674
+ //#region src/harness/fake.ts
675
+ const lineCount = (text) => text ? text.split("\n").length - (text.endsWith("\n") ? 1 : 0) : 0;
676
+ /**
677
+ * Writes canned files and returns a canned message on every turn, after running its `script`'s
678
+ * real sandbox actions. Harness-placed skills are written under `~/` where its layout's adapter
679
+ * puts them (`~/.agents/skills` by default, as Cursor's does).
680
+ */
681
+ const createFakeHarness = (descriptor) => {
682
+ let starts = 0;
683
+ return { async start(sandbox, _signal, { skills, workspaceSkills, mcp }) {
684
+ if (descriptor.failWith) throw new InfraError(descriptor.failWith);
685
+ const hangs = descriptor.startHangs;
686
+ if (hangs === true || starts++ < (hangs ?? 0)) return new Promise(() => {});
687
+ const dir = `~/${layoutOf(descriptor).harnessSkillDir}`;
688
+ for (const skill of skills) {
689
+ for (const f of skillFiles(skill)) await sandbox.writeFile(`${dir}/${skill.name}/${f.path}`, f.content);
690
+ debugLog("env", `skill ${skill.name} placed at ${dir}/${skill.name}/ by the fake agent`);
691
+ }
692
+ const names = [...skills.map((s) => s.name), ...workspaceSkills];
693
+ const loaded = [names.length ? `skills: ${names.join(", ")}` : "", Object.keys(mcp).length ? `mcp: ${Object.keys(mcp).join(", ")}` : ""].filter(Boolean);
694
+ let calls = 0;
695
+ let turns = 0;
696
+ const skilled = names.length > 0 && descriptor.withSkill;
697
+ const files = (skilled ? descriptor.withSkill?.files : void 0) ?? descriptor.files ?? {};
698
+ const reply = (skilled ? descriptor.withSkill?.text : void 0) ?? descriptor.text ?? "Done.";
699
+ const flaked = Math.random() < (descriptor.flakeRate ?? 0);
700
+ return {
701
+ async send(prompt, signal) {
702
+ const startedAt = Date.now();
703
+ const script = descriptor.script ?? [];
704
+ const steps = script[Math.min(turns++, script.length - 1)] ?? [];
705
+ const at = () => {
706
+ const ms = Date.now() - startedAt;
707
+ return {
708
+ ts: new Date(startedAt + ms).toISOString(),
709
+ atMs: ms
710
+ };
711
+ };
712
+ const transcript = [{
713
+ ...at(),
714
+ type: "text",
715
+ text: `Working on: ${prompt}`,
716
+ durationMs: 0
717
+ }, ...loaded.length ? [{
718
+ ...at(),
719
+ type: "text",
720
+ text: `Loaded ${loaded.join("; ")}`,
721
+ durationMs: 0
722
+ }] : []];
723
+ const done = (text, status) => ({
724
+ text,
725
+ status,
726
+ startedAt: new Date(startedAt).toISOString(),
727
+ durationMs: Date.now() - startedAt,
728
+ transcript,
729
+ usage: null,
730
+ model: descriptor.servedModel ?? null,
731
+ unreported: []
732
+ });
733
+ const write = async (path, content) => {
734
+ const time = at();
735
+ const before = await sandbox.readFile(path);
736
+ await sandbox.writeFile(path, content);
737
+ debugLog("harness", `tool write ${path} (${before === null ? "create" : "modify"})`);
738
+ transcript.push({
739
+ ...time,
740
+ type: "file-edit",
741
+ toolCallId: `call_${++calls}`,
742
+ path,
743
+ kind: before === null ? "create" : "modify",
744
+ added: lineCount(content),
745
+ removed: before === null ? 0 : lineCount(before),
746
+ durationMs: Date.now() - startedAt - time.atMs
747
+ });
748
+ };
749
+ if (descriptor.reasoning) transcript.push({
750
+ ...at(),
751
+ type: "reasoning",
752
+ text: descriptor.reasoning,
753
+ durationMs: 0
754
+ });
755
+ for (const tool of descriptor.tools ?? []) transcript.push({
756
+ ...at(),
757
+ type: "tool",
758
+ toolCallId: `call_${++calls}`,
759
+ tool: tool.tool,
760
+ input: tool.input,
761
+ output: tool.output,
762
+ ok: tool.ok ?? true,
763
+ durationMs: 0
764
+ });
765
+ if (flaked) {
766
+ const text = "I ran out of ideas and left the workspace unchanged.";
767
+ transcript.push({
768
+ ...at(),
769
+ type: "text",
770
+ text,
771
+ durationMs: 0
772
+ });
773
+ return done(text, "completed");
774
+ }
775
+ let scripted;
776
+ for (const step of steps) if ("exec" in step) {
777
+ const time = at();
778
+ debugLog("harness", `tool shell called: $ ${step.exec}`);
779
+ const r = await sandbox.exec(step.exec, { signal });
780
+ debugLog("harness", `tool shell returned exit ${r.exitCode} in ${Date.now() - startedAt - time.atMs}ms`);
781
+ transcript.push({
782
+ ...time,
783
+ type: "tool",
784
+ toolCallId: `call_${++calls}`,
785
+ tool: "shell",
786
+ input: { command: step.exec },
787
+ output: r.stdout,
788
+ ok: r.exitCode === 0,
789
+ durationMs: Date.now() - startedAt - time.atMs
790
+ });
791
+ } else if ("write" in step) for (const [path, content] of Object.entries(step.write)) await write(path, content);
792
+ else if ("mcp" in step) {
793
+ const time = at();
794
+ const [server = "", tool = ""] = step.mcp.split("/");
795
+ debugLog("harness", `tool mcp__${server}__${tool} called`);
796
+ const r = await callMcpTool(sandbox, mcp, server, tool, signal);
797
+ debugLog("harness", `tool mcp__${server}__${tool} ${r.ok ? "returned" : "failed"}`);
798
+ transcript.push({
799
+ ...time,
800
+ type: "tool",
801
+ toolCallId: `call_${++calls}`,
802
+ tool: `mcp__${server}__${tool}`,
803
+ input: {},
804
+ output: r.output,
805
+ ok: r.ok,
806
+ durationMs: Date.now() - startedAt - time.atMs
807
+ });
808
+ } else if ("error" in step) {
809
+ debugLog("harness", `turn failed: ${step.error}`);
810
+ transcript.push({
811
+ ...at(),
812
+ type: "error",
813
+ message: step.error
814
+ });
815
+ return done("", "error");
816
+ } else scripted = step.reply;
817
+ if (descriptor.delayMs) try {
818
+ await setTimeout$1(descriptor.delayMs, void 0, descriptor.ignoreAbort ? {} : { signal });
819
+ } catch {
820
+ return done("", "aborted");
821
+ }
822
+ for (const [path, content] of Object.entries(files)) await write(path, content);
823
+ const text = scripted ?? reply;
824
+ transcript.push({
825
+ ...at(),
826
+ type: "text",
827
+ text,
828
+ durationMs: 0
829
+ });
830
+ return done(text, "completed");
831
+ },
832
+ async close() {}
833
+ };
834
+ } };
835
+ };
836
+ /**
837
+ * Starts a stdio MCP server in the sandbox, as an agent would, and calls one tool with no
838
+ * arguments: initialize, then the call.
839
+ */
840
+ async function callMcpTool(sandbox, servers, name, tool, signal) {
841
+ const server = servers[name];
842
+ if (!server) {
843
+ const known = Object.keys(servers);
844
+ throw new InfraError(`The fake agent's script calls MCP server "${name}", which the environment doesn't have (it has ${known.length ? known.join(", ") : "none"})`);
845
+ }
846
+ if (server.transport !== "stdio") throw new InfraError(`The fake agent calls stdio MCP servers only; "${name}" is ${server.transport}`);
847
+ const messages = [
848
+ {
849
+ jsonrpc: "2.0",
850
+ id: 1,
851
+ method: "initialize",
852
+ params: {
853
+ protocolVersion: "2024-11-05",
854
+ capabilities: {},
855
+ clientInfo: {
856
+ name: "reeval-fake-agent",
857
+ version: "1"
858
+ }
859
+ }
860
+ },
861
+ {
862
+ jsonrpc: "2.0",
863
+ method: "notifications/initialized"
864
+ },
865
+ {
866
+ jsonrpc: "2.0",
867
+ id: 2,
868
+ method: "tools/call",
869
+ params: {
870
+ name: tool,
871
+ arguments: {}
872
+ }
873
+ }
874
+ ].map((m) => shellQuote(JSON.stringify(m)));
875
+ const command = [server.command, ...server.args].map(shellQuote).join(" ");
876
+ const script = [
877
+ "d=$(mktemp -d) && : > \"$d/out\" && : > \"$d/err\" || exit 1",
878
+ "{",
879
+ " trap '' PIPE",
880
+ ` printf '%s\\n' ${messages.join(" ")} 2>/dev/null`,
881
+ " i=0",
882
+ ` while [ $i -lt 300 ] && [ ! -f "$d/code" ] && ! grep -Eq '"id": ?2[,}]' "$d/out"; do sleep 0.1; i=$((i + 1)); done`,
883
+ `} | { ${command} > "$d/out" 2> "$d/err"; echo $? > "$d/code"; }`,
884
+ "code=$(cat \"$d/code\")",
885
+ "cat \"$d/out\"; cat \"$d/err\" >&2; rm -rf \"$d\"; exit $code"
886
+ ].join("\n");
887
+ const r = await sandbox.exec(script, {
888
+ signal,
889
+ env: server.env
890
+ });
891
+ const reply = r.stdout.split("\n").flatMap((line) => {
892
+ try {
893
+ return [JSON.parse(line)];
894
+ } catch {
895
+ return [];
896
+ }
897
+ }).find((m) => m.id === 2);
898
+ if (!reply) return {
899
+ output: r.stderr.trim() || `no reply (exit ${r.exitCode})`,
900
+ ok: false
901
+ };
902
+ if (reply.error) return {
903
+ output: reply.error.message,
904
+ ok: false
905
+ };
906
+ return {
907
+ output: (reply.result?.content ?? []).flatMap((c) => c.type === "text" ? [c.text] : []).join("\n"),
908
+ ok: !reply.result?.isError
909
+ };
910
+ }
911
+ //#endregion
912
+ //#region src/harness/index.ts
913
+ async function createHarness(descriptor) {
914
+ if (descriptor.kind === "fake") return createFakeHarness(descriptor);
915
+ const { createAgentHarness } = await import("./agent-DpE80WPz.js");
916
+ return createAgentHarness(descriptor);
917
+ }
918
+ /** Where each harness's agent looks for repo skills, relative to the workspace. */
919
+ const workspaceSkillDir = (descriptor) => layoutOf(descriptor).workspaceSkillDir;
920
+ //#endregion
921
+ //#region src/attempt/environment.ts
922
+ /**
923
+ * Resolves the attempt's environment (files, skills, secrets and their placeholders)
924
+ * and finds the image a Docker sandbox starts from, which preflight prepared. Runs on the test's
925
+ * signal, before the eval timer starts.
926
+ */
927
+ async function prepareEnvironment(a, root, runId, where, signal) {
928
+ const { spec, record } = a;
929
+ const env = await resolveEnvironment(spec.environment, spec.env, root, where, layoutOf(spec.harness).rulesFile, spec.egress.policy, (w) => preparedWorkspace(root, runId, w));
930
+ a.env = env;
931
+ record.environment = env.record;
932
+ const { secrets, brokered } = env.record;
933
+ record.env = {
934
+ vars: Object.keys(env.vars).filter((n) => !secrets.includes(n)).sort(),
935
+ secrets,
936
+ brokered
937
+ };
938
+ const debug = attemptDebug();
939
+ if (debug) traceEnvironment(debug, spec, env);
940
+ record.image = null;
941
+ if (spec.sandbox.kind === "docker") {
942
+ const { preparedFor } = await import("./preflight-DlQvbySb.js");
943
+ const { attemptImage, dockerProvider } = await import("./providers-B9yTdIqF.js");
944
+ signal.throwIfAborted();
945
+ const prepared = preparedFor(root, runId, dockerProvider({ engine: spec.sandbox.engine ?? "docker" }), spec.sandbox, spec.harness);
946
+ if (!prepared) throw new InfraError(`The sandbox image for ${where} wasn't prepared before the run started`);
947
+ record.image = attemptImage(prepared);
948
+ a.image = record.image.ref;
949
+ const { ref, id, base, install, agentCli } = record.image;
950
+ debugLog("image", `ready ${ref} (${id.replace(/^sha256:/, "").slice(0, 12)}) · prepared before the run${ref !== base ? ` · on ${base}` : ""}${agentCli ? ` · agent CLI ${agentCli}` : ""}${install.length ? ` · install: ${install.join(" && ")}` : ""}`);
951
+ } else debugLog("image", `none: the ${spec.sandbox.kind} sandbox runs on the host`);
952
+ }
953
+ //#endregion
954
+ //#region src/judge/size.ts
955
+ /** Roughly four characters a token, as `reeval analyze` counts: an estimate, not the model's. */
956
+ const CHARS_PER_TOKEN = 4;
957
+ const tokensOf = (chars) => Math.ceil(chars / CHARS_PER_TOKEN);
958
+ const k = (tokens) => tokens < 1e3 ? String(tokens) : `${Math.round(tokens / 1e3)}k`;
959
+ const diffItems = (patch) => patch.split(/(?=^diff --git )/m).map((part) => ({
960
+ name: /^diff --git a\/.+ b\/(.+)$/m.exec(part)?.[1] ?? "",
961
+ chars: part.length
962
+ })).filter((item) => item.name);
963
+ /** The state's parts by size, each with its biggest files where it has any. */
964
+ function partsOf(state, question) {
965
+ return [...Object.entries(state).map(([name, value]) => {
966
+ const text = typeof value === "string" ? value : JSON.stringify(value);
967
+ const items = name === "diff" && typeof value === "string" ? diffItems(value) : value && typeof value === "object" && !Array.isArray(value) ? Object.entries(value).map(([file, content]) => ({
968
+ name: file,
969
+ chars: typeof content === "string" ? content.length : JSON.stringify(content).length
970
+ })) : [];
971
+ return {
972
+ name,
973
+ chars: text?.length ?? 0,
974
+ items
975
+ };
976
+ }), {
977
+ name: "question",
978
+ chars: question.length,
979
+ items: []
980
+ }].sort((a, b) => b.chars - a.chars);
981
+ }
982
+ /** The estimated size of what the judge reads, in tokens. */
983
+ const inputTokens = (state, question) => tokensOf(JSON.stringify(state).length + question.length);
984
+ /**
985
+ * `diff 306k, of which dist/assets/index-….js is 296k; output 1k; …`: the state's parts by
986
+ * estimated tokens, naming the biggest files in each.
987
+ */
988
+ function describeSize(state, question) {
989
+ const parts = partsOf(state, question);
990
+ const big = parts.filter((p) => tokensOf(p.chars) >= 1e3);
991
+ return (big.length ? big : parts.slice(0, 1)).map((p) => {
992
+ const top = p.items.sort((a, b) => b.chars - a.chars).slice(0, 3).filter((item) => tokensOf(item.chars) >= 1e3).map((item) => `${item.name} is ${k(tokensOf(item.chars))}`);
993
+ return `${p.name} ${k(tokensOf(p.chars))}${top.length ? `, of which ${top.join(", ")}` : ""}`;
994
+ }).join("; ");
995
+ }
996
+ /** `Narrow state.diff, state.transcript or state.output.`: the state's parts, largest first. */
997
+ function stateHint(state, question) {
998
+ const keys = partsOf(state, question).filter((p) => p.name !== "question").map((p) => `state.${p.name}`);
999
+ if (!keys.length) return "";
1000
+ const last = keys.pop();
1001
+ return ` Narrow ${keys.length ? `${keys.join(", ")} or ${last}` : last}.`;
1002
+ }
1003
+ /**
1004
+ * A provider's error, as the provider worded it, then what the judge's input held: its estimated
1005
+ * size and biggest parts and files, and the `state` keys that were on, largest first. reeval knows
1006
+ * no model's limit, so this follows every provider error.
1007
+ */
1008
+ const withInputSize = (message, state, question) => `${message}\nThe judge's input was ~${k(inputTokens(state, question))} tokens (estimated): ${describeSize(state, question)}.${stateHint(state, question)}`;
1009
+ //#endregion
1010
+ //#region src/judge/state.ts
1011
+ function turnState(t) {
1012
+ return {
1013
+ ...t.task !== void 0 ? { task: t.task } : {},
1014
+ ...t.output !== void 0 ? { output: t.output } : {},
1015
+ ...t.transcript !== void 0 ? { transcript: t.transcript } : {}
1016
+ };
1017
+ }
1018
+ /** The JSON the judge reads: the graded turn's keys at the top, or every turn under `turns`. */
1019
+ function stateOf(d) {
1020
+ const last = d.turns.at(-1) ?? {};
1021
+ const { transcript, ...top } = d.all ? {} : turnState(last);
1022
+ return {
1023
+ ...d.all ? { turns: d.turns.map(turnState) } : top,
1024
+ ...d.files ? { files: d.files } : {},
1025
+ ...d.diff !== void 0 ? { diff: d.diff } : {},
1026
+ ...d.contents ? { contents: d.contents } : {},
1027
+ ...d.checks ? { checks: d.checks } : {},
1028
+ ...transcript !== void 0 ? { transcript } : {}
1029
+ };
1030
+ }
1031
+ //#endregion
1032
+ //#region src/attempt/judge.ts
1033
+ const noJudgeModel = (configFile) => `c.judge() has no model. Set judge: { model: 'typesafe-ai/jev-1.14.0' } in ${configFile}, on the group or eval, or pass { model } to this call.`;
1034
+ /** A turn this attempt sent becomes its index; any other value is graded as the output. */
1035
+ function judgeOn(turns, on) {
1036
+ const turn = turns.indexOf(on);
1037
+ if (turn >= 0) return { turn };
1038
+ if (typeof on === "object" && typeof on?.exec === "function") throw new Error("c.judge() on is a turn this attempt didn't send");
1039
+ return { value: on };
1040
+ }
1041
+ const workspacePath = (path) => path.replace(/^\.\//, "");
1042
+ /** A `contents` entry that names one file rather than matching many. */
1043
+ const isExactPath = (entry) => !entry.startsWith("!") && !/[*?[\]{}]/.test(entry);
1044
+ /**
1045
+ * The judge's state for what the `state` option chose: the graded turn's task and output (or every turn's),
1046
+ * its changed files and diff as `judgeDiff` shows them, files whole, `c.exec` results and the
1047
+ * transcript.
1048
+ */
1049
+ async function draftState(src) {
1050
+ const { subject, wanted } = src;
1051
+ const at = src.turns.indexOf(subject);
1052
+ const turns = (wanted.turns === "all" && at >= 0 ? src.turns.slice(0, at + 1) : [subject]).map((turn) => ({
1053
+ ...wanted.task ? { task: turn.prompt } : {},
1054
+ ...wanted.output ? { output: turn === subject ? src.output : turn.text } : {},
1055
+ ...wanted.transcript ? { transcript: transcriptForJudge(turn.transcript, wanted.transcript).join("\n") } : {}
1056
+ }));
1057
+ const contents = await filesWhole(wanted.contents, src);
1058
+ const shown = judgeDiff(subject.diff, {
1059
+ ...Array.isArray(wanted.diff) ? { patterns: wanted.diff } : {},
1060
+ ...contents ? { inFull: new Set(Object.keys(contents)) } : {}
1061
+ });
1062
+ const checks = checksShown(wanted.checks, src.checks);
1063
+ return {
1064
+ draft: {
1065
+ all: wanted.turns === "all",
1066
+ turns,
1067
+ ...wanted.files ? { files: shown.files } : {},
1068
+ ...wanted.diff !== false ? { diff: shown.patch } : {},
1069
+ ...contents ? { contents } : {},
1070
+ ...checks ? { checks } : {}
1071
+ },
1072
+ dropped: wanted.diff !== false ? shown.dropped : []
1073
+ };
1074
+ }
1075
+ /** Exact paths (`null` when missing) and every workspace file the patterns match, in path order. */
1076
+ async function filesWhole(entries, src) {
1077
+ if (!entries.length) return void 0;
1078
+ const exact = entries.filter(isExactPath).map(workspacePath);
1079
+ const matches = globFilter(entries.map(workspacePath));
1080
+ const matched = entries.some((e) => !isExactPath(e)) ? (await src.listFiles()).filter((path) => matches(path)) : [];
1081
+ const paths = [.../* @__PURE__ */ new Set([...exact, ...matched])].sort();
1082
+ const contents = {};
1083
+ for (const path of paths) {
1084
+ const text = await src.readFile(path);
1085
+ contents[path] = text?.includes("\0") ? "[reeval: dropped binary file]" : text;
1086
+ }
1087
+ return contents;
1088
+ }
1089
+ function checksShown(which, ran) {
1090
+ if (which === false) return void 0;
1091
+ if (which !== true) {
1092
+ const missing = which.find((command) => !ran.some((r) => r.command === command));
1093
+ if (missing !== void 0) throw new Error(`c.judge() state.checks names a command this attempt hasn't run: ${JSON.stringify(missing)}`);
1094
+ }
1095
+ return ran.filter((r) => which === true || which.includes(r.command)).map(({ command, exitCode, stdout, stderr }) => ({
1096
+ command,
1097
+ exitCode,
1098
+ stdout,
1099
+ stderr
1100
+ }));
1101
+ }
1102
+ /**
1103
+ * Grades a turn (the last by default) or a value against a question, under
1104
+ * the judge provider's concurrency lease. Every call's input is recorded, including calls that
1105
+ * fail. A call that fails returns a `JudgeFailure` and fails the attempt as a judge error after the eval's body, so the judges and
1106
+ * checks after it still run.
1107
+ */
1108
+ function judgeCaller(a, readFile) {
1109
+ const ask = async (question, { on, ...call } = {}) => {
1110
+ if (typeof question !== "string" || !question.trim()) throw new Error(`c.judge() needs a question: a non-empty string (got ${JSON.stringify(question)})`);
1111
+ const problem = judgeProblem(call, { at: "call" });
1112
+ if (problem) throw new Error(problem);
1113
+ const badArts = badJudgeArtifacts(call.artifacts);
1114
+ if (badArts) throw new Error(`c.judge() artifacts ${badArts}`);
1115
+ const subject = on && "turn" in on ? a.turns[on.turn] : a.turns.at(-1);
1116
+ if (!subject) throw new Error(on && "turn" in on ? `c.judge() on names turn ${on.turn}, but this attempt has ${a.turns.length}` : "c.judge() has no turn to grade: call c.send() first");
1117
+ const options = mergeJudge(a.spec.judge, call);
1118
+ const { model } = options;
1119
+ if (model === void 0) {
1120
+ a.judgeErrored = true;
1121
+ throw new Error(noJudgeModel(getRunContext().config.file));
1122
+ }
1123
+ const wanted = resolveStateOptions(options.state);
1124
+ if (on && "value" in on && !wanted.output) throw new Error("c.judge() on gives the output, but state.output is false");
1125
+ const { state: _state, maxConcurrent, ...judgeOptions } = options;
1126
+ const output = on && "value" in on ? on.value : subject.text;
1127
+ const { draft, dropped } = await draftState({
1128
+ turns: a.turns,
1129
+ subject,
1130
+ output,
1131
+ wanted,
1132
+ checks: a.record.checks,
1133
+ readFile,
1134
+ listFiles: async () => workspaceFiles(await sandboxFor(a, "c.judge()"), a.signal)
1135
+ });
1136
+ const name = judgeModelName(model);
1137
+ const state = stateOf(draft);
1138
+ const resolved = resolveJudgeArtifacts(call.artifacts, a.artifactBlobs);
1139
+ const refs = artifactRefsOf(resolved);
1140
+ const decide = decideState(state, filePartsOf(resolved));
1141
+ const input = {
1142
+ question,
1143
+ model: name,
1144
+ stateOptions: wanted,
1145
+ state,
1146
+ ...refs.length ? { artifacts: refs } : {},
1147
+ ...dropped.length ? { dropped } : {}
1148
+ };
1149
+ a.record.judgeInputs?.push(input);
1150
+ const start = performance.now();
1151
+ const ms = () => Math.round(performance.now() - start);
1152
+ const atLeast = options.threshold ?? .8;
1153
+ a.report({
1154
+ kind: "judge-start",
1155
+ question
1156
+ });
1157
+ debugLog("judge", `asking ${name}: ${JSON.stringify(clip(question, 160))}`);
1158
+ if (dropped.length) debugLog("judge", `left out of the judge's input: ${dropped.join(" · ")}`);
1159
+ debugTrace("judge", "prompt", input, "judge prompt");
1160
+ const failed = (message, retries) => {
1161
+ a.judgeErrored = true;
1162
+ a.judgeErrors.push(`c.judge(${JSON.stringify(clip(question, 80))}) failed: ${message}`);
1163
+ input.error = message;
1164
+ if (retries?.length) input.retries = retries;
1165
+ debugLog("judge", `${name} · ${formatDuration(ms())} · error: ${message}`);
1166
+ debugTrace("judge", "error", {
1167
+ error: message,
1168
+ retries
1169
+ }, "judge error");
1170
+ a.report({
1171
+ kind: "judge-end",
1172
+ question,
1173
+ verdict: "error",
1174
+ score: null,
1175
+ threshold: null,
1176
+ ms: ms()
1177
+ });
1178
+ return {
1179
+ verdict: "error",
1180
+ score: null,
1181
+ threshold: atLeast,
1182
+ question,
1183
+ model: name,
1184
+ error: message,
1185
+ ...refs.length ? { artifacts: refs } : {}
1186
+ };
1187
+ };
1188
+ const provider = name.split("/")[0];
1189
+ try {
1190
+ const { verdict, retries } = await a.timed("judgeMs", () => withLease(maxConcurrent ? { [`judge:${provider}`]: maxConcurrent } : {}, () => runJudge(question, decide, {
1191
+ ...judgeOptions,
1192
+ model,
1193
+ threshold: atLeast
1194
+ }, a.signal), a.signal));
1195
+ const recorded = refs.length ? {
1196
+ ...verdict,
1197
+ artifacts: refs
1198
+ } : verdict;
1199
+ a.record.judgements.push(retries.length ? {
1200
+ ...recorded,
1201
+ retries
1202
+ } : recorded);
1203
+ const { score } = verdict;
1204
+ debugLog("judge", `${verdict.resolvedModel ?? verdict.model} · ${formatDuration(ms())} · score ${score.toFixed(2)} ${score >= verdict.threshold ? "≥" : "<"} ${verdict.threshold} · ${verdict.verdict}${retries.length ? ` · ${retries.length} retries` : ""} · reason: P(yes) ${score.toFixed(2)} is ${score >= verdict.threshold ? "at or above" : "below"} the ${verdict.threshold} threshold`);
1205
+ debugTrace("judge", "response", {
1206
+ verdict,
1207
+ retries
1208
+ }, "judge response");
1209
+ a.report({
1210
+ kind: "judge-end",
1211
+ question,
1212
+ verdict: verdict.verdict,
1213
+ score,
1214
+ threshold: verdict.threshold,
1215
+ ms: ms()
1216
+ });
1217
+ return recorded;
1218
+ } catch (error) {
1219
+ if (a.signal.aborted) {
1220
+ a.judgeErrored = true;
1221
+ throw error;
1222
+ }
1223
+ const { retries } = error;
1224
+ const message = error instanceof Error ? error.message : String(error);
1225
+ return failed(isApiError(error) ? withInputSize(message, state, question) : message, retries);
1226
+ }
1227
+ };
1228
+ return async (question, { on, ...options } = {}) => {
1229
+ const call = on === void 0 ? options : {
1230
+ ...options,
1231
+ on: judgeOn(a.turns, on)
1232
+ };
1233
+ return Object.defineProperty(await ask(question, call), SUBJECT, { value: question });
1234
+ };
1235
+ }
1236
+ //#endregion
1237
+ //#region src/sandbox/index.ts
1238
+ async function createSandbox(descriptor, options) {
1239
+ try {
1240
+ switch (descriptor.kind) {
1241
+ case "fake": return await createFakeSandbox(descriptor, options.env);
1242
+ case "docker": {
1243
+ const { createDockerSandbox } = await import("./docker-DNmMPOBb.js");
1244
+ return await createDockerSandbox(descriptor, options);
1245
+ }
1246
+ }
1247
+ } catch (error) {
1248
+ if (error instanceof InfraError || options.signal.aborted) throw error;
1249
+ const kind = descriptor.kind === "docker" ? descriptor.engine ?? "docker" : descriptor.kind;
1250
+ throw new InfraError(`Failed to create ${kind} sandbox: ${error.message}`);
1251
+ }
1252
+ }
1253
+ //#endregion
1254
+ //#region src/attempt/record.ts
1255
+ function classify(error, signal) {
1256
+ const message = error instanceof Error ? error.message : String(error);
1257
+ if (signal.aborted) return isTimeout(signal.reason) ? {
1258
+ kind: "timeout",
1259
+ message: "The eval timed out"
1260
+ } : {
1261
+ kind: "aborted",
1262
+ message: "The run was cancelled"
1263
+ };
1264
+ if (error instanceof InfraError) return {
1265
+ kind: "infra",
1266
+ message
1267
+ };
1268
+ return {
1269
+ kind: "assertion",
1270
+ message
1271
+ };
1272
+ }
1273
+ /**
1274
+ * Why the attempt failed, into the record: the eval's own timeout, a cancel, an infra error, or an
1275
+ * assertion, which is the judge's when a judge call failed or gave a failing verdict.
1276
+ */
1277
+ function recordFailure(a, error, timedOut) {
1278
+ const { record } = a;
1279
+ record.error = timedOut ? {
1280
+ kind: "timeout",
1281
+ message: "The eval timed out"
1282
+ } : classify(error, a.signal);
1283
+ record.error.message = capMiddle(record.error.message);
1284
+ if (error instanceof PrepareError && !timedOut) {
1285
+ const { class: kind, stage, ref, cause, log, setBy } = error.failure;
1286
+ record.error.prepare = {
1287
+ class: kind,
1288
+ stage,
1289
+ ref,
1290
+ cause,
1291
+ log,
1292
+ setBy: setBy ?? []
1293
+ };
1294
+ }
1295
+ const judgeFailed = a.judgeErrored || record.judgements.some((j) => j.verdict !== "pass");
1296
+ if (record.error.kind === "assertion" && judgeFailed) record.error.kind = "judge";
1297
+ record.outcome = record.error.kind === "infra" ? "infra_error" : "fail";
1298
+ }
1299
+ /**
1300
+ * A process the kernel killed at the sandbox's memory limit makes the attempt an infra error,
1301
+ * whatever its checks said: the limit, not the agent, decided it. Returns the error to fail it with.
1302
+ */
1303
+ async function recordOom(a, sandbox) {
1304
+ const oom = await sandbox?.oomKills?.().catch(() => null);
1305
+ const at = oom && `the ${oom.limit} limit${oom.swap ? ` (+${oom.swap} swap)` : ""}`;
1306
+ if (oom) debugLog("teardown", `oom_kill ${oom.kills} at ${at}`);
1307
+ if (!oom?.kills) return void 0;
1308
+ const message = `out of memory: ${oom.kills === 1 ? "1 process" : `${oom.kills} processes`} killed at ${at}; raise ${sandboxProvider(a.spec.sandbox)}({ memory })`;
1309
+ a.record.error = {
1310
+ kind: "infra",
1311
+ message,
1312
+ resource: {
1313
+ cause: "oom",
1314
+ kills: oom.kills
1315
+ }
1316
+ };
1317
+ a.record.outcome = "infra_error";
1318
+ return new InfraError(message);
1319
+ }
1320
+ /**
1321
+ * The attempt's egress policy and, when its sandbox records it, what it contacted and was refused.
1322
+ * Refused hosts the harness always tries are `expectedBlocked`, summarised in one `-v` line.
1323
+ */
1324
+ function recordEgress(a, sandbox) {
1325
+ const traffic = sandbox?.traffic?.();
1326
+ const expected = expectedBlockedFor(a.spec.harness);
1327
+ const refused = traffic?.blocked ?? [];
1328
+ const isExpected = (h) => !!expected?.hosts.includes(h.host);
1329
+ const expectedBlocked = refused.filter(isExpected);
1330
+ a.record.egress = {
1331
+ policy: a.spec.egress.policy,
1332
+ allowed: allowedHosts(a.spec.egress),
1333
+ recorded: !!traffic,
1334
+ contacted: traffic?.contacted ?? [],
1335
+ blocked: refused.filter((h) => !isExpected(h)),
1336
+ expectedBlocked
1337
+ };
1338
+ if (expected && expectedBlocked.length) {
1339
+ const hosts = expectedBlocked.map((h) => `${h.host}:${h.port} ×${h.connections}`).join(", ");
1340
+ debugLog("gateway", `expected blocks, not logged per request: ${hosts} · ${expected.why}`);
1341
+ }
1342
+ a.record.servedModels = sandbox?.servedModels?.() ?? null;
1343
+ }
1344
+ /** Writes the attempt's record, redacted, and returns what the runner reports. */
1345
+ async function writeRecord(a, stagingDir, id) {
1346
+ const { record, spec } = a;
1347
+ const recordPath = join(stagingDir, "evals", recordFileName(attemptKey(id, spec.cell.key, spec.n)));
1348
+ await mkdir(join(stagingDir, "evals"), { recursive: true });
1349
+ await writeCaptureSidecars(stagingDir, recordFileName(attemptKey(id, spec.cell.key, spec.n)), a.artifactBlobs);
1350
+ const tmp = `${recordPath}.${process.pid}.tmp`;
1351
+ await writeFile(tmp, redactSecrets(JSON.stringify(record, null, 2)));
1352
+ await rename(tmp, recordPath);
1353
+ return {
1354
+ outcome: record.outcome,
1355
+ errorKind: record.error?.kind ?? null,
1356
+ judgements: record.judgements.map((j) => ({
1357
+ verdict: j.verdict,
1358
+ score: j.score,
1359
+ threshold: j.threshold,
1360
+ question: clip(j.question, 200)
1361
+ })),
1362
+ turnErrors: turnErrors(record.turns.map((t) => t.transcript)),
1363
+ ...record.error?.prepare ? { prepare: {
1364
+ ref: record.error.prepare.ref,
1365
+ stage: record.error.prepare.stage
1366
+ } } : {}
1367
+ };
1368
+ }
1369
+ /** `turn N error: <message>` for each turn with an error event. */
1370
+ function turnErrors(transcripts) {
1371
+ return transcripts.flatMap((events, i) => {
1372
+ const found = events.find((e) => e.type === "error");
1373
+ return found?.type === "error" ? [`turn ${i + 1} error: ${redactSecrets(clip(found.message.trim(), 500))}`] : [];
1374
+ });
1375
+ }
1376
+ //#endregion
1377
+ //#region src/sandbox/phases.ts
1378
+ const strip = ({ phase: _, ...sub }) => sub;
1379
+ /**
1380
+ * Which brokered secrets the gateway swaps in when. `to: 'checks'` secrets are active only while
1381
+ * at least one check or setup step is running, so a request the agent makes between them gets
1382
+ * the placeholder.
1383
+ */
1384
+ function brokerPhases(box, subs) {
1385
+ const agent = subs.filter((s) => s.phase === "agent").map(strip);
1386
+ const all = subs.map(strip);
1387
+ const perPhase = box.setSubstitutions && all.length > agent.length;
1388
+ let running = 0;
1389
+ return {
1390
+ async start() {
1391
+ if (all.length > 0) await box.setSubstitutions?.(agent);
1392
+ },
1393
+ async checks(fn) {
1394
+ if (!perPhase) return fn();
1395
+ if (running === 0) await box.setSubstitutions?.(all);
1396
+ running++;
1397
+ try {
1398
+ return await fn();
1399
+ } finally {
1400
+ if (--running === 0) await box.setSubstitutions?.(agent);
1401
+ }
1402
+ }
1403
+ };
1404
+ }
1405
+ //#endregion
1406
+ //#region src/attempt/state.ts
1407
+ /** Extra time an attempt gets, outside its eval timeout, when its image may need building. */
1408
+ const SETUP_BUDGET_MS = 12e5;
1409
+ function createAttempt(spec, signal, progress, start) {
1410
+ const record = {
1411
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1412
+ outcome: "pass",
1413
+ error: null,
1414
+ turns: [],
1415
+ diff: null,
1416
+ checks: [],
1417
+ judgements: [],
1418
+ timings: {
1419
+ setupMs: 0,
1420
+ agentMs: 0,
1421
+ diffMs: 0,
1422
+ checksMs: 0,
1423
+ judgeMs: 0
1424
+ },
1425
+ judgeInputs: []
1426
+ };
1427
+ let starting;
1428
+ let ended = false;
1429
+ const a = {
1430
+ spec,
1431
+ record,
1432
+ turns: [],
1433
+ start(who) {
1434
+ if (!starting && ended) return Promise.reject(/* @__PURE__ */ new Error(`${who} came after the eval's body returned`));
1435
+ starting ??= start(a, who).catch((error) => {
1436
+ a.startFailure = { error };
1437
+ throw error;
1438
+ });
1439
+ return starting;
1440
+ },
1441
+ async endStarts() {
1442
+ ended = true;
1443
+ await starting?.catch(() => {});
1444
+ },
1445
+ report: (update) => progress({
1446
+ at: Date.now(),
1447
+ ...update
1448
+ }),
1449
+ async timed(phase, fn) {
1450
+ const start = performance.now();
1451
+ try {
1452
+ return await fn();
1453
+ } finally {
1454
+ record.timings[phase] += Math.round(performance.now() - start);
1455
+ }
1456
+ },
1457
+ signal,
1458
+ judgeErrored: false,
1459
+ judgeErrors: [],
1460
+ captureNames: /* @__PURE__ */ new Set(),
1461
+ backgrounds: [],
1462
+ artifactBlobs: /* @__PURE__ */ new Map()
1463
+ };
1464
+ return a;
1465
+ }
1466
+ const startTimeoutMs = (harness) => parseDuration(harness.startTimeout ?? "90s");
1467
+ /**
1468
+ * Setup steps and the harness's start (once retried) run outside the eval timeout, so the
1469
+ * attempt's own limit makes room for them.
1470
+ */
1471
+ function setupAllowance(planned) {
1472
+ const setupMs = planned.environment.layers.flatMap((l) => l.spec.setup ?? []).reduce((ms, step) => ms + parseDuration((typeof step === "string" ? void 0 : step.timeout) ?? "5m"), 0);
1473
+ const { sandbox } = planned;
1474
+ return setupMs + (sandbox.kind === "docker" && !!(sandbox.from || sandbox.dockerfile || sandbox.install?.length) ? SETUP_BUDGET_MS : 0) + 2 * startTimeoutMs(planned.harness);
1475
+ }
1476
+ /** The eval's timer, paused while the sandbox and its setup steps start. */
1477
+ function evalTimer(ms) {
1478
+ const controller = new AbortController();
1479
+ let left = ms;
1480
+ let since = Date.now();
1481
+ let timer;
1482
+ const fire = () => controller.abort(new DOMException(`Eval timed out after ${ms / 1e3}s`, "TimeoutError"));
1483
+ const start = () => {
1484
+ since = Date.now();
1485
+ timer = setTimeout(fire, Math.max(0, left));
1486
+ };
1487
+ start();
1488
+ return {
1489
+ signal: controller.signal,
1490
+ pause() {
1491
+ clearTimeout(timer);
1492
+ left -= Date.now() - since;
1493
+ },
1494
+ resume: start,
1495
+ clear: () => clearTimeout(timer)
1496
+ };
1497
+ }
1498
+ //#endregion
1499
+ //#region src/attempt/session.ts
1500
+ const tailOf = (text) => text.trim().split("\n").slice(-10).join("\n");
1501
+ /** Setup steps in order, each with its own timeout; a failure is an infra error. */
1502
+ async function runSetup(a, box, env) {
1503
+ const { record, spec } = a;
1504
+ const steps = env.setup;
1505
+ record.setup = [];
1506
+ for (const [i, step] of steps.entries()) {
1507
+ const what = `setup step ${i + 1}/${steps.length} "${clip(step.run, 80)}" in environment ${spec.environment.name}`;
1508
+ const start = performance.now();
1509
+ const stepSignal = AbortSignal.any([a.signal, AbortSignal.timeout(step.timeoutMs)]);
1510
+ const r = await asCheck(a, () => box.exec(step.run, {
1511
+ signal: stepSignal,
1512
+ env: env.checkVars,
1513
+ ...step.cwd ? { cwd: step.cwd } : {}
1514
+ })).catch((error) => {
1515
+ if (a.signal.aborted) throw error;
1516
+ if (!stepSignal.aborted) throw new InfraError(`${what} couldn't run: ${error instanceof Error ? error.message : error}`);
1517
+ return null;
1518
+ });
1519
+ const durationMs = Math.round(performance.now() - start);
1520
+ const output = r ? [r.stdout, r.stderr].filter(Boolean).join("\n") : "";
1521
+ record.setup.push({
1522
+ run: step.run,
1523
+ exitCode: r?.exitCode ?? null,
1524
+ durationMs,
1525
+ output
1526
+ });
1527
+ debugLog("env", `setup ${i + 1}/${steps.length} $ ${clip(step.run, 120)} → ${r ? `exit ${r.exitCode}` : `timed out after ${formatDuration(step.timeoutMs)}`} · ${formatDuration(durationMs)}${step.cwd ? ` · in ${step.cwd}` : ""}`);
1528
+ debugTrace("env", "setup", {
1529
+ run: step.run,
1530
+ cwd: step.cwd,
1531
+ exitCode: r?.exitCode ?? null,
1532
+ durationMs,
1533
+ stdout: r?.stdout,
1534
+ stderr: r?.stderr
1535
+ }, `setup ${i + 1} output`);
1536
+ if (r?.exitCode === 0) continue;
1537
+ throw new InfraError(r ? `${what} exited ${r.exitCode} after ${formatDuration(durationMs)} (see setup.log):\n${tailOf(r.stderr || r.stdout)}` : `${what} timed out after ${formatDuration(step.timeoutMs)} (see setup.log)`);
1538
+ }
1539
+ }
1540
+ const WORKSPACE_TAR = ".reeval-workspace.tar";
1541
+ /** Unpacks the git workspace's tarball into the (empty) workspace. */
1542
+ async function copyWorkspace(box, workspace, signal) {
1543
+ const start = performance.now();
1544
+ const tar = await readFile(workspace.tar);
1545
+ await box.writeFile(WORKSPACE_TAR, tar);
1546
+ const r = await box.exec(`tar -xf ${WORKSPACE_TAR} && rm -f ${WORKSPACE_TAR}`, { signal });
1547
+ if (r.exitCode !== 0) throw new InfraError(`Couldn't unpack the workspace ${workspace.label} into the sandbox (the image needs tar): ${tailOf(r.stderr || r.stdout)}`);
1548
+ if (workspace.init) {
1549
+ const at = "2000-01-01T00:00:00Z";
1550
+ const init = await box.exec(`git init -q -b main && git add -A && git -c commit.gpgsign=false commit -q --no-verify -m ${shellQuote(`${workspace.label} at ${workspace.commit}`)}`, {
1551
+ signal,
1552
+ env: {
1553
+ GIT_AUTHOR_NAME: "reeval",
1554
+ GIT_AUTHOR_EMAIL: "reeval@localhost",
1555
+ GIT_AUTHOR_DATE: at,
1556
+ GIT_COMMITTER_NAME: "reeval",
1557
+ GIT_COMMITTER_EMAIL: "reeval@localhost",
1558
+ GIT_COMMITTER_DATE: at
1559
+ }
1560
+ });
1561
+ if (init.exitCode !== 0) throw new InfraError(`Couldn't give the workspace ${workspace.label} a .git (init: true needs git in the image): ${tailOf(init.stderr || init.stdout)}`);
1562
+ }
1563
+ debugLog("env", `workspace ${workspace.label} at ${workspace.commit.slice(0, 12)} (${tar.byteLength} bytes of tar${workspace.init ? ", with a .git" : ""}) in ${formatDuration(performance.now() - start)}`);
1564
+ }
1565
+ /**
1566
+ * Starts the harness session within its `startTimeout`. A start that doesn't become ready in time
1567
+ * is aborted (and closed, should it finish later) and started once more: a stuck bootstrap is
1568
+ * usually transient, and nothing has run yet, so the retry can't change what the attempt
1569
+ * measures. A second timeout is an infra error; a start that fails outright isn't retried.
1570
+ */
1571
+ async function startHarness(a, harness, box, options) {
1572
+ const { harness: descriptor } = a.spec;
1573
+ const ms = startTimeoutMs(descriptor);
1574
+ const late = `${descriptor.kind}: session did not become ready within ${formatDuration(ms)}`;
1575
+ for (let tries = 1;; tries++) {
1576
+ const stop = new AbortController();
1577
+ let timer;
1578
+ const timedOut = new Promise((resolve) => {
1579
+ timer = setTimeout(() => resolve(null), ms);
1580
+ });
1581
+ const starting = harness.start(box, AbortSignal.any([a.signal, stop.signal]), options);
1582
+ const session = await Promise.race([starting, timedOut]).finally(() => clearTimeout(timer));
1583
+ if (session) return session;
1584
+ stop.abort(new InfraError(late));
1585
+ starting.then((s) => s.close().catch(() => {}), () => {});
1586
+ a.signal.throwIfAborted();
1587
+ if (tries === 2) throw new InfraError(`${late} (tried twice)`);
1588
+ a.record.startRetries = [{
1589
+ error: late,
1590
+ waitedMs: ms
1591
+ }];
1592
+ debugLog("harness", `${late}; starting it once more`);
1593
+ }
1594
+ }
1595
+ /**
1596
+ * Starts the sandbox (with the brokered secrets its gateway swaps in), writes the
1597
+ * environment's files and workspace skills, runs its setup steps, snapshots the workspace for
1598
+ * the diff, and starts the harness session with the harness-placed skills and MCP servers.
1599
+ */
1600
+ async function startSession(a, runId) {
1601
+ const startedAt = performance.now();
1602
+ const { spec } = a;
1603
+ const env = a.env;
1604
+ const box = await createSandbox(spec.sandbox, {
1605
+ runId,
1606
+ timeoutMs: spec.timeoutMs + setupAllowance(spec) + 6e4,
1607
+ signal: a.signal,
1608
+ env: env.vars,
1609
+ image: a.image,
1610
+ brokering: brokeringFor(spec.harness),
1611
+ expectedBlocked: expectedBlockedFor(spec.harness)?.hosts,
1612
+ substitutions: env.substitutions,
1613
+ egress: spec.egress
1614
+ });
1615
+ a.sandbox = box;
1616
+ if (spec.sandbox.kind === "fake") debugLog("container", `fake sandbox at ${box.workdir}`);
1617
+ a.phases = brokerPhases(box, env.substitutions);
1618
+ if (a.signal.aborted) box.destroy();
1619
+ a.signal.throwIfAborted();
1620
+ await a.phases.start();
1621
+ if (env.substitutions.length) {
1622
+ const agent = env.substitutions.filter((s) => s.phase === "agent").map((s) => s.name);
1623
+ debugLog("broker", `gateway swaps in ${agent.join(", ") || "nothing"} for the agent`);
1624
+ }
1625
+ if (env.workspace) await copyWorkspace(box, env.workspace, a.signal);
1626
+ for (const f of env.files) {
1627
+ await box.writeFile(f.path, f.content);
1628
+ const size = typeof f.content === "string" ? Buffer.byteLength(f.content) : f.content.byteLength;
1629
+ debugLog("env", `file ${f.path} (${size} bytes${f.executable ? ", executable" : ""})`);
1630
+ }
1631
+ const executable = env.files.flatMap((f) => f.executable ? [f.path.startsWith("~/") ? `${box.home}/${f.path.slice(2)}` : f.path] : []);
1632
+ if (executable.length) {
1633
+ const paths = executable.map((p) => shellQuote(p.startsWith("-") ? `./${p}` : p));
1634
+ const r = await box.exec(`chmod +x ${paths.join(" ")}`, { signal: a.signal });
1635
+ if (r.exitCode !== 0) throw new InfraError(`Couldn't make ${executable.join(", ")} executable: ${tailOf(r.stderr || r.stdout)}`);
1636
+ }
1637
+ const skillDir = workspaceSkillDir(spec.harness);
1638
+ for (const skill of env.workspaceSkills) {
1639
+ const files = skillFiles(skill);
1640
+ for (const f of files) await box.writeFile(`${skillDir}/${skill.name}/${f.path}`, f.content);
1641
+ debugLog("env", `skill ${skill.name} → ${skillDir}/${skill.name}/ (${files.length} files, workspace)`);
1642
+ }
1643
+ const layout = layoutOf(spec.harness);
1644
+ for (const skill of env.skills) debugLog("env", `skill ${skill.name} → the harness adapter places it (~/${layout.harnessSkillDir}/${skill.name}/ for ${spec.harness.kind === "fake" ? `the fake ${spec.harness.layout ?? "cursor"} layout` : spec.harness.kind})`);
1645
+ for (const [name, server] of Object.entries(env.mcp)) debugLog("env", `mcp ${name} (${server.transport}${server.transport === "stdio" ? `: ${server.command} ${server.args.join(" ")}` : `: ${server.url}`}) → the harness adapter writes its MCP config`);
1646
+ await runSetup(a, box, env);
1647
+ await snapshot(box, a.signal);
1648
+ debugLog("env", "workspace snapshot taken: the diff starts here");
1649
+ const harness = await createHarness(spec.harness);
1650
+ const harnessStart = performance.now();
1651
+ debugLog("harness", `starting ${harnessId(spec.harness)}: bootstrap and session`);
1652
+ const session = await startHarness(a, harness, box, {
1653
+ skills: env.skills,
1654
+ workspaceSkills: env.workspaceSkills.map((s) => s.name),
1655
+ mcp: env.mcp
1656
+ });
1657
+ debugLog("harness", `${harnessId(spec.harness)} session ready in ${formatDuration(performance.now() - harnessStart)}`);
1658
+ a.session = session;
1659
+ const startMs = performance.now() - startedAt;
1660
+ debugLog("container", `sandbox and session started in ${formatDuration(startMs)}`);
1661
+ a.report({
1662
+ kind: "sandbox-ready",
1663
+ ms: Math.round(a.record.timings.setupMs + startMs)
1664
+ });
1665
+ return [box, session];
1666
+ }
1667
+ //#endregion
1668
+ //#region src/attempt/turns.ts
1669
+ /** How long a turn gets to stop once its attempt is aborted, before reeval stops waiting for it. */
1670
+ const TURN_STOP_GRACE_MS = 3e3;
1671
+ /**
1672
+ * Resolves `grace` ms after `signal` aborts, with the turn as cut off; `cancel` drops the timer
1673
+ * once the turn settles on its own.
1674
+ */
1675
+ function cutOff(signal, startedAt, grace = TURN_STOP_GRACE_MS) {
1676
+ let timer;
1677
+ let onAbort = () => {};
1678
+ const cutoff = new Promise((resolve) => {
1679
+ onAbort = () => {
1680
+ timer = setTimeout(() => {
1681
+ const message = `The agent didn't stop within ${grace / 1e3}s of being cancelled, so reeval stopped waiting for its turn`;
1682
+ const at = Date.now() - Date.parse(startedAt);
1683
+ resolve({
1684
+ text: "",
1685
+ status: "aborted",
1686
+ transcript: [{
1687
+ type: "error",
1688
+ message,
1689
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1690
+ atMs: at
1691
+ }],
1692
+ usage: null,
1693
+ model: null,
1694
+ unreported: [],
1695
+ startedAt,
1696
+ durationMs: at
1697
+ });
1698
+ }, grace);
1699
+ };
1700
+ if (signal.aborted) onAbort();
1701
+ else signal.addEventListener("abort", onAbort, { once: true });
1702
+ });
1703
+ const cancel = () => {
1704
+ clearTimeout(timer);
1705
+ signal.removeEventListener("abort", onAbort);
1706
+ };
1707
+ return {
1708
+ cutoff,
1709
+ cancel
1710
+ };
1711
+ }
1712
+ /**
1713
+ * One turn. Sends the prompt to the started session, records the turn as it goes (an
1714
+ * errored entry first, so a turn that throws is still in the record), takes the agent's context
1715
+ * after the first, and diffs the workspace since the snapshot.
1716
+ */
1717
+ function turnSender(a, takeContext, checks) {
1718
+ return async (prompt) => {
1719
+ const [box, agent] = await a.start("c.send()");
1720
+ const entry = {
1721
+ prompt,
1722
+ text: "",
1723
+ status: "error",
1724
+ transcript: [],
1725
+ usage: null,
1726
+ model: null,
1727
+ unreported: [],
1728
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1729
+ durationMs: 0
1730
+ };
1731
+ a.record.turns.push(entry);
1732
+ const turnNumber = a.record.turns.length;
1733
+ a.report({
1734
+ kind: "turn-start",
1735
+ turn: turnNumber
1736
+ });
1737
+ debugLog("harness", `turn ${turnNumber} started: ${JSON.stringify(clip(prompt, 160))}`);
1738
+ debugTrace("harness", "prompt", {
1739
+ turn: turnNumber,
1740
+ prompt
1741
+ }, `turn ${turnNumber} prompt`);
1742
+ const { cutoff, cancel } = cutOff(a.signal, entry.startedAt);
1743
+ const sendStarted = Date.now();
1744
+ const { raw, ...result } = await a.timed("agentMs", () => Promise.race([agent.send(prompt, a.signal), cutoff])).finally(cancel);
1745
+ const sendWallMs = Date.now() - sendStarted;
1746
+ Object.assign(entry, result, raw ? { raw } : {}, { durationMs: Math.max(result.durationMs, sendWallMs) });
1747
+ for (const event of result.transcript) debugTrace("harness", "transcript", {
1748
+ turn: turnNumber,
1749
+ ...event
1750
+ }, `transcript ${event.type}`);
1751
+ await takeContext(box, a.signal);
1752
+ const count = (...types) => result.transcript.filter((e) => types.includes(e.type)).length;
1753
+ const failure = result.transcript.find((e) => e.type === "error");
1754
+ a.report({
1755
+ kind: "turn-end",
1756
+ turn: turnNumber,
1757
+ status: result.status,
1758
+ ms: result.durationMs,
1759
+ tools: count("tool", "web-search", "web-fetch"),
1760
+ fileEdits: count("file-edit"),
1761
+ ...failure?.type === "error" ? { error: redactSecrets(clip(failure.message.trim(), 300)) } : {}
1762
+ });
1763
+ const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
1764
+ debugLog("harness", `turn ${turnNumber} ${result.status} in ${formatDuration(result.durationMs)} · ${plural(count("tool", "web-search", "web-fetch"), "tool call")} · ${plural(count("file-edit"), "file edit")}${failure && "message" in failure ? ` · error: ${clip(String(failure.message), 200)}` : ""}${result.model ? ` · model ${result.model}` : ""}`);
1765
+ a.signal.throwIfAborted();
1766
+ const diff = await a.timed("diffMs", () => diffSinceSnapshot(box, a.signal));
1767
+ a.record.diff = diff;
1768
+ const data = {
1769
+ prompt,
1770
+ ...result,
1771
+ diff
1772
+ };
1773
+ const turn = Object.freeze({
1774
+ ...data,
1775
+ ...checks
1776
+ });
1777
+ a.turns.push(turn);
1778
+ return turn;
1779
+ };
1780
+ }
1781
+ //#endregion
1782
+ //#region src/attempt/index.ts
1783
+ const TRACE_BUFFER_DEFAULT = 8 * 2 ** 20;
1784
+ async function runAttempt(spec, fn, handle) {
1785
+ const ctx = getRunContext();
1786
+ const { config, runId } = ctx;
1787
+ const verbosity = ctx.verbosity ?? 0;
1788
+ const id = evalId(relativeFile(config.root, handle.file), [...spec.groups, spec.name]);
1789
+ const debug = new AttemptDebug({
1790
+ dir: attemptDebugDir(config.root, runId, id, spec.cell.key, spec.n),
1791
+ verbosity,
1792
+ traceLimit: config.debug?.traceBufferBytes ?? TRACE_BUFFER_DEFAULT,
1793
+ ...verbosity > 0 ? { emit: handle.debug } : {}
1794
+ });
1795
+ return withDebug(debug, () => attemptBody(spec, fn, handle, id, debug));
1796
+ }
1797
+ async function attemptBody(spec, fn, { signal: testSignal, setMeta, progress, unawaited, expectError, assertions }, id, debug) {
1798
+ const ctx = getRunContext();
1799
+ const { runId, stagingDir, config } = ctx;
1800
+ tracePlan(debug, spec, ctx);
1801
+ let timer;
1802
+ let clock;
1803
+ const a = createAttempt(spec, testSignal, progress, async (a, who) => {
1804
+ debug.log("container", `starting the sandbox and session for ${who}`);
1805
+ clock?.pause();
1806
+ try {
1807
+ return await a.timed("setupMs", () => startSession(a, runId));
1808
+ } finally {
1809
+ clock?.resume();
1810
+ }
1811
+ });
1812
+ const destroy = () => void a.sandbox?.destroy();
1813
+ testSignal.addEventListener("abort", destroy, { once: true });
1814
+ const takeContext = contextTaker(a);
1815
+ const { exec, readFile, writeFile } = createChecks(a);
1816
+ const c = {
1817
+ get signal() {
1818
+ return a.signal;
1819
+ },
1820
+ turns: a.turns,
1821
+ exec,
1822
+ readFile,
1823
+ writeFile,
1824
+ serve: (command, port) => serveCommand(a, command, port),
1825
+ capture: (target, options) => capturePage(a, target, options),
1826
+ attempt: {
1827
+ id: attemptId(spec.cell.key, spec.n),
1828
+ cell: spec.cell,
1829
+ n: spec.n
1830
+ },
1831
+ send: turnSender(a, takeContext, {
1832
+ exec,
1833
+ readFile
1834
+ }),
1835
+ judge: judgeCaller(a, readFile)
1836
+ };
1837
+ const timedOut = () => !!timer?.aborted && !testSignal.aborted;
1838
+ let failure;
1839
+ let oom;
1840
+ try {
1841
+ a.report({ kind: "sandbox" });
1842
+ const where = `${id} [${attemptId(spec.cell.key, spec.n)}]`;
1843
+ await a.timed("setupMs", () => prepareEnvironment(a, config.root, runId, where, testSignal));
1844
+ clock = evalTimer(spec.timeoutMs);
1845
+ timer = clock.signal;
1846
+ a.signal = AbortSignal.any([testSignal, timer]);
1847
+ timer.addEventListener("abort", destroy, { once: true });
1848
+ await fn(c).catch((error) => {
1849
+ throw a.startFailure ? a.startFailure.error : error;
1850
+ });
1851
+ a.signal.throwIfAborted();
1852
+ const missed = await unawaited() ?? softFailure(assertions()) ?? expectError();
1853
+ if (a.startFailure) throw a.startFailure.error;
1854
+ if (missed) throw missed;
1855
+ const [only, ...more] = a.judgeErrors;
1856
+ if (only) throw new Error(more.length ? `${more.length + 1} judge calls failed:\n${a.judgeErrors.join("\n")}` : only);
1857
+ } catch (error) {
1858
+ recordFailure(a, error, timedOut());
1859
+ failure = { error: timedOut() ? new TimeoutError(`Eval timed out after ${spec.timeoutMs / 1e3}s`) : error };
1860
+ } finally {
1861
+ if (!a.signal.aborted) await unawaited().catch(() => {});
1862
+ clock?.clear();
1863
+ await a.endStarts();
1864
+ for (const bg of a.backgrounds) await bg.kill().catch(() => {});
1865
+ const { sandbox } = a;
1866
+ if (sandbox && !a.signal.aborted) await takeContext(sandbox, AbortSignal.timeout(3e4));
1867
+ if (sandbox) a.report({ kind: "cleanup" });
1868
+ testSignal.removeEventListener("abort", destroy);
1869
+ const teardown = performance.now();
1870
+ await a.session?.close().catch(() => {});
1871
+ if (a.session) debug.log("teardown", "harness session closed");
1872
+ recordEgress(a, sandbox);
1873
+ oom = a.signal.aborted ? void 0 : await recordOom(a, sandbox);
1874
+ await sandbox?.destroy();
1875
+ if (sandbox) debug.log("teardown", `sandbox destroyed (${formatDuration(performance.now() - teardown)} for teardown)`);
1876
+ const { record } = a;
1877
+ const checked = assertionRecords(assertions());
1878
+ if (checked.length) record.assertions = checked;
1879
+ for (const x of checked) {
1880
+ const why = x.message ? ` · ${clip(x.message.split("\n")[0] ?? "", 160)}` : "";
1881
+ debug.log("check", `assert ${x.status === "pass" ? "✓" : "✗"} ${x.label} (${x.matcher}, ${x.status})${why}`);
1882
+ }
1883
+ const error = record.error ? ` · ${record.error.kind}: ${record.error.message}` : "";
1884
+ debug.log("teardown", `outcome ${record.outcome}${error}`);
1885
+ const rel = (path) => relativeFile(config.root, path) || ".";
1886
+ const scratch = `.reeval/runs/${runId}`;
1887
+ debug.log("teardown", `record ${scratch}.json · artifacts ${scratch}/${evalSlug(id)}/${evalSlug(spec.cell.key)}/${spec.n} · debug ${rel(debug.dir)}`);
1888
+ setMeta(await writeRecord(a, stagingDir, id));
1889
+ await debug.finish(debug.verbosity === 2 || record.outcome !== "pass");
1890
+ }
1891
+ if (oom) throw oom;
1892
+ if (failure) throw failure.error;
1893
+ }
1894
+ //#endregion
1895
+ //#region src/contract/declaration.ts
1896
+ /** A node's options: any object, until the cascade checks it. Its JSON Schema is the type's. */
1897
+ const evalOptionsDataSchema = z.record(z.string(), z.unknown());
1898
+ const declaredModeSchema = z.enum([
1899
+ "run",
1900
+ "skip",
1901
+ "only"
1902
+ ]).describe("How a group or eval was declared: `group`/`evaluate`, `.skip` or `.only`.");
1903
+ const declaredFamilySchema = z.strictObject({
1904
+ name: z.string().describe("The name template passed to `evaluate.each`."),
1905
+ case: z.number().int().min(0).describe("The case's index, from 0."),
1906
+ cases: z.number().int().min(2).describe("How many cases the list has.")
1907
+ }).describe("Where a case of a pooled `evaluate.each` sits in its list.");
1908
+ const declaredTodoSchema = z.strictObject({
1909
+ type: z.literal("todo"),
1910
+ name: z.string().describe("The name passed to `.todo`.")
1911
+ }).describe("`evaluate.todo` or `group.todo`.");
1912
+ const declaredEvalSchema = z.strictObject({
1913
+ type: z.literal("eval"),
1914
+ name: z.string().describe("The eval's name, as passed to `evaluate` (a case's, for `evaluate.each`)."),
1915
+ mode: declaredModeSchema,
1916
+ options: evalOptionsDataSchema.describe("The eval's own options, before the cascade."),
1917
+ body: z.string().describe("Names the eval's body to whoever runs it."),
1918
+ family: declaredFamilySchema.optional().describe("Set on each case of a pooled `evaluate.each`: its template, index and the list's length.")
1919
+ }).describe("An `evaluate`: one eval, planned into a test per attempt.");
1920
+ const declaredGroupSchema = z.strictObject({
1921
+ type: z.literal("group"),
1922
+ name: z.string().describe("The group's name, as passed to `group`."),
1923
+ mode: declaredModeSchema,
1924
+ options: evalOptionsDataSchema.describe("The group's own options, before the cascade."),
1925
+ get children() {
1926
+ return z.array(declaredNodeSchema);
1927
+ }
1928
+ }).describe("A `group`: options its evals and groups inherit, and its children in declaration order.");
1929
+ const declaredNodeSchema = z.discriminatedUnion("type", [
1930
+ declaredEvalSchema,
1931
+ declaredGroupSchema,
1932
+ declaredTodoSchema
1933
+ ]).describe("One node of the tree: a group, an eval or a todo.");
1934
+ const declarationTreeSchema = z.strictObject({ children: z.array(declaredNodeSchema) }).describe("An eval file's top-level groups, evals and todos, in declaration order.");
1935
+ const NODE_SCHEMAS = {
1936
+ eval: declaredEvalSchema,
1937
+ group: declaredGroupSchema,
1938
+ todo: declaredTodoSchema
1939
+ };
1940
+ /**
1941
+ * What's wrong with a declaration tree's shape, or undefined: the field, by its path from the
1942
+ * tree's `children`. Options are left to the cascade.
1943
+ */
1944
+ const declarationProblem = (children) => dataProblem(declarationTreeSchema, { children }, "The declaration tree", (node) => Object.keys(NODE_SCHEMAS[String(node.type)]?.shape ?? {}));
1945
+ //#endregion
1946
+ //#region src/plan-eval.ts
1947
+ /** Lets the attempt's own eval timer fire before the engine's backstop, so the message and cleanup are its own. */
1948
+ const GRACE_MS = 5e3;
1949
+ function configLevel(config) {
1950
+ return {
1951
+ harness: config.harness,
1952
+ sandbox: config.sandbox,
1953
+ environment: config.environment,
1954
+ compare: void 0,
1955
+ runs: config.runs,
1956
+ gate: config.gate,
1957
+ env: config.env,
1958
+ egress: config.egress !== void 0 ? { inherited: config.egress } : {},
1959
+ judge: config.judge,
1960
+ capture: config.capture,
1961
+ question: config.question,
1962
+ timeout: void 0,
1963
+ tags: [],
1964
+ pin: [],
1965
+ sources: {}
1966
+ };
1967
+ }
1968
+ /**
1969
+ * The tests an eval registers, or `null` when the run's tags leave it out (it isn't counted in
1970
+ * `order`). Throws a UsageError for options the plan refuses.
1971
+ */
1972
+ function planEval(declaration, ctx) {
1973
+ const { name, mode, groups: enclosing, options: own } = declaration;
1974
+ const groups = enclosing.map((g) => g.name);
1975
+ const where = `evaluate("${[...groups, name].join(" › ")}")`;
1976
+ checkOptions(own, where);
1977
+ const levels = [...enclosing.map((g) => ({
1978
+ options: g.options,
1979
+ where: `group("${g.name}")`
1980
+ })), {
1981
+ options: own,
1982
+ where
1983
+ }];
1984
+ const resolvedEval = cascade(configLevel(ctx.config), levels, where, {
1985
+ profile: ctx.profile,
1986
+ runs: ctx.runs
1987
+ });
1988
+ const { resolved } = resolvedEval;
1989
+ if (!matchesTags(resolved.tags, ctx.tags)) return null;
1990
+ const timeoutMs = resolved.timeout === void 0 ? ctx.config.timeoutMs : parseDuration(resolved.timeout);
1991
+ const plannedAttempts = resolvedEval.skip ? [] : expand(resolved);
1992
+ if (ctx.config.secrets.unbrokered === "error") {
1993
+ const [first] = unbrokered(resolved.env, plannedAttempts);
1994
+ if (first) throw new UsageError(unbrokeredError(first, where, ctx.config.file));
1995
+ }
1996
+ for (const planned of plannedAttempts) {
1997
+ const why = unenforceable(planned.egress, planned.sandbox, where);
1998
+ if (why) throw new UsageError(why);
1999
+ const mcp = refusedMcp(planned.harness, planned.environment.layers);
2000
+ if (mcp) throw new UsageError(`${where} on ${harnessId(planned.harness)}: ${mcp}`);
2001
+ }
2002
+ const path = ctx.file ? relativeFile(ctx.config.root, ctx.file) : null;
2003
+ const rankFile = relativeFile(ctx.config.root, ctx.file ?? "");
2004
+ const family = declaration.family && {
2005
+ id: evalId(rankFile, [...groups, declaration.family.name]),
2006
+ ...declaration.family
2007
+ };
2008
+ const harnessBy = resolved.sources?.harness ?? ctx.config.file;
2009
+ const plan = evalPlan(resolvedEval, plannedAttempts, {
2010
+ configFile: ctx.config.file,
2011
+ file: path
2012
+ });
2013
+ const setByOf = (planned) => ({
2014
+ sandbox: sandboxSetBy(planned, resolved, ctx.config.file),
2015
+ harness: harnessBy
2016
+ });
2017
+ if (resolvedEval.skip) {
2018
+ const [first] = expand(resolved);
2019
+ if (!first) return [];
2020
+ const attempt = {
2021
+ name,
2022
+ groups,
2023
+ tags: resolved.tags,
2024
+ cell: first.cell,
2025
+ n: 1,
2026
+ label: "",
2027
+ harness: first.harness,
2028
+ sandbox: first.sandbox,
2029
+ setBy: setByOf(first),
2030
+ gate: "none",
2031
+ brokered: [],
2032
+ secretVars: [],
2033
+ egress: first.egress,
2034
+ plan,
2035
+ ...family ? { family } : {}
2036
+ };
2037
+ return [{
2038
+ name: `${name} [${attemptId(first.cell.key, 1)}]`,
2039
+ mode: "skip",
2040
+ attempt
2041
+ }];
2042
+ }
2043
+ const cells = [];
2044
+ return plannedAttempts.map((planned) => {
2045
+ if (!cells.includes(planned.cell.key)) cells.push(planned.cell.key);
2046
+ const workspace = workspaceOf(planned.environment.layers);
2047
+ const files = filesOf(planned.environment.layers);
2048
+ const setBy = setByOf(planned);
2049
+ const id = evalId(rankFile, [...groups, name]);
2050
+ const key = attemptKey(id, planned.cell.key, planned.n);
2051
+ const skip = ctx.completed?.includes(key) ? "already completed in this run" : void 0;
2052
+ const attempt = {
2053
+ name,
2054
+ groups,
2055
+ tags: resolved.tags,
2056
+ cell: planned.cell,
2057
+ n: planned.n,
2058
+ label: planned.label,
2059
+ harness: planned.harness,
2060
+ sandbox: planned.sandbox,
2061
+ setBy,
2062
+ gate: planned.gate,
2063
+ ...planned.minLift !== void 0 ? { minLift: planned.minLift } : {},
2064
+ brokered: brokeredIn(resolved.env, planned),
2065
+ secretVars: secretVarsIn(resolved.env, planned),
2066
+ egress: planned.egress,
2067
+ ...workspace ? { workspace } : {},
2068
+ ...files.length ? { files } : {},
2069
+ plan: skip ? {
2070
+ ...plan,
2071
+ skip
2072
+ } : plan,
2073
+ ...skip ? { skip } : {},
2074
+ ...family ? { family } : {}
2075
+ };
2076
+ return {
2077
+ name: `${name} [${attemptId(planned.cell.key, planned.n)}]`,
2078
+ mode: skip ? "skip" : mode,
2079
+ attempt,
2080
+ timeout: skip ? void 0 : timeoutMs + setupAllowance(planned) + GRACE_MS,
2081
+ gate: skip ? void 0 : {
2082
+ limits: attemptLimits(planned, ctx.concurrency),
2083
+ rank: {
2084
+ eval: id,
2085
+ file: rankFile,
2086
+ order: ctx.order,
2087
+ n: planned.n,
2088
+ cell: cells.indexOf(planned.cell.key)
2089
+ }
2090
+ },
2091
+ spec: skip ? void 0 : {
2092
+ ...planned,
2093
+ name,
2094
+ groups,
2095
+ timeoutMs,
2096
+ env: resolved.env,
2097
+ judge: resolved.judge,
2098
+ capture: resolved.capture,
2099
+ sources: {
2100
+ ...resolved.sources,
2101
+ sandbox: setBy.sandbox
2102
+ },
2103
+ notes: resolvedEval.notes
2104
+ }
2105
+ };
2106
+ });
2107
+ }
2108
+ /** The run-wide cap, plus the sandbox provider's and harness's own caps when they set one. */
2109
+ function attemptLimits(planned, concurrency) {
2110
+ const limits = { attempts: concurrency };
2111
+ const { sandbox, harness } = planned;
2112
+ if (sandbox.maxConcurrent) limits[`sandbox:${sandboxProvider(sandbox)}`] = sandbox.maxConcurrent;
2113
+ if (harness.maxConcurrent) limits[`harness:${harness.kind}`] = harness.maxConcurrent;
2114
+ return limits;
2115
+ }
2116
+ //#endregion
2117
+ //#region src/evaluate.ts
2118
+ let groupStack = [];
2119
+ /** Each eval file's declarations as `evaluate` and `group` were called, and each eval's body by id. */
2120
+ const trees = /* @__PURE__ */ new Map();
2121
+ const bodies = /* @__PURE__ */ new Map();
2122
+ /** Options as the tree holds them; from TypeScript they may still hold function forms. */
2123
+ const data = (options) => options;
2124
+ function declare(node) {
2125
+ const parent = groupStack.at(-1)?.node;
2126
+ if (parent) return void parent.children.push(node);
2127
+ const file = collectingFile() ?? "";
2128
+ const tree = trees.get(file) ?? { children: [] };
2129
+ trees.set(file, tree);
2130
+ tree.children.push(node);
2131
+ }
2132
+ const missingIn = (where) => (field, full) => {
2133
+ throw new UsageError(`${field} in ${where}: ${full} doesn't exist. Paths are relative to the file that declares it.`);
2134
+ };
2135
+ /**
2136
+ * The options with a `sandbox` written as data given absolute host paths against `base`, the
2137
+ * eval file, as `docker()` and `fakeSandbox()` resolve theirs against the file that calls them.
2138
+ */
2139
+ function withPaths(options, where, base = callerDir() ?? process.cwd()) {
2140
+ if (typeof options !== "object" || options === null || !("sandbox" in options)) return options;
2141
+ return {
2142
+ ...options,
2143
+ sandbox: sandboxPaths(options.sandbox, base, "sandbox", missingIn(where))
2144
+ };
2145
+ }
2146
+ /** `(name, fn)`, `(name, options, fn)` as in Vitest, or `(name, fn, options)`. */
2147
+ function splitArgs(what, name, a, b) {
2148
+ if (typeof a === "function" && typeof b === "function") throw new UsageError(`${what}("${name}") got two functions. Options must be a plain object; helpers such as secret are only available in defineConfig and defineEnvironment callbacks. Put the secret in an environment (environment: …) or in the config's env.`);
2149
+ if (typeof a === "function") return [b ?? {}, a];
2150
+ if (typeof b === "function") return [a ?? {}, b];
2151
+ throw new UsageError(what === "group" ? `group("${name}") has no function. Pass the group's body, e.g. group("${name}", () => { … }).` : `${what}("${name}") has no function. Pass the eval's body, e.g. ${what}("${name}", async (c) => { … }).`);
2152
+ }
2153
+ /** `evaluate.only` and `group.only` are refused in CI, where they'd silently skip the rest. */
2154
+ function checkOnly(mode, what) {
2155
+ if (mode !== "only" || !inCI()) return;
2156
+ const error = /* @__PURE__ */ new Error(`${what}.only isn't allowed in CI`);
2157
+ error.name = "OnlyInCiError";
2158
+ throw error;
2159
+ }
2160
+ const defineGroup = (mode) => (name, a, b) => {
2161
+ checkOnly(mode, "group");
2162
+ const [written, fn] = splitArgs("group", name, a, b);
2163
+ const options = withPaths(written, `group("${name}")`);
2164
+ checkOptions(options, `group("${name}")`);
2165
+ const node = {
2166
+ type: "group",
2167
+ name,
2168
+ mode,
2169
+ options: data(options),
2170
+ children: []
2171
+ };
2172
+ declare(node);
2173
+ const stack = [...groupStack, {
2174
+ name,
2175
+ options,
2176
+ node
2177
+ }];
2178
+ registerGroup(name, mode, () => {
2179
+ const outer = groupStack;
2180
+ groupStack = stack;
2181
+ try {
2182
+ fn();
2183
+ } finally {
2184
+ groupStack = outer;
2185
+ }
2186
+ });
2187
+ };
2188
+ const group = Object.assign(defineGroup("run"), {
2189
+ skip: defineGroup("skip"),
2190
+ only: defineGroup("only"),
2191
+ todo: (name) => registerTodo(name)
2192
+ });
2193
+ /**
2194
+ * A placeholder, `evaluate.todo` or `group.todo`: one eval entry that never runs, counted as
2195
+ * skipped and shown as todo. The enclosing groups' tags filter it as they do an eval.
2196
+ */
2197
+ function registerTodo(name) {
2198
+ declare({
2199
+ type: "todo",
2200
+ name
2201
+ });
2202
+ const tags = [...new Set(groupStack.flatMap((g) => g.options.tags ?? []))];
2203
+ if (matchesTags(tags, getRunContext().tags)) registerTest(name, { mode: "todo" }, async () => {});
2204
+ }
2205
+ /** The evals declared so far, by file and name path, so a second one with the same id is refused. */
2206
+ const declared = /* @__PURE__ */ new Set();
2207
+ function checkUnique(where, groups, name, hint) {
2208
+ const key = `${collectingFile() ?? ""}\0${[...groups, name].join("\0")}`;
2209
+ if (declared.has(key)) throw new UsageError(`${where} is declared twice in this file. Each eval needs its own name, or its attempts are merged into one eval.${hint}`);
2210
+ declared.add(key);
2211
+ }
2212
+ function register(mode, name, own, fn, hint = "", family) {
2213
+ const groups = groupStack.map((g) => g.name);
2214
+ checkUnique(`evaluate("${[...groups, name].join(" › ")}")`, groups, name, hint);
2215
+ const body = `b${bodies.size + 1}`;
2216
+ bodies.set(body, fn);
2217
+ declare({
2218
+ type: "eval",
2219
+ name,
2220
+ mode,
2221
+ options: data(own),
2222
+ body,
2223
+ ...family ? { family } : {}
2224
+ });
2225
+ const tests = planEval({
2226
+ name,
2227
+ mode,
2228
+ groups: groupStack.map(({ name, options }) => ({
2229
+ name,
2230
+ options
2231
+ })),
2232
+ options: own,
2233
+ ...family ? { family } : {}
2234
+ }, {
2235
+ ...getRunContext(),
2236
+ file: collectingFile(),
2237
+ order: registered
2238
+ });
2239
+ if (!tests) return;
2240
+ registered++;
2241
+ for (const { name: testName, spec, ...options } of tests) registerTest(testName, options, spec ? (handle) => runAttempt(spec, fn, handle) : async () => {});
2242
+ }
2243
+ /** Evals registered so far in this worker, for the scheduler's tie-break. */
2244
+ let registered = 0;
2245
+ const defineEval = (mode) => (name, a, b) => {
2246
+ checkOnly(mode, "evaluate");
2247
+ const [own, fn] = splitArgs("evaluate", name, a, b);
2248
+ register(mode, name, withPaths(own, `evaluate("${name}")`), fn);
2249
+ };
2250
+ /** `$key` is a property of the case, `%s` the case itself, `%#` its index. */
2251
+ function formatName(template, item, index) {
2252
+ return template.replace(/%#/g, String(index)).replace(/%s/g, typeof item === "string" ? item : JSON.stringify(item)).replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (whole, key) => isObjectLike(item) && key in item ? String(item[key]) : whole);
2253
+ }
2254
+ function checkEachOptions(options) {
2255
+ if (options === void 0) return {};
2256
+ if (isObjectLike(options) && Object.keys(options).every((k) => k === "pool")) {
2257
+ const { pool } = options;
2258
+ if (pool === void 0) return {};
2259
+ if (typeof pool === "boolean") return { pool };
2260
+ }
2261
+ throw new UsageError(`evaluate.each(cases, options) takes { pool?: boolean } (got ${JSON.stringify(options)}). Eval options such as compare go after the name: evaluate.each(cases)(name, { compare }, fn).`);
2262
+ }
2263
+ /**
2264
+ * One eval per case, pooled into one suite:
2265
+ * `evaluate.each(cases)('adds $a and $b', async (c, { a, b }) => …)`.
2266
+ */
2267
+ const evalEach = (cases, options) => (name, a, b) => {
2268
+ const { pool = true } = checkEachOptions(options);
2269
+ if (!Array.isArray(cases)) throw new UsageError(`evaluate.each takes an array of cases (got ${typeof cases}), e.g. evaluate.each([{ n: 1 }, { n: 2 }])("case $n", async (c, item) => { … }).`);
2270
+ const [written, fn] = splitArgs("evaluate.each", name, a, b);
2271
+ const own = withPaths(written, `evaluate.each("${name}")`);
2272
+ const hint = ` For evaluate.each, put $key, %s or %# in the name ("${name}") so each case's name differs.`;
2273
+ const pooled = pool && cases.length >= 2;
2274
+ cases.forEach((item, i) => {
2275
+ const family = pooled ? {
2276
+ name,
2277
+ case: i,
2278
+ cases: cases.length
2279
+ } : void 0;
2280
+ register("run", formatName(name, item, i), own, (c) => fn(c, item), hint, family);
2281
+ });
2282
+ };
2283
+ const evaluate = Object.assign(defineEval("run"), {
2284
+ skip: defineEval("skip"),
2285
+ only: defineEval("only"),
2286
+ todo: registerTodo,
2287
+ each: evalEach
2288
+ });
2289
+ /**
2290
+ * Declares a tree's groups and evals as the calls that made it would, in the file being
2291
+ * collected; `bodyFor` gives each eval's body by its id. A tree from another language is
2292
+ * checked against the contract first. Its relative host paths resolve against the directory of
2293
+ * the file being collected, as a TypeScript eval file's do against its own.
2294
+ */
2295
+ function declareTree(nodes, bodyFor) {
2296
+ const problem = declarationProblem(nodes);
2297
+ if (problem) throw new UsageError(problem);
2298
+ const file = collectingFile();
2299
+ declareNodes(nodes, bodyFor, file ? dirname(file) : process.cwd());
2300
+ }
2301
+ function declareNodes(nodes, bodyFor, base) {
2302
+ for (const node of nodes) if (node.type === "todo") registerTodo(node.name);
2303
+ else if (node.type === "group") {
2304
+ const children = node.children;
2305
+ const where = `group("${node.name}")`;
2306
+ defineGroup(node.mode)(node.name, optionsFrom(node.options, where, base), () => declareNodes(children, bodyFor, base));
2307
+ } else if (node.family) {
2308
+ const options = optionsFrom(node.options, `evaluate("${node.name}")`, base);
2309
+ checkOnly(node.mode, "evaluate");
2310
+ register(node.mode, node.name, options, bodyFor(node.body), "", node.family);
2311
+ } else defineEval(node.mode)(node.name, optionsFrom(node.options, `evaluate("${node.name}")`, base), bodyFor(node.body));
2312
+ }
2313
+ /**
2314
+ * Options from their data forms, with host paths absolute against `base`: environments become
2315
+ * `Environment`s again.
2316
+ */
2317
+ function optionsFrom(written, where, base) {
2318
+ refuseIllegalCompare(written, where);
2319
+ const { environment, compare, ...rest } = withPaths(written, where, base);
2320
+ const missing = missingIn(where);
2321
+ const asEnv = (env, field) => environmentFrom(environmentPaths(env, base, field, missing));
2322
+ const own = environment !== void 0 ? { environment: asEnv(environment, "environment") } : {};
2323
+ if (compare === void 0) return {
2324
+ ...rest,
2325
+ ...own
2326
+ };
2327
+ const variants = compare.variants.map((v, i) => asEnv(v, `compare.variants[${i}]`));
2328
+ if (compare.baseline !== void 0) return {
2329
+ ...rest,
2330
+ compare: {
2331
+ ...compare,
2332
+ baseline: asEnv(compare.baseline, "compare.baseline"),
2333
+ variants
2334
+ }
2335
+ };
2336
+ return {
2337
+ ...rest,
2338
+ ...own,
2339
+ compare: {
2340
+ ...compare,
2341
+ variants
2342
+ }
2343
+ };
2344
+ }
2345
+ //#endregion
2346
+ export { assertionRecords as i, evaluate as n, group as r, declareTree as t };
2347
+
2348
+ //# sourceMappingURL=evaluate-DKIl54Hb.js.map