@tangle-network/agent-bench 0.3.5 → 0.3.6

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 (182) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +13 -1
  3. package/dist/adapters.d.ts +15 -0
  4. package/dist/adapters.js +43 -0
  5. package/dist/adapters.js.map +1 -0
  6. package/dist/benchmarks/_harness.d.ts +125 -0
  7. package/dist/benchmarks/_harness.js +33 -0
  8. package/dist/benchmarks/_harness.js.map +1 -0
  9. package/dist/benchmarks/aec-bench.d.ts +27 -0
  10. package/dist/benchmarks/aec-bench.js +8 -0
  11. package/dist/benchmarks/aec-bench.js.map +1 -0
  12. package/dist/benchmarks/agentbench.d.ts +16 -0
  13. package/dist/benchmarks/agentbench.js +10 -0
  14. package/dist/benchmarks/agentbench.js.map +1 -0
  15. package/dist/benchmarks/appworld.d.ts +37 -0
  16. package/dist/benchmarks/appworld.js +14 -0
  17. package/dist/benchmarks/appworld.js.map +1 -0
  18. package/dist/benchmarks/bfcl.d.ts +18 -0
  19. package/dist/benchmarks/bfcl.js +10 -0
  20. package/dist/benchmarks/bfcl.js.map +1 -0
  21. package/dist/benchmarks/cad-design.d.ts +45 -0
  22. package/dist/benchmarks/cad-design.js +7 -0
  23. package/dist/benchmarks/cad-design.js.map +1 -0
  24. package/dist/benchmarks/cadbench.d.ts +19 -0
  25. package/dist/benchmarks/cadbench.js +8 -0
  26. package/dist/benchmarks/cadbench.js.map +1 -0
  27. package/dist/benchmarks/cadgenbench.d.ts +22 -0
  28. package/dist/benchmarks/cadgenbench.js +8 -0
  29. package/dist/benchmarks/cadgenbench.js.map +1 -0
  30. package/dist/benchmarks/commit0.d.ts +31 -0
  31. package/dist/benchmarks/commit0.js +10 -0
  32. package/dist/benchmarks/commit0.js.map +1 -0
  33. package/dist/benchmarks/crag.d.ts +14 -0
  34. package/dist/benchmarks/crag.js +9 -0
  35. package/dist/benchmarks/crag.js.map +1 -0
  36. package/dist/benchmarks/dabstep.d.ts +18 -0
  37. package/dist/benchmarks/dabstep.js +10 -0
  38. package/dist/benchmarks/dabstep.js.map +1 -0
  39. package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
  40. package/dist/benchmarks/enterpriseops-gym.js +10 -0
  41. package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
  42. package/dist/benchmarks/finresearchbench.d.ts +15 -0
  43. package/dist/benchmarks/finresearchbench.js +8 -0
  44. package/dist/benchmarks/finresearchbench.js.map +1 -0
  45. package/dist/benchmarks/finsearchcomp.d.ts +49 -0
  46. package/dist/benchmarks/finsearchcomp.js +7 -0
  47. package/dist/benchmarks/finsearchcomp.js.map +1 -0
  48. package/dist/benchmarks/frames.d.ts +59 -0
  49. package/dist/benchmarks/frames.js +13 -0
  50. package/dist/benchmarks/frames.js.map +1 -0
  51. package/dist/benchmarks/hotpotqa.d.ts +48 -0
  52. package/dist/benchmarks/hotpotqa.js +15 -0
  53. package/dist/benchmarks/hotpotqa.js.map +1 -0
  54. package/dist/benchmarks/humaneval.d.ts +53 -0
  55. package/dist/benchmarks/humaneval.js +15 -0
  56. package/dist/benchmarks/humaneval.js.map +1 -0
  57. package/dist/benchmarks/mind2web.d.ts +41 -0
  58. package/dist/benchmarks/mind2web.js +9 -0
  59. package/dist/benchmarks/mind2web.js.map +1 -0
  60. package/dist/benchmarks/nomiracl.d.ts +15 -0
  61. package/dist/benchmarks/nomiracl.js +9 -0
  62. package/dist/benchmarks/nomiracl.js.map +1 -0
  63. package/dist/benchmarks/open-rag-bench.d.ts +14 -0
  64. package/dist/benchmarks/open-rag-bench.js +9 -0
  65. package/dist/benchmarks/open-rag-bench.js.map +1 -0
  66. package/dist/benchmarks/programbench.d.ts +38 -0
  67. package/dist/benchmarks/programbench.js +10 -0
  68. package/dist/benchmarks/programbench.js.map +1 -0
  69. package/dist/benchmarks/rag-shared.d.ts +42 -0
  70. package/dist/benchmarks/rag-shared.js +39 -0
  71. package/dist/benchmarks/rag-shared.js.map +1 -0
  72. package/dist/benchmarks/ragbench.d.ts +16 -0
  73. package/dist/benchmarks/ragbench.js +9 -0
  74. package/dist/benchmarks/ragbench.js.map +1 -0
  75. package/dist/benchmarks/simpleqa.d.ts +64 -0
  76. package/dist/benchmarks/simpleqa.js +11 -0
  77. package/dist/benchmarks/simpleqa.js.map +1 -0
  78. package/dist/benchmarks/swe-bench.d.ts +56 -0
  79. package/dist/benchmarks/swe-bench.js +14 -0
  80. package/dist/benchmarks/swe-bench.js.map +1 -0
  81. package/dist/benchmarks/t2-ragbench.d.ts +14 -0
  82. package/dist/benchmarks/t2-ragbench.js +9 -0
  83. package/dist/benchmarks/t2-ragbench.js.map +1 -0
  84. package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
  85. package/dist/benchmarks/tau-bench-shared.js +10 -0
  86. package/dist/benchmarks/tau-bench-shared.js.map +1 -0
  87. package/dist/benchmarks/tau2-bench.d.ts +7 -0
  88. package/dist/benchmarks/tau2-bench.js +11 -0
  89. package/dist/benchmarks/tau2-bench.js.map +1 -0
  90. package/dist/benchmarks/tau3-banking.d.ts +15 -0
  91. package/dist/benchmarks/tau3-banking.js +9 -0
  92. package/dist/benchmarks/tau3-banking.js.map +1 -0
  93. package/dist/benchmarks/terminal-bench.d.ts +24 -0
  94. package/dist/benchmarks/terminal-bench.js +8 -0
  95. package/dist/benchmarks/terminal-bench.js.map +1 -0
  96. package/dist/benchmarks/toollm.d.ts +16 -0
  97. package/dist/benchmarks/toollm.js +10 -0
  98. package/dist/benchmarks/toollm.js.map +1 -0
  99. package/dist/benchmarks/trata-hedge.d.ts +32 -0
  100. package/dist/benchmarks/trata-hedge.js +7 -0
  101. package/dist/benchmarks/trata-hedge.js.map +1 -0
  102. package/dist/benchmarks/types.d.ts +107 -0
  103. package/dist/benchmarks/types.js +1 -0
  104. package/dist/benchmarks/types.js.map +1 -0
  105. package/dist/benchmarks/webarena-verified.d.ts +16 -0
  106. package/dist/benchmarks/webarena-verified.js +10 -0
  107. package/dist/benchmarks/webarena-verified.js.map +1 -0
  108. package/dist/chunk-2PVVP7GN.js +197 -0
  109. package/dist/chunk-2PVVP7GN.js.map +1 -0
  110. package/dist/chunk-2XU6OGEN.js +170 -0
  111. package/dist/chunk-2XU6OGEN.js.map +1 -0
  112. package/dist/chunk-53UPUNBZ.js +325 -0
  113. package/dist/chunk-53UPUNBZ.js.map +1 -0
  114. package/dist/chunk-5SBJCB6W.js +144 -0
  115. package/dist/chunk-5SBJCB6W.js.map +1 -0
  116. package/dist/chunk-7WSD27QQ.js +118 -0
  117. package/dist/chunk-7WSD27QQ.js.map +1 -0
  118. package/dist/chunk-C7T7WEK2.js +103 -0
  119. package/dist/chunk-C7T7WEK2.js.map +1 -0
  120. package/dist/chunk-CKUVRZ2T.js +251 -0
  121. package/dist/chunk-CKUVRZ2T.js.map +1 -0
  122. package/dist/chunk-HBSWHQNJ.js +30 -0
  123. package/dist/chunk-HBSWHQNJ.js.map +1 -0
  124. package/dist/chunk-HHXFIHXC.js +116 -0
  125. package/dist/chunk-HHXFIHXC.js.map +1 -0
  126. package/dist/chunk-IFAV6KEM.js +276 -0
  127. package/dist/chunk-IFAV6KEM.js.map +1 -0
  128. package/dist/chunk-INNOYXCP.js +387 -0
  129. package/dist/chunk-INNOYXCP.js.map +1 -0
  130. package/dist/chunk-J3KDJNX2.js +182 -0
  131. package/dist/chunk-J3KDJNX2.js.map +1 -0
  132. package/dist/chunk-JRWWGMK7.js +148 -0
  133. package/dist/chunk-JRWWGMK7.js.map +1 -0
  134. package/dist/chunk-JTHWEDEW.js +32 -0
  135. package/dist/chunk-JTHWEDEW.js.map +1 -0
  136. package/dist/chunk-KDIKRJGB.js +120 -0
  137. package/dist/chunk-KDIKRJGB.js.map +1 -0
  138. package/dist/chunk-LRRD7NAG.js +301 -0
  139. package/dist/chunk-LRRD7NAG.js.map +1 -0
  140. package/dist/chunk-ODT47UAY.js +221 -0
  141. package/dist/chunk-ODT47UAY.js.map +1 -0
  142. package/dist/chunk-PA2ZKHJC.js +230 -0
  143. package/dist/chunk-PA2ZKHJC.js.map +1 -0
  144. package/dist/chunk-PPYSEKFM.js +182 -0
  145. package/dist/chunk-PPYSEKFM.js.map +1 -0
  146. package/dist/chunk-PUIRNYI7.js +189 -0
  147. package/dist/chunk-PUIRNYI7.js.map +1 -0
  148. package/dist/chunk-R36V2VP7.js +169 -0
  149. package/dist/chunk-R36V2VP7.js.map +1 -0
  150. package/dist/chunk-R67DFVLO.js +142 -0
  151. package/dist/chunk-R67DFVLO.js.map +1 -0
  152. package/dist/chunk-SEVJPLZC.js +260 -0
  153. package/dist/chunk-SEVJPLZC.js.map +1 -0
  154. package/dist/chunk-SYDW647C.js +318 -0
  155. package/dist/chunk-SYDW647C.js.map +1 -0
  156. package/dist/chunk-TBKU5XQI.js +228 -0
  157. package/dist/chunk-TBKU5XQI.js.map +1 -0
  158. package/dist/chunk-TSWPNOYM.js +147 -0
  159. package/dist/chunk-TSWPNOYM.js.map +1 -0
  160. package/dist/chunk-UAIOHCUK.js +27 -0
  161. package/dist/chunk-UAIOHCUK.js.map +1 -0
  162. package/dist/chunk-UPAMRDX4.js +233 -0
  163. package/dist/chunk-UPAMRDX4.js.map +1 -0
  164. package/dist/chunk-VQRS7VUC.js +342 -0
  165. package/dist/chunk-VQRS7VUC.js.map +1 -0
  166. package/dist/chunk-X3BTXCJ4.js +262 -0
  167. package/dist/chunk-X3BTXCJ4.js.map +1 -0
  168. package/dist/chunk-X5YKXC6V.js +211 -0
  169. package/dist/chunk-X5YKXC6V.js.map +1 -0
  170. package/dist/chunk-Y6O2OCUO.js +130 -0
  171. package/dist/chunk-Y6O2OCUO.js.map +1 -0
  172. package/dist/chunk-YCGY7UIZ.js +208 -0
  173. package/dist/chunk-YCGY7UIZ.js.map +1 -0
  174. package/dist/chunk-Z7ML6L77.js +162 -0
  175. package/dist/chunk-Z7ML6L77.js.map +1 -0
  176. package/dist/chunk-ZEWMTR5M.js +136 -0
  177. package/dist/chunk-ZEWMTR5M.js.map +1 -0
  178. package/dist/index.d.ts +355 -0
  179. package/dist/index.js +1908 -0
  180. package/dist/index.js.map +1 -0
  181. package/package.json +22 -6
  182. package/scripts/verify-packed-consumer.mjs +12 -1
package/dist/index.js ADDED
@@ -0,0 +1,1908 @@
1
+ import {
2
+ ADAPTERS,
3
+ resolveAdapter
4
+ } from "./chunk-5SBJCB6W.js";
5
+ import "./chunk-2PVVP7GN.js";
6
+ import "./chunk-VQRS7VUC.js";
7
+ import "./chunk-Z7ML6L77.js";
8
+ import {
9
+ createRagBenchAdapter
10
+ } from "./chunk-R67DFVLO.js";
11
+ import "./chunk-SEVJPLZC.js";
12
+ import {
13
+ createSweBenchAdapter,
14
+ scoreSweReport,
15
+ sweEvaluationArgv,
16
+ swePatchOutput
17
+ } from "./chunk-CKUVRZ2T.js";
18
+ import {
19
+ createT2RagBenchAdapter
20
+ } from "./chunk-Y6O2OCUO.js";
21
+ import "./chunk-HBSWHQNJ.js";
22
+ import "./chunk-UAIOHCUK.js";
23
+ import "./chunk-R36V2VP7.js";
24
+ import "./chunk-JRWWGMK7.js";
25
+ import "./chunk-INNOYXCP.js";
26
+ import "./chunk-PA2ZKHJC.js";
27
+ import "./chunk-PPYSEKFM.js";
28
+ import "./chunk-TBKU5XQI.js";
29
+ import {
30
+ createNoMiraclAdapter
31
+ } from "./chunk-TSWPNOYM.js";
32
+ import {
33
+ createOpenRagBenchAdapter
34
+ } from "./chunk-KDIKRJGB.js";
35
+ import "./chunk-ZEWMTR5M.js";
36
+ import "./chunk-PUIRNYI7.js";
37
+ import "./chunk-C7T7WEK2.js";
38
+ import {
39
+ runRefineLoop
40
+ } from "./chunk-JTHWEDEW.js";
41
+ import "./chunk-2XU6OGEN.js";
42
+ import {
43
+ createCragAdapter
44
+ } from "./chunk-HHXFIHXC.js";
45
+ import {
46
+ FINAL_ANSWER_SENTINEL,
47
+ answerScoreToBenchScore,
48
+ contextBlock,
49
+ contextsFrom,
50
+ normalizeAnswer,
51
+ parseCitations,
52
+ parseFinalAnswer,
53
+ ragAnswerOutput,
54
+ scoreAnswerArtifact,
55
+ tokenF1
56
+ } from "./chunk-X3BTXCJ4.js";
57
+ import "./chunk-J3KDJNX2.js";
58
+ import "./chunk-X5YKXC6V.js";
59
+ import "./chunk-YCGY7UIZ.js";
60
+ import "./chunk-UPAMRDX4.js";
61
+ import "./chunk-ODT47UAY.js";
62
+ import "./chunk-7WSD27QQ.js";
63
+ import "./chunk-SYDW647C.js";
64
+ import "./chunk-IFAV6KEM.js";
65
+ import {
66
+ StagedJudgeError,
67
+ runStagedJudge
68
+ } from "./chunk-LRRD7NAG.js";
69
+ import "./chunk-53UPUNBZ.js";
70
+
71
+ // src/run-benchmarks.ts
72
+ import { mkdirSync, writeFileSync } from "fs";
73
+ import { openSandboxRun } from "@tangle-network/agent-runtime/loops";
74
+
75
+ // src/resolve-client.ts
76
+ import {
77
+ createExecutor,
78
+ inlineSandboxClient,
79
+ resolveSandboxClient
80
+ } from "@tangle-network/agent-runtime/loops";
81
+ import { Sandbox } from "@tangle-network/sandbox";
82
+
83
+ // src/search-tool.ts
84
+ var webSearchTool = {
85
+ type: "function",
86
+ function: {
87
+ name: "web_search",
88
+ description: "Search the live web for current facts, market data, filings, and news. Returns the top results as title, date, snippet, and URL. Call it as many times as needed, refining the query, before you answer.",
89
+ parameters: {
90
+ type: "object",
91
+ properties: {
92
+ query: {
93
+ type: "string",
94
+ description: "The search query. Be specific \u2014 include entity, metric, period, and units."
95
+ }
96
+ },
97
+ required: ["query"]
98
+ }
99
+ }
100
+ };
101
+ var formatHits = (hits, maxHits) => {
102
+ if (hits.length === 0) return "No results found for that query. Try a different query.";
103
+ return hits.slice(0, maxHits).map((h, i) => {
104
+ const date = h.publishedAt ? ` (${h.publishedAt.slice(0, 10)})` : "";
105
+ return `[${i + 1}] ${h.title ?? "(untitled)"}${date}
106
+ ${h.snippet ?? ""}
107
+ ${h.url ?? ""}`;
108
+ }).join("\n\n");
109
+ };
110
+ var makeSearchExecutor = (cfg) => {
111
+ const maxHits = cfg.maxHits ?? 6;
112
+ return async (name, args) => {
113
+ if (name !== webSearchTool.function.name) {
114
+ throw new Error(`search executor received unknown tool '${name}' (only '${webSearchTool.function.name}')`);
115
+ }
116
+ const query = typeof args.query === "string" ? args.query.trim() : "";
117
+ if (!query) return 'web_search requires a non-empty "query" string.';
118
+ const res = await fetch(`${cfg.routerBaseUrl}/search`, {
119
+ method: "POST",
120
+ headers: { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` },
121
+ body: JSON.stringify({ provider: cfg.provider, query })
122
+ });
123
+ if (!res.ok) {
124
+ throw new Error(`web_search ${cfg.provider} HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
125
+ }
126
+ const body = await res.json();
127
+ const hits = body.data ?? [];
128
+ console.error(` [web_search:${cfg.provider}] "${query.slice(0, 80)}" -> ${hits.length} hits`);
129
+ return formatHits(hits, maxHits);
130
+ };
131
+ };
132
+
133
+ // src/resolve-client.ts
134
+ function resolveBenchClient(opts) {
135
+ const { backend, routerBaseUrl, routerKey, model, searchProvider } = opts;
136
+ if (backend === "router") {
137
+ if (searchProvider) {
138
+ return inlineSandboxClient(
139
+ createExecutor({
140
+ backend: "router-tools",
141
+ routerBaseUrl,
142
+ routerKey,
143
+ model,
144
+ tools: [webSearchTool],
145
+ executeToolCall: makeSearchExecutor({ routerBaseUrl, routerKey, provider: searchProvider })
146
+ })
147
+ );
148
+ }
149
+ return inlineSandboxClient(createExecutor({ backend: "router", routerBaseUrl, routerKey, model }));
150
+ }
151
+ if (backend === "bridge") {
152
+ const bridgeBearer = opts.bridgeBearer ?? routerKey;
153
+ if (!bridgeBearer) throw new Error("resolveBenchClient: backend 'bridge' needs bridgeBearer or routerKey");
154
+ return resolveSandboxClient({
155
+ backend: "bridge",
156
+ bridge: { url: opts.bridgeUrl, bearer: bridgeBearer, model, timeoutMs: opts.timeoutMs }
157
+ });
158
+ }
159
+ return new Sandbox({
160
+ baseUrl: opts.sandboxBaseUrl ?? "https://sandbox.tangle.tools",
161
+ apiKey: routerKey,
162
+ timeoutMs: opts.timeoutMs ?? 12e5
163
+ });
164
+ }
165
+
166
+ // src/run-pool.ts
167
+ async function runPool(items, concurrency, worker, opts) {
168
+ const results = new Array(items.length);
169
+ let next = 0;
170
+ const drain = async () => {
171
+ while (next < items.length) {
172
+ const index = next++;
173
+ const item = items[index];
174
+ let outcome;
175
+ try {
176
+ outcome = { index, item, ok: true, value: await worker(item, index) };
177
+ } catch (err) {
178
+ outcome = { index, item, ok: false, error: err instanceof Error ? err.message : String(err) };
179
+ }
180
+ results[index] = outcome;
181
+ opts?.onResult?.(outcome);
182
+ }
183
+ };
184
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, drain));
185
+ return results;
186
+ }
187
+
188
+ // src/run-benchmarks.ts
189
+ function finalText(events) {
190
+ let text = "";
191
+ for (const ev of events) {
192
+ const e = ev;
193
+ const data = e.data ?? {};
194
+ if (e.type === "message.part.updated") {
195
+ const part = data.part;
196
+ const partType = data.partType ?? part?.type;
197
+ if (partType === "text") {
198
+ if (typeof data.delta === "string") text += data.delta;
199
+ else if (typeof part?.text === "string") text = part.text;
200
+ }
201
+ } else if (typeof data.finalText === "string") text = data.finalText;
202
+ else if (typeof data.response === "string") text = data.response;
203
+ }
204
+ return text.trim();
205
+ }
206
+ var openSandboxShot = async ({ adapter, task, cell, prompt, routerBaseUrl, routerKey, bridgeUrl, bridgeBearer, sandboxBaseUrl, timeoutMs, resolveClient }) => {
207
+ const client = (resolveClient ?? resolveBenchClient)({
208
+ backend: cell.backend ?? "router",
209
+ routerBaseUrl,
210
+ routerKey,
211
+ model: cell.model,
212
+ ...bridgeUrl ? { bridgeUrl } : {},
213
+ ...bridgeBearer ? { bridgeBearer } : {},
214
+ ...sandboxBaseUrl ? { sandboxBaseUrl } : {},
215
+ ...cell.searchProvider ? { searchProvider: cell.searchProvider } : {},
216
+ ...timeoutMs ? { timeoutMs } : {}
217
+ });
218
+ const harness = cell.harness ?? cell.profile?.metadata?.backendType ?? "opencode";
219
+ const profile = cell.profile ?? { name: cell.label, metadata: { backendType: harness } };
220
+ const uniq = Math.random().toString(36).slice(2, 8);
221
+ const agentRun = {
222
+ profile,
223
+ name: cell.label,
224
+ taskToPrompt: () => "",
225
+ sandboxOverrides: {
226
+ name: `bench-${adapter.name}-${task.id}-${uniq}`.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 60),
227
+ environment: "universal",
228
+ backend: { type: harness, model: { provider: "openai", model: cell.model, baseUrl: routerBaseUrl } }
229
+ }
230
+ };
231
+ const deliverable = {
232
+ kind: "events",
233
+ fromEvents: (events) => adapter.output ? adapter.output.parse(events) : finalText(events)
234
+ };
235
+ const controller = new AbortController();
236
+ const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
237
+ const runOptions = {
238
+ agentRun,
239
+ signal: controller.signal,
240
+ runId: `bench:${adapter.name}:${task.id}:${uniq}`,
241
+ scenarioId: task.id
242
+ };
243
+ const boxSetup = adapter.boxSetup;
244
+ if (boxSetup) {
245
+ runOptions.beforeStart = async ({ box, sessionId }) => {
246
+ const setup = boxSetup(task);
247
+ const sres = await box.exec(setup.command, {
248
+ timeoutMs: 3e5,
249
+ sessionId,
250
+ ...setup.cwd ? { cwd: setup.cwd } : {}
251
+ });
252
+ if (sres.exitCode !== 0)
253
+ throw new Error(
254
+ `boxSetup failed (exit ${sres.exitCode}): ${(sres.stderr ?? "").slice(0, 200)}`
255
+ );
256
+ };
257
+ }
258
+ const run = await openSandboxRun(client, runOptions, deliverable);
259
+ try {
260
+ const turn = await run.start(prompt ?? task.prompt);
261
+ let artifact = (turn.out ?? "").trim();
262
+ let boxExtractError;
263
+ if (adapter.boxExtract) {
264
+ try {
265
+ const ex = adapter.boxExtract(task);
266
+ const res = await run.box.exec(ex.command, {
267
+ timeoutMs: 12e4,
268
+ sessionId: run.sessionId,
269
+ ...ex.cwd ? { cwd: ex.cwd } : {}
270
+ });
271
+ const boxArtifact = (res.stdout ?? "").trim();
272
+ if (res.exitCode !== 0)
273
+ boxExtractError = `exit ${res.exitCode}: ${(res.stderr ?? "").slice(0, 160)}`;
274
+ else if (boxArtifact.length > 0) artifact = boxArtifact;
275
+ if (process.env.BENCH_ARTIFACT_DIR) {
276
+ try {
277
+ mkdirSync(process.env.BENCH_ARTIFACT_DIR, { recursive: true });
278
+ const safe = `${adapter.name}_${task.id}_${uniq}`.replace(/[^a-zA-Z0-9_.-]/g, "_");
279
+ const map = await run.box.exec(
280
+ 'echo "PWD:"; pwd; echo "LS:"; ls -la; echo "GITROOTS:"; find / -maxdepth 5 -type d -name .git 2>/dev/null; echo "SETTINGS:"; find / -maxdepth 8 -name global_settings.py -path "*conf*" 2>/dev/null',
281
+ { timeoutMs: 6e4, sessionId: run.sessionId }
282
+ ).catch((e) => ({ exitCode: -1, stdout: "", stderr: String(e) }));
283
+ writeFileSync(
284
+ `${process.env.BENCH_ARTIFACT_DIR}/${safe}.exec.json`,
285
+ JSON.stringify(
286
+ { sessionId: run.sessionId, extract: res, map: { exitCode: map.exitCode, stdout: map.stdout, stderr: map.stderr } },
287
+ null,
288
+ 2
289
+ )
290
+ );
291
+ } catch {
292
+ }
293
+ }
294
+ } catch (err) {
295
+ boxExtractError = err instanceof Error ? err.message.slice(0, 160) : String(err);
296
+ }
297
+ }
298
+ const detail = turn.readError !== void 0 ? `read: ${turn.readError.slice(0, 160)}` : boxExtractError !== void 0 ? `boxExtract: ${boxExtractError}` : void 0;
299
+ if (process.env.BENCH_ARTIFACT_DIR) {
300
+ try {
301
+ mkdirSync(process.env.BENCH_ARTIFACT_DIR, { recursive: true });
302
+ const safe = `${adapter.name}_${task.id}_${uniq}`.replace(/[^a-zA-Z0-9_.-]/g, "_");
303
+ writeFileSync(`${process.env.BENCH_ARTIFACT_DIR}/${safe}.patch`, artifact);
304
+ } catch {
305
+ }
306
+ }
307
+ return {
308
+ artifact,
309
+ ok: artifact.length > 0,
310
+ ...detail ? { detail } : {}
311
+ };
312
+ } finally {
313
+ if (timer) clearTimeout(timer);
314
+ await run.close();
315
+ }
316
+ };
317
+ function parseMaybeJson(value) {
318
+ try {
319
+ return JSON.parse(value);
320
+ } catch {
321
+ return value;
322
+ }
323
+ }
324
+ function redactJudgeLeak(value) {
325
+ if (Array.isArray(value)) return value.map(redactJudgeLeak);
326
+ if (!value || typeof value !== "object") return value;
327
+ const out = {};
328
+ for (const [key, child] of Object.entries(value)) {
329
+ const normalizedKey = key.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
330
+ if (/(^|_)(gold|expected|reference|solution|answer)(_|$)/.test(normalizedKey)) continue;
331
+ out[key] = redactJudgeLeak(child);
332
+ }
333
+ return out;
334
+ }
335
+ function safeFeedback(score) {
336
+ return {
337
+ resolved: score.resolved,
338
+ score: score.score,
339
+ ...score.detail ? { detail: redactJudgeLeak(parseMaybeJson(score.detail)) } : {}
340
+ };
341
+ }
342
+ function truncate(value, max = 4e3) {
343
+ return value.length <= max ? value : `${value.slice(0, max)}
344
+ ...[truncated ${value.length - max} chars]`;
345
+ }
346
+ function retryPrompt(task, history, scores) {
347
+ const attempts = history.map((h) => {
348
+ const score = scores.get(h.round);
349
+ return [
350
+ `Attempt ${h.round}:`,
351
+ "Artifact:",
352
+ truncate(h.artifact),
353
+ score ? `Checker feedback: ${JSON.stringify(safeFeedback(score))}` : void 0
354
+ ].filter(Boolean).join("\n");
355
+ }).join("\n\n");
356
+ return [
357
+ "Retry the benchmark task. The previous artifact did not pass the checker.",
358
+ "Use only the original task statement and supplied context. Do not invent facts.",
359
+ "Return the corrected artifact in exactly the format requested by the original task.",
360
+ "",
361
+ "Original task:",
362
+ task.prompt,
363
+ "",
364
+ "Previous attempts and safe checker feedback:",
365
+ attempts
366
+ ].join("\n");
367
+ }
368
+ async function loopedShot(input, shot, attempts) {
369
+ const scores = /* @__PURE__ */ new Map();
370
+ const result = await runRefineLoop({
371
+ rounds: attempts,
372
+ prompt: (round, history) => round === 1 ? input.task.prompt : retryPrompt(input.task, history, scores),
373
+ runShot: async (prompt, round) => {
374
+ const out = await shot({ ...input, prompt, attempt: round });
375
+ return { artifact: out.artifact, note: out.detail };
376
+ },
377
+ judge: async (artifact, round) => {
378
+ const score = await input.adapter.judge(input.task, artifact);
379
+ scores.set(round, score);
380
+ return { valid: score.resolved, score: score.score };
381
+ }
382
+ });
383
+ const best = result.rounds.reduce((winner, candidate) => {
384
+ const a = scores.get(winner.round);
385
+ const b = scores.get(candidate.round);
386
+ if (!a) return candidate;
387
+ if (!b) return winner;
388
+ if (b.resolved && !a.resolved) return candidate;
389
+ if (b.resolved === a.resolved && b.score > a.score) return candidate;
390
+ return winner;
391
+ }, result.rounds[0]);
392
+ const bestScore = scores.get(best.round);
393
+ return {
394
+ artifact: best.artifact,
395
+ ok: best.artifact.trim().length > 0,
396
+ detail: JSON.stringify({
397
+ mode: "refine-loop",
398
+ attempts: result.rounds.length,
399
+ selectedAttempt: best.round,
400
+ resolvedDuringLoop: result.resolved,
401
+ selectedScore: bestScore?.score ?? null,
402
+ rounds: result.rounds.map((round) => ({
403
+ attempt: round.round,
404
+ score: scores.get(round.round)?.score ?? null,
405
+ resolved: scores.get(round.round)?.resolved ?? null,
406
+ note: round.note ?? null
407
+ }))
408
+ })
409
+ };
410
+ }
411
+ function combineDetails(runDetail, scoreDetail) {
412
+ if (runDetail && scoreDetail) {
413
+ return JSON.stringify({ run: parseMaybeJson(runDetail), score: parseMaybeJson(scoreDetail) });
414
+ }
415
+ return runDetail ?? scoreDetail;
416
+ }
417
+ async function prepareBenchmarks(benchmarks, resolve4, opts) {
418
+ const ready = [];
419
+ const unavailable = [];
420
+ for (const benchmark of benchmarks) {
421
+ const adapter = resolve4(benchmark);
422
+ try {
423
+ await adapter.preflight();
424
+ const tasks = await adapter.loadTasks({
425
+ ...opts.n !== void 0 ? { limit: opts.n } : {},
426
+ ...opts.ids ? { ids: opts.ids } : {},
427
+ ...opts.split ? { split: opts.split } : {}
428
+ });
429
+ if (tasks.length === 0) {
430
+ unavailable.push({ benchmark, reason: "loadTasks returned no tasks" });
431
+ continue;
432
+ }
433
+ if (opts.verifyJudge !== false) {
434
+ const gold = await adapter.goldArtifact(tasks[0]);
435
+ if (gold !== void 0) {
436
+ const verdict = await adapter.judge(tasks[0], gold);
437
+ if (!verdict.resolved) {
438
+ unavailable.push({ benchmark, reason: `judge rejected its own gold on ${tasks[0].id} \u2014 judge is miscalibrated` });
439
+ continue;
440
+ }
441
+ }
442
+ }
443
+ ready.push({ benchmark, adapter, tasks });
444
+ } catch (err) {
445
+ unavailable.push({ benchmark, reason: err instanceof Error ? err.message : String(err) });
446
+ }
447
+ }
448
+ return { ready, unavailable };
449
+ }
450
+ async function runBenchmarks(opts) {
451
+ if (opts.benchmarks.length === 0) throw new Error("runBenchmarks: no benchmarks selected");
452
+ if (opts.cells.length === 0) throw new Error("runBenchmarks: no cells to run");
453
+ const reps = Math.max(1, opts.reps ?? 1);
454
+ const loopAttempts = Math.max(1, opts.loopAttempts ?? 1);
455
+ const shot = opts.runShot ?? openSandboxShot;
456
+ const { ready, unavailable } = await prepareBenchmarks(opts.benchmarks, opts.resolveAdapter ?? resolveAdapter, opts);
457
+ const jobs = [];
458
+ for (const { benchmark, adapter, tasks } of ready)
459
+ for (const cell of opts.cells) for (const task of tasks) for (let rep = 0; rep < reps; rep += 1) jobs.push({ benchmark, adapter, cell, task, rep });
460
+ const perTask = [];
461
+ await runPool(jobs, Math.max(1, opts.concurrency ?? 4), async (job, index) => {
462
+ const startedAt = Date.now();
463
+ let result;
464
+ try {
465
+ const shotInput = {
466
+ adapter: job.adapter,
467
+ task: job.task,
468
+ cell: job.cell,
469
+ routerBaseUrl: opts.routerBaseUrl,
470
+ routerKey: opts.routerKey,
471
+ ...opts.bridgeUrl ? { bridgeUrl: opts.bridgeUrl } : {},
472
+ ...opts.bridgeBearer ? { bridgeBearer: opts.bridgeBearer } : {},
473
+ ...opts.sandboxBaseUrl ? { sandboxBaseUrl: opts.sandboxBaseUrl } : {},
474
+ ...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {},
475
+ ...opts.resolveClient ? { resolveClient: opts.resolveClient } : {}
476
+ };
477
+ const out = loopAttempts > 1 ? await loopedShot(shotInput, shot, loopAttempts) : await shot(shotInput);
478
+ const score = await job.adapter.judge(job.task, out.artifact);
479
+ result = {
480
+ benchmark: job.benchmark,
481
+ cell: job.cell.label,
482
+ taskId: job.task.id,
483
+ rep: job.rep,
484
+ resolved: out.ok && score.resolved,
485
+ score: out.ok ? score.score : 0,
486
+ ok: out.ok,
487
+ ...out.detail ?? score.detail ? { detail: combineDetails(out.detail, score.detail) } : {},
488
+ wallMs: Date.now() - startedAt
489
+ };
490
+ } catch (err) {
491
+ result = {
492
+ benchmark: job.benchmark,
493
+ cell: job.cell.label,
494
+ taskId: job.task.id,
495
+ rep: job.rep,
496
+ resolved: false,
497
+ score: 0,
498
+ ok: false,
499
+ detail: err instanceof Error ? err.message.slice(0, 200) : String(err),
500
+ wallMs: Date.now() - startedAt
501
+ };
502
+ }
503
+ void index;
504
+ perTask.push(result);
505
+ opts.onResult?.(result);
506
+ return result;
507
+ });
508
+ const rows = aggregate(perTask);
509
+ return {
510
+ rows,
511
+ perTask,
512
+ benchmarks: ready.map((r) => r.benchmark),
513
+ cells: opts.cells.map((c) => c.label),
514
+ unavailable
515
+ };
516
+ }
517
+ function aggregate(perTask) {
518
+ const byKey = /* @__PURE__ */ new Map();
519
+ for (const r of perTask) {
520
+ const key = `${r.benchmark}\0${r.cell}`;
521
+ const e = byKey.get(key) ?? { benchmark: r.benchmark, cell: r.cell, n: 0, resolved: 0, errored: 0, scoreSum: 0 };
522
+ e.n += 1;
523
+ if (!r.ok) e.errored += 1;
524
+ else {
525
+ if (r.resolved) e.resolved += 1;
526
+ e.scoreSum += r.score;
527
+ }
528
+ byKey.set(key, e);
529
+ }
530
+ const rows = [...byKey.values()].map((e) => {
531
+ const denom = Math.max(1, e.n - e.errored);
532
+ return {
533
+ benchmark: e.benchmark,
534
+ cell: e.cell,
535
+ n: e.n,
536
+ resolved: e.resolved,
537
+ errored: e.errored,
538
+ resolveRate: e.resolved / denom,
539
+ meanScore: e.scoreSum / denom
540
+ };
541
+ });
542
+ rows.sort((a, b) => a.benchmark === b.benchmark ? b.resolveRate - a.resolveRate : a.benchmark < b.benchmark ? -1 : 1);
543
+ return rows;
544
+ }
545
+ function printBenchmarksReport(report) {
546
+ const lines = [];
547
+ const w = Math.max(8, ...report.rows.map((r) => r.cell.length));
548
+ const b = Math.max(9, ...report.rows.map((r) => r.benchmark.length));
549
+ lines.push(`${"benchmark".padEnd(b)} ${"cell".padEnd(w)} resolve mean n err`);
550
+ for (const r of report.rows)
551
+ lines.push(
552
+ `${r.benchmark.padEnd(b)} ${r.cell.padEnd(w)} ${(r.resolveRate * 100).toFixed(1).padStart(6)}% ${r.meanScore.toFixed(3).padStart(5)} ${String(r.n).padStart(3)} ${String(r.errored).padStart(3)}`
553
+ );
554
+ for (const u of report.unavailable) lines.push(`(skipped ${u.benchmark}: ${u.reason})`);
555
+ return lines.join("\n");
556
+ }
557
+
558
+ // src/pier-agent.ts
559
+ import assert from "assert/strict";
560
+ import { createHash } from "crypto";
561
+ import { chmod, lstat, mkdir as mkdir2, realpath as realpath2, writeFile } from "fs/promises";
562
+ import { dirname, isAbsolute, join as join2, resolve as resolve2 } from "path";
563
+ import { executePreparedAgentCandidate } from "@tangle-network/agent-runtime";
564
+ import { canonicalJson } from "@tangle-network/agent-eval";
565
+
566
+ // src/pier-task-outcome.ts
567
+ import { spawn } from "child_process";
568
+ import { mkdir, mkdtemp, realpath, rm } from "fs/promises";
569
+ import { tmpdir } from "os";
570
+ import { join, resolve } from "path";
571
+ import {
572
+ captureAgentCandidateWorkspaceFiles
573
+ } from "@tangle-network/agent-runtime/candidate-execution";
574
+ async function capturePierTaskOutcome(input) {
575
+ input.signal?.throwIfAborted();
576
+ const repositoryRoot = resolve(input.repositoryRoot);
577
+ const head = (await git(repositoryRoot, ["rev-parse", "HEAD"], void 0, {}, input.signal)).stdout.toString("utf8").trim();
578
+ const headTree = (await git(repositoryRoot, ["rev-parse", "HEAD^{tree}"], void 0, {}, input.signal)).stdout.toString("utf8").trim();
579
+ if (head !== input.baseCommit || headTree !== input.baseTree) {
580
+ throw new Error("Pier task outcome repository drifted from its signed base");
581
+ }
582
+ const gitDir = resolve(
583
+ (await git(repositoryRoot, ["rev-parse", "--absolute-git-dir"], void 0, {}, input.signal)).stdout.toString("utf8").trim()
584
+ );
585
+ if (await realpath(gitDir) !== gitDir || gitDir.includes(":")) {
586
+ throw new Error("Pier task outcome Git directory is unsupported");
587
+ }
588
+ const replacements = (await git(
589
+ repositoryRoot,
590
+ ["for-each-ref", "--format=%(refname)", "refs/replace"],
591
+ void 0,
592
+ {},
593
+ input.signal
594
+ )).stdout.toString("utf8").trim();
595
+ if (replacements) throw new Error("Pier task outcome repository contains Git replace refs");
596
+ const temporary = await mkdtemp(join(tmpdir(), "pier-task-outcome-"));
597
+ try {
598
+ const objectDirectory = join(temporary, "objects");
599
+ const indexFile = join(temporary, "index");
600
+ await mkdir(objectDirectory);
601
+ const environment = {
602
+ GIT_INDEX_FILE: indexFile,
603
+ GIT_OBJECT_DIRECTORY: objectDirectory,
604
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: join(gitDir, "objects")
605
+ };
606
+ await git(repositoryRoot, ["read-tree", input.baseTree], void 0, environment, input.signal);
607
+ const patch = Uint8Array.from(input.patch);
608
+ if (patch.byteLength > 0) {
609
+ await git(
610
+ repositoryRoot,
611
+ ["apply", "--cached", "--binary", "--whitespace=nowarn", "-"],
612
+ patch,
613
+ environment,
614
+ input.signal
615
+ );
616
+ }
617
+ const resultTree = (await git(repositoryRoot, ["write-tree"], void 0, environment, input.signal)).stdout.toString("utf8").trim();
618
+ const entries = parseTreeEntries(
619
+ (await git(
620
+ repositoryRoot,
621
+ ["ls-tree", "-rz", "--full-tree", resultTree],
622
+ void 0,
623
+ environment,
624
+ input.signal
625
+ )).stdout
626
+ );
627
+ if (entries.length === 0) throw new Error("Pier task outcome tree cannot be empty");
628
+ const files = await Promise.all(
629
+ entries.map(async (entry) => {
630
+ if (entry.type !== "blob" || entry.mode !== "100644" && entry.mode !== "100755") {
631
+ throw new Error(`Pier task outcome contains a non-regular entry: ${entry.path}`);
632
+ }
633
+ return {
634
+ path: safeGitPath(entry.path),
635
+ mode: entry.mode === "100755" ? 493 : 420,
636
+ bytes: (await git(
637
+ repositoryRoot,
638
+ ["cat-file", "blob", entry.object],
639
+ void 0,
640
+ environment,
641
+ input.signal
642
+ )).stdout
643
+ };
644
+ })
645
+ );
646
+ const captured = await captureAgentCandidateWorkspaceFiles(files, {
647
+ limits: { maxEmbeddedArtifactBytes: 512 * 1024 * 1024 }
648
+ });
649
+ return {
650
+ kind: "workspace",
651
+ resultTree,
652
+ afterState: captured.snapshot.material,
653
+ archive: captured.archive,
654
+ gitDiff: patch
655
+ };
656
+ } finally {
657
+ await rm(temporary, { recursive: true, force: true });
658
+ }
659
+ }
660
+ function parseTreeEntries(bytes) {
661
+ const raw = Buffer.from(bytes);
662
+ const decoded = raw.toString("utf8");
663
+ if (!Buffer.from(decoded, "utf8").equals(raw)) {
664
+ throw new Error("Pier task outcome contains a non-UTF-8 path");
665
+ }
666
+ return decoded.split("\0").filter(Boolean).map((row) => {
667
+ const tab = row.indexOf(" ");
668
+ const [mode, type, object] = row.slice(0, tab).split(" ");
669
+ const path = row.slice(tab + 1);
670
+ if (tab < 1 || !mode || !type || !object || !path) {
671
+ throw new Error("Pier task outcome contains a malformed Git entry");
672
+ }
673
+ return { mode, type, object, path };
674
+ });
675
+ }
676
+ function safeGitPath(path) {
677
+ const parts = path.split("/");
678
+ if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === "..") || parts[0]?.toLowerCase() === ".git") {
679
+ throw new Error(`Pier task outcome contains an unsafe Git path: ${path}`);
680
+ }
681
+ return path;
682
+ }
683
+ async function git(repositoryRoot, args, input, extraEnvironment, signal) {
684
+ signal?.throwIfAborted();
685
+ const environment = Object.fromEntries(
686
+ Object.entries(process.env).filter(([name]) => !name.startsWith("GIT_"))
687
+ );
688
+ Object.assign(environment, {
689
+ GIT_CONFIG_NOSYSTEM: "1",
690
+ GIT_CONFIG_GLOBAL: "/dev/null",
691
+ GIT_CONFIG_SYSTEM: "/dev/null",
692
+ GIT_TERMINAL_PROMPT: "0",
693
+ GIT_NO_REPLACE_OBJECTS: "1",
694
+ LC_ALL: "C",
695
+ ...extraEnvironment
696
+ });
697
+ return await new Promise((resolveResult, reject) => {
698
+ const child = spawn(
699
+ "git",
700
+ [
701
+ "-c",
702
+ "core.hooksPath=/dev/null",
703
+ "-c",
704
+ "protocol.file.allow=never",
705
+ "-C",
706
+ repositoryRoot,
707
+ ...args
708
+ ],
709
+ { env: environment, stdio: ["pipe", "pipe", "pipe"], signal }
710
+ );
711
+ const stdout = [];
712
+ const stderr = [];
713
+ let outputBytes = 0;
714
+ const collect = (target) => (chunk) => {
715
+ outputBytes += chunk.byteLength;
716
+ if (outputBytes > 64 * 1024 * 1024) {
717
+ child.kill("SIGKILL");
718
+ reject(new Error(`git ${args[0] ?? "<command>"} exceeded the output limit`));
719
+ return;
720
+ }
721
+ target.push(Buffer.from(chunk));
722
+ };
723
+ child.stdout.on("data", collect(stdout));
724
+ child.stderr.on("data", collect(stderr));
725
+ child.once("error", reject);
726
+ child.once("close", (code, childSignal) => {
727
+ const out = Buffer.concat(stdout);
728
+ const err = Buffer.concat(stderr);
729
+ if (code !== 0) {
730
+ reject(
731
+ new Error(
732
+ `git ${args[0] ?? "<command>"} failed (${childSignal ?? code}): ${err.toString("utf8").trim()}`
733
+ )
734
+ );
735
+ return;
736
+ }
737
+ resolveResult({ stdout: out, stderr: err });
738
+ });
739
+ child.stdin.end(input);
740
+ });
741
+ }
742
+
743
+ // src/pier-agent.ts
744
+ var adapterImportPath = "pier_agents.tangle_candidate:TangleCandidateAgent";
745
+ var sha256Pattern = /^sha256:[a-f0-9]{64}$/;
746
+ function createPierCandidateRecoveryExecutor(controller) {
747
+ return {
748
+ execute: async () => {
749
+ throw new Error("recovery-only Pier executor cannot start a candidate");
750
+ },
751
+ stop: async (request) => {
752
+ assertTerminationAcknowledged(
753
+ await controller.terminateAndWait({
754
+ executionId: request.executionId,
755
+ executionPlanDigest: request.executionPlanDigest
756
+ })
757
+ );
758
+ return { stopped: true };
759
+ },
760
+ capture: async (request) => {
761
+ const result = await controller.captureResult(request);
762
+ if (!result) return {};
763
+ return {
764
+ evidence: Buffer.from(
765
+ canonicalJson({
766
+ schemaVersion: 1,
767
+ kind: "pier-candidate-recovery-capture",
768
+ executionPlanDigest: request.executionPlanDigest,
769
+ officialResult: Buffer.from(result.resultBytes).toString("base64"),
770
+ taskPatch: Buffer.from(result.taskPatch).toString("base64")
771
+ })
772
+ )
773
+ };
774
+ }
775
+ };
776
+ }
777
+ function sha256(bytes) {
778
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
779
+ }
780
+ function nonEmpty(value, label) {
781
+ if (!value || value.includes("\0")) throw new Error(`${label} must be non-empty without NUL`);
782
+ return value;
783
+ }
784
+ function absoluteHostPath(value, label) {
785
+ if (!isAbsolute(value)) throw new Error(`${label} must be an absolute host path`);
786
+ return resolve2(value);
787
+ }
788
+ function digest(value, label) {
789
+ if (!sha256Pattern.test(value)) throw new Error(`${label} is not a SHA-256 digest`);
790
+ return value;
791
+ }
792
+ function record(value, label) {
793
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
794
+ throw new Error(`${label} must be an object`);
795
+ }
796
+ return value;
797
+ }
798
+ function parseExactJson(bytes, label) {
799
+ try {
800
+ return JSON.parse(Buffer.from(bytes).toString("utf8"));
801
+ } catch (error) {
802
+ throw new Error(`${label} bytes are not UTF-8 JSON`, { cause: error });
803
+ }
804
+ }
805
+ function safeRelativePath(value, label) {
806
+ const parts = value.split("/");
807
+ if (!value || value.includes("\0") || value.includes("\\") || isAbsolute(value) || parts.some((part) => !part || part === "." || part === "..")) {
808
+ throw new Error(`${label} must be a canonical relative POSIX path`);
809
+ }
810
+ return value;
811
+ }
812
+ async function assertRealDirectory(path, label) {
813
+ const stats = await lstat(path);
814
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
815
+ throw new Error(`${label} must be a real directory`);
816
+ }
817
+ if (await realpath2(path) !== path) {
818
+ throw new Error(`${label} has a symlinked path component`);
819
+ }
820
+ }
821
+ async function materializeExecutorFiles(root, files, expected, label) {
822
+ const expectedByPath = new Map(expected.map((file) => [file.path, file]));
823
+ if (expectedByPath.size !== expected.length || files.length !== expected.length) {
824
+ throw new Error(`${label} file set differs from signed evidence`);
825
+ }
826
+ await mkdir2(root, { mode: 448 });
827
+ await assertRealDirectory(root, label);
828
+ for (const file of files) {
829
+ const relative = safeRelativePath(file.path, `${label} file path`);
830
+ const identity = expectedByPath.get(relative);
831
+ if (!identity) throw new Error(`${label} contains unsigned file ${relative}`);
832
+ if (file.mode !== identity.mode || sha256(file.bytes) !== identity.sha256 || identity.byteLength !== void 0 && file.bytes.byteLength !== identity.byteLength) {
833
+ throw new Error(`${label} file ${relative} differs from signed evidence`);
834
+ }
835
+ const target = join2(root, relative);
836
+ const parent = dirname(target);
837
+ if (parent !== root) await mkdir2(parent, { recursive: true, mode: 448 });
838
+ await writeFile(target, file.bytes, { flag: "wx", mode: 384 });
839
+ await chmod(target, file.mode);
840
+ const stats = await lstat(target);
841
+ if (!stats.isFile() || stats.isSymbolicLink() || stats.nlink !== 1 || (stats.mode & 511) !== file.mode) {
842
+ throw new Error(`${label} file ${relative} is not a singly-linked regular file`);
843
+ }
844
+ }
845
+ }
846
+ function protectedEnvironment(prepared, request) {
847
+ const protectedEntries = [];
848
+ for (const [name, value] of Object.entries(request.launch.env)) {
849
+ const publicValue = prepared.launch.env[name];
850
+ if (publicValue === void 0) {
851
+ protectedEntries.push([name, value]);
852
+ } else if (publicValue !== value) {
853
+ throw new Error(`executor request changed signed public environment ${name}`);
854
+ }
855
+ }
856
+ for (const name of Object.keys(prepared.launch.env)) {
857
+ if (!(name in request.launch.env)) {
858
+ throw new Error(`executor request omitted signed public environment ${name}`);
859
+ }
860
+ }
861
+ return Object.freeze(Object.fromEntries(protectedEntries));
862
+ }
863
+ async function stagePreparedPierCandidateExecution(options, request) {
864
+ const { prepared } = options;
865
+ nonEmpty(options.pierVersion, "pierVersion");
866
+ const directory = absoluteHostPath(options.directory, "directory");
867
+ await mkdir2(directory, { mode: 448 });
868
+ await assertRealDirectory(directory, "directory");
869
+ if (request.memory.mode !== "disabled") {
870
+ throw new Error("Pier transport does not yet implement protected isolated memory");
871
+ }
872
+ const taskDirectory = join2(directory, "task");
873
+ const candidateDirectory = request.inputs.candidate ? join2(directory, "candidate") : void 0;
874
+ const profileDirectory = join2(directory, "profile");
875
+ await materializeExecutorFiles(
876
+ taskDirectory,
877
+ request.inputs.task.files,
878
+ request.inputs.task.snapshot.material.files,
879
+ "task executor input"
880
+ );
881
+ if (request.inputs.candidate && candidateDirectory) {
882
+ await materializeExecutorFiles(
883
+ candidateDirectory,
884
+ request.inputs.candidate.files,
885
+ request.inputs.candidate.snapshot.material.files,
886
+ "candidate executor input"
887
+ );
888
+ }
889
+ await materializeExecutorFiles(
890
+ profileDirectory,
891
+ request.profileActivation.files.map((file) => ({
892
+ path: file.path,
893
+ mode: file.mode,
894
+ bytes: Buffer.from(file.content, "utf8")
895
+ })),
896
+ request.profilePlan.value.material.files.map((file) => ({
897
+ path: file.relPath,
898
+ mode: file.mode,
899
+ sha256: file.contentSha256
900
+ })),
901
+ "profile executor input"
902
+ );
903
+ const planDigest = digest(request.executionPlan.value.digest, "execution plan digest");
904
+ if (sha256(request.executionPlan.bytes) !== planDigest) {
905
+ throw new Error("prepared execution-plan bytes do not match their runtime digest");
906
+ }
907
+ assert.deepEqual(
908
+ parseExactJson(request.executionPlan.bytes, "execution plan"),
909
+ request.executionPlan.value.material,
910
+ "prepared execution-plan bytes differ from runtime material"
911
+ );
912
+ const receiptDigest = digest(
913
+ request.materializationReceipt.digest,
914
+ "materialization receipt digest"
915
+ );
916
+ if (sha256(request.materializationReceipt.bytes) !== receiptDigest) {
917
+ throw new Error("prepared materialization-receipt bytes do not match their runtime digest");
918
+ }
919
+ const { digest: _receiptDigest, ...receiptMaterial } = request.materializationReceipt.value;
920
+ assert.deepEqual(
921
+ parseExactJson(request.materializationReceipt.bytes, "materialization receipt"),
922
+ receiptMaterial,
923
+ "prepared receipt bytes differ from runtime material"
924
+ );
925
+ const planPath = join2(directory, "execution-plan.json");
926
+ const receiptPath = join2(directory, "materialization-receipt.json");
927
+ await Promise.all([
928
+ writeFile(planPath, request.executionPlan.bytes, { mode: 384, flag: "wx" }),
929
+ writeFile(receiptPath, request.materializationReceipt.bytes, { mode: 384, flag: "wx" })
930
+ ]);
931
+ const agentArgs = [
932
+ "--agent-import-path",
933
+ adapterImportPath,
934
+ "--model",
935
+ nonEmpty(request.resolvedModel.requested, "resolved requested model"),
936
+ "--agent-kwarg",
937
+ `plan_path=${planPath}`,
938
+ "--agent-kwarg",
939
+ `receipt_path=${receiptPath}`,
940
+ "--agent-kwarg",
941
+ `expected_receipt_digest=${receiptDigest}`,
942
+ "--agent-kwarg",
943
+ `trace_run_id=${nonEmpty(request.trace.runId, "trace run id")}`,
944
+ "--agent-kwarg",
945
+ `task_dir=${taskDirectory}`,
946
+ "--agent-kwarg",
947
+ `profile_dir=${profileDirectory}`,
948
+ "--agent-kwarg",
949
+ `pier_version=${options.pierVersion}`
950
+ ];
951
+ if (candidateDirectory !== void 0) {
952
+ agentArgs.push("--agent-kwarg", `candidate_dir=${candidateDirectory}`);
953
+ }
954
+ return {
955
+ executionId: request.executionId,
956
+ directory,
957
+ taskDirectory,
958
+ ...candidateDirectory ? { candidateDirectory } : {},
959
+ profileDirectory,
960
+ planPath,
961
+ receiptPath,
962
+ agentArgs,
963
+ evaluatorEnv: protectedEnvironment(prepared, request),
964
+ attemptArgs: ["--n-attempts", "1", "--max-retries", "0"]
965
+ };
966
+ }
967
+ function exceptionType(value) {
968
+ const info = value && typeof value === "object" ? value : void 0;
969
+ for (const key of ["exception_type", "type", "name"]) {
970
+ if (typeof info?.[key] === "string") return info[key];
971
+ }
972
+ return void 0;
973
+ }
974
+ function protectedCaptureFromPierResult(request, value) {
975
+ const result = record(value, "Pier trial result");
976
+ const agentResult = record(result.agent_result, "Pier agent_result");
977
+ const metadata = record(agentResult.metadata, "Pier agent_result.metadata");
978
+ const expected = {
979
+ executionId: request.executionId,
980
+ bundleDigest: request.executionPlan.value.material.runCell.bundleDigest,
981
+ executionPlanDigest: request.executionPlan.value.digest,
982
+ materializationReceiptDigest: request.materializationReceipt.digest
983
+ };
984
+ for (const [name, identity] of Object.entries(expected)) {
985
+ if (metadata[name] !== identity) {
986
+ throw new Error(`Pier result ${name} does not match the prepared execution`);
987
+ }
988
+ }
989
+ const reported = record(metadata.termination, "Pier termination");
990
+ const type = exceptionType(result.exception_info);
991
+ if (type?.includes("AgentTimeout")) {
992
+ return {
993
+ executionId: request.executionId,
994
+ termination: {
995
+ kind: "timeout",
996
+ timeoutMs: request.hardLimits.timeoutMs
997
+ }
998
+ };
999
+ }
1000
+ if (type?.includes("Cancelled") || reported.kind === "cancelled") {
1001
+ return { executionId: request.executionId, termination: { kind: "cancelled" } };
1002
+ }
1003
+ if (reported.kind === "exit" && Number.isInteger(reported.exitCode)) {
1004
+ return {
1005
+ executionId: request.executionId,
1006
+ termination: { kind: "exit", exitCode: reported.exitCode }
1007
+ };
1008
+ }
1009
+ throw new Error(`Pier result has no recognized truthful termination${type ? ` (${type})` : ""}`);
1010
+ }
1011
+ function sealPierTrialResult(value) {
1012
+ if (!(value.resultBytes instanceof Uint8Array) || !(value.taskPatch instanceof Uint8Array)) {
1013
+ throw new Error("Pier trial result must contain raw result and patch bytes");
1014
+ }
1015
+ const parsed = parseExactJson(value.resultBytes, "Pier result.json");
1016
+ assert.deepEqual(parsed, value.value, "Pier parsed result differs from result.json bytes");
1017
+ const resultBytes = Uint8Array.from(value.resultBytes);
1018
+ const taskPatch = Uint8Array.from(value.taskPatch);
1019
+ return Object.freeze({
1020
+ value: parsed,
1021
+ get resultBytes() {
1022
+ return Uint8Array.from(resultBytes);
1023
+ },
1024
+ get taskPatch() {
1025
+ return Uint8Array.from(taskPatch);
1026
+ }
1027
+ });
1028
+ }
1029
+ function abortReason(signal) {
1030
+ const reason = signal.reason;
1031
+ if (reason instanceof Error) return reason;
1032
+ return new Error(`Pier candidate execution aborted${reason ? `: ${String(reason)}` : ""}`);
1033
+ }
1034
+ function assertTerminationAcknowledged(acknowledgement) {
1035
+ if (acknowledgement.processExited !== true || acknowledgement.containersRemoved !== true) {
1036
+ throw new Error("Pier termination did not acknowledge process and container death");
1037
+ }
1038
+ }
1039
+ async function awaitAbortableTrial(trial, signal) {
1040
+ if (signal.aborted) {
1041
+ assertTerminationAcknowledged(await trial.terminateAndWait());
1042
+ throw abortReason(signal);
1043
+ }
1044
+ return await new Promise((resolveResult, reject) => {
1045
+ let settled = false;
1046
+ let aborting = false;
1047
+ const settle = (callback) => {
1048
+ if (settled) return;
1049
+ settled = true;
1050
+ signal.removeEventListener("abort", onAbort);
1051
+ callback();
1052
+ };
1053
+ const onAbort = () => {
1054
+ aborting = true;
1055
+ void trial.terminateAndWait().then(
1056
+ (acknowledgement) => {
1057
+ try {
1058
+ assertTerminationAcknowledged(acknowledgement);
1059
+ settle(() => reject(abortReason(signal)));
1060
+ } catch (error) {
1061
+ settle(() => reject(error));
1062
+ }
1063
+ },
1064
+ (error) => settle(() => reject(new Error("Pier termination failed", { cause: error })))
1065
+ );
1066
+ };
1067
+ signal.addEventListener("abort", onAbort, { once: true });
1068
+ trial.result.then(
1069
+ (result) => {
1070
+ if (!aborting && !signal.aborted) settle(() => resolveResult(result));
1071
+ },
1072
+ (error) => {
1073
+ if (!aborting && !signal.aborted) settle(() => reject(error));
1074
+ }
1075
+ );
1076
+ });
1077
+ }
1078
+ async function executePreparedPierCandidate(options) {
1079
+ const trials = /* @__PURE__ */ new Map();
1080
+ const officialResults = /* @__PURE__ */ new Map();
1081
+ const trialIdentity = (executionId, executionPlanDigest) => JSON.stringify([executionId, executionPlanDigest]);
1082
+ const executor = {
1083
+ execute: async (request, context) => {
1084
+ context.signal.throwIfAborted();
1085
+ const staged = await stagePreparedPierCandidateExecution(options, request);
1086
+ context.signal.throwIfAborted();
1087
+ const identity = trialIdentity(request.executionId, request.executionPlan.value.digest);
1088
+ if (trials.has(identity)) throw new Error("Pier trial identity is already active");
1089
+ const trial = options.controller.start(staged, {
1090
+ request,
1091
+ traceStore: context.traceStore,
1092
+ signal: context.signal,
1093
+ deadlineAtMs: context.deadlineAtMs
1094
+ });
1095
+ if (trial.identity.executionId !== request.executionId || trial.identity.executionPlanDigest !== request.executionPlan.value.digest) {
1096
+ assertTerminationAcknowledged(await trial.terminateAndWait());
1097
+ throw new Error("Pier controller returned a different durable trial identity");
1098
+ }
1099
+ const active = { handle: trial };
1100
+ trials.set(identity, active);
1101
+ const result = sealPierTrialResult(await awaitAbortableTrial(trial, context.signal));
1102
+ active.result = result;
1103
+ officialResults.set(request.executionId, result);
1104
+ return protectedCaptureFromPierResult(request, result.value);
1105
+ },
1106
+ stop: async (request) => {
1107
+ assertTerminationAcknowledged(
1108
+ await options.controller.terminateAndWait({
1109
+ executionId: request.executionId,
1110
+ executionPlanDigest: request.executionPlanDigest
1111
+ })
1112
+ );
1113
+ return { stopped: true };
1114
+ },
1115
+ capture: async (request) => {
1116
+ const identity = trialIdentity(request.executionId, request.executionPlanDigest);
1117
+ const active = trials.get(identity);
1118
+ let result = active?.result;
1119
+ if (!result && active) {
1120
+ try {
1121
+ result = sealPierTrialResult(await active.handle.result);
1122
+ } catch {
1123
+ result = void 0;
1124
+ }
1125
+ }
1126
+ if (!result) {
1127
+ const recovered = await options.controller.captureResult(request);
1128
+ result = recovered ? sealPierTrialResult(recovered) : void 0;
1129
+ }
1130
+ if (!result) return {};
1131
+ officialResults.set(request.executionId, result);
1132
+ const task = options.prepared.benchmark.task;
1133
+ const outcome = task.outcome;
1134
+ if (outcome.kind !== "workspace") {
1135
+ throw new Error("Pier candidate execution requires a workspace task outcome");
1136
+ }
1137
+ const repository = task.repository;
1138
+ if (!repository) throw new Error("Pier workspace task is missing repository identity");
1139
+ const taskOutcome = await capturePierTaskOutcome({
1140
+ repositoryRoot: options.prepared.roots.staging.taskRoot,
1141
+ baseCommit: repository.baseCommit,
1142
+ baseTree: repository.baseTree,
1143
+ patch: result.taskPatch
1144
+ });
1145
+ trials.delete(identity);
1146
+ return { taskOutcome };
1147
+ }
1148
+ };
1149
+ const grader = {
1150
+ name: options.grader.name,
1151
+ version: options.grader.version,
1152
+ artifact: options.grader.artifact,
1153
+ run: async (input) => {
1154
+ const official = officialResults.get(input.executionId);
1155
+ if (!official) throw new Error("Pier official result is missing for executable grading");
1156
+ return await options.grader.run({
1157
+ ...input,
1158
+ officialResult: {
1159
+ value: parseExactJson(official.resultBytes, "Pier result.json"),
1160
+ bytes: Uint8Array.from(official.resultBytes)
1161
+ }
1162
+ });
1163
+ }
1164
+ };
1165
+ try {
1166
+ return await executePreparedAgentCandidate(options.prepared, {
1167
+ executor,
1168
+ grader,
1169
+ outputArtifacts: options.outputArtifacts,
1170
+ traceStore: options.traceStore,
1171
+ claimStore: options.claimStore
1172
+ });
1173
+ } finally {
1174
+ officialResults.clear();
1175
+ trials.clear();
1176
+ }
1177
+ }
1178
+
1179
+ // src/pier-trial-controller.ts
1180
+ import { execFileSync, spawn as spawn2 } from "child_process";
1181
+ import { createHash as createHash2 } from "crypto";
1182
+ import {
1183
+ accessSync,
1184
+ closeSync,
1185
+ constants,
1186
+ existsSync,
1187
+ fsyncSync,
1188
+ lstatSync,
1189
+ mkdirSync as mkdirSync2,
1190
+ openSync,
1191
+ readFileSync,
1192
+ readdirSync,
1193
+ realpathSync,
1194
+ renameSync,
1195
+ rmSync,
1196
+ writeFileSync as writeFileSync2
1197
+ } from "fs";
1198
+ import { isAbsolute as isAbsolute2, join as join3, resolve as resolve3 } from "path";
1199
+ import { fileURLToPath } from "url";
1200
+ var identityFile = "identity.json";
1201
+ var terminalFile = "terminal.json";
1202
+ var stopFile = "stop-requested";
1203
+ var sha256Pattern2 = /^sha256:[a-f0-9]{64}$/;
1204
+ var defaultDockerConnectionId = "local-default";
1205
+ function nonEmpty2(value, label) {
1206
+ if (!value || value.includes("\0")) throw new Error(`${label} must be non-empty without NUL`);
1207
+ return value;
1208
+ }
1209
+ function stableDockerConnectionId(value) {
1210
+ if (!/^[A-Za-z0-9._:-]{1,200}$/.test(value)) {
1211
+ throw new Error("Docker connection id must be a non-secret stable name");
1212
+ }
1213
+ return value;
1214
+ }
1215
+ function absolutePath(value, label) {
1216
+ if (!isAbsolute2(value)) throw new Error(`${label} must be an absolute path`);
1217
+ return resolve3(value);
1218
+ }
1219
+ function validateEnvironment(environment, label) {
1220
+ for (const [name, value] of Object.entries(environment)) {
1221
+ if (!name || name.includes("\0") || value?.includes("\0")) {
1222
+ throw new Error(`${label} contains an invalid name or value`);
1223
+ }
1224
+ }
1225
+ }
1226
+ function resolveExecutable(command, searchPath, label) {
1227
+ nonEmpty2(command, label);
1228
+ const candidates = command.includes("/") ? [absolutePath(command, label)] : (searchPath ?? "").split(":").filter(Boolean).map((directory) => join3(directory, command));
1229
+ for (const candidate of candidates) {
1230
+ try {
1231
+ accessSync(candidate, constants.X_OK);
1232
+ return realpathSync(candidate);
1233
+ } catch (error) {
1234
+ if (error.code !== "ENOENT" && error.code !== "EACCES") {
1235
+ throw error;
1236
+ }
1237
+ }
1238
+ }
1239
+ throw new Error(`${label} is not executable on the evaluator PATH`);
1240
+ }
1241
+ function syncDirectory(directory) {
1242
+ const fd = openSync(directory, "r");
1243
+ try {
1244
+ fsyncSync(fd);
1245
+ } finally {
1246
+ closeSync(fd);
1247
+ }
1248
+ }
1249
+ function writeJsonAtomic(directory, name, value) {
1250
+ const target = join3(directory, name);
1251
+ const temporary = join3(directory, `.${name}.${process.pid}.${Date.now()}.tmp`);
1252
+ const fd = openSync(temporary, "wx", 384);
1253
+ try {
1254
+ writeFileSync2(fd, `${JSON.stringify(value)}
1255
+ `, "utf8");
1256
+ fsyncSync(fd);
1257
+ } finally {
1258
+ closeSync(fd);
1259
+ }
1260
+ renameSync(temporary, target);
1261
+ syncDirectory(directory);
1262
+ }
1263
+ function touchDurable(directory, name) {
1264
+ try {
1265
+ const fd = openSync(join3(directory, name), "wx", 384);
1266
+ try {
1267
+ fsyncSync(fd);
1268
+ } finally {
1269
+ closeSync(fd);
1270
+ }
1271
+ syncDirectory(directory);
1272
+ } catch (error) {
1273
+ if (error.code !== "EEXIST") throw error;
1274
+ }
1275
+ }
1276
+ function readJson(path, label) {
1277
+ let value;
1278
+ try {
1279
+ value = JSON.parse(readFileSync(path, "utf8"));
1280
+ } catch (error) {
1281
+ throw new Error(`${label} is not valid JSON`, { cause: error });
1282
+ }
1283
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1284
+ throw new Error(`${label} must be an object`);
1285
+ }
1286
+ return value;
1287
+ }
1288
+ function parseProcessIdentity(value, label) {
1289
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1290
+ throw new Error(`${label} must be an object`);
1291
+ }
1292
+ const record2 = value;
1293
+ if (!Number.isSafeInteger(record2.pid) || !Number.isSafeInteger(record2.processGroupId) || !Number.isSafeInteger(record2.sessionId) || typeof record2.startTicks !== "string" || !/^\d+$/.test(record2.startTicks)) {
1294
+ throw new Error(`${label} is malformed`);
1295
+ }
1296
+ return {
1297
+ pid: record2.pid,
1298
+ processGroupId: record2.processGroupId,
1299
+ sessionId: record2.sessionId,
1300
+ startTicks: record2.startTicks
1301
+ };
1302
+ }
1303
+ function parsePersistedIdentity(path) {
1304
+ const record2 = readJson(path, "Pier trial identity");
1305
+ if (record2.schemaVersion !== 1 || record2.kind !== "pier-trial-identity" || !["allocating", "starting", "running"].includes(record2.state) || typeof record2.executionId !== "string" || typeof record2.executionPlanDigest !== "string" || !sha256Pattern2.test(record2.executionPlanDigest) || typeof record2.jobsDirectory !== "string" || typeof record2.jobName !== "string" || typeof record2.dockerCommand !== "string" || typeof record2.dockerConnectionId !== "string") {
1306
+ throw new Error("Pier trial identity is malformed");
1307
+ }
1308
+ return {
1309
+ schemaVersion: 1,
1310
+ kind: "pier-trial-identity",
1311
+ state: record2.state,
1312
+ executionId: record2.executionId,
1313
+ executionPlanDigest: record2.executionPlanDigest,
1314
+ ...record2.supervisor ? { supervisor: parseProcessIdentity(record2.supervisor, "Pier supervisor identity") } : {},
1315
+ ...record2.pier ? { pier: parseProcessIdentity(record2.pier, "Pier process identity") } : {},
1316
+ jobsDirectory: absolutePath(record2.jobsDirectory, "persisted jobs directory"),
1317
+ jobName: nonEmpty2(record2.jobName, "persisted job name"),
1318
+ dockerCommand: nonEmpty2(record2.dockerCommand, "persisted Docker command"),
1319
+ dockerConnectionId: stableDockerConnectionId(record2.dockerConnectionId)
1320
+ };
1321
+ }
1322
+ function parseTerminal(path) {
1323
+ const record2 = readJson(path, "Pier trial terminal");
1324
+ const allowedKeys = /* @__PURE__ */ new Set([
1325
+ "schemaVersion",
1326
+ "kind",
1327
+ "status",
1328
+ "processExited",
1329
+ "containersRemoved",
1330
+ "removedContainers",
1331
+ "exitCode",
1332
+ "signal",
1333
+ "error",
1334
+ "recoveredByPid"
1335
+ ]);
1336
+ const hasMalformedOptionalField = record2.removedContainers !== void 0 && (!Number.isSafeInteger(record2.removedContainers) || record2.removedContainers < 0) || record2.exitCode !== void 0 && record2.exitCode !== null && !Number.isSafeInteger(record2.exitCode) || record2.signal !== void 0 && record2.signal !== null && (typeof record2.signal !== "string" || !/^SIG[A-Z0-9]+$/.test(record2.signal)) || record2.error !== void 0 && typeof record2.error !== "string" || record2.recoveredByPid !== void 0 && (!Number.isSafeInteger(record2.recoveredByPid) || record2.recoveredByPid < 1);
1337
+ if (Object.keys(record2).some((key) => !allowedKeys.has(key)) || record2.schemaVersion !== 1 || record2.kind !== "pier-trial-terminal" || !["completed", "stopped", "failed"].includes(record2.status) || typeof record2.processExited !== "boolean" || typeof record2.containersRemoved !== "boolean" || hasMalformedOptionalField) {
1338
+ throw new Error("Pier trial terminal is malformed");
1339
+ }
1340
+ return {
1341
+ schemaVersion: 1,
1342
+ kind: "pier-trial-terminal",
1343
+ status: record2.status,
1344
+ processExited: record2.processExited,
1345
+ containersRemoved: record2.containersRemoved,
1346
+ ...record2.removedContainers !== void 0 ? { removedContainers: record2.removedContainers } : {},
1347
+ ...record2.exitCode !== void 0 ? { exitCode: record2.exitCode } : {},
1348
+ ...record2.signal !== void 0 ? { signal: record2.signal } : {},
1349
+ ...record2.error !== void 0 ? { error: record2.error } : {},
1350
+ ...record2.recoveredByPid !== void 0 ? { recoveredByPid: record2.recoveredByPid } : {}
1351
+ };
1352
+ }
1353
+ function processIdentity(pid) {
1354
+ const text = readFileSync(`/proc/${pid}/stat`, "utf8");
1355
+ const closing = text.lastIndexOf(")");
1356
+ if (closing < 0) throw new Error(`cannot parse process identity for ${pid}`);
1357
+ const fields = text.slice(closing + 2).trim().split(/\s+/);
1358
+ const processGroupId = Number(fields[2]);
1359
+ const sessionId = Number(fields[3]);
1360
+ const startTicks = fields[19];
1361
+ if (!Number.isSafeInteger(processGroupId) || !Number.isSafeInteger(sessionId) || !/^\d+$/.test(startTicks ?? "")) {
1362
+ throw new Error(`cannot parse process identity for ${pid}`);
1363
+ }
1364
+ return { pid, processGroupId, sessionId, startTicks };
1365
+ }
1366
+ function processMatches(identity) {
1367
+ try {
1368
+ const current = processIdentity(identity.pid);
1369
+ return current.startTicks === identity.startTicks && current.processGroupId === identity.processGroupId && current.sessionId === identity.sessionId;
1370
+ } catch (error) {
1371
+ if (error.code === "ENOENT") return false;
1372
+ throw error;
1373
+ }
1374
+ }
1375
+ function signalProcess(identity, signal, group) {
1376
+ if (!processMatches(identity)) return;
1377
+ try {
1378
+ process.kill(group ? -identity.processGroupId : identity.pid, signal);
1379
+ } catch (error) {
1380
+ if (error.code !== "ESRCH") throw error;
1381
+ }
1382
+ }
1383
+ function sessionMembers(sessionId) {
1384
+ const members = [];
1385
+ for (const entry of readdirSync("/proc", { withFileTypes: true })) {
1386
+ if (!entry.isDirectory() || !/^\d+$/.test(entry.name)) continue;
1387
+ try {
1388
+ const identity = processIdentity(Number(entry.name));
1389
+ if (identity.sessionId === sessionId) members.push(identity);
1390
+ } catch (error) {
1391
+ if (error.code !== "ENOENT") throw error;
1392
+ }
1393
+ }
1394
+ return members;
1395
+ }
1396
+ async function signalSessionUntilEmpty(identity, signal, timeoutMs, intervalMs) {
1397
+ const deadline = Date.now() + timeoutMs;
1398
+ while (true) {
1399
+ const members = sessionMembers(identity.sessionId);
1400
+ if (members.length === 0) return true;
1401
+ for (const member of members) signalProcess(member, signal, false);
1402
+ if (Date.now() >= deadline) return false;
1403
+ await sleep(intervalMs);
1404
+ }
1405
+ }
1406
+ function sanitizeProject(value) {
1407
+ let project = value.toLowerCase();
1408
+ if (!/^[a-z0-9]/.test(project)) project = `0${project}`;
1409
+ return project.replace(/[^a-z0-9_-]/g, "-");
1410
+ }
1411
+ function trialProjects(identity) {
1412
+ const jobRoot = join3(identity.jobsDirectory, identity.jobName);
1413
+ if (!existsSync(jobRoot)) return [];
1414
+ return readdirSync(jobRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => sanitizeProject(entry.name));
1415
+ }
1416
+ function matchingContainers(identity, dockerEnvironment) {
1417
+ const projects = trialProjects(identity);
1418
+ if (projects.length === 0) return [];
1419
+ const output = execFileSync(
1420
+ identity.dockerCommand,
1421
+ ["ps", "-a", "--format", '{{.ID}} {{.Label "com.docker.compose.project"}}'],
1422
+ {
1423
+ encoding: "utf8",
1424
+ env: dockerEnvironment,
1425
+ timeout: 3e4,
1426
+ maxBuffer: 4 * 1024 * 1024
1427
+ }
1428
+ );
1429
+ return output.split("\n").filter(Boolean).flatMap((line) => {
1430
+ const tab = line.indexOf(" ");
1431
+ if (tab < 1) return [];
1432
+ const id = line.slice(0, tab);
1433
+ const project = line.slice(tab + 1);
1434
+ return projects.some(
1435
+ (expected) => project === expected || project.startsWith(`${expected}__verifier__`)
1436
+ ) ? [id] : [];
1437
+ });
1438
+ }
1439
+ function removeTrialContainers(identity, dockerEnvironment) {
1440
+ const containers = matchingContainers(identity, dockerEnvironment);
1441
+ if (containers.length > 0) {
1442
+ try {
1443
+ execFileSync(identity.dockerCommand, ["rm", "-f", ...containers], {
1444
+ encoding: "utf8",
1445
+ env: dockerEnvironment,
1446
+ timeout: 3e4,
1447
+ maxBuffer: 4 * 1024 * 1024
1448
+ });
1449
+ } catch (error) {
1450
+ if (matchingContainers(identity, dockerEnvironment).length > 0) throw error;
1451
+ }
1452
+ }
1453
+ const remaining = matchingContainers(identity, dockerEnvironment);
1454
+ if (remaining.length > 0) {
1455
+ throw new Error(`Pier task containers survived removal: ${remaining.join(", ")}`);
1456
+ }
1457
+ return containers.length;
1458
+ }
1459
+ function sleep(milliseconds) {
1460
+ return new Promise((resolveSleep) => setTimeout(resolveSleep, milliseconds));
1461
+ }
1462
+ async function waitUntil(predicate, timeoutMs, intervalMs) {
1463
+ const deadline = Date.now() + timeoutMs;
1464
+ while (!predicate()) {
1465
+ if (Date.now() >= deadline) return false;
1466
+ await sleep(intervalMs);
1467
+ }
1468
+ return true;
1469
+ }
1470
+ function trialKey(identity) {
1471
+ if (!sha256Pattern2.test(identity.executionPlanDigest)) {
1472
+ throw new Error("executionPlanDigest must be a SHA-256 digest");
1473
+ }
1474
+ return createHash2("sha256").update(JSON.stringify([nonEmpty2(identity.executionId, "executionId"), identity.executionPlanDigest])).digest("hex");
1475
+ }
1476
+ function assertRealDirectory2(path, label) {
1477
+ const stats = lstatSync(path);
1478
+ if (!stats.isDirectory() || stats.isSymbolicLink() || realpathSync(path) !== path) {
1479
+ throw new Error(`${label} must be a real canonical directory`);
1480
+ }
1481
+ }
1482
+ var FilePierCandidateTrialController = class {
1483
+ directory;
1484
+ launch;
1485
+ readResult;
1486
+ dockerConnection;
1487
+ supervisorPath;
1488
+ pollIntervalMs;
1489
+ constructor(options) {
1490
+ this.directory = absolutePath(options.directory, "Pier controller directory");
1491
+ this.launch = options.launch;
1492
+ this.readResult = options.readResult;
1493
+ const configuredDocker = options.dockerConnection;
1494
+ if (configuredDocker?.id === defaultDockerConnectionId) {
1495
+ throw new Error(`${defaultDockerConnectionId} is reserved for the implicit local connection`);
1496
+ }
1497
+ const dockerConnection = configuredDocker ?? {
1498
+ id: defaultDockerConnectionId,
1499
+ env: {}
1500
+ };
1501
+ const connectionId = stableDockerConnectionId(dockerConnection.id);
1502
+ validateEnvironment(dockerConnection.env, "Docker connection environment");
1503
+ this.dockerConnection = Object.freeze({
1504
+ id: connectionId,
1505
+ env: Object.freeze({ ...dockerConnection.env })
1506
+ });
1507
+ this.supervisorPath = absolutePath(
1508
+ options.supervisorPath ?? fileURLToPath(new URL("./pier-trial-supervisor.mjs", import.meta.url)),
1509
+ "Pier supervisor path"
1510
+ );
1511
+ this.pollIntervalMs = options.pollIntervalMs ?? 25;
1512
+ if (!Number.isSafeInteger(this.pollIntervalMs) || this.pollIntervalMs < 1) {
1513
+ throw new Error("pollIntervalMs must be a positive integer");
1514
+ }
1515
+ mkdirSync2(this.directory, { recursive: true, mode: 448 });
1516
+ assertRealDirectory2(this.directory, "Pier controller directory");
1517
+ }
1518
+ start(staged, context) {
1519
+ if (!this.launch) throw new Error("this Pier controller is recovery-only");
1520
+ const identity = {
1521
+ executionId: context.request.executionId,
1522
+ executionPlanDigest: context.request.executionPlan.value.digest
1523
+ };
1524
+ if (staged.executionId !== identity.executionId) {
1525
+ throw new Error("staged Pier execution does not match the runtime request");
1526
+ }
1527
+ const spec = this.launch(staged, context);
1528
+ this.validateSpec(spec);
1529
+ for (const [name, value] of Object.entries(this.dockerConnection.env)) {
1530
+ if (Object.prototype.hasOwnProperty.call(spec.env, name) && spec.env[name] !== value) {
1531
+ throw new Error(`Pier child environment conflicts with Docker connection variable ${name}`);
1532
+ }
1533
+ }
1534
+ const pierEnvironment = Object.freeze({
1535
+ ...spec.env,
1536
+ ...this.dockerConnection.env
1537
+ });
1538
+ mkdirSync2(spec.jobsDirectory, { recursive: true, mode: 448 });
1539
+ assertRealDirectory2(spec.jobsDirectory, "Pier jobs directory");
1540
+ const dockerCommand = resolveExecutable(
1541
+ spec.dockerCommand ?? "docker",
1542
+ process.env.PATH,
1543
+ "Docker command"
1544
+ );
1545
+ const pierCommand = resolveExecutable(spec.command, pierEnvironment.PATH, "Pier command");
1546
+ const controlDirectory = this.controlDirectory(identity);
1547
+ try {
1548
+ mkdirSync2(controlDirectory, { mode: 448 });
1549
+ } catch (error) {
1550
+ if (error.code === "EEXIST") {
1551
+ throw new Error("Pier trial identity already has durable control state");
1552
+ }
1553
+ throw error;
1554
+ }
1555
+ assertRealDirectory2(controlDirectory, "Pier trial control directory");
1556
+ const jobRoot = join3(spec.jobsDirectory, spec.jobName);
1557
+ let jobRootCreated = false;
1558
+ try {
1559
+ mkdirSync2(jobRoot, { mode: 448 });
1560
+ jobRootCreated = true;
1561
+ assertRealDirectory2(jobRoot, "Pier job directory");
1562
+ } catch (error) {
1563
+ rmSync(controlDirectory, { recursive: true, force: true });
1564
+ if (jobRootCreated) rmSync(jobRoot, { recursive: true, force: true });
1565
+ if (error.code === "EEXIST") {
1566
+ throw new Error("Pier job name must be unique; its job directory already exists");
1567
+ }
1568
+ throw error;
1569
+ }
1570
+ const allocating = {
1571
+ schemaVersion: 1,
1572
+ kind: "pier-trial-identity",
1573
+ state: "allocating",
1574
+ ...identity,
1575
+ jobsDirectory: absolutePath(spec.jobsDirectory, "Pier jobs directory"),
1576
+ jobName: nonEmpty2(spec.jobName, "Pier job name"),
1577
+ dockerCommand: nonEmpty2(dockerCommand, "Docker command"),
1578
+ dockerConnectionId: this.dockerConnection.id
1579
+ };
1580
+ try {
1581
+ writeJsonAtomic(controlDirectory, identityFile, allocating);
1582
+ } catch (error) {
1583
+ rmSync(controlDirectory, { recursive: true, force: true });
1584
+ rmSync(jobRoot, { recursive: true, force: true });
1585
+ throw error;
1586
+ }
1587
+ const supervisor = spawn2(process.execPath, [this.supervisorPath, controlDirectory], {
1588
+ detached: true,
1589
+ // Launch data arrives over fd 3. The trusted supervisor receives only
1590
+ // explicitly declared process variables, never the evaluator environment.
1591
+ env: { ...this.dockerConnection.env },
1592
+ stdio: ["ignore", "ignore", "ignore", "pipe"]
1593
+ });
1594
+ if (supervisor.pid === void 0) throw new Error("Pier supervisor started without a pid");
1595
+ const controlPipe = supervisor.stdio[3];
1596
+ let supervisorIdentity;
1597
+ try {
1598
+ supervisorIdentity = processIdentity(supervisor.pid);
1599
+ const persisted = {
1600
+ ...allocating,
1601
+ state: "starting",
1602
+ supervisor: supervisorIdentity
1603
+ };
1604
+ writeJsonAtomic(controlDirectory, identityFile, persisted);
1605
+ if (!controlPipe) throw new Error("Pier supervisor has no protected control pipe");
1606
+ controlPipe.on("error", () => void 0);
1607
+ controlPipe.end(
1608
+ JSON.stringify({
1609
+ command: pierCommand,
1610
+ args: spec.args,
1611
+ cwd: spec.cwd,
1612
+ env: pierEnvironment,
1613
+ jobsDirectory: spec.jobsDirectory,
1614
+ jobName: spec.jobName,
1615
+ dockerCommand
1616
+ })
1617
+ );
1618
+ } catch (error) {
1619
+ controlPipe?.destroy();
1620
+ if (supervisorIdentity) signalProcess(supervisorIdentity, "SIGKILL", false);
1621
+ else {
1622
+ try {
1623
+ process.kill(supervisor.pid, "SIGKILL");
1624
+ } catch (killError) {
1625
+ if (killError.code !== "ESRCH") throw killError;
1626
+ }
1627
+ }
1628
+ throw error;
1629
+ }
1630
+ supervisor.unref();
1631
+ controlPipe.unref?.();
1632
+ const result = this.waitForResult(controlDirectory, spec, context.deadlineAtMs);
1633
+ return {
1634
+ identity,
1635
+ result,
1636
+ terminateAndWait: async () => await this.terminateAndWait(identity)
1637
+ };
1638
+ }
1639
+ async terminateAndWait(requested) {
1640
+ const controlDirectory = this.controlDirectory(requested);
1641
+ assertRealDirectory2(controlDirectory, "Pier trial control directory");
1642
+ const persisted = parsePersistedIdentity(join3(controlDirectory, identityFile));
1643
+ if (persisted.executionId !== requested.executionId || persisted.executionPlanDigest !== requested.executionPlanDigest) {
1644
+ throw new Error("persisted Pier trial identity differs from the stop request");
1645
+ }
1646
+ this.assertDockerConnection(persisted);
1647
+ touchDurable(controlDirectory, stopFile);
1648
+ if (!existsSync(join3(controlDirectory, terminalFile))) {
1649
+ if (persisted.supervisor) signalProcess(persisted.supervisor, "SIGTERM", false);
1650
+ }
1651
+ const terminalAppeared = await waitUntil(
1652
+ () => existsSync(join3(controlDirectory, terminalFile)),
1653
+ persisted.supervisor ? 1e4 : 1,
1654
+ this.pollIntervalMs
1655
+ );
1656
+ if (!terminalAppeared) await this.forceRecovery(controlDirectory);
1657
+ let terminal = parseTerminal(join3(controlDirectory, terminalFile));
1658
+ const latest = parsePersistedIdentity(join3(controlDirectory, identityFile));
1659
+ const liveProcesses = latest.pier ? sessionMembers(latest.pier.sessionId).length : 0;
1660
+ this.assertDockerConnection(latest);
1661
+ const liveContainers = matchingContainers(latest, this.dockerConnection.env).length;
1662
+ if (liveProcesses > 0 || liveContainers > 0) {
1663
+ await this.forceRecovery(controlDirectory);
1664
+ terminal = parseTerminal(join3(controlDirectory, terminalFile));
1665
+ }
1666
+ if (!terminal.processExited || !terminal.containersRemoved) {
1667
+ throw new Error(
1668
+ `Pier recovery did not prove process/container death${terminal.error ? `: ${terminal.error}` : ""}`
1669
+ );
1670
+ }
1671
+ return { processExited: true, containersRemoved: true };
1672
+ }
1673
+ async captureResult(requested) {
1674
+ const controlDirectory = this.controlDirectory(requested);
1675
+ assertRealDirectory2(controlDirectory, "Pier trial control directory");
1676
+ const persisted = parsePersistedIdentity(join3(controlDirectory, identityFile));
1677
+ if (persisted.executionId !== requested.executionId || persisted.executionPlanDigest !== requested.executionPlanDigest) {
1678
+ throw new Error("persisted Pier trial identity differs from the capture request");
1679
+ }
1680
+ this.assertDockerConnection(persisted);
1681
+ const terminal = parseTerminal(join3(controlDirectory, terminalFile));
1682
+ if (!terminal.processExited || !terminal.containersRemoved) {
1683
+ throw new Error("Pier result capture requires proven process and container death");
1684
+ }
1685
+ if (!this.readResult) {
1686
+ throw new Error("Pier result capture has no restart-safe result reader");
1687
+ }
1688
+ return await this.readResult({
1689
+ jobsDirectory: persisted.jobsDirectory,
1690
+ jobName: persisted.jobName
1691
+ });
1692
+ }
1693
+ controlDirectory(identity) {
1694
+ return join3(this.directory, trialKey(identity));
1695
+ }
1696
+ assertDockerConnection(identity) {
1697
+ if (identity.dockerConnectionId !== this.dockerConnection.id) {
1698
+ throw new Error(
1699
+ `Pier trial requires Docker connection ${identity.dockerConnectionId}; configured ${this.dockerConnection.id}`
1700
+ );
1701
+ }
1702
+ }
1703
+ validateSpec(spec) {
1704
+ nonEmpty2(spec.command, "Pier command");
1705
+ if (!Array.isArray(spec.args) || spec.args.some((arg) => typeof arg !== "string" || arg.includes("\0"))) {
1706
+ throw new Error("Pier arguments must be strings without NUL");
1707
+ }
1708
+ absolutePath(spec.cwd, "Pier cwd");
1709
+ absolutePath(spec.jobsDirectory, "Pier jobs directory");
1710
+ if (!/^[A-Za-z0-9._-]{1,200}$/.test(spec.jobName)) {
1711
+ throw new Error("Pier job name must be filesystem-neutral");
1712
+ }
1713
+ validateEnvironment(spec.env, "Pier child environment");
1714
+ }
1715
+ async waitForResult(controlDirectory, spec, deadlineAtMs) {
1716
+ const waitMs = Math.max(0, deadlineAtMs - Date.now());
1717
+ const appeared = await waitUntil(
1718
+ () => existsSync(join3(controlDirectory, terminalFile)),
1719
+ waitMs,
1720
+ this.pollIntervalMs
1721
+ );
1722
+ if (!appeared) throw new Error("Pier supervisor emitted no terminal acknowledgement");
1723
+ const terminal = parseTerminal(join3(controlDirectory, terminalFile));
1724
+ if (!terminal.processExited || !terminal.containersRemoved) {
1725
+ throw new Error("Pier supervisor did not prove process and container death");
1726
+ }
1727
+ if (terminal.status !== "completed") {
1728
+ throw new Error(terminal.error ? `Pier process failed: ${terminal.error}` : "Pier process stopped");
1729
+ }
1730
+ return await spec.readResult();
1731
+ }
1732
+ async forceRecovery(controlDirectory) {
1733
+ const latest = parsePersistedIdentity(join3(controlDirectory, identityFile));
1734
+ this.assertDockerConnection(latest);
1735
+ const errors = [];
1736
+ if (latest.pier) {
1737
+ try {
1738
+ await signalSessionUntilEmpty(latest.pier, "SIGTERM", 2e3, this.pollIntervalMs);
1739
+ } catch (error) {
1740
+ errors.push(`could not signal Pier session: ${error instanceof Error ? error.message : String(error)}`);
1741
+ }
1742
+ }
1743
+ if (latest.supervisor) {
1744
+ try {
1745
+ signalProcess(latest.supervisor, "SIGKILL", false);
1746
+ if (!await waitUntil(() => !processMatches(latest.supervisor), 5e3, this.pollIntervalMs)) {
1747
+ errors.push(`Pier supervisor ${latest.supervisor.pid} survived SIGKILL`);
1748
+ }
1749
+ } catch (error) {
1750
+ errors.push(`could not kill Pier supervisor: ${error instanceof Error ? error.message : String(error)}`);
1751
+ }
1752
+ }
1753
+ if (latest.pier) {
1754
+ try {
1755
+ if (!await signalSessionUntilEmpty(
1756
+ latest.pier,
1757
+ "SIGKILL",
1758
+ 5e3,
1759
+ this.pollIntervalMs
1760
+ )) {
1761
+ errors.push(`Pier process session ${latest.pier.sessionId} survived SIGKILL`);
1762
+ }
1763
+ } catch (error) {
1764
+ errors.push(`could not kill Pier session: ${error instanceof Error ? error.message : String(error)}`);
1765
+ }
1766
+ }
1767
+ let removedContainers = 0;
1768
+ try {
1769
+ removedContainers = removeTrialContainers(latest, this.dockerConnection.env);
1770
+ } catch (error) {
1771
+ errors.push(`could not remove Pier containers: ${error instanceof Error ? error.message : String(error)}`);
1772
+ }
1773
+ if (errors.length > 0) {
1774
+ throw new Error(`Pier recovery incomplete: ${errors.join("; ")}`);
1775
+ }
1776
+ writeJsonAtomic(controlDirectory, terminalFile, {
1777
+ schemaVersion: 1,
1778
+ kind: "pier-trial-terminal",
1779
+ status: "stopped",
1780
+ processExited: true,
1781
+ containersRemoved: true,
1782
+ removedContainers,
1783
+ recoveredByPid: process.pid
1784
+ });
1785
+ }
1786
+ };
1787
+
1788
+ // src/pier-result-grader.ts
1789
+ import { spawn as spawn3 } from "child_process";
1790
+ import { createHash as createHash3 } from "crypto";
1791
+ import { chmod as chmod2, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
1792
+ import { tmpdir as tmpdir2 } from "os";
1793
+ import { join as join4 } from "path";
1794
+ var sha2562 = (bytes) => `sha256:${createHash3("sha256").update(bytes).digest("hex")}`;
1795
+ function createPierResultGrader(descriptor) {
1796
+ return {
1797
+ ...descriptor,
1798
+ run: async (input) => {
1799
+ input.signal.throwIfAborted();
1800
+ const implementation = Uint8Array.from(input.implementation.bytes);
1801
+ if (implementation.byteLength !== input.implementation.byteLength) {
1802
+ throw new Error("Pier grader implementation length changed before execution");
1803
+ }
1804
+ const evidence = Uint8Array.from(input.officialResult.bytes);
1805
+ const evaluation = await executeGrader(implementation, evidence, input.signal);
1806
+ input.signal.throwIfAborted();
1807
+ return {
1808
+ evaluation,
1809
+ evidence,
1810
+ binding: {
1811
+ implementationDigest: sha2562(implementation),
1812
+ taskOutcomeDigest: input.outcome.evidence.digest,
1813
+ outputDigest: sha2562(evidence)
1814
+ }
1815
+ };
1816
+ }
1817
+ };
1818
+ }
1819
+ async function executeGrader(implementation, resultBytes, signal) {
1820
+ const directory = await mkdtemp2(join4(tmpdir2(), "pier-result-grader-"));
1821
+ const script = join4(directory, "grader.mjs");
1822
+ try {
1823
+ await writeFile2(script, implementation, { flag: "wx", mode: 384 });
1824
+ await chmod2(script, 320);
1825
+ const stdout = await runNode(script, resultBytes, signal);
1826
+ const parsed = JSON.parse(stdout.toString("utf8"));
1827
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1828
+ throw new Error("Pier result grader returned a non-object evaluation");
1829
+ }
1830
+ return parsed;
1831
+ } finally {
1832
+ await rm2(directory, { recursive: true, force: true });
1833
+ }
1834
+ }
1835
+ async function runNode(script, input, signal) {
1836
+ signal.throwIfAborted();
1837
+ return await new Promise((resolveResult, reject) => {
1838
+ const child = spawn3(process.execPath, ["--no-warnings", script], {
1839
+ env: {
1840
+ PATH: process.env.PATH,
1841
+ HOME: process.env.HOME,
1842
+ LANG: "C",
1843
+ LC_ALL: "C"
1844
+ },
1845
+ stdio: ["pipe", "pipe", "pipe"],
1846
+ signal
1847
+ });
1848
+ const stdout = [];
1849
+ const stderr = [];
1850
+ let bytes = 0;
1851
+ const collect = (target) => (chunk) => {
1852
+ bytes += chunk.byteLength;
1853
+ if (bytes > 1024 * 1024) {
1854
+ child.kill("SIGKILL");
1855
+ reject(new Error("Pier result grader exceeded its output limit"));
1856
+ return;
1857
+ }
1858
+ target.push(Buffer.from(chunk));
1859
+ };
1860
+ child.stdout.on("data", collect(stdout));
1861
+ child.stderr.on("data", collect(stderr));
1862
+ child.once("error", reject);
1863
+ child.once("close", (code, childSignal) => {
1864
+ if (code !== 0) {
1865
+ reject(
1866
+ new Error(
1867
+ `Pier result grader failed (${childSignal ?? code}): ${Buffer.concat(stderr).toString("utf8").trim()}`
1868
+ )
1869
+ );
1870
+ return;
1871
+ }
1872
+ resolveResult(Buffer.concat(stdout));
1873
+ });
1874
+ child.stdin.end(input);
1875
+ });
1876
+ }
1877
+ export {
1878
+ ADAPTERS,
1879
+ FINAL_ANSWER_SENTINEL,
1880
+ FilePierCandidateTrialController,
1881
+ StagedJudgeError,
1882
+ answerScoreToBenchScore,
1883
+ contextBlock,
1884
+ contextsFrom,
1885
+ createCragAdapter,
1886
+ createNoMiraclAdapter,
1887
+ createOpenRagBenchAdapter,
1888
+ createPierCandidateRecoveryExecutor,
1889
+ createPierResultGrader,
1890
+ createRagBenchAdapter,
1891
+ createSweBenchAdapter,
1892
+ createT2RagBenchAdapter,
1893
+ executePreparedPierCandidate,
1894
+ normalizeAnswer,
1895
+ parseCitations,
1896
+ parseFinalAnswer,
1897
+ printBenchmarksReport,
1898
+ ragAnswerOutput,
1899
+ resolveAdapter,
1900
+ runBenchmarks,
1901
+ runStagedJudge,
1902
+ scoreAnswerArtifact,
1903
+ scoreSweReport,
1904
+ sweEvaluationArgv,
1905
+ swePatchOutput,
1906
+ tokenF1
1907
+ };
1908
+ //# sourceMappingURL=index.js.map