agent-trials 1.0.0 → 1.0.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Interview your coding agent before you ship with it.
4
4
 
5
- `agent-trials` runs the same task suite through every coding agent CLI you have installed and prints a scoreboard: who passed, how long they took, what they spent. Every task runs in a throwaway copy of your repo, so agents can do their worst and your working tree stays clean.
5
+ `agent-trials` runs the same task suite through every coding agent CLI you have installed and prints a scoreboard: who passed, how long they took, what they spent. Each task gets a temporary working copy. Commands still run on your host.
6
6
 
7
7
  ![demo](demo.png)
8
8
 
@@ -10,6 +10,10 @@ Interview your coding agent before you ship with it.
10
10
 
11
11
  Public benchmarks score agents on tasks someone else picked. Your repo is not that benchmark. The question that matters before you let an agent loose on a codebase: does it fix YOUR bugs, in YOUR stack, at a sane price? Write the tasks once, run every candidate, keep the receipts.
12
12
 
13
+ ## execution safety
14
+
15
+ A temporary working copy is not a security sandbox. Agents and suite commands run with your host permissions and environment; built-in Claude and Gemini commands disable approval prompts. Repository dotfiles and secrets may be copied. Use only trusted suites in a disposable VM with no sensitive files or credentials. Do not run this on a workstation containing private data.
16
+
13
17
  ## install
14
18
 
15
19
  ```sh
@@ -31,6 +35,7 @@ agent-trials run # run the suite, print the board, write TRIALS.md
31
35
  ```json
32
36
  {
33
37
  "verify": "npm test",
38
+ "verifyPaths": ["verify"],
34
39
  "timeoutSec": 600,
35
40
  "tasks": [
36
41
  {
@@ -47,10 +52,13 @@ agent-trials run # run the suite, print the board, write TRIALS.md
47
52
 
48
53
  Each cell (agent x task) works like this:
49
54
 
50
- 1. your repo, minus `node_modules` and `.git`, is copied to a temp sandbox
55
+ 1. your repo, minus `node_modules` and `.git`, is copied to a temporary working copy
51
56
  2. optional `setup` command runs once (`npm ci` and friends)
52
- 3. the agent runs headless on the task prompt, permission checks off, timeout-guarded
53
- 4. your `verify` command runs in the sandbox; exit 0 means pass
57
+ 3. the agent runs headless on the task prompt, using the selected CLI flags, timeout-guarded
58
+ 4. checker paths are restored from your repo, so the agent cannot rewrite its own test
59
+ 5. your `verify` command runs in the working copy; exit 0 means pass
60
+
61
+ Step 4 restores `verifyPaths` from the suite, or a top-level `verify/` dir when `verifyPaths` is not set. A verify command that reads mutable state outside those paths can still be gamed; put your checkers in `verify/` or list them.
54
62
 
55
63
  Builtin agents: `claude`, `codex`, `gemini`. Custom agents are one argv array away: anything installed, any flags, `{prompt}` gets replaced with the task prompt.
56
64
 
@@ -73,11 +81,13 @@ cost $1.11 $1.10 $2.06
73
81
  winner: haiku (3/3, median 60.9s)
74
82
  ```
75
83
 
84
+ Read this board with caveats. `opus`, `sonnet`, and `haiku` are requested CLI aliases; that run predates model-identity capture (`--json` now records `modelUsed` per cell when the agent CLI reports it). One run of three toy tasks is a smoke test, not a comparative benchmark. Costs are CLI-reported, not invoice costs.
85
+
76
86
  What the board caught:
77
87
 
78
- - haiku, the cheap one, went 3/3 with the fastest median. It also spent the most: $2.06, because it ran more turns. Pass rate and price do not move together.
79
- - an earlier run caught the agent asking a clarifying question ("should -0 count as zero?") into an empty terminal, then exiting 0 having changed nothing. Exit code means nothing; your verify command is the ground truth.
80
- - sonnet lost slug-test outright. The task was write a test file that runs clean under plain node; it didn't.
88
+ - haiku went 3/3 with the fastest median while spending the most: $2.06. Pass rate and price did not move together.
89
+ - in one earlier run, the agent asked a clarifying question ("should -0 count as zero?") into an empty terminal, then exited 0 having changed nothing. One observed example, not a general claim. This showed why exit code alone cannot be the verdict; the verify command is what counts.
90
+ - sonnet failed slug-test.
81
91
 
82
92
  Full results with per-cell costs: [LEADERBOARD.md](LEADERBOARD.md)
83
93
 
@@ -86,7 +96,7 @@ Full results with per-cell costs: [LEADERBOARD.md](LEADERBOARD.md)
86
96
  - pass/fail is your verify command's exit code. The tool cannot judge taste, only outcomes.
87
97
  - runs spend your agent quota and your auth; costs are reported when the agent's JSON output includes them (claude does)
88
98
  - agent CLIs must be installed and logged in
89
- - macOS/Linux; the sandbox copy and verify assume `sh`. Windows untested
99
+ - macOS/Linux; the working copy and verify assume `sh`. Windows untested
90
100
  - scoring is binary per task. No partial credit for "almost fixed it"
91
101
 
92
102
  ## flags
@@ -99,7 +109,7 @@ Full results with per-cell costs: [LEADERBOARD.md](LEADERBOARD.md)
99
109
  | `--jobs <n>` | `2` | parallel agent x task cells |
100
110
  | `--out <file>` | `TRIALS.md` | markdown report path, `none` skips |
101
111
  | `--json <file>` | - | raw per-cell results |
102
- | `--keep` | off | keep sandboxes under /tmp for debugging |
112
+ | `--keep` | off | keep working copies under /tmp for debugging |
103
113
 
104
114
  ## why the suite format is the point
105
115
 
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { existsSync, writeFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { agentInstalled, BUILTIN_AGENTS, loadSuite, renderBoard, renderMarkdown, resolveAgents, runSuite, writeMarkdown, verdictOf, } from "./trials.js";
5
- const VERSION = "1.0.0";
5
+ const VERSION = "1.0.1";
6
6
  const HELP = `agent-trials ${VERSION}
7
7
  Interview your coding agent before you ship with it.
8
8
 
@@ -18,7 +18,7 @@ run flags:
18
18
  --jobs <n> parallel cells (default: 2)
19
19
  --out <file> markdown report path (default: TRIALS.md, "none" skips)
20
20
  --json <file> also write raw results JSON
21
- --keep keep sandbox dirs for debugging
21
+ --keep keep working copies for debugging
22
22
  `;
23
23
  const STARTER_SUITE = {
24
24
  name: "my repo trials",
package/dist/trials.js CHANGED
@@ -22,6 +22,9 @@ export function loadSuite(file) {
22
22
  if (!t.verify && !raw.verify)
23
23
  throw new Error(`${file}: task "${t.id}" has no verify and suite has no default`);
24
24
  }
25
+ if (raw.verifyPaths !== undefined && !Array.isArray(raw.verifyPaths)) {
26
+ throw new Error(`${file}: "verifyPaths" must be an array of repo-relative paths`);
27
+ }
25
28
  return raw;
26
29
  }
27
30
  export function resolveAgents(names, suite) {
@@ -83,12 +86,28 @@ function parseAgentJson(stdout) {
83
86
  try {
84
87
  const j = JSON.parse(stdout.trim());
85
88
  const cost = j.total_cost_usd ?? j.cost_usd ?? j.stats?.total_cost_usd;
86
- return typeof cost === "number" ? { costUsd: cost } : {};
89
+ const model = j.model ?? Object.keys(j.modelUsage ?? {})[0];
90
+ const out = {};
91
+ if (typeof cost === "number")
92
+ out.costUsd = cost;
93
+ if (typeof model === "string" && model)
94
+ out.modelUsed = model;
95
+ return out;
87
96
  }
88
97
  catch {
89
98
  return {};
90
99
  }
91
100
  }
101
+ // The checker files are restored from the source repo after the agent runs,
102
+ // so an agent cannot rewrite its own test. The suite marks them with
103
+ // verifyPaths; a top-level verify/ dir is the convention fallback.
104
+ function protectedPaths(root, suite) {
105
+ const rels = suite.verifyPaths ?? (existsSync(path.join(root, "verify")) ? ["verify"] : []);
106
+ return rels.filter((rel) => !path.isAbsolute(rel) &&
107
+ !rel.split(/[\\/]+/).includes("..") &&
108
+ !SKIP_DIRS.has(rel.split(/[\\/]+/)[0]) &&
109
+ existsSync(path.join(root, rel)));
110
+ }
92
111
  export async function runSuite(opts) {
93
112
  const { root, suite, agents, jobs, keep, onCell } = opts;
94
113
  const timeoutMs = (suite.timeoutSec ?? 600) * 1000;
@@ -131,6 +150,12 @@ export async function runSuite(opts) {
131
150
  out.error = agentRes.timedOut ? "timeout" : `spawn failed: ${agentRes.stderr.slice(-160)}`;
132
151
  }
133
152
  const verifyCmd = task.verify ?? suite.verify;
153
+ const restored = protectedPaths(root, suite);
154
+ for (const rel of restored) {
155
+ cpSync(path.join(root, rel), path.join(sandbox, rel), { recursive: true });
156
+ }
157
+ if (restored.length)
158
+ out.verifyRestored = restored;
134
159
  const v = await runCmd(["sh", "-c", verifyCmd], sandbox, timeoutMs);
135
160
  out.verifyExit = v.code;
136
161
  out.pass = out.agentExit === 0 && v.code === 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-trials",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Interview your coding agent before you ship with it. Run the same tasks through every agent CLI you have installed and get a scoreboard.",
5
5
  "type": "module",
6
6
  "bin": {