---
title: "Run Claude Agent SDK agents in a Promigence sandbox"
description: "Keep the Claude Agent SDK loop in your process and run Claude Code itself inside a Promigence sandbox, with your Anthropic key stored as a secret."
url: "https://www.promigence.ai/docs/guides/claude-agent-sdk"
site: "Promigence"
---

# Run Claude Agent SDK agents in a Promigence sandbox

Keep the Claude Agent SDK loop in your process and run Claude Code itself inside a Promigence sandbox, with your Anthropic key stored as a secret.

The Claude Agent SDK starts Claude Code as a child process and talks to it over stdin and stdout. Its
`spawnClaudeCodeProcess` option lets you replace that spawn. The `promigence-claude-agent-sdk` package
gives you a function for that option, so Claude Code runs inside a Promigence sandbox while your
program keeps the loop, reads the messages and decides what happens next. Anything the agent edits,
installs or deletes stays in the sandbox.

## What you need

- A Promigence API key, exported as `PROMIGENCE_API_KEY`.
- Node.js 20 or newer.
- `@anthropic-ai/claude-agent-sdk` 0.3.276 or newer. The adapter was written and tested against 0.3.276.
- An Anthropic API key, stored once as a Promigence secret (below).

## Install

```bash
npm install promigence-claude-agent-sdk @anthropic-ai/claude-agent-sdk promigence
export PROMIGENCE_API_KEY=prom_api_...
promigence secrets set ANTHROPIC_API_KEY --from-env ANTHROPIC_API_KEY
```

`promigence secrets set` reads the value from your `ANTHROPIC_API_KEY` variable and stores it
write-only. From then on your code only
ever names the secret. The value goes into the agent's environment inside the sandbox; it is never
put on a command line or written to a file there, and the package refuses a credential passed as a
plain environment value.

## A minimal example

The sandbox needs Claude Code installed. The built-in base snapshot has Node.js, so one `npm install`
inside the sandbox is enough. You also have to tell the SDK where the executable lives inside the
sandbox, because the path it would find on your own machine does not exist there.

```ts
import { query } from "@anthropic-ai/claude-agent-sdk";
import { PromigenceClient, Sandbox } from "promigence";
import { spawnInPromigence } from "promigence-claude-agent-sdk";

const client = new PromigenceClient();
const sandbox = await Sandbox.create({ client, timeoutMs: 15 * 60_000, spendCap: 5 });
await sandbox.commands.run("command -v claude || npm install -g @anthropic-ai/claude-code");

try {
  for await (const message of query({
    prompt: "Fix the failing test in src/parser.ts",
    options: {
      pathToClaudeCodeExecutable: "/usr/local/bin/claude",
      spawnClaudeCodeProcess: spawnInPromigence({
        client,
        sandboxId: sandbox.sandboxId,
        cwd: "/workspace",
        secrets: ["ANTHROPIC_API_KEY"],
        onStderr: (text) => process.stderr.write(text),
      }),
    },
  })) {
    if (message.type === "result") console.error(`[${message.subtype}]`);
  }
} finally {
  await sandbox.kill();
}
```

If you would rather not install Claude Code yourself, `promigence agent start claude` prepares a
sandbox with Claude Code in it, binds your stored `ANTHROPIC_API_KEY`, and prints the sandbox id to
pass as `sandboxId`.

`spawnInPromigence` takes a few more options. `executablePath` overrides where Claude Code is inside
the sandbox. `env` adds plain settings. `envForward` controls which of the SDK's own environment
travels: by default only `CLAUDE_*` and `ANTHROPIC_*` settings, and never anything shaped like a
credential. Put a token you do want the agent to have in `secrets` after storing it with
`promigence secrets set`.

## One sandbox per query

You create the sandbox and you end it. The package attaches one Claude Code process to the sandbox
you name; it does not create, copy or end sandboxes on its own. The usual shape is one sandbox per
task: create it, run the query, kill it in a `finally` block as above.

Running two agents in the same sandbox at once needs a different `envDir` and `stderrPath` for each,
since both default to fixed paths.

## Setting a spend cap

Pass `spendCap` (US dollars) to `Sandbox.create`, or set `PROMIGENCE_SPEND_CAP` in the environment.
Without either, your organization's default cap applies. The cap is the most that sandbox may spend;
Promigence stops it rather than let it go over.

## Cleaning up

`await sandbox.kill()` ends the sandbox and its billing. Put it in `finally` so a thrown error or an
interrupted loop does not leave one running. `timeoutMs` is a second guard: the sandbox ends on its
own when that time is up. `promigence sandbox list` shows anything still running, and
`promigence sandbox kill <id>` ends one by hand.

## What is not supported yet

- Closing the agent's stdin does not end it. A query finishes when the SDK stops the agent after its
  own grace period.
- There is no live stderr stream. `onStderr` receives the tail once the agent exits.
- The SDK's local debug file and session store do not carry across to a remote agent.
- If `pathToClaudeCodeExecutable` ends in `.js` or `.mjs`, set `executable: "node"` in the SDK
  options and leave `executablePath` unset.
- Multi-line environment values lose trailing newlines.
- TypeScript only. The Python Agent SDK has no equivalent spawn hook, so there is no Python version.

## Next

Read [the agent briefing](https://www.promigence.ai/agents) for what a sandbox gives an agent, or start with
[the quickstart](https://www.promigence.ai/docs/quickstart).

