@tangle-network/agent-bench 0.9.2 → 0.9.4

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.4
4
+
5
+ Requires Runtime 0.202.0 so Bench consumers receive explicit failed-execution settlement and retained failure recovery.
6
+ Supports an optional Node test concurrency limit for hosts with constrained memory.
7
+
8
+ ## 0.9.3
9
+
10
+ Requires Runtime 0.201.0, Interface 2.5.0, Eval 0.176, and Knowledge 15 for scoped candidate knowledge.
11
+ Publishing waits until dependency archives can be downloaded and integrity-checked, including registry propagation delays.
12
+
3
13
  ## 0.9.2
4
14
 
5
15
  Requires Interface 2.4.0 and Runtime 0.200.0 for the shared execution contracts.
package/HARNESS.md CHANGED
@@ -37,6 +37,14 @@ pnpm verify:bench
37
37
  pnpm verify:official-optimizers
38
38
  ```
39
39
 
40
+ Set `AGENT_BENCH_PACKAGE_TEST_CONCURRENCY=1` to run Node source test files sequentially on memory-constrained hosts.
41
+ The value must be a positive safe integer; leaving it unset preserves Node's default concurrency.
42
+ Vitest uses the worker limit in `bench/vitest.config.ts`.
43
+
44
+ Packing resolves the Runtime dependency from `workspace:^` to a version range.
45
+ A new Runtime compatibility line does not update previously published Bench packages.
46
+ Check the packed dependency range and release Bench when consumers must receive that Runtime version.
47
+
40
48
  `verify:official-optimizers` exercises the official Optimize Anything bridge, engine identities, equal input budgets, resume compatibility, candidate callbacks, accounting, and package provenance. Its deterministic candidate improvement is deliberately a fixture. It does **not** reproduce the published GEPA or Omni benchmark numbers.
41
49
 
42
50
  ### Bounded benchmark matrix
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-bench",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "type": "module",
5
5
  "description": "Benchmark adapters and execution for agent-runtime across coding, tool-use, RAG, memory, browser, and terminal tasks.",
6
6
  "repository": {
@@ -25,11 +25,11 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@tangle-network/agent-eval": ">=0.175.0 <0.176.0",
29
- "@tangle-network/agent-interface": "^2.4.0",
30
- "@tangle-network/agent-knowledge": "^14.0.3",
28
+ "@tangle-network/agent-eval": ">=0.176.0 <0.177.0",
29
+ "@tangle-network/agent-interface": "^2.5.0",
30
+ "@tangle-network/agent-knowledge": "^15.0.0",
31
31
  "@tangle-network/sandbox": ">=0.36.4 <0.38.0",
32
- "@tangle-network/agent-runtime": "^0.200.0"
32
+ "@tangle-network/agent-runtime": "^0.202.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@arethetypeswrong/cli": "0.18.5",
@@ -8,6 +8,21 @@ const execFileAsync = promisify(execFile)
8
8
  const benchDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
9
9
  const sourceDir = path.join(benchDir, 'src')
10
10
 
11
+ export function packageNodeTestArgs(files, env = process.env) {
12
+ const raw = env.AGENT_BENCH_PACKAGE_TEST_CONCURRENCY
13
+ const concurrency = raw === undefined ? undefined : Number(raw)
14
+ if (raw !== undefined && (!/^[1-9]\d*$/.test(raw) || !Number.isSafeInteger(concurrency))) {
15
+ throw new Error('AGENT_BENCH_PACKAGE_TEST_CONCURRENCY must be a positive safe integer')
16
+ }
17
+ return [
18
+ '--test',
19
+ ...(concurrency === undefined ? [] : [`--test-concurrency=${concurrency}`]),
20
+ '--import',
21
+ 'tsx',
22
+ ...files,
23
+ ]
24
+ }
25
+
11
26
  export function resolvePackageTestTimeoutMs(env = process.env) {
12
27
  const raw = env.AGENT_BENCH_PACKAGE_TEST_TIMEOUT_MS
13
28
  if (raw === undefined) return undefined
@@ -74,7 +89,7 @@ async function main() {
74
89
  if (nodeTests.length > 0) {
75
90
  await run(
76
91
  process.execPath,
77
- ['--test', '--import', 'tsx', ...nodeTests.map((file) => path.relative(benchDir, file))],
92
+ packageNodeTestArgs(nodeTests.map((file) => path.relative(benchDir, file))),
78
93
  {
79
94
  ...process.env,
80
95
  TSX_TSCONFIG_PATH: 'tsconfig.public.json',
@@ -1,6 +1,20 @@
1
1
  import assert from 'node:assert/strict'
2
2
  import { test } from 'node:test'
3
- import { resolvePackageTestTimeoutMs, run } from './run-package-tests.mjs'
3
+ import { packageNodeTestArgs, resolvePackageTestTimeoutMs, run } from './run-package-tests.mjs'
4
+
5
+ test('package test concurrency reaches Node without changing selected files', () => {
6
+ const files = ['src/first.test.mts', 'src/second.test.ts']
7
+ assert.deepEqual(packageNodeTestArgs(files, {}), ['--test', '--import', 'tsx', ...files])
8
+ assert.deepEqual(packageNodeTestArgs(files, { AGENT_BENCH_PACKAGE_TEST_CONCURRENCY: '1' }), [
9
+ '--test', '--test-concurrency=1', '--import', 'tsx', ...files,
10
+ ])
11
+ for (const value of ['', '0', '-1', '1.5', 'Infinity', '9007199254740992']) {
12
+ assert.throws(
13
+ () => packageNodeTestArgs(files, { AGENT_BENCH_PACKAGE_TEST_CONCURRENCY: value }),
14
+ /must be a positive safe integer/,
15
+ )
16
+ }
17
+ })
4
18
 
5
19
  test('package test timeout is optional and caller-controlled', () => {
6
20
  assert.equal(resolvePackageTestTimeoutMs({}), undefined)
@@ -118,32 +118,44 @@ export async function waitForPublishedDependencies(
118
118
  }
119
119
  }
120
120
 
121
- async function probeNpm(dependency, { timeoutMs }) {
121
+ export async function probeNpm(dependency, { timeoutMs, run = execFileAsync }) {
122
+ const scratch = await mkdtemp(path.join(tmpdir(), 'agent-bench-dependency-probe-'))
122
123
  try {
123
- const { stdout } = await execFileAsync(
124
+ // A fresh cache makes npm retrieve and integrity-check the archive, even when
125
+ // registry metadata has propagated before the archive becomes available.
126
+ const { stdout } = await run(
124
127
  'npm',
125
128
  [
126
- 'view',
129
+ 'pack',
127
130
  `${dependency.name}@${dependency.spec}`,
128
- 'version',
129
131
  '--json',
132
+ '--ignore-scripts',
130
133
  '--prefer-online',
134
+ '--cache',
135
+ path.join(scratch, 'cache'),
136
+ '--pack-destination',
137
+ scratch,
131
138
  ],
132
139
  {
133
140
  env: { ...process.env, npm_config_color: 'false' },
134
- maxBuffer: 1024 * 1024,
141
+ maxBuffer: 10 * 1024 * 1024,
135
142
  timeout: Math.min(30_000, timeoutMs),
136
143
  },
137
144
  )
138
- const version = JSON.parse(stdout)
145
+ const packed = JSON.parse(stdout)
139
146
  const available =
140
- (typeof version === 'string' && version.length > 0) ||
141
- (Array.isArray(version) && version.length > 0)
147
+ Array.isArray(packed) &&
148
+ packed.length === 1 &&
149
+ packed[0].name === dependency.name &&
150
+ typeof packed[0].version === 'string' &&
151
+ packed[0].version.length > 0
142
152
  return available
143
153
  ? { available: true }
144
- : { available: false, detail: 'npm returned no matching version' }
154
+ : { available: false, detail: 'npm returned no matching package archive' }
145
155
  } catch (error) {
146
156
  return { available: false, detail: summarizeCommandError(error) }
157
+ } finally {
158
+ await rm(scratch, { recursive: true, force: true })
147
159
  }
148
160
  }
149
161
 
@@ -1,8 +1,11 @@
1
1
  import assert from 'node:assert/strict'
2
+ import { access } from 'node:fs/promises'
3
+ import path from 'node:path'
2
4
  import test from 'node:test'
3
5
 
4
6
  import {
5
7
  collectRequiredTangleDependencies,
8
+ probeNpm,
6
9
  waitForPublishedDependencies,
7
10
  } from './wait-for-published-dependencies.mjs'
8
11
 
@@ -94,3 +97,70 @@ test('fails at the deadline and names every dependency still missing', async ()
94
97
  )
95
98
  assert.equal(currentTime, 100)
96
99
  })
100
+
101
+ test('waits for the exact package archive after metadata is published', async () => {
102
+ const dependency = { name: '@tangle-network/agent-runtime', spec: '0.200.0' }
103
+ let currentTime = 0
104
+ let archiveAttempts = 0
105
+ const scratchDirectories = []
106
+ const result = await waitForPublishedDependencies([dependency], {
107
+ timeoutMs: 100,
108
+ intervalMs: 25,
109
+ now: () => currentTime,
110
+ sleep: async (milliseconds) => {
111
+ currentTime += milliseconds
112
+ },
113
+ report: () => {},
114
+ probe: (requested, options) => probeNpm(requested, {
115
+ ...options,
116
+ run: async (command, args, executionOptions) => {
117
+ assert.equal(command, 'npm')
118
+ // Metadata is already visible, but its archive is not installable yet.
119
+ if (args[0] === 'view') return { stdout: JSON.stringify('0.200.0') }
120
+ assert.equal(args[0], 'pack')
121
+ assert.equal(args[1], '@tangle-network/agent-runtime@0.200.0')
122
+ assert.ok(args.includes('--ignore-scripts'))
123
+ const scratch = args[args.indexOf('--pack-destination') + 1]
124
+ assert.equal(args[args.indexOf('--cache') + 1], path.join(scratch, 'cache'))
125
+ scratchDirectories.push(scratch)
126
+ assert.ok(executionOptions.timeout <= 100 - currentTime)
127
+ archiveAttempts++
128
+ if (archiveAttempts === 1) {
129
+ throw Object.assign(new Error('archive unavailable'), {
130
+ stderr: 'npm error E404 GET agent-runtime-0.200.0.tgz',
131
+ })
132
+ }
133
+ return { stdout: JSON.stringify([{ name: dependency.name, version: '0.200.0' }]) }
134
+ },
135
+ }),
136
+ })
137
+ assert.deepEqual(result, { attempts: 2, elapsedMs: 25 })
138
+ assert.equal(archiveAttempts, 2)
139
+ assert.equal(new Set(scratchDirectories).size, 2)
140
+ for (const scratch of scratchDirectories) {
141
+ await assert.rejects(access(scratch), { code: 'ENOENT' })
142
+ }
143
+ })
144
+
145
+ test('reports a permanently invalid package archive at the existing deadline', async () => {
146
+ let currentTime = 0
147
+ await assert.rejects(waitForPublishedDependencies([
148
+ { name: '@tangle-network/agent-runtime', spec: '0.200.0' },
149
+ ], {
150
+ timeoutMs: 50,
151
+ intervalMs: 25,
152
+ now: () => currentTime,
153
+ sleep: async (milliseconds) => {
154
+ currentTime += milliseconds
155
+ },
156
+ report: () => {},
157
+ probe: (dependency, options) => probeNpm(dependency, {
158
+ ...options,
159
+ run: async () => {
160
+ throw Object.assign(new Error('archive integrity mismatch'), {
161
+ stderr: 'npm error code EINTEGRITY',
162
+ })
163
+ },
164
+ }),
165
+ }), /Timed out after 50ms and 2 attempts[\s\S]*agent-runtime@0\.200\.0: npm error code EINTEGRITY/)
166
+ })