kjrjay-pi-sandbox

内容来源:README.md(说明文档) · 原始地址 · 查看安装指南

原始内容

Pi Container Sandbox

A Pi extension that runs file and shell tools inside an isolated container workspace while keeping Pi, authentication, model calls, sessions, and the TUI on the host.

The extension routes read, write, edit, bash, ls, find, grep, and user ! commands into the container. The host repository's .git directory is never mounted into it.

[!IMPORTANT] A container reduces accidental host modification; it is not an absolute security boundary. Review SECURITY.md before using untrusted images or repositories.

Contents

Features

  • Routes Pi's standard file and shell tools into Apple Container or Docker.
  • Keeps host credentials, sessions, model calls, and the repository .git directory outside the container.
  • Creates validated Git checkpoints on an isolated sandbox branch or fast-forwards the current branch.
  • Supports automatic checkpoints after a model turn, agent run, or fully settled cycle.
  • Preserves or removes containers according to configurable lifecycle policy.
  • Supports protected host-untracked overlays, lockfile-based dependency bootstrap, and shared package caches.
  • Provides a read-only review agent and guarded in-container rebasing.
  • Supports disabled, dynamic, or fixed Docker port publication on host loopback.

Requirements

  • Pi with Git package support.
  • A supported runtime: Apple container or docker.
  • A Git repository with an existing HEAD commit.
  • A clean tracked host worktree. Untracked files are handled separately by hostUntrackedFiles.
  • A container image with the tools needed by the project. Core sandbox operations require common shell utilities, Bash, Git, and tar; sandbox grep requires ripgrep.

Installation

Pi packages execute with the installing user's full permissions. Review index.ts and SECURITY.md before installation.

Install from npm

Install globally for the current user:

pi install npm:@kjrjay/pi-sandbox

Install only for the current trusted project:

pi install -l npm:@kjrjay/pi-sandbox

Project-local installation writes to .pi/settings.json, which can be shared with the project. Pi installs the package after the project is trusted.

To pin a specific release:

pi install npm:@kjrjay/pi-sandbox@0.1.0

Pinned npm versions remain fixed during package updates. Install a newer version explicitly when upgrading a pinned installation.

Install from GitHub

The latest source can also be installed directly from GitHub:

pi install git:github.com/kjrjay/pi-sandbox

To pin a Git installation, append a tag or full commit hash.

Try without installing

Load the npm package temporarily for one Pi invocation:

pi -e npm:@kjrjay/pi-sandbox

Update

Update an unpinned installation:

pi update --extensions

Use pi list to inspect installed package sources. Restart Pi after installation, or run /reload in an existing session.

Manual installation

Alternatively, clone the repository into Pi's global extension directory:

git clone https://github.com/kjrjay/pi-sandbox.git \
  ~/.pi/agent/extensions/container-sandbox

Pi auto-discovers index.ts from that directory. Pull updates with Git and run /reload afterward.

Uninstallation

Before uninstalling, checkpoint or preserve any named target branches and generated refs/heads/pi-sandbox-* branches, then stop active sandbox operations.

Remove a global npm installation:

pi remove npm:@kjrjay/pi-sandbox

For a project-local npm installation:

pi remove -l npm:@kjrjay/pi-sandbox

If the package was installed from GitHub, use its Git source instead:

pi remove git:github.com/kjrjay/pi-sandbox

Use pi list when unsure which source is recorded. Restart Pi or run /reload after removal.

For a manual clone, remove its directory:

rm -rf ~/.pi/agent/extensions/container-sandbox

Package removal intentionally does not delete configuration, caches, containers, or Git refs. Remove these only after reviewing anything that may contain recoverable work.

Optional configuration and cache cleanup:

rm -f ~/.pi/agent/extensions/pi-sandbox.json
rm -rf ~/.pi/agent/cache/container-sandbox

Docker containers created by the extension can be listed by their managed label:

docker ps -a --filter label=pi.container-sandbox.managed=true

Generated sandbox branches and recovery refs can be reviewed in each affected repository with:

git for-each-ref refs/heads/pi-sandbox- refs/pi-sandbox-recovery/

Named sandbox:<branch> targets use the exact branch name and should be reviewed separately.

Do not delete containers, branches, or refs until their work is committed, exported, or no longer needed.

Quick start

After installation, add a partial global configuration at:

~/.pi/agent/extensions/pi-sandbox.json

Example Docker configuration:

{
  "runtime": "docker",
  "image": "pi-tool-sandbox:latest",
  "target": "sandbox",
  "checkpointFrequency": "agent",
  "dockerPortMode": "dynamic",
  "dockerPortRange": "8000-8010",
  "lifecycle": "stopped"
}

A trusted project can override global values with:

<project>/.pi/pi-sandbox.json

Start Pi from a clean Git worktree. The container is created lazily before the first agent run. Use /sandbox status to inspect the effective configuration and active container.

How it works

  1. The extension validates the repository and runtime configuration.
  2. It creates a local clone from the authoritative host Git ref and copies that workspace into the container.
  3. Pi's normal file and shell tools execute against the container copy.
  4. At the configured boundary, the extension stages and validates sandbox changes.
  5. It transfers Git objects through a temporary bundle and publishes them to the selected target.

Runtime command capture is bounded on the host. Bash output streams through Pi without retaining an unbounded duplicate buffer.

Targets

sandbox — default

Each session checkpoints into an isolated generated local host branch:

refs/heads/pi-sandbox-<session-hash>

Use target: "sandbox:<branch>" for an exact stable branch and container identity. For example, sandbox:feat/feature-b publishes to refs/heads/feat/feature-b. Named branches are limited to 96 characters and must be valid Git branch names. Container names use a bounded pi-<repo>-<readable-branch>-<hash> form derived from the repository path and exact target ref. The checked-out host branch, index, and worktree remain unchanged. The extension refuses to update a target branch while it is checked out in any host worktree. A host lock in the repository's common Git directory prevents concurrent Pi sessions from owning the same target branch and container. The lock is reserved during session startup, before the container is created; an idle open Pi session therefore continues to own its configured target.

current

Validated checkpoints fast-forward the branch that was checked out when the sandbox started, updating the host index and worktree. This mode:

  • requires an attached local branch;
  • uses a per-worktree lock to prevent concurrent sessions;
  • refuses publication if the host branch or tracked worktree changes unexpectedly.

If the branch advances, run /sandbox rebase. The rebase occurs inside the container before the host branch is fast-forwarded.

Checkpoints

checkpointFrequency controls automatic checkpoint timing:

Value Boundary
turn After each internal model response and its tool calls. This is the default and gives the finest-grained recovery.
agent After one low-level agent tool loop, normally one checkpoint per submitted prompt.
settled After retries, compaction retries, and queued continuations have finished.

At each checkpoint, the extension:

  1. Stages the sandbox tree against the authoritative baseline.
  2. Generates a commit message with the active model, with a timestamp fallback.
  3. Reconstructs one commit using the expected parent and host Git author identity.
  4. Transfers it through a temporary Git bundle and host ref.
  5. Validates object integrity, parentage, ancestry, and commit count.
  6. Advances the sandbox branch with compare-and-swap or guarded-fast-forwards the current branch.

Copied host-untracked overlay files are removed from the checkpoint index. /sandbox checkpoint runs the same operation manually.

Workspace and lifecycle

New workspaces come from a local host clone and are shallow by default. No remote clone or remote Git authentication is required.

Existing containers are reused only after validating their image, package-cache mount, managed metadata, repository identity, configuration, and authoritative Git ref. Clean stale workspaces are reseeded. Mismatched or recoverable containers are preserved and rejected rather than silently overwritten.

lifecycle controls shutdown behavior:

Value Behavior
remove Checkpoint and remove the container. This is the default.
stopped Checkpoint and stop the container for reuse.
running Checkpoint and leave the container running.

/sandbox stop checkpoints and then stops stopped or running containers, or removes remove containers. A later sandbox tool call can start the sandbox again.

Host-untracked files

hostUntrackedFiles controls files already untracked on the host:

Value Behavior
ignore Do not copy them. This is the default. Host files are not deleted or modified.
copy Copy non-ignored files into the container as a checkpoint-protected overlay.

Discovery follows .gitignore and optional .pi-sandboxignore rules. On reuse, previous and current overlay manifests are reconciled so files removed or newly ignored on the host are removed from the container before checkpointing.

Copied files remain readable by sandbox processes. Exclude credentials and other sensitive files.

Docker networking

When runtime is docker, dockerPortMode controls publication of every container port in dockerPortRange:

Mode Behavior
disabled Publish no ports.
dynamic Map each container port to a Docker-assigned host port on 127.0.0.1. This is the default and avoids port conflicts.
fixed Map each container port to the same numbered host port on 127.0.0.1. Startup fails when a selected host port is occupied.

Examples:

Dynamic: 127.0.0.1:49153 -> container:8000
Fixed:   127.0.0.1:8000  -> container:8000

In both publishing modes, the server inside the container must bind to 0.0.0.0 on a configured container port. Resolved mappings appear in the startup notification, /sandbox status, and the agent system prompt. They remain stable while a stopped container is preserved.

dockerPortRange accepts one port or an ascending range of at most 100 ports. Set hostGateway to a hostname such as host.docker.internal to add Docker's host-gateway mapping. Host-gateway access is disabled by default because it lets sandbox processes connect to services on the Docker host.

These options do not affect Apple Container.

Dependencies and package caches

installDeps supports:

Value Behavior
never Do not install dependencies. This is the default.
auto Run fixed lockfile-based bootstrap logic for npm, pnpm, Yarn, Bun, uv, or pip.

New containers bind-mount the extension-owned cache directory:

~/.pi/agent/cache/container-sandbox/packages:/var/cache/pi-packages

Download caches are shared across containers. Project node_modules and virtual environments remain inside each workspace. With installDeps: "auto", bootstrap runs whenever a new or reused sandbox workspace starts.

Review agent

/sandbox review creates an isolated, in-memory reviewer session. With no instructions, it reviews the latest sandbox commit. Text after -- can select a scope and provide guidance:

/sandbox review -- check the last 3 commits for security issues
/sandbox review -- commit hash 21e81311a

The reviewer has a separate context and read-only tools routed into the existing container. It cannot use host filesystem or shell tools. The completed report records the resolved commits, instructions, tool activity, diff stat, model, and usage metadata without modifying files or starting a fix loop.

Requested commits must exist in the sandbox clone. Set gitCloneDepth: 0 when reviews need older host history.

Rebasing

/sandbox rebase checkpoints pending work and rebases it inside the container onto the latest commit of the original host branch. A clean result is bundled to the host, validated, and published to the selected target.

If conflicts occur, automatic checkpoints pause. Resolve conflicts inside the container, stage them, and continue the rebase. Use /sandbox rebase-status to inspect progress or /sandbox rebase-abort to restore the pre-rebase sandbox state.

Non-fast-forward rewrites of the host base branch are rejected. If the host branch advances during final current publication, the rebased work is preserved under refs/pi-sandbox-recovery/*.

Commands

Command Description
/sandbox Show status.
/sandbox status Show effective configuration and runtime status.
/sandbox attach <host-image-path> [-- message] Attach an explicitly selected host screenshot to a new user message.
/sandbox checkpoint Create and publish a checkpoint immediately.
/sandbox review [-- instructions] Review sandbox commits with the read-only reviewer.
/sandbox rebase Rebase sandbox work onto the latest host base.
/sandbox rebase-status Show pending rebase state.
/sandbox rebase-abort Abort the pending rebase.
/sandbox stop Checkpoint and stop or remove the container.

Mutating commands wait for the active agent to become idle.

/sandbox attach is intended for screenshots that exist only on the host. It reads the explicitly named host image, uses Pi's normal image normalization and resizing, and sends it directly as multimodal message content; it does not copy the image into the container. For example:

/sandbox attach ~/Desktop/screenshot.png -- Explain the error shown here

Without -- message, the default prompt is Please inspect this screenshot. If the agent is busy, the image and message are queued as a follow-up.

Configuration

Configuration precedence, from lowest to highest:

  1. Built-in defaults.
  2. Global ~/.pi/agent/extensions/pi-sandbox.json.
  3. Trusted project <project>/.pi/pi-sandbox.json.
  4. CLI flags.

Both JSON files may contain only the values they override. Unknown keys and invalid values are rejected.

Option Default Description
runtime "docker" container or docker.
image "pi-tool-sandbox:latest" Container image used by the sandbox.
dockerPortMode "dynamic" disabled, dynamic, or fixed.
dockerPortRange "8000-8010" Docker container port or ascending range, up to 100 ports.
hostGateway "" Optional Docker hostname mapped to host-gateway.
target "sandbox" sandbox, exact sandbox:<branch> (up to 96 characters), or current.
checkpointFrequency "turn" turn, agent, or settled.
hostUntrackedFiles "ignore" ignore or copy.
gitCloneDepth 1 Local clone depth; 0 uses full history.
gitCommitCoAuthor "Pi <pi@localhost>" Optional commit-message co-author trailer.
gitCommitAiMaxDiffBytes 20000 Maximum staged diff sent for commit-message generation.
installDeps "never" never or auto.
lifecycle "remove" remove, stopped, or running.
passEnv [] Allowlist of host environment-variable names exposed to sandbox commands.
review.model "" Reviewer model; empty uses the current session model.
review.thinkingLevel "high" Reviewer thinking level.
review.maxDiffBytes 100000 Maximum patch included directly in the review prompt.

Example complete configuration:

{
  "runtime": "docker",
  "image": "pi-tool-sandbox:latest",
  "dockerPortMode": "dynamic",
  "dockerPortRange": "8000-8010",
  "hostGateway": "",
  "target": "sandbox",
  "checkpointFrequency": "agent",
  "hostUntrackedFiles": "ignore",
  "gitCloneDepth": 1,
  "gitCommitCoAuthor": "Pi <pi@localhost>",
  "gitCommitAiMaxDiffBytes": 20000,
  "installDeps": "never",
  "lifecycle": "stopped",
  "passEnv": [],
  "review": {
    "model": "",
    "thinkingLevel": "high",
    "maxDiffBytes": 100000
  }
}

Commit author and committer identity come from host Git configuration. The extension does not interpret Mise or other host tool-version configuration; required tools must exist in the image.

CLI flags

Flag Description
--no-sandbox Disable sandbox routing for this run. Standard tools execute on the host.
--sandbox-runtime <container|docker> Override the runtime.
--sandbox-image <image> Override the image.
--sandbox-docker-port-mode <disabled|dynamic|fixed> Override Docker port publication mode.
--sandbox-docker-port-range <port|start-end> Override Docker container ports.
--sandbox-target <sandbox|sandbox:branch|current> Override the checkpoint target and optional exact branch.
--sandbox-checkpoint-frequency <turn|agent|settled> Override checkpoint boundary.
--sandbox-git-clone-depth <n> Override local clone depth.
--sandbox-install-deps <auto|never> Override dependency bootstrap.
--sandbox-lifecycle <remove|stopped|running> Override shutdown behavior.
--sandbox-env FOO,BAR Override the environment-variable allowlist.

Tests

Run the permanent control-plane suite from the extension directory:

./tests/run.sh

The suite uses isolated Pi state, starts no containers, calls no models, and is designed to finish within 30 seconds. Container and model workflows currently require explicit manual integration checks; see tests/README.md.

Security and limitations

There are no model-callable host shell or host Git tools. Host commands use fixed internal operations. The current target has a larger trust boundary because materializing commits can invoke repository-configured Git LFS or checkout filters.

The runtime, image, networking, shared writable package cache, forwarded environment variables, Git parsers, and other Pi extensions remain outside this extension's isolation guarantee.

See: