Give a sandboxed Claude or Codex agent a GitHub repository to work in
October 19, 20262 min read
Agno's ClaudeAgent and CodexAgent accept repository=Repository(url, branch="main") beside sandbox=, and on a session's first turn the sandbox resolves that branch to one commit, fetches it with --depth=1 and starts the harness in /workspace/repository. Every committed run then reports the pin at metadata.sandbox.repository as its url, branch and commit.
import os
from agno.agents.codex import CodexAgent
from agno.db.postgres import PostgresDb
from agno.sandbox import Repository
from agno.sandbox.checkpoint import FileCheckpointStore
from agno.sandbox.docker import DockerSandbox
engineer = CodexAgent(
id="product-engineer",
db=PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai"),
sandbox=DockerSandbox(
image="agno-sandbox:local",
network="isolated", # the computer needs egress to reach GitHub
checkpoints=FileCheckpointStore("/srv/agno/checkpoints"),
env={"GITHUB_READ_TOKEN": os.environ["GITHUB_READ_TOKEN"]}, # private repositories only
),
repository=Repository(url="https://github.com/acme/app", branch="main"),
sandbox_mode="workspace-write",
)
result = engineer.run("Run the test suite and report what fails", session_id="upgrade-1")
print(result.metadata["sandbox"]["repository"]) # url, branch and the commit turn 1 pinnedLater turns read the pin back from native/repository.json and skip the fetch, so new pushes to the branch leave a running session where it started. The engineer's commits and uncommitted files travel in each turn's checkpoint, which covers both a warm resume and a restore into a new computer.
We ran a FixtureAgent with the same repository= on a real DockerSandbox and Postgres, with the fixture runtime standing in for the model, against the public octocat/Hello-World repository. Turn 1 pinned master at 7fd1a60. A shell command in the warm computer, playing the engineer, appended a line to README. Turn 2 resumed that computer. We then removed the container, and turn 3 came up on a new one restored from turn 2's checkpoint, with HEAD still at 7fd1a60 and git status still showing M README.

Private repositories authenticate with GITHUB_READ_TOKEN from the sandbox environment. Git receives it only through the child process environment, with prompts, redirects, credential helpers and hooks turned off. An agent with a repository refuses GITHUB_TOKEN with Sandbox repository access requires GITHUB_READ_TOKEN; remove GITHUB_TOKEN. On the first turn, run metadata can also choose the starting point with {"repository": {"commit": "<40-hex sha>"}} or {"repository": {"branch": "release"}}, and later turns ignore it once the pin exists.
See the cookbook, and learn more about working files for harness agents in the documentation.
Frequently asked questions
Set repository=Repository(url, branch) on the ClaudeAgent or CodexAgent next to sandbox=. On the first turn of each session the sandbox fetches that branch's current commit into /workspace/repository, and the harness runs with that folder as its working directory.
No. The first turn records the url, branch and commit in native/repository.json, and every later turn of that session keeps that commit. Each committed run publishes the pin at metadata.sandbox.repository.
Yes. Commits and uncommitted files under /workspace/repository are part of every turn's checkpoint, so they are there whether the next turn resumes the warm computer or restores into a new one.
Put a read-only GITHUB_READ_TOKEN in the sandbox environment. Agno passes it to Git only through the child process environment, and an agent with a repository refuses GITHUB_TOKEN.


