Using Sandboxes — Agentuity Documentation

Using Sandboxes

Run code in isolated containers with ctx.sandbox

Execute code in isolated, ephemeral containers with configurable runtimes, networking, and file access. The sandbox service is available via ctx.sandbox in agents and c.var.sandbox in routes.

For patterns, one-shot vs interactive workflows, and snapshot configuration, see Sandbox SDK Usage.

run(options)

Execute a single command in a disposable sandbox. The sandbox is automatically created and destroyed after execution completes. Use this for stateless, one-shot tasks.

ParamTypeRequiredDescription
optionsSandboxRunOptionsyesExecution options (see below)

SandboxRunOptions fields:

FieldTypeRequiredDescription
commandobjectyes{ exec: string[], files?: FileToWrite[] }
runtimestringnoRuntime name (e.g., 'bun:1', 'python:3.14')
runtimeIdstringnoRuntime ID (e.g., 'srt_xxx')
namestringnoSandbox name
descriptionstringnoSandbox description
resourcesSandboxResourcesnoCPU, memory, and disk limits
envRecord<string, string>noEnvironment variables
networkSandboxNetworkConfigno{ enabled?: boolean, port?: number }
timeoutSandboxTimeoutConfigno{ idle?: string, execution?: string }
snapshotstringnoSnapshot ID or tag to restore from
dependenciesstring[]noApt packages to install
packagesstring[]nonpm/bun packages to install globally
metadataRecord<string, unknown>noUser-defined metadata

Returns: Promise<SandboxRunResult>

interface SandboxRunResult {
  sandboxId: string;     // Sandbox ID
  exitCode: number;      // Process exit code
  durationMs: number;    // Execution time in milliseconds
  stdout?: string;       // Captured stdout
  stderr?: string;       // Captured stderr
}

Example

import { createAgent } from '@agentuity/runtime';
import { z } from 'zod';
 
const agent = createAgent('CodeRunner', {
  schema: {
    input: z.object({ code: z.string() }),
    output: z.object({ output: z.string(), exitCode: z.number() }),
  },
  handler: async (ctx, input) => {
    const result = await ctx.sandbox.run({
      runtime: 'bun:1',
      command: {
        exec: ['bun', 'run', 'script.ts'],
        files: [
          { path: 'script.ts', content: Buffer.from(input.code) },
        ],
      },
      timeout: { execution: '30s' },
    });
 
    ctx.logger.info('Exit code: %d, duration: %dms', result.exitCode, result.durationMs);
    return { output: result.stdout ?? '', exitCode: result.exitCode };
  },
});

create(options?)

Create an interactive sandbox that persists across multiple commands. Use this for multi-step workflows where you need to inspect state between commands.

ParamTypeRequiredDescription
optionsSandboxCreateOptionsnoCreation options (see run() fields above, plus command and files)

Additional fields beyond run() options:

FieldTypeDescription
commandSandboxCommandInitial command to execute, with optional mode: 'oneshot' | 'interactive'
filesFileToWrite[]Files to write to the workspace on creation

Returns: Promise<Sandbox>

The Sandbox object provides methods for interacting with the running container:

interface Sandbox {
  id: string;
  status: string;
  name?: string;
  description?: string;
  stdout: StreamReader;         // Read-only stdout stream
  stderr: StreamReader;         // Read-only stderr stream
  interleaved: boolean;         // True if stdout/stderr share a stream
 
  execute(options: ExecuteOptions): Promise<Execution>;
  writeFiles(files: FileToWrite[]): Promise<void>;
  readFile(path: string): Promise<ReadableStream<Uint8Array>>;
  listFiles(path?: string): Promise<SandboxFileInfo[]>;
  mkDir(path: string, recursive?: boolean): Promise<void>;
  rmFile(path: string): Promise<void>;
  rmDir(path: string, recursive?: boolean): Promise<void>;
  setEnv(env: Record<string, string | null>): Promise<Record<string, string>>;
  pause(): Promise<void>;
  resume(): Promise<void>;
  destroy(): Promise<void>;
}

Example

const sandbox = await ctx.sandbox.create({
  runtime: 'bun:1',
  network: { enabled: true },
  timeout: { idle: '10m' },
});
 
try {
  // Write files and execute commands
  await sandbox.writeFiles([
    { path: 'index.ts', content: Buffer.from('console.log("hello")') },
  ]);
 
  const exec = await sandbox.execute({
    command: ['bun', 'run', 'index.ts'],
  });
 
  ctx.logger.info('Exit code: %d', exec.exitCode);
} finally {
  await sandbox.destroy();
}

connect(sandboxId)

Get a full Sandbox instance for an existing sandbox by ID. Useful for reconnecting to a sandbox created earlier or in a different handler.

ParamTypeRequiredDescription
sandboxIdstringyesSandbox ID to connect to

Returns: Promise<Sandbox>

Example

// Reconnect to a previously created sandbox
const sandbox = await ctx.sandbox.connect('sbx_abc123');
const files = await sandbox.listFiles('/home/agentuity');
ctx.logger.info('Found %d files', files.length);

get(sandboxId)

Get sandbox metadata without creating a full interactive connection.

ParamTypeRequiredDescription
sandboxIdstringyesSandbox ID

Returns: Promise<SandboxInfo> -- metadata including status, runtime, creation time, and resource configuration.

list(params?)

List sandboxes with optional filtering and pagination.

ParamTypeRequiredDescription
paramsListSandboxesParamsnoPagination and filter options

Returns: Promise<ListSandboxesResponse> with sandboxes array and total count.

destroy(sandboxId)

Permanently destroy a sandbox and release its resources.

ParamTypeRequiredDescription
sandboxIdstringyesSandbox ID

Returns: Promise<void>

pause(sandboxId)

Pause a running sandbox, creating a checkpoint of its current state. The sandbox can be resumed later with resume().

ParamTypeRequiredDescription
sandboxIdstringyesSandbox ID

Returns: Promise<void>

resume(sandboxId)

Resume a paused or evacuated sandbox from its checkpoint.

ParamTypeRequiredDescription
sandboxIdstringyesSandbox ID

Returns: Promise<void>

Running Background Jobs

Run processes that outlive a single execute() call. Jobs continue in the background and can be monitored or stopped.

createJob(options)

Start a long-running job in the sandbox.

ParamTypeRequiredDescription
optionsCreateJobOptionsyesJob configuration
options.commandstring[]yesCommand and arguments to execute
options.streamsobjectnoStream configuration for output capture
options.streams.stdoutstringnoStream ID for stdout output
options.streams.stderrstringnoStream ID for stderr output

Returns: Promise<Job>

interface Job {
  jobId: string;
  sandboxId: string;
  command: string[];
  status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
  exitCode?: number | null;
  startedAt?: string | null;
  completedAt?: string | null;
  error?: string | null;
  stdoutStreamUrl?: string | null;
  stderrStreamUrl?: string | null;
}

Example

const sandbox = await ctx.sandbox.create({ runtime: 'bun:1' });
 
const job = await sandbox.createJob({
  command: ['bun', 'run', 'server.ts'],
});
 
ctx.logger.info('Job started: %s (status: %s)', job.jobId, job.status);

getJob(jobId)

Get current status and details of a job.

ParamTypeRequiredDescription
jobIdstringyesJob identifier

Returns: Promise<Job>

Example

const job = await sandbox.getJob(jobId);
ctx.logger.info('Job %s: %s (exit: %d)', job.jobId, job.status, job.exitCode);

listJobs(limit?)

List jobs in the sandbox.

ParamTypeRequiredDescription
limitnumbernoMaximum number of jobs to return

Returns: Promise<{ jobs: Job[] }>

Example

const { jobs } = await sandbox.listJobs();
for (const job of jobs) {
  ctx.logger.info('%s: %s', job.jobId, job.status);
}

stopJob(jobId, force?)

Stop a running job. By default sends SIGTERM for graceful shutdown. Use force for SIGKILL.

ParamTypeRequiredDescription
jobIdstringyesJob identifier
forcebooleannoUse SIGKILL instead of SIGTERM

Returns: Promise<Job> (updated job with new status)

Example

// Graceful stop
const stopped = await sandbox.stopJob(job.jobId);
 
// Force kill
const killed = await sandbox.stopJob(job.jobId, true);

Snapshots

Snapshots capture the state of a sandbox for reuse. Access snapshot operations via ctx.sandbox.snapshot.

snapshot.create(sandboxId, options?)

Create a snapshot from a running sandbox.

ParamTypeRequiredDescription
sandboxIdstringyesSandbox ID to snapshot
optionsSnapshotCreateOptionsnoSnapshot metadata

SnapshotCreateOptions fields (all optional):

FieldTypeDescription
namestringDisplay name (letters, numbers, underscores, dashes)
descriptionstringSnapshot description
tagstringVersion tag (defaults to 'latest')
publicbooleanMake publicly accessible

Returns: Promise<SnapshotInfo>

interface SnapshotInfo {
  snapshotId: string;
  name: string;
  description?: string;
  tag?: string;
  sizeBytes: number;
  fileCount: number;
  createdAt: string;
  public?: boolean;
}

snapshot.get(snapshotId)

Get snapshot details. Returns: Promise<SnapshotInfo>

snapshot.list(params?)

List snapshots with optional filtering.

ParamTypeRequiredDescription
paramsSnapshotListParamsnoFilter and pagination options

SnapshotListParams fields (all optional):

FieldTypeDescription
sandboxIdstringFilter by sandbox ID
limitnumberMax results
offsetnumberResults to skip
sortstringSort field
direction'asc' | 'desc'Sort direction (default 'desc')

Returns: Promise<SnapshotListResponse> with snapshots array and total count.

snapshot.delete(snapshotId)

Delete a snapshot. Returns: Promise<void>

snapshot.tag(snapshotId, tag)

Update the tag on a snapshot. Pass null to remove the tag.

ParamTypeRequiredDescription
snapshotIdstringyesSnapshot ID
tagstring | nullyesNew tag, or null to clear

Returns: Promise<SnapshotInfo>

Error Handling

The sandbox API throws specific errors for common failure modes:

  • SandboxNotFoundError: Thrown when the sandbox returns HTTP 404. This can happen if the sandbox was destroyed or is still initializing. The error includes the sandbox ID for debugging.
try {
  const result = await sandbox.execute({ command: ['echo', 'hello'] });
} catch (error) {
  if (error._tag === 'SandboxNotFoundError') {
    ctx.logger.warn('Sandbox not ready, may still be initializing');
  }
}