Multiple Agents in one Sandbox

Use box.agents when several AgentProfiles should share one computer, filesystem, local network, and resource envelope.

A Sandbox remains the trust boundary. Each Agent is an existing durable session—not another VM, database, scheduler, or lifecycle system.

Choose the right primitive

NeedPrimitive
One Agent conversationbox.prompt() / box.session(id)
Several trusted Agents on one computerbox.agents
Separate machines or hard CPU/memory boundariesFleet
Team strategy, supervisors, debate, recursive delegationagent-runtime over Sandbox sessions
Measurement and promotionagent-eval

Create a managed Sandbox

ts
import type { AgentProfile } from "@tangle-network/agent-interface";
import { Sandbox } from "@tangle-network/sandbox/agent";

const client = new Sandbox({
  apiKey: process.env.TANGLE_API_KEY!,
  baseUrl: process.env.SANDBOX_BASE_URL!,
});

const plannerProfile = {
  name: "planner",
  harness: "opencode",
  prompt: { appendSystemPrompt: "Plan before editing files." },
} satisfies AgentProfile;

const builderProfile = {
  name: "builder",
  harness: "claude-code",
  prompt: { appendSystemPrompt: "Implement and run tests." },
} satisfies AgentProfile;

const box = await client.create({
  agent: false,
});

agent: false keeps the managed runtime but skips a default Agent. Do not use bare: true; bare Sandboxes have no managed session runtime.

Start independent sessions concurrently

ts
const [planner, builder] = await box.agents.startMany([
  {
    id: "planner-v1",
    name: "Planner",
    backend: { type: "opencode", profile: plannerProfile },
  },
  {
    id: "builder-v1",
    name: "Builder",
    backend: { type: "claude-code", profile: builderProfile },
  },
]);

const [plan, implementation] = await Promise.all([
  planner.prompt("Inspect the repository and produce a precise plan."),
  builder.prompt("Inspect the repository and implement the request."),
]);

Different session IDs have independent execution queues. Turns within one session remain serialized so conversation state cannot be mutated by two executions at once.

startMany() is parallel, not transactional. If one start fails, another may already exist. Use stable IDs, then inspect list() or get() before retrying.

Durable identity and exact retry behavior

A caller-selected Agent ID is the durable session ID.

  • start({ id }) creates a new generation.
  • get(id) reattaches from any SDK process.
  • The server serializes competing creates for the same ID.
  • An exact replay of the same validated create request returns the existing session.
  • The same ID with a changed profile, model, credential, title, or other create input returns 409 SESSION_CREATE_CONFLICT.

The server—not a client cache—owns correctness.

Shared workspaces

Shared mode is the default. Agents see the same project files immediately.

ts
const reviewer = await box.agents.start({
  id: "reviewer-shared",
  backend: { type: "codex", profile: reviewerProfile },
});

This is ordinary same-computer behavior. It does not merge simultaneous edits and is not a security boundary. Coordinate writes or use file preconditions when more than one actor can edit the same path.

Isolated Git worktrees

Use an isolated workspace when independent changes or different harness control files must coexist:

ts
const reviewer = await box.agents.start({
  id: "reviewer-v1",
  backend: { type: "codex", profile: reviewerProfile },
  workspace: {
    mode: "isolated",
    parentSessionId: "builder-v1",
  },
});

const changes = await reviewer.changes();
console.log(changes.diff);

await reviewer.commit({
  message: "Apply reviewer corrections",
});

An isolated Agent is a SandboxTaskSession. It requires a Git repository.

Lifecycle is explicit:

  • commit() merges or applies the selected changes, then removes the owned worktree and task branch.
  • The task rollback route discards changes, then removes the worktree and task branch.
  • box.agents.stop(id) deletes the session. For an isolated Agent it first removes the owned worktree and task branch. If cleanup cannot be proven, deletion fails and the durable session record retains the identifiers for a safe retry.

Stopping an isolated Agent without committing intentionally discards its unmerged work.

Reattach after a restart

ts
const reloaded = await client.get(box.id);
if (!reloaded) throw new Error("Sandbox no longer exists");

const planner = await reloaded.agents.get("planner-v1");
if (!planner) throw new Error("planner-v1 no longer exists");

console.log(await planner.status());

For a known isolated session:

ts
const reviewer = reloaded.agents.task("reviewer-v1");
console.log(await reviewer.changes());

task(id) returns a typed handle without a network lookup; its first operation validates the durable task session.

Profile upgrades

Do not hot-mutate an active AgentProfile. Create a new session generation:

ts
const candidate = await box.agents.start({
  id: "planner-v2-candidate",
  backend: { type: "opencode", profile: updatedPlannerProfile },
  workspace: {
    mode: "isolated",
    parentSessionId: "planner-v1",
  },
});

await candidate.prompt("Verify the new profile against this repository.");

// Promote only after verification.
await box.agents.stop("planner-v1");
const plannerV2 = await box.agents.start({
  id: "planner-v2",
  backend: { type: "opencode", profile: updatedPlannerProfile },
});

Use a new ID so observers can distinguish generations and an active turn never changes authority underneath itself.

Authentication and delegation

Keep TANGLE_API_KEY and box.agents lifecycle operations on a trusted backend. Browser clients receive short-lived scoped tokens for the exact session or read surface they need.

A session-bound token cannot enumerate or operate a sibling session. A delegated token never becomes a general Agent manager. See Authentication and browser access.

Trust and resources

Sibling Agents share a broad trust domain:

  • project files and local network;
  • processes and terminals;
  • CPU, memory, disk, and accelerator pressure;
  • Sandbox-visible secrets.

Use separate Sandboxes for different tenants, mutually untrusted Agents, separate secret domains, hard resource isolation, or independent failure boundaries.

Branching is not one operation

State being copiedAPI
Conversation historysession.fork()
File changes inside one Sandboxisolated Agent / SandboxTaskSession
Complete filesystembox.snapshot() and restore/create

Cleanup

ts
try {
  // Drive the Agent workflow.
} finally {
  await box.delete();
}

stop(id) deletes one Agent session. box.delete() deletes the entire computer and every session inside it.