Skip to main content

Code Execution

The execute_code tool lets an agent run a short Python program in an isolated, server-side sandbox and read back its stdout, stderr, and exit code in the same turn. It's for work the agent should do rather than reason about — math and statistics, parsing or reshaping data the user pasted or a tool returned, validating a regex or algorithm, or generating a small file.

Code execution is a powerful capability, so it is off by default and protected by two independent gates plus first-use human approval. It ships disabled unless an operator wires the sandbox sidecar and a team explicitly opts in.

What It Does

Given a Python program, the sandbox runs it once in a fresh, empty working directory and returns the captured result. The agent can pass input alongside the code:

ParameterRequiredDescription
codeYesThe Python program to run. Print results to stdout — the agent reads stdout back. Standard library only.
languageNoRuntime to use. Phase 1 supports only python (the default).
stdinNoText piped to the program's standard input.
timeout_msNoWall-clock limit in milliseconds. The server clamps this to its maximum; omit to use the server default.
filesNoInput files written into the working directory before the program runs. Each item is {"name": "data.csv", "content": "..."}. Names must be plain filenames (no path separators).

The result the agent reads back includes:

  • stdout / stderr — captured output (large output is head-kept and tail-dropped, with truncated=true when that happens)
  • exit_code — the process exit code
  • duration_ms — wall-clock execution time
  • killedtrue when the run exceeded its time limit
  • oomtrue when the run exhausted its memory budget
  • output_files — metadata (name, size, truncated) for any files the program wrote

Each run starts fresh. Nothing persists between calls, so a later call cannot see an earlier call's variables or files — everything the program needs is passed through code, stdin, or files.

Safety Properties

The sandbox is built for defense in depth. Every run is isolated so agent-authored code can compute freely without reaching anything it shouldn't:

  • No network access — the program cannot reach the internet or any internal service.
  • No secrets, credentials, or database access — the child process runs with a default-deny, allowlisted environment and never inherits the platform's environment, so service API keys and other secrets are absent from executed code by construction.
  • Standard library only — Phase 1 has no third-party packages installed.
  • Ephemeral working directory — a fresh directory is created per job and removed afterwards.
  • Resource bounds — POSIX rlimits cap CPU seconds, file-write size, open files, and process count (a fork-bomb guard), and a wall-clock timeout kills the whole process tree.
  • Cannot call other tools — code runs in isolation and has no path back into the agent's toolset.

The Two Gates

Beyond first-use approval, execute_code only exists for a run when both of these are true:

  1. Operator wiring — the sandbox sidecar is configured (SANDBOX_EXECUTOR_URL set). When it's unset, execute_code is never registered for any agent.
  2. Per-team opt-in — the team has turned on the agent_sandbox setting. This is default-deny: with no setting (or any lookup error), the tool is stripped from the run.

On top of those, execute_code is risk tier T4, so the first time an agent in a team tries to run code, the call pauses for human approval before anything executes. After that, a team can set a tool policy to allow it without re-approving each time.

Enabling Code Execution

1. Operator: wire the sandbox sidecar

Run the sandbox-executor service and point core-api at it:

# core-api
SANDBOX_EXECUTOR_URL=http://sandbox-executor:9099
SANDBOX_EXECUTOR_API_KEY=<shared secret> # must match the sidecar's key
SANDBOX_EXECUTOR_TIMEOUT_SECONDS=65 # core-api → sidecar request timeout

# sandbox-executor sidecar
SANDBOX_EXECUTOR_SERVICE_API_KEY=<shared secret> # required outside local; fails closed on startup if empty
SANDBOX_EXECUTOR_DEFAULT_TIMEOUT_MS=30000 # per-job wall-clock default
SANDBOX_EXECUTOR_MAX_TIMEOUT_MS=60000 # per-job wall-clock ceiling
SANDBOX_EXECUTOR_MAX_CONCURRENT_JOBS=4 # concurrent jobs sharing the container memory budget

The shared secret bridges the two services: core-api sends it as SANDBOX_EXECUTOR_API_KEY and the sidecar validates it as SANDBOX_EXECUTOR_SERVICE_API_KEY. The sidecar fails closed on startup if the key is empty outside development / dev / local / test environments.

If SANDBOX_EXECUTOR_URL is left empty, the tool is never registered and code execution is fully disabled — no further configuration needed.

2. Per team: opt in

With the sidecar wired, each team must still enable the agent_sandbox setting before its agents can run code. Until a team opts in, execute_code is stripped from every run for that team. An operator can also enable it fleet-wide with a single global setting.

Self-Hosting

The sandbox is a standalone sidecar (services/sandbox-executor/) that core-api calls over a simple HTTP contract — POST /execute, authenticated with a shared X-Service-API-Key Bearer token, plus GET /healthz and GET /metrics. The isolation substrate sits entirely behind that contract, so core-api never sees how code is sandboxed.

In Phase 1 the sidecar runs each job as a locked-down child process inside an already-hardened container — cap_drop: ALL, no-new-privileges, non-root, read-only root filesystem, a tmpfs scratch space, memory / CPU / PID limits, and no external network. Network isolation is enforced at the container layer.

Because the substrate is swappable behind the same /execute contract, a stronger per-job isolation layer (network-namespace + seccomp via nsjail, gVisor, or Firecracker) is a documented Phase 3 upgrade that slots in without changing core-api or the agent tool.