CompanyOS Tasks run on sandboxed Claude and Codex agents pinned to one commit
October 19, 20261 min read
Agno's CompanyOS now accepts a Task for a ClaudeAgent or CodexAgent that has a sandbox and a repository, and the Task response reports sandboxed: true. The Product needs a git Source for the same repository, and the first successful run writes the exact commit it worked on to metadata.checkout.
Later attempts reuse that session and send the pinned commit back to the sandbox. A run that reports any other commit fails the attempt, and the Task stays In progress with the error. A pinned Task can only move to another sandboxed executor. The inert engineering executor type is gone.
There was no model key or Docker for this check, so we used the release's own test doubles. FixtureAgent, Agno's credential-free harness double, stood in for ClaudeAgent and CodexAgent with a DockerSandbox that never started. A scripted run output stood in for the harness turn, the queue ran on an embedded Postgres with fakeredis coordination, and the routes ran for real. Real output:
accept without git Source: 409 Sandboxed executor requires a Product git Source matching repository URL https://github.com/octocat/Hello-World
accept engineer: 200 sandboxed = True status = ready
POST runs attempt 1: 202 {'status': 'PENDING'}
after attempt 1: in_review | result: COMPLETED README says: Hello World!
metadata.checkout: {"source_id": "1a7d572e-ab93-45b5-a5b7-afa269d9cc1e", "url": "https://github.com/octocat/Hello-World", "branch": "master", "commit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
POST runs attempt 2 (revision): 202
after attempt 2 on another commit: in_progress | result: ERROR Sandboxed run repository pin does not match the Task checkout
same session: True
reassign pinned Task to plain agent: 409 Task checkout is already pinned and requires a sandboxed executor; create a new TaskEach Task also keeps one session owner across attempts, task.user_id. Company checks the run permission of whoever presses start, then runs the attempt in the owner's session and records the starter as started_by. With the release's 09_task_session_ownership.py cookbook, Alice started a local workflow Task and Bob asked for the revision:
Bob requests attempt 2: 202
same session: True | task.user_id: alice
started_by: ['alice', 'bob']
run owners: ['alice', 'alice']Recovery after a restart reconciles existing reservations before it checks the current Routine policy, and it never queues new work. Repeated automatic dispatch failures for the same Task and reason now share one failed history row, with a count in /company/attention under automation_failures. Refresh deletes completed automation history after CompanyConfig.automation_history_retention_days, 30 days by default.
See the cookbook, and learn more about sandboxes and checkpoints in the documentation.
Frequently asked questions
A git Source whose reference matches the agent's repository URL. Without it, accepting the Task returns 409 "Sandboxed executor requires a Product git Source matching repository URL". For a remote repository, the git Source takes a GitHub HTTPS URL and an optional config.branch.
The repository pin saved with the first successful sandboxed run, holding the Source id, URL, branch and commit. Later attempts send that commit to the sandbox, and a run that reports a different one ends with an ERROR result.
The Task owner's. Every attempt runs as task.user_id, Company checks the starting person's run permission first, and the transition records that person as started_by.
Shared PostgreSQL for runs and queue admission, Redis coordination and durable checkpoint storage. A PostgreSQL advisory lock admits one sandboxed Task start at a time, so concurrent starts are refused before dispatch.

