Skip to content

Add pi_env: Pi coding-agent environment (HF sandbox + transparent-proxy logprobs)#999

Open
sergiopaniego wants to merge 17 commits into
huggingface:mainfrom
sergiopaniego:pi-hf-sandbox-backend
Open

Add pi_env: Pi coding-agent environment (HF sandbox + transparent-proxy logprobs)#999
sergiopaniego wants to merge 17 commits into
huggingface:mainfrom
sergiopaniego:pi-hf-sandbox-backend

Conversation

@sergiopaniego

@sergiopaniego sergiopaniego commented Jul 22, 2026

Copy link
Copy Markdown
Member

Add pi_env: Pi coding-agent environment (HF sandbox + transparent-proxy logprobs)

What this adds

A new environment, pi_env, that runs the Pi coding agent as an OpenEnv environment. It mirrors opencode_env one-to-one and ships the same two layers:

  1. Harness primitivePiSessionFactory / PiSession / PiConfig / PiTask. Drives one Pi rollout inside a sandbox in-process (installs the pi CLI, writes models.json pointing at any OpenAI-compatible endpoint, runs pi --print --mode json headless). This is what a loop-owning trainer (e.g. TRL AsyncGRPOTrainer + HarnessRolloutWorker) calls directly.
  2. Deployable HTTP envPiEnv (MCP client) + FastAPI server (PiEnvironment with a single run_rollout tool, task catalog, Gradio UI at /web) + Dockerfile, so pi_env can also be deployed as an HF Space and driven over HTTP, like every other env in the repo.

In transparent_proxy mode an in-sandbox proxy fronts the LLM endpoint, injects logprobs=true, and captures per-turn (completion_token_ids, per_token_logps) to proxy_trace.jsonl for GRPO.

Pi uses its builtin tools (read/bash/edit/write/…), so it is a self-contained coding agent — no external tool bridge. The default sandbox backend is the Hugging Face sandbox (HFSandboxBackend, huggingface_hub.Sandbox); the agent cold-installs Node + Pi per rollout on a python:3.12 image.

Relationship to opencode_env / #998 (stacked)

pi_env is deliberately a thin sibling of opencode_env: it imports the sandbox backend and the interception proxy from opencode_env rather than duplicating them (from opencode_env.sandbox import HFSandboxBackend, …, and the proxy source is uploaded from the opencode_env package). Concretely it reuses HFSandboxBackend + interception.py, both introduced in #998.

Validation

The harness primitive is validated end-to-end; the HTTP layer is validated at the wiring level.

Level Result
Unit tests (tests/envs/test_pi_runtime.py, test_pi_factory_lifecycle.py) 11 passing
Mode-B integration (Pi → real interception.py → fake OpenAI server, streaming) token-faithful trace captured
cpu-basic smoke in a real HF sandbox (Node bootstrap + npm install + black-box run + Mode-B proxy trace) green
HTTP server smoke (boot app, /health, /schema, /reset) green (no sandbox created)
Loop-owning GRPO training on HF Jobs (Qwen3-4B, MBPP, h200x2, remote HF sandboxes via a tunnel) 2/2 steps, trainer exit=0, TRAIN_LOOP_DONE

Training metrics (real gradient steps, non-zero reward — Pi actually solved MBPP in the remote sandbox):

step 1:  loss 0.05247   grad_norm 0.71   reward 0.64    train_seq_len 1957
step 2:  loss 0.2795    grad_norm 0.81   reward 0.4348  train_seq_len 4011
train_runtime 1229s   train_loss 0.166

Full self-contained training repro (vLLM + NCCL + cloudflared tunnel + PiSessionFactory):
https://gist.github.com/sergiopaniego/ed71514ff1ae53cf522b23c0b890d939

TRL note (not part of this PR)

The loop-owning training path is agent-agnostic and needed no reconciler changes for Pi. One latent TRL bug surfaced: print_prompt_completions_sample's format_entry assumes str message content, but Pi emits structured content ([{"type":"text",…}]), tripping a rich TypeError. Worked around in the repro (disable log_completions + a one-line coerce patch); to be fixed upstream in TRL. The known empty-choices guard (huggingface/trl#6420) is also patched in the repro.

Caveats

  • Docker server image: server/Dockerfile installs openenv-opencode-env for the shared sandbox; it resolves once Add HFSandboxBackend for the OpenCode harness #998 is on main. A local openenv build on this branch needs the ref repointed to the branch carrying HFSandboxBackend.
  • Sandbox teardown: with the loop-owning trainer, a few buffered-generation sandboxes can outlive a run and need hf jobs cancel (TRL async-worker teardown behavior, not pi_env).
  • No pre-baked sandbox image yet (cold-install is validated). A pre-baked Node+Pi+proxy image is a follow-up optimization, mirroring opencode's sandbox/hf_image.

Docs

Adds docs/source/environments/pi.md (via sync_env_docs.py --fix), a catalog card in docs/source/environments.md, and a _toctree.yml nav entry. check-env-docs passes.

Testing

cd envs/pi_env && python -m pytest ../../tests/envs/test_pi_runtime.py ../../tests/envs/test_pi_factory_lifecycle.py -q

AI-assisted PR.


Note

Medium Risk
Large new surface (remote sandboxes, LLM API keys, arbitrary setup/verify bash) and a hard dependency on opencode_env for production Docker builds until #998 lands on main.

Overview
Introduces pi_env, a new OpenEnv environment that runs the Pi coding agent in a Hugging Face sandbox (default) against any OpenAI-compatible endpoint, with optional transparent-proxy per-token logprobs for GRPO—parallel to opencode_env but Pi + HF instead of OpenCode + E2B.

The package adds an in-process harness (PiSessionFactory / PiSession, models.json + headless pi, shared opencode_env.sandbox + interception.py) and a deployable HTTP/MCP stack: PiEnvironment with run_rollout, PiEnv client, endpoint catalog (vllm / openai / hf_router), Gradio UI at /web, server and pre-baked sandbox Dockerfiles, plus CI matrix entries pi-env and pi-sandbox. Tasks use (instruction, setup, verify) bash commands; reward is verify pass rate unless /root/logs/verifier/reward.txt overrides.

Docs gain environments/pi.md, a catalog card, and toctree entry. Depends on opencode_env for HFSandboxBackend and the proxy until shared code moves to openenv.core.

Reviewed by Cursor Bugbot for commit 75d354e. Bugbot is set up for automated code reviews on this repo. Configure here.

…roxy

PiSessionFactory/PiSession/PiConfig mirror the OpenCode primitive: install the
pi CLI (Node bootstrap + npm), write models.json pointing at an OpenAI-compatible
endpoint, run pi --print --mode json headless, and (transparent_proxy mode)
capture per-token logprobs via the shared interception proxy for GRPO.

The sandbox backend (HFSandboxBackend) and interception proxy are imported from
opencode_env for now; the plan is to consolidate both into openenv.core in a
follow-up. Includes builder + factory-lifecycle unit tests.
Mirrors opencode_env's second layer so pi_env is both an in-process harness
primitive and a deployable OpenEnv environment: PiEnv MCP client + FastAPI
server (PiEnvironment with the run_rollout tool, task catalog, Gradio UI) +
Dockerfile + openenv.yaml. HF sandbox (image, HF_TOKEN) replaces E2B
(template, E2B_API_KEY); in-sandbox paths are /root; the sandbox backend and
interception proxy are imported from opencode_env. Adds uv.lock and the
pi-env server-image entry to docker-build.yml.
sync_env_docs.py --fix generates the docs/source/environments/pi.md stub
from the README; the HTML catalog card in environments.md and the
_toctree.yml nav entry are added manually (the script does not manage
those). check-env-docs now passes.
Copilot AI review requested due to automatic review settings July 22, 2026 13:53
Comment thread envs/pi_env/server/pi_environment.py
Comment thread envs/pi_env/pi_runtime.py Outdated
Comment thread envs/pi_env/pi_runtime.py
@bot-ci-comment

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Comment thread envs/pi_env/pi_runtime.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new pi_env environment (patterned after opencode_env) to run the Pi coding agent inside a Hugging Face sandbox, with an optional transparent-proxy mode that captures token-level logprobs for GRPO-style training. The PR also extends opencode_env with a Hugging Face sandbox backend and updates related runtime/path handling, plus docs + CI docker builds.

Changes:

  • Introduces envs/pi_env (harness primitive + FastAPI/MCP server + Gradio UI + Docker build).
  • Adds HFSandboxBackend to opencode_env and updates proxy/opencode path helpers to respect sandbox_home.
  • Adds/updates tests, docs navigation, and docker-build workflow entries.

Reviewed changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/envs/test_pi_runtime.py Unit tests for pi_env runtime builders (commands/paths/models.json).
tests/envs/test_pi_factory_lifecycle.py Ensures PiSessionFactory.create() tears down sandboxes on post-provision failure.
tests/envs/test_opencode_sandbox_home.py Regression tests for sandbox_home-derived path helpers in opencode_env.
tests/envs/test_opencode_hf_sandbox.py Unit tests for HF sandbox background-job polling semantics.
tests/envs/test_opencode_factory_lifecycle.py Ensures OpenCodeSessionFactory.create() tears down sandboxes on post-provision failure.
envs/pi_env/task.py Adds PiTask payload model and coercion helper.
envs/pi_env/server/pi_environment.py Implements MCP environment with run_rollout tool orchestrating a full rollout.
envs/pi_env/server/gradio_ui.py Adds /web Gradio UI for running rollouts and inspecting results/logprobs.
envs/pi_env/server/Dockerfile Multi-stage build for deployable pi_env server image; installs opencode_env for shared sandbox/proxy.
envs/pi_env/server/catalog.py Endpoint shorthand resolution (vllm/openai/hf_router) via env vars + defaults.
envs/pi_env/server/app.py FastAPI app wiring + Gradio mount under /web.
envs/pi_env/server/init.py Declares the server package.
envs/pi_env/README.md End-user documentation for harness + server usage.
envs/pi_env/pyproject.toml Defines openenv-pi-env package deps and server entrypoint.
envs/pi_env/pi_runtime.py Pure builders for Pi install/run commands and sandbox path helpers.
envs/pi_env/openenv.yaml OpenEnv spec for deploying pi_env as a Space.
envs/pi_env/models.py Pydantic models for server results/state (RolloutResult, RolloutTurn, etc.).
envs/pi_env/harness.py PiSessionFactory/PiSession harness implementation, incl. transparent proxy startup.
envs/pi_env/config.py PiConfig configuration model for harness runs.
envs/pi_env/client.py Typed PiEnv MCP client wrapper for run_rollout.
envs/pi_env/.gitignore Env-local ignores for dev artifacts.
envs/pi_env/.dockerignore Build-context exclusions for server image.
envs/pi_env/init.py Public re-exports for client/harness/models + shared sandbox backend.
envs/opencode_env/uv.lock Lockfile updates for dependency set (incl. newer huggingface_hub).
envs/opencode_env/sandbox/hf.py Adds HF sandbox backend implementation (SDK Sandbox + polling wait()).
envs/opencode_env/sandbox/hf_image/Dockerfile Adds a pre-baked image for faster HF-sandbox rollouts.
envs/opencode_env/sandbox/init.py Exposes HFSandboxBackend (with stubs when hub version is too old).
envs/opencode_env/README.md Documents switching OpenCode harness from E2B to HF sandboxes.
envs/opencode_env/pyproject.toml Adds huggingface_hub>=1.22 dependency.
envs/opencode_env/opencode_runtime.py Adds sandbox_home-aware helpers for proxy/opencode paths.
envs/opencode_env/harness.py Uses new path helpers; ensures sandbox teardown on post-provision failures.
docs/source/environments/pi.md Adds docs page for pi_env.
docs/source/environments/opencode.md Adds HF sandbox backend docs for opencode_env.
docs/source/environments.md Adds Pi environment card to catalog page.
docs/source/_toctree.yml Adds pi_env docs page to navigation.
.github/workflows/docker-build.yml Adds docker-build entries for opencode-sandbox and pi-env.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +29 to +37
# Pi harness primitive — sandbox + proxy + agent driver
"httpx>=0.27.0",
# >=1.22 ships huggingface_hub.Sandbox (HFSandboxBackend); core only floors 0.20.
"huggingface_hub>=1.22",
# Shared sandbox backend + interception proxy live in opencode_env for now
# (plan: consolidate into openenv.core). Kept a peer dep — install from the
# same repo (the server Dockerfile installs it explicitly):
# pip install "openenv-opencode-env @ git+https://github.com/huggingface/OpenEnv.git#subdirectory=envs/opencode_env"
]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional for now: pi_env reuses the sandbox backend + interception proxy from opencode_env instead of duplicating them (this PR is stacked on #998). The plan is to consolidate both into openenv.core (tracked with #940 / the harness RFC), at which point the peer dependency goes away. Documented in the README and pyproject.toml.

Comment thread envs/pi_env/client.py Outdated
Comment on lines +82 to +84
setup: Bash commands run sequentially **before** the agent starts.
Each command runs in the sandbox; non-zero exit aborts setup.
verify: Bash commands run sequentially **after** the agent exits.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs were reworded in 7798e3ce: setup runs "at rollout start", not strictly before the agent. It races the agent for ~1-2s, which is fine in practice because Pi takes >=20s to make its first model call. Kept consistent with opencode_env; a strict before/after ordering is a shared follow-up for both envs.

Comment thread envs/pi_env/.dockerignore Outdated
Comment on lines +11 to +12
# NEVER ship secrets into the image. The container reads E2B_API_KEY etc.
# from runtime env vars (HF Space secrets, ``docker run -e``).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 271f94a6 — the comment now references HF_TOKEN.

Comment on lines +122 to +126
class HFSandboxBackend:
"""Creates Hugging Face sandboxes for OpenCode rollouts.

``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
"""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the opencode_env backend (part of #998). The docstring was slightly off — cold-install (image="python:3.12") is supported; the pre-baked image is only an optimization. Tightened in #998 (968485db).

Copilot AI review requested due to automatic review settings July 22, 2026 14:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 37 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

envs/pi_env/pyproject.toml:37

  • pyproject.toml describes opencode_env as a peer dependency, but pi_env imports it at module import time (e.g. pi_env/__init__.py and pi_env/harness.py). As a result, pip install openenv-pi-env will fail at import-time unless openenv-opencode-env is also installed. Either declare the dependency explicitly (hard dep or extra) or make these imports optional and raise a clear error only when sandbox/proxy features are used.
    # Shared sandbox backend + interception proxy live in opencode_env for now
    # (plan: consolidate into openenv.core). Kept a peer dep — install from the
    # same repo (the server Dockerfile installs it explicitly):
    #   pip install "openenv-opencode-env @ git+https://github.com/huggingface/OpenEnv.git#subdirectory=envs/opencode_env"
]

envs/opencode_env/sandbox/hf.py:126

  • The HFSandboxBackend docstring says the image "must ship the opencode CLI, node, and the in-sandbox proxy", but elsewhere (and in this PR) the harness supports cold-installing these dependencies on a plain python:3.12 image. This docstring is misleading and may cause unnecessary coupling to pre-baked images.
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

Comment thread envs/pi_env/pi_runtime.py Outdated
f"mkdir -p {home}/.pi/agent {home}/logs/agent {home}/logs/verifier {home}/task {home}/workdir && "
# Bootstrap Node 22 when the image ships none new enough (pi needs >=22.19).
'if ! command -v node >/dev/null 2>&1 || '
'! node -e "process.exit(+process.versions.node.split(\'.\')[0]>=22?0:1)"; then '

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 271f94a6 — the guard now checks major+minor (>=22.19), not just the major version.

Comment on lines +333 to +339
# Run setup commands one at a time, *before* the agent starts.
# The factory has already started the agent in start_agent()
# during create(); to keep the order "setup → agent → verify"
# we'd need to restructure. As a pragmatic compromise we run
# setup IMMEDIATELY after create(), which races with the agent
# for ~1-2s but is fine for typical pip/git/download work
# because pi itself takes >=20s to make its first model call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs were reworded in 7798e3ce: setup runs "at rollout start", not strictly before the agent. It races the agent for ~1-2s, which is fine in practice because Pi takes >=20s to make its first model call. Kept consistent with opencode_env; a strict before/after ordering is a shared follow-up for both envs.

Comment thread envs/pi_env/server/Dockerfile Outdated
Comment on lines +50 to +58
# The shared sandbox backend + interception proxy live in opencode_env (kept a
# peer dep in pyproject, not a hard dep, so local dev / training installs stay
# lean). Install it explicitly into the venv so the server can import
# ``opencode_env.sandbox``. TEMPORARY: repoint to the branch carrying
# HFSandboxBackend until it lands in main; removed once the shared sandbox
# consolidates into openenv.core.
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --python .venv/bin/python \
"openenv-opencode-env @ git+https://github.com/huggingface/OpenEnv.git#subdirectory=envs/opencode_env"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Known and temporary: it points at main, which resolves once #998 merges. It disappears when the shared sandbox moves to openenv.core. Noted in the PR description.

Comment thread envs/pi_env/server/pi_environment.py Outdated
Comment on lines +83 to +88
from pi_env import (
HFSandboxBackend,
PiConfig,
PiSessionFactory,
PiTask,
)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7798e3ce — the server now imports the primitive from its defining modules, not the top-level package (which re-exports the client), per the server/client-separation invariant.

…, pipefail

- README: correct the HF sandbox docs URL, drop redundant/circular links
- .dockerignore: HF_TOKEN, not E2B_API_KEY
- pi_runtime: gate Node >=22.19 (not just major 22); use the full model id and
  select by --provider so a slashed id isn't split; set -o pipefail so the
  piped tee doesn't mask Pi's exit code
Copilot AI review requested due to automatic review settings July 22, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 37 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

envs/pi_env/server/pi_environment.py:337

  • setup is documented/treated as running before the agent, but the current flow calls factory.create() (which starts Pi) and only then runs the setup commands. That can race with the agent and violates the advertised (instruction, setup, verify) ordering (README + tool signature comments).
            # Run setup commands one at a time, *before* the agent starts.
            # The factory has already started the agent in start_agent()
            # during create(); to keep the order "setup → agent → verify"
            # we'd need to restructure. As a pragmatic compromise we run
            # setup IMMEDIATELY after create(), which races with the agent

envs/opencode_env/sandbox/hf.py:126

  • HFSandboxBackend docstring says the image must ship opencode + proxy, but the harness supports cold-install (and docs/README explicitly recommend image="python:3.12" + runtime install). This is misleading for users choosing between cold-install vs the prebaked image.
class HFSandboxBackend:
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

envs/pi_env/server/Dockerfile:59

  • The Docker build installs openenv-opencode-env from the moving main branch with no ref/SHA pin. That makes builds non-reproducible and can break unexpectedly when main changes (especially important for HF Spaces rebuilds).
RUN --mount=type=cache,target=/root/.cache/uv \
    uv pip install --python .venv/bin/python \
    "openenv-opencode-env @ git+https://github.com/huggingface/OpenEnv.git#subdirectory=envs/opencode_env"

Comment thread envs/pi_env/__init__.py
Comment on lines +24 to +32
from opencode_env.sandbox import (
HFSandboxBackend,
SandboxBackend,
SandboxHandle,
)

from .client import PiEnv
from .config import PiConfig
from .harness import PiSession, PiSessionFactory

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional for now: pi_env reuses the sandbox backend + interception proxy from opencode_env instead of duplicating them (this PR is stacked on #998). The plan is to consolidate both into openenv.core (tracked with #940 / the harness RFC), at which point the peer dependency goes away. Documented in the README and pyproject.toml.

…docs

Import the primitive from its defining modules instead of the top-level
pi_env package (which re-exports the client), per the server/client-separation
invariant. Reword the setup docs: it runs at rollout start, not strictly
before the agent.
Copilot AI review requested due to automatic review settings July 22, 2026 14:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 37 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

envs/pi_env/pyproject.toml:37

  • pi_env imports and re-exports opencode_env.sandbox (and pi_env.harness imports opencode_env) but openenv-opencode-env is not declared as an install dependency. As written, pip install openenv-pi-env will fail at import/runtime unless users manually install openenv-opencode-env, which contradicts the current “peer dep” comment. Consider making the dependency explicit (or making the import truly optional with stubs).
    # Pi harness primitive — sandbox + proxy + agent driver
    "httpx>=0.27.0",
    # >=1.22 ships huggingface_hub.Sandbox (HFSandboxBackend); core only floors 0.20.
    "huggingface_hub>=1.22",
    # Shared sandbox backend + interception proxy live in opencode_env for now
    # (plan: consolidate into openenv.core). Kept a peer dep — install from the
    # same repo (the server Dockerfile installs it explicitly):
    #   pip install "openenv-opencode-env @ git+https://github.com/huggingface/OpenEnv.git#subdirectory=envs/opencode_env"
]

envs/pi_env/server/Dockerfile:58

  • The Docker build installs openenv-opencode-env from the GitHub default branch with no ref/sha pin, which makes builds non-reproducible and makes stacked-PR validation harder (it will silently pull whatever main is at build time). It would be safer to add an ARG so CI / developers can pin a branch, tag, or commit SHA when needed.
# The shared sandbox backend + interception proxy live in opencode_env (kept a
# peer dep in pyproject, not a hard dep, so local dev / training installs stay
# lean). Install it explicitly into the venv so the server can import
# ``opencode_env.sandbox``. TEMPORARY: repoint to the branch carrying
# HFSandboxBackend until it lands in main; removed once the shared sandbox
# consolidates into openenv.core.
RUN --mount=type=cache,target=/root/.cache/uv \
    uv pip install --python .venv/bin/python \
    "openenv-opencode-env @ git+https://github.com/huggingface/OpenEnv.git#subdirectory=envs/opencode_env"

envs/opencode_env/sandbox/hf.py:126

  • HFSandboxBackend is now reused by pi_env as a generic HF sandbox backend, and the harnesses can also cold-install required binaries. The docstring currently reads as OpenCode-specific (“image must ship opencode CLI, node, and proxy”), which is misleading for other consumers and for the cold-install path.
class HFSandboxBackend:
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

…dead field

- harness/server: size the sandbox lifetime and MCP-tool timeout to the full
  Node+Pi cold install, not just the agent
- README: note the opencode_env install prerequisite for the primitive path;
  correct the server Dockerfile comment
- config: drop unused request_timeout_ms
- client: document the endpoint shorthand
Copilot AI review requested due to automatic review settings July 22, 2026 14:59
Comment thread envs/pi_env/server/pi_environment.py Outdated
if proc is None:
return 0
if not proc.running:
return int(proc.exit_code) if proc.exit_code is not None else 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reaped process reports exit zero

Medium Severity

HFBgJob.wait treats a missing pid in processes() as exit code 0. If the agent exits non-zero and is reaped between polls, the harness reports success, so agent_exit_code and any logic that depends on it become wrong.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 82d3c19. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is opencode_env's hf.py (part of #998), shown here because #999 is stacked on it. Verified against a real sandbox that finished processes stay listed, so a vanished pid is a torn-down sandbox — fixed in #998 (acf3143a): wait() now raises instead of returning 0. Propagates to #999 on rebase once #998 lands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 37 changed files in this pull request and generated 4 comments.

Comment thread envs/pi_env/harness.py
Comment on lines +446 to +454
self._exec_with_retry(
sandbox,
"pip install --quiet 'fastapi>=0.104' 'uvicorn[standard]>=0.24' "
"'httpx>=0.27' 2>&1 | tail -20",
timeout=180,
attempts=3,
backoff_s=2.0,
label="proxy deps install",
)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e1e3c27fset -o pipefail on the proxy-deps install so a failed pip isn't masked by tail.

Comment on lines +138 to +150
def _command_rows(items: list[dict[str, Any]]) -> list[list[str]]:
rows: list[list[str]] = []
for it in items or []:
cmd = it.get("cmd", "")
rows.append(
[
cmd if len(cmd) <= 80 else cmd[:77] + "...",
str(it.get("exit_code", "")),
f"{it.get('duration_s', 0):.2f}s",
(it.get("stderr") or "").splitlines()[-1][:80] if it.get("exit_code") else "",
]
)
return rows

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e1e3c27f — the UI now uses next(reversed(...), "") so an empty stderr split no longer raises IndexError.

Comment on lines 450 to 451
if not proxy_already_present:
# Install proxy deps (idempotent on retries).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e1e3c27fset -o pipefail on the proxy-deps install so a failed pip isn't masked by tail.

Comment on lines +160 to +164
if not (base_url and api_key and model):
raise ValueError(
"must provide either ``endpoint`` (one of "
f"{ENDPOINT_KINDS}) or all of base_url + api_key + model"
)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e1e3c27f — the message now renders ', '.join(ENDPOINT_KINDS), so no duplicated paren.

Mirror opencode_env's pre-baked image: bake Node + Pi + proxy deps so rollouts
skip the cold install (the proxy source is uploaded at runtime since it lives
in opencode_env). Also drop the stale '(default)' on transparent_proxy in the
harness docstring (the factory default is black_box).
Copilot AI review requested due to automatic review settings July 22, 2026 15:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 38 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

envs/pi_env/server/gradio_ui.py:148

  • _command_rows can raise IndexError when a command fails with an empty stderr (because .splitlines()[-1] is applied to an empty list). This would break the Gradio UI rendering for some failures (e.g., SIGKILL/network blips that yield no stderr).
def _command_rows(items: list[dict[str, Any]]) -> list[list[str]]:
    rows: list[list[str]] = []
    for it in items or []:
        cmd = it.get("cmd", "")
        rows.append(
            [
                cmd if len(cmd) <= 80 else cmd[:77] + "...",
                str(it.get("exit_code", "")),
                f"{it.get('duration_s', 0):.2f}s",
                (it.get("stderr") or "").splitlines()[-1][:80] if it.get("exit_code") else "",
            ]

envs/opencode_env/sandbox/hf.py:126

  • The HFSandboxBackend docstring says the sandbox image must already include opencode + node + proxy, but the harness code/docs also support cold-installing these at runtime (e.g. image="python:3.12"). This docstring is misleading for users choosing an image.
class HFSandboxBackend:
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

Comment on lines +51 to +58
# Inside-sandbox paths the server writes/reads (HF sandbox execs as root).
HOME = "/root"
WORKDIR = f"{HOME}/workdir"
INSTRUCTION_PATH = f"{HOME}/task/instruction.md"
REWARD_FILE = f"{HOME}/logs/verifier/reward.txt"
PROXY_LOG = f"{HOME}/logs/agent/proxy.log"
AGENT_LOG = f"{HOME}/logs/agent/pi.jsonl"
VERIFY_TIMEOUT_S = 120

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8bb0c3b8 (and 1c466584 for opencode) — setup commands now get SETUP_TIMEOUT_S=300 while verify keeps 120, so pip installs / downloads in setup aren't cut off.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 38 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

envs/opencode_env/sandbox/hf.py:126

  • This docstring claims the HF sandbox image must already include opencode + node + the proxy, but the harness supports cold-installing these at runtime (e.g. image="python:3.12" per README and the install logic in the harness). The docstring is misleading for users configuring the backend.
class HFSandboxBackend:
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

Comment thread envs/pi_env/server/pi_environment.py Outdated

# Late credential check — keeps the server importable in dev /
# docs-only contexts.
if not (os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 95d7d4f7 — the check now uses huggingface_hub.get_token(), which covers HF_TOKEN, the legacy env var, and a cached hf auth login token, instead of hard-coding an env var name.

…e separately

- token check via huggingface_hub.get_token() (HF_TOKEN, legacy var, or a
  cached login) instead of guessing env var names
- _start_proxy: skip the pip install when the deps import, independent of the
  proxy source upload, so the pre-baked image (deps baked, source not) skips it
Copilot AI review requested due to automatic review settings July 22, 2026 15:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 38 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

envs/opencode_env/sandbox/hf.py:126

  • The HFSandboxBackend docstring claims the sandbox image must already include opencode/node/proxy, but the harness supports cold-installing these (e.g. image="python:3.12" per README and _bootstrap_sandbox). This is misleading for users choosing an image.
class HFSandboxBackend:
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

Comment on lines +24 to +27
import json
import os
import time
from typing import Any, Optional

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 68eb2b84 — removed the unused os import (the token check moved to huggingface_hub.get_token()).

Copilot AI review requested due to automatic review settings July 23, 2026 11:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 38 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

envs/opencode_env/sandbox/hf.py:126

  • The HFSandboxBackend docstring states the sandbox image must already include opencode + node + proxy, but the harness bootstrap logic supports cold-installing these (e.g. README usage with image="python:3.12"). This docstring mismatch can mislead users into thinking a pre-baked image is required.
    """Creates Hugging Face sandboxes for OpenCode rollouts.

    ``image`` must ship the ``opencode`` CLI, node, and the in-sandbox proxy.
    """

Copilot AI review requested due to automatic review settings July 23, 2026 12:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

envs/pi_env/server/pi_environment.py:347

  • This comment says setup runs "before the agent starts", but the following lines correctly explain the agent has already been started during factory.create(). Rewording avoids misleading future readers about the ordering guarantees (especially since the race is explicitly accepted here).
            # Run setup commands one at a time, *before* the agent starts.
            # The factory has already started the agent in start_agent()
            # during create(); to keep the order "setup → agent → verify"
            # we'd need to restructure. As a pragmatic compromise we run
            # setup IMMEDIATELY after create(), which races with the agent
            # for ~1-2s but is fine for typical pip/git/download work
            # because pi itself takes >=20s to make its first model call.

Comment thread envs/pi_env/client.py
Comment on lines +44 to +49
class PiEnv(MCPToolClient):
"""Typed client for the pi_env MCP server.

Inherits ``reset`` / ``call_tool`` / ``list_tools`` / ``from_docker_image``
/ context-manager semantics from :class:`MCPToolClient`.
"""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 599718ec. PiEnv now defaults message_timeout_s to 1800s (the server caps a rollout at 1500s), still overridable per call. opencode_env's OpenCodeEnv (now on main) has the same 60s vs 900s mismatch, worth the same fix in a follow-up.

Copilot AI review requested due to automatic review settings July 23, 2026 12:19

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 599718e. Configure here.

if override is not None:
result.reward = override
elif verify:
result.reward = verify_passed / len(verify)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed agent still scored

Medium Severity

After wait_for_completion, only a TimeoutError sets result.error. A non-zero agent_exit_code leaves error as None, so verify still runs and can assign a reward. The nearby comment says a failed agent should leave reward unset, matching the earlier PR fix intent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 599718e. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 23, 2026 12:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Mirror of the OpenCodeSessionFactory retry: session creation (sandbox + Pi
install + proxy + agent) is the flakiest step, and a single transient failure
dropped the rollout as unscorable. create() now retries create_attempts times
with exponential backoff; the per-attempt logic moved to _create_once, which
already tears its own sandbox down on failure, so a retry never leaks.
Copilot AI review requested due to automatic review settings July 24, 2026 06:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 24, 2026 10:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants