reeval 0.10.0 → 0.12.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 (295) hide show
  1. package/README.md +64 -39
  2. package/THIRD_PARTY_NOTICES.md +75 -106
  3. package/bin/mcp-timer.cjs +224 -0
  4. package/dist/{adapter-DWlpOtRn.js → adapter-DigeVFWC.js} +3 -3
  5. package/dist/{adapter-DWlpOtRn.js.map → adapter-DigeVFWC.js.map} +1 -1
  6. package/dist/{agent-CaS_2Sce.js → agent-CatUEo5h.js} +21 -6
  7. package/dist/{agent-CaS_2Sce.js.map → agent-CatUEo5h.js.map} +1 -1
  8. package/dist/{assemble-B9Alds-z.js → assemble-CF063ms6.js} +19 -17
  9. package/dist/assemble-CF063ms6.js.map +1 -0
  10. package/dist/assemble-CJoD_Rzr.js +1 -0
  11. package/dist/bridge/index.mjs +15 -7
  12. package/dist/{capture-CNb9SuU5.js → capture-BiD7lVaM.js} +26 -18
  13. package/dist/{capture-CNb9SuU5.js.map → capture-BiD7lVaM.js.map} +1 -1
  14. package/dist/capture-Dp3q7K7Y.js +1 -0
  15. package/dist/{cli-D303Vj5N.js → cli-B5zQCR2r.js} +266 -547
  16. package/dist/cli-B5zQCR2r.js.map +1 -0
  17. package/dist/{cli-spec-BPZbQG2p.js → cli-spec-BXIBUlaK.js} +46 -18
  18. package/dist/cli-spec-BXIBUlaK.js.map +1 -0
  19. package/dist/cli.js +2 -2
  20. package/dist/{command-BT9a37ZR.js → command-BMHXHqMT.js} +17 -14
  21. package/dist/command-BMHXHqMT.js.map +1 -0
  22. package/dist/command-C94CQNh8.js +91 -0
  23. package/dist/command-C94CQNh8.js.map +1 -0
  24. package/dist/command-CVtgyqlM.js +335 -0
  25. package/dist/command-CVtgyqlM.js.map +1 -0
  26. package/dist/{command-BLfT7vnQ.js → command-CaZgCTIw.js} +13 -17
  27. package/dist/command-CaZgCTIw.js.map +1 -0
  28. package/dist/{command-CMJtHKRa.js → command-DTgBZR0k.js} +6 -6
  29. package/dist/{command-CMJtHKRa.js.map → command-DTgBZR0k.js.map} +1 -1
  30. package/dist/{commands-1p-bIWhN.js → commands-BXzIDB3X.js} +1 -1
  31. package/dist/commands-CjpGBUWr.js +666 -0
  32. package/dist/commands-CjpGBUWr.js.map +1 -0
  33. package/dist/{config-DEgnUiU-.js → config-Dv5MG7Rp.js} +31 -34
  34. package/dist/config-Dv5MG7Rp.js.map +1 -0
  35. package/dist/{context-C1mxXh80.js → context-Dgn48BqT.js} +13 -2
  36. package/dist/context-Dgn48BqT.js.map +1 -0
  37. package/dist/contrasts-CcoMLC_y.js +101 -0
  38. package/dist/contrasts-CcoMLC_y.js.map +1 -0
  39. package/dist/db-BeqRiWLl.js +602 -0
  40. package/dist/db-BeqRiWLl.js.map +1 -0
  41. package/dist/db-CJDRVQal.js +2 -0
  42. package/dist/{dist-B8hJImYt.js → dist-CMjp3qqF.js} +81 -14
  43. package/dist/dist-CMjp3qqF.js.map +1 -0
  44. package/dist/{docker-sZrNrr82.js → docker-BbTlMmYi.js} +2 -2
  45. package/dist/{docker-sZrNrr82.js.map → docker-BbTlMmYi.js.map} +1 -1
  46. package/dist/{docker-7b_2SaH7.js → docker-CCXdbREk.js} +20 -4
  47. package/dist/{docker-7b_2SaH7.js.map → docker-CCXdbREk.js.map} +1 -1
  48. package/dist/{docker-yauK-9X8.js → docker-DC69dKxB.js} +1 -1
  49. package/dist/{doctor-BYNok4UO.js → doctor-CqttrZcs.js} +56 -22
  50. package/dist/doctor-CqttrZcs.js.map +1 -0
  51. package/dist/{egress-C4PZTSSe.js → egress-BfK_O9ce.js} +435 -31
  52. package/dist/egress-BfK_O9ce.js.map +1 -0
  53. package/dist/{environment-DNjRk0nU.js → environment-BBz1Yes9.js} +225 -12
  54. package/dist/environment-BBz1Yes9.js.map +1 -0
  55. package/dist/{evaluate-D4zeRGBF.js → evaluate-CXQ1o-pd.js} +539 -562
  56. package/dist/evaluate-CXQ1o-pd.js.map +1 -0
  57. package/dist/{evaluate-C1UVL21l.d.ts → evaluate-yjZKUexW.d.ts} +2 -2
  58. package/dist/facts-DtENlf9s.js +898 -0
  59. package/dist/facts-DtENlf9s.js.map +1 -0
  60. package/dist/{files-BXUdY8tr.js → files-B-eUpCZ1.js} +30 -7
  61. package/dist/files-B-eUpCZ1.js.map +1 -0
  62. package/dist/files-BsQbYz2i.js +65 -0
  63. package/dist/files-BsQbYz2i.js.map +1 -0
  64. package/dist/{files-DQmHE7pb.js → files-COHQLEHE.js} +6 -17
  65. package/dist/files-COHQLEHE.js.map +1 -0
  66. package/dist/files-DqCBW7i8.js +1 -0
  67. package/dist/files-Ha-jsQXf.js +1 -0
  68. package/dist/generate-Cs2TMH_k.js +1647 -0
  69. package/dist/generate-Cs2TMH_k.js.map +1 -0
  70. package/dist/{git-DfG-YJ1u.js → git-DhykwgKF.js} +2 -2
  71. package/dist/{git-DfG-YJ1u.js.map → git-DhykwgKF.js.map} +1 -1
  72. package/dist/{harness-B8QC9u0O.js → harness-BAjw1fcr.js} +3 -3
  73. package/dist/{harness-B8QC9u0O.js.map → harness-BAjw1fcr.js.map} +1 -1
  74. package/dist/harness.d.ts +1 -1
  75. package/dist/harness.js +1 -1
  76. package/dist/{header-hK-Z_Po9.js → header-kSmWatcO.js} +10 -3
  77. package/dist/{header-hK-Z_Po9.js.map → header-kSmWatcO.js.map} +1 -1
  78. package/dist/{images-0On0cztu.js → images-BmJRuTpm.js} +61 -13
  79. package/dist/images-BmJRuTpm.js.map +1 -0
  80. package/dist/index.d.ts +3 -3
  81. package/dist/index.js +4 -4
  82. package/dist/init-Bwc9byQr.js +543 -0
  83. package/dist/init-Bwc9byQr.js.map +1 -0
  84. package/dist/init-D7Y1ny1z.js +1 -0
  85. package/dist/insights-B2Xmhz9o.js +64 -0
  86. package/dist/insights-B2Xmhz9o.js.map +1 -0
  87. package/dist/{invocations-DbGfBAA_.js → invocations-CQTbP092.js} +3 -3
  88. package/dist/{invocations-DbGfBAA_.js.map → invocations-CQTbP092.js.map} +1 -1
  89. package/dist/{judge-DW_GDOmB.js → judge-Dt6KW6_O.js} +5 -3
  90. package/dist/judge-Dt6KW6_O.js.map +1 -0
  91. package/dist/judge.d.ts +7 -6
  92. package/dist/judge.js +8 -7
  93. package/dist/judge.js.map +1 -1
  94. package/dist/{live-jlfszAbb.js → live-DYd2QEyR.js} +2 -2
  95. package/dist/{live-jlfszAbb.js.map → live-DYd2QEyR.js.map} +1 -1
  96. package/dist/{model-h_cDtcRr.js → model-B-mel-Gv.js} +46 -20
  97. package/dist/model-B-mel-Gv.js.map +1 -0
  98. package/dist/model-BGKYVpoj.js +1 -0
  99. package/dist/{model-DaVCY3rb.js → model-C7tSwT7f.js} +2 -2
  100. package/dist/{model-DaVCY3rb.js.map → model-C7tSwT7f.js.map} +1 -1
  101. package/dist/{model-oqcDplJG.js → model-CWlAqmbU.js} +150 -46
  102. package/dist/model-CWlAqmbU.js.map +1 -0
  103. package/dist/model-DHY-dfoO.js +1 -0
  104. package/dist/{node-B3ydHo6t.js → node-D0mtjRAO.js} +3 -3
  105. package/dist/{node-B3ydHo6t.js.map → node-D0mtjRAO.js.map} +1 -1
  106. package/dist/{options-Bh2W63dK.js → options-CPUW97_7.js} +9 -12
  107. package/dist/options-CPUW97_7.js.map +1 -0
  108. package/dist/{pairs-CnInRu-G.js → pairs-DP6XOJby.js} +2 -2
  109. package/dist/{pairs-CnInRu-G.js.map → pairs-DP6XOJby.js.map} +1 -1
  110. package/dist/{patch-Zac5aIRM.js → patch-D4oluWzH.js} +2 -2
  111. package/dist/{patch-Zac5aIRM.js.map → patch-D4oluWzH.js.map} +1 -1
  112. package/dist/{plan-C9Cy27Ay.js → plan-OkwUcJQ9.js} +31 -12
  113. package/dist/plan-OkwUcJQ9.js.map +1 -0
  114. package/dist/plugins-CUWMnSNb.js +654 -0
  115. package/dist/plugins-CUWMnSNb.js.map +1 -0
  116. package/dist/{preflight-CPWpQeBI.js → preflight-B6pY89bF.js} +1 -1
  117. package/dist/{preflight-D7iFPKeK.js → preflight-Bjm7_ylE.js} +14 -10
  118. package/dist/preflight-Bjm7_ylE.js.map +1 -0
  119. package/dist/{prepare-doGxaAGR.js → prepare-CQmK3SBn.js} +71 -24
  120. package/dist/prepare-CQmK3SBn.js.map +1 -0
  121. package/dist/prepare-DbdgtZKe.js +2 -0
  122. package/dist/{provenance-DmE9uPVt.js → provenance-B3Y17oCC.js} +2 -2
  123. package/dist/{provenance-DmE9uPVt.js.map → provenance-B3Y17oCC.js.map} +1 -1
  124. package/dist/providers-CXN2RFz-.js +1 -0
  125. package/dist/{providers-tAOD1NQN.js → providers-eoniqK6w.js} +24 -10
  126. package/dist/providers-eoniqK6w.js.map +1 -0
  127. package/dist/query-CLRZmWBT.js +139 -0
  128. package/dist/query-CLRZmWBT.js.map +1 -0
  129. package/dist/query-CmBDYt0_.js +1 -0
  130. package/dist/{questions-DjAwMxZ3.js → questions-1XOulevV.js} +6 -6
  131. package/dist/{questions-DjAwMxZ3.js.map → questions-1XOulevV.js.map} +1 -1
  132. package/dist/{record-BKmMvZ6O.js → record-kk9M1hj-.js} +8 -4
  133. package/dist/record-kk9M1hj-.js.map +1 -0
  134. package/dist/remarks-1521zwfW.js +38 -0
  135. package/dist/remarks-1521zwfW.js.map +1 -0
  136. package/dist/remarks-BhAV1H4V.js +1 -0
  137. package/dist/remarks-DlJCqWRs.js +278 -0
  138. package/dist/remarks-DlJCqWRs.js.map +1 -0
  139. package/dist/{remote-HDRt16ew.js → remote-cE2VWfkJ.js} +3 -3
  140. package/dist/{remote-HDRt16ew.js.map → remote-cE2VWfkJ.js.map} +1 -1
  141. package/dist/resolve-C4zeccCH.js +724 -0
  142. package/dist/resolve-C4zeccCH.js.map +1 -0
  143. package/dist/resolve-D30yZYmB.js +1 -0
  144. package/dist/{results-DuauQI3Z.js → results-Drtt0uFl.js} +33 -7
  145. package/dist/results-Drtt0uFl.js.map +1 -0
  146. package/dist/{resume-BRfa8E_s.js → resume-BEFazUfy.js} +4 -4
  147. package/dist/{resume-BRfa8E_s.js.map → resume-BEFazUfy.js.map} +1 -1
  148. package/dist/rules-BFzQqPIm.js +317 -0
  149. package/dist/rules-BFzQqPIm.js.map +1 -0
  150. package/dist/run-BeMy92Us.js +88 -0
  151. package/dist/run-BeMy92Us.js.map +1 -0
  152. package/dist/{sandbox-D3A8oUb7.js → sandbox-CQaJj7n9.js} +2 -2
  153. package/dist/{sandbox-D3A8oUb7.js.map → sandbox-CQaJj7n9.js.map} +1 -1
  154. package/dist/sandbox.d.ts +1 -1
  155. package/dist/sandbox.js +1 -1
  156. package/dist/server-V4-2w0l8.js +1 -0
  157. package/dist/{server-XDleKaF_.js → server-xK0les4f.js} +8 -8
  158. package/dist/{server-XDleKaF_.js.map → server-xK0les4f.js.map} +1 -1
  159. package/dist/{shared-DYpIb1BI.js → shared-CA61BuPJ.js} +25 -3
  160. package/dist/shared-CA61BuPJ.js.map +1 -0
  161. package/dist/source-BDow_oW8.js +1 -0
  162. package/dist/{source-C8xumIWj.js → source-Q1hEKbCs.js} +2 -2
  163. package/dist/{source-C8xumIWj.js.map → source-Q1hEKbCs.js.map} +1 -1
  164. package/dist/src-B9L9yGKI.js +1 -0
  165. package/dist/{src-ofUaYbuY.js → src-Bcah6ZkT.js} +1 -1
  166. package/dist/{src-DvCg0NBs.js → src-BhmaorOU.js} +28 -6
  167. package/dist/{src-DvCg0NBs.js.map → src-BhmaorOU.js.map} +1 -1
  168. package/dist/{src-CF17Ug08.js → src-C0CeKiLU.js} +19 -4
  169. package/dist/{src-CF17Ug08.js.map → src-C0CeKiLU.js.map} +1 -1
  170. package/dist/src-C0xowZ3C.js +2 -0
  171. package/dist/src-CHIfIKUu.js +2 -0
  172. package/dist/src-DLtyz4w5.js +2 -0
  173. package/dist/src-DPIjmReZ.js +2 -0
  174. package/dist/{src-C_lPtowT.js → src-DXNh5mTr.js} +1 -1
  175. package/dist/{src-FGXhkEkH.js → src-DiLMkIr9.js} +142 -13
  176. package/dist/src-DiLMkIr9.js.map +1 -0
  177. package/dist/{src-B7treWa8.js → src-LekQrr_S.js} +324 -32
  178. package/dist/src-LekQrr_S.js.map +1 -0
  179. package/dist/src-bCnsA6Vp.js +2 -0
  180. package/dist/{src-u8_ixn_F.js → src-cbCb-MPy.js} +20 -4
  181. package/dist/src-cbCb-MPy.js.map +1 -0
  182. package/dist/{src-DvK0EIZx.js → src-qGoFkuJF.js} +55 -6
  183. package/dist/{src-DvK0EIZx.js.map → src-qGoFkuJF.js.map} +1 -1
  184. package/dist/{src-jzagbuqH.js → src-ynBVosfX.js} +1 -1
  185. package/dist/state-BQR_BCm4.js +1 -0
  186. package/dist/{state-C__9swoG.js → state-BmZ8QVZ8.js} +12 -12
  187. package/dist/{state-C__9swoG.js.map → state-BmZ8QVZ8.js.map} +1 -1
  188. package/dist/studio-app/assets/index-CnYJijyI.js +58 -0
  189. package/dist/studio-app/index.html +1 -1
  190. package/dist/suite-BaHPmAGy.js +1 -0
  191. package/dist/suite-C8sw2pox.js +481 -0
  192. package/dist/suite-C8sw2pox.js.map +1 -0
  193. package/dist/summary-0oQNGskv.js +837 -0
  194. package/dist/summary-0oQNGskv.js.map +1 -0
  195. package/dist/testing.d.ts +2 -2
  196. package/dist/testing.js +2 -2
  197. package/dist/{text-gEcKjAyp.js → text-CrVy5ioL.js} +18 -2
  198. package/dist/text-CrVy5ioL.js.map +1 -0
  199. package/dist/{transcript-BNNANU8A.js → transcript-BTxJpW4R.js} +2 -2
  200. package/dist/{transcript-BNNANU8A.js.map → transcript-BTxJpW4R.js.map} +1 -1
  201. package/dist/types-C5vI3g1C.js.map +1 -1
  202. package/dist/{types-g17bRAzt.d.ts → types-GraZtfHN.d.ts} +858 -39
  203. package/dist/usage-BDo2q_yu.js +1 -0
  204. package/dist/{command-Bge8F7Ko.js → validate-BdoXUuL_.js} +35 -383
  205. package/dist/validate-BdoXUuL_.js.map +1 -0
  206. package/dist/view/index.d.ts +20 -9
  207. package/dist/view/index.js +7 -6
  208. package/dist/views-BoUoX4_A.js +125 -0
  209. package/dist/views-BoUoX4_A.js.map +1 -0
  210. package/dist/views-noVQNCUP.js +2 -0
  211. package/dist/{worker-entry-yOchGYYv.js → worker-entry-D0-GErmU.js} +2 -2
  212. package/dist/{worker-entry-yOchGYYv.js.map → worker-entry-D0-GErmU.js.map} +1 -1
  213. package/dist/{workspace-D8sSgpiy.js → workspace-B90YDU_a.js} +1 -1
  214. package/dist/{workspace-CgyHEM0l.js → workspace-tjZFkjxC.js} +4 -4
  215. package/dist/{workspace-CgyHEM0l.js.map → workspace-tjZFkjxC.js.map} +1 -1
  216. package/package.json +10 -8
  217. package/skills/eval-rubric/SKILL.md +130 -0
  218. package/dist/agent-CURAwx_q.js +0 -334
  219. package/dist/agent-CURAwx_q.js.map +0 -1
  220. package/dist/assemble-B9Alds-z.js.map +0 -1
  221. package/dist/assemble-J5MukZFY.js +0 -1
  222. package/dist/capture-jquXZBKK.js +0 -1
  223. package/dist/cli-D303Vj5N.js.map +0 -1
  224. package/dist/cli-spec-BPZbQG2p.js.map +0 -1
  225. package/dist/command-BLfT7vnQ.js.map +0 -1
  226. package/dist/command-BT9a37ZR.js.map +0 -1
  227. package/dist/command-Bge8F7Ko.js.map +0 -1
  228. package/dist/command-BkFjijN2.js +0 -1
  229. package/dist/commands-BFx16WB8.js +0 -225
  230. package/dist/commands-BFx16WB8.js.map +0 -1
  231. package/dist/config-DEgnUiU-.js.map +0 -1
  232. package/dist/context-C1mxXh80.js.map +0 -1
  233. package/dist/db-CdHizc4_.js +0 -1406
  234. package/dist/db-CdHizc4_.js.map +0 -1
  235. package/dist/db-Dm1cRJJP.js +0 -2
  236. package/dist/dist-B8hJImYt.js.map +0 -1
  237. package/dist/doctor-BYNok4UO.js.map +0 -1
  238. package/dist/duration-CEOQww97.js +0 -26
  239. package/dist/duration-CEOQww97.js.map +0 -1
  240. package/dist/egress-C4PZTSSe.js.map +0 -1
  241. package/dist/environment-DNjRk0nU.js.map +0 -1
  242. package/dist/evaluate-D4zeRGBF.js.map +0 -1
  243. package/dist/family-D2b7WD10.js +0 -67
  244. package/dist/family-D2b7WD10.js.map +0 -1
  245. package/dist/files-BXUdY8tr.js.map +0 -1
  246. package/dist/files-BhyltlDB.js +0 -1
  247. package/dist/files-DQmHE7pb.js.map +0 -1
  248. package/dist/files-ZZPqfAo6.js +0 -1
  249. package/dist/images-0On0cztu.js.map +0 -1
  250. package/dist/init-B77Rpcqc.js +0 -360
  251. package/dist/init-B77Rpcqc.js.map +0 -1
  252. package/dist/init-BrOWin3W.js +0 -1
  253. package/dist/judge-DW_GDOmB.js.map +0 -1
  254. package/dist/model-NM3ID0uR.js +0 -1
  255. package/dist/model-h_cDtcRr.js.map +0 -1
  256. package/dist/model-nwWJvaUR.js +0 -1
  257. package/dist/model-oqcDplJG.js.map +0 -1
  258. package/dist/options-Bh2W63dK.js.map +0 -1
  259. package/dist/plan-C9Cy27Ay.js.map +0 -1
  260. package/dist/preflight-D7iFPKeK.js.map +0 -1
  261. package/dist/prepare-BGjePFQl.js +0 -2
  262. package/dist/prepare-doGxaAGR.js.map +0 -1
  263. package/dist/process-CSVO_jsc.js +0 -119
  264. package/dist/process-CSVO_jsc.js.map +0 -1
  265. package/dist/providers-gDMko8fl.js +0 -1
  266. package/dist/providers-tAOD1NQN.js.map +0 -1
  267. package/dist/query-Bes9Kh6Y.js +0 -91
  268. package/dist/query-Bes9Kh6Y.js.map +0 -1
  269. package/dist/query-C_G_svLU.js +0 -1
  270. package/dist/record-BKmMvZ6O.js.map +0 -1
  271. package/dist/results-DuauQI3Z.js.map +0 -1
  272. package/dist/server-C7sg1ndo.js +0 -1
  273. package/dist/shared-DYpIb1BI.js.map +0 -1
  274. package/dist/source-fGd0SaKP.js +0 -1
  275. package/dist/src-B7treWa8.js.map +0 -1
  276. package/dist/src-DNnG4e4R.js +0 -2
  277. package/dist/src-DTbrY1hi.js +0 -2
  278. package/dist/src-DdyJEh2v.js +0 -2
  279. package/dist/src-Dj9PAroc.js +0 -2
  280. package/dist/src-Du8H85Qq.js +0 -2
  281. package/dist/src-FGXhkEkH.js.map +0 -1
  282. package/dist/src-pJZOrYR9.js +0 -1
  283. package/dist/src-u8_ixn_F.js.map +0 -1
  284. package/dist/state-CgjQo29K.js +0 -1
  285. package/dist/studio-app/assets/index-DrQc2Yxd.js +0 -58
  286. package/dist/summary-BLAVLySU.js +0 -385
  287. package/dist/summary-BLAVLySU.js.map +0 -1
  288. package/dist/summary-DgyjUh_l.js +0 -192
  289. package/dist/summary-DgyjUh_l.js.map +0 -1
  290. package/dist/text-gEcKjAyp.js.map +0 -1
  291. package/dist/tools-GCyNYSc1.js +0 -249
  292. package/dist/tools-GCyNYSc1.js.map +0 -1
  293. package/dist/views-BW6oCDUQ.js +0 -2
  294. package/dist/views-BmQF4UCI.js +0 -225
  295. package/dist/views-BmQF4UCI.js.map +0 -1
@@ -1,35 +1,34 @@
1
1
  import { t as isObjectLike } from "./object-CLf2-tNq.js";
2
2
  import { n as UsageError, r as isTimeout, t as TimeoutError } from "./errors-B6BSMs03.js";
3
- import { t as parseDuration } from "./duration-CEOQww97.js";
4
- import { n as dataProblem } from "./shared-DYpIb1BI.js";
5
- 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-Bh2W63dK.js";
6
- 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-DNjRk0nU.js";
7
- 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";
8
- import { l as runJudge, s as judgeModelName, u as isApiError } from "./judge-DW_GDOmB.js";
9
- import { n as badCharacterAt } from "./config-DEgnUiU-.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-CPUW97_7.js";
5
+ import { E as sandboxPaths, a as callerDir, d as environmentFrom, f as environmentPaths, h as globFilter, j as workspaceOf, m as filesOf } from "./environment-BBz1Yes9.js";
6
+ import { c as relativeFile, i as evalSlug, n as attemptKey, r as evalId, s as recordFileName, t as attemptId } from "./ids-Bp6ut7ZD.js";
7
+ import { c as judgeModelName, d as isApiError, u as runJudge } from "./judge-Dt6KW6_O.js";
8
+ import "./config-Dv5MG7Rp.js";
10
9
  import { t as inCI } from "./env-BQqj93gu.js";
11
- import { o as redactSecrets, s as registerSecret } from "./redact-DJ9JCShu.js";
10
+ import { t as MCP_TIMER, ut as shellQuote } from "./src-LekQrr_S.js";
11
+ import { o as redactSecrets } from "./redact-DJ9JCShu.js";
12
+ import { O as pluginSourceLabel, S as pluginBuildId, b as localSkills, c as PluginProblem, d as builtPlugins, f as droppedPaths, h as layeredPlugins, l as buildRefusal, m as fetchesAtStart, n as PLUGIN_DIR, o as PLUGIN_ROOT, p as duplicateLocalPlugin, r as PLUGIN_MANIFEST, s as PLUGIN_ROOT_VAR, v as localPlugin, w as pluginCall } from "./plugins-CUWMnSNb.js";
12
13
  import { t as InfraError } from "./types-C5vI3g1C.js";
13
- 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-C4PZTSSe.js";
14
- import { S as placeholder } from "./src-B7treWa8.js";
15
- import { n as shellQuote, t as run } from "./process-CSVO_jsc.js";
14
+ import { S as writePluginFiles, _ as expectedBlockedFor, a as unenforceable, b as refusedMcp, f as agentMcp, h as claudeMcpServers, m as brokeringFor, n as allowedHosts, o as createFakeSandbox, r as egressLabel, x as refusedPlugins, y as layoutOf } from "./egress-BfK_O9ce.js";
16
15
  import { i as registerTest, n as getRunContext, o as SUBJECT, r as registerGroup, t as collectingFile } from "./runtime-PX_oXiCS.js";
17
16
  import { a as withDebug, i as debugTrace, n as attemptDebug, r as debugLog, t as AttemptDebug } from "./attempt-CGPV3BZY.js";
18
17
  import { t as attemptDebugDir } from "./files-Ca0d6Uq3.js";
19
- import { n as clip, r as formatDuration, t as capMiddle } from "./text-gEcKjAyp.js";
20
- 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-CNb9SuU5.js";
21
- import { t as contextTaker } from "./context-C1mxXh80.js";
18
+ import { i as formatDuration, n as clip, t as capMiddle } from "./text-CrVy5ioL.js";
19
+ 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-BiD7lVaM.js";
20
+ import { t as contextTaker } from "./context-Dgn48BqT.js";
21
+ import { a as skillFiles, i as resolveEnvironment } from "./resolve-C4zeccCH.js";
22
22
  import { t as PrepareError } from "./provider-CmAYTQQs.js";
23
- import { i as preparedWorkspace } from "./workspace-CgyHEM0l.js";
24
- import { n as judgeDiff } from "./patch-Zac5aIRM.js";
25
- import { t as transcriptForJudge } from "./transcript-BNNANU8A.js";
23
+ import { i as preparedWorkspace } from "./workspace-tjZFkjxC.js";
24
+ import { n as judgeDiff } from "./patch-D4oluWzH.js";
25
+ import { t as transcriptForJudge } from "./transcript-BTxJpW4R.js";
26
26
  import { r as withLease } from "./client-fcwgwOAD.js";
27
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
28
- import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
29
- import { parse } from "yaml";
27
+ import { dirname, join } from "node:path";
28
+ import { parse, stringify } from "yaml";
30
29
  import { z } from "zod";
31
30
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
32
- import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
31
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
33
32
  //#region src/attempt/assertions.ts
34
33
  const MESSAGE_LIMIT = 1024;
35
34
  const LABEL_LIMIT = 60;
@@ -136,6 +135,11 @@ function traceEnvironment(debug, spec, env) {
136
135
  setup: env.setup,
137
136
  skills: env.skills.map((s) => s.name),
138
137
  workspaceSkills: env.workspaceSkills.map((s) => s.name),
138
+ plugins: env.plugins.map((p) => ({
139
+ name: p.name,
140
+ parts: p.parts,
141
+ files: p.files.length
142
+ })),
139
143
  mcp: Object.fromEntries(Object.entries(env.mcp).map(([name, m]) => [name, m.transport === "stdio" ? {
140
144
  transport: m.transport,
141
145
  command: m.command,
@@ -155,545 +159,36 @@ function traceEnvironment(debug, spec, env) {
155
159
  }, "resolved environment");
156
160
  }
157
161
  //#endregion
158
- //#region src/environment/resolve.ts
159
- const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
160
- const DEFAULT_SETUP_TIMEOUT = "5m";
161
- function broker(secrets, name, value, hosts, where = ["header"], phase = "agent") {
162
- const at = badCharacterAt(value);
163
- 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.`);
164
- const sub = {
165
- name,
166
- placeholder: placeholder(),
167
- value,
168
- hosts: [...hosts],
169
- in: [...where],
170
- phase
171
- };
172
- secrets.brokered.push(sub);
173
- return sub.placeholder;
174
- }
175
- /** The host value, or its placeholder when it's brokered to `secret.hosts` (else `hosts`). */
176
- function hostValue(name, secret, where, secrets, hosts = secret.hosts) {
177
- const from = secret.from ?? name;
178
- const value = process.env[from];
179
- if (!value) {
180
- if (secret.optional) {
181
- secrets.unset.push(name);
182
- return;
183
- }
184
- const source = from === name ? name : `${name} (from ${from})`;
185
- throw new InfraError(`Secret ${source} for ${where} isn't set. Set it in the environment or .env.local.`);
186
- }
187
- registerSecret(value);
188
- if (secret.to === "checks") secrets.checks.push(name);
189
- else secrets.names.push(name);
190
- if (!hosts) return value;
191
- return broker(secrets, name, value, hosts, secret.in, secret.to === "checks" ? "checks" : "agent");
192
- }
193
- /** Each host variable in the template, or with `hosts` one placeholder per variable. */
194
- function template(value, where, secrets, hosts) {
195
- const placeholders = /* @__PURE__ */ new Map();
196
- return value.template.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => {
197
- const v = process.env[name];
198
- if (!v) throw new InfraError(`secret.template for ${where} needs ${name}, which isn't set. Set it in the environment or .env.local.`);
199
- registerSecret(v);
200
- if (!hosts) return v;
201
- const p = placeholders.get(name) ?? broker(secrets, name, v, hosts);
202
- placeholders.set(name, p);
203
- return p;
204
- });
205
- }
206
- /** A `SKILL.md` split into its YAML frontmatter's `name` and `description` and its body. */
207
- function parseSkillMd(text, where) {
208
- const match = /^---\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)([\s\S]*)$/.exec(text);
209
- if (!match) return { body: text };
210
- let front;
211
- try {
212
- front = parse(match[1] ?? "");
213
- } catch (error) {
214
- const reason = error.message.split("\n")[0];
215
- throw new InfraError(`${where}: the SKILL.md frontmatter isn't valid YAML (${reason}). Quote values that contain ": ".`);
216
- }
217
- 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`);
218
- const field = (key) => {
219
- const value = front?.[key];
220
- if (value == null) return void 0;
221
- if (typeof value !== "string") throw new InfraError(`${where}: the SKILL.md ${key} must be a string (got ${typeof value})`);
222
- return value.trim() || void 0;
223
- };
224
- return {
225
- name: field("name"),
226
- description: field("description"),
227
- body: match[2] ?? ""
228
- };
229
- }
230
- /** A gitignore-style test for `DEFAULT_SKILL_EXCLUDE`-shaped patterns against a skill-relative path. */
231
- function excludeMatcher(patterns) {
232
- const rules = patterns.map((pattern) => {
233
- const dirOnly = pattern.endsWith("/");
234
- const body = pattern.replace(/\/+$/, "");
235
- const anchored = body.includes("/");
236
- const glob = body.replace(/^\/+/, "").split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*");
237
- return {
238
- dirOnly,
239
- re: new RegExp(`${anchored ? "^" : "(?:^|/)"}${glob}$`)
240
- };
241
- });
242
- return (path, isDir) => rules.some((r) => (isDir || !r.dirOnly) && r.re.test(path));
243
- }
244
- function parseSkill(dir, source, exclude) {
245
- const front = parseSkillMd(readFileSync(join(dir, "SKILL.md"), "utf8"), `Skill ${basename(dir)} in ${source}`);
246
- const name = front.name || basename(dir);
247
- checkSkill(name, front.description, source);
248
- const skip = excludeMatcher(exclude);
249
- const paths = [];
250
- const excluded = [];
251
- const walk = (rel) => {
252
- for (const entry of readdirSync(join(dir, rel), { withFileTypes: true })) {
253
- const path = rel ? `${rel}/${entry.name}` : entry.name;
254
- const isDir = entry.isDirectory();
255
- if (entry.name === ".git") continue;
256
- if (skip(path, isDir)) excluded.push(isDir ? `${path}/` : path);
257
- else if (isDir) walk(path);
258
- else if (entry.isFile() && path !== "SKILL.md") paths.push(path);
259
- }
260
- };
261
- walk("");
262
- const files = paths.sort().map((path) => ({
263
- path,
264
- content: readFileSync(join(dir, path), "utf8")
265
- }));
266
- return {
267
- skill: {
268
- name,
269
- description: front.description ?? "",
270
- content: front.body,
271
- files
272
- },
273
- excluded: excluded.sort()
274
- };
275
- }
276
- function checkSkill(name, description, source) {
277
- 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 -)`);
278
- 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.`);
279
- }
280
- function loadSkills(dir, source, only, exclude = DEFAULT_SKILL_EXCLUDE) {
281
- 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.`);
282
- 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();
283
- if (dirs.length === 0) throw new InfraError(`Skill source ${source} has no SKILL.md (and no subdirectories with one)`);
284
- const skills = dirs.map((d) => parseSkill(d, source, exclude));
285
- if (!only) return skills;
286
- const missing = only.filter((n) => !skills.some((s) => s.skill.name === n));
287
- if (missing.length) throw new InfraError(`only: ${JSON.stringify(missing)} not found in ${source} (found: ${skills.map((s) => s.skill.name).join(", ")})`);
288
- return skills.filter((s) => only.includes(s.skill.name));
289
- }
290
- async function git(args, cwd, what) {
291
- const r = await run("git", args, { cwd });
292
- if (r.exitCode !== 0) throw new InfraError(`${what} failed: ${r.stderr.trim()}`);
293
- return r.stdout.trim();
294
- }
295
- /** One clone per source and ref per worker process; the commit is recorded. */
296
- const clones = /* @__PURE__ */ new Map();
297
- function cloneSkills(root, url, ref) {
298
- const key = `${url}#${ref ?? ""}`;
299
- let clone = clones.get(key);
300
- if (!clone) {
301
- clone = (async () => {
302
- const dir = join(root, ".reeval", "cache", "skills", `${sha256(key).slice(0, 16)}-${process.pid}`);
303
- await rm(dir, {
304
- recursive: true,
305
- force: true
306
- });
307
- await mkdir(dir, { recursive: true });
308
- const what = `Cloning ${url}${ref ? ` at ${ref}` : ""}`;
309
- await git(["init", "-q"], dir, what);
310
- await git([
311
- "fetch",
312
- "-q",
313
- "--depth",
314
- "1",
315
- url,
316
- ref ?? "HEAD"
317
- ], dir, what);
318
- await git([
319
- "-c",
320
- "advice.detachedHead=false",
321
- "checkout",
322
- "-q",
323
- "FETCH_HEAD"
324
- ], dir, what);
325
- return {
326
- dir,
327
- commit: await git(["rev-parse", "HEAD"], dir, what)
328
- };
329
- })();
330
- clones.set(key, clone);
331
- clone.catch(() => clones.delete(key));
332
- }
333
- return clone;
334
- }
335
- const packs = /* @__PURE__ */ new Map();
336
- /** `npm pack` into the cache, unpacked; returns the package directory. */
337
- function unpackNpm(root, spec) {
338
- let pack = packs.get(spec);
339
- if (!pack) {
340
- pack = (async () => {
341
- const dir = join(root, ".reeval", "cache", "skills", `npm-${sha256(spec).slice(0, 16)}-${process.pid}`);
342
- await rm(dir, {
343
- recursive: true,
344
- force: true
345
- });
346
- await mkdir(dir, { recursive: true });
347
- const packed = await run("npm", [
348
- "pack",
349
- spec,
350
- "--silent",
351
- "--pack-destination",
352
- dir
353
- ], { cwd: dir });
354
- const tarball = packed.stdout.trim().split("\n").at(-1);
355
- if (packed.exitCode !== 0 || !tarball) throw new InfraError(`npm pack ${spec} failed: ${packed.stderr.trim()}`);
356
- const untar = await run("tar", ["-xzf", tarball], { cwd: dir });
357
- if (untar.exitCode !== 0) throw new InfraError(`Unpacking ${spec} failed: ${untar.stderr.trim()}`);
358
- return join(dir, "package");
359
- })();
360
- packs.set(spec, pack);
361
- pack.catch(() => packs.delete(spec));
362
- }
363
- return pack;
364
- }
365
- async function resolveSkills(root, s) {
366
- switch (s.source) {
367
- case "local": {
368
- const label = `local:${relative(root, s.path).split(sep).join("/") || "."}`;
369
- return {
370
- skills: loadSkills(s.path, label, s.only, s.exclude),
371
- source: label,
372
- commit: null
373
- };
374
- }
375
- case "git": {
376
- const { url } = parseGitRepo(s.repo);
377
- const { dir, commit } = await cloneSkills(root, url, s.ref);
378
- const path = s.path ?? (!existsSync(join(dir, "SKILL.md")) && existsSync(join(dir, "skills")) ? "skills" : "");
379
- const label = `${s.repo}${s.ref ? `#${s.ref}` : ""}${path ? `:${path}` : ""}`;
380
- return {
381
- skills: loadSkills(join(dir, path), label, s.only, s.exclude),
382
- source: label,
383
- commit
384
- };
385
- }
386
- case "registry": {
387
- const [repo = "", name = ""] = s.id.split("@");
388
- const { dir, commit } = await cloneSkills(root, `https://github.com/${repo}.git`, s.version);
389
- const label = `registry:${s.id}${s.version ? `#${s.version}` : ""}`;
390
- return {
391
- skills: loadSkills(existsSync(join(dir, "skills")) ? join(dir, "skills") : dir, label, [name]),
392
- source: label,
393
- commit
394
- };
395
- }
396
- case "npm": {
397
- const dir = await unpackNpm(root, s.spec);
398
- const label = `npm:${isAbsolute(s.spec) ? relative(root, s.spec).split(sep).join("/") : s.spec}:${s.path}`;
399
- return {
400
- skills: loadSkills(join(dir, s.path), label, s.only, s.exclude),
401
- source: label,
402
- commit: null
403
- };
404
- }
405
- case "inline": {
406
- checkSkill(s.name, s.description, "skill.inline");
407
- const files = Object.entries(s.files).sort(([a], [b]) => a < b ? -1 : 1).map(([path, content]) => ({
408
- path,
409
- content
410
- }));
411
- return {
412
- skills: [{
413
- skill: {
414
- name: s.name,
415
- description: s.description,
416
- content: s.content,
417
- files
418
- },
419
- excluded: []
420
- }],
421
- source: "inline",
422
- commit: null
423
- };
424
- }
425
- }
426
- }
427
- const skillHash = (s) => sha256(JSON.stringify(s));
428
- const readHostFile = (f, where) => {
429
- 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.`);
430
- return readFileSync(f.path);
431
- };
432
- /**
433
- * Layers the environment (root first; a later layer wins on shared keys, and each such key is
434
- * an override), over the options `env` merged down the cascade. `where` names the attempt, and
435
- * `rulesFile` is the workspace file the harness's agent reads `rules` from.
436
- */
437
- async function resolveEnvironment(environment, optionsEnv, root, where, rulesFile, egress = "allow-all", prepared = () => void 0) {
438
- const envWhere = `environment ${environment.name} (${where})`;
439
- const overrides = /* @__PURE__ */ new Set();
440
- const env = { ...optionsEnv };
441
- const files = /* @__PURE__ */ new Map();
442
- const mcp = /* @__PURE__ */ new Map();
443
- const skills = /* @__PURE__ */ new Map();
444
- let rules;
445
- const setup = [];
446
- const seen = {
447
- env: /* @__PURE__ */ new Set(),
448
- files: /* @__PURE__ */ new Set(),
449
- mcp: /* @__PURE__ */ new Set()
450
- };
451
- let egressSet = false;
452
- let workspace;
453
- let sandboxSet = false;
454
- for (const { spec } of environment.layers) {
455
- if (spec.sandbox !== void 0) {
456
- if (sandboxSet) overrides.add("sandbox");
457
- sandboxSet = true;
458
- }
459
- if (spec.workspace !== void 0) {
460
- if (workspace) overrides.add("workspace");
461
- workspace = spec.workspace;
462
- }
463
- if (spec.egress !== void 0) {
464
- if (egressSet) overrides.add("egress");
465
- egressSet = true;
466
- }
467
- for (const [k, v] of Object.entries(spec.env ?? {})) {
468
- if (seen.env.has(k)) overrides.add(`env.${k}`);
469
- seen.env.add(k);
470
- env[k] = v;
471
- }
472
- for (const [path, v] of Object.entries(spec.files ?? {})) {
473
- if (seen.files.has(path)) overrides.add(`files.${path}`);
474
- seen.files.add(path);
475
- files.set(path, v);
476
- }
477
- if (spec.rules !== void 0) {
478
- if (rules !== void 0) overrides.add("rules");
479
- rules = spec.rules;
480
- }
481
- for (const [name, server] of Object.entries(spec.mcp ?? {})) {
482
- if (seen.mcp.has(name)) overrides.add(`mcp.${name}`);
483
- seen.mcp.add(name);
484
- mcp.set(name, server);
485
- }
486
- for (const step of spec.setup ?? []) {
487
- const s = typeof step === "string" ? { run: step } : step;
488
- setup.push({
489
- run: s.run,
490
- timeoutMs: parseDuration(s.timeout ?? DEFAULT_SETUP_TIMEOUT),
491
- ...s.cwd ? { cwd: s.cwd } : {}
492
- });
493
- }
494
- const layerSkills = /* @__PURE__ */ new Set();
495
- for (const source of spec.skills ?? []) {
496
- const found = await resolveSkills(root, source);
497
- for (const loaded of found.skills) {
498
- const name = loaded.skill.name;
499
- if (skills.has(name) && !layerSkills.has(name)) overrides.add(`skills.${name}`);
500
- layerSkills.add(name);
501
- skills.delete(name);
502
- skills.set(name, {
503
- ...loaded,
504
- placement: source.placement,
505
- source: found.source,
506
- commit: found.commit
507
- });
508
- }
509
- }
510
- }
511
- const secrets = {
512
- names: [],
513
- checks: [],
514
- unset: [],
515
- brokered: []
516
- };
517
- const vars = {};
518
- const checkVars = {};
519
- for (const [name, value] of Object.entries(env)) {
520
- if (!isSecret(value)) {
521
- vars[name] = value;
522
- continue;
523
- }
524
- const resolved = hostValue(name, value, envWhere, secrets);
525
- if (resolved === void 0) continue;
526
- if (value.to === "checks") checkVars[name] = resolved;
527
- else vars[name] = resolved;
528
- }
529
- const secretOf = (value, key, hosts) => {
530
- if (typeof value === "string") return value;
531
- if (isSecretTemplate(value)) return template(value, envWhere, secrets, hosts);
532
- return hostValue(key, value, envWhere, secrets, value.hosts ?? hosts);
533
- };
534
- const resolvedMcp = {};
535
- for (const [name, s] of mcp) if (s.transport === "stdio") {
536
- const e = {};
537
- for (const [k, v] of Object.entries(s.env)) {
538
- const r = secretOf(v, k);
539
- if (r !== void 0) e[k] = r;
540
- }
541
- resolvedMcp[name] = {
542
- transport: "stdio",
543
- command: s.command,
544
- args: s.args,
545
- env: e
546
- };
547
- } else {
548
- const h = {};
549
- const hosts = mcpHosts(s.url);
550
- for (const [k, v] of Object.entries(s.headers)) {
551
- const r = secretOf(v, k, hosts);
552
- if (r !== void 0) h[k] = r;
553
- }
554
- resolvedMcp[name] = {
555
- transport: s.transport,
556
- url: s.url,
557
- headers: h
558
- };
559
- }
560
- const fileList = [...files].map(([path, v]) => ({
561
- path,
562
- content: isHostFile(v) ? readHostFile(v, envWhere) : v,
563
- ...isHostFile(v) && statSync(v.path).mode & 64 ? { executable: true } : {}
564
- }));
565
- const rulesContent = rules === void 0 ? void 0 : isHostFile(rules) ? Buffer.from(readHostFile(rules, envWhere)).toString("utf8") : rules;
566
- const skillList = [...skills.values()];
567
- const skillRecords = skillList.map(({ skill, placement, source, commit, excluded }) => ({
568
- name: skill.name,
569
- source,
570
- commit,
571
- sha256: skillHash(skill),
572
- placement,
573
- excluded
574
- }));
575
- const fileRecords = fileList.map((f) => ({
576
- path: f.path,
577
- sha256: sha256(f.content)
578
- }));
579
- const executable = fileList.flatMap((f) => f.executable ? [f.path] : []);
580
- const rulesRecord = rulesContent === void 0 ? void 0 : {
581
- path: rulesFile,
582
- sha256: sha256(rulesContent)
583
- };
584
- if (rulesContent !== void 0) fileList.push({
585
- path: rulesFile,
586
- content: rulesContent
587
- });
588
- const secretNames = [.../* @__PURE__ */ new Set([...secrets.names, ...secrets.checks])].sort();
589
- const brokered = secrets.brokered.map((b) => JSON.stringify([
590
- b.name,
591
- b.hosts,
592
- b.in,
593
- b.phase
594
- ])).sort();
595
- const brokeredNames = [...new Set(secrets.brokered.map((b) => b.name))].sort();
596
- const varNames = Object.keys(vars).filter((n) => !secrets.names.includes(n)).sort();
597
- const literalVars = Object.fromEntries(varNames.map((n) => [n, vars[n]]));
598
- const mcpShape = Object.fromEntries(Object.entries(resolvedMcp).map(([name, s]) => s.transport === "stdio" ? [name, {
599
- command: s.command,
600
- args: s.args,
601
- env: Object.keys(s.env).sort()
602
- }] : [name, {
603
- transport: s.transport,
604
- url: s.url,
605
- headers: Object.keys(s.headers).sort()
606
- }]));
607
- const tree = workspace && prepared(workspace);
608
- if (workspace && !tree) throw new InfraError(`The workspace git(${JSON.stringify(workspaceLabel(workspace))}) for ${envWhere} wasn't prepared before the run started`);
609
- const workspaceRecord = workspace && tree ? {
610
- source: isAbsolute(workspace.url) ? relative(root, workspace.url).split(sep).join("/") || "." : workspace.repo,
611
- ref: workspace.ref ?? null,
612
- path: workspace.path ?? null,
613
- commit: tree.commit,
614
- tree: tree.tree,
615
- ...workspace.init ? { init: true } : {}
616
- } : void 0;
617
- const digest = `sha256:${sha256(JSON.stringify({
618
- setup,
619
- skills: skillRecords.map((s) => [
620
- s.name,
621
- s.sha256,
622
- s.placement
623
- ]),
624
- mcp: mcpShape,
625
- files: fileRecords,
626
- ...executable.length ? { executable } : {},
627
- rules: rulesRecord?.sha256,
628
- vars: literalVars,
629
- secrets: secretNames,
630
- checks: [...secrets.checks].sort(),
631
- unset: [...secrets.unset].sort(),
632
- brokered,
633
- ...egress === "allow-all" ? {} : { egress: typeof egress === "string" ? egress : [...egress.allow].sort() },
634
- ...workspaceRecord ? { workspace: workspaceRecord.init ? [workspaceRecord.tree, "init"] : workspaceRecord.tree } : {}
635
- }))}`;
636
- return {
637
- record: {
638
- name: environment.name,
639
- parent: environment.parent,
640
- overrides: [...overrides],
641
- digest,
642
- setup: setup.map(stepCommand),
643
- skills: skillRecords,
644
- mcp: Object.keys(resolvedMcp).sort(),
645
- files: rulesRecord ? [...fileRecords, rulesRecord] : fileRecords,
646
- vars: varNames,
647
- secrets: secretNames,
648
- brokered: brokeredNames,
649
- unset: [...secrets.unset].sort(),
650
- egress,
651
- ...workspaceRecord ? { workspace: workspaceRecord } : {}
652
- },
653
- setup,
654
- skills: skillList.filter((s) => s.placement === "harness").map((s) => s.skill),
655
- workspaceSkills: skillList.filter((s) => s.placement === "workspace").map((s) => s.skill),
656
- mcp: resolvedMcp,
657
- files: fileList,
658
- vars,
659
- checkVars,
660
- substitutions: secrets.brokered,
661
- ...workspace && tree ? { workspace: {
662
- tar: tree.tar,
663
- label: workspaceLabel(workspace),
664
- commit: tree.commit,
665
- ...workspace.init ? { init: true } : {}
666
- } } : {}
667
- };
668
- }
669
- /** A skill as a `SKILL.md` file plus its other files, for writing into a directory. */
670
- function skillFiles(skill) {
671
- return [{
672
- path: "SKILL.md",
673
- content: `${`---\nname: ${skill.name}\ndescription: ${JSON.stringify(skill.description)}\n---\n`}${skill.content}`
674
- }, ...skill.files];
675
- }
676
- //#endregion
677
162
  //#region src/harness/fake.ts
678
163
  const lineCount = (text) => text ? text.split("\n").length - (text.endsWith("\n") ? 1 : 0) : 0;
679
164
  /**
680
165
  * Writes canned files and returns a canned message on every turn, after running its `script`'s
681
166
  * real sandbox actions. Harness-placed skills are written under `~/` where its layout's adapter
682
- * puts them (`~/.agents/skills` by default, as Cursor's does).
167
+ * puts them (`~/.agents/skills` by default, as Cursor's does). Plugins are already copied under
168
+ * `~/.reeval/plugins/`; where its layout takes plugin items flat, their skills arrive in `skills`.
169
+ * Where it takes them namespaced, it writes its layout's plugin files (Claude Code's settings)
170
+ * and leaves the plugins' MCP servers to the plugins, as that agent does.
683
171
  */
684
172
  const createFakeHarness = (descriptor) => {
685
173
  let starts = 0;
686
- return { async start(sandbox, _signal, { skills, workspaceSkills, mcp }) {
174
+ return { async start(sandbox, _signal, { skills, workspaceSkills, mcp: all, plugins }) {
687
175
  if (descriptor.failWith) throw new InfraError(descriptor.failWith);
688
176
  const hangs = descriptor.startHangs;
689
177
  if (hangs === true || starts++ < (hangs ?? 0)) return new Promise(() => {});
690
- const dir = `~/${layoutOf(descriptor).harnessSkillDir}`;
178
+ const layout = layoutOf(descriptor);
179
+ const mcp = agentMcp(layout, all, plugins);
180
+ for (const path of await writePluginFiles(sandbox, layout, plugins)) debugLog("env", `plugin settings → ${path} by the fake agent`);
181
+ const dir = `~/${layout.harnessSkillDir}`;
691
182
  for (const skill of skills) {
692
183
  for (const f of skillFiles(skill)) await sandbox.writeFile(`${dir}/${skill.name}/${f.path}`, f.content);
693
184
  debugLog("env", `skill ${skill.name} placed at ${dir}/${skill.name}/ by the fake agent`);
694
185
  }
695
186
  const names = [...skills.map((s) => s.name), ...workspaceSkills];
696
- const loaded = [names.length ? `skills: ${names.join(", ")}` : "", Object.keys(mcp).length ? `mcp: ${Object.keys(mcp).join(", ")}` : ""].filter(Boolean);
187
+ const loaded = [
188
+ names.length ? `skills: ${names.join(", ")}` : "",
189
+ plugins.length ? `plugins: ${plugins.map((p) => `${p.name} (${p.parts.join(", ")})`).join(", ")}` : "",
190
+ Object.keys(mcp).length ? `mcp: ${Object.keys(mcp).join(", ")}` : ""
191
+ ].filter(Boolean);
697
192
  let calls = 0;
698
193
  let turns = 0;
699
194
  const skilled = names.length > 0 && descriptor.withSkill;
@@ -882,7 +377,7 @@ async function callMcpTool(sandbox, servers, name, tool, signal) {
882
377
  " trap '' PIPE",
883
378
  ` printf '%s\\n' ${messages.join(" ")} 2>/dev/null`,
884
379
  " i=0",
885
- ` while [ $i -lt 300 ] && [ ! -f "$d/code" ] && ! grep -Eq '"id": ?2[,}]' "$d/out"; do sleep 0.1; i=$((i + 1)); done`,
380
+ ` while [ $i -lt 900 ] && [ ! -f "$d/code" ] && ! grep -Eq '"id": ?2[,}]' "$d/out"; do sleep 0.1; i=$((i + 1)); done`,
886
381
  `} | { ${command} > "$d/out" 2> "$d/err"; echo $? > "$d/code"; }`,
887
382
  "code=$(cat \"$d/code\")",
888
383
  "cat \"$d/out\"; cat \"$d/err\" >&2; rm -rf \"$d\"; exit $code"
@@ -915,7 +410,7 @@ async function callMcpTool(sandbox, servers, name, tool, signal) {
915
410
  //#region src/harness/index.ts
916
411
  async function createHarness(descriptor) {
917
412
  if (descriptor.kind === "fake") return createFakeHarness(descriptor);
918
- const { createAgentHarness } = await import("./agent-CaS_2Sce.js");
413
+ const { createAgentHarness } = await import("./agent-CatUEo5h.js");
919
414
  return createAgentHarness(descriptor);
920
415
  }
921
416
  /** Where each harness's agent looks for repo skills, relative to the workspace. */
@@ -929,7 +424,10 @@ const workspaceSkillDir = (descriptor) => layoutOf(descriptor).workspaceSkillDir
929
424
  */
930
425
  async function prepareEnvironment(a, root, runId, where, signal) {
931
426
  const { spec, record } = a;
932
- const env = await resolveEnvironment(spec.environment, spec.env, root, where, layoutOf(spec.harness).rulesFile, spec.egress.policy, (w) => preparedWorkspace(root, runId, w));
427
+ const env = await resolveEnvironment(spec.environment, spec.env, root, where, layoutOf(spec.harness).rulesFile, spec.egress.policy, (w) => preparedWorkspace(root, runId, w), {
428
+ harness: spec.harness,
429
+ allowed: allowedHosts(spec.egress)
430
+ });
933
431
  a.env = env;
934
432
  record.environment = env.record;
935
433
  const { secrets, brokered } = env.record;
@@ -942,13 +440,29 @@ async function prepareEnvironment(a, root, runId, where, signal) {
942
440
  if (debug) traceEnvironment(debug, spec, env);
943
441
  record.image = null;
944
442
  if (spec.sandbox.kind === "docker") {
945
- const { preparedFor } = await import("./preflight-CPWpQeBI.js");
946
- const { attemptImage, dockerProvider } = await import("./providers-gDMko8fl.js");
443
+ const { preparedFor } = await import("./preflight-B6pY89bF.js");
444
+ const { attemptImage, dockerProvider, preparedImage } = await import("./providers-CXN2RFz-.js");
947
445
  signal.throwIfAborted();
948
- const prepared = preparedFor(root, runId, dockerProvider({ engine: spec.sandbox.engine ?? "docker" }), spec.sandbox, spec.harness);
446
+ const provider = dockerProvider({ engine: spec.sandbox.engine ?? "docker" });
447
+ const builds = builtPlugins(spec.environment.layers);
448
+ const prepared = preparedFor(root, runId, provider, spec.sandbox, spec.harness, builds);
949
449
  if (!prepared) throw new InfraError(`The sandbox image for ${where} wasn't prepared before the run started`);
950
450
  record.image = attemptImage(prepared);
951
451
  a.image = record.image.ref;
452
+ const built = preparedImage(prepared).plugins ?? [];
453
+ for (const [i, plugin] of env.plugins.entries()) {
454
+ if (plugin.declared.build === void 0) continue;
455
+ const found = built.find((b) => b.id === pluginBuildId(plugin.declared));
456
+ if (!found) throw new InfraError(`The ${plugin.name} plugin's build for ${where} wasn't prepared before the run started`);
457
+ if (found.commit !== plugin.commit) throw new InfraError(`The ${plugin.name} plugin's ref moved during the run: it was built at ${found.commit?.slice(0, 12)} and is now ${plugin.commit?.slice(0, 12)}. Pin ref to a commit or tag.`);
458
+ plugin.built = {
459
+ dir: found.dir,
460
+ tag: found.tag
461
+ };
462
+ const pluginRecord = env.record.plugins?.[i];
463
+ if (pluginRecord) pluginRecord.build = found.tag;
464
+ debugLog("image", `plugin ${plugin.name} built in ${found.tag}`);
465
+ }
952
466
  const { ref, id, base, install, agentCli } = record.image;
953
467
  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(" && ")}` : ""}`);
954
468
  } else debugLog("image", `none: the ${spec.sandbox.kind} sandbox runs on the host`);
@@ -1243,7 +757,7 @@ async function createSandbox(descriptor, options) {
1243
757
  switch (descriptor.kind) {
1244
758
  case "fake": return await createFakeSandbox(descriptor, options.env);
1245
759
  case "docker": {
1246
- const { createDockerSandbox } = await import("./docker-yauK-9X8.js");
760
+ const { createDockerSandbox } = await import("./docker-DC69dKxB.js");
1247
761
  return await createDockerSandbox(descriptor, options);
1248
762
  }
1249
763
  }
@@ -1322,11 +836,12 @@ async function recordOom(a, sandbox) {
1322
836
  }
1323
837
  /**
1324
838
  * The attempt's egress policy and, when its sandbox records it, what it contacted and was refused.
1325
- * Refused hosts the harness always tries are `expectedBlocked`, summarised in one `-v` line.
839
+ * Refused hosts the harness always tries (in a session like this one) are `expectedBlocked`,
840
+ * summarised in one `-v` line.
1326
841
  */
1327
842
  function recordEgress(a, sandbox) {
1328
843
  const traffic = sandbox?.traffic?.();
1329
- const expected = expectedBlockedFor(a.spec.harness);
844
+ const expected = expectedBlockedFor(a.spec.harness, (a.env?.plugins.length ?? 0) > 0);
1330
845
  const refused = traffic?.blocked ?? [];
1331
846
  const isExpected = (h) => !!expected?.hosts.includes(h.host);
1332
847
  const expectedBlocked = refused.filter(isExpected);
@@ -1377,6 +892,118 @@ function turnErrors(transcripts) {
1377
892
  });
1378
893
  }
1379
894
  //#endregion
895
+ //#region src/harness/unpack.ts
896
+ const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
897
+ const FRONTMATTER = /^\uFEFF?---\r?\n/;
898
+ const FRONTMATTER_BLOCK = /^\uFEFF?---\r?\n(?:---|([\s\S]*?)\r?\n---)[ \t]*(?:\r?\n|$)/;
899
+ const RULE = /^rules\/([^/]+)\.mdc?$/;
900
+ const MARKDOWN = (dir) => new RegExp(`^${dir}/([^/]+)\\.md$`);
901
+ /** A subagent's or command's Markdown with its frontmatter rewritten by `format`. */
902
+ function reformatted(text, part, what, format) {
903
+ const block = FRONTMATTER_BLOCK.exec(text);
904
+ let front = {};
905
+ if (block) {
906
+ try {
907
+ front = parse(block[1] ?? "") ?? {};
908
+ } catch {
909
+ throw new InfraError(`${what}'s frontmatter isn't valid YAML`);
910
+ }
911
+ if (!isRecord(front)) throw new InfraError(`${what}'s frontmatter isn't a YAML mapping`);
912
+ }
913
+ const body = block ? text.slice(block[0].length).replace(/^\r?\n/, "") : text.replace(/^\uFEFF/, "");
914
+ const out = format(part, front);
915
+ const yaml = Object.keys(out).length ? stringify(out, { lineWidth: 0 }) : "";
916
+ return yaml ? `---\n${yaml}---\n\n${body}` : body;
917
+ }
918
+ /** Cursor's handlers for one plugin's `hooks/hooks.json`, by Cursor's event name. */
919
+ function hookHandlers(plugin, text, hooks) {
920
+ const what = `the ${plugin.name} plugin's hooks/hooks.json`;
921
+ let raw;
922
+ try {
923
+ raw = JSON.parse(text);
924
+ } catch {
925
+ throw new InfraError(`${what} isn't valid JSON`);
926
+ }
927
+ const events = isRecord(raw) && isRecord(raw.hooks) ? raw.hooks : {};
928
+ const out = {};
929
+ for (const [event, groups] of Object.entries(events)) {
930
+ const named = hooks.events[event];
931
+ if (!named) throw new InfraError(`${what} has ${event} hooks, which the agent has no event for`);
932
+ for (const group of Array.isArray(groups) ? groups.filter(isRecord) : []) {
933
+ const matcher = hooks.matcher(event, typeof group.matcher === "string" ? group.matcher : void 0);
934
+ if (matcher === null) continue;
935
+ for (const h of Array.isArray(group.hooks) ? group.hooks.filter(isRecord) : []) {
936
+ if ((h.type ?? "command") !== "command" || typeof h.command !== "string") throw new InfraError(`${what} has a ${event} hook that isn't a command`);
937
+ out[named] ??= [];
938
+ out[named].push({
939
+ type: "command",
940
+ command: h.command.replaceAll(PLUGIN_ROOT, plugin.dir),
941
+ ...typeof h.timeout === "number" ? { timeout: h.timeout } : {},
942
+ ...matcher === void 0 ? {} : { matcher }
943
+ });
944
+ }
945
+ }
946
+ }
947
+ return out;
948
+ }
949
+ /**
950
+ * The workspace files that give a flat agent the plugins' rules, subagents, commands and hooks,
951
+ * by path relative to the workspace: each part `unpack` names and the plugin keeps, with
952
+ * `${CLAUDE_PLUGIN_ROOT}` filled in with the plugin's `dir`. The hooks of every plugin go in one
953
+ * file, after the handlers of `hooksFile` (the workspace's own, or null).
954
+ */
955
+ function unpackPlugins(plugins, unpack, hooksFile = null) {
956
+ const out = [];
957
+ let hooks;
958
+ for (const plugin of plugins) {
959
+ const keeps = (part, dir) => dir !== void 0 && plugin.parts.includes(part);
960
+ for (const f of plugin.files) {
961
+ const text = () => Buffer.from(f.content).toString("utf8").replaceAll(PLUGIN_ROOT, plugin.dir);
962
+ const rule = RULE.exec(f.path);
963
+ if (rule && keeps("rules", unpack.rules)) {
964
+ const body = text();
965
+ out.push({
966
+ path: `${unpack.rules}/${plugin.name}-${rule[1]}.mdc`,
967
+ content: FRONTMATTER.test(body) ? body : `---\nalwaysApply: true\n---\n\n${body}`
968
+ });
969
+ continue;
970
+ }
971
+ for (const [part, dir] of [["agents", unpack.agents], ["commands", unpack.commands]]) {
972
+ const name = MARKDOWN(part).exec(f.path)?.[1];
973
+ if (!name || !keeps(part, dir)) continue;
974
+ const what = `the ${plugin.name} plugin's ${part === "agents" ? "subagent" : "command"} ${f.path}`;
975
+ out.push({
976
+ path: `${dir}/${name}.md`,
977
+ content: unpack.frontmatter ? reformatted(text(), part, what, unpack.frontmatter) : text()
978
+ });
979
+ }
980
+ if (f.path === "hooks/hooks.json" && unpack.hooks && plugin.parts.includes("hooks")) {
981
+ if (!hooks) {
982
+ let own = {};
983
+ try {
984
+ own = hooksFile === null ? {} : JSON.parse(hooksFile);
985
+ } catch {
986
+ throw new InfraError(`The workspace's ${unpack.hooks.file} isn't valid JSON, so reeval can't add the plugins' hooks to it`);
987
+ }
988
+ hooks = isRecord(own) ? {
989
+ version: 1,
990
+ ...own
991
+ } : { version: 1 };
992
+ }
993
+ const merged = isRecord(hooks.hooks) ? { ...hooks.hooks } : {};
994
+ const raw = Buffer.from(f.content).toString("utf8");
995
+ for (const [event, handlers] of Object.entries(hookHandlers(plugin, raw, unpack.hooks))) merged[event] = [...Array.isArray(merged[event]) ? merged[event] : [], ...handlers];
996
+ hooks.hooks = merged;
997
+ }
998
+ }
999
+ }
1000
+ if (hooks && unpack.hooks) out.push({
1001
+ path: unpack.hooks.file,
1002
+ content: `${JSON.stringify(hooks, null, 2)}\n`
1003
+ });
1004
+ return out;
1005
+ }
1006
+ //#endregion
1380
1007
  //#region src/sandbox/phases.ts
1381
1008
  const strip = ({ phase: _, ...sub }) => sub;
1382
1009
  /**
@@ -1406,6 +1033,70 @@ function brokerPhases(box, subs) {
1406
1033
  };
1407
1034
  }
1408
1035
  //#endregion
1036
+ //#region src/attempt/mcp-startup.ts
1037
+ /**
1038
+ * The timer and its log beside the workspace: `/reeval/bin/mcp-timer.cjs` in Docker. `.cjs`, since
1039
+ * a sandbox's directories may be in a `"type": "module"` package.
1040
+ */
1041
+ const TIMER = "../bin/mcp-timer.cjs";
1042
+ const LOG = "../mcp-startup.jsonl";
1043
+ /** The attempt's error when an MCP server never answered `initialize`. */
1044
+ const mcpTimeoutMessage = (server) => `MCP server ${server} didn't answer initialize within 60s`;
1045
+ /**
1046
+ * Puts the timer in the sandbox and runs every stdio server through it, so whichever process
1047
+ * starts a server (the agent's CLI, once a session or once a turn, or the fake agent) logs when it
1048
+ * answered. Returns the servers unchanged, and no log, when none is stdio, or with `untimed`'s
1049
+ * reason when the sandbox has no `node` (an `image` used as is), rather than break them.
1050
+ */
1051
+ async function timeMcpServers(box, servers, signal) {
1052
+ if (!Object.values(servers).some((s) => s.transport === "stdio")) return { servers };
1053
+ const found = await box.exec("command -v node", { signal });
1054
+ const node = found.exitCode === 0 ? found.stdout.trim().split("\n")[0] : "";
1055
+ if (!node) return {
1056
+ servers,
1057
+ untimed: "the sandbox has no node to run the timer"
1058
+ };
1059
+ await box.writeFile(TIMER, await readFile(MCP_TIMER, "utf8"));
1060
+ await box.writeFile(LOG, "");
1061
+ const timer = `${box.workdir.replace(/[\\/][^\\/]*$/, "")}/bin/mcp-timer.cjs`;
1062
+ const wrapped = Object.fromEntries(Object.entries(servers).map(([name, s]) => [name, s.transport === "stdio" ? {
1063
+ ...s,
1064
+ command: node,
1065
+ args: [
1066
+ timer,
1067
+ "--name",
1068
+ name,
1069
+ "--",
1070
+ s.command,
1071
+ ...s.args
1072
+ ]
1073
+ } : s]));
1074
+ let seen = 0;
1075
+ return {
1076
+ servers: wrapped,
1077
+ log: { async read() {
1078
+ const complete = (await box.readFile(LOG).catch(() => null) ?? "").split("\n").slice(0, -1);
1079
+ const fresh = complete.slice(seen);
1080
+ seen = complete.length;
1081
+ return fresh.flatMap((line) => {
1082
+ try {
1083
+ const entry = JSON.parse(line);
1084
+ return typeof entry.server === "string" ? [entry] : [];
1085
+ } catch {
1086
+ return [];
1087
+ }
1088
+ });
1089
+ } }
1090
+ };
1091
+ }
1092
+ /** Each server start in `entries`, with how long it took to answer `initialize`; null when it didn't. */
1093
+ const mcpStartupOf = (entries) => entries.map((e) => ({
1094
+ server: e.server,
1095
+ ms: e.initializedAt === void 0 ? null : Math.max(0, e.initializedAt - e.spawnedAt)
1096
+ }));
1097
+ /** The first server in `entries` that never answered `initialize`. */
1098
+ const timedOutServer = (entries) => entries.find((e) => e.timeout)?.server;
1099
+ //#endregion
1409
1100
  //#region src/attempt/state.ts
1410
1101
  /** Extra time an attempt gets, outside its eval timeout, when its image may need building. */
1411
1102
  const SETUP_BUDGET_MS = 12e5;
@@ -1566,6 +1257,131 @@ async function copyWorkspace(box, workspace, signal) {
1566
1257
  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)}`);
1567
1258
  }
1568
1259
  /**
1260
+ * A built plugin's directory, copied from where its layer left it in the image, less the parts
1261
+ * `only` leaves out, with the manifest as reeval read it.
1262
+ */
1263
+ async function copyBuiltPlugin(a, box, plugin, dir) {
1264
+ const dropped = droppedPaths(plugin.parts).map((p) => shellQuote(`${dir}/${p}`));
1265
+ const r = await box.exec(`mkdir -p ${shellQuote(dir)} && cp -a ${shellQuote(plugin.built.dir)}/. ${shellQuote(dir)}/${dropped.length ? ` && rm -rf ${dropped.join(" ")}` : ""}`, { signal: a.signal });
1266
+ if (r.exitCode !== 0) throw new InfraError(`Couldn't copy the ${plugin.name} plugin's build from ${plugin.built.tag}: ${tailOf(r.stderr || r.stdout)}`);
1267
+ const manifest = plugin.files.find((f) => f.path === PLUGIN_MANIFEST);
1268
+ if (manifest) await box.writeFile(`${dir}/${PLUGIN_MANIFEST}`, manifest.content);
1269
+ }
1270
+ /**
1271
+ * Copies each plugin to `PLUGIN_DIR/<name>/` before the workspace snapshot (a built one from its
1272
+ * layer in the image, any other from the host), and points its MCP servers' `${CLAUDE_PLUGIN_ROOT}`
1273
+ * at that directory, for the adapter to load from there.
1274
+ */
1275
+ async function copyPlugins(a, box, env) {
1276
+ const out = [];
1277
+ for (const plugin of env.plugins) {
1278
+ const at = `${PLUGIN_DIR}/${plugin.name}`;
1279
+ const dir = `${box.home}/${at.slice(2)}`;
1280
+ const { built } = plugin;
1281
+ if (built) await copyBuiltPlugin(a, box, {
1282
+ ...plugin,
1283
+ built
1284
+ }, dir);
1285
+ else for (const f of plugin.files) await box.writeFile(`${at}/${f.path}`, f.content);
1286
+ const executable = built ? [] : plugin.files.flatMap((f) => f.executable ? [`${dir}/${f.path}`] : []);
1287
+ if (executable.length) {
1288
+ const r = await box.exec(`chmod +x ${executable.map(shellQuote).join(" ")}`, { signal: a.signal });
1289
+ if (r.exitCode !== 0) throw new InfraError(`Couldn't make the ${plugin.name} plugin's ${executable.length} executable files executable: ${tailOf(r.stderr || r.stdout)}`);
1290
+ }
1291
+ const root = (v) => v.replaceAll(PLUGIN_ROOT, dir);
1292
+ for (const key of plugin.servers) {
1293
+ const server = env.mcp[key];
1294
+ if (server?.transport !== "stdio") continue;
1295
+ env.mcp[key] = {
1296
+ ...server,
1297
+ command: root(server.command),
1298
+ args: server.args.map(root),
1299
+ env: {
1300
+ ...Object.fromEntries(Object.entries(server.env).map(([k, v]) => [k, root(v)])),
1301
+ [PLUGIN_ROOT_VAR]: dir
1302
+ }
1303
+ };
1304
+ }
1305
+ out.push({
1306
+ name: plugin.name,
1307
+ dir,
1308
+ parts: plugin.parts,
1309
+ servers: plugin.servers
1310
+ });
1311
+ debugLog("env", `plugin ${plugin.name} → ${at}/ (${built ? `built in ${built.tag}` : `${plugin.files.length} files`}; ${plugin.parts.join(", ")})`);
1312
+ }
1313
+ return out;
1314
+ }
1315
+ /**
1316
+ * The parts a flat agent has unpacked, as a built plugin's copy in the sandbox has them: its
1317
+ * build's output, which `files` (the source before the build) may not match. Builds run on Docker
1318
+ * only, so the sandbox has `find`.
1319
+ */
1320
+ async function unpackedFromCopy(a, box, dir) {
1321
+ const r = await box.exec(`cd ${shellQuote(dir)} && find rules agents commands hooks -maxdepth 1 -type f 2>/dev/null; true`, { signal: a.signal });
1322
+ const out = [];
1323
+ for (const path of r.stdout.split("\n").filter(Boolean)) {
1324
+ const content = await box.readFile(`${dir}/${path}`);
1325
+ if (content !== null) out.push({
1326
+ path,
1327
+ content: Buffer.from(content)
1328
+ });
1329
+ }
1330
+ return out;
1331
+ }
1332
+ /**
1333
+ * Unpacks the plugins' rules, subagents, commands and hooks into the workspace where a flat agent
1334
+ * reads them, before the snapshot, so the diff leaves them out. A file the workspace already has
1335
+ * is an infra error, except the hooks file, whose own handlers the plugins' join.
1336
+ */
1337
+ async function writeUnpackedPlugins(a, box, env, plugins, unpack) {
1338
+ const placed = [];
1339
+ for (const p of plugins) {
1340
+ const resolved = env.plugins.find((r) => r.name === p.name);
1341
+ const files = resolved?.built ? await unpackedFromCopy(a, box, p.dir) : resolved?.files ?? [];
1342
+ placed.push({
1343
+ ...p,
1344
+ files
1345
+ });
1346
+ }
1347
+ const files = unpackPlugins(placed, unpack, unpack.hooks ? await box.readFile(unpack.hooks.file) : null);
1348
+ const fresh = files.filter((f) => f.path !== unpack.hooks?.file);
1349
+ if (fresh.length) {
1350
+ const taken = (await box.exec(`for p in ${fresh.map((f) => shellQuote(f.path)).join(" ")}; do { [ -e "$p" ] || [ -L "$p" ]; } && echo "$p"; done; true`, { signal: a.signal })).stdout.trim().split("\n").filter(Boolean);
1351
+ if (taken.length) throw new InfraError(`The workspace already has ${taken.join(", ")}, which a plugin would replace: rename the plugin's file, or leave the plugin out of this cell`);
1352
+ }
1353
+ for (const f of files) {
1354
+ await box.writeFile(f.path, f.content);
1355
+ debugLog("env", `plugin part → ${f.path} (unpacked for the agent)`);
1356
+ }
1357
+ }
1358
+ /**
1359
+ * For an agent that starts a plugin's MCP servers from the plugin itself: rewrites each placed
1360
+ * plugin's `.mcp.json` to the servers as reeval resolved them, through the MCP timer, so they're
1361
+ * timed like the environment's. A manifest that declares `mcpServers` is pointed at that file.
1362
+ * Only namespaced layouts, which refuse a plugin server reading a host secret, so no secret value
1363
+ * lands in the file.
1364
+ */
1365
+ async function writePluginMcp(box, env, plugins, servers) {
1366
+ for (const placed of plugins) {
1367
+ if (!placed.servers.length) continue;
1368
+ const own = Object.fromEntries(placed.servers.flatMap((key) => {
1369
+ const s = servers[key];
1370
+ return s ? [[key.slice(placed.name.length + 1), s]] : [];
1371
+ }));
1372
+ const at = `${PLUGIN_DIR}/${placed.name}`;
1373
+ const json = (v) => `${JSON.stringify(v, null, 2)}\n`;
1374
+ await box.writeFile(`${at}/.mcp.json`, json({ mcpServers: claudeMcpServers(own) }));
1375
+ const manifest = env.plugins.find((p) => p.name === placed.name)?.files.find((f) => f.path === PLUGIN_MANIFEST);
1376
+ const parsed = manifest ? JSON.parse(Buffer.from(manifest.content).toString("utf8")) : {};
1377
+ if (parsed.mcpServers !== void 0) await box.writeFile(`${at}/${PLUGIN_MANIFEST}`, json({
1378
+ ...parsed,
1379
+ mcpServers: "./.mcp.json"
1380
+ }));
1381
+ debugLog("env", `plugin ${placed.name}'s MCP servers → ${at}/.mcp.json, through the timer`);
1382
+ }
1383
+ }
1384
+ /**
1569
1385
  * Starts the harness session within its `startTimeout`. A start that doesn't become ready in time
1570
1386
  * is aborted (and closed, should it finish later) and started once more: a stuck bootstrap is
1571
1387
  * usually transient, and nothing has run yet, so the retry can't change what the attempt
@@ -1597,7 +1413,7 @@ async function startHarness(a, harness, box, options) {
1597
1413
  }
1598
1414
  /**
1599
1415
  * Starts the sandbox (with the brokered secrets its gateway swaps in), writes the
1600
- * environment's files and workspace skills, runs its setup steps, snapshots the workspace for
1416
+ * environment's files, workspace skills and plugins, runs its setup steps, snapshots the workspace for
1601
1417
  * the diff, and starts the harness session with the harness-placed skills and MCP servers.
1602
1418
  */
1603
1419
  async function startSession(a, runId) {
@@ -1611,7 +1427,7 @@ async function startSession(a, runId) {
1611
1427
  env: env.vars,
1612
1428
  image: a.image,
1613
1429
  brokering: brokeringFor(spec.harness),
1614
- expectedBlocked: expectedBlockedFor(spec.harness)?.hosts,
1430
+ expectedBlocked: expectedBlockedFor(spec.harness, env.plugins.length > 0)?.hosts,
1615
1431
  substitutions: env.substitutions,
1616
1432
  egress: spec.egress
1617
1433
  });
@@ -1643,19 +1459,28 @@ async function startSession(a, runId) {
1643
1459
  for (const f of files) await box.writeFile(`${skillDir}/${skill.name}/${f.path}`, f.content);
1644
1460
  debugLog("env", `skill ${skill.name} → ${skillDir}/${skill.name}/ (${files.length} files, workspace)`);
1645
1461
  }
1462
+ const plugins = await copyPlugins(a, box, env);
1646
1463
  const layout = layoutOf(spec.harness);
1464
+ if (layout.pluginUnpack && plugins.length) await writeUnpackedPlugins(a, box, env, plugins, layout.pluginUnpack);
1647
1465
  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})`);
1648
1466
  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`);
1649
1467
  await runSetup(a, box, env);
1650
1468
  await snapshot(box, a.signal);
1651
1469
  debugLog("env", "workspace snapshot taken: the diff starts here");
1470
+ const timed = await timeMcpServers(box, env.mcp, a.signal);
1471
+ if (timed.log) {
1472
+ a.mcpStartup = timed.log;
1473
+ debugLog("env", "stdio MCP servers start through reeval's MCP timer");
1474
+ } else if (timed.untimed) debugLog("env", `stdio MCP servers start untimed: ${timed.untimed}`);
1475
+ if (layout.pluginPlacement === "namespaced") await writePluginMcp(box, env, plugins, timed.servers);
1652
1476
  const harness = await createHarness(spec.harness);
1653
1477
  const harnessStart = performance.now();
1654
1478
  debugLog("harness", `starting ${harnessId(spec.harness)}: bootstrap and session`);
1655
1479
  const session = await startHarness(a, harness, box, {
1656
1480
  skills: env.skills,
1657
1481
  workspaceSkills: env.workspaceSkills.map((s) => s.name),
1658
- mcp: env.mcp
1482
+ mcp: timed.servers,
1483
+ plugins
1659
1484
  });
1660
1485
  debugLog("harness", `${harnessId(spec.harness)} session ready in ${formatDuration(performance.now() - harnessStart)}`);
1661
1486
  a.session = session;
@@ -1712,6 +1537,46 @@ function cutOff(signal, startedAt, grace = TURN_STOP_GRACE_MS) {
1712
1537
  cancel
1713
1538
  };
1714
1539
  }
1540
+ /** How often a turn reads the MCP timer's log for a server that never answered `initialize`. */
1541
+ const MCP_POLL_MS = 2e3;
1542
+ /**
1543
+ * Reads the MCP timer's log through a turn. `signal` aborts as soon as a server has timed out, so
1544
+ * an agent stuck waiting for it stops; `end` takes the turn's last entries.
1545
+ */
1546
+ function watchMcpStartup(log) {
1547
+ const stop = new AbortController();
1548
+ const entries = [];
1549
+ let timedOut;
1550
+ let at = 0;
1551
+ let reading;
1552
+ const poll = async () => {
1553
+ entries.push(...await log.read());
1554
+ timedOut ??= timedOutServer(entries);
1555
+ if (timedOut && !stop.signal.aborted) {
1556
+ at = Date.now();
1557
+ stop.abort(new InfraError(mcpTimeoutMessage(timedOut)));
1558
+ }
1559
+ };
1560
+ const timer = setInterval(() => {
1561
+ reading ??= poll().finally(() => {
1562
+ reading = void 0;
1563
+ });
1564
+ }, MCP_POLL_MS);
1565
+ return {
1566
+ signal: stop.signal,
1567
+ close: () => clearInterval(timer),
1568
+ async end() {
1569
+ clearInterval(timer);
1570
+ await reading;
1571
+ await poll();
1572
+ return {
1573
+ entries,
1574
+ timedOut,
1575
+ at: at || Date.now()
1576
+ };
1577
+ }
1578
+ };
1579
+ }
1715
1580
  /**
1716
1581
  * One turn. Sends the prompt to the started session, records the turn as it goes (an
1717
1582
  * errored entry first, so a turn that throws is still in the record), takes the agent's context
@@ -1742,11 +1607,48 @@ function turnSender(a, takeContext, checks) {
1742
1607
  turn: turnNumber,
1743
1608
  prompt
1744
1609
  }, `turn ${turnNumber} prompt`);
1745
- const { cutoff, cancel } = cutOff(a.signal, entry.startedAt);
1610
+ const watch = a.mcpStartup && watchMcpStartup(a.mcpStartup);
1611
+ const signal = watch ? AbortSignal.any([a.signal, watch.signal]) : a.signal;
1612
+ const { cutoff, cancel } = cutOff(signal, entry.startedAt);
1746
1613
  const sendStarted = Date.now();
1747
- const { raw, ...result } = await a.timed("agentMs", () => Promise.race([agent.send(prompt, a.signal), cutoff])).finally(cancel);
1614
+ const sending = agent.send(prompt, signal).catch((error) => {
1615
+ if (!watch?.signal.aborted || a.signal.aborted) throw error;
1616
+ return {
1617
+ text: "",
1618
+ status: "aborted",
1619
+ transcript: [],
1620
+ usage: null,
1621
+ model: null,
1622
+ unreported: [],
1623
+ startedAt: entry.startedAt,
1624
+ durationMs: Date.now() - sendStarted
1625
+ };
1626
+ });
1627
+ const { raw, ...sent } = await a.timed("agentMs", () => Promise.race([sending, cutoff])).catch(async (error) => {
1628
+ if (watch) entry.mcpStartup = mcpStartupOf((await watch.end()).entries);
1629
+ throw error;
1630
+ }).finally(() => {
1631
+ cancel();
1632
+ watch?.close();
1633
+ });
1748
1634
  const sendWallMs = Date.now() - sendStarted;
1749
- Object.assign(entry, result, raw ? { raw } : {}, { durationMs: Math.max(result.durationMs, sendWallMs) });
1635
+ const startup = await watch?.end();
1636
+ const timedOut = startup?.timedOut && mcpTimeoutMessage(startup.timedOut);
1637
+ const seenAt = startup?.at ?? Date.now();
1638
+ const result = timedOut ? {
1639
+ ...sent,
1640
+ status: "error",
1641
+ transcript: [...sent.transcript, {
1642
+ type: "error",
1643
+ message: timedOut,
1644
+ ts: new Date(seenAt).toISOString(),
1645
+ atMs: Math.max(0, seenAt - Date.parse(sent.startedAt))
1646
+ }].sort((x, y) => x.atMs - y.atMs)
1647
+ } : sent;
1648
+ Object.assign(entry, result, raw ? { raw } : {}, {
1649
+ durationMs: Math.max(result.durationMs, sendWallMs),
1650
+ ...startup ? { mcpStartup: mcpStartupOf(startup.entries) } : {}
1651
+ });
1750
1652
  for (const event of result.transcript) debugTrace("harness", "transcript", {
1751
1653
  turn: turnNumber,
1752
1654
  ...event
@@ -1765,6 +1667,12 @@ function turnSender(a, takeContext, checks) {
1765
1667
  });
1766
1668
  const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
1767
1669
  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}` : ""}`);
1670
+ for (const s of entry.mcpStartup ?? []) debugLog("harness", `turn ${turnNumber} mcp ${s.server} ${s.ms === null ? "didn't answer initialize" : `answered initialize in ${formatDuration(s.ms)}`}`);
1671
+ if (timedOut) {
1672
+ const error = new InfraError(timedOut);
1673
+ a.startFailure ??= { error };
1674
+ throw error;
1675
+ }
1768
1676
  a.signal.throwIfAborted();
1769
1677
  const diff = await a.timed("diffMs", () => diffSinceSnapshot(box, a.signal));
1770
1678
  a.record.diff = diff;
@@ -1996,11 +1904,18 @@ function planEval(declaration, ctx) {
1996
1904
  const [first] = unbrokered(resolved.env, plannedAttempts);
1997
1905
  if (first) throw new UsageError(unbrokeredError(first, where, ctx.config.file));
1998
1906
  }
1907
+ const pluginNotes = [];
1908
+ const pluginLabels = /* @__PURE__ */ new Map();
1999
1909
  for (const planned of plannedAttempts) {
2000
1910
  const why = unenforceable(planned.egress, planned.sandbox, where);
2001
1911
  if (why) throw new UsageError(why);
1912
+ const on = `${where} on ${harnessId(planned.harness)}`;
2002
1913
  const mcp = refusedMcp(planned.harness, planned.environment.layers);
2003
- if (mcp) throw new UsageError(`${where} on ${harnessId(planned.harness)}: ${mcp}`);
1914
+ if (mcp) throw new UsageError(`${on}: ${mcp}`);
1915
+ const plugins = planPlugins(planned, ctx.config.root);
1916
+ if (plugins.refusal) throw new UsageError(`${on}: ${plugins.refusal}`);
1917
+ for (const note of plugins.notes) if (!pluginNotes.includes(note)) pluginNotes.push(note);
1918
+ if (plugins.labels.length) pluginLabels.set(planned, plugins.labels);
2004
1919
  }
2005
1920
  const path = ctx.file ? relativeFile(ctx.config.root, ctx.file) : null;
2006
1921
  const rankFile = relativeFile(ctx.config.root, ctx.file ?? "");
@@ -2013,6 +1928,7 @@ function planEval(declaration, ctx) {
2013
1928
  configFile: ctx.config.file,
2014
1929
  file: path
2015
1930
  });
1931
+ plan.notes.push(...pluginNotes);
2016
1932
  const setByOf = (planned) => ({
2017
1933
  sandbox: sandboxSetBy(planned, resolved, ctx.config.file),
2018
1934
  harness: harnessBy
@@ -2048,7 +1964,11 @@ function planEval(declaration, ctx) {
2048
1964
  if (!cells.includes(planned.cell.key)) cells.push(planned.cell.key);
2049
1965
  const workspace = workspaceOf(planned.environment.layers);
2050
1966
  const files = filesOf(planned.environment.layers);
2051
- const setBy = setByOf(planned);
1967
+ const builds = builtPlugins(planned.environment.layers);
1968
+ const setBy = builds.length ? {
1969
+ ...setByOf(planned),
1970
+ pluginBuilds: `environment ${planned.environment.name}${path ? ` in ${path}` : ""}`
1971
+ } : setByOf(planned);
2052
1972
  const id = evalId(rankFile, [...groups, name]);
2053
1973
  const key = attemptKey(id, planned.cell.key, planned.n);
2054
1974
  const skip = ctx.completed?.includes(key) ? "already completed in this run" : void 0;
@@ -2069,6 +1989,8 @@ function planEval(declaration, ctx) {
2069
1989
  egress: planned.egress,
2070
1990
  ...workspace ? { workspace } : {},
2071
1991
  ...files.length ? { files } : {},
1992
+ ...pluginLabels.has(planned) ? { plugins: pluginLabels.get(planned) } : {},
1993
+ ...builds.length ? { pluginBuilds: builds } : {},
2072
1994
  plan: skip ? {
2073
1995
  ...plan,
2074
1996
  skip
@@ -2108,6 +2030,61 @@ function planEval(declaration, ctx) {
2108
2030
  };
2109
2031
  });
2110
2032
  }
2033
+ /**
2034
+ * What the plan can tell of an attempt's plugins before any is fetched: a build outside Docker, a
2035
+ * local plugin's content, and the harness's refusal, as a refusal; the commands that fetch a
2036
+ * package at start, as notes under `allow-all`; and a label for each plugin. Git and npm plugins
2037
+ * are read when the attempt resolves its environment, which refuses them the same way.
2038
+ */
2039
+ function planPlugins(planned, root) {
2040
+ const { layers } = planned.environment;
2041
+ const sources = layeredPlugins(layers);
2042
+ if (sources.length === 0) return {
2043
+ notes: [],
2044
+ labels: []
2045
+ };
2046
+ const read = [];
2047
+ const unread = [];
2048
+ const labels = [];
2049
+ for (const source of sources) {
2050
+ const call = pluginCall(source, root);
2051
+ const p = localPlugin(source, root);
2052
+ if (p instanceof PluginProblem) return {
2053
+ refusal: p.message,
2054
+ notes: [],
2055
+ labels
2056
+ };
2057
+ const build = buildRefusal(source, planned.sandbox, p?.name, root);
2058
+ if (build) return {
2059
+ refusal: build,
2060
+ notes: [],
2061
+ labels
2062
+ };
2063
+ if (p) read.push(p);
2064
+ else unread.push(call);
2065
+ labels.push(p ? `${p.name} (${p.kept.join(", ")})` : pluginSourceLabel(source, root));
2066
+ }
2067
+ const twice = duplicateLocalPlugin(layers, root);
2068
+ if (twice) return {
2069
+ refusal: twice,
2070
+ notes: [],
2071
+ labels
2072
+ };
2073
+ const own = {
2074
+ skills: localSkills(layers, root),
2075
+ mcp: [...new Set(layers.flatMap((l) => Object.keys(l.spec.mcp ?? {})))]
2076
+ };
2077
+ const refusal = refusedPlugins(planned.harness, read, own, unread);
2078
+ if (refusal) return {
2079
+ refusal,
2080
+ notes: [],
2081
+ labels
2082
+ };
2083
+ return {
2084
+ ...fetchesAtStart(read, allowedHosts(planned.egress)),
2085
+ labels
2086
+ };
2087
+ }
2111
2088
  /** The run-wide cap, plus the sandbox provider's and harness's own caps when they set one. */
2112
2089
  function attemptLimits(planned, concurrency) {
2113
2090
  const limits = { attempts: concurrency };
@@ -2348,4 +2325,4 @@ function optionsFrom(written, where, base) {
2348
2325
  //#endregion
2349
2326
  export { assertionRecords as i, evaluate as n, group as r, declareTree as t };
2350
2327
 
2351
- //# sourceMappingURL=evaluate-D4zeRGBF.js.map
2328
+ //# sourceMappingURL=evaluate-CXQ1o-pd.js.map