PAIPAI

@pai/sandbox-local

Trusted local-process SandboxProvider implementation for local coding agents and demos.

Package: @pai/sandbox-local

Implements the SandboxProvider contract against a local directory and, when explicitly enabled, the host shell. It is for local coding agents, demos, and development workflows where the agent is meant to work in a real checkout. It is not a security sandbox.

Quick start

import { createAgentRuntime } from "@pai/core";
import { sandboxCapability } from "@pai/sandbox";
import { createLocalSandboxProvider } from "@pai/sandbox-local";

const runtime = createAgentRuntime({
  agent, // tools: { ...sandboxTools() }
  capabilities: {
    sandbox: sandboxCapability({
      provider: createLocalSandboxProvider({
        rootDir: process.cwd(),
        unsafeAllowHostCommands: true,
      }),
    }),
  },
});

Options

type LocalSandboxProviderOptions = {
  /** Host directory exposed as /workspace. Defaults to a temp directory. */
  rootDir?: string;
  /** Working directory when `runCommand.cwd` is omitted. Defaults to /workspace. */
  defaultCwd?: string;
  /** Idle lifetime for sandboxes created without `ttlMs`. Defaults to 5 minutes. */
  defaultTtlMs?: number;
  /** Required before `runCommand` will execute anything. */
  unsafeAllowHostCommands?: boolean;
  /** Inherit `process.env` for commands. Defaults to true. */
  inheritEnv?: boolean;
  /** Applied to every command. */
  env?: Record<string, string>;
  /** Provider-owned state. Defaults to `.pai/sandboxes/local` under the root. */
  stateDir?: string;
  /** Shell for commands. Defaults to `$SHELL`, then Node's default. */
  shell?: string;
  now?: () => number;
  generateId?: () => string;
};

Behaviour notes

  • Commands are opt-in. Without unsafeAllowHostCommands: true, runCommand throws a TypeError rather than running anything. The flag is named to stop a caller mistaking this for isolated execution.
  • Files map onto rootDir. It is exposed as /workspace, and absolute host paths under it also resolve, which is what a local coding agent needs. /tmp is a provider-owned per-sandbox directory.
  • kill() removes provider-owned temp state and never deletes workspace files. connect() and list() rediscover live sandboxes from a registry file under stateDir, so a restarted process finds them again.
  • Only { access: "full" } is accepted. Host execution cannot enforce egress control, so create rejects any other explicit network policy rather than pretending to honour it.
  • probe() checks rootDir, and creates nothing. That directory is all a local sandbox depends on, so a root that is missing, is not a directory, or cannot be written into is SandboxUnavailableError. With no rootDir configured there is nothing to check, because create mints a fresh temporary one.
  • resources is rejected, for the same reason.
  • bindingKey is ignored, so concurrent creates for one key can produce two sandboxes.

Security contract

Commands run on the host as the current user, with the same files, network, credentials, tools, and environment the process has. The provider constrains readFile, writeFile, and listDir to the configured workspace and rejects symlink and traversal escapes, but that containment does not extend to arbitrary shell commands. For untrusted code or customer data use an isolated provider: @pai/sandbox-e2b, @pai/sandbox-gke, or @pai/sandbox-docker.

On this page