@tangle-network/agent-bench 0.1.0 → 0.3.5

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 (139) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/HARNESS.md +302 -0
  3. package/README.md +26 -1
  4. package/fixtures/aec-bench.json +18 -0
  5. package/fixtures/agentbench-dbbench.json +22 -0
  6. package/fixtures/bfcl.json +45 -0
  7. package/fixtures/commit0.json +72 -0
  8. package/fixtures/crag.json +10 -0
  9. package/fixtures/dabstep.json +22 -0
  10. package/fixtures/enterpriseops-gym.json +103 -0
  11. package/fixtures/finresearchbench.json +21 -0
  12. package/fixtures/finsearchcomp.json +66 -0
  13. package/fixtures/frames.json +26 -0
  14. package/fixtures/hotpotqa.json +182 -0
  15. package/fixtures/nomiracl.json +26 -0
  16. package/fixtures/open-rag-bench.json +16 -0
  17. package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
  18. package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
  19. package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
  20. package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
  21. package/fixtures/pier-agent/no-model-task/task.toml +35 -0
  22. package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
  23. package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
  24. package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
  25. package/fixtures/programbench.json +17 -0
  26. package/fixtures/ragbench.json +21 -0
  27. package/fixtures/simpleqa.json +121 -0
  28. package/fixtures/t2-ragbench.json +13 -0
  29. package/fixtures/tau2-bench.json +16 -0
  30. package/fixtures/tau3-banking.json +16 -0
  31. package/fixtures/toollm.json +28 -0
  32. package/fixtures/webarena-verified.json +20 -0
  33. package/package.json +39 -15
  34. package/pier_agents/__init__.py +18 -0
  35. package/pier_agents/candidate_contract.py +755 -0
  36. package/pier_agents/process_boundary.py +321 -0
  37. package/pier_agents/tangle_candidate.py +907 -0
  38. package/pier_agents/workspace_boundary.py +368 -0
  39. package/scripts/appworld_driver.py +359 -0
  40. package/scripts/cadbench_prepare.py +22 -0
  41. package/scripts/cadgenbench_hard_parts.py +48 -0
  42. package/scripts/clbench_codebase_judge.py +73 -0
  43. package/scripts/commit0_judge.py +170 -0
  44. package/scripts/dabstep_judge.py +42 -0
  45. package/scripts/enterpriseops_gym_judge.py +281 -0
  46. package/scripts/programbench_judge.py +120 -0
  47. package/scripts/render-gate-chart.mjs +176 -0
  48. package/scripts/run-package-tests.mjs +56 -0
  49. package/scripts/terminate-pier-trial.mts +66 -0
  50. package/scripts/trata-hedge/README.md +56 -0
  51. package/scripts/trata-hedge/run.sh +60 -0
  52. package/scripts/trata-hedge/solve.py +83 -0
  53. package/scripts/verify-packed-consumer.mjs +224 -0
  54. package/scripts/verify-pier-agent.mts +715 -0
  55. package/scripts/verify-pier-pair.mts +74 -0
  56. package/scripts/verify-pier-recovery.mts +139 -0
  57. package/src/adapters.ts +26 -0
  58. package/src/benchmarks/_harness.test.mts +178 -0
  59. package/src/benchmarks/_harness.ts +239 -16
  60. package/src/benchmarks/agentbench.ts +163 -0
  61. package/src/benchmarks/appworld.test.mts +15 -9
  62. package/src/benchmarks/bfcl.ts +346 -0
  63. package/src/benchmarks/crag.ts +137 -0
  64. package/src/benchmarks/dabstep.test.mts +70 -0
  65. package/src/benchmarks/dabstep.ts +212 -0
  66. package/src/benchmarks/external-adapters.test.mts +150 -0
  67. package/src/benchmarks/finresearchbench.ts +269 -0
  68. package/src/benchmarks/humaneval.ts +20 -8
  69. package/src/benchmarks/nomiracl.ts +180 -0
  70. package/src/benchmarks/open-rag-bench.ts +153 -0
  71. package/src/benchmarks/rag-benchmarks.test.mts +138 -0
  72. package/src/benchmarks/rag-shared.ts +327 -0
  73. package/src/benchmarks/ragbench.ts +171 -0
  74. package/src/benchmarks/swe-bench.test.mts +61 -0
  75. package/src/benchmarks/swe-bench.ts +201 -19
  76. package/src/benchmarks/t2-ragbench.ts +166 -0
  77. package/src/benchmarks/tau-bench-shared.ts +214 -0
  78. package/src/benchmarks/tau2-bench.ts +30 -0
  79. package/src/benchmarks/tau3-banking.ts +29 -0
  80. package/src/benchmarks/terminal-bench.test.mts +33 -0
  81. package/src/benchmarks/terminal-bench.ts +23 -8
  82. package/src/benchmarks/toollm.ts +254 -0
  83. package/src/benchmarks/types.ts +42 -0
  84. package/src/benchmarks/webarena-verified.ts +200 -0
  85. package/src/commit0-prereqs.sh +0 -0
  86. package/src/coordination-mcp-container-reach.mts +181 -0
  87. package/src/decoder-live.mts +1 -1
  88. package/src/examples/README.md +103 -39
  89. package/src/examples/benchmark-matrix.mts +101 -0
  90. package/src/examples/lean-proof-gate.README.md +77 -0
  91. package/src/examples/lean-proof-gate.mts +162 -0
  92. package/src/examples/lean-verify.ts +95 -0
  93. package/src/examples/lean.Dockerfile +12 -0
  94. package/src/examples/math-demo.mts +9 -7
  95. package/src/examples/strategy-demo.mts +10 -12
  96. package/src/gate.ts +3 -2
  97. package/src/hev-eval.mts +69 -0
  98. package/src/hev-improve.mts +169 -0
  99. package/src/hev-structural.mts +688 -0
  100. package/src/index.ts +73 -0
  101. package/src/mbpp-structural.mts +662 -0
  102. package/src/pier-agent.test-fixtures.mts +19 -0
  103. package/src/pier-agent.test.mts +363 -0
  104. package/src/pier-agent.ts +657 -0
  105. package/src/pier-result-grader.mjs +30 -0
  106. package/src/pier-result-grader.test.mts +62 -0
  107. package/src/pier-result-grader.ts +108 -0
  108. package/src/pier-task-outcome.test.mts +117 -0
  109. package/src/pier-task-outcome.ts +240 -0
  110. package/src/pier-trial-controller.test.mts +412 -0
  111. package/src/pier-trial-controller.ts +858 -0
  112. package/src/pier-trial-supervisor.mjs +352 -0
  113. package/src/resolve-client.ts +25 -2
  114. package/src/run-benchmarks-cli.mts +72 -0
  115. package/src/run-benchmarks-report.ts +66 -0
  116. package/src/run-benchmarks.test.mts +231 -0
  117. package/src/run-benchmarks.ts +589 -0
  118. package/src/smoke-structural-rollout.mts +393 -0
  119. package/src/swe-bench-env.test.ts +207 -0
  120. package/src/swe-bench-env.ts +554 -0
  121. package/src/swe-jail.ts +293 -0
  122. package/src/swe-self-improve.mts +84 -0
  123. package/src/swe-structural-judge-policy.test.ts +117 -0
  124. package/src/swe-structural-judge-policy.ts +133 -0
  125. package/src/swe-structural-policy.test.ts +124 -0
  126. package/src/swe-structural-policy.ts +132 -0
  127. package/src/swe-structural-provenance.test.ts +93 -0
  128. package/src/swe-structural-provenance.ts +138 -0
  129. package/src/swe-structural.mts +1260 -0
  130. package/src/swe-temp.ts +14 -0
  131. package/src/tb-container-executor.mts +234 -0
  132. package/src/tb-container-executor.test.mts +99 -0
  133. package/src/tb-supervisor-sidecar.mts +222 -0
  134. package/src/trata-gepa.mts +1 -1
  135. package/steerers/eops-itsm-population.json +1 -0
  136. package/tb_agents/opencode_refine_agent.py +117 -0
  137. package/tb_agents/opencode_router_agent.py +406 -0
  138. package/tb_agents/opencode_supervisor_agent.py +239 -0
  139. package/tb_agents/script_agent.py +66 -0
@@ -0,0 +1,715 @@
1
+ import { execFileSync } from 'node:child_process'
2
+ import { createHash } from 'node:crypto'
3
+ import {
4
+ chmodSync,
5
+ cpSync,
6
+ lstatSync,
7
+ mkdirSync,
8
+ mkdtempSync,
9
+ readFileSync,
10
+ readdirSync,
11
+ rmSync,
12
+ writeFileSync,
13
+ } from 'node:fs'
14
+ import { tmpdir } from 'node:os'
15
+ import path from 'node:path'
16
+ import { fileURLToPath } from 'node:url'
17
+
18
+ import { canonicalJson, InMemoryTraceStore } from '@tangle-network/agent-eval'
19
+ import {
20
+ sealCandidateBenchmarkSuite,
21
+ sealCandidateBenchmarkTask,
22
+ } from '@tangle-network/agent-eval/contract'
23
+ import type {
24
+ AgentCandidateArtifactRef,
25
+ AgentCandidateBundle,
26
+ AgentCandidateWorkspaceSnapshotEvidence,
27
+ Sha256Digest,
28
+ } from '@tangle-network/agent-interface'
29
+ import {
30
+ createAgentCandidateWorkspacePort,
31
+ disposePreparedAgentCandidateExecution,
32
+ FileAgentCandidateExecutionClaimStore,
33
+ prepareAgentCandidateExecution,
34
+ type AgentCandidateExecutionPorts,
35
+ type AgentCandidateOutputArtifactPort,
36
+ type AgentCandidateTaskExecution,
37
+ type ResolvedAgentCandidateContainer,
38
+ sealAgentCandidateBundle,
39
+ verifyAgentCandidateBundle,
40
+ } from '@tangle-network/agent-runtime'
41
+
42
+ import { executePreparedPierCandidate } from '../src/pier-agent'
43
+ import { createPierResultGrader } from '../src/pier-result-grader'
44
+ import { FilePierCandidateTrialController } from '../src/pier-trial-controller'
45
+
46
+ const pinnedPierCommit = 'e69a20e4e0ac073ec71fde0274bab3d9f40bac87'
47
+ const pinnedPierVersion = '0.3.0'
48
+ const modelRequest = 'openai/gpt-5.4'
49
+ const fixtureImage = 'ghcr.io/tangle-network/devcontainers/universal:latest'
50
+ const prepareOnly = process.env.PIER_PREPARE_ONLY === '1'
51
+ const proofArm = process.env.PIER_PROOF_ARM
52
+ if (proofArm !== 'failure' && proofArm !== 'success') {
53
+ throw new Error('PIER_PROOF_ARM must be explicitly set to failure or success')
54
+ }
55
+ const expectedReward = proofArm === 'success' ? 1 : 0
56
+ const expectedPatchApplied = proofArm === 'success' ? 1 : 0
57
+ const benchDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
58
+ const pierRepo = path.resolve(process.env.PIER_REPO ?? path.join(benchDir, '..', '..', 'pier'))
59
+ const fixtureSource = path.join(benchDir, 'fixtures', 'pier-agent', 'no-model-task')
60
+ const scratch = mkdtempSync(path.join(tmpdir(), 'agent-bench-pier-runtime-'))
61
+ const taskDir = path.join(scratch, 'task')
62
+ const taskRoot = path.join(scratch, 'task-workspace')
63
+ const candidateRoot = path.join(scratch, 'candidate-workspace')
64
+ const profileRoot = path.join(scratch, 'profile-workspace')
65
+ const jobsDir = path.join(scratch, 'jobs')
66
+
67
+ function output(
68
+ command: string,
69
+ args: string[],
70
+ cwd = benchDir,
71
+ env: NodeJS.ProcessEnv = process.env,
72
+ ): string {
73
+ return execFileSync(command, args, {
74
+ cwd,
75
+ env,
76
+ encoding: 'utf8',
77
+ stdio: ['ignore', 'pipe', 'pipe'],
78
+ timeout: 10 * 60_000,
79
+ maxBuffer: 20 * 1024 * 1024,
80
+ }).trim()
81
+ }
82
+
83
+ function sha256(bytes: Uint8Array): Sha256Digest {
84
+ return `sha256:${createHash('sha256').update(bytes).digest('hex')}`
85
+ }
86
+
87
+ function canonicalBytes(value: unknown): Buffer {
88
+ return Buffer.from(canonicalJson(value), 'utf8')
89
+ }
90
+
91
+ function sealCanonical<T extends object>(material: T): T & { digest: Sha256Digest } {
92
+ return { ...material, digest: sha256(canonicalBytes(material)) }
93
+ }
94
+
95
+ function embedded(bytes: Uint8Array) {
96
+ return {
97
+ encoding: 'base64' as const,
98
+ content: Buffer.from(bytes).toString('base64'),
99
+ sha256: sha256(bytes),
100
+ byteLength: bytes.byteLength,
101
+ }
102
+ }
103
+
104
+ function workspaceSnapshot(
105
+ root: string,
106
+ paths: readonly string[],
107
+ ): AgentCandidateWorkspaceSnapshotEvidence {
108
+ const files = paths
109
+ .map((relative) => {
110
+ const absolute = path.join(root, relative)
111
+ const bytes = readFileSync(absolute)
112
+ const mode = lstatSync(absolute).mode & 0o777
113
+ if (mode !== 0o644 && mode !== 0o755) {
114
+ throw new Error(`unsupported fixture mode ${mode.toString(8)}: ${relative}`)
115
+ }
116
+ return {
117
+ path: relative,
118
+ mode: mode as 0o644 | 0o755,
119
+ sha256: sha256(bytes),
120
+ byteLength: bytes.byteLength,
121
+ }
122
+ })
123
+ .sort((left, right) => left.path.localeCompare(right.path))
124
+ const material = {
125
+ kind: 'agent-candidate-workspace-manifest' as const,
126
+ files,
127
+ } satisfies AgentCandidateWorkspaceSnapshotEvidence['material']
128
+ const manifest = canonicalBytes(material)
129
+ return {
130
+ kind: 'agent-candidate-workspace-snapshot',
131
+ digest: sha256(manifest),
132
+ material,
133
+ manifest: embedded(manifest),
134
+ archive: embedded(Buffer.from(`pre-materialized:${sha256(manifest)}`, 'utf8')),
135
+ }
136
+ }
137
+
138
+ function deterministicRepository(root: string, seed: string): { commit: string; tree: string } {
139
+ mkdirSync(path.join(root, 'src'), { recursive: true })
140
+ cpSync(seed, path.join(root, 'src', 'status.txt'))
141
+ chmodSync(path.join(root, 'src', 'status.txt'), 0o644)
142
+ output('git', ['init', '-b', 'main', root])
143
+ output('git', ['-C', root, 'config', 'user.email', 'fixture@tangle.tools'])
144
+ output('git', ['-C', root, 'config', 'user.name', 'Tangle Fixture'])
145
+ output('git', [
146
+ '-C',
147
+ root,
148
+ 'remote',
149
+ 'add',
150
+ 'origin',
151
+ 'git@github.com:tangle-network/agent-bench-pier-fixture.git',
152
+ ])
153
+ output('git', ['-c', 'core.hooksPath=/dev/null', '-C', root, 'add', '-A'])
154
+ output(
155
+ 'git',
156
+ ['-c', 'core.hooksPath=/dev/null', '-C', root, 'commit', '-m', 'baseline'],
157
+ benchDir,
158
+ {
159
+ ...process.env,
160
+ GIT_AUTHOR_DATE: '2000-01-01T00:00:00Z',
161
+ GIT_COMMITTER_DATE: '2000-01-01T00:00:00Z',
162
+ },
163
+ )
164
+ return {
165
+ commit: output('git', ['-C', root, 'rev-parse', 'HEAD']),
166
+ tree: output('git', ['-C', root, 'rev-parse', 'HEAD^{tree}']),
167
+ }
168
+ }
169
+
170
+ function publicOciIdentity(
171
+ image: string,
172
+ ): { indexDigest: Sha256Digest; manifestDigest: Sha256Digest } {
173
+ const material = JSON.parse(
174
+ output('docker', ['buildx', 'imagetools', 'inspect', image, '--format', '{{json .Manifest}}']),
175
+ ) as {
176
+ digest?: string
177
+ manifests?: Array<{
178
+ digest?: string
179
+ platform?: { os?: string; architecture?: string }
180
+ }>
181
+ }
182
+ const topDigest = material.digest
183
+ if (!topDigest?.match(/^sha256:[a-f0-9]{64}$/)) {
184
+ throw new Error(`public image omitted a valid index digest: ${topDigest}`)
185
+ }
186
+ const selected = material.manifests?.find(
187
+ (entry) => entry.platform?.os === 'linux' && entry.platform.architecture === 'amd64',
188
+ )
189
+ const manifestDigest = selected?.digest
190
+ if (!manifestDigest) throw new Error('public image has no linux/amd64 manifest')
191
+ if (!manifestDigest.match(/^sha256:[a-f0-9]{64}$/)) {
192
+ throw new Error(`public image returned an invalid platform manifest: ${manifestDigest}`)
193
+ }
194
+ return {
195
+ indexDigest: topDigest as Sha256Digest,
196
+ manifestDigest: manifestDigest as Sha256Digest,
197
+ }
198
+ }
199
+
200
+ function findTrialResult(root: string): { path: string; value: Record<string, any> } | undefined {
201
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
202
+ if (!entry.isDirectory()) continue
203
+ const child = path.join(root, entry.name)
204
+ const candidate = path.join(child, 'result.json')
205
+ try {
206
+ const value = JSON.parse(readFileSync(candidate, 'utf8')) as Record<string, any>
207
+ if (typeof value.trial_name === 'string') return { path: candidate, value }
208
+ } catch {}
209
+ const nested = findTrialResult(child)
210
+ if (nested) return nested
211
+ }
212
+ return undefined
213
+ }
214
+
215
+ function assertTreeOmits(root: string, forbidden: string): void {
216
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
217
+ const absolute = path.join(root, entry.name)
218
+ if (entry.isDirectory()) {
219
+ assertTreeOmits(absolute, forbidden)
220
+ } else if (entry.isFile() && readFileSync(absolute).includes(Buffer.from(forbidden))) {
221
+ throw new Error(`protected value persisted in ${path.relative(root, absolute)}`)
222
+ }
223
+ }
224
+ }
225
+
226
+ function outputArtifactStore(graderBytes: Uint8Array): {
227
+ outputArtifacts: AgentCandidateOutputArtifactPort
228
+ graderArtifact: AgentCandidateArtifactRef
229
+ } {
230
+ const stored = new Map<string, Uint8Array>()
231
+ const put = (bytes: Uint8Array, purpose: string): AgentCandidateArtifactRef => {
232
+ const detached = Uint8Array.from(bytes)
233
+ const digest = sha256(detached)
234
+ const artifact: AgentCandidateArtifactRef = {
235
+ locator: {
236
+ kind: 's3',
237
+ bucket: 'agent-bench-pier-proof',
238
+ key: `${purpose}/${digest.slice('sha256:'.length)}`,
239
+ },
240
+ sha256: digest,
241
+ byteLength: detached.byteLength,
242
+ }
243
+ stored.set(digest, detached)
244
+ return artifact
245
+ }
246
+ const graderArtifact = put(graderBytes, 'graders')
247
+ return {
248
+ graderArtifact,
249
+ outputArtifacts: {
250
+ put: async ({ bytes, purpose, signal }) => {
251
+ signal?.throwIfAborted()
252
+ return put(bytes, purpose)
253
+ },
254
+ read: async (artifact) => {
255
+ const bytes = stored.get(artifact.sha256)
256
+ if (!bytes) throw new Error(`missing Pier proof artifact ${artifact.sha256}`)
257
+ return Uint8Array.from(bytes)
258
+ },
259
+ },
260
+ }
261
+ }
262
+
263
+ try {
264
+ if (!prepareOnly) {
265
+ const pierHead = output('git', ['rev-parse', 'HEAD'], pierRepo)
266
+ if (pierHead !== pinnedPierCommit) {
267
+ throw new Error(`Pier checkout mismatch: expected ${pinnedPierCommit}, got ${pierHead}`)
268
+ }
269
+ const pierStatus = output('git', ['status', '--porcelain'], pierRepo)
270
+ if (pierStatus !== '') throw new Error(`Pier checkout must be clean: ${pierStatus}`)
271
+ const pierVersion = output('uv', ['run', 'pier', '--version'], pierRepo)
272
+ if (pierVersion !== pinnedPierVersion) {
273
+ throw new Error(`Pier version mismatch: expected ${pinnedPierVersion}, got ${pierVersion}`)
274
+ }
275
+ }
276
+
277
+ cpSync(fixtureSource, taskDir, { recursive: true })
278
+ for (const relative of ['environment/seed/src/status.txt', 'tests/seed/src/status.txt']) {
279
+ chmodSync(path.join(taskDir, relative), 0o644)
280
+ }
281
+ for (const relative of ['pre_artifacts.sh', 'tests/test.sh']) {
282
+ chmodSync(path.join(taskDir, relative), 0o755)
283
+ }
284
+
285
+ const contextDigest = sha256(
286
+ Buffer.concat([
287
+ readFileSync(path.join(taskDir, 'environment', 'Dockerfile')),
288
+ readFileSync(path.join(taskDir, 'environment', 'seed', 'src', 'status.txt')),
289
+ ]),
290
+ ).slice(7, 23)
291
+ const identity = prepareOnly
292
+ ? {
293
+ indexDigest: `sha256:${'1'.repeat(64)}` as Sha256Digest,
294
+ manifestDigest: `sha256:${'2'.repeat(64)}` as Sha256Digest,
295
+ }
296
+ : publicOciIdentity(fixtureImage)
297
+ const pinnedImage = `${fixtureImage}@${identity.indexDigest}`
298
+ if (!prepareOnly) output('docker', ['pull', '--platform', 'linux/amd64', pinnedImage])
299
+ const platform = prepareOnly
300
+ ? 'linux/amd64'
301
+ : output('docker', [
302
+ 'image',
303
+ 'inspect',
304
+ '--format',
305
+ '{{.Os}}/{{.Architecture}}',
306
+ pinnedImage,
307
+ ])
308
+ if (platform !== 'linux/amd64') throw new Error(`fixture image platform drifted: ${platform}`)
309
+
310
+ const configPath = path.join(taskDir, 'task.toml')
311
+ const config = readFileSync(configPath, 'utf8')
312
+ writeFileSync(
313
+ configPath,
314
+ config.replace(
315
+ '[environment]\n',
316
+ `[environment]\ndocker_image = "${pinnedImage}"\nos = "linux"\n`,
317
+ ),
318
+ )
319
+ mkdirSync(candidateRoot)
320
+ mkdirSync(profileRoot)
321
+ const instruction = readFileSync(path.join(taskDir, 'instruction.md'), 'utf8')
322
+ const runner = `import pathlib, sys
323
+ task = pathlib.Path.cwd()
324
+ profile = task / 'AGENTS.md'
325
+ assert profile.is_file() and profile.read_text().strip()
326
+ assert sys.argv[-1] == ${JSON.stringify(instruction)}
327
+ ${proofArm === 'success' ? "(task / 'src/status.txt').write_text('ready\\nowner=tangle\\n')" : ''}
328
+ `
329
+ writeFileSync(path.join(candidateRoot, 'runner.py'), runner)
330
+ chmodSync(path.join(candidateRoot, 'runner.py'), 0o755)
331
+ const repositoryState = deterministicRepository(
332
+ taskRoot,
333
+ path.join(taskDir, 'environment', 'seed', 'src', 'status.txt'),
334
+ )
335
+
336
+ const taskWorkspace = workspaceSnapshot(taskRoot, ['src/status.txt'])
337
+ const candidateWorkspace = workspaceSnapshot(candidateRoot, ['runner.py'])
338
+ const bundleWithoutDigest = {
339
+ kind: 'agent-candidate-bundle' as const,
340
+ digestAlgorithm: 'rfc8785-sha256' as const,
341
+ profile: {
342
+ name: `pier-no-model-runtime-${proofArm}`,
343
+ prompt: {
344
+ instructions: [
345
+ 'Edit only the task repository. Follow the exact user instruction and verify the result.',
346
+ ],
347
+ },
348
+ model: { default: modelRequest, reasoningEffort: 'xhigh' as const },
349
+ harness: 'codex' as const,
350
+ resources: { failOnError: true as const },
351
+ },
352
+ code: {
353
+ kind: 'no-op' as const,
354
+ reason: 'proposer-no-change' as const,
355
+ repository: {
356
+ kind: 'github' as const,
357
+ owner: 'tangle-network',
358
+ repo: 'agent-bench-pier-fixture',
359
+ },
360
+ baseCommit: repositoryState.commit,
361
+ baseTree: repositoryState.tree,
362
+ },
363
+ execution: {
364
+ harness: 'codex' as const,
365
+ harnessVersion: 'agent-bench-pier/2.0.0',
366
+ launch: {
367
+ kind: 'candidate-entrypoint' as const,
368
+ entrypoint: 'runner.py',
369
+ interpreter: 'python3' as const,
370
+ },
371
+ instructionDelivery: { kind: 'argv-append' as const },
372
+ cwd: { workspace: 'task' as const, path: '.' },
373
+ environment: { kind: 'evaluator-task-container' as const },
374
+ workspace: candidateWorkspace,
375
+ isolation: {
376
+ network: 'disabled' as const,
377
+ remoteIntegrations: 'disabled' as const,
378
+ candidateSecrets: 'disabled' as const,
379
+ },
380
+ },
381
+ memory: { mode: 'disabled' as const },
382
+ }
383
+ const bundle: AgentCandidateBundle = sealAgentCandidateBundle(bundleWithoutDigest)
384
+ const container: ResolvedAgentCandidateContainer = {
385
+ source: 'evaluator-task-container',
386
+ image: fixtureImage,
387
+ indexDigest: identity.indexDigest,
388
+ manifestDigest: identity.manifestDigest,
389
+ platform: { os: 'linux', architecture: 'amd64' },
390
+ }
391
+ const graderBytes = readFileSync(new URL('../src/pier-result-grader.mjs', import.meta.url))
392
+ const { outputArtifacts, graderArtifact } = outputArtifactStore(graderBytes)
393
+ const grader = createPierResultGrader({
394
+ name: 'pier-official-result',
395
+ version: '1.0.0',
396
+ artifact: graderArtifact,
397
+ })
398
+ const executionId = `pier-no-model-${proofArm}-${contextDigest}`
399
+ const benchmarkTask = sealCandidateBenchmarkTask({
400
+ kind: 'agent-candidate-benchmark-task',
401
+ digestAlgorithm: 'rfc8785-sha256',
402
+ benchmark: {
403
+ name: 'pier-runtime-proof',
404
+ version: '1',
405
+ splitDigest: `sha256:${'b'.repeat(64)}`,
406
+ },
407
+ scenario: {
408
+ id: `agent-bench/pier-candidate-no-model-${proofArm}`,
409
+ kind: 'coding',
410
+ scenarioDigest: sha256(Buffer.from(`pier-runtime-proof:${proofArm}`, 'utf8')),
411
+ },
412
+ instruction,
413
+ repository: {
414
+ identity: 'github.com/tangle-network/agent-bench-pier-fixture',
415
+ rootIdentity: 'tangle-network/agent-bench-pier-fixture',
416
+ baseCommit: repositoryState.commit,
417
+ baseTree: repositoryState.tree,
418
+ },
419
+ outcome: { kind: 'workspace' },
420
+ attempt: { maxAttempts: 1, retryPolicy: 'none' },
421
+ model: {
422
+ requested: modelRequest,
423
+ provider: 'openai',
424
+ model: 'gpt-5.4',
425
+ snapshot: 'gpt-5.4-no-model-proof',
426
+ reasoningEffort: 'xhigh',
427
+ },
428
+ grader: {
429
+ name: grader.name,
430
+ version: grader.version,
431
+ format: 'tangle-grader',
432
+ artifact: grader.artifact,
433
+ },
434
+ workspace: taskWorkspace,
435
+ evaluatorTaskContainer: container,
436
+ limits: {
437
+ timeoutMs: 60_000,
438
+ maxSteps: 8,
439
+ maxModelCalls: 0,
440
+ maxInputTokens: 0,
441
+ maxOutputTokens: 0,
442
+ maxCostUsd: 0,
443
+ },
444
+ })
445
+ const benchmark = sealCandidateBenchmarkSuite({
446
+ tasks: [benchmarkTask],
447
+ reps: 1,
448
+ seeds: [42],
449
+ })
450
+ const task: AgentCandidateTaskExecution = {
451
+ executionId,
452
+ runCell: sealCanonical({
453
+ kind: 'agent-candidate-run-cell' as const,
454
+ experimentDigest: sha256(Buffer.from('pier-runtime-proof-experiment', 'utf8')),
455
+ arm: 'candidate' as const,
456
+ bundleDigest: bundle.digest,
457
+ suiteDigest: benchmark.suite.digest,
458
+ taskDigest: benchmarkTask.digest,
459
+ taskIndex: 0,
460
+ repetition: 0,
461
+ seed: 42,
462
+ attempt: 1,
463
+ }),
464
+ benchmarkSuite: benchmark.suite,
465
+ task: benchmarkTask,
466
+ executionRoots: { taskRoot: '/app', candidateRoot: '/opt/tangle-candidate' },
467
+ stagingRoots: { taskRoot, candidateRoot, profileRoot },
468
+ }
469
+ const workspaces = createAgentCandidateWorkspacePort()
470
+ const ports: AgentCandidateExecutionPorts = {
471
+ artifacts: {
472
+ read: async () => {
473
+ throw new Error('the proof uses only embedded artifacts')
474
+ },
475
+ },
476
+ repositories: { resolve: async () => taskRoot },
477
+ workspaces: {
478
+ materialize: async ({ role, snapshot, archive, destination }) => {
479
+ if (destination === taskRoot || destination === candidateRoot) return
480
+ await workspaces.materialize({
481
+ role,
482
+ snapshot,
483
+ archive,
484
+ destination,
485
+ })
486
+ },
487
+ },
488
+ containers: { resolve: async () => container },
489
+ models: {
490
+ resolve: async ({ requested, reasoningEffort }) => {
491
+ if (!reasoningEffort) throw new Error('resolved model requires reasoning effort')
492
+ return {
493
+ requested,
494
+ provider: 'openai',
495
+ model: 'gpt-5.4',
496
+ snapshot: 'gpt-5.4-no-model-proof',
497
+ reasoningEffort,
498
+ }
499
+ },
500
+ reserveGrant: async ({ limits, preparationId, expiresAtMs }) => ({
501
+ preparationId,
502
+ digest: `sha256:${'c'.repeat(64)}`,
503
+ expiresAtMs,
504
+ enforcedLimits: limits,
505
+ network: { mode: 'disabled' },
506
+ }),
507
+ activateGrant: async () => ({
508
+ env: { MODEL_GATEWAY_TOKEN: 'zero-model-proof' },
509
+ }),
510
+ settleGrant: async ({ preparationId }) => ({
511
+ preparationId,
512
+ grantDigest: `sha256:${'c'.repeat(64)}`,
513
+ closed: true,
514
+ calls: [],
515
+ }),
516
+ },
517
+ memory: {
518
+ reset: async () => {
519
+ throw new Error('disabled memory must not be reset')
520
+ },
521
+ activate: async () => {
522
+ throw new Error('disabled memory must not be activated')
523
+ },
524
+ close: async () => {
525
+ throw new Error('disabled memory must not be closed')
526
+ },
527
+ },
528
+ }
529
+
530
+ const verified = await verifyAgentCandidateBundle(bundle, ports)
531
+ const prepared = await prepareAgentCandidateExecution(verified, task, ports)
532
+ if (prepareOnly) {
533
+ const disposal = await disposePreparedAgentCandidateExecution(prepared)
534
+ if (disposal.disposed !== true) throw new Error('prepared candidate was not disposed')
535
+ process.stdout.write(
536
+ `${JSON.stringify({
537
+ prepared: true,
538
+ disposed: true,
539
+ executionPlanDigest: prepared.executionPlan.value.digest,
540
+ graderDigest: benchmarkTask.grader.artifact.sha256,
541
+ })}\n`,
542
+ )
543
+ } else {
544
+ const traceStore = new InMemoryTraceStore()
545
+ const claimStore = new FileAgentCandidateExecutionClaimStore({
546
+ directory: path.join(scratch, 'claims'),
547
+ })
548
+ let acceptedRewards: { reward: number; patch_applied: number } | undefined
549
+ let acceptedTrialPath: string | undefined
550
+ const jobName = `tangle-runtime-candidate-no-model-${proofArm}`
551
+ const controller = new FilePierCandidateTrialController({
552
+ directory: path.join(scratch, 'trial-control'),
553
+ readResult: async ({ jobsDirectory, jobName }) => {
554
+ const trialResult = findTrialResult(path.join(jobsDirectory, jobName))
555
+ if (!trialResult) return undefined
556
+ return {
557
+ value: trialResult.value,
558
+ resultBytes: readFileSync(trialResult.path),
559
+ taskPatch: readFileSync(
560
+ path.join(path.dirname(trialResult.path), 'artifacts', 'model.patch'),
561
+ ),
562
+ }
563
+ },
564
+ launch: (staged, { request }) => {
565
+ const evaluatorArgs = Object.keys(staged.evaluatorEnv).flatMap((name) => [
566
+ '--agent-env',
567
+ `${name}=\${${name}}`,
568
+ ])
569
+ return {
570
+ command: 'uv',
571
+ args: [
572
+ 'run',
573
+ 'pier',
574
+ 'run',
575
+ '--path',
576
+ taskDir,
577
+ ...staged.agentArgs,
578
+ ...staged.attemptArgs,
579
+ ...evaluatorArgs,
580
+ '--env',
581
+ 'docker',
582
+ '--job-name',
583
+ jobName,
584
+ '--jobs-dir',
585
+ jobsDir,
586
+ '--n-concurrent',
587
+ '1',
588
+ '--agent-timeout-multiplier',
589
+ '2',
590
+ '--quiet',
591
+ ],
592
+ cwd: pierRepo,
593
+ env: { ...process.env, PYTHONPATH: benchDir, ...staged.evaluatorEnv },
594
+ jobsDirectory: jobsDir,
595
+ jobName,
596
+ readResult: async () => {
597
+ const trialResult = findTrialResult(jobsDir)
598
+ if (!trialResult) throw new Error(`Pier emitted no trial result under ${jobsDir}`)
599
+ const result = trialResult.value
600
+ if (result.exception_info !== null) {
601
+ throw new Error(
602
+ `Pier trial captured an exception: ${JSON.stringify(result.exception_info)}`,
603
+ )
604
+ }
605
+ const rewards = result.verifier_result?.rewards
606
+ if (
607
+ rewards?.reward !== expectedReward ||
608
+ rewards?.patch_applied !== expectedPatchApplied
609
+ ) {
610
+ throw new Error(
611
+ `Pier ${proofArm} control returned unexpected rewards: ${JSON.stringify(rewards)}`,
612
+ )
613
+ }
614
+ const agentResult = result.agent_result
615
+ for (const name of ['n_input_tokens', 'n_cache_tokens', 'n_output_tokens', 'cost_usd']) {
616
+ if (agentResult?.[name] !== null) {
617
+ throw new Error(
618
+ `Pier must not author protected usage ${name}: ${agentResult?.[name]}`,
619
+ )
620
+ }
621
+ }
622
+ const observedElapsedMs = agentResult?.metadata?.observedElapsedMs
623
+ if (!Number.isInteger(observedElapsedMs) || observedElapsedMs < 0) {
624
+ throw new Error(`Pier omitted protected elapsed time: ${observedElapsedMs}`)
625
+ }
626
+ const endedAt = Date.now()
627
+ await traceStore.appendRun({
628
+ runId: request.trace.runId,
629
+ scenarioId: benchmarkTask.scenario.id,
630
+ startedAt: endedAt - observedElapsedMs,
631
+ endedAt,
632
+ status: 'completed',
633
+ tags: { ...request.trace.tags },
634
+ })
635
+ acceptedRewards = rewards
636
+ acceptedTrialPath = path.relative(jobsDir, trialResult.path)
637
+ return {
638
+ value: result,
639
+ resultBytes: readFileSync(trialResult.path),
640
+ taskPatch: readFileSync(
641
+ path.join(path.dirname(trialResult.path), 'artifacts', 'model.patch'),
642
+ ),
643
+ }
644
+ },
645
+ }
646
+ },
647
+ })
648
+ const finalized = await executePreparedPierCandidate({
649
+ prepared,
650
+ directory: path.join(scratch, 'sealed'),
651
+ pierVersion: pinnedPierVersion,
652
+ traceStore,
653
+ claimStore,
654
+ outputArtifacts,
655
+ grader,
656
+ controller,
657
+ })
658
+ if (!finalized.succeeded) {
659
+ throw new Error(`runtime rejected the protected Pier capture: ${finalized.reason}`)
660
+ }
661
+ if (!acceptedRewards || !acceptedTrialPath) {
662
+ throw new Error('atomic Pier executor returned without accepted verifier evidence')
663
+ }
664
+ if (
665
+ finalized.receipt.value.benchmarkResult.material.score !== expectedReward ||
666
+ finalized.receipt.value.benchmarkResult.material.passed !== (expectedReward === 1)
667
+ ) {
668
+ throw new Error(
669
+ `runtime receipt rejected the official Pier result: ${JSON.stringify(finalized.receipt.value.benchmarkResult.material)}`,
670
+ )
671
+ }
672
+ assertTreeOmits(scratch, 'zero-model-proof')
673
+ const usage = finalized.receipt.value.usage
674
+ if (
675
+ usage.modelCalls !== 0 ||
676
+ usage.inputTokens !== 0 ||
677
+ usage.outputTokens !== 0 ||
678
+ usage.costUsd !== 0 ||
679
+ finalized.receipt.value.trace.modelCallCount !== 0
680
+ ) {
681
+ throw new Error(`runtime minted nonzero usage for a zero-model run: ${JSON.stringify(usage)}`)
682
+ }
683
+
684
+ console.log(
685
+ JSON.stringify(
686
+ {
687
+ arm: proofArm,
688
+ reward: acceptedRewards.reward,
689
+ patchApplied: acceptedRewards.patch_applied,
690
+ modelCalls: usage.modelCalls,
691
+ inputTokens: usage.inputTokens,
692
+ outputTokens: usage.outputTokens,
693
+ costUsd: usage.costUsd,
694
+ pierContextUsage: null,
695
+ profileExcludedByVerifier: true,
696
+ container: {
697
+ image: fixtureImage,
698
+ indexDigest: identity.indexDigest,
699
+ manifestDigest: identity.manifestDigest,
700
+ platform,
701
+ },
702
+ executionPlanDigest: prepared.executionPlan.value.digest,
703
+ materializationReceiptDigest: prepared.materializationReceipt.digest,
704
+ runReceiptDigest: finalized.receipt.digest,
705
+ trial: acceptedTrialPath,
706
+ },
707
+ null,
708
+ 2,
709
+ ),
710
+ )
711
+ }
712
+ } finally {
713
+ if (process.env.KEEP_PIER_FIXTURE !== '1') rmSync(scratch, { recursive: true, force: true })
714
+ else console.error(`Pier runtime fixture retained at ${scratch}`)
715
+ }