The generic sandboxing machinery in src/robocode/utils/ was copied into a new package, prpl-agent-utils, and trimmed down to the parts that are not specific to this project. Robocode should depend on that package instead of keeping its own copy, so fixes land in one place.
Overlapping code:
utils/claude_auth.py — copied nearly verbatim (env var names change from ROBOCODE_* to PRPL_AGENT_*).
utils/sandbox.py — sandbox directory setup, git init/commit helpers, the write-validation hook, the clean subprocess environment.
utils/docker_sandbox.py — auth argument construction and the docker run prefix.
utils/backends/claude.py — CLI argument construction and stream-json parsing.
docker/ — Dockerfile, entrypoint, firewall script.
What robocode keeps: MCP render tools, primitives and their manifests, the blackbox and models-off mounts, the env server and remote proxies, the OpenCode backend, Apptainer, and the retry/budget accounting in GenerationMetrics.
The likely shape is that prpl-agent-utils grows a small number of extension points (extra bind mounts, extra CLI arguments, extra firewall domains) and robocode's SandboxConfig becomes a subclass. Robocode's stream parser tracks considerably more than the generic one (rate limits, autocompaction, turn limits, per-model usage), so either the generic parser grows optional fields or robocode keeps its own parser and reuses everything else.
Two fixes made during the port are worth taking regardless of whether this refactor happens:
- On macOS, Claude credentials live in the Keychain rather than
~/.claude/.credentials.json, so the local sandbox path must resolve the token through _get_claude_oauth_token() and not only the file-copy flow. Robocode's local mode currently fails on macOS without CLAUDE_CODE_OAUTH_TOKEN set.
docker/build.sh should not pass USER_UID/USER_GID on macOS: host GID 20 collides with an existing group in the image, and Docker Desktop maps bind-mount ownership through VirtioFS anyway.
The generic sandboxing machinery in
src/robocode/utils/was copied into a new package,prpl-agent-utils, and trimmed down to the parts that are not specific to this project. Robocode should depend on that package instead of keeping its own copy, so fixes land in one place.Overlapping code:
utils/claude_auth.py— copied nearly verbatim (env var names change fromROBOCODE_*toPRPL_AGENT_*).utils/sandbox.py— sandbox directory setup, git init/commit helpers, the write-validation hook, the clean subprocess environment.utils/docker_sandbox.py— auth argument construction and thedocker runprefix.utils/backends/claude.py— CLI argument construction andstream-jsonparsing.docker/— Dockerfile, entrypoint, firewall script.What robocode keeps: MCP render tools, primitives and their manifests, the blackbox and models-off mounts, the env server and remote proxies, the OpenCode backend, Apptainer, and the retry/budget accounting in
GenerationMetrics.The likely shape is that
prpl-agent-utilsgrows a small number of extension points (extra bind mounts, extra CLI arguments, extra firewall domains) and robocode'sSandboxConfigbecomes a subclass. Robocode's stream parser tracks considerably more than the generic one (rate limits, autocompaction, turn limits, per-model usage), so either the generic parser grows optional fields or robocode keeps its own parser and reuses everything else.Two fixes made during the port are worth taking regardless of whether this refactor happens:
~/.claude/.credentials.json, so the local sandbox path must resolve the token through_get_claude_oauth_token()and not only the file-copy flow. Robocode's local mode currently fails on macOS withoutCLAUDE_CODE_OAUTH_TOKENset.docker/build.shshould not passUSER_UID/USER_GIDon macOS: host GID 20 collides with an existing group in the image, and Docker Desktop maps bind-mount ownership through VirtioFS anyway.