MazeBench: ice-maze puzzle site, world editor, and coding-agent benchmark runner. `mazebench launch` serves the website locally.
Project description
MazeBench
The MazeBench env can run in the browser, in the terminal, driven by a local coding agent (Codex CLI or Claude Code), and through Prime Intellect Verifiers v1. The Verifiers integration includes both normal multi-turn chat-model runs and Codex CLI runs.
Install from PyPI and launch
pip install mazebench # or: uv tool install mazebench
mazebench launch # serves the site and opens your browser
The wheel bundles the whole Node site/runtime; mazebench launch materializes
a writable workspace at ~/.mazebench/site (drafts, run outputs, and account
state live there) and serves it. Node.js must be installed; ffmpeg + a
Chromium-family browser are needed for replay videos, and the codex /
claude CLIs for agent runs. Options: mazebench launch port=3000 host=127.0.0.1 open=false. From a repo checkout, mazebench launch serves the
checkout instead (same as npm run dev).
Releases are published by .github/workflows/publish.yml (PyPI trusted
publishing) whenever a GitHub Release is created; see
docs/packaging.md.
The local site: Play, Build, and Agent modes
npm run dev (or mazebench launch) serves the full site at
http://localhost:3000 with three modes on the home page (the frontend design
matches mazebench.com — the chrome is ported from the MazeJam repo):
- Play Mode (
/play) — play the master world (the 256-room world agents are benchmarked on), any local draft world, or downloaded online worlds. - Build Mode (
/build) — make and save your own worlds locally, without publishing anywhere. Each draft world is a real game directory undergames/draft-*(gitignored), so the level editor (/author/<world>/<level>), world-map editor, play page, flyover, and the agent runner all work on it. You can also edit the master world itself (its levels live ingames/maze/), copy the master world into a draft, and import/export worlds as JSON in the samemazebench-build-world-v1format the hosted site uses. - Agent Mode (
/agent) — launch Codex CLI, Claude Code (your subscriptions, via the same runner asmazebench model=...), or Prime Verifiers runs from the browser, against the master world, any local draft, or a downloaded community world. Runs stream live: per-move actions, gems, the ASCII board, the agent's reasoning, the runner log, and the replay video when the run finishes. Run artifacts land underoutputs/maze-local/site/<run-id>/.
Sync drafts with mazebench.com
Build Mode can connect to your account on the hosted site
(https://dev.mazebench.com, soon mazebench.com): the local server holds a
session token in data/remote.json and talks to the site server-to-server —
push local drafts up, pull site drafts down, and download published community
worlds for agent runs. Connect either via the browser link flow
(/link-local on the hosted site) or by pasting your mazebench_session
cookie value manually. Drafts stay drafts — publishing remains a deliberate
action on the site.
The mazebench command
There is a small mazebench CLI that wraps every workflow. It is a launcher:
it shells out to the repo's Node scripts (game engine, local-agent runner,
replay/video) and, for the Prime path, to the prime/uv CLIs. Node.js is a
prerequisite for all runs.
Install it from a checkout (recommended — the CLI needs the repo's Node scripts at runtime):
# with uv (this repo already uses a uv-managed .venv)
uv pip install -e .
# or with plain pip
pip install -e .
# add the Prime Intellect Verifiers integration
uv pip install -e ".[prime]"
Local agent runs execute inside a container by default so the agent is isolated from your filesystem. Build the image once (needs Docker):
mazebench build # or: npm run maze:build-image / docker build -t mazebench-agent .
Then, without Prime, drive the maze with your own local agent and get a replay
video out the other end (credentials are read from OPENAI_API_KEY /
ANTHROPIC_API_KEY in your environment):
mazebench model=codex moves=10
mazebench model=claude moves=10 level=HxI
See Run with a local coding agent for details, and docs/packaging.md for publishing to PyPI.
Run with a local coding agent (no Prime)
This path uses your Codex or Claude Code auth. There is no Prime intercept
server and no reward scoring — the agent just plays the game by shelling out to
scripts/codex-play.js (a stateful CLI over scripts/maze-bridge.js), and the
runner then renders a replay video from the session.
Prefer not to remember the flags? Run the interactive setup and pick each option with the arrow keys:
mazebench wizard # or: npm run maze:wizard
The wizard's "Custom…" model choice lists your actual Codex models (read from
~/.codex/models_cache.json), then — for Codex — lets you pick the reasoning
effort (low/medium/high/xhigh) and toggle Fast mode. You can also set
these directly:
mazebench model=codex model_name=gpt-5.5 reasoning=xhigh codex_fast=true moves=10
mazebench model=claude model_name=opus moves=10
model_name is forwarded to codex -m / claude --model; reasoning maps to
codex -c model_reasoning_effort and codex_fast=true to the priority
service tier.
One-liners:
# via the mazebench CLI
mazebench model=codex moves=10
mazebench model=claude moves=10
# equivalently, via npm
npm run maze:codex-local -- moves=10
npm run maze:claude-local -- moves=10
Common options (all accept key=value or --key value):
| key | meaning | default |
|---|---|---|
model |
codex or claude |
required |
container |
true (run in Docker, host FS isolated) or false (host) |
true |
tools |
false (sandboxed to the maze) or true (full access) |
false |
mode |
text (ASCII board) or vision (rendered PNGs) |
text |
moves |
maze action budget shown to the agent | 20 |
game |
game dir under games/, e.g. a Build Mode draft world id |
maze |
level |
world level id, e.g. HxI or level_HxI |
level_HxI |
view |
top … side camera pitch |
top-diagonal |
yaw |
0–3 camera yaw |
0 |
gems |
unique gems required for game_won |
100 |
vision_width, vision_height |
PNG size in vision mode | 512 |
model_name |
underlying LLM id (codex -m / claude --model) |
agent default |
reasoning |
Codex reasoning effort: low / medium / high / xhigh |
model default |
codex_fast |
Codex Fast mode (priority tier, ~1.5× speed) | false |
video |
on / off |
on |
fast, draft |
faster/cheaper video capture | off |
dry_run |
print the agent command + prompt and exit | off |
Use dry_run=on to preview exactly what will run (the container/agent command
and the full play prompt) without spending any tokens or needing Docker.
Isolation: runs happen in a container by default
Every local agent run executes inside a container (container=true, the
default), so the agent cannot touch your filesystem — only an output
directory is mounted, and API credentials are passed via environment variables.
This is the strong, OS-level guarantee; it does not depend on the agent CLI
behaving.
- One-time build:
mazebench build(ornpm run maze:build-image). The image bundles Node, the maze runtime, a headless Chromium + ffmpeg (for vision and video), and thecodex/claudeCLIs. See the Dockerfile. - Credentials (forwarded into the container as env, or mounted):
- Codex:
model=codexauto-mounts your Codex subscription login (~/.codex/auth.json, read-only) when it exists — no setup needed. Otherwise setOPENAI_API_KEY, or passcodex_auth=<path>. - Claude: on macOS,
model=claudeauto-detects your Claude Code subscription login from the Keychain and mounts just that credential (a short-lived, read-only temp file, deleted after the run) — no setup needed. Otherwise setANTHROPIC_API_KEYorCLAUDE_CODE_OAUTH_TOKEN, or passclaude_auth=<path>to a.credentials.json. - Only the single credential is exposed — never your whole
~/.codexor~/.claude(which hold history, memories and logs).
- Codex:
- What's mounted: only
outputs/maze-local(writable, for artifacts). Nothing else on your disk is visible to the container. The network stays on because the agent needs to reach the model API. - Escape hatch:
container=falseruns on the host instead, protected only by the per-CLItoolssandbox below (weaker — see the Codex read caveat). docker_bin=podmanandimage=<tag>override the runtime and image tag.
Sandboxing (tools)
Inside the container (or on the host with container=false), tools is a
second layer. With tools=false (default) the agent is meant to only drive the
maze helper; how strictly that is enforced depends on the agent (below).
tools=true removes the guardrails (write files, run any command, use the
network) — inside the container that is still safe for your host.
- Codex — On the host (
container=false,tools=false), Codex runs undercodex exec --sandbox workspace-write: writes confined to the run folder, network disabled by default. Caveat: Codex's OS sandbox does not restrict reads, so a determined agent could still read files it knows the path to. Inside the container, Codex's sandbox (bubblewrap) can't create user namespaces under Docker, so Codex runs with--dangerously-bypass-approvals-and-sandbox— the container itself is the sandbox. So for Codex-in-container,tools=falseis a prompt-level instruction ("only play the maze"), not an OS guarantee; host isolation comes from the container.tools=truealways uses--dangerously-bypass-approvals-and-sandbox. - Claude Code (
tools=false): runs under--permission-mode dontAskwith an allowlist of only the maze helper command (Bash(node <helper> *)), so every other tool — Read, Write, WebFetch, other Bash — is auto-denied. Claude also blocks command chaining per-subcommand, so the allowlist can't be widened with; other-cmd. This is a true "maze-only" lockdown at the agent level.tools=trueuses--permission-mode bypassPermissions.
Notes:
- In vision mode with
tools=false, Claude is additionally allowed to read the rendered frames (Read(<run>/frames/**)) so it can see the maze. Under Codex's sandbox the headless-browser render may be blocked; if a frame fails, the helper falls back to the ASCII board for that turn (frame_error). - These are the best guardrails each CLI offers; neither replaces running untrusted agents in a real VM/container if that is your threat model.
Text vs vision observations
By default the agent plays from the ASCII board (mode=text). In mode=vision
the helper renders a perspective PNG of the current room each turn (via the same
scripts/maze-render-frame.js renderer the Verifiers vision taskset uses),
drops the ASCII board, and prints a frame_image path in the JSON. The agent —
Codex CLI and Claude Code are both multimodal — opens that PNG to decide its
move. Frames are saved under <run>/frames/.
mazebench model=codex moves=10 mode=vision
mazebench model=claude moves=10 mode=vision vision_width=768 vision_height=768
npm run maze:codex-local -- moves=10 mode=vision
Vision mode boots a headless browser to render each frame, so it is noticeably
slower than text mode and needs a Chromium-family browser (the same dependency
as the replay video). If a frame fails to render, the helper falls back to the
ASCII board for that turn and reports frame_error.
Each run writes a timestamped directory under outputs/maze-local/<model>/:
session.json— full state and per-action replayactions.jsonl— per-turn action logscorecard.json,maze_scorecard.json,maze_actions.txtmaze_replay.mp4— the replay video (unlessvideo=off)reasoning.json,agent.log,agent-events.jsonl— the agent's reasoning (see below)
Reasoning logs
For both model=codex and model=claude, the runner captures the agent's
structured event stream (codex exec --json / claude -p --output-format stream-json) and distills a per-move reasoning log. reasoning.json is exactly
what you'd want to skim:
[
{ "move": 1, "action": "right", "reasoning": "The exit is east, so...",
"moved": true, "gems": 0, "room": "level_HxI" }
]
The reasoning for each move is the agent's commentary (Codex agent_message /
Claude text blocks, plus Claude thinking blocks if extended thinking is on)
since its previous action. agent.log is the same trace in human-readable form,
agent-events.jsonl is the raw event stream, and the runner prints a per-move
summary to the terminal at the end of the run.
Prerequisites: Node.js, plus ffmpeg and a Chromium-family browser for the
video (already used by npm run maze:replay). The agent binary must be on your
PATH — codex for model=codex, claude for model=claude — or pass
codex_bin=/claude_bin= with an explicit path.
To (re)build a video from any finished run or Prime eval directory:
mazebench replay outputs/maze-local/codex/<run-dir>/
# or
npm run maze:replay -- outputs/maze-local/codex/<run-dir>/
1. Set up the web app with Node
Install dependencies, run tests, then start the local server:
npm install
npm test
npm run dev
Open the app at:
http://localhost:3000
2. Run the game in the terminal, see the scorecard, and save video
Start an interactive terminal game:
npm run maze:terminal
Controls:
- Arrow keys: move
i/k: rotate camera up / downj/l: rotate camera left / rightzoru: undor: resetq: quit and print the scorecard
Interactive runs write replay files under outputs/maze-terminal/<timestamp>/.
When the run ends, answer the video prompt to save maze_replay.mp4.
For a non-interactive run that saves the scorecard and video:
npm run maze:terminal -- --moves UDLR --once --record-replay --video --fast --draft --fps 20 --width 400 --height 400
The output folder contains:
maze_scorecard.jsonmaze_actions.txtmaze_replay.jsonresults.jsonlmaze_replay.mp4when video is enabled
3. Run with Prime Intellect Verifiers and see results
The mazebench prime subcommands wrap the steps below (they print the exact
underlying command before running it):
mazebench prime install # prime env install mazebench
mazebench prime eval model=openai/gpt-5-nano n=1 r=1 # normal chat-model eval
mazebench prime codex model=openai/gpt-5-codex # Codex CLI via Verifiers (section 4)
mazebench prime vision model=openai/gpt-4.1-mini # vision observations (section 5)
Install uv if you do not already have it:
curl -LsSf https://astral.sh/uv/install.sh | sh
On macOS, Homebrew also works:
brew install uv
Install the local environment from ./environments/mazebench:
prime env install mazebench
Run a small eval:
prime eval run mazebench -m openai/gpt-5-nano -n 1 -r 1 -s --max-turns 8 -d
Use your own configured model after -m, or omit -m to use your Prime
default. The terminal prints the run summary. MazeBench v1 stores replay
artifacts under info.maze_actions, info.maze_scorecard, and
info.maze_replay in results.jsonl.
View saved evals:
prime eval view
Export scorecard files and a replay video from a saved eval directory:
npm run maze:replay -- environments/mazebench/outputs/evals/<model>/<run-id>
4. Run Codex through Verifiers v1
MazeBench includes a mazebench_codex v1 plugin. It uses your local codex
CLI as the harness, routes Codex model calls through the Verifiers v1
interception server, and writes replay artifacts under outputs/maze-codex-v1/.
cd environments/mazebench
uv run eval mazebench_codex \
-m openai/gpt-5-codex \
-n 1 -r 1 \
--taskset.max-actions 100 \
--max-turns 40 \
--rich false
--taskset.max-actions is the maze action budget. --max-turns is still useful
because Verifiers counts Codex's internal model/tool-call turns while the CLI is
working. The harness strips the openai/ prefix before invoking codex exec so
Codex can use its local model metadata, while Verifiers still routes/bills the
Prime model.
Export the saved v1 trace to scorecard files and video the same way as normal MazeBench evals:
npm run maze:replay -- environments/mazebench/outputs/evals/<model>/<run-id>
5. Run vision observations
The normal mazebench taskset can send perspective PNG observations instead of
ASCII boards:
cd environments/mazebench
uv run eval mazebench \
-m openai/gpt-4.1-mini \
-n 1 -r 1 \
--taskset.observation-mode vision \
--taskset.vision-width 512 \
--taskset.vision-height 512 \
--max-turns 8 \
--rich false
Vision mode keeps the same JS game state, allowed commands, terminal conditions, and reward functions as ASCII mode. It sends a short text status plus a perspective image of the current room, with no ASCII board.
License
MIT. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mazebench-0.2.0.tar.gz.
File metadata
- Download URL: mazebench-0.2.0.tar.gz
- Upload date:
- Size: 9.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d196dc88f8ac543fbf3f1b958fe62adf650948a27cbaffe82873dad725dec5d
|
|
| MD5 |
28a79fcb52c47b5c1ee9125756606f16
|
|
| BLAKE2b-256 |
20f5f93ddc340f55acc9d7869ed7364317edf169a675140317b925c875b7f112
|
Provenance
The following attestation bundles were made for mazebench-0.2.0.tar.gz:
Publisher:
publish.yml on mazebench/MazeBench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mazebench-0.2.0.tar.gz -
Subject digest:
7d196dc88f8ac543fbf3f1b958fe62adf650948a27cbaffe82873dad725dec5d - Sigstore transparency entry: 2091287512
- Sigstore integration time:
-
Permalink:
mazebench/MazeBench@a7ff42c0f426b6173d35f8e59a72a69d559075b3 -
Branch / Tag:
refs/tags/v0.2.0-alpha - Owner: https://github.com/mazebench
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a7ff42c0f426b6173d35f8e59a72a69d559075b3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mazebench-0.2.0-py3-none-any.whl.
File metadata
- Download URL: mazebench-0.2.0-py3-none-any.whl
- Upload date:
- Size: 9.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81404dd0b0d954008435d7f7be3a9c93773e0e97f6c09e7dcf667e5726214a3c
|
|
| MD5 |
5faf545c142432184ee24fe71e243ab9
|
|
| BLAKE2b-256 |
8e042f4258f1d25b43c59d9674d085fa79a10138826ea6f04ce983903a576cc9
|
Provenance
The following attestation bundles were made for mazebench-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on mazebench/MazeBench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mazebench-0.2.0-py3-none-any.whl -
Subject digest:
81404dd0b0d954008435d7f7be3a9c93773e0e97f6c09e7dcf667e5726214a3c - Sigstore transparency entry: 2091287576
- Sigstore integration time:
-
Permalink:
mazebench/MazeBench@a7ff42c0f426b6173d35f8e59a72a69d559075b3 -
Branch / Tag:
refs/tags/v0.2.0-alpha - Owner: https://github.com/mazebench
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a7ff42c0f426b6173d35f8e59a72a69d559075b3 -
Trigger Event:
release
-
Statement type: