From 0e63ed1feb569aaf826c788bc227ab4827e784f4 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sun, 9 Aug 2026 16:10:03 -0700 Subject: [PATCH] feat(tools): stat-based special-file guard for read_file + readtool eval harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit read_file on a workspace FIFO/socket blocked until the exec timeout — the existing device guard is name-based (/dev/*, /proc/*) and cannot see an arbitrary special file. Add _special_file_kind(): one os.stat on the resolved path, refusing FIFO/socket/char/block devices with a plain note ('no read was attempted') instead of hanging. Host-visible filesystems only; regular files, dirs, and missing paths unchanged. Also adds evals/readtool/: an A/B harness that runs the real AIAgent against hostile-file fixtures (huge lockfile, one-line bundle, FIFO, NFD filenames, lying extensions) and measures accuracy, turns, tool calls, and tokens. Measured for this guard (3 reps, file-only arm): qwen3.8-max fifo task tokens 122k -> 26k (-79%), turns 9.3 -> 5.0; opus-4.8 tokens 40k -> 23k; accuracy held 1.00 both arms. --- evals/readtool/README.md | 69 ++++++ evals/readtool/fixtures.py | 225 ++++++++++++++++++++ evals/readtool/report.py | 118 ++++++++++ evals/readtool/results/SUMMARY.md | 35 +++ evals/readtool/runner.py | 206 ++++++++++++++++++ evals/readtool/tasks.py | 178 ++++++++++++++++ tests/tools/test_read_special_file_guard.py | 76 +++++++ tools/file_tools.py | 49 +++++ 8 files changed, 956 insertions(+) create mode 100644 evals/readtool/README.md create mode 100644 evals/readtool/fixtures.py create mode 100644 evals/readtool/report.py create mode 100644 evals/readtool/results/SUMMARY.md create mode 100644 evals/readtool/runner.py create mode 100644 evals/readtool/tasks.py create mode 100644 tests/tools/test_read_special_file_guard.py diff --git a/evals/readtool/README.md b/evals/readtool/README.md new file mode 100644 index 0000000000000..fe7fd6d74a714 --- /dev/null +++ b/evals/readtool/README.md @@ -0,0 +1,69 @@ +# Read-Tool Eval + +A/B harness measuring how `read_file` engineering choices affect real agent +runs. Motivated by Command Code's read-tool writeup (Aug 2026), which +benchmarked ten harnesses on hostile-file handling — and whose Hermes column +contained several errors (we already ship a per-line clamp, did-you-mean +suggestions, notebook/docx/xlsx extraction, PDF conversion, and a device-path +blocklist). This eval tests the failure shapes for real, through the real +`AIAgent`, instead of trusting anyone's capability table. + +## What it measures + +Every task runs the full Hermes agent (file + terminal + search toolsets) +against a deterministic hostile workspace: + +| fixture | shape | tasks | +|---|---|---| +| `package-lock.json` | 80K lines, 2.7MB — token tarpit | `lockfile_version` | +| `src/app.min.js` | one 600KB line matching greps | `minified_backoff` | +| `logs/server.log` | 150K lines, one ERROR near tail | `log_error_hunt` | +| `data/report.txt` | 412 lines — past-EOF probe | `past_eof` | +| `config/overrides.yaml` | empty file | `empty_config` | +| `notes/Meeting…PM.txt` | NFD + U+202F + U+2019 filename | `unicode_filename` | +| `AGENTS.md` vs `AGENT.md` | near-miss filename | `near_miss_filename` | +| `logs/live.pipe` | FIFO — blocks naive reads | `fifo_hang` | +| `data/data.txt` | PNG bytes behind a .txt name | `lying_extension` | + +Metrics per task: **accuracy** (substring/regex graders against planted +ground truth), **api_turns**, **tool_calls**, **read_file_calls**, +**total_tokens**, **wall_s**. Efficiency aggregates are per-task means, +never sums. + +## Running + +```bash +# Baseline (3 reps, both models) +python3 evals/readtool/runner.py --model anthropic/claude-opus-4.8 \ + --provider openrouter --reps 3 --label baseline +python3 evals/readtool/runner.py --model qwen/qwen3.8-max \ + --provider openrouter --reps 3 --label baseline + +# After a feature change, re-run with a new label: +python3 evals/readtool/runner.py --model qwen/qwen3.8-max \ + --provider openrouter --reps 3 --label feat-stat-guard + +# Compare +python3 evals/readtool/report.py --labels baseline feat-stat-guard +``` + +Rules of engagement (from hermesbench discipline): + +- **3 reps minimum**; single-run deltas within ±3% are noise, not wins. +- Never edit `tools/` while a run is in flight — the runner imports the + live tree. +- Two models on purpose: a frontier model (opus) that can absorb sloppy + reads, and a strong open model (qwen-max) where harness quality shows. + A feature that only helps qwen still counts — that's the population the + hardening serves. +- Errored task-runs score 0 and stay in the accuracy denominator but are + excluded from efficiency means. + +## Results layout + +``` +results/