{
  "site": "https://docs.alineo.tech",
  "pages": [
    {
      "url": "https://docs.alineo.tech/docs/core/ai-resources",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/ai-resources.md",
      "title": "AI Resources",
      "description": "Use the alineo docs with LLMs and coding agents — llms.txt, per-page Markdown, and agent skills.",
      "headings": [
        "llms.txt",
        "Per-page Markdown",
        "MCP server",
        "Agent skills"
      ],
      "sections": [
        {
          "heading": null,
          "content": "The entire alineo documentation is published in formats built for LLMs and coding\nagents, not just browsers. These cover every section — Core SDK, Workflow Builder,\nAgent SDK, and the CLI."
        },
        {
          "heading": "llmstxt",
          "content": "`/llms.txt` is a structured index of every\ndocumentation page, each entry linking to that page's Markdown form. Point an agent\nat it to let it discover and pull only the pages it needs."
        },
        {
          "heading": "llmstxt",
          "content": "`/llms-full.txt` is the entire\ndocumentation concatenated into a single Markdown file — drop it into a context\nwindow whole."
        },
        {
          "heading": "per-page-markdown",
          "content": "Every documentation page has a clean Markdown version at the same path under\n`/llms.mdx/`. For example:"
        },
        {
          "heading": "per-page-markdown",
          "content": "At the top of any page, the **Copy Markdown** button copies that Markdown to your\nclipboard, and the **Open** menu opens the page directly in ChatGPT, Claude, or\nCursor, or shows it as raw Markdown."
        },
        {
          "heading": "per-page-markdown",
          "content": "When citing or linking a page, use its canonical URL — the one without the `.md`\nsuffix."
        },
        {
          "heading": "mcp-server",
          "content": "`https://docs.alineo.tech/mcp` is a Model Context Protocol\nserver that lets an MCP client search and read these docs from inside your editor or\nagent. It exposes three tools: `search_docs`, `get_doc`, and `list_docs`. It's\nread-only and covers documentation only."
        },
        {
          "heading": "mcp-server",
          "content": "Add alineo docs to Cursor"
        },
        {
          "heading": "mcp-server",
          "content": "Or add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project):"
        },
        {
          "heading": "mcp-server",
          "content": "Add to `opencode.json`:"
        },
        {
          "heading": "mcp-server",
          "content": "Any MCP client that supports a remote (HTTP) server: point it at\n`https://docs.alineo.tech/mcp`. No authentication."
        },
        {
          "heading": "mcp-server",
          "content": "This is separate from the alineo SDK/CLI itself, which is what actually *runs*\nsandboxed agents."
        },
        {
          "heading": "agent-skills",
          "content": "Install the alineo skill so your coding agent follows the\nSDK's conventions and knows where to look in the docs:"
        },
        {
          "heading": "agent-skills",
          "content": "Two skills are published from the repo:"
        },
        {
          "heading": "agent-skills",
          "content": "Skill"
        },
        {
          "heading": "agent-skills",
          "content": "Covers"
        },
        {
          "heading": "agent-skills",
          "content": "`alineo`"
        },
        {
          "heading": "agent-skills",
          "content": "The agent SDK (`load` / `resume` / `attach` / `spawn`, prompt & bash streaming, session control) and the `alineo` CLI."
        },
        {
          "heading": "agent-skills",
          "content": "`bun`"
        },
        {
          "heading": "agent-skills",
          "content": "The Bun runtime, package manager, test runner, and bundler used across the repo."
        },
        {
          "heading": "agent-skills",
          "content": "Add `--skill bun` (or omit `--skill` to install both). The source lives in\n`.agents/skills/` in the\nrepo."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core.md",
      "title": "Core SDK",
      "description": "The @alineo-labs/sandbox package — sandboxes, exec, file ops, snapshots, storage adapters, and error handling.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/getting-started\" title=\"Getting Started\" description=\"Install @alineo-labs/sandbox, run your first sandbox, and understand the core model.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts\" title=\"Concepts\" description=\"Sandboxes, ExecHandle, the event stream, and storage adapters.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/building\" title=\"Building\" description=\"exec, file operations, and snapshots.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/patterns\" title=\"Patterns\" description=\"Error handling, timeouts, run management, and observability.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/adapters\" title=\"Storage Adapters\" description=\"SQLite for local dev, Postgres for production, or bring your own.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/api-reference\" title=\"API Reference\" description=\"Complete reference for Sandbox, SandboxHandle, ExecHandle, and errors.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/adapters/custom",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/adapters/custom.md",
      "title": "Custom adapter",
      "description": "Implement IStorageAdapter to use any storage backend with @alineo-labs/sandbox.",
      "headings": [
        "Interface",
        "Method reference",
        "LedgerEntry",
        "Example: in-memory adapter",
        "Using a custom adapter"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`IStorageAdapter` is the interface that both `SQLiteAdapter` and `PostgresAdapter` implement. You can implement it yourself to use any storage backend — Redis, DynamoDB, a REST API, etc."
        },
        {
          "heading": "interface",
          "content": "Import types from `@alineo-labs/core`:"
        },
        {
          "heading": "method-reference",
          "content": "Method"
        },
        {
          "heading": "method-reference",
          "content": "Required"
        },
        {
          "heading": "method-reference",
          "content": "Description"
        },
        {
          "heading": "method-reference",
          "content": "`connect()`"
        },
        {
          "heading": "method-reference",
          "content": "No"
        },
        {
          "heading": "method-reference",
          "content": "Initialize connections. Called lazily, once, the first time the client needs the adapter — not by any method you call yourself."
        },
        {
          "heading": "method-reference",
          "content": "`close()`"
        },
        {
          "heading": "method-reference",
          "content": "No"
        },
        {
          "heading": "method-reference",
          "content": "Release connections. Called when the event loop drains naturally (`beforeExit`) — long-running servers never reach this, so the pool stays open for the process lifetime by design."
        },
        {
          "heading": "method-reference",
          "content": "`append(entry)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Persist a single ledger event. Called during every live exec."
        },
        {
          "heading": "method-reference",
          "content": "`readAll(name, sandboxId)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return all events for a session in ascending `ts` order. Used by `resume()`."
        },
        {
          "heading": "method-reference",
          "content": "`lastCheckpoint(name, sandboxId)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return the most recent `checkpoint_created` entry, or `null`."
        },
        {
          "heading": "method-reference",
          "content": "`listSandboxDetails(name, opts?)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return sessions with this name, newest first."
        },
        {
          "heading": "method-reference",
          "content": "`listAllSandboxDetails(opts?)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return all sessions across all names, newest first."
        },
        {
          "heading": "method-reference",
          "content": "`getSandboxDetails(name, sandboxId)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return a single session, or `null`."
        },
        {
          "heading": "method-reference",
          "content": "`deleteSandbox(name, sandboxId)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Delete all events for a session."
        },
        {
          "heading": "method-reference",
          "content": "`listCheckpoints(name, sandboxId)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return all checkpoints for a session, in creation order."
        },
        {
          "heading": "method-reference",
          "content": "`getEnvironment(name)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return the cached record for a named environment, or `null` if not built yet."
        },
        {
          "heading": "method-reference",
          "content": "`saveEnvironment(record)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Upsert an environment record after a successful build."
        },
        {
          "heading": "method-reference",
          "content": "`deleteEnvironment(name)`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Remove the record for a named environment. Does not delete the server-side snapshot."
        },
        {
          "heading": "method-reference",
          "content": "`listEnvironments()`"
        },
        {
          "heading": "method-reference",
          "content": "Yes"
        },
        {
          "heading": "method-reference",
          "content": "Return all environment records, newest first."
        },
        {
          "heading": "example-in-memory-adapter",
          "content": "Useful for testing:"
        },
        {
          "heading": "using-a-custom-adapter",
          "content": "Pass it to `Sandbox` the same way as the built-in adapters:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/adapters",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/adapters.md",
      "title": "Storage Adapters",
      "description": "Choose where @alineo-labs/sandbox persists your run ledger.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/adapters/sqlite\" title=\"SQLite\" description=\"Zero-config, WAL mode. The right default for local dev and single-process deploys.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/adapters/postgres\" title=\"Postgres\" description=\"For production multi-process deployments with a shared ledger.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/adapters/custom\" title=\"Custom adapter\" description=\"Implement IStorageAdapter to use any storage backend.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/adapters/postgres",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/adapters/postgres.md",
      "title": "Postgres adapter",
      "description": "Shared ledger for production multi-process deployments.",
      "headings": [
        "Install",
        "Usage",
        "Constructor",
        "Connection string format",
        "Migrations",
        "When to use",
        "Environment-based config",
        "When to use SQLite instead"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`@alineo-labs/postgres` stores the run ledger in a Postgres database. Use it when multiple processes need to share the same ledger, or when you need persistent storage on platforms without local disk."
        },
        {
          "heading": "constructor",
          "content": "Argument"
        },
        {
          "heading": "constructor",
          "content": "Type"
        },
        {
          "heading": "constructor",
          "content": "Description"
        },
        {
          "heading": "constructor",
          "content": "`connectionString`"
        },
        {
          "heading": "constructor",
          "content": "`string`"
        },
        {
          "heading": "constructor",
          "content": "Postgres connection string, e.g. `\"postgres://user:pass@host:5432/dbname\"`"
        },
        {
          "heading": "connection-string-format",
          "content": "The adapter uses the `postgres` package internally, which also respects the `PGPASSWORD`, `PGUSER`, `PGHOST`, `PGPORT`, and `PGDATABASE` environment variables if you prefer to configure via env."
        },
        {
          "heading": "migrations",
          "content": "Migrations run automatically as `CREATE TABLE IF NOT EXISTS` the first time the adapter is used — safe on every startup, no migration tool required."
        },
        {
          "heading": "migrations",
          "content": "Schema created:"
        },
        {
          "heading": "when-to-use",
          "content": "**Production deployments** — multiple workers sharing a ledger"
        },
        {
          "heading": "when-to-use",
          "content": "**Cloud environments** — no persistent local disk available"
        },
        {
          "heading": "when-to-use",
          "content": "**Shared visibility** — multiple services or dashboards reading run history"
        },
        {
          "heading": "environment-based-config",
          "content": "For production, set `DATABASE_URL` in your environment and never hardcode credentials."
        },
        {
          "heading": "when-to-use-sqlite-instead",
          "content": "For local development and single-process scripts, `@alineo-labs/sqlite` is simpler — no database server required. See SQLite adapter."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/adapters/sqlite",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/adapters/sqlite.md",
      "title": "SQLite adapter",
      "description": "Zero-config storage with WAL mode. The right default for local dev and single-process deploys.",
      "headings": [
        "Install",
        "Usage",
        "Constructor",
        "WAL mode",
        "When to use",
        "When to switch to Postgres",
        "Schema"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`@alineo-labs/sqlite` is the recommended adapter for local development and single-process applications. It requires no external services — just a file path."
        },
        {
          "heading": "usage",
          "content": "`CREATE TABLE IF NOT EXISTS` migrations run automatically on first use, so it's safe to construct the adapter fresh on every startup."
        },
        {
          "heading": "constructor",
          "content": "Argument"
        },
        {
          "heading": "constructor",
          "content": "Type"
        },
        {
          "heading": "constructor",
          "content": "Description"
        },
        {
          "heading": "constructor",
          "content": "`path`"
        },
        {
          "heading": "constructor",
          "content": "`string`"
        },
        {
          "heading": "constructor",
          "content": "File path for the SQLite database. Use `\":memory:\"` for an in-memory database (data lost on close)."
        },
        {
          "heading": "wal-mode",
          "content": "`SQLiteAdapter` enables WAL (Write-Ahead Logging) mode automatically the first time it's used. WAL mode prevents writers from blocking readers, so multiple concurrent sandbox sessions in the same process are safe."
        },
        {
          "heading": "when-to-use",
          "content": "**Local development** — no infra required, ledger is a file you can inspect"
        },
        {
          "heading": "when-to-use",
          "content": "**Single-process scripts** — CLI tools, one-off runs"
        },
        {
          "heading": "when-to-use",
          "content": "**Testing** — use `\":memory:\"` for a clean database per test run"
        },
        {
          "heading": "when-to-switch-to-postgres",
          "content": "Switch to `@alineo-labs/postgres` when:"
        },
        {
          "heading": "when-to-switch-to-postgres",
          "content": "Multiple processes need to share the same ledger"
        },
        {
          "heading": "when-to-switch-to-postgres",
          "content": "You're deploying to a platform without persistent local disk"
        },
        {
          "heading": "when-to-switch-to-postgres",
          "content": "You need to query ledger data with SQL from external tools"
        },
        {
          "heading": "schema",
          "content": "The following is created automatically on first use:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/api-reference/alineo-client",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/api-reference/alineo-client.md",
      "title": "Sandbox",
      "description": "The main client — sandbox(), resume(), restoreSnapshot(), connect(), and sandboxes management.",
      "headings": [
        "Constructor",
        "SandboxClientOptions",
        "Methods",
        "sandbox()",
        "resume()",
        "restoreSnapshot()",
        "connect()",
        "sandboxes",
        "ListSandboxOptions",
        "SandboxHandle class",
        "SandboxHandle methods",
        "sb.credentials",
        "SandboxHandle properties"
      ],
      "sections": [
        {
          "heading": "sandboxclientoptions",
          "content": "Option"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Type"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Required"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Description"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`baseUrl`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`string`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Yes"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "OpenSandbox server URL, e.g. `\"http://localhost:8080\"`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`apiKey`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`string`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "No"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "OpenSandbox API key. Empty string for local dev with no auth."
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`adapter`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`IStorageAdapter`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Yes"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Storage adapter for the run ledger"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`maxConcurrency`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`number`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "No"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Max simultaneous active sandboxes. Omit for no limit."
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`useServerProxy`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`boolean`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "No"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Route execd and proxy traffic through the server. Required when the server runs in Docker via `alineo init`. Defaults to `false`."
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`credentialBroker`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "`CredentialBroker`"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "No"
        },
        {
          "heading": "sandboxclientoptions",
          "content": "Backend for `sb.credentials.*`. Defaults to `OpenSandboxCredentialBroker` (`@alineo-labs/vault`) — see Credentials."
        },
        {
          "heading": "sandbox",
          "content": "Creates a container, waits until it reaches `Running` state, and returns a live `SandboxHandle` object. Writes a `sandbox_created` event to the ledger."
        },
        {
          "heading": "sandbox",
          "content": "If `maxConcurrency` is set, awaits a slot before creating the container. The slot is released when `sb.close()` is called."
        },
        {
          "heading": "sandbox",
          "content": "See Sandboxes for `SandboxOptions` reference."
        },
        {
          "heading": "resume",
          "content": "Restores a sandbox session from its last checkpoint (or the checkpoint matching `opts.tag`, if given):"
        },
        {
          "heading": "resume",
          "content": "Reads the ledger to find the last `checkpoint_created` event (or the last one matching `opts.tag`)"
        },
        {
          "heading": "resume",
          "content": "Creates a new container from the snapshot"
        },
        {
          "heading": "resume",
          "content": "Populates a replay cache from execs that completed before the checkpoint"
        },
        {
          "heading": "resume",
          "content": "Re-registers any credentials the sandbox had bound (see Credentials) — `opts.resolveCredential` supplies values that can't be re-derived automatically"
        },
        {
          "heading": "resume",
          "content": "Returns a `SandboxHandle` — pre-checkpoint execs return cached results; later execs run live"
        },
        {
          "heading": "resume",
          "content": "Throws `SandboxClientError` (404) if the session is not found or has no matching checkpoint. Throws `SandboxError` if a bound credential can't be resolved."
        },
        {
          "heading": "restoresnapshot",
          "content": "Creates a fresh sandbox from a snapshot ID **without** exec replay — unlike `resume()`, the new sandbox starts with a clean exec history; nothing from the ledger is replayed. Use this when you want to restore a checkpointed environment and run new commands from scratch, rather than continue exactly where the checkpoint left off."
        },
        {
          "heading": "restoresnapshot",
          "content": "This is also what `sb.fork()` uses under the hood — see Forking sandboxes for the relationship between `checkpoint()`, `fork()`, `resume()`, and `restoreSnapshot()`."
        },
        {
          "heading": "connect",
          "content": "Attaches to an already-`Running` sandbox by ID, no snapshot or ledger replay involved — the container must already be running (throws `SandboxClientError` (409) otherwise). Use this to reconnect to a live sandbox created outside the current process, e.g. from a saved `sandboxId`."
        },
        {
          "heading": "connect",
          "content": "`opts.resources` is only needed if you intend to call `.fork()` on the returned `SandboxHandle` afterwards — the control API doesn't echo back a running sandbox's own resource limits, so there's no way to discover them here; without it, `.fork()` is unavailable on the returned object."
        },
        {
          "heading": "sandboxes",
          "content": "Access to the ledger's session history."
        },
        {
          "heading": "listsandboxoptions",
          "content": "Option"
        },
        {
          "heading": "listsandboxoptions",
          "content": "Type"
        },
        {
          "heading": "listsandboxoptions",
          "content": "Description"
        },
        {
          "heading": "listsandboxoptions",
          "content": "`status`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "`SandboxStatus`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "Filter by status: `\"running\"` or `\"completed\"`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "`limit`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "`number`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "Max results to return"
        },
        {
          "heading": "listsandboxoptions",
          "content": "`before`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "`number`"
        },
        {
          "heading": "listsandboxoptions",
          "content": "Return only sessions started before this Unix timestamp (ms)"
        },
        {
          "heading": "sandboxhandle-class",
          "content": "See SandboxHandle below for the object returned by `sandbox()` and `resume()`."
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Method"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Returns"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Description"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.exec(cmd, opts?)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`ExecHandle`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Run a shell command"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.execCode(code, opts?)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`ExecHandle`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Run code via the interpreter"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.proxy(port)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<{ url, headers }>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Get a proxied URL for an in-sandbox port"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.metrics()`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<{ cpu, memory, timestamp }>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Current CPU and memory usage"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.writeFile(path, content)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Write a UTF-8 file into the container"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.readFile(path)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<string>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Read a file from the container as a string"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.moveFile(from, to)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Move or rename a file"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.deleteFile(path)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Delete a file"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.createDirectory(path)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Create a directory (and parents)"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.deleteDirectory(path)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Delete a directory"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.getFileInfo(path)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<FileInfo>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "File metadata: size, type, mode, timestamps"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.replaceInFiles(replacements)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "In-place substring replacement across files"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.transfer(path, target)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Copy a file to another `SandboxHandle` instance"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.searchFiles(pattern, path?)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<string[]>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Search for files matching a glob"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.listDirectory(path, opts?)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<FileInfo[]>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "List directory entries with metadata"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.checkpoint(name?)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<string>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Snapshot the container state, returns the snapshot ID"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.listCheckpoints()`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<CheckpointInfo[]>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "All checkpoints for this sandbox"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.fork(tag?, runId?, opts?)`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<SandboxHandle>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Snapshot and return an independent copy"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`sb.close()`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "`Promise<void>`"
        },
        {
          "heading": "sandboxhandle-methods",
          "content": "Delete the container and release resources"
        },
        {
          "heading": "sbcredentials",
          "content": "Register, update, remove, and list credentials injected into this sandbox's outbound requests — requires the sandbox to have been created with `credentialProxy: true`. See Credentials."
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "Property"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "Type"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "Description"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "`sb.sandboxId`"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "`string`"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "OpenSandbox container ID"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "`sb.name`"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "`string`"
        },
        {
          "heading": "sandboxhandle-properties",
          "content": "User-provided name or auto-generated"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/api-reference/errors",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/api-reference/errors.md",
      "title": "Errors",
      "description": "CommandError, SandboxError, ExecConnectionError, WorkflowError, and SandboxClientError — the full alineo error hierarchy.",
      "headings": [
        "Error hierarchy",
        "WorkflowError",
        "CommandError",
        "Properties",
        "Avoiding CommandError",
        "SandboxError",
        "Properties",
        "ExecConnectionError",
        "Properties",
        "SandboxClientError"
      ],
      "sections": [
        {
          "heading": "error-hierarchy",
          "content": "`SandboxError`, `ExecConnectionError`, and `CommandError` all extend `WorkflowError`, which extends `Error` — catch all three with `e instanceof WorkflowError`. `SandboxClientError` is a separate, sibling class thrown by `Sandbox` client methods (`sandbox()`, `resume()`, `sandboxes.*`, `environment()`) rather than by `Sandbox` methods — see SandboxClientError below."
        },
        {
          "heading": "workflowerror",
          "content": "Base class. Has `message` and `name` properties. `name` is always `\"WorkflowError\"`."
        },
        {
          "heading": "commanderror",
          "content": "Thrown when `sb.exec()` exits with a non-zero code and `strict: true` (the default)."
        },
        {
          "heading": "properties",
          "content": "Property"
        },
        {
          "heading": "properties",
          "content": "Type"
        },
        {
          "heading": "properties",
          "content": "Description"
        },
        {
          "heading": "properties",
          "content": "`exitCode`"
        },
        {
          "heading": "properties",
          "content": "`number`"
        },
        {
          "heading": "properties",
          "content": "The process exit code"
        },
        {
          "heading": "properties",
          "content": "`command`"
        },
        {
          "heading": "properties",
          "content": "`string`"
        },
        {
          "heading": "properties",
          "content": "The command string that was run"
        },
        {
          "heading": "properties",
          "content": "`sandboxId`"
        },
        {
          "heading": "properties",
          "content": "`string`"
        },
        {
          "heading": "properties",
          "content": "The sandbox ID where it ran"
        },
        {
          "heading": "properties",
          "content": "`message`"
        },
        {
          "heading": "properties",
          "content": "`string`"
        },
        {
          "heading": "properties",
          "content": "`\"Command exited with code N: <cmd>\"`"
        },
        {
          "heading": "properties",
          "content": "`name`"
        },
        {
          "heading": "properties",
          "content": "`string`"
        },
        {
          "heading": "properties",
          "content": "`\"CommandError\"`"
        },
        {
          "heading": "avoiding-commanderror",
          "content": "Pass `{ strict: false }` to get the exit code in the result:"
        },
        {
          "heading": "sandboxerror",
          "content": "Thrown by methods on `SandboxHandle` itself: `resume()` if the container never reaches `Running` (or enters `Failed`/`Terminated`), an exec call while the sandbox is paused, `fork()` when the deps don't support it, or a failed snapshot wait."
        },
        {
          "heading": "sandboxerror",
          "content": "A failure during `client.sandbox()`'s own initial creation throws `SandboxClientError`, not `SandboxError` — see below."
        },
        {
          "heading": "properties-1",
          "content": "Property"
        },
        {
          "heading": "properties-1",
          "content": "Type"
        },
        {
          "heading": "properties-1",
          "content": "Description"
        },
        {
          "heading": "properties-1",
          "content": "`sandboxId`"
        },
        {
          "heading": "properties-1",
          "content": "`string \\| undefined`"
        },
        {
          "heading": "properties-1",
          "content": "Container ID if one was assigned before the failure"
        },
        {
          "heading": "properties-1",
          "content": "`message`"
        },
        {
          "heading": "properties-1",
          "content": "`string`"
        },
        {
          "heading": "properties-1",
          "content": "Describes what failed"
        },
        {
          "heading": "properties-1",
          "content": "`name`"
        },
        {
          "heading": "properties-1",
          "content": "`string`"
        },
        {
          "heading": "properties-1",
          "content": "`\"SandboxError\"`"
        },
        {
          "heading": "execconnectionerror",
          "content": "Thrown when the execd daemon inside the container never becomes ready within the retry window (\\~15 seconds). The container is `Running` from OpenSandbox's perspective, but the exec daemon isn't accepting connections."
        },
        {
          "heading": "properties-2",
          "content": "Property"
        },
        {
          "heading": "properties-2",
          "content": "Type"
        },
        {
          "heading": "properties-2",
          "content": "Description"
        },
        {
          "heading": "properties-2",
          "content": "`sandboxId`"
        },
        {
          "heading": "properties-2",
          "content": "`string`"
        },
        {
          "heading": "properties-2",
          "content": "The sandbox ID"
        },
        {
          "heading": "properties-2",
          "content": "`message`"
        },
        {
          "heading": "properties-2",
          "content": "`string`"
        },
        {
          "heading": "properties-2",
          "content": "`\"execd not ready for sandbox <id>\"`"
        },
        {
          "heading": "properties-2",
          "content": "`name`"
        },
        {
          "heading": "properties-2",
          "content": "`string`"
        },
        {
          "heading": "properties-2",
          "content": "`\"ExecConnectionError\"`"
        },
        {
          "heading": "properties-2",
          "content": "This usually means the container image doesn't include execd, or the container started but the execd process crashed."
        },
        {
          "heading": "sandboxclienterror",
          "content": "Thrown by the `Sandbox` client for API-level errors (404 not found, timeouts waiting for Running state):"
        },
        {
          "heading": "sandboxclienterror",
          "content": "`SandboxClientError` is not a `WorkflowError` — it's a separate class for client-level errors."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/api-reference",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/api-reference.md",
      "title": "API Reference",
      "description": "Complete reference for every public symbol exported from @alineo-labs/sandbox.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/api-reference/alineo-client\" title=\"Sandbox\" description=\"The main client — sandbox(), resume(), restoreSnapshot(), connect(), and sandboxes management. No adapter connect()/close() call needed.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/api-reference/workflow-run\" title=\"ExecHandle\" description=\"The PromiseLike returned by sb.exec() — pipe(), stdout() generator, result(), and await.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/api-reference/builder\" title=\"Builder API\" description=\"workflow(), WorkflowBuilder, and SandboxBuilder from @alineo-labs/workflow.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/api-reference/errors\" title=\"Errors\" description=\"CommandError, SandboxError, ExecConnectionError, WorkflowError, SandboxClientError.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/api-reference/workflow-run",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/api-reference/workflow-run.md",
      "title": "ExecHandle",
      "description": "The object returned by sb.exec() and sb.execCode() — PromiseLike<ExecResult> with pipe(), stdout(), and result().",
      "headings": [
        "Consumption modes",
        "await (direct)",
        "pipe()",
        "stdout()",
        "result()",
        "ExecResult",
        "Strict mode",
        "Multiple consumers",
        "In the workflow builder"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`ExecHandle` is returned by `sb.exec()` and `sb.execCode()`. It implements `PromiseLike<ExecResult>` — you can `await` it directly, pipe it, or consume it as an async generator."
        },
        {
          "heading": "await-direct",
          "content": "Resolves after the command completes. `stdout` is the full buffered stdout string."
        },
        {
          "heading": "pipe",
          "content": "Streams stdout chunks to any writable with a `write(chunk: string)` method. Resolves when the command completes."
        },
        {
          "heading": "stdout",
          "content": "Async generator yielding stdout chunks as they arrive."
        },
        {
          "heading": "result",
          "content": "Explicit promise form. Equivalent to `await handle`."
        },
        {
          "heading": "strict-mode",
          "content": "By default, `exec()` throws `CommandError` if `exitCode !== 0` after the handle resolves. Pass `{ strict: false }` to get the result instead:"
        },
        {
          "heading": "strict-mode",
          "content": "See Error handling for more on `CommandError`."
        },
        {
          "heading": "multiple-consumers",
          "content": "An `ExecHandle` can only be consumed once — the underlying stream starts draining as soon as `sb.exec()`/`sb.execCode()` is called, in the `ExecHandle` constructor, whether or not anything ever consumes it. All consumption modes (`pipe`, `stdout`, `await`, `result`) share the same internal chunk buffer."
        },
        {
          "heading": "in-the-workflow-builder",
          "content": "The `SandboxBuilder` in `@alineo-labs/workflow` queues `exec()` calls but doesn't return `ExecHandle` instances — ops are executed during flush. Use `readFile(path, as)` to capture values into `vars`:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/credentials",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/credentials.md",
      "title": "Credentials",
      "description": "Register credentials that get injected into outbound requests without the sandbox process ever holding them.",
      "headings": [
        "Enabling it",
        "Registering a credential",
        "Injection modes",
        "`header` (recommended)",
        "`substitution`",
        "Removing a credential",
        "Where the value comes from — `source`",
        "resume() and fork() never drop a credential silently",
        "Custom credential backends"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Sandboxes often need to call an authenticated API — GitHub, Slack, an internal service. The obvious way is to pass a token through `env`, but that puts it somewhere any code running in the sandbox can read, log, or accidentally echo back to you — including code an agent wrote itself."
        },
        {
          "heading": null,
          "content": "`sb.credentials.set()` registers a credential and a rule for where it applies. From then on, matching outbound requests get the credential injected automatically, at the network layer — the value itself never enters the container's filesystem or environment."
        },
        {
          "heading": "enabling-it",
          "content": "Credential injection rides on the same opt-in egress layer as network policy — pass `credentialProxy: true` when creating the sandbox:"
        },
        {
          "heading": "enabling-it",
          "content": "A sandbox created without `networkPolicy`/`credentialProxy` has ordinary, unrestricted egress — nothing here applies to it, and `sb.credentials.*` throws if you call it on one. `networkPolicy` itself is a separate concern (which hosts the sandbox may reach at all, allow/deny style — see Network policy); `credentialProxy` is what makes injection available. Setting `defaultAction: \"allow\"` with an empty `egress` list, as above, keeps egress wide open while turning injection on — tighten `egress` if you also want to restrict which hosts are reachable at all. `credentialProxy` also needs the server on `egress.mode = \"dns+nft\"` (the `alineo init` default)."
        },
        {
          "heading": "registering-a-credential",
          "content": "Any request the sandbox makes to `api.github.com` now gets that header added automatically — a plain `curl https://api.github.com/user` with no `Authorization` header of its own comes back authenticated. Requests to any other host are untouched. Run `env` inside the sandbox and the token isn't there."
        },
        {
          "heading": "registering-a-credential",
          "content": "`CredentialBinding` field"
        },
        {
          "heading": "registering-a-credential",
          "content": "Type"
        },
        {
          "heading": "registering-a-credential",
          "content": "Description"
        },
        {
          "heading": "registering-a-credential",
          "content": "`host`"
        },
        {
          "heading": "registering-a-credential",
          "content": "`string`"
        },
        {
          "heading": "registering-a-credential",
          "content": "FQDN the credential applies to"
        },
        {
          "heading": "registering-a-credential",
          "content": "`pathPrefix`"
        },
        {
          "heading": "registering-a-credential",
          "content": "`string` (optional)"
        },
        {
          "heading": "registering-a-credential",
          "content": "Narrows the binding to requests whose path starts with this"
        },
        {
          "heading": "registering-a-credential",
          "content": "`injection`"
        },
        {
          "heading": "registering-a-credential",
          "content": "`CredentialInjection`"
        },
        {
          "heading": "registering-a-credential",
          "content": "Where the value goes — see below"
        },
        {
          "heading": "header-recommended",
          "content": "Adds `name: <value>` to every matching outbound request. This is the default and the right choice for anything that takes a bearer token or API-key header."
        },
        {
          "heading": "substitution",
          "content": "For APIs that want the secret in the URL or body rather than a header. The sidecar replaces **every literal occurrence** of `placeholder` in the listed request surfaces with the real value."
        },
        {
          "heading": "substitution",
          "content": "The outbound request **must already contain `placeholder` verbatim** — the sidecar only substitutes, it doesn't append. Put it in the base URL you give your client:"
        },
        {
          "heading": "substitution",
          "content": "`sb.credentials.listBindings()` is lossy for substitution bindings (the vault doesn't echo the substitution config back), but `resume()` / `fork()` recover the full shape from the ledger."
        },
        {
          "heading": "substitution",
          "content": "The old `{ type: \"query\"; param }` and `{ type: \"path\"; segment }` shapes were removed in 0.4.0 (they only ever threw `UnsupportedInjectionError`). Replace `{ type: \"query\"; param: \"k\" }` with `{ type: \"substitution\"; placeholder: \"__CRED__\"; in: [\"query\"] }` and add `?k=__CRED__` to the request URL."
        },
        {
          "heading": "removing-a-credential",
          "content": "Requests to that host go out unauthenticated from then on."
        },
        {
          "heading": "where-the-value-comes-from--source",
          "content": "`set()` takes an optional fourth argument describing how to re-derive the value later. It's not needed for a sandbox you register a credential on and close normally — only `resume()` and `fork()` (below) ever read it:"
        },
        {
          "heading": "where-the-value-comes-from--source",
          "content": "`{ type: \"env\", varName }` — re-read from `process.env` automatically, no extra code needed."
        },
        {
          "heading": "where-the-value-comes-from--source",
          "content": "`{ type: \"external\" }` (the default if you omit `source` entirely) — alineo has no way to reproduce this value on its own (a one-time minted token, something generated at call time). Resuming or forking a sandbox with one of these requires you to supply it explicitly — see below."
        },
        {
          "heading": "resume-and-fork-never-drop-a-credential-silently",
          "content": "If a sandbox with bound credentials is resumed or forked and one of them can't be resolved, alineo throws rather than quietly continuing without it — a credential that silently stops being injected is a worse failure mode than a loud one."
        },
        {
          "heading": "resume-and-fork-never-drop-a-credential-silently",
          "content": "Anything registered with `{ type: \"env\" }` resolves on its own. Anything else needs `resolveCredential` to return a value for it, or the call throws naming exactly which credential it couldn't resolve."
        },
        {
          "heading": "resume-and-fork-never-drop-a-credential-silently",
          "content": "`sb.fork()` carries over the parent's own bound credentials to the child automatically — the child starts out able to make the same authenticated requests the parent could, with no re-registration needed. The same resolution rules apply: pass `resolveCredential` to `fork()` if any of the carried-over credentials need one."
        },
        {
          "heading": "custom-credential-backends",
          "content": "`sb.credentials.*` is backed by a `CredentialBroker` interface (the same shape as `IStorageAdapter` — see Storage adapters). `@alineo-labs/vault`'s `OpenSandboxCredentialBroker` is the default, wired up automatically — nothing to configure for the common case. Pass `SandboxClientOptions.credentialBroker` to use a different implementation."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/environments",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/environments.md",
      "title": "Environments",
      "description": "Define a named setup recipe once, snapshot it, and spawn cheap isolated sandboxes from it on demand.",
      "headings": [
        "Defining an environment",
        "Spawning a sandbox",
        "Rebuilding after setup changes",
        "Inspecting an environment",
        "Managing environments",
        "Example",
        "Limitations"
      ],
      "sections": [
        {
          "heading": null,
          "content": "An **environment** is a named sandbox configuration with a setup recipe. It's built once — installs packages, writes config, compiles assets — snapshots the result, and restores from that snapshot on every subsequent call. Setup never runs again unless you explicitly rebuild."
        },
        {
          "heading": null,
          "content": "Each sandbox spawned from an environment is fully isolated. Changes in one do not affect others or the snapshot."
        },
        {
          "heading": "defining-an-environment",
          "content": "`client.environment(name, opts)` returns an `Environment` object. No I/O happens at this point."
        },
        {
          "heading": "defining-an-environment",
          "content": "Option"
        },
        {
          "heading": "defining-an-environment",
          "content": "Type"
        },
        {
          "heading": "defining-an-environment",
          "content": "Description"
        },
        {
          "heading": "defining-an-environment",
          "content": "`image`"
        },
        {
          "heading": "defining-an-environment",
          "content": "`string \\| { uri, auth? }`"
        },
        {
          "heading": "defining-an-environment",
          "content": "Container image. Same format as `SandboxOptions.image`."
        },
        {
          "heading": "defining-an-environment",
          "content": "`resources`"
        },
        {
          "heading": "defining-an-environment",
          "content": "`{ cpu: string; memory: string; gpu?: string }`"
        },
        {
          "heading": "defining-an-environment",
          "content": "Applied to both the build sandbox and each spawned sandbox."
        },
        {
          "heading": "defining-an-environment",
          "content": "`setup`"
        },
        {
          "heading": "defining-an-environment",
          "content": "`(sb: SandboxHandle) => Promise<void>`"
        },
        {
          "heading": "defining-an-environment",
          "content": "Runs once to configure the environment."
        },
        {
          "heading": "defining-an-environment",
          "content": "`shell`"
        },
        {
          "heading": "defining-an-environment",
          "content": "`string`"
        },
        {
          "heading": "defining-an-environment",
          "content": "Shell binary for all `exec()` calls in this environment. Defaults to `\"/bin/sh\"`."
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "The first call builds and snapshots the environment. Every subsequent call restores from the cached snapshot. Concurrent first calls are safe — setup runs exactly once even if multiple callers race."
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`env.sandbox(extra?)` accepts additional per-spawn options:"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "Option"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "Type"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "Description"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`env`"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`Record<string, string>`"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "Environment variables set at container startup."
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`hooks`"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`SandboxHooks`"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "Observability hooks (e.g. `otelHooks(tracer)` from `@alineo-labs/otel`)."
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`shell`"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "`string`"
        },
        {
          "heading": "spawning-a-sandbox",
          "content": "Shell override for this sandbox only. Falls back to `EnvironmentOptions.shell`."
        },
        {
          "heading": "rebuilding-after-setup-changes",
          "content": "Call `env.rebuild()` to force a fresh build, discarding the cached snapshot:"
        },
        {
          "heading": "rebuilding-after-setup-changes",
          "content": "There is no automatic invalidation. Call `rebuild()` explicitly whenever your setup script changes."
        },
        {
          "heading": "inspecting-an-environment",
          "content": "Returns `null` if the environment has never been built. `builtAt` is a Unix timestamp in milliseconds."
        },
        {
          "heading": "managing-environments",
          "content": "`delete` removes the ledger record only — it does not delete the server-side snapshot. Cleaning up orphaned snapshots is handled by OpenSandbox's TTL policy or via the OpenSandbox API directly."
        },
        {
          "heading": "limitations",
          "content": "Images must have `sh` and `base64`. Both are present in Debian, Ubuntu, and Alpine. Pass `shell: \"/bin/bash\"` if your setup commands require bash-specific syntax."
        },
        {
          "heading": "limitations",
          "content": "There is no automatic invalidation. If your setup function changes, the snapshot is stale until you call `env.rebuild()`."
        },
        {
          "heading": "limitations",
          "content": "Snapshots live on the OpenSandbox server. If the server prunes a snapshot (TTL), `env.sandbox()` detects the missing snapshot via `getSnapshot` and rebuilds automatically."
        },
        {
          "heading": "limitations",
          "content": "Concurrent `rebuild()` calls are not deduplicated. For production use, call `rebuild()` from a single coordinated process such as a deploy script."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/event-stream",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/event-stream.md",
      "title": "Event stream",
      "description": "How ExecHandle streams stdout from the sandbox in real time — pipe, async generator, and await.",
      "headings": [
        "Modes",
        "Pipe",
        "Async generator",
        "Await",
        "result()",
        "ExecResult type",
        "Strict mode",
        "What gets streamed"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`sb.exec()` and `sb.execCode()` both return an `ExecHandle`. Under the hood, execd sends execution output as an SSE stream. `ExecHandle` wraps that stream and exposes three consumption modes."
        },
        {
          "heading": "pipe",
          "content": "Send stdout directly to any writable with a `write(chunk: string)` method:"
        },
        {
          "heading": "pipe",
          "content": "This is the most common mode. Output flows in real time as the command runs."
        },
        {
          "heading": "async-generator",
          "content": "Iterate stdout chunks one at a time:"
        },
        {
          "heading": "async-generator",
          "content": "Use this when you want to inspect or transform output as it arrives."
        },
        {
          "heading": "await",
          "content": "Await the handle directly to get the full result after the command completes:"
        },
        {
          "heading": "await",
          "content": "This buffers all stdout in memory and resolves once the command exits. Use when you need the full output as a string."
        },
        {
          "heading": "result",
          "content": "`.result()` is an explicit promise form that resolves to `{ stdout, stderr, exitCode }`:"
        },
        {
          "heading": "strict-mode",
          "content": "By default, `exec()` throws `CommandError` if the exit code is non-zero (strict mode). Pass `{ strict: false }` to get the `ExecResult` instead:"
        },
        {
          "heading": "what-gets-streamed",
          "content": "Only stdout is streamed via `pipe()` and the `stdout()` generator. Stderr is buffered separately and available via `(await handle).stderr` or `.result()`."
        },
        {
          "heading": "what-gets-streamed",
          "content": "The underlying SSE stream from execd also carries exit code information, which `ExecHandle` uses to populate `exitCode` in the result."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts.md",
      "title": "Concepts",
      "description": "The core ideas behind the sandbox client — what things are and how they relate.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/workflows\" title=\"Workflows\" description=\"What a workflow is, how it's defined, and how it runs.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/sandboxes\" title=\"Sandboxes\" description=\"Isolated Docker containers managed by OpenSandbox.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/environments\" title=\"Environments\" description=\"Define a setup recipe once, snapshot it, and spawn isolated sandboxes from it on demand.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/steps\" title=\"Steps\" description=\"The unit of work — leaf steps and control-flow steps.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/refs-and-state\" title=\"Refs & state\" description=\"How to capture step output and thread values through a workflow.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/event-stream\" title=\"Event stream\" description=\"The AsyncIterable that streams events as steps execute.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/storage-adapters\" title=\"Storage adapters\" description=\"How the ledger persists every event for durability and replay.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/concepts/credentials\" title=\"Credentials\" description=\"Inject credentials into outbound requests without the sandbox process ever holding them.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/network-policy",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/network-policy.md",
      "title": "Network policy",
      "description": "Control which hosts a sandbox may reach — set an allow/deny policy at creation, or change it at runtime with sb.egress.*.",
      "headings": [
        "NetworkPolicy",
        "Rule targets",
        "Changing the policy at runtime",
        "Persistence across resume and fork",
        "Relationship to credential injection"
      ],
      "sections": [
        {
          "heading": null,
          "content": "By default a sandbox has ordinary, unrestricted outbound network access. Pass a `networkPolicy` to `client.sandbox()` and an **egress sidecar** is attached to the container: every DNS query and (in `dns+nft` mode) every raw-IP connection is checked against your rules before it leaves."
        },
        {
          "heading": null,
          "content": "The egress sidecar needs the OpenSandbox server to have `egress.image` configured. `alineo init`\nsets this up by default (`opensandbox/egress:v1.1.7`, `mode = \"dns+nft\"`). On an older config or a\n`uvx opensandbox-server` host, add an `[egress]` section to `~/.config/alineo/server.toml` (or\n`~/.sandbox.toml`) and restart the server. Omit `networkPolicy` entirely and none of this applies\n— no sidecar is attached."
        },
        {
          "heading": "networkpolicy",
          "content": "Field"
        },
        {
          "heading": "networkpolicy",
          "content": "Type"
        },
        {
          "heading": "networkpolicy",
          "content": "Description"
        },
        {
          "heading": "networkpolicy",
          "content": "`defaultAction`"
        },
        {
          "heading": "networkpolicy",
          "content": "`\"allow\" \\| \"deny\"`"
        },
        {
          "heading": "networkpolicy",
          "content": "What to do when no rule matches. Defaults to `\"deny\"` server-side if omitted."
        },
        {
          "heading": "networkpolicy",
          "content": "`egress`"
        },
        {
          "heading": "networkpolicy",
          "content": "`NetworkRule[]`"
        },
        {
          "heading": "networkpolicy",
          "content": "Ordered allow/deny rules."
        },
        {
          "heading": "networkpolicy",
          "content": "Each `NetworkRule` is `{ action: \"allow\" | \"deny\"; target: string }`."
        },
        {
          "heading": "rule-targets",
          "content": "`target` is one of:"
        },
        {
          "heading": "rule-targets",
          "content": "**An FQDN** — `\"api.github.com\"`. Matched against the DNS query name."
        },
        {
          "heading": "rule-targets",
          "content": "**A wildcard domain** — `\"*.openai.com\"`. The `*.` prefix is the sidecar's only wildcard form (it matches one or more leading labels)."
        },
        {
          "heading": "rule-targets",
          "content": "**A bare IPv4/IPv6 address** — `\"10.0.0.5\"`, `\"2606:4700::1111\"`."
        },
        {
          "heading": "rule-targets",
          "content": "**A CIDR block** — `\"10.0.0.0/8\"`, `\"fd00::/8\"`."
        },
        {
          "heading": "rule-targets",
          "content": "IP and CIDR rules are enforced at the **nftables layer**, so they only take effect when the server runs `egress.mode = \"dns+nft\"`, and they gate **raw-IP egress only**. A CIDR rule does *not* authorize resolving a *domain* that happens to point into that range — for reach-by-name you still need a domain rule."
        },
        {
          "heading": "rule-targets",
          "content": "A malformed `target` (a URL, whitespace, a space-containing string) throws `SandboxClientError` locally before any server round-trip. The same check is exported as `isValidEgressTarget` from `@alineo-labs/opensandbox`."
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "`sb.egress.*` adjusts a **running** sandbox's policy through its sidecar. The change applies immediately — no restart, no new sandbox."
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "Method"
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "Behavior"
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "`sb.egress.patch(rules)`"
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "Merge rules in. An incoming rule **replaces** any existing rule with the same `target`; every other rule and `defaultAction` are untouched."
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "`sb.egress.delete(targets)`"
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "Remove rules by `target`. Unknown targets are silently ignored."
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "`sb.egress.get()`"
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "Returns the sidecar's status envelope: `{ status?, mode?, enforcementMode?, policy? }`."
        },
        {
          "heading": "changing-the-policy-at-runtime",
          "content": "These only work on a sandbox created **with** a `networkPolicy` — without one there is no sidecar and the calls error."
        },
        {
          "heading": "persistence-across-resume-and-fork",
          "content": "Runtime `sb.egress.*` changes are **sidecar-local**: they don't survive the sidecar restarting and OpenSandbox's snapshot/checkpoint doesn't capture them. Every `patch` / `delete` is written to the ledger (`EgressRuleAdded` / `EgressRuleRemoved`), and `Sandbox.resume()` folds whatever is still live back into the resumed sandbox's boot policy — so a still-wanted allowance is re-applied automatically."
        },
        {
          "heading": "persistence-across-resume-and-fork",
          "content": "`sb.fork()` does **not** carry runtime egress rules. A fork is a fresh branch and starts with a wide-open `defaultAction: \"allow\"` policy."
        },
        {
          "heading": "relationship-to-credential-injection",
          "content": "`networkPolicy` (which hosts are reachable) and `credentialProxy` (transparent credential injection) are separate concerns that ride the same sidecar. `credentialProxy: true` **requires** `networkPolicy` to also be set. See Credentials."
        },
        {
          "heading": "relationship-to-credential-injection",
          "content": "For an agent that should hold a specific host behind a **human decision** before it's reachable, see Permission gate."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/refs-and-state",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/refs-and-state.md",
      "title": "Refs & state",
      "description": "Capture exec output and thread values between steps using ExecResult and the workflow builder's vars.",
      "headings": [
        "Capturing stdout",
        "Reading files as state",
        "State in the workflow builder",
        "Passing state between sandboxes"
      ],
      "sections": [
        {
          "heading": null,
          "content": "There's no special `Ref<T>` type. Output from one step is just a TypeScript variable — you capture it from the `ExecResult` and pass it to the next step however you like."
        },
        {
          "heading": "capturing-stdout",
          "content": "`await sb.exec(cmd)` returns `{ stdout, stderr, exitCode }`. Use `stdout` directly in subsequent commands:"
        },
        {
          "heading": "reading-files-as-state",
          "content": "`sb.readFile(path)` returns the file content as a string:"
        },
        {
          "heading": "state-in-the-workflow-builder",
          "content": "When using `@alineo-labs/workflow`, the `SandboxBuilder` doesn't return values from `exec()` — steps are queued, not executed yet. Use `readFile(path, as)` to capture a file into the `vars` map, then read from `vars` after the workflow resolves:"
        },
        {
          "heading": "state-in-the-workflow-builder",
          "content": "The `when` predicate also receives runtime state:"
        },
        {
          "heading": "state-in-the-workflow-builder",
          "content": "`ctx.stdout` is the accumulated stdout from all execs in the sandbox so far. `ctx.vars` holds anything captured via `readFile(..., as)`."
        },
        {
          "heading": "passing-state-between-sandboxes",
          "content": "In `.sequence()`, each stage receives the previous stage's `WorkflowResult`:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/sandboxes",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/sandboxes.md",
      "title": "Sandboxes",
      "description": "Isolated Docker containers managed by OpenSandbox — how they're created, configured, and closed.",
      "headings": [
        "Creating a sandbox",
        "SandboxOptions",
        "Sandbox lifecycle",
        "SandboxHandle properties",
        "Multiple sandboxes",
        "Concurrency limits"
      ],
      "sections": [
        {
          "heading": null,
          "content": "A sandbox is a Docker container managed by OpenSandbox. `client.sandbox()` creates one and returns a live `SandboxHandle` object. The container persists until `sb.close()` is called."
        },
        {
          "heading": "creating-a-sandbox",
          "content": "`client.sandbox()` does three things:"
        },
        {
          "heading": "creating-a-sandbox",
          "content": "Calls the OpenSandbox control API to create the container"
        },
        {
          "heading": "creating-a-sandbox",
          "content": "Waits until the container reaches `Running` state"
        },
        {
          "heading": "creating-a-sandbox",
          "content": "Returns the `SandboxHandle` object"
        },
        {
          "heading": "sandboxoptions",
          "content": "Option"
        },
        {
          "heading": "sandboxoptions",
          "content": "Type"
        },
        {
          "heading": "sandboxoptions",
          "content": "Description"
        },
        {
          "heading": "sandboxoptions",
          "content": "`image`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`string \\| { uri, auth? }`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Container image. String form: `\"ubuntu:22.04\"`. Object form for private registries: `{ uri: \"ghcr.io/org/image\", auth: { username, password } }`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`resources`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`{ cpu: string; memory: string; gpu?: string }`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Resource limits. Required. `cpu` is a Kubernetes-style string like `\"500m\"` or `\"2\"`. `memory` is `\"256Mi\"`, `\"1Gi\"`, etc."
        },
        {
          "heading": "sandboxoptions",
          "content": "`env`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`Record<string, string>`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Environment variables set in the container at startup"
        },
        {
          "heading": "sandboxoptions",
          "content": "`metadata`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`Record<string, string>`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Arbitrary key-value labels attached to the sandbox (e.g. `{ runId: \"ci-42\" }`)"
        },
        {
          "heading": "sandboxoptions",
          "content": "`name`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`string`"
        },
        {
          "heading": "sandboxoptions",
          "content": "User-provided name for the run — used as the ledger key. Auto-generated if omitted."
        },
        {
          "heading": "sandboxoptions",
          "content": "`timeout`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`number`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Sandbox lifetime in seconds. Defaults to the OpenSandbox server's default."
        },
        {
          "heading": "sandboxoptions",
          "content": "`hooks`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`SandboxHooks`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Lifecycle callbacks for observability. See Observability."
        },
        {
          "heading": "sandboxoptions",
          "content": "`networkPolicy`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`{ defaultAction, egress }`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Optional outbound network policy — which hosts the sandbox may reach. Required to use `credentialProxy`. See Network policy."
        },
        {
          "heading": "sandboxoptions",
          "content": "`credentialProxy`"
        },
        {
          "heading": "sandboxoptions",
          "content": "`boolean`"
        },
        {
          "heading": "sandboxoptions",
          "content": "Opt-in to credential injection via `sb.credentials.*`. See Credentials."
        },
        {
          "heading": "sandbox-lifecycle",
          "content": "Always use `try/finally` to ensure the container is deleted even if a step throws:"
        },
        {
          "heading": "sandbox-lifecycle",
          "content": "`close()` is idempotent — subsequent calls are no-ops."
        },
        {
          "heading": "multiple-sandboxes",
          "content": "Each `client.sandbox()` call creates an independent container. Hold them as separate variables:"
        },
        {
          "heading": "concurrency-limits",
          "content": "`SandboxClientOptions.maxConcurrency` caps how many sandboxes may be active at once. When at capacity, `client.sandbox()` awaits until a slot is free (released by `sb.close()`):"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/steps",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/steps.md",
      "title": "Steps",
      "description": "What you can do with a SandboxHandle — exec, execCode, file operations, checkpoint, and control flow.",
      "headings": [
        "Shell commands — exec()",
        "Code interpreter — execCode()",
        "File operations",
        "Checkpoints",
        "Control flow (workflow builder)"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Once you have a `SandboxHandle`, you call methods on it to do work. Each call is a \"step\" — it runs inside the container and its result is logged to the ledger."
        },
        {
          "heading": "shell-commands--exec",
          "content": "`sb.exec(cmd)` runs a shell command and returns an `ExecHandle`."
        },
        {
          "heading": "shell-commands--exec",
          "content": "By default, `exec()` throws `CommandError` on non-zero exit (`strict: true`). Pass `{ strict: false }` to get the exit code instead of an exception."
        },
        {
          "heading": "code-interpreter--execcode",
          "content": "`sb.execCode(code)` runs code via the sandbox's code interpreter (Python, JS, etc.) using the execd `/code` endpoint."
        },
        {
          "heading": "code-interpreter--execcode",
          "content": "Import `CodeLanguage` from `@alineo-labs/sandbox`:"
        },
        {
          "heading": "code-interpreter--execcode",
          "content": "Requires a code-interpreter image (e.g. `opensandbox/code-interpreter`)."
        },
        {
          "heading": "file-operations",
          "content": "Method"
        },
        {
          "heading": "file-operations",
          "content": "Description"
        },
        {
          "heading": "file-operations",
          "content": "`sb.writeFile(path, content)`"
        },
        {
          "heading": "file-operations",
          "content": "Write a UTF-8 string into the container"
        },
        {
          "heading": "file-operations",
          "content": "`sb.readFile(path)`"
        },
        {
          "heading": "file-operations",
          "content": "Read a file as a UTF-8 string"
        },
        {
          "heading": "file-operations",
          "content": "`sb.moveFile(from, to)`"
        },
        {
          "heading": "file-operations",
          "content": "Move or rename a file inside the container"
        },
        {
          "heading": "file-operations",
          "content": "`sb.deleteFile(path)`"
        },
        {
          "heading": "file-operations",
          "content": "Delete a file from the container"
        },
        {
          "heading": "file-operations",
          "content": "`sb.searchFiles(pattern, path?)`"
        },
        {
          "heading": "file-operations",
          "content": "Search for files matching a glob; returns an array of matches"
        },
        {
          "heading": "file-operations",
          "content": "`sb.listDirectory(path, opts?)`"
        },
        {
          "heading": "file-operations",
          "content": "List directory entries; `opts.depth` controls recursion depth"
        },
        {
          "heading": "file-operations",
          "content": "See File operations for full examples."
        },
        {
          "heading": "checkpoints",
          "content": "`sb.checkpoint(name?)` snapshots the container's filesystem and writes the snapshot ID to the ledger. Pass an optional label for easier identification."
        },
        {
          "heading": "checkpoints",
          "content": "Use `client.resume(sandboxId)` to restore from the last checkpoint. See Snapshots & replay."
        },
        {
          "heading": "control-flow-workflow-builder",
          "content": "The `@alineo-labs/workflow` package adds queued control-flow steps: `retry`, `when`, `forEach`. These are only available in the workflow builder (`SandboxBuilder`), not on the `SandboxHandle` class directly."
        },
        {
          "heading": "control-flow-workflow-builder",
          "content": "See Control flow for full docs."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/storage-adapters",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/storage-adapters.md",
      "title": "Storage adapters",
      "description": "How the IStorageAdapter interface persists every exec event for durability and replay.",
      "headings": [
        "Why persistence matters",
        "Choosing an adapter",
        "SQLite",
        "Postgres",
        "Connect and close",
        "Events written to the ledger",
        "Custom adapter"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Every live exec, checkpoint, and sandbox lifecycle event is written to a storage adapter as it happens. This makes runs durable — if your process crashes, the ledger contains everything needed to know what ran and what was captured. (Replayed execs in a resumed sandbox return cached output instantly and do not write new events — see Checkpoint & resume.)"
        },
        {
          "heading": "why-persistence-matters",
          "content": "The ledger enables:"
        },
        {
          "heading": "why-persistence-matters",
          "content": "**Audit** — see which commands ran, when, and what they output"
        },
        {
          "heading": "why-persistence-matters",
          "content": "**Resume** — `client.resume(sandboxId)` reads the ledger to rebuild replay cache and restore the container"
        },
        {
          "heading": "why-persistence-matters",
          "content": "**Observability** — `client.sandboxes.list()` returns run history across all sessions"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "Adapter"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "Package"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "When to use"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "`SQLiteAdapter`"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "`@alineo-labs/sqlite`"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "Local dev, single-process apps, scripts"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "`PostgresAdapter`"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "`@alineo-labs/postgres`"
        },
        {
          "heading": "choosing-an-adapter",
          "content": "Production, multi-process, shared ledger"
        },
        {
          "heading": "sqlite",
          "content": "Zero-dependency, WAL mode enabled for concurrent access:"
        },
        {
          "heading": "sqlite",
          "content": "The file is created if it doesn't exist. WAL mode means readers don't block writers, so multiple concurrent sandbox sessions are safe. No `connect()` call is needed — see Connect and close below."
        },
        {
          "heading": "postgres",
          "content": "For production deployments where multiple processes share a ledger:"
        },
        {
          "heading": "connect-and-close",
          "content": "You never need to call `connect()` or `close()` on the *adapter* — it initializes lazily (`CREATE TABLE IF NOT EXISTS` migrations run automatically on first use) and closes itself via a `process.on(\"beforeExit\", ...)` hook, which only fires when the event loop drains naturally. Long-running servers never reach `beforeExit`, so the pool stays open for the process lifetime — that's expected, not a leak. (`Sandbox` itself does have its own `connect()` method, unrelated to the adapter — see connect() — for attaching to an already-running sandbox by ID.) Just construct the client and start using it:"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "Event"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "When"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`sandbox_created`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "Container created and reached Running state"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`exec_start`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`sb.exec()` or `sb.execCode()` called"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`exec_event`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "Stdout/stderr chunk received from execd"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`exec_complete`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "exec finished; payload has `{ seq, exitCode }`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`checkpoint_created`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`sb.checkpoint()` completed; payload has `{ snapshotId }`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`sandbox_closed`"
        },
        {
          "heading": "events-written-to-the-ledger",
          "content": "`sb.close()` called"
        },
        {
          "heading": "custom-adapter",
          "content": "Implement `IStorageAdapter` to use any backend. See Custom adapter."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/concepts/workflows",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/concepts/workflows.md",
      "title": "Workflows",
      "description": "The two usage modes — direct sandbox API and the @alineo-labs/workflow lazy builder.",
      "headings": [
        "Direct sandbox API",
        "Workflow builder",
        "Multi-sandbox pipelines",
        "Getting results"
      ],
      "sections": [
        {
          "heading": null,
          "content": "There are two ways to work with sandboxes. Both use the same underlying `SandboxHandle` and `ExecHandle` types."
        },
        {
          "heading": "direct-sandbox-api",
          "content": "You create a sandbox, call methods on it, and close it. This is the simplest and most flexible mode — you have full control over the lifecycle and can use ordinary TypeScript between steps."
        },
        {
          "heading": "direct-sandbox-api",
          "content": "Use the direct API when:"
        },
        {
          "heading": "direct-sandbox-api",
          "content": "You need to branch on output values mid-workflow"
        },
        {
          "heading": "direct-sandbox-api",
          "content": "You want to run different commands based on external state"
        },
        {
          "heading": "direct-sandbox-api",
          "content": "Your workflow is straightforward (a few steps, no retry logic)"
        },
        {
          "heading": "workflow-builder",
          "content": "`@alineo-labs/workflow` provides a lazy builder that queues operations synchronously and flushes them when `.pipe()` or `.result()` is awaited. The builder handles sandbox lifecycle (create + close) automatically."
        },
        {
          "heading": "workflow-builder",
          "content": "Use the workflow builder when:"
        },
        {
          "heading": "workflow-builder",
          "content": "You need `retry`, `when`, `forEach`, or `parallel`"
        },
        {
          "heading": "workflow-builder",
          "content": "You want lifecycle managed automatically (no try/finally)"
        },
        {
          "heading": "workflow-builder",
          "content": "You're building multi-sandbox pipelines with `.parallel()` or `.sequence()`"
        },
        {
          "heading": "multi-sandbox-pipelines",
          "content": "`WorkflowBuilder` supports chaining multiple sandbox stages:"
        },
        {
          "heading": "getting-results",
          "content": "Both the direct API and the workflow builder give you access to captured output:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/building/control-flow",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/building/control-flow.md",
      "title": "Control flow",
      "description": "retry, when, forEach, parallel, and sequence — composable workflow primitives from @alineo-labs/workflow.",
      "headings": [
        "retry",
        "RetryOptions",
        "when",
        "forEach",
        "ForEachOptions",
        "parallel",
        "sequence",
        "Composing control flow"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Control-flow primitives are provided by `@alineo-labs/workflow`. They're available on `SandboxBuilder` (the callback argument in `.sandbox()`) and are queued synchronously — nothing runs until `.pipe()` or `.result()` is awaited."
        },
        {
          "heading": "retry",
          "content": "Retry an inner callback up to `maxAttempts` times on failure. Retries on any thrown error, including `CommandError` from a non-zero exit."
        },
        {
          "heading": "retryoptions",
          "content": "Option"
        },
        {
          "heading": "retryoptions",
          "content": "Type"
        },
        {
          "heading": "retryoptions",
          "content": "Default"
        },
        {
          "heading": "retryoptions",
          "content": "Description"
        },
        {
          "heading": "retryoptions",
          "content": "`delayMs`"
        },
        {
          "heading": "retryoptions",
          "content": "`number`"
        },
        {
          "heading": "retryoptions",
          "content": "`1000`"
        },
        {
          "heading": "retryoptions",
          "content": "Delay between retries in milliseconds"
        },
        {
          "heading": "retryoptions",
          "content": "`backoff`"
        },
        {
          "heading": "retryoptions",
          "content": "`\"fixed\" \\| \"exponential\"`"
        },
        {
          "heading": "retryoptions",
          "content": "`\"fixed\"`"
        },
        {
          "heading": "retryoptions",
          "content": "`\"exponential\"` doubles the delay each attempt"
        },
        {
          "heading": "retryoptions",
          "content": "With exponential backoff, attempt delays are: `delayMs`, `delayMs * 2`, `delayMs * 4`, etc."
        },
        {
          "heading": "when",
          "content": "Branch on runtime state. The predicate receives the current context: `stdout` accumulated across every exec run so far in the sandbox, `exitCode` from the most recent exec, and `vars` captured so far:"
        },
        {
          "heading": "when",
          "content": "The `else` branch (third argument) is optional. `ctx.stdout` is accumulated stdout from all execs so far. `ctx.vars` holds values captured with `sb.readFile(path, as)`."
        },
        {
          "heading": "foreach",
          "content": "Iterate over a list of items. Each item runs the inner callback in the same sandbox:"
        },
        {
          "heading": "foreachoptions",
          "content": "Option"
        },
        {
          "heading": "foreachoptions",
          "content": "Type"
        },
        {
          "heading": "foreachoptions",
          "content": "Default"
        },
        {
          "heading": "foreachoptions",
          "content": "Description"
        },
        {
          "heading": "foreachoptions",
          "content": "`concurrency`"
        },
        {
          "heading": "foreachoptions",
          "content": "`number`"
        },
        {
          "heading": "foreachoptions",
          "content": "`1`"
        },
        {
          "heading": "foreachoptions",
          "content": "Number of items to process simultaneously (within the same sandbox)"
        },
        {
          "heading": "parallel",
          "content": "Run the same operation across multiple sandbox configurations simultaneously:"
        },
        {
          "heading": "parallel",
          "content": "Each configuration gets its own container. All run concurrently; results are merged in declaration order."
        },
        {
          "heading": "sequence",
          "content": "Run sandboxes one after another, passing each stage's result to the next:"
        },
        {
          "heading": "composing-control-flow",
          "content": "`retry`, `when`, and `forEach` compose — you can nest them freely:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/building/exec",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/building/exec.md",
      "title": "exec & execCode",
      "description": "Run shell commands with exec() and interpreted code with execCode().",
      "headings": [
        "exec()",
        "Multi-line scripts",
        "ExecOptions",
        "Exec timeout",
        "Strict vs non-strict",
        "Using captured output in subsequent commands",
        "execCode()",
        "Stateless execution",
        "Stateful execution",
        "ExecCodeOptions",
        "CodeLanguage",
        "proxy()",
        "metrics()"
      ],
      "sections": [
        {
          "heading": "exec",
          "content": "`sb.exec(cmd, opts?)` runs a shell command inside the container and returns an `ExecHandle`."
        },
        {
          "heading": "multi-line-scripts",
          "content": "Pass a multi-line string to run a bash script:"
        },
        {
          "heading": "execoptions",
          "content": "Option"
        },
        {
          "heading": "execoptions",
          "content": "Type"
        },
        {
          "heading": "execoptions",
          "content": "Default"
        },
        {
          "heading": "execoptions",
          "content": "Description"
        },
        {
          "heading": "execoptions",
          "content": "`strict`"
        },
        {
          "heading": "execoptions",
          "content": "`boolean`"
        },
        {
          "heading": "execoptions",
          "content": "`true`"
        },
        {
          "heading": "execoptions",
          "content": "Throw `CommandError` on non-zero exit. Set to `false` to get the exit code instead."
        },
        {
          "heading": "execoptions",
          "content": "`cwd`"
        },
        {
          "heading": "execoptions",
          "content": "`string`"
        },
        {
          "heading": "execoptions",
          "content": "—"
        },
        {
          "heading": "execoptions",
          "content": "Working directory inside the sandbox"
        },
        {
          "heading": "execoptions",
          "content": "`env`"
        },
        {
          "heading": "execoptions",
          "content": "`Record<string, string>`"
        },
        {
          "heading": "execoptions",
          "content": "—"
        },
        {
          "heading": "execoptions",
          "content": "Extra environment variables for this exec only"
        },
        {
          "heading": "execoptions",
          "content": "`timeoutMs`"
        },
        {
          "heading": "execoptions",
          "content": "`number`"
        },
        {
          "heading": "execoptions",
          "content": "—"
        },
        {
          "heading": "execoptions",
          "content": "Abort the command after this many milliseconds"
        },
        {
          "heading": "exec-timeout",
          "content": "Use `timeoutMs` to abort a command that runs too long:"
        },
        {
          "heading": "using-captured-output-in-subsequent-commands",
          "content": "Template literals with exec output work because the exec is already awaited — there's no ordering issue."
        },
        {
          "heading": "execcode",
          "content": "`sb.execCode(code, opts?)` runs code via the sandbox's code interpreter (Python, JS, TypeScript) using the execd `/code` endpoint. Requires a code-interpreter image."
        },
        {
          "heading": "stateless-execution",
          "content": "Each call without a context runs in an isolated interpreter session:"
        },
        {
          "heading": "stateful-execution",
          "content": "Contexts must be created first via `createCodeContext()` — you can't hand-roll one. Pass the returned context to make variables persist across calls:"
        },
        {
          "heading": "execcodeoptions",
          "content": "Option"
        },
        {
          "heading": "execcodeoptions",
          "content": "Type"
        },
        {
          "heading": "execcodeoptions",
          "content": "Description"
        },
        {
          "heading": "execcodeoptions",
          "content": "`context`"
        },
        {
          "heading": "execcodeoptions",
          "content": "`{ id: string, language: CodeLanguage }`"
        },
        {
          "heading": "execcodeoptions",
          "content": "Stateful interpreter session. Variables defined in one call are available in the next."
        },
        {
          "heading": "proxy",
          "content": "Returns a proxied URL and auth headers for a port running inside the sandbox. Use this to send HTTP requests to a server started with `exec`."
        },
        {
          "heading": "proxy",
          "content": "The returned `url` is routable from outside the sandbox. The `headers` contain the execd auth token required by OpenSandbox's proxy layer — pass them along with every request."
        },
        {
          "heading": "metrics",
          "content": "Returns current CPU and memory usage for the sandbox container."
        },
        {
          "heading": "metrics",
          "content": "Useful for monitoring resource consumption during heavy workloads or detecting runaway processes before a timeout fires."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/building/file-ops",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/building/file-ops.md",
      "title": "File operations",
      "description": "Write, read, move, delete, search, list, patch, and transfer files inside the sandbox container.",
      "headings": [
        "writeFile",
        "readFile",
        "moveFile",
        "deleteFile",
        "searchFiles",
        "listDirectory",
        "createDirectory",
        "deleteDirectory",
        "getFileInfo",
        "replaceInFiles",
        "transfer",
        "Pattern: patch a file",
        "Pattern: write a generated script and run it",
        "File ops in the workflow builder"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`SandboxHandle` exposes file operation methods that cover reading, writing, patching, searching, and transferring files. All paths are inside the container."
        },
        {
          "heading": "writefile",
          "content": "Write a UTF-8 string to a path inside the container. Creates parent directories automatically."
        },
        {
          "heading": "readfile",
          "content": "Read a file from the container as a UTF-8 string:"
        },
        {
          "heading": "movefile",
          "content": "Move or rename a file inside the container:"
        },
        {
          "heading": "deletefile",
          "content": "Delete a file from the container:"
        },
        {
          "heading": "searchfiles",
          "content": "Search for files matching a glob pattern. Returns an array of matching paths:"
        },
        {
          "heading": "searchfiles",
          "content": "The second argument is the base path to search from. Defaults to `/` if omitted."
        },
        {
          "heading": "listdirectory",
          "content": "List entries in a directory. Returns metadata for each entry:"
        },
        {
          "heading": "createdirectory",
          "content": "Create a directory (and all parents) inside the container without running `exec`:"
        },
        {
          "heading": "deletedirectory",
          "content": "Delete a directory from the container:"
        },
        {
          "heading": "getfileinfo",
          "content": "Return metadata for a file or directory — size, type, mode, owner, and timestamps:"
        },
        {
          "heading": "replaceinfiles",
          "content": "Replace a substring in one or more files in a single API call. More efficient than `readFile` → string replace → `writeFile` for targeted edits:"
        },
        {
          "heading": "transfer",
          "content": "Copy a file from this sandbox into another sandbox. Useful after `fork()` to move results between containers:"
        },
        {
          "heading": "pattern-patch-a-file",
          "content": "Use `replaceInFiles` for in-place text replacements — no `exec`/`sed` needed:"
        },
        {
          "heading": "file-ops-in-the-workflow-builder",
          "content": "`writeFile`, `readFile`, `deleteFile`, and `moveFile` are available in `SandboxBuilder` for use in `@alineo-labs/workflow` (`createDirectory`, `deleteDirectory`, `getFileInfo`, `replaceInFiles`, `searchFiles`, `listDirectory`, and `transfer` are not queueable — call them directly on a `SandboxHandle` outside the workflow builder). `readFile` takes an extra `as` argument to store the result in `vars`:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/building",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/building.md",
      "title": "Building Workflows",
      "description": "Practical guides for each step type and capability in the builder API.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/building/exec\" title=\"exec & execCode\" description=\"Run shell commands and code in the sandbox interpreter.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/building/file-ops\" title=\"File operations\" description=\"Write, read, search, and manage files inside the container.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/building/control-flow\" title=\"Control flow\" description=\"retry, when, forEach, and parallel — composable within a sandbox.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/building/snapshots\" title=\"Snapshots & replay\" description=\"Checkpoint the container state and replay from it.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/building/snapshots",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/building/snapshots.md",
      "title": "Snapshots & replay",
      "description": "Checkpoint the container filesystem with checkpoint() and restore from it with client.resume().",
      "headings": [
        "Taking a checkpoint",
        "Resuming from a checkpoint",
        "How replay works",
        "Checkpoint in the workflow builder",
        "Forking a live sandbox",
        "Limitations"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Snapshots let you capture the container's state mid-run and restore from that point later. This is useful for expensive setup steps (like installing dependencies) that don't need to repeat on every run."
        },
        {
          "heading": "taking-a-checkpoint",
          "content": "`sb.checkpoint(name?)` snapshots the container's filesystem, waits until the snapshot is ready, and writes a `checkpoint_created` event to the ledger:"
        },
        {
          "heading": "taking-a-checkpoint",
          "content": "The optional `name` argument is a tag stored in the ledger. It can be used to resume from a specific checkpoint by name — see Named checkpoints."
        },
        {
          "heading": "resuming-from-a-checkpoint",
          "content": "`client.resume(sandboxId)` reads the ledger, finds the last `checkpoint_created` event, restores a new container from that snapshot, and returns a `SandboxHandle` object with a replay cache."
        },
        {
          "heading": "resuming-from-a-checkpoint",
          "content": "The resumed sandbox has a new `sandboxId` (it's a new container). The `name` stays the same."
        },
        {
          "heading": "how-replay-works",
          "content": "When you call `client.resume(sandboxId)`:"
        },
        {
          "heading": "how-replay-works",
          "content": "The ledger is read to find the last checkpoint"
        },
        {
          "heading": "how-replay-works",
          "content": "A new container is created from the snapshot"
        },
        {
          "heading": "how-replay-works",
          "content": "All execs that completed **before** the checkpoint are loaded into a replay cache, keyed by sequence number"
        },
        {
          "heading": "how-replay-works",
          "content": "On the resumed sandbox, execs with sequence numbers in the cache return the cached result immediately — no round-trip to the container"
        },
        {
          "heading": "how-replay-works",
          "content": "Execs with sequence numbers **after** the last cached exec run live on the new container"
        },
        {
          "heading": "checkpoint-in-the-workflow-builder",
          "content": "`sb.checkpoint()` is also available in `SandboxBuilder`:"
        },
        {
          "heading": "forking-a-live-sandbox",
          "content": "To create an independent copy without closing the original, use `sb.fork()` — it snapshots and immediately returns a new live sandbox. See Forking sandboxes."
        },
        {
          "heading": "limitations",
          "content": "`client.resume()` requires at least one checkpoint in the session's ledger — it throws if none exists."
        },
        {
          "heading": "limitations",
          "content": "Snapshots are managed by OpenSandbox. Availability depends on your server's storage configuration."
        },
        {
          "heading": "limitations",
          "content": "Only the container filesystem is snapshotted. In-flight SSE streams and environment state are not captured."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/getting-started/how-it-works",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/getting-started/how-it-works.md",
      "title": "How it works",
      "description": "SandboxHandle as a first-class object, ExecHandle, the durable ledger, and the @alineo-labs/workflow lazy layer.",
      "headings": [
        "SandboxHandle as a first-class object",
        "ExecHandle",
        "The durable ledger",
        "Checkpoint and resume",
        "The @alineo-labs/workflow lazy layer"
      ],
      "sections": [
        {
          "heading": "sandboxhandle-as-a-first-class-object",
          "content": "`client.sandbox()` creates a container and returns a live `SandboxHandle` object. You hold it as a variable, call methods on it, and `close()` it when done. Multiple sandboxes are just multiple variables — no special API."
        },
        {
          "heading": "sandboxhandle-as-a-first-class-object",
          "content": "Always wrap sandbox usage in `try/finally` so the container is cleaned up even if an exec throws:"
        },
        {
          "heading": "exechandle",
          "content": "`sb.exec()` returns an `ExecHandle` — a `PromiseLike<ExecResult>` with three consumption modes:"
        },
        {
          "heading": "exechandle",
          "content": "Mode"
        },
        {
          "heading": "exechandle",
          "content": "How"
        },
        {
          "heading": "exechandle",
          "content": "When to use"
        },
        {
          "heading": "exechandle",
          "content": "Await"
        },
        {
          "heading": "exechandle",
          "content": "`const { stdout, exitCode } = await sb.exec(...)`"
        },
        {
          "heading": "exechandle",
          "content": "You need the full result before continuing"
        },
        {
          "heading": "exechandle",
          "content": "Pipe"
        },
        {
          "heading": "exechandle",
          "content": "`await sb.exec(...).pipe(process.stdout)`"
        },
        {
          "heading": "exechandle",
          "content": "Real-time output to a writable"
        },
        {
          "heading": "exechandle",
          "content": "Generator"
        },
        {
          "heading": "exechandle",
          "content": "`for await (const chunk of sb.exec(...).stdout())`"
        },
        {
          "heading": "exechandle",
          "content": "Process chunks individually"
        },
        {
          "heading": "exechandle",
          "content": "All three modes drive the same underlying SSE stream from execd. `.result()` resolves to `{ stdout, stderr, exitCode }` once the stream is fully consumed."
        },
        {
          "heading": "the-durable-ledger",
          "content": "Every live `exec()` call writes three events to the storage adapter (replayed execs on a resumed sandbox skip this — see Checkpoint and resume below):"
        },
        {
          "heading": "the-durable-ledger",
          "content": "`client.sandbox()` writes `sandbox_created`. `sb.close()` writes `sandbox_closed`."
        },
        {
          "heading": "the-durable-ledger",
          "content": "This means every run is fully replayable from the ledger — even if the process crashes mid-exec."
        },
        {
          "heading": "checkpoint-and-resume",
          "content": "`sb.checkpoint()` calls the OpenSandbox snapshot API, waits for the snapshot to become ready, and writes a `checkpoint_created` event with the snapshot ID."
        },
        {
          "heading": "checkpoint-and-resume",
          "content": "`client.resume(sandboxId)` reads the ledger, finds the last checkpoint, restores a new container from the snapshot, and populates a **replay cache** from execs that completed before the checkpoint."
        },
        {
          "heading": "checkpoint-and-resume",
          "content": "On the resumed sandbox, calling `sb.exec()` with the same sequence hits the cache — returning the stored result instantly, without writing any new `exec_start`/`exec_event`/`exec_complete` events (they're already in the ledger from the original run). Subsequent execs run live on the restored container and are logged normally."
        },
        {
          "heading": "the-alineo-labsworkflow-lazy-layer",
          "content": "`@alineo-labs/workflow` adds a declarative layer on top of the core API. You queue operations synchronously inside a builder callback; the queue is flushed when `.pipe()` or `.result()` is awaited."
        },
        {
          "heading": "the-alineo-labsworkflow-lazy-layer",
          "content": "The workflow builder handles sandbox lifecycle automatically. Use it when you want control flow (`retry`, `when`, `forEach`) or multi-sandbox pipelines (`.parallel()`, `.sequence()`)."
        },
        {
          "heading": "the-alineo-labsworkflow-lazy-layer",
          "content": "For straightforward tasks — run a command, capture output, read a file — the direct `SandboxHandle` API is simpler."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/getting-started",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/getting-started.md",
      "title": "Getting Started",
      "description": "Go from zero to a running workflow in a few minutes.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/getting-started/what-is-alineo\" title=\"What is the sandbox client?\" description=\"The three-sentence pitch and a quick architecture overview.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/getting-started/installation\" title=\"Installation\" description=\"Install the package, pick a storage adapter, and connect the client.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/getting-started/quickstart\" title=\"Quick start\" description=\"Write and run your first workflow end-to-end.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/getting-started/how-it-works\" title=\"How it works\" description=\"Builder → engine → ledger → event stream — the full mental model.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/getting-started/installation",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/getting-started/installation.md",
      "title": "Installation",
      "description": "Install @alineo-labs/sandbox and a storage adapter, then wire up the client.",
      "headings": [
        "Install packages",
        "Create the client",
        "Options",
        "Local OpenSandbox"
      ],
      "sections": [
        {
          "heading": "install-packages",
          "content": "For production, use the Postgres adapter instead:"
        },
        {
          "heading": "options",
          "content": "Option"
        },
        {
          "heading": "options",
          "content": "Type"
        },
        {
          "heading": "options",
          "content": "Description"
        },
        {
          "heading": "options",
          "content": "`baseUrl`"
        },
        {
          "heading": "options",
          "content": "`string`"
        },
        {
          "heading": "options",
          "content": "OpenSandbox server URL"
        },
        {
          "heading": "options",
          "content": "`apiKey`"
        },
        {
          "heading": "options",
          "content": "`string`"
        },
        {
          "heading": "options",
          "content": "OpenSandbox API key (empty string for local dev)"
        },
        {
          "heading": "options",
          "content": "`adapter`"
        },
        {
          "heading": "options",
          "content": "`IStorageAdapter`"
        },
        {
          "heading": "options",
          "content": "Storage adapter for the run ledger"
        },
        {
          "heading": "options",
          "content": "`maxConcurrency`"
        },
        {
          "heading": "options",
          "content": "`number`"
        },
        {
          "heading": "options",
          "content": "Max simultaneous active sandboxes (default: unlimited)"
        },
        {
          "heading": "options",
          "content": "`useServerProxy`"
        },
        {
          "heading": "options",
          "content": "`boolean`"
        },
        {
          "heading": "options",
          "content": "Route execd and proxy traffic through the server. Required when the server runs in Docker via `alineo init`. Defaults to `false`."
        },
        {
          "heading": "local-opensandbox",
          "content": "Run a local sandbox server with `uvx opensandbox-server`. Create `~/.sandbox.toml`:"
        },
        {
          "heading": "local-opensandbox",
          "content": "Then start the server:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/getting-started/quickstart",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/getting-started/quickstart.md",
      "title": "Quick start",
      "description": "Run your first sandbox command in under five minutes.",
      "headings": [
        "Install",
        "Start a local sandbox server",
        "Hello world",
        "Capture output",
        "Write and read files",
        "Orchestrate with the workflow builder",
        "Next steps"
      ],
      "sections": [
        {
          "heading": "start-a-local-sandbox-server",
          "content": "Run OpenSandbox locally with `uvx opensandbox-server`. See Installation for the full config."
        },
        {
          "heading": "capture-output",
          "content": "Await the `ExecHandle` directly to get `{ stdout, stderr, exitCode }`:"
        },
        {
          "heading": "orchestrate-with-the-workflow-builder",
          "content": "For multi-step workflows with control flow, use `@alineo-labs/workflow`:"
        },
        {
          "heading": "orchestrate-with-the-workflow-builder",
          "content": "The workflow builder manages sandbox lifecycle automatically — no `try/finally` needed."
        },
        {
          "heading": "next-steps",
          "content": "How it works — the full mental model"
        },
        {
          "heading": "next-steps",
          "content": "exec & execCode — all exec options and streaming modes"
        },
        {
          "heading": "next-steps",
          "content": "File operations — writeFile, readFile, and more"
        },
        {
          "heading": "next-steps",
          "content": "Control flow — retry, when, forEach, parallel"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/getting-started/what-is-alineo",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/getting-started/what-is-alineo.md",
      "title": "What is the sandbox client?",
      "description": "@alineo-labs/sandbox gives you live sandbox containers as first-class objects — spawn, exec, checkpoint, resume.",
      "headings": [
        "What you can do",
        "Two usage modes"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`@alineo-labs/sandbox` is a sandbox execution substrate built on top of OpenSandbox. You call `client.sandbox()` to get a live container, call methods on it, and close it when done. Every exec is durably logged to a ledger — if a run is interrupted, `client.resume()` restores from the last checkpoint and replays prior execs from cache."
        },
        {
          "heading": "what-you-can-do",
          "content": "**Run commands** — `sb.exec(\"cmd\")` returns an `ExecHandle`. Await it, pipe it, or iterate stdout chunk by chunk."
        },
        {
          "heading": "what-you-can-do",
          "content": "**Run code** — `sb.execCode(code)` runs Python or JavaScript via the sandbox's interpreter."
        },
        {
          "heading": "what-you-can-do",
          "content": "**Read and write files** — `writeFile`, `readFile`, `moveFile`, `deleteFile`, `searchFiles`, `listDirectory`."
        },
        {
          "heading": "what-you-can-do",
          "content": "**Checkpoint and resume** — `sb.checkpoint()` snapshots the container. `client.resume(sandboxId)` restores it later."
        },
        {
          "heading": "what-you-can-do",
          "content": "**Manage runs** — `client.sandboxes.list()`, `.get()`, `.delete()` — full ledger access."
        },
        {
          "heading": "what-you-can-do",
          "content": "**Orchestrate** — `@alineo-labs/workflow` adds a lazy builder for `retry`, `when`, `forEach`, and parallel sandbox pipelines."
        },
        {
          "heading": "two-usage-modes",
          "content": "**Direct** — you hold the `SandboxHandle` object and call methods imperatively:"
        },
        {
          "heading": "two-usage-modes",
          "content": "**Workflow builder** — queue operations synchronously, flush on `.pipe()` or `.result()`:"
        },
        {
          "heading": "two-usage-modes",
          "content": "The workflow builder manages sandbox lifecycle automatically — no `try/finally` required."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/error-handling",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/error-handling.md",
      "title": "Error handling",
      "description": "Strict vs non-strict exec, CommandError, SandboxError, and ExecConnectionError.",
      "headings": [
        "Strict mode (default)",
        "Non-strict mode",
        "Checking exit code and branching",
        "Other error types",
        "SandboxError",
        "ExecConnectionError",
        "Error hierarchy",
        "Catching all alineo errors"
      ],
      "sections": [
        {
          "heading": "strict-mode-default",
          "content": "By default, `sb.exec()` throws `CommandError` if the command exits with a non-zero code. This is \"strict mode\" — it mirrors how shell `set -e` works:"
        },
        {
          "heading": "strict-mode-default",
          "content": "`CommandError` properties:"
        },
        {
          "heading": "strict-mode-default",
          "content": "`exitCode: number` — the process exit code"
        },
        {
          "heading": "strict-mode-default",
          "content": "`command: string` — the command string"
        },
        {
          "heading": "strict-mode-default",
          "content": "`sandboxId: string` — which sandbox it ran in"
        },
        {
          "heading": "non-strict-mode",
          "content": "Pass `{ strict: false }` to get the exit code in the result instead of throwing:"
        },
        {
          "heading": "non-strict-mode",
          "content": "This is useful for commands where a non-zero exit is a valid outcome (like `test`, `grep`, `diff`)."
        },
        {
          "heading": "checking-exit-code-and-branching",
          "content": "Combine non-strict exec with conditional logic:"
        },
        {
          "heading": "checking-exit-code-and-branching",
          "content": "Or use `when` from `@alineo-labs/workflow` for the same pattern in a builder:"
        },
        {
          "heading": "sandboxerror",
          "content": "Thrown when a sandbox fails to create, boot, or reach `Running` state:"
        },
        {
          "heading": "sandboxerror",
          "content": "`SandboxError.sandboxId` may be set if the container was assigned an ID before failing."
        },
        {
          "heading": "execconnectionerror",
          "content": "Thrown when execd inside the sandbox never becomes ready. The container is running from OpenSandbox's perspective, but the exec daemon isn't accepting connections after the retry window:"
        },
        {
          "heading": "error-hierarchy",
          "content": "Import from `@alineo-labs/sandbox`:"
        },
        {
          "heading": "catching-all-alineo-errors",
          "content": "Use `WorkflowError` as the base class to catch any alineo-specific error:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/flue",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/flue.md",
      "title": "Flue integration",
      "description": "Use a @alineo-labs/sandbox SandboxHandle as a Flue session environment with @alineo-labs/flue.",
      "headings": [
        "Install",
        "Usage",
        "API",
        "SandboxApi coverage",
        "Known limits",
        "Sandbox lifecycle"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`@alineo-labs/flue` adapts a `SandboxHandle` (from `@alineo-labs/sandbox`) to Flue's `SandboxFactory` interface, so these sandboxes can back any Flue agent that needs a container to exec commands and manage files."
        },
        {
          "heading": "usage",
          "content": "Flue loads sandbox adapters from a file at `<source-dir>/sandboxes/<name>.ts`. Create `src/sandboxes/alineo.ts` in your Flue project:"
        },
        {
          "heading": "usage",
          "content": "Then reference it in your agent definition:"
        },
        {
          "heading": "api",
          "content": "Parameter"
        },
        {
          "heading": "api",
          "content": "Type"
        },
        {
          "heading": "api",
          "content": "Description"
        },
        {
          "heading": "api",
          "content": "`sandbox`"
        },
        {
          "heading": "api",
          "content": "`SandboxHandle`"
        },
        {
          "heading": "api",
          "content": "An already-created sandbox instance. Lifecycle is the caller's responsibility — the adapter never calls `sb.close()`."
        },
        {
          "heading": "api",
          "content": "`opts.cwd`"
        },
        {
          "heading": "api",
          "content": "`string`"
        },
        {
          "heading": "api",
          "content": "Working directory passed to `createSandboxSessionEnv`. Defaults to `\"/\"`."
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "The adapter implements all nine `SandboxApi` methods:"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "Method"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "Backed by"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`exec(cmd, opts?)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.exec()` with `strict: false`. Timeout via `Promise.race` + `Math.ceil`."
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`readFile(path)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.readFile()`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`readFileBuffer(path)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.exec(\"base64 -w0 <path>\")` → decoded `Uint8Array`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`writeFile(path, string)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.writeFile()`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`writeFile(path, Uint8Array)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "Base64-encoded, piped through `base64 -d` in the container"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`stat(path)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.exec(\"stat -c '%F\\|%s\\|%Y' <path>\")` → parsed `FileStat`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`readdir(path)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.listDirectory(path, { depth: 1 })` → entry names"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`exists(path)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.exec(\"test -e <path>\")` → exit code check"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`mkdir(path, opts?)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.exec(\"mkdir [-p] <path>\")`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`rm(path, opts?)`"
        },
        {
          "heading": "sandboxapi-coverage",
          "content": "`sb.exec(\"rm [-r] [-f] <path>\")`"
        },
        {
          "heading": "known-limits",
          "content": "**Binary write cap** — `writeFile` with a `Uint8Array` base64-encodes the content and passes it through the shell. This relies on the Linux `ARG_MAX` limit (\\~2 MB), so binary writes are capped at roughly **1.5 MB**. String writes (`writeFile(path, string)`) use the native file upload API and have no size restriction."
        },
        {
          "heading": "known-limits",
          "content": "**No mid-flight cancellation** — `exec` honors `timeoutMs` via `Promise.race`, but `AbortSignal` is ignored. The underlying `SandboxHandle.exec()` does not expose signal propagation to the HTTP layer."
        },
        {
          "heading": "sandbox-lifecycle",
          "content": "The adapter never calls `sb.close()`. You are responsible for closing the sandbox when your agent session ends:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/fork",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/fork.md",
      "title": "Forking sandboxes",
      "description": "Create independent sandbox copies from a live checkpoint with sb.fork().",
      "headings": [
        "Basic usage",
        "How it works",
        "Branching experiments",
        "Concurrency",
        "Relationship to checkpoint and resume"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`sb.fork()` snapshots the current sandbox and returns a new independent `SandboxHandle` from that state — without closing the original. Both containers keep running from the same filesystem point."
        },
        {
          "heading": "basic-usage",
          "content": "The optional string argument is a tag stored in the ledger (same as `sb.checkpoint(tag)`). Both the original and the fork remain live and independent after the call."
        },
        {
          "heading": "how-it-works",
          "content": "`sb.fork(tag?)` does three things:"
        },
        {
          "heading": "how-it-works",
          "content": "Calls `createSnapshot` on the current container and waits for the snapshot to be ready"
        },
        {
          "heading": "how-it-works",
          "content": "Writes a `checkpoint_created` event to the ledger (same as `sb.checkpoint()`)"
        },
        {
          "heading": "how-it-works",
          "content": "Spins up a new container from that snapshot and returns it as a `SandboxHandle`"
        },
        {
          "heading": "how-it-works",
          "content": "The forked sandbox gets a new `sandboxId` and a ledger session named `fork-<parentName>-<shortId>`. The original sandbox is unaffected."
        },
        {
          "heading": "how-it-works",
          "content": "If the original sandbox has any credentials registered via `sb.credentials.set()`, the fork carries them over automatically — see Credentials."
        },
        {
          "heading": "branching-experiments",
          "content": "Fork is well-suited for running diverging experiments from a shared base without rebuilding:"
        },
        {
          "heading": "concurrency",
          "content": "Each forked sandbox acquires a concurrency slot (counts against `SandboxClientOptions.maxConcurrency`). The original sandbox holds its slot as well. If you fork N times from one sandbox, you need N+1 available slots."
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "`sb.checkpoint()`"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "`sb.fork()`"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Snapshots the container"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Writes `checkpoint_created` to ledger"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Original sandbox keeps running"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Returns a new live sandbox"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "No"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Use with `client.resume()`"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Yes (snapshot is in ledger)"
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "Because `fork()` writes a `checkpoint_created` event, the resulting snapshot is visible in `sb.listCheckpoints()` and can also be used with `client.resume()`."
        },
        {
          "heading": "relationship-to-checkpoint-and-resume",
          "content": "`sb.fork()` itself is really `sb.checkpoint()` followed by `client.restoreSnapshot()`, not `client.resume()` — the new sandbox starts with a clean exec history and does not replay any prior execs from the ledger, unlike `resume()`. The table above still applies since the same underlying snapshot is usable with either call afterwards; it's `fork()`'s own behavior that matches `restoreSnapshot()`, not `resume()`."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns.md",
      "title": "Patterns",
      "description": "Cross-cutting concerns — how to handle failure, time, and observability.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/patterns/timeouts-and-cancellation\" title=\"Timeouts & cancellation\" description=\"Sandbox lifetime, bash timeout command, and try/finally cleanup.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/patterns/error-handling\" title=\"Error handling\" description=\"Strict vs non-strict exec, CommandError, SandboxError, and ExecConnectionError.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/patterns/run-management\" title=\"Run management\" description=\"List, resume, and delete runs from the ledger.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/patterns/observability\" title=\"Observability\" description=\"WorkflowHooks and OpenTelemetry tracing with @alineo-labs/otel.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/core/patterns/flue\" title=\"Flue integration\" description=\"Use @alineo-labs/sandbox sandboxes as Flue session environments with @alineo-labs/flue.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/named-checkpoints",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/named-checkpoints.md",
      "title": "Named checkpoints",
      "description": "Tag checkpoints with human-readable names, list them, and resume from any specific point.",
      "headings": [
        "Tagging a checkpoint",
        "Listing checkpoints",
        "Resuming from a named checkpoint",
        "Example: branching from a base state"
      ],
      "sections": [
        {
          "heading": null,
          "content": "By default, `client.resume()` restores from the most recent checkpoint. Named checkpoints let you tag specific points in a session and resume from any of them by name."
        },
        {
          "heading": "tagging-a-checkpoint",
          "content": "Pass a string to `sb.checkpoint()` to attach a tag:"
        },
        {
          "heading": "tagging-a-checkpoint",
          "content": "The tag is stored in the ledger alongside the snapshot ID. It has no effect on the snapshot itself."
        },
        {
          "heading": "listing-checkpoints",
          "content": "`sb.listCheckpoints()` returns all checkpoints for the sandbox in creation order:"
        },
        {
          "heading": "listing-checkpoints",
          "content": "Each `CheckpointInfo` has:"
        },
        {
          "heading": "listing-checkpoints",
          "content": "Field"
        },
        {
          "heading": "listing-checkpoints",
          "content": "Type"
        },
        {
          "heading": "listing-checkpoints",
          "content": "Description"
        },
        {
          "heading": "listing-checkpoints",
          "content": "`snapshotId`"
        },
        {
          "heading": "listing-checkpoints",
          "content": "`string`"
        },
        {
          "heading": "listing-checkpoints",
          "content": "OpenSandbox snapshot ID"
        },
        {
          "heading": "listing-checkpoints",
          "content": "`tag`"
        },
        {
          "heading": "listing-checkpoints",
          "content": "`string?`"
        },
        {
          "heading": "listing-checkpoints",
          "content": "Tag passed to `sb.checkpoint()`, if any"
        },
        {
          "heading": "listing-checkpoints",
          "content": "`createdAt`"
        },
        {
          "heading": "listing-checkpoints",
          "content": "`number`"
        },
        {
          "heading": "listing-checkpoints",
          "content": "Unix timestamp (ms) when the checkpoint was created"
        },
        {
          "heading": "resuming-from-a-named-checkpoint",
          "content": "Pass `{ tag }` to `client.resume()` to restore from a specific checkpoint instead of the latest:"
        },
        {
          "heading": "resuming-from-a-named-checkpoint",
          "content": "Without a tag, `client.resume()` falls back to the most recent checkpoint — existing behaviour is unchanged."
        },
        {
          "heading": "resuming-from-a-named-checkpoint",
          "content": "`client.resume()` throws `SandboxClientError` (status 404) if no checkpoint with the given tag is found in the session's ledger."
        },
        {
          "heading": "example-branching-from-a-base-state",
          "content": "A common pattern is to take one base checkpoint and branch different experiments from it:"
        },
        {
          "heading": "example-branching-from-a-base-state",
          "content": "Both branches restore from the same `\"base\"` snapshot independently — no interference between them."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/observability",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/observability.md",
      "title": "Observability",
      "description": "SandboxHooks lifecycle callbacks and OpenTelemetry tracing with @alineo-labs/otel.",
      "headings": [
        "SandboxHooks",
        "Hook signatures",
        "OpenTelemetry with @alineo-labs/otel",
        "Span structure",
        "OtelHooksOptions",
        "Setting up OTEL"
      ],
      "sections": [
        {
          "heading": "sandboxhooks",
          "content": "`SandboxHooks` is a set of lifecycle callbacks you can pass to `client.sandbox()` to observe what's happening during a run. All hooks are optional."
        },
        {
          "heading": "hook-signatures",
          "content": "Hook"
        },
        {
          "heading": "hook-signatures",
          "content": "When"
        },
        {
          "heading": "hook-signatures",
          "content": "Arguments"
        },
        {
          "heading": "hook-signatures",
          "content": "`onSandboxCreated`"
        },
        {
          "heading": "hook-signatures",
          "content": "Container created and running"
        },
        {
          "heading": "hook-signatures",
          "content": "`(sandboxId, name)`"
        },
        {
          "heading": "hook-signatures",
          "content": "`onExecStart`"
        },
        {
          "heading": "hook-signatures",
          "content": "`sb.exec()` called"
        },
        {
          "heading": "hook-signatures",
          "content": "`(sandboxId, seq, cmd)`"
        },
        {
          "heading": "hook-signatures",
          "content": "`onExecComplete`"
        },
        {
          "heading": "hook-signatures",
          "content": "exec finished"
        },
        {
          "heading": "hook-signatures",
          "content": "`(sandboxId, seq, ExecResult)`"
        },
        {
          "heading": "hook-signatures",
          "content": "`onCheckpoint`"
        },
        {
          "heading": "hook-signatures",
          "content": "`sb.checkpoint()` completed"
        },
        {
          "heading": "hook-signatures",
          "content": "`(sandboxId, snapshotId, name?)`"
        },
        {
          "heading": "hook-signatures",
          "content": "`onSandboxClosed`"
        },
        {
          "heading": "hook-signatures",
          "content": "`sb.close()` completed"
        },
        {
          "heading": "hook-signatures",
          "content": "`(sandboxId)`"
        },
        {
          "heading": "hook-signatures",
          "content": "`onSandboxFailed`"
        },
        {
          "heading": "hook-signatures",
          "content": "sandbox creation or boot failed"
        },
        {
          "heading": "hook-signatures",
          "content": "`(sandboxId, error)`"
        },
        {
          "heading": "opentelemetry-with-alineo-labsotel",
          "content": "`@alineo-labs/otel` provides an `otelHooks()` factory that produces `SandboxHooks` emitting OTEL traces:"
        },
        {
          "heading": "otelhooksoptions",
          "content": "Option"
        },
        {
          "heading": "otelhooksoptions",
          "content": "Type"
        },
        {
          "heading": "otelhooksoptions",
          "content": "Default"
        },
        {
          "heading": "otelhooksoptions",
          "content": "Description"
        },
        {
          "heading": "otelhooksoptions",
          "content": "`recordExitCode`"
        },
        {
          "heading": "otelhooksoptions",
          "content": "`boolean`"
        },
        {
          "heading": "otelhooksoptions",
          "content": "`true`"
        },
        {
          "heading": "otelhooksoptions",
          "content": "Add `process.exit_code` attribute to exec spans"
        },
        {
          "heading": "setting-up-otel",
          "content": "`otelHooks` uses the `@opentelemetry/api` package — it integrates with whatever OTEL SDK you have configured in your application. Example with the Node SDK:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/run-management",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/run-management.md",
      "title": "Run management",
      "description": "List, inspect, resume, and delete past sandbox sessions from the ledger.",
      "headings": [
        "List all sessions",
        "SandboxDetails",
        "Filter by name",
        "Filter by status or date",
        "Get a single session",
        "Delete a session",
        "Resume from a checkpoint",
        "Typical cleanup pattern"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Every sandbox session is stored in the ledger. `client.sandboxes` gives you full access to that history."
        },
        {
          "heading": "list-all-sessions",
          "content": "Sessions are returned newest first."
        },
        {
          "heading": "sandboxdetails",
          "content": "Each session has:"
        },
        {
          "heading": "sandboxdetails",
          "content": "Field"
        },
        {
          "heading": "sandboxdetails",
          "content": "Type"
        },
        {
          "heading": "sandboxdetails",
          "content": "Description"
        },
        {
          "heading": "sandboxdetails",
          "content": "`sandboxId`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`string`"
        },
        {
          "heading": "sandboxdetails",
          "content": "OpenSandbox container ID"
        },
        {
          "heading": "sandboxdetails",
          "content": "`name`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`string`"
        },
        {
          "heading": "sandboxdetails",
          "content": "User-provided name (or auto-generated)"
        },
        {
          "heading": "sandboxdetails",
          "content": "`status`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`SandboxStatus`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`\"running\"` or `\"completed\"` — that's the full set; there is no `\"failed\"` or `\"cancelled\"` status"
        },
        {
          "heading": "sandboxdetails",
          "content": "`startedAt`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`number`"
        },
        {
          "heading": "sandboxdetails",
          "content": "Unix timestamp (ms) of `sandbox_created` event"
        },
        {
          "heading": "sandboxdetails",
          "content": "`completedAt`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`number?`"
        },
        {
          "heading": "sandboxdetails",
          "content": "Unix timestamp (ms) of `sandbox_closed` event"
        },
        {
          "heading": "sandboxdetails",
          "content": "`execCount`"
        },
        {
          "heading": "sandboxdetails",
          "content": "`number`"
        },
        {
          "heading": "sandboxdetails",
          "content": "Number of completed execs"
        },
        {
          "heading": "sandboxdetails",
          "content": "There is no `error` field on `SandboxDetails` — a sandbox that failed to start doesn't get a ledger entry with a `\"failed\"` status; it just never has a `sandbox_created` event to derive one from."
        },
        {
          "heading": "delete-a-session",
          "content": "Removes all ledger events for the session. Does not affect the container (which is already closed):"
        },
        {
          "heading": "resume-from-a-checkpoint",
          "content": "If a session was checkpointed before it was closed, you can restore it:"
        },
        {
          "heading": "resume-from-a-checkpoint",
          "content": "`client.resume()` throws `SandboxClientError` (status 404) if no session with that ID is found in the ledger, and if no checkpoint exists in the session's history."
        },
        {
          "heading": "typical-cleanup-pattern",
          "content": "For scripts that accumulate many sessions, periodically clean up completed runs:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/core/patterns/timeouts-and-cancellation",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/core/patterns/timeouts-and-cancellation.md",
      "title": "Timeouts & cancellation",
      "description": "Sandbox lifetime limits, bash-level command timeouts, and cleanup with try/finally.",
      "headings": [
        "Sandbox lifetime",
        "Per-command timeout via bash",
        "Cleanup with try/finally",
        "Handling long-running commands",
        "Retry with backoff"
      ],
      "sections": [
        {
          "heading": "sandbox-lifetime",
          "content": "Set a maximum lifetime for the entire sandbox container via `SandboxOptions.timeout` (in seconds). The OpenSandbox server terminates the container after this duration:"
        },
        {
          "heading": "sandbox-lifetime",
          "content": "This is a hard limit on the container — not on individual commands."
        },
        {
          "heading": "per-command-timeout-via-bash",
          "content": "Use the bash `timeout` command to limit how long a single command can run. The `timeout` utility exits with code `124` if the limit is reached:"
        },
        {
          "heading": "per-command-timeout-via-bash",
          "content": "To get the actual timeout exit code:"
        },
        {
          "heading": "cleanup-with-tryfinally",
          "content": "Always wrap sandbox usage in `try/finally` to ensure `sb.close()` runs even if an exec throws or a timeout fires:"
        },
        {
          "heading": "cleanup-with-tryfinally",
          "content": "`close()` is idempotent — calling it multiple times is safe."
        },
        {
          "heading": "handling-long-running-commands",
          "content": "For commands that might run indefinitely, combine `timeout` with error handling:"
        },
        {
          "heading": "retry-with-backoff",
          "content": "For flaky operations, use `retry` from `@alineo-labs/workflow` instead of manual retry loops. It handles backoff and re-runs cleanly:"
        },
        {
          "heading": "retry-with-backoff",
          "content": "See Control flow for full retry docs."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow.md",
      "title": "Workflow Builder",
      "description": "@alineo-labs/workflow — a lazy declarative layer on top of the Core SDK for orchestrating multi-sandbox pipelines.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/getting-started\" title=\"Getting Started\" description=\"What the workflow builder is and when to use it over the Core SDK.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/building\" title=\"Building\" description=\"SandboxBuilder ops, control flow, parallel execution, and capturing values.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/api-reference\" title=\"API Reference\" description=\"Complete reference for workflow(), WorkflowBuilder, and SandboxBuilder.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/api-reference/builder",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/api-reference/builder.md",
      "title": "Builder API",
      "description": "workflow(), WorkflowBuilder, and SandboxBuilder — the @alineo-labs/workflow lazy orchestration layer.",
      "headings": [
        "workflow()",
        "WorkflowBuilder",
        ".sandbox()",
        ".parallel()",
        ".sequence()",
        ".pipe()",
        ".result()",
        "SandboxBuilder",
        ".exec()",
        ".execCode()",
        ".writeFile()",
        ".readFile()",
        ".moveFile()",
        ".deleteFile()",
        ".checkpoint()",
        ".retry()",
        ".when()",
        ".forEach()"
      ],
      "sections": [
        {
          "heading": "workflow",
          "content": "Creates a `WorkflowBuilder` attached to a `Sandbox` client."
        },
        {
          "heading": "workflowbuilder",
          "content": "Collects stages and executes them all when `.pipe()` or `.result()` is awaited. Sandbox lifecycle (create + close) is managed automatically."
        },
        {
          "heading": "sandbox",
          "content": "Add a sandbox stage. The `fn` callback receives a `SandboxBuilder` to queue operations synchronously. Multiple `.sandbox()` calls run sequentially."
        },
        {
          "heading": "parallel",
          "content": "Run the same operation across multiple containers simultaneously. All configs receive the same `fn`. Results are merged."
        },
        {
          "heading": "sequence",
          "content": "Run sandboxes one after another. Each step's callback receives the previous step's `WorkflowResult` as `prev`:"
        },
        {
          "heading": "pipe",
          "content": "Execute the workflow and stream stdout to a writable. Flushes all queued stages."
        },
        {
          "heading": "result",
          "content": "Execute the workflow and return the combined result:"
        },
        {
          "heading": "sandboxbuilder",
          "content": "Queues operations synchronously. All methods return `this` for chaining. Nothing runs until the `WorkflowBuilder` is flushed."
        },
        {
          "heading": "exec",
          "content": "Queue a shell command."
        },
        {
          "heading": "execcode",
          "content": "Queue a code execution via the interpreter."
        },
        {
          "heading": "writefile",
          "content": "Queue a file write into the sandbox."
        },
        {
          "heading": "readfile",
          "content": "Queue a file read. The content is stored in `vars[as]` and available via `WorkflowResult.vars`."
        },
        {
          "heading": "movefile",
          "content": "Queue a file move inside the sandbox."
        },
        {
          "heading": "deletefile",
          "content": "Queue a file deletion."
        },
        {
          "heading": "checkpoint",
          "content": "Queue a container snapshot."
        },
        {
          "heading": "retry",
          "content": "Queue a retry block. Runs up to `maxAttempts` total attempts (the first run plus retries), waiting `delayMs` between each on failure."
        },
        {
          "heading": "when",
          "content": "Queue a conditional branch. `pred` is evaluated at flush time with the current execution context."
        },
        {
          "heading": "foreach",
          "content": "Queue iteration over a list of items."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/api-reference",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/api-reference.md",
      "title": "API Reference",
      "description": "Complete reference for the @alineo-labs/workflow lazy orchestration layer.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/api-reference/builder\" title=\"Builder API\" description=\"workflow(), WorkflowBuilder, and SandboxBuilder from @alineo-labs/workflow.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/building/capturing-values",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/building/capturing-values.md",
      "title": "Capturing Values",
      "description": "Use readFile(path, as) to pull file contents out of the sandbox and into WorkflowResult.vars.",
      "headings": [
        "readFile",
        "Multiple values",
        "Passing values between sequence steps",
        "stdout"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`SandboxBuilder` doesn't return `ExecHandle` — ops are queued, not executed. To get a value out of the sandbox, write it to a file inside the container and read it back with `readFile`."
        },
        {
          "heading": "readfile",
          "content": "After the workflow resolves, `vars.nodeVersion` contains the file's content as a string."
        },
        {
          "heading": "multiple-values",
          "content": "Capture as many values as you need — each gets its own key:"
        },
        {
          "heading": "passing-values-between-sequence-steps",
          "content": "`readFile` values are available in the next step via `prev.vars`:"
        },
        {
          "heading": "stdout",
          "content": "The combined stdout from all exec calls is also available in `WorkflowResult.stdout`. For selective capture, `readFile` is more reliable than parsing stdout."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/building/control-flow",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/building/control-flow.md",
      "title": "Control Flow",
      "description": "retry, when, and forEach — conditional branching and iteration inside a SandboxBuilder.",
      "headings": [
        "retry",
        "RetryOptions",
        "when",
        "Predicate context",
        "forEach",
        "ForEachOptions",
        "Composing"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Control flow primitives let you express branching and looping inside the `SandboxBuilder` callback. They queue lazily like every other operation, and the predicates/callbacks run at flush time with the current execution context."
        },
        {
          "heading": "retry",
          "content": "Retry a block of operations up to `maxAttempts` times on failure."
        },
        {
          "heading": "retryoptions",
          "content": "Option"
        },
        {
          "heading": "retryoptions",
          "content": "Type"
        },
        {
          "heading": "retryoptions",
          "content": "Default"
        },
        {
          "heading": "retryoptions",
          "content": "Description"
        },
        {
          "heading": "retryoptions",
          "content": "`delayMs`"
        },
        {
          "heading": "retryoptions",
          "content": "`number`"
        },
        {
          "heading": "retryoptions",
          "content": "`1000`"
        },
        {
          "heading": "retryoptions",
          "content": "Wait between attempts (ms)"
        },
        {
          "heading": "retryoptions",
          "content": "`backoff`"
        },
        {
          "heading": "retryoptions",
          "content": "`\"fixed\" \\| \"exponential\"`"
        },
        {
          "heading": "retryoptions",
          "content": "`\"fixed\"`"
        },
        {
          "heading": "retryoptions",
          "content": "Delay growth strategy"
        },
        {
          "heading": "retryoptions",
          "content": "With `backoff: \"exponential\"`, the delay doubles each attempt (`delayMs * 2^(attempt-1)`): 1s, 2s, 4s, ..."
        },
        {
          "heading": "when",
          "content": "Branch on runtime state. The predicate receives the current execution context."
        },
        {
          "heading": "predicate-context",
          "content": "The `otherwise` branch is optional. If the predicate is false and no `otherwise` is provided, the when block is skipped."
        },
        {
          "heading": "foreach",
          "content": "Iterate over a list of items, queueing the same operations for each."
        },
        {
          "heading": "foreach",
          "content": "Run iterations in parallel with `concurrency`:"
        },
        {
          "heading": "foreachoptions",
          "content": "Option"
        },
        {
          "heading": "foreachoptions",
          "content": "Type"
        },
        {
          "heading": "foreachoptions",
          "content": "Default"
        },
        {
          "heading": "foreachoptions",
          "content": "Description"
        },
        {
          "heading": "foreachoptions",
          "content": "`concurrency`"
        },
        {
          "heading": "foreachoptions",
          "content": "`number`"
        },
        {
          "heading": "foreachoptions",
          "content": "`1`"
        },
        {
          "heading": "foreachoptions",
          "content": "Max parallel iterations"
        },
        {
          "heading": "foreachoptions",
          "content": "With `concurrency > 1`, each parallel branch flushes against a shallow copy of the outer `FlushContext`. `stdout`/`exitCode` writes inside those branches don't propagate back to the outer context afterward — only `vars` (an object reference) does. Don't rely on `ctx.stdout`/`ctx.exitCode` reflecting what happened inside a concurrent `forEach` after it completes."
        },
        {
          "heading": "composing",
          "content": "Control flow primitives nest naturally:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/building",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/building.md",
      "title": "Building",
      "description": "SandboxBuilder operations, control flow, parallel/sequence patterns, and capturing values.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/building/sandbox-builder\" title=\"SandboxBuilder\" description=\"The object passed to every workflow callback — queues operations synchronously, flushes them when the workflow is awaited.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/building/control-flow\" title=\"Control Flow\" description=\"retry, when, and forEach — conditional branching and iteration inside a SandboxBuilder.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/building/parallel-sequence\" title=\"Parallel & Sequence\" description=\"Run the same workflow across multiple containers simultaneously, or chain sandboxes where each step sees the previous result.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/building/capturing-values\" title=\"Capturing Values\" description=\"Use readFile(path, as) to pull file contents out of the sandbox and into WorkflowResult.vars.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/building/parallel-sequence",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/building/parallel-sequence.md",
      "title": "Parallel & Sequence",
      "description": "Run the same workflow across multiple containers simultaneously, or chain sandboxes where each step sees the previous result.",
      "headings": [
        "parallel",
        "Cross-version matrix testing",
        "sequence",
        "SequenceStep",
        "Combining parallel and sequence"
      ],
      "sections": [
        {
          "heading": "parallel",
          "content": "Run the same set of operations across multiple containers at the same time."
        },
        {
          "heading": "parallel",
          "content": "All configs receive the same `fn` callback. The sandboxes run concurrently — stdout from all of them is merged in the result. Each sandbox is its own isolated container."
        },
        {
          "heading": "sequence",
          "content": "Chain sandboxes one after another. Each step's callback receives the previous step's `WorkflowResult` as `prev`."
        },
        {
          "heading": "sequencestep",
          "content": "`prev` is `undefined` for the first step."
        },
        {
          "heading": "combining-parallel-and-sequence",
          "content": "`.parallel()` and `.sequence()` are both methods on `WorkflowBuilder` and can be chained:"
        },
        {
          "heading": "combining-parallel-and-sequence",
          "content": "Each call adds a stage. Stages run in the order they are added."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/building/sandbox-builder",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/building/sandbox-builder.md",
      "title": "SandboxBuilder",
      "description": "The object passed to every workflow callback — queues operations synchronously, flushes them when the workflow is awaited.",
      "headings": [
        "exec",
        "execCode",
        "writeFile",
        "readFile",
        "moveFile",
        "deleteFile",
        "checkpoint"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`SandboxBuilder` is the object your callback receives inside `.sandbox()`, `.parallel()`, and `.sequence()`. Every method queues an operation. Nothing runs until the `WorkflowBuilder` is flushed via `.pipe()` or `.result()`."
        },
        {
          "heading": null,
          "content": "All methods return `this` for chaining."
        },
        {
          "heading": "exec",
          "content": "Queue a shell command. `strict: true` (default) throws `CommandError` on non-zero exit. `strict: false` captures the exit code instead."
        },
        {
          "heading": "execcode",
          "content": "Queue a code snippet via the interpreter. There's no `language` shorthand or `stateful` flag — `ExecCodeOptions` only takes an optional `context: { id, language }`. Reusing the same `id` across calls shares interpreter state between them; a call with no `context` runs stateless."
        },
        {
          "heading": "execcode",
          "content": "`SandboxBuilder` has no queued equivalent of `SandboxHandle.createCodeContext()` — you supply the context object directly (as above) rather than obtaining one from execd first. If you need a context created via `createCodeContext()`, use the direct `SandboxHandle` API outside the workflow builder instead (see Executing code)."
        },
        {
          "heading": "writefile",
          "content": "Queue a file write into the sandbox filesystem."
        },
        {
          "heading": "readfile",
          "content": "Queue a file read. The content lands in `WorkflowResult.vars[\"result\"]` after the workflow completes."
        },
        {
          "heading": "movefile",
          "content": "Queue a file move within the sandbox."
        },
        {
          "heading": "deletefile",
          "content": "Queue a file deletion."
        },
        {
          "heading": "checkpoint",
          "content": "Queue a container snapshot. The checkpoint name is optional. After checkpointing, `client.resume(sandboxId)` can restore the container to this state."
        },
        {
          "heading": "checkpoint",
          "content": "See Snapshots for the full checkpoint/resume model."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/getting-started",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/getting-started.md",
      "title": "Getting Started",
      "description": "What the Workflow Builder is, and how to run your first workflow.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/getting-started/what-is-workflow\" title=\"What is the Workflow Builder?\" description=\"@alineo-labs/workflow adds a lazy declarative layer over the Core SDK — one await at the end, lifecycle managed automatically.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/workflow/getting-started/quickstart\" title=\"Quickstart\" description=\"Run your first workflow — install, configure, and run a multi-step pipeline in one await.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/getting-started/quickstart",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/getting-started/quickstart.md",
      "title": "Quickstart",
      "description": "Run your first workflow — install, configure, and run a multi-step pipeline in one await.",
      "headings": [
        "Install",
        "Configure",
        "Run a workflow",
        "Capture output",
        "Next steps"
      ],
      "sections": [
        {
          "heading": "configure",
          "content": "No `connect()` call needed — the adapter initializes lazily on first use."
        },
        {
          "heading": "run-a-workflow",
          "content": "No `close()` call needed either — `Sandbox` has no such method; the adapter closes itself automatically when the process exits."
        },
        {
          "heading": "run-a-workflow",
          "content": "The callback receives a `SandboxBuilder`. All calls inside it queue operations synchronously — nothing runs until `.pipe()` or `.result()` is awaited. The sandbox is created, all ops are flushed in order, then the sandbox is closed automatically."
        },
        {
          "heading": "capture-output",
          "content": "Use `.result()` instead of `.pipe()` to get the combined stdout and any captured file values:"
        },
        {
          "heading": "next-steps",
          "content": "SandboxBuilder operations — the full set of queued ops"
        },
        {
          "heading": "next-steps",
          "content": "Control flow — retry, when, forEach"
        },
        {
          "heading": "next-steps",
          "content": "Parallel & sequence — multi-sandbox patterns"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/workflow/getting-started/what-is-workflow",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/workflow/getting-started/what-is-workflow.md",
      "title": "What is the Workflow Builder?",
      "description": "@alineo-labs/workflow adds a lazy declarative layer over the Core SDK — one await at the end, lifecycle managed automatically.",
      "headings": [
        "Core SDK vs Workflow Builder",
        "When to use the Workflow Builder",
        "Installation"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`@alineo-labs/workflow` is a separate package that sits on top of the `alineo` Core SDK. It gives you a builder API where you describe what should happen synchronously, then flush everything with a single `await` at the end."
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Core SDK (`alineo`)"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Workflow Builder (`@alineo-labs/workflow`)"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Style"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Imperative — hold objects, call methods"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Declarative — describe ops, flush once"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Await"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Per-operation"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Once at the end"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Lifecycle"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Manual `try/finally sb.close()`"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Managed automatically"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Multi-sandbox"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Multiple variables"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "`.parallel()`, `.sequence()`"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Streaming"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "`ExecHandle.pipe()`"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "`.pipe(writable)` on the workflow"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "Values"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "`await sb.exec(...)`"
        },
        {
          "heading": "core-sdk-vs-workflow-builder",
          "content": "`readFile(path, as)` → `vars`"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "Use `@alineo-labs/workflow` when:"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You want the simplest possible multi-sandbox orchestration"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You need `.parallel()` across N containers without managing each one manually"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You want to sequence sandboxes where each step depends on the previous result"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You want a single `await` regardless of how many sandboxes and operations are involved"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "Use the Core SDK directly when:"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You need fine-grained control over individual exec results mid-flow"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You're integrating sandbox execution into existing imperative async code"
        },
        {
          "heading": "when-to-use-the-workflow-builder",
          "content": "You want to stream output interleaved with other async work"
        },
        {
          "heading": "installation",
          "content": "The workflow builder requires `alineo` as a peer dependency — you'll already have it if you're using the Core SDK."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent.md",
      "title": "Alineo SDK",
      "description": "Run AI coding agents (Pi) in isolated sandbox containers with a simple TypeScript API.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started\" title=\"Getting Started\" description=\"Load an agent spec, send prompts, and stream responses in minutes.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/api-reference\" title=\"API Reference\" description=\"Complete reference for Alineo, AgentSpec, AgentStream, and all Pi RPC commands.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started.md",
      "title": "Getting Started",
      "description": "Load an agent spec, send a prompt, and understand snapshotting, workspace setup, and streaming.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/quickstart\" title=\"Quick start\" description=\"Load an agent spec, send a prompt, and stream the response in minutes.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/snapshotting\" title=\"Snapshotting\" description=\"How Alineo.load() caches the Pi install so subsequent loads take seconds instead of minutes.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/workspace-setup\" title=\"Workspace setup\" description=\"Declarative bash steps that run after Pi install and are baked into the snapshot.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/streaming\" title=\"Streaming & tool events\" description=\"Observe Pi's text output, tool calls, and lifecycle events in real time.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/reliability\" title=\"Reliability & error recovery\" description=\"Handle transient API errors with auto-retry, observe retry events, and abort mid-flight bash commands.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/session-inspection\" title=\"Session inspection & control\" description=\"Inspect token usage, retrieve session history, list available commands, and export HTML transcripts.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/getting-started/permissions\" title=\"Permission gate (human-in-the-loop)\" description=\"Pause tool calls for human approval, restrict the toolset, and hold network egress until someone approves.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/permissions",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/permissions.md",
      "title": "Permission gate (human-in-the-loop)",
      "description": "Pause an agent's tool calls for human approval — a mode shorthand or a full per-tool policy, plus a gate that holds outbound network egress until someone approves.",
      "headings": [
        "Modes",
        "Full policy",
        "PermissionRule",
        "Actions",
        "Resolving a request from the stream",
        "Handler form — `prompt({ onPermission })`",
        "Inspecting what's paused",
        "Audit trail",
        "Lifecycle notes",
        "Holding network egress for approval"
      ],
      "sections": [
        {
          "heading": null,
          "content": "By default a sandboxed Pi agent runs its tools without asking (`pi --mode rpc --approve`). `AgentSpec.permissions` puts a gate in front of every tool call: some run free, some pause and wait for a human, some are refused outright."
        },
        {
          "heading": null,
          "content": "Enforcement runs **inside the Pi process** (its `tool_call` hook). It stops a misbehaving *model* — it is not a barrier against a process with shell access inside the sandbox actively working around it. For that, hold the network itself: see Holding network egress for approval below."
        },
        {
          "heading": "modes",
          "content": "The quickest form is a string:"
        },
        {
          "heading": "modes",
          "content": "Mode"
        },
        {
          "heading": "modes",
          "content": "Behavior"
        },
        {
          "heading": "modes",
          "content": "`\"auto\"`"
        },
        {
          "heading": "modes",
          "content": "Never ask. Identical to omitting `permissions` — no gate is loaded at all. (Default.)"
        },
        {
          "heading": "modes",
          "content": "`\"ask\"`"
        },
        {
          "heading": "modes",
          "content": "Pause before **every** tool call."
        },
        {
          "heading": "modes",
          "content": "`\"readonly\"`"
        },
        {
          "heading": "modes",
          "content": "Restrict the model's toolset to the read-only tools (`read`, `grep`, `find`, `ls`) via Pi's `setActiveTools`, so it never sees `write` / `edit` / `bash`. Any `bash` left reachable is `classify`-triaged."
        },
        {
          "heading": "full-policy",
          "content": "For anything finer, `permissions` takes an object:"
        },
        {
          "heading": "full-policy",
          "content": "`PermissionPolicy` field"
        },
        {
          "heading": "full-policy",
          "content": "Type"
        },
        {
          "heading": "full-policy",
          "content": "Description"
        },
        {
          "heading": "full-policy",
          "content": "`default`"
        },
        {
          "heading": "full-policy",
          "content": "`PermissionAction`"
        },
        {
          "heading": "full-policy",
          "content": "Action when no rule matches. Defaults to `\"ask\"`."
        },
        {
          "heading": "full-policy",
          "content": "`rules`"
        },
        {
          "heading": "full-policy",
          "content": "`PermissionRule[]`"
        },
        {
          "heading": "full-policy",
          "content": "Evaluated in order; the **last** matching rule wins."
        },
        {
          "heading": "full-policy",
          "content": "`disabledTools`"
        },
        {
          "heading": "full-policy",
          "content": "`string[]`"
        },
        {
          "heading": "full-policy",
          "content": "Tools the agent may never call. Stripped from the model's tool list at session start, with an unconditional `deny` backstop for anything registered later (SDK / MCP tools)."
        },
        {
          "heading": "full-policy",
          "content": "`restrictToTools`"
        },
        {
          "heading": "full-policy",
          "content": "`string[]`"
        },
        {
          "heading": "full-policy",
          "content": "If set, the **only** tools the model may see. An allowlist (`disabledTools` is a denylist). `\"readonly\"` expands to this."
        },
        {
          "heading": "permissionrule",
          "content": "Field"
        },
        {
          "heading": "permissionrule",
          "content": "Type"
        },
        {
          "heading": "permissionrule",
          "content": "Description"
        },
        {
          "heading": "permissionrule",
          "content": "`tool`"
        },
        {
          "heading": "permissionrule",
          "content": "`string`"
        },
        {
          "heading": "permissionrule",
          "content": "Tool name or glob (`*` = any run of chars, `?` = one). `\"bash\"`, `\"write\"`, `\"*\"`."
        },
        {
          "heading": "permissionrule",
          "content": "`pattern`"
        },
        {
          "heading": "permissionrule",
          "content": "`string?`"
        },
        {
          "heading": "permissionrule",
          "content": "Glob matched against a tool-specific target: the command for `bash`, the path for `read` / `write` / `edit`, the query for `grep`. **Anchored** — `\"git *\"` matches `\"git status\"` but not `\"x && git status\"`; use `\"*git*\"` for a substring match. Omit to match any call to `tool`."
        },
        {
          "heading": "permissionrule",
          "content": "`action`"
        },
        {
          "heading": "permissionrule",
          "content": "`PermissionAction`"
        },
        {
          "heading": "permissionrule",
          "content": "What to do on a match."
        },
        {
          "heading": "permissionrule",
          "content": "`limit`"
        },
        {
          "heading": "permissionrule",
          "content": "`{ count, windowMs }?`"
        },
        {
          "heading": "permissionrule",
          "content": "`rate_limit` only: the ceiling and rolling window."
        },
        {
          "heading": "actions",
          "content": "Action"
        },
        {
          "heading": "actions",
          "content": "Effect"
        },
        {
          "heading": "actions",
          "content": "`allow`"
        },
        {
          "heading": "actions",
          "content": "Run it, no prompt."
        },
        {
          "heading": "actions",
          "content": "`ask`"
        },
        {
          "heading": "actions",
          "content": "Pause, emit a `permission_request`, wait for a human decision."
        },
        {
          "heading": "actions",
          "content": "`deny`"
        },
        {
          "heading": "actions",
          "content": "Refuse, with a reason the model reads and can adjust to."
        },
        {
          "heading": "actions",
          "content": "`rate_limit`"
        },
        {
          "heading": "actions",
          "content": "Allow up to `limit.count` matching calls per `limit.windowMs`, then deny."
        },
        {
          "heading": "actions",
          "content": "`classify`"
        },
        {
          "heading": "actions",
          "content": "Best-effort read-vs-write triage (today: `bash` / `powershell` only). Splits the command on `&&` / `\\|\\|` / `;` / `\\|` / newline and checks each part against a built-in safe-reader list (`ls`, `cat`, `grep`, `git status`, …). All parts read-only → `allow`; anything unrecognised, a redirect, `sudo`, `rm` → falls through to `ask`. For any other tool, `classify` behaves as `ask`."
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "When the gate pauses a call it emits a `permission_request` event. Answer it with `agent.resolvePermission()`:"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "`PermissionDecision`:"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "`kind`"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "Effect"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "`\"once\"`"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "Allow this one call."
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "`\"always\"`"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "Allow this call and auto-allow every other still-pending request for the same tool. Does **not** persist past the session."
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "`\"reject\"`"
        },
        {
          "heading": "resolving-a-request-from-the-stream",
          "content": "Block the call. `feedback` (if given) becomes the reason the model reads. Every other still-pending request for the same tool is rejected too."
        },
        {
          "heading": "handler-form--prompt-onpermission-",
          "content": "To skip the hand-wired loop, pass an `onPermission` handler. It's called for each request and its return value auto-resolves it; the `permission_request` / `permission_resolved` events still flow through the stream."
        },
        {
          "heading": "inspecting-whats-paused",
          "content": "`agent.listPendingPermissions()` returns the tool calls currently waiting — useful after reconnecting to a session:"
        },
        {
          "heading": "inspecting-whats-paused",
          "content": "An operator reconnecting over `/permission-stream` is replayed the outstanding requests, and the auto-deny timeout is suspended while an operator is attached."
        },
        {
          "heading": "audit-trail",
          "content": "Every request and resolution is written to the ledger as `permission_requested` / `permission_resolved` — **metadata only, never raw tool arguments**. Read it back with any storage adapter or `alineo logs <session>`."
        },
        {
          "heading": "lifecycle-notes",
          "content": "`abort()` auto-rejects any pending approvals; `steer()` leaves them open."
        },
        {
          "heading": "lifecycle-notes",
          "content": "`Alineo.resume()` closes out approvals that were dropped when the previous Pi process ended."
        },
        {
          "heading": "lifecycle-notes",
          "content": "Ambient user extensions (`settings.json`, `.pi/extensions/`) still load but cannot bypass the gate — Pi's hook semantics are first-block-wins."
        },
        {
          "heading": "lifecycle-notes",
          "content": "Fully durable pauses across `sb.pause()` / checkpoint need an upstream Pi change and are tracked as a follow-up."
        },
        {
          "heading": "lifecycle-notes",
          "content": "See the runnable `examples/human-in-the-loop` for an end-to-end walkthrough."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "The permission gate above stops a misbehaving model. To gate a *host* regardless of what runs inside the sandbox, mark a credential binding in `AgentSpec.env` with `approval: \"hold\"`:"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "On `Alineo.load()`:"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "The sandbox starts `defaultAction: \"allow\"` with a single `deny` rule for `api.github.com` — everything else, including the agent's own model traffic, works normally."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "The `GITHUB_TOKEN` credential is **not registered in the vault at all** (the vault refuses a binding whose host isn't allowed)."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "The first outbound request to the held host is denied at the sidecar, which fires a webhook. That pauses the request and calls the `onEgressRequest` handler you passed to `Alineo.load()`."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "On approval the gate opens the egress rule (`sb.egress.patch`) and **then** registers the credential — so the secret literally does not exist inside the sandbox until a human approves."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "`EgressDecision`"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Effect"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "`\"allow-once\"`"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Open the host and inject the credential; both are reversed when the turn ends."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "`\"allow-always\"`"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Open the host and inject the credential permanently (for the agent's life)."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "`\"deny\"`"
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Leave the host denied. The model sees the failed request and moves on."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Loading a spec that has a `\"hold\"` binding **without** an `onEgressRequest` handler throws."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Enforcement is entirely **out-of-process at the egress sidecar** — a compromised in-sandbox agent cannot reach a held host until a human approves, no matter what it does inside the sandbox."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "`agent.pendingEgressRequests()` lists what's waiting; `agent.egressGate` (an `EgressApprovalGate`) is exposed for direct control. The listener is started on `load()` and stopped on `close()`."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "Requests and resolutions land on the ledger as `PermissionRequested` / `PermissionResolved` with `tool: \"network\"`."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "The sidecar reaches the host process at the Docker bridge gateway (`172.17.0.1`) by default — override with `ALINEO_EGRESS_APPROVAL_HOST` for other network topologies."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "The deny-webhook signal is not yet unified into the Pi tool-permission stream, so network\napprovals do **not** appear in `listPendingPermissions()` or the chat UI alongside tool\npermissions. There is also no automatic re-run of the request that hit the denial — the model\nretries on its own (the window is effectively instant). Both are tracked follow-ups."
        },
        {
          "heading": "holding-network-egress-for-approval",
          "content": "See the runnable `examples/agent-egress-approval`."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/quickstart",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/quickstart.md",
      "title": "Quick start",
      "description": "Load an agent spec, send a prompt, and stream the response in minutes.",
      "headings": [
        "Install",
        "Start a local sandbox server",
        "Write an agent spec",
        "Load the agent and send a prompt",
        "Read and write files",
        "Session management",
        "Next steps"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`alineo` wraps a Pi coding agent CLI in an OpenSandbox container and exposes it through a simple TypeScript API. The agent can read and write files inside the sandbox, run shell commands and Python scripts autonomously, and stream its responses back to the host."
        },
        {
          "heading": "start-a-local-sandbox-server",
          "content": "Run OpenSandbox locally. See Core SDK — Installation for the full setup."
        },
        {
          "heading": "start-a-local-sandbox-server",
          "content": "The quickest path:"
        },
        {
          "heading": "write-an-agent-spec",
          "content": "Create `agents/hello-agent.json`:"
        },
        {
          "heading": "write-an-agent-spec",
          "content": "`${GEMINI_API_KEY}` is interpolated from `process.env.GEMINI_API_KEY` at load time."
        },
        {
          "heading": "load-the-agent-and-send-a-prompt",
          "content": "`Alineo.load()` logs timing for each phase. The first run installs the Pi CLI and checkpoints the sandbox — on the order of a minute. Subsequent runs restore from that snapshot in a few seconds:"
        },
        {
          "heading": "load-the-agent-and-send-a-prompt",
          "content": "See Snapshotting for details."
        },
        {
          "heading": "read-and-write-files",
          "content": "`agent.sandbox` gives direct access to the underlying `SandboxHandle` — bypassing Pi entirely:"
        },
        {
          "heading": "next-steps",
          "content": "Snapshotting — how fast restores work and how to control the cache"
        },
        {
          "heading": "next-steps",
          "content": "Workspace setup — bake files and dependencies into the snapshot"
        },
        {
          "heading": "next-steps",
          "content": "Streaming & tool events — observe Pi's tool calls in real time"
        },
        {
          "heading": "next-steps",
          "content": "Alineo API reference — full reference for all methods and types"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/reliability",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/reliability.md",
      "title": "Reliability & error recovery",
      "description": "Handle transient API errors with auto-retry, observe retry events, and abort mid-flight bash commands.",
      "headings": [
        "Auto-retry",
        "Disabling auto-retry",
        "Aborting a retry in progress",
        "Aborting a bash command",
        "Summary"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Pi has built-in transient-error recovery that fires automatically when the AI provider returns a 429 (rate limit) or 5xx (server error). The `alineo` bridge surfaces this as observable events so you can build responsive UIs without writing retry logic yourself."
        },
        {
          "heading": "auto-retry",
          "content": "Auto-retry is **on by default**: Pi retries up to 3 times with exponential backoff (2 s → 4 s → 8 s). You don't need to configure anything for it to work."
        },
        {
          "heading": "auto-retry",
          "content": "When a transient error occurs mid-prompt, Pi pauses internally and emits `auto_retry_start`. After the delay it retries, and on completion emits `auto_retry_end`."
        },
        {
          "heading": "auto-retry",
          "content": "`auto_retry_start` and `auto_retry_end` are part of the `AgentEvent` discriminated union — they're always present in the stream, so no extra setup is required."
        },
        {
          "heading": "disabling-auto-retry",
          "content": "If you want full control over when retries happen — for example to gate them on user confirmation — disable auto-retry and handle failures yourself:"
        },
        {
          "heading": "disabling-auto-retry",
          "content": "Re-enable it at any time with `agent.setAutoRetry(true)`. The setting persists across prompts until changed."
        },
        {
          "heading": "aborting-a-retry-in-progress",
          "content": "If a retry is currently waiting (counting down the backoff delay), you can cancel it immediately:"
        },
        {
          "heading": "aborting-a-retry-in-progress",
          "content": "Pi fails the current operation immediately and emits `auto_retry_end` with `success: false`. No-op when no retry is pending."
        },
        {
          "heading": "aborting-a-bash-command",
          "content": "`agent.abortBash()` stops a currently-executing bash command without cancelling the whole prompt. Pi receives the abort, the bash result is marked as errored, and Pi continues with whatever it would do next (typically reporting the failure to the user):"
        },
        {
          "heading": "aborting-a-bash-command",
          "content": "`abortBash()` is a no-op when no bash command is running — safe to call speculatively."
        },
        {
          "heading": "summary",
          "content": "Method / Event"
        },
        {
          "heading": "summary",
          "content": "What it does"
        },
        {
          "heading": "summary",
          "content": "`setAutoRetry(true)`"
        },
        {
          "heading": "summary",
          "content": "Enable auto-retry on transient errors (default)"
        },
        {
          "heading": "summary",
          "content": "`setAutoRetry(false)`"
        },
        {
          "heading": "summary",
          "content": "Disable — handle `auto_retry_end` failures manually"
        },
        {
          "heading": "summary",
          "content": "`abortRetry()`"
        },
        {
          "heading": "summary",
          "content": "Cancel the current retry countdown; Pi fails immediately"
        },
        {
          "heading": "summary",
          "content": "`abortBash()`"
        },
        {
          "heading": "summary",
          "content": "Stop the running bash command; prompt continues"
        },
        {
          "heading": "summary",
          "content": "`auto_retry_start`"
        },
        {
          "heading": "summary",
          "content": "Fires when a retry attempt is about to begin"
        },
        {
          "heading": "summary",
          "content": "`auto_retry_end`"
        },
        {
          "heading": "summary",
          "content": "Fires when the retry sequence completes (success or exhausted)"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/session-inspection",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/session-inspection.md",
      "title": "Session inspection & control",
      "description": "Inspect token usage, retrieve session history, list available commands, and control how Pi processes queued messages.",
      "headings": [
        "Token usage and cost",
        "Context window pressure",
        "Retrieving the last response",
        "Fork entry points",
        "Available commands",
        "Naming sessions",
        "Exporting an HTML transcript",
        "Queue processing modes",
        "Steering mode",
        "Follow-up mode"
      ],
      "sections": [
        {
          "heading": null,
          "content": "The agent SDK exposes a set of methods for inspecting the current Pi session without streaming a new prompt. These are useful for building dashboards, debugging token usage, navigating session history, and managing how Pi handles queued messages."
        },
        {
          "heading": "token-usage-and-cost",
          "content": "`agent.getSessionStats()` returns a snapshot of the current session's resource consumption:"
        },
        {
          "heading": "token-usage-and-cost",
          "content": "This is a synchronous read from Pi — it doesn't start a new prompt. Call it after any `prompt()` completes to track cumulative usage across a long session."
        },
        {
          "heading": "context-window-pressure",
          "content": "`contextUsage.percent` tells you how full Pi's context window is. Pi will auto-compact when it approaches the limit (see Streaming — Compaction events), but you can also monitor it proactively:"
        },
        {
          "heading": "retrieving-the-last-response",
          "content": "`agent.getLastAssistantText()` returns the text of Pi's most recent assistant message without opening a new stream:"
        },
        {
          "heading": "retrieving-the-last-response",
          "content": "Returns `null` if Pi has not yet responded in the current session. Useful for building \"copy last response\" buttons or logging the final assistant message after a long autonomous run."
        },
        {
          "heading": "fork-entry-points",
          "content": "`agent.getForkMessages()` lists the entry points in the current session that can be used as `fork()` targets:"
        },
        {
          "heading": "fork-entry-points",
          "content": "Each entry corresponds to a user turn in Pi's session history. Forking creates a new session branch at that point — useful for trying alternative responses or replaying from a known state."
        },
        {
          "heading": "available-commands",
          "content": "`agent.getCommands()` lists all slash commands Pi can understand in the current environment — including commands contributed by installed extensions, prompt templates, and skills:"
        },
        {
          "heading": "available-commands",
          "content": "This is primarily useful when building a command palette or auto-complete UI on top of the agent."
        },
        {
          "heading": "naming-sessions",
          "content": "`agent.setSessionName()` sets a display name for the current Pi session. The name is stored as metadata in the session file — it doesn't affect the session ID or behaviour:"
        },
        {
          "heading": "exporting-an-html-transcript",
          "content": "`agent.exportHtml()` generates a static HTML file containing the full session transcript and writes it to the sandbox filesystem:"
        },
        {
          "heading": "exporting-an-html-transcript",
          "content": "You can optionally specify where the file should be written inside the container:"
        },
        {
          "heading": "queue-processing-modes",
          "content": "When `agent.followUp()` or `agent.steer()` are called while a prompt is in flight, Pi queues the message and processes it after the current turn. Two methods control how that queue is consumed."
        },
        {
          "heading": "steering-mode",
          "content": "`agent.setSteeringMode()` controls how Pi applies queued steering messages:"
        },
        {
          "heading": "steering-mode",
          "content": "`\"all\"` (default) — Pi applies every queued steer at once when it processes the queue"
        },
        {
          "heading": "steering-mode",
          "content": "`\"one-at-a-time\"` — Pi applies steers one per turn, giving you finer control over the conversation"
        },
        {
          "heading": "follow-up-mode",
          "content": "`agent.setFollowUpMode()` mirrors `setSteeringMode()` but for follow-up messages queued with `agent.followUp()`:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/snapshotting",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/snapshotting.md",
      "title": "Snapshotting",
      "description": "How Alineo.load() caches the Pi install so subsequent loads take seconds instead of minutes.",
      "headings": [
        "fromSnapshot",
        "Cache invalidation",
        "Force rebuild",
        "How the snapshot store works"
      ],
      "sections": [
        {
          "heading": null,
          "content": "On the first call to `Alineo.load()`, the SDK installs the Pi CLI inside a `node:22` sandbox, runs any workspace setup steps, then checkpoints the container. On every subsequent call it restores from that snapshot — skipping the install entirely."
        },
        {
          "heading": "fromsnapshot",
          "content": "`agent.fromSnapshot` is `true` when the agent was restored from a snapshot:"
        },
        {
          "heading": "cache-invalidation",
          "content": "The snapshot is keyed on a hash of the fields that affect the installed environment:"
        },
        {
          "heading": "cache-invalidation",
          "content": "`cli` — currently always `\"pi\"`"
        },
        {
          "heading": "cache-invalidation",
          "content": "`cliVersion` — pinned version or `\"latest\"`"
        },
        {
          "heading": "cache-invalidation",
          "content": "`packages` — APT packages sorted alphabetically"
        },
        {
          "heading": "cache-invalidation",
          "content": "`setup` — workspace setup steps (names, commands, working directories)"
        },
        {
          "heading": "cache-invalidation",
          "content": "Any change to these fields automatically invalidates the snapshot and triggers a full rebuild on the next `Alineo.load()` call. Fields that don't affect the snapshot — `model`, `provider`, `env`, `resources` — never invalidate the cache."
        },
        {
          "heading": "force-rebuild",
          "content": "Pass `{ rebuild: true }` to force a full reinstall regardless of the cached snapshot:"
        },
        {
          "heading": "force-rebuild",
          "content": "This is useful after updating a package version that isn't tracked through the spec (e.g. a package installed by a setup step that fetches from the internet)."
        },
        {
          "heading": "how-the-snapshot-store-works",
          "content": "Snapshot records are stored in `agent-snapshots.json` alongside the ledger database (`.alineo/agent-snapshots.json` by default). Each record maps `specName + setupHash` → `snapshotId`. On restore, `client.restoreSnapshot(snapshotId)` recreates the container from the saved image."
        },
        {
          "heading": "how-the-snapshot-store-works",
          "content": "If the snapshot no longer exists on the OpenSandbox server (e.g. after a server restart), `Alineo.load()` detects the failure, logs `[agent] snapshot stale, rebuilding...`, and falls back to a full install automatically."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/streaming",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/streaming.md",
      "title": "Streaming & tool events",
      "description": "Observe Pi's text output and tool calls in real time using AgentStream and AgentEvent.",
      "headings": [
        "AgentEvent",
        "Text-only with textOnly()",
        "Observing tool calls",
        "Collecting tool events",
        "bash()",
        "Lifecycle events",
        "Observing thinking and tool-call deltas",
        "Compaction events",
        "Queue events"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`agent.prompt()` and `agent.bash()` return an `AgentStream` — an `AsyncIterable<AgentEvent>`. Each event is a discriminated union that tells you whether Pi is writing text or using a tool."
        },
        {
          "heading": "agentevent",
          "content": "Event"
        },
        {
          "heading": "agentevent",
          "content": "When it fires"
        },
        {
          "heading": "agentevent",
          "content": "`text`"
        },
        {
          "heading": "agentevent",
          "content": "Pi is writing a text chunk"
        },
        {
          "heading": "agentevent",
          "content": "`tool_start`"
        },
        {
          "heading": "agentevent",
          "content": "Pi has invoked a tool (e.g. `bash`, `write_file`)"
        },
        {
          "heading": "agentevent",
          "content": "`tool_update`"
        },
        {
          "heading": "agentevent",
          "content": "Partial output from a long-running tool"
        },
        {
          "heading": "agentevent",
          "content": "`tool_end`"
        },
        {
          "heading": "agentevent",
          "content": "Tool completed — includes the full result and whether it errored"
        },
        {
          "heading": "agentevent",
          "content": "`extension_ui`"
        },
        {
          "heading": "agentevent",
          "content": "A Pi extension requested UI interaction (dialog auto-cancelled; forwarded for observability)"
        },
        {
          "heading": "agentevent",
          "content": "`permission_request`"
        },
        {
          "heading": "agentevent",
          "content": "The permission gate paused a tool call for approval — resolve with `agent.resolvePermission()`"
        },
        {
          "heading": "agentevent",
          "content": "`permission_resolved`"
        },
        {
          "heading": "agentevent",
          "content": "A `permission_request` was answered (by a caller, a batched decision, or the timeout)"
        },
        {
          "heading": "agentevent",
          "content": "`auto_retry_start`"
        },
        {
          "heading": "agentevent",
          "content": "Pi is retrying after a transient error (429, 5xx)"
        },
        {
          "heading": "agentevent",
          "content": "`auto_retry_end`"
        },
        {
          "heading": "agentevent",
          "content": "Retry sequence completed — `success` indicates whether it recovered"
        },
        {
          "heading": "agentevent",
          "content": "`agent_start`"
        },
        {
          "heading": "agentevent",
          "content": "Pi began processing the prompt"
        },
        {
          "heading": "agentevent",
          "content": "`agent_end`"
        },
        {
          "heading": "agentevent",
          "content": "Pi finished the full agent run (all turns complete); includes all messages"
        },
        {
          "heading": "agentevent",
          "content": "`turn_start`"
        },
        {
          "heading": "agentevent",
          "content": "A new LLM turn began; includes `turnIndex` and `timestamp`"
        },
        {
          "heading": "agentevent",
          "content": "`turn_end`"
        },
        {
          "heading": "agentevent",
          "content": "A turn completed with its assistant message and tool results"
        },
        {
          "heading": "agentevent",
          "content": "`message_start`"
        },
        {
          "heading": "agentevent",
          "content": "A new assistant message began streaming"
        },
        {
          "heading": "agentevent",
          "content": "`message_update`"
        },
        {
          "heading": "agentevent",
          "content": "Streaming delta from an in-flight message; `delta` is the raw Pi event (text, thinking, tool call delta, etc.)"
        },
        {
          "heading": "agentevent",
          "content": "`message_end`"
        },
        {
          "heading": "agentevent",
          "content": "An assistant message completed"
        },
        {
          "heading": "agentevent",
          "content": "`queue_update`"
        },
        {
          "heading": "agentevent",
          "content": "The steering/follow-up queue changed (e.g. after `followUp()`)"
        },
        {
          "heading": "agentevent",
          "content": "`compaction_start`"
        },
        {
          "heading": "agentevent",
          "content": "Pi began compacting context (manual or automatic)"
        },
        {
          "heading": "agentevent",
          "content": "`compaction_end`"
        },
        {
          "heading": "agentevent",
          "content": "Context compaction finished; `result` has token counts, `aborted` if it was cancelled"
        },
        {
          "heading": "agentevent",
          "content": "`extension_error`"
        },
        {
          "heading": "agentevent",
          "content": "A Pi extension threw an error"
        },
        {
          "heading": "text-only-with-textonly",
          "content": "If you only care about the text output, use the `textOnly()` helper to filter the stream:"
        },
        {
          "heading": "text-only-with-textonly",
          "content": "`textOnly()` is a thin generator that passes through `text` events and drops everything else. It returns `AsyncIterable<string>` — the same interface as the old `PromptStream`."
        },
        {
          "heading": "observing-tool-calls",
          "content": "Iterate the raw `AgentStream` to see every tool Pi invokes:"
        },
        {
          "heading": "collecting-tool-events",
          "content": "You can collect tool events alongside the text response:"
        },
        {
          "heading": "bash",
          "content": "`agent.bash()` runs a shell command inside Pi's working context and returns the same `AgentStream` type — but unlike `prompt()`, it's not incrementally streamed. Pi returns bash output synchronously, so the full output arrives as a single `text` event once the command completes; no `tool_start`/`tool_update`/`tool_end` events are emitted for it:"
        },
        {
          "heading": "lifecycle-events",
          "content": "Every `prompt()` call fires a sequence of lifecycle events around the text and tool events. These are useful for measuring per-turn latency, building progress indicators, or capturing the full structured response."
        },
        {
          "heading": "lifecycle-events",
          "content": "The event order within a single turn is always:"
        },
        {
          "heading": "observing-thinking-and-tool-call-deltas",
          "content": "`message_update` carries the raw Pi delta in its `delta` field. In addition to `text_delta` (which the bridge also surfaces as a `text` event), it can carry thinking and tool-call deltas from models that support extended thinking:"
        },
        {
          "heading": "compaction-events",
          "content": "Auto-compaction fires between turns when Pi's context fills past a threshold. The `compaction_start` and `compaction_end` events let you surface this to the user instead of silently pausing:"
        },
        {
          "heading": "compaction-events",
          "content": "`reason` is one of `\"manual\"` (triggered by `agent.compact()`), `\"threshold\"` (auto, approaching context limit), or `\"overflow\"` (auto, context was full)."
        },
        {
          "heading": "queue-events",
          "content": "`queue_update` fires when the steering or follow-up queue changes — for example, immediately after `agent.followUp()` is called while a prompt is in flight:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/getting-started/workspace-setup",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/getting-started/workspace-setup.md",
      "title": "Workspace setup",
      "description": "Declarative bash steps that run after Pi install and are baked into the snapshot.",
      "headings": [
        "Defining setup steps",
        "Log output",
        "Cache invalidation",
        "Self-contained steps"
      ],
      "sections": [
        {
          "heading": null,
          "content": "The `setup` field in an agent spec lets you declare a list of named bash commands that run inside the sandbox after the Pi CLI is installed, before the snapshot is taken. Because the steps are baked into the snapshot, they only run once — subsequent `Alineo.load()` calls restore the prepared workspace in seconds."
        },
        {
          "heading": "defining-setup-steps",
          "content": "`env` values are normally plain strings, but any entry can instead be an object describing a credential to inject rather than a container environment variable — the value never becomes part of the sandbox's own `env` at all:"
        },
        {
          "heading": "defining-setup-steps",
          "content": "See Credentials for what injection means and how it behaves across `resume()`/`fork()`/spawned children."
        },
        {
          "heading": "defining-setup-steps",
          "content": "Each step is a `SetupStep`:"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Field"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Type"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Required"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Description"
        },
        {
          "heading": "defining-setup-steps",
          "content": "`name`"
        },
        {
          "heading": "defining-setup-steps",
          "content": "`string`"
        },
        {
          "heading": "defining-setup-steps",
          "content": "yes"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Human-readable label shown in logs and included in the setup hash"
        },
        {
          "heading": "defining-setup-steps",
          "content": "`run`"
        },
        {
          "heading": "defining-setup-steps",
          "content": "`string`"
        },
        {
          "heading": "defining-setup-steps",
          "content": "yes"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Bash command to execute"
        },
        {
          "heading": "defining-setup-steps",
          "content": "`cwd`"
        },
        {
          "heading": "defining-setup-steps",
          "content": "`string`"
        },
        {
          "heading": "defining-setup-steps",
          "content": "no"
        },
        {
          "heading": "defining-setup-steps",
          "content": "Working directory. The command runs as `cd <cwd> && <run>`"
        },
        {
          "heading": "log-output",
          "content": "Setup steps are logged with timing between the Pi install and the checkpoint:"
        },
        {
          "heading": "log-output",
          "content": "On subsequent loads, none of these lines appear — the workspace is already in the snapshot."
        },
        {
          "heading": "cache-invalidation",
          "content": "Any change to a step's `name`, `run`, or `cwd` is included in the setup hash and automatically invalidates the snapshot. The next `Alineo.load()` will run all steps again from scratch and create a new checkpoint."
        },
        {
          "heading": "self-contained-steps",
          "content": "Setup steps that fetch from the internet (e.g. `git clone`, `npm install`) will re-fetch on every cache bust. For reproducible builds, pin versions explicitly:"
        },
        {
          "heading": "self-contained-steps",
          "content": "For fully self-contained steps that don't require network access at all:"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/api-reference/agent",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/api-reference/agent.md",
      "title": "Alineo",
      "description": "Complete reference for the Alineo class, AgentSpec, AgentStream, and all Pi RPC commands.",
      "headings": [
        "Alineo",
        "Alineo.load()",
        "Alineo.resume()",
        "Alineo.attach()",
        "Spawning child agents",
        "agent.spawn()",
        "Streaming",
        "agent.prompt()",
        "agent.bash()",
        "Mid-flight control",
        "agent.steer()",
        "agent.followUp()",
        "agent.abort()",
        "Permissions & approvals",
        "agent.resolvePermission()",
        "agent.listPendingPermissions()",
        "agent.pendingEgressRequests()",
        "agent.egressGate",
        "Session management",
        "agent.newSession()",
        "agent.clone()",
        "agent.fork()",
        "agent.switchSession()",
        "agent.getMessages()",
        "Model control",
        "agent.setModel()",
        "agent.cycleModel()",
        "agent.getAvailableModels()",
        "agent.setThinkingLevel()",
        "agent.cycleThinkingLevel()",
        "Reliability",
        "agent.setAutoRetry()",
        "agent.abortRetry()",
        "agent.abortBash()",
        "Session inspection",
        "agent.getSessionStats()",
        "agent.getLastAssistantText()",
        "agent.getForkMessages()",
        "agent.getCommands()",
        "agent.setSessionName()",
        "agent.exportHtml()",
        "Advanced control",
        "agent.setSteeringMode()",
        "agent.setFollowUpMode()",
        "Context management",
        "agent.setAutoCompaction()",
        "agent.compact()",
        "Environment and debugging",
        "agent.setEnv()",
        "agent.getLogs()",
        "agent.close()",
        "Properties",
        "agent.sandbox",
        "agent.sandboxId",
        "agent.name",
        "agent.fromSnapshot",
        "AgentSpec",
        "Example",
        "SetupStep",
        "AgentStream",
        "AgentEvent",
        "textOnly()",
        "SessionStats",
        "PiSlashCommand",
        "alineo.config.json"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`alineo` has no storage-adapter dependency of its own — every method below that takes `opts.adapter` requires it. Use `SQLiteAdapter` from `@alineo-labs/sqlite` for local dev or `PostgresAdapter` from `@alineo-labs/postgres` for production."
        },
        {
          "heading": "alineo",
          "content": "A live AI coding agent running inside an OpenSandbox container. Wraps a Pi CLI process (`pi --mode rpc --approve`) in an HTTP bridge so the host can send prompts and receive streamed responses."
        },
        {
          "heading": "alineoload",
          "content": "Validate `spec`, spin up a `node:22` sandbox, install the Pi CLI and any `setup` steps, and return a ready `Alineo`. `spec` is an already-parsed object, not a file path — read one from disk yourself first (`await Bun.file(path).json()`), fetch it over HTTP, or build it programmatically. It's validated internally regardless (via `validateAgentSpec()`), so a raw `JSON.parse()`'d object works fine."
        },
        {
          "heading": "alineoload",
          "content": "On first load the container is checkpointed after install. Subsequent calls restore from the snapshot — skipping the install and starting in \\~3s instead of \\~90s. See Snapshotting."
        },
        {
          "heading": "alineoload",
          "content": "`spawnDepth`/`maxAgents` override the spec's own fields — see Spawning child agents below."
        },
        {
          "heading": "alineoload",
          "content": "`onEgressRequest` is **required** when the spec has any `env` credential binding with `approval: \"hold\"` — it decides each first outbound request to a held host. See Permission gate — holding network egress."
        },
        {
          "heading": "alineoresume",
          "content": "Reconnect to an existing sandbox after the host process has exited. The container must still be running. Only the bridge process is restarted — Pi and the workspace are untouched. Pi resumes the most recent session via `--continue`."
        },
        {
          "heading": "alineoresume",
          "content": "Unlike `load()`, `resume()` still accepts a bare path (`specPath`) — or, if you already have the spec object in memory, pass it directly via `spec` and skip the read. If neither is set, the ledger is queried for the sandbox's name and the spec is read from `./agents/<name>.json`."
        },
        {
          "heading": "alineoattach",
          "content": "Connect to an already-running sandbox **without** touching its Pi bridge — unlike `resume()`, which kills and restarts the bridge process. Use this when you only need `.spawn()`/`.sandbox`, not `.prompt()`/`.bash()` (the returned `Alineo` has no bridge, so those throw)."
        },
        {
          "heading": "alineoattach",
          "content": "The main caller is `alineo fork`: it runs as a fresh CLI process started BY the very Pi bash-tool call it's attaching to (a session forking a child from inside its own turn) — going through `resume()` there would kill the bridge currently running the call itself."
        },
        {
          "heading": "agentspawn",
          "content": "Fork **this agent's own live sandbox** — filesystem, installed packages, checked-out state, everything currently on disk — into a brand-new independent sandbox running its own Pi bridge. Unlike `Alineo.load()` (always starts from a spec's own snapshot) or `agent.fork()`/`agent.clone()` (Pi's own conversation-branching — same container, same bridge, new session branch), this is sandbox-level forking: the child sees exactly what this agent's sandbox sees right now, including uncommitted work. No install/setup steps run — the child inherits whatever is already installed on this agent's sandbox."
        },
        {
          "heading": "agentspawn",
          "content": "Refuses immediately unless this agent's own spawn-depth budget (`spawnDepth` in the spec, or `opts.spawnDepth` to override) is a positive integer — `0` means no budget left, `undefined` means spawning was never enabled. Each spawn force-decrements the budget (`current - 1`) into the child's env, regardless of what the child's own spec says."
        },
        {
          "heading": "agentspawn",
          "content": "`maxAgents` (spec field or `opts.maxAgents`) is a separate, optional ceiling on total descendants for this lineage, independent of nesting depth. Unset means uncapped for this dimension — only `spawnDepth` gates whether spawning is allowed at all. **Not** coordinated across sibling branches spawned in parallel; it's a per-lineage counter."
        },
        {
          "heading": "agentprompt",
          "content": "Send a message to Pi and stream the response. Pi maintains its own conversation context across calls within a session."
        },
        {
          "heading": "agentprompt",
          "content": "`onPermission` auto-resolves each `permission_request` on the stream with the handler's decision, instead of calling `resolvePermission()` by hand — the events still flow through the stream. Only meaningful when the spec sets `permissions`. See Permission gate."
        },
        {
          "heading": "agentbash",
          "content": "Run a shell command inside Pi's working context. Returns the same `AgentStream` type as `prompt()`, but not incrementally streamed — Pi returns bash output synchronously, so the full output arrives as a single `text` event once the command completes."
        },
        {
          "heading": "agentsteer",
          "content": "Redirect Pi's current response mid-flight. Pi acknowledges the instruction and adjusts its output. Best called after a short delay into a `prompt()` stream."
        },
        {
          "heading": "agentfollowup",
          "content": "Queue a message for Pi to process after it finishes the current task. Pi receives the message as a new turn in the same session once the active response completes."
        },
        {
          "heading": "agentabort",
          "content": "Cancel Pi's current operation. The in-flight `prompt()` stream ends with whatever was generated before the abort. Any pending permission requests are auto-rejected (`steer()` leaves them open)."
        },
        {
          "heading": "permissions--approvals",
          "content": "Active only when `AgentSpec.permissions` is set to something other than `\"auto\"`. See Permission gate for the full model."
        },
        {
          "heading": "agentresolvepermission",
          "content": "Answer a `permission_request` event. `PermissionDecision` is `{ kind: \"once\" }`, `{ kind: \"always\" }`, or `{ kind: \"reject\"; feedback?: string }`. `always` / `reject` also clear every other still-pending request for the same tool."
        },
        {
          "heading": "agentlistpendingpermissions",
          "content": "Tool calls currently paused awaiting a decision — each `{ requestId, tool, target, title, since }`. Useful after reconnecting to a session to discover approvals still outstanding."
        },
        {
          "heading": "agentpendingegressrequests",
          "content": "Outbound network requests to `approval: \"hold\"` hosts currently waiting for an `onEgressRequest` decision — each `{ host, since }`. Empty unless the spec has held credential bindings."
        },
        {
          "heading": "agentegressgate",
          "content": "The host-side listener that holds egress to `approval: \"hold\"` hosts until `onEgressRequest` approves. Present only when the spec has held bindings; started on `load()`, stopped on `close()`."
        },
        {
          "heading": "agentnewsession",
          "content": "Reset Pi's conversation context. Pi forgets all prior messages. The sandbox filesystem is unchanged — files written in previous turns remain."
        },
        {
          "heading": "agentclone",
          "content": "Branch the current Pi session at the current position, creating a new session file. Returns whether the clone was cancelled."
        },
        {
          "heading": "agentfork",
          "content": "Branch from a specific message entry in the conversation history. `entryId` comes from `getForkMessages()` (see below), not `getMessages()` — `PiMessage` has no `id`/`entryId` field. Returns the text of the forked message and whether it was cancelled."
        },
        {
          "heading": "agentswitchsession",
          "content": "Switch Pi to a different session file on disk."
        },
        {
          "heading": "agentgetmessages",
          "content": "Retrieve Pi's full conversation history for the current session."
        },
        {
          "heading": "agentsetmodel",
          "content": "Switch Pi to a specific model. The model must be in Pi's configured model list. Returns the activated `PiModel`."
        },
        {
          "heading": "agentcyclemodel",
          "content": "Cycle Pi to the next configured model. Returns the new model info, or `null` if only one model is configured."
        },
        {
          "heading": "agentgetavailablemodels",
          "content": "List all models available to Pi under the current provider configuration."
        },
        {
          "heading": "agentsetthinkinglevel",
          "content": "Set Pi's reasoning level. Only effective on models that support extended thinking. `level` is `\"none\" | \"low\" | \"medium\" | \"high\"`."
        },
        {
          "heading": "agentcyclethinkinglevel",
          "content": "Cycle Pi's thinking level. Returns `null` if the current model doesn't support thinking."
        },
        {
          "heading": "agentsetautoretry",
          "content": "Enable or disable Pi's automatic retry on transient errors (429, 500, 502, 503, 504). Auto-retry is **on by default**: 3 attempts with exponential backoff (2 s / 4 s / 8 s)."
        },
        {
          "heading": "agentsetautoretry",
          "content": "Disable it when you want to handle errors yourself by observing `auto_retry_start` and `auto_retry_end` events in the stream:"
        },
        {
          "heading": "agentabortretry",
          "content": "Abort an in-progress auto-retry immediately. Pi stops waiting and fails the current operation, emitting `auto_retry_end` with `success: false`."
        },
        {
          "heading": "agentabortbash",
          "content": "Abort a currently-executing bash command without cancelling the whole prompt. No-op when no bash command is running."
        },
        {
          "heading": "agentgetsessionstats",
          "content": "Retrieve token usage, cost, and message counts for the current session."
        },
        {
          "heading": "agentgetsessionstats",
          "content": "See SessionStats for the full type."
        },
        {
          "heading": "agentgetlastassistanttext",
          "content": "Retrieve the text of Pi's most recent assistant response without needing to iterate the stream. Returns `null` if Pi hasn't responded yet in the current session."
        },
        {
          "heading": "agentgetforkmessages",
          "content": "List the fork entry points available in the current session. Each entry has `entryId` (suitable for passing to `fork()`) and `text` (the message content at that point)."
        },
        {
          "heading": "agentgetcommands",
          "content": "List Pi's available slash commands, including extensions, prompt templates, and skills. Returns `PiSlashCommand[]`."
        },
        {
          "heading": "agentsetsessionname",
          "content": "Set a display name for the current Pi session."
        },
        {
          "heading": "agentexporthtml",
          "content": "Export a static HTML transcript of the current session to the sandbox filesystem. Returns the container path of the generated file. Use `agent.sandbox.readFile(path)` to retrieve the contents."
        },
        {
          "heading": "agentsetsteeringmode",
          "content": "Control how Pi processes queued steering messages. `\"all\"` applies all queued steers at once; `\"one-at-a-time\"` applies them sequentially between turns."
        },
        {
          "heading": "agentsetfollowupmode",
          "content": "Control how Pi processes queued follow-up messages. `\"all\"` sends all queued follow-ups at once; `\"one-at-a-time\"` sends them sequentially."
        },
        {
          "heading": "agentsetautocompaction",
          "content": "Enable or disable Pi's automatic context compaction."
        },
        {
          "heading": "agentcompact",
          "content": "Manually trigger Pi's context compaction. Returns a `CompactResult`: `{ summary, firstKeptEntryId, tokensBefore, estimatedTokensAfter }`."
        },
        {
          "heading": "agentsetenv",
          "content": "Merge `vars` into the sandbox environment. Writes to `/etc/alineo-env` inside the container and restarts Pi so it picks up the new values. Awaits Pi's readiness before returning."
        },
        {
          "heading": "agentgetlogs",
          "content": "Retrieve the last 200 bridge log entries as a plain text string. Useful for debugging Pi startup or RPC issues."
        },
        {
          "heading": "agentclose",
          "content": "Delete the sandbox container and release all resources. Always call in a `finally` block."
        },
        {
          "heading": "agentsandbox",
          "content": "Direct access to the underlying `SandboxHandle` — the full Core SDK Sandbox API, bypassing Pi. Use this to read or write files, run shell commands, or inspect the container independently of Pi."
        },
        {
          "heading": "agentsandboxid",
          "content": "OpenSandbox container ID for this agent's sandbox."
        },
        {
          "heading": "agentname",
          "content": "Name from the agent spec."
        },
        {
          "heading": "agentfromsnapshot",
          "content": "`true` when this agent was restored from a cached snapshot (fast path). `false` on first load or after `{ rebuild: true }`."
        },
        {
          "heading": "agentspec",
          "content": "The JSON shape of an agent spec file. Pass the path to `Alineo.load(specPath)`."
        },
        {
          "heading": "agentspec",
          "content": "Field"
        },
        {
          "heading": "agentspec",
          "content": "Type"
        },
        {
          "heading": "agentspec",
          "content": "Description"
        },
        {
          "heading": "agentspec",
          "content": "`name`"
        },
        {
          "heading": "agentspec",
          "content": "`string`"
        },
        {
          "heading": "agentspec",
          "content": "Unique identifier. Used as the sandbox session name."
        },
        {
          "heading": "agentspec",
          "content": "`cli`"
        },
        {
          "heading": "agentspec",
          "content": "`\"pi\"`"
        },
        {
          "heading": "agentspec",
          "content": "CLI to run. Only `\"pi\"` is supported."
        },
        {
          "heading": "agentspec",
          "content": "`cliVersion`"
        },
        {
          "heading": "agentspec",
          "content": "`string?`"
        },
        {
          "heading": "agentspec",
          "content": "npm version specifier for the Pi CLI (e.g. `\"1.2.3\"`, `\"^1.2.0\"`, or a dist-tag like `\"latest\"`). Passed to `npm install -g @earendil-works/pi-coding-agent@<cliVersion>`. Omit to install whatever npm resolves as latest. Included in the setup-hash cache key, so changing it forces a fresh Pi CLI install."
        },
        {
          "heading": "agentspec",
          "content": "`model`"
        },
        {
          "heading": "agentspec",
          "content": "`string?`"
        },
        {
          "heading": "agentspec",
          "content": "Model ID passed to Pi via `--model`."
        },
        {
          "heading": "agentspec",
          "content": "`provider`"
        },
        {
          "heading": "agentspec",
          "content": "`string?`"
        },
        {
          "heading": "agentspec",
          "content": "AI provider passed via `--provider`. Omit for a direct Google API key."
        },
        {
          "heading": "agentspec",
          "content": "`packages`"
        },
        {
          "heading": "agentspec",
          "content": "`string[]?`"
        },
        {
          "heading": "agentspec",
          "content": "APT packages to install before Pi (e.g. `[\"python3\", \"git\"]`)."
        },
        {
          "heading": "agentspec",
          "content": "`env`"
        },
        {
          "heading": "agentspec",
          "content": "`Record<string, string \\| CredentialEnvBinding>?`"
        },
        {
          "heading": "agentspec",
          "content": "Env vars for the sandbox. Values may reference host env: `\"${MY_VAR}\"`. A value can also be a `CredentialEnvBinding` (`{ credential, host, injection, approval? }`) — that key never becomes a container env var; it's registered with the credential broker instead. `approval: \"hold\"` holds egress to `host` until approved. See Credentials and Permission gate."
        },
        {
          "heading": "agentspec",
          "content": "`resources`"
        },
        {
          "heading": "agentspec",
          "content": "`{ cpu: string; memory: string; gpu? }?`"
        },
        {
          "heading": "agentspec",
          "content": "Container resource limits. Falls back to `alineo.config.json` defaults."
        },
        {
          "heading": "agentspec",
          "content": "`setup`"
        },
        {
          "heading": "agentspec",
          "content": "`SetupStep[]?`"
        },
        {
          "heading": "agentspec",
          "content": "Workspace setup steps — run after Pi install, baked into the snapshot."
        },
        {
          "heading": "agentspec",
          "content": "`permissions`"
        },
        {
          "heading": "agentspec",
          "content": "`\"auto\" \\| \"ask\" \\| \"readonly\" \\| PermissionPolicy?`"
        },
        {
          "heading": "agentspec",
          "content": "Human-in-the-loop tool-call gate. Omit (or `\"auto\"`) for the current behavior — no gate. See Permission gate."
        },
        {
          "heading": "agentspec",
          "content": "`title`"
        },
        {
          "heading": "agentspec",
          "content": "`string?`"
        },
        {
          "heading": "agentspec",
          "content": "Human-readable display name."
        },
        {
          "heading": "agentspec",
          "content": "`description`"
        },
        {
          "heading": "agentspec",
          "content": "`string?`"
        },
        {
          "heading": "agentspec",
          "content": "Short description of the agent."
        },
        {
          "heading": "agentspec",
          "content": "`author`"
        },
        {
          "heading": "agentspec",
          "content": "`string?`"
        },
        {
          "heading": "agentspec",
          "content": "Author name."
        },
        {
          "heading": "agentspec",
          "content": "`categories`"
        },
        {
          "heading": "agentspec",
          "content": "`string[]?`"
        },
        {
          "heading": "agentspec",
          "content": "Arbitrary category tags."
        },
        {
          "heading": "agentspec",
          "content": "`metadata`"
        },
        {
          "heading": "agentspec",
          "content": "`Record<string,string>?`"
        },
        {
          "heading": "agentspec",
          "content": "Not read anywhere in `alineo`; has no effect on the sandbox."
        },
        {
          "heading": "agentspec",
          "content": "`registryDependencies`"
        },
        {
          "heading": "agentspec",
          "content": "`string[]?`"
        },
        {
          "heading": "agentspec",
          "content": "Other agent spec URLs. Not read by `alineo` itself — used by `alineo add`, which fetches and saves each one first, depth-first."
        },
        {
          "heading": "agentspec",
          "content": "`spawnDepth`"
        },
        {
          "heading": "agentspec",
          "content": "`number?`"
        },
        {
          "heading": "agentspec",
          "content": "Nesting-depth budget for `agent.spawn()`, force-decremented into each child's env. Non-negative integer. Required (directly or via `--depth`/`opts.spawnDepth`) for `agent.spawn()` to be allowed at all."
        },
        {
          "heading": "agentspec",
          "content": "`maxAgents`"
        },
        {
          "heading": "agentspec",
          "content": "`number?`"
        },
        {
          "heading": "agentspec",
          "content": "Optional cap on total descendants for this lineage, independent of `spawnDepth`. Non-negative integer. Unset means uncapped — unlike `spawnDepth`, omitting this doesn't disable spawning."
        },
        {
          "heading": "setupstep",
          "content": "A single workspace setup step."
        },
        {
          "heading": "setupstep",
          "content": "Field"
        },
        {
          "heading": "setupstep",
          "content": "Type"
        },
        {
          "heading": "setupstep",
          "content": "Required"
        },
        {
          "heading": "setupstep",
          "content": "Description"
        },
        {
          "heading": "setupstep",
          "content": "`name`"
        },
        {
          "heading": "setupstep",
          "content": "`string`"
        },
        {
          "heading": "setupstep",
          "content": "yes"
        },
        {
          "heading": "setupstep",
          "content": "Human-readable label, shown in logs and included in the setup hash"
        },
        {
          "heading": "setupstep",
          "content": "`run`"
        },
        {
          "heading": "setupstep",
          "content": "`string`"
        },
        {
          "heading": "setupstep",
          "content": "yes"
        },
        {
          "heading": "setupstep",
          "content": "Bash command to execute"
        },
        {
          "heading": "setupstep",
          "content": "`cwd`"
        },
        {
          "heading": "setupstep",
          "content": "`string?`"
        },
        {
          "heading": "setupstep",
          "content": "no"
        },
        {
          "heading": "setupstep",
          "content": "Working directory. Runs the command as `cd <cwd> && <run>`"
        },
        {
          "heading": "setupstep",
          "content": "See Workspace setup for details and examples."
        },
        {
          "heading": "agentstream",
          "content": "Returned by `agent.prompt()` and `agent.bash()`. Iterate with `for await` to receive events as Pi generates them:"
        },
        {
          "heading": "agentstream",
          "content": "Use `textOnly(stream)` to filter to just the text chunks."
        },
        {
          "heading": "agentevent",
          "content": "See Streaming & tool events for usage examples."
        },
        {
          "heading": "textonly",
          "content": "Filter an `AgentStream` to just `text` events, yielding the `text` string of each. Equivalent to the old `PromptStream` behavior."
        },
        {
          "heading": "sessionstats",
          "content": "Returned by `agent.getSessionStats()`."
        },
        {
          "heading": "sessionstats",
          "content": "Field"
        },
        {
          "heading": "sessionstats",
          "content": "Type"
        },
        {
          "heading": "sessionstats",
          "content": "Description"
        },
        {
          "heading": "sessionstats",
          "content": "`sessionId`"
        },
        {
          "heading": "sessionstats",
          "content": "`string`"
        },
        {
          "heading": "sessionstats",
          "content": "Pi's internal session identifier"
        },
        {
          "heading": "sessionstats",
          "content": "`sessionFile`"
        },
        {
          "heading": "sessionstats",
          "content": "`string?`"
        },
        {
          "heading": "sessionstats",
          "content": "Path to the session file on disk"
        },
        {
          "heading": "sessionstats",
          "content": "`userMessages`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Number of user turns"
        },
        {
          "heading": "sessionstats",
          "content": "`assistantMessages`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Number of assistant turns"
        },
        {
          "heading": "sessionstats",
          "content": "`toolCalls`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Total tool calls made"
        },
        {
          "heading": "sessionstats",
          "content": "`toolResults`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Total tool results received"
        },
        {
          "heading": "sessionstats",
          "content": "`totalMessages`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Sum of all messages"
        },
        {
          "heading": "sessionstats",
          "content": "`tokens.input`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Input tokens consumed"
        },
        {
          "heading": "sessionstats",
          "content": "`tokens.output`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Output tokens generated"
        },
        {
          "heading": "sessionstats",
          "content": "`tokens.cacheRead`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Tokens read from prompt cache"
        },
        {
          "heading": "sessionstats",
          "content": "`tokens.cacheWrite`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Tokens written to prompt cache"
        },
        {
          "heading": "sessionstats",
          "content": "`tokens.total`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Total tokens (all categories)"
        },
        {
          "heading": "sessionstats",
          "content": "`cost`"
        },
        {
          "heading": "sessionstats",
          "content": "`number`"
        },
        {
          "heading": "sessionstats",
          "content": "Estimated cost in USD"
        },
        {
          "heading": "sessionstats",
          "content": "`contextUsage`"
        },
        {
          "heading": "sessionstats",
          "content": "`object?`"
        },
        {
          "heading": "sessionstats",
          "content": "`{ tokens, contextWindow, percent }` — context fill"
        },
        {
          "heading": "pislashcommand",
          "content": "Returned by `agent.getCommands()`."
        },
        {
          "heading": "pislashcommand",
          "content": "Field"
        },
        {
          "heading": "pislashcommand",
          "content": "Type"
        },
        {
          "heading": "pislashcommand",
          "content": "Description"
        },
        {
          "heading": "pislashcommand",
          "content": "`name`"
        },
        {
          "heading": "pislashcommand",
          "content": "`string`"
        },
        {
          "heading": "pislashcommand",
          "content": "Invokable command name (without the leading `/`)"
        },
        {
          "heading": "pislashcommand",
          "content": "`description`"
        },
        {
          "heading": "pislashcommand",
          "content": "`string?`"
        },
        {
          "heading": "pislashcommand",
          "content": "Human-readable description"
        },
        {
          "heading": "pislashcommand",
          "content": "`source`"
        },
        {
          "heading": "pislashcommand",
          "content": "`\"extension\" \\| \"prompt\" \\| \"skill\"`"
        },
        {
          "heading": "pislashcommand",
          "content": "Where the command comes from"
        },
        {
          "heading": "pislashcommand",
          "content": "`sourceInfo`"
        },
        {
          "heading": "pislashcommand",
          "content": "`unknown`"
        },
        {
          "heading": "pislashcommand",
          "content": "Metadata about the owning resource"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`alineo` reads `alineo.config.json` from the current working directory to configure the client. All fields are optional."
        },
        {
          "heading": "alineoconfigjson",
          "content": "Field"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Default"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Description"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`serverUrl`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`http://127.0.0.1:8080`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "OpenSandbox server URL"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`apiKey`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`\"\"`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "OpenSandbox API key (empty for local dev)"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`adapterPath`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`./.alineo/ledger.db`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Path to the SQLite ledger database"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`useServerProxy`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`true`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Route execd traffic through the server. Required when using `alineo init`."
        },
        {
          "heading": "alineoconfigjson",
          "content": "`agentsDir`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`./agents`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Directory containing agent spec files"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`defaults.resources`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`{ cpu: \"1000m\", memory: \"1Gi\" }`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Default resource limits when the spec omits `resources`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`alineo init` writes this file automatically."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/agent/api-reference",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/agent/api-reference.md",
      "title": "API Reference",
      "description": "Complete reference for the Alineo SDK.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/agent/api-reference/agent\" title=\"Alineo\" description=\"Complete reference for the Alineo class, AgentSpec, AgentStream, and all Pi RPC commands.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo.md",
      "title": "alineo",
      "description": "alineo — a local CLI built on the alineo SDK package: start a local OpenSandbox server, manage agent spec files, and spawn/prompt/fork agent sessions directly from the shell.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "<Card href=\"/docs/alineo/getting-started\" title=\"Getting Started\" description=\"Install alineo, start a local OpenSandbox server, and fetch your first agent spec.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/alineo/commands\" title=\"Commands\" description=\"Full reference for every alineo command — SDK config, spec management, and agent session lifecycle.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/alineo/registry\" title=\"Registry Format\" description=\"Publish your own agent spec files so others can fetch them with a single command.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/alineo/using-sandboxes\" title=\"Running an agent spec\" description=\"Load an agent spec saved by alineo add with Alineo.load().\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/using-sandboxes",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/using-sandboxes.md",
      "title": "Running an agent spec",
      "description": "Load an agent spec saved by alineo add with Alineo.load() — alineo add itself never creates a sandbox.",
      "headings": [
        "Setup",
        "Loading and prompting",
        "What happens on load",
        "Accessing the underlying sandbox",
        "Multiple agents",
        "Lifecycle",
        "See also"
      ],
      "sections": [
        {
          "heading": null,
          "content": "`alineo add` only fetches and saves an `AgentSpec` JSON file — it never creates a sandbox, runs setup, or checkpoints anything. To actually run the agent programmatically, load the spec with `alineo`'s `Alineo.load()`, as shown below. If you just want to run it from the shell without writing TypeScript, use `alineo spawn` instead — it wraps the same `Alineo.load()` call."
        },
        {
          "heading": "loading-and-prompting",
          "content": "`Alineo.load()` reads `alineo.config.json` itself — the same file `alineo init` writes — for the OpenSandbox server URL, `useServerProxy`, and default resource limits. You don't construct a `Sandbox` client or pass connection details by hand — the one thing you still construct yourself is the storage adapter (`opts.adapter`), since `alineo` doesn't depend on any specific one (`SQLiteAdapter` for local dev, `PostgresAdapter` for production)."
        },
        {
          "heading": "what-happens-on-load",
          "content": "**First load for a spec**: spins up a `node:22` sandbox, installs the Pi CLI and any `setup` steps from the spec, then checkpoints the sandbox. This is the slow path."
        },
        {
          "heading": "what-happens-on-load",
          "content": "**Subsequent loads**: restore from that checkpoint, skipping the install entirely — much faster. The snapshot is keyed on a hash of `cli`, `cliVersion`, `packages`, and `setup`, so changing any of those forces a fresh install on the next load."
        },
        {
          "heading": "what-happens-on-load",
          "content": "Pass `{ rebuild: true }` to force a full reinstall regardless of the cache: `Alineo.load(specPath, { adapter, rebuild: true })`."
        },
        {
          "heading": "what-happens-on-load",
          "content": "See Snapshotting for the full details."
        },
        {
          "heading": "accessing-the-underlying-sandbox",
          "content": "`agent.sandbox` is the live `SandboxHandle` from `@alineo-labs/core` — use it for anything not covered by the `Alineo` API, like reading files or exposing a port:"
        },
        {
          "heading": "multiple-agents",
          "content": "Each `Alineo.load()` call for the same spec restores an independent sandbox from the same checkpoint — running one doesn't affect another:"
        },
        {
          "heading": "lifecycle",
          "content": "`Alineo.load()` creates a sandbox (or resumes one). It costs resources until `agent.close()` is called."
        },
        {
          "heading": "lifecycle",
          "content": "Always call `agent.close()` when done — use `try/finally` to ensure it runs on error."
        },
        {
          "heading": "lifecycle",
          "content": "The checkpoint from the first load is preserved. You can `Alineo.load()` the same spec as many times as needed without repeating setup."
        },
        {
          "heading": "see-also",
          "content": "alineo overview — the full `Alineo` API: prompting, streaming, sessions, snapshotting"
        },
        {
          "heading": "see-also",
          "content": "alineo core SDK — exec"
        },
        {
          "heading": "see-also",
          "content": "alineo core SDK — file operations"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/getting-started",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/getting-started.md",
      "title": "What is alineo?",
      "description": "alineo starts a local OpenSandbox server, manages alineo spec files, and drives agent sessions directly from the command line — spawn, prompt, fork, and orchestrate without writing TypeScript.",
      "headings": [
        "When to use alineo",
        "How it relates to the `alineo` SDK package",
        "Next steps"
      ],
      "sections": [
        {
          "heading": null,
          "content": "The `alineo` CLI is built on the `alineo` SDK package. It covers two layers:"
        },
        {
          "heading": null,
          "content": "**SDK config and spec management** — `init` starts a local OpenSandbox server; `add`/`list`/`remove` manage `AgentSpec` JSON files on disk. Neither of these touches a sandbox."
        },
        {
          "heading": null,
          "content": "**Alineo session lifecycle** — `spawn`/`prompt`/`fork`/`agents`/`kill`/`logs` wrap `Alineo.load()`/`resume()`/`attach()`/`spawn()` directly, so you can start, talk to, and orchestrate agent sessions entirely from the shell — including recursive forking, where a running session forks its own live sandbox into child agents (see `alineo fork` in the Commands reference)."
        },
        {
          "heading": null,
          "content": "The first command starts a local OpenSandbox server via Docker and writes `alineo.config.json` to your project. The second fetches an agent spec from a URL (or local file) and saves it under `./agents/<name>.json`. The third starts a real agent sandbox from that spec, sends one prompt, and prints the reply — no TypeScript required."
        },
        {
          "heading": "when-to-use-alineo",
          "content": "You want a local OpenSandbox server running without manually configuring or running `uvx opensandbox-server`."
        },
        {
          "heading": "when-to-use-alineo",
          "content": "You want to fetch a shared agent spec (e.g. a pre-configured Pi coding-agent setup) from a URL instead of writing the JSON by hand."
        },
        {
          "heading": "when-to-use-alineo",
          "content": "You want to start, prompt, or orchestrate agent sessions directly from the shell or from inside a Pi session (see the Pi extension), without writing a TypeScript entry point."
        },
        {
          "heading": "how-it-relates-to-the-alineo-sdk-package",
          "content": "`alineo spawn`/`prompt`/`fork`/`agents`/`kill`/`logs` are thin CLI wrappers around the `alineo` SDK package's own `Alineo` class — same behavior, same `alineo.config.json`, same ledger. Reach for the `alineo` SDK package directly instead of the CLI when you need to drive an agent programmatically (streaming responses in your own app, custom tool-event handling, etc.):"
        },
        {
          "heading": "how-it-relates-to-the-alineo-sdk-package",
          "content": "`Alineo.load()` reads `alineo.config.json` itself (the same file `alineo init` writes) to know where the OpenSandbox server is — you don't pass connection details manually."
        },
        {
          "heading": "next-steps",
          "content": "<Card href=\"/docs/alineo/getting-started/quickstart\" title=\"Quickstart\" description=\"Install alineo, start OpenSandbox, and fetch your first agent spec.\" />"
        },
        {
          "heading": "next-steps",
          "content": "<Card href=\"/docs/alineo/commands\" title=\"Commands\" description=\"Full reference for every alineo command.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/getting-started/quickstart",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/getting-started/quickstart.md",
      "title": "Quickstart",
      "description": "Start a local OpenSandbox server and fetch your first agent spec in under five minutes.",
      "headings": [
        "Prerequisites",
        "Start OpenSandbox locally",
        "Fetch an agent spec",
        "List your saved specs",
        "Run the agent",
        "Remove a spec",
        "Next steps"
      ],
      "sections": [
        {
          "heading": "prerequisites",
          "content": "Docker installed and running."
        },
        {
          "heading": "prerequisites",
          "content": "Bun installed."
        },
        {
          "heading": "start-opensandbox-locally",
          "content": "Run `alineo init` once per project. It starts an OpenSandbox server in Docker and writes a `alineo.config.json` to your project root."
        },
        {
          "heading": "start-opensandbox-locally",
          "content": "If the server is already running from a previous `init`, `alineo init` prints the URL and exits immediately without starting a second container."
        },
        {
          "heading": "fetch-an-agent-spec",
          "content": "Point `alineo add` at any URL (or local file path) that returns a valid `AgentSpec` JSON:"
        },
        {
          "heading": "fetch-an-agent-spec",
          "content": "`alineo add` fetches the spec, validates it (`name` and `cli: \"pi\"` are required), resolves any `registryDependencies` first (fetched recursively, depth-first), and writes it to `<agentsDir>/<name>.json` — `agentsDir` defaults to `./agents`. It does **not** create a sandbox, run setup, or checkpoint anything — that happens later, the first time you `Alineo.load()` the spec."
        },
        {
          "heading": "run-the-agent",
          "content": "`Alineo.load()` (from `alineo`) does the actual sandbox work: on first load it spins up a `node:22` sandbox, installs the Pi CLI and any `setup` steps, then checkpoints it. Subsequent loads restore from that snapshot, skipping the install."
        },
        {
          "heading": "run-the-agent",
          "content": "`Alineo.load()` reads `alineo.config.json` itself for the OpenSandbox server URL and other defaults — the same file `alineo init` wrote in step 1. You don't need to construct a `Sandbox` client or pass connection details by hand — the one thing you still construct yourself is the storage adapter (`opts.adapter`), since `alineo` doesn't depend on any specific one."
        },
        {
          "heading": "remove-a-spec",
          "content": "This deletes `agents/code-reviewer.json`. It's purely a local file operation — no sandbox or snapshot is touched. If you'd previously run `Alineo.load()` on that spec, its sandbox snapshot (tracked separately, in `agent-snapshots.json` next to your ledger) is unaffected."
        },
        {
          "heading": "next-steps",
          "content": "Commands reference — all flags for each command"
        },
        {
          "heading": "next-steps",
          "content": "Registry format — publish your own agent spec"
        },
        {
          "heading": "next-steps",
          "content": "Running an agent spec — full details on `Alineo.load()`"
        },
        {
          "heading": "next-steps",
          "content": "alineo overview — the full Alineo API (prompting, streaming, sessions, snapshotting)"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/add",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/add.md",
      "title": "alineo add",
      "description": "Fetch an agent spec from a URL or local file and save it to your project's agents directory.",
      "headings": [
        "Arguments",
        "Options",
        "What it does",
        "Examples",
        "Output",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`url`"
        },
        {
          "heading": "arguments",
          "content": "URL or local file path to an `AgentSpec` JSON. Required."
        },
        {
          "heading": "options",
          "content": "Option"
        },
        {
          "heading": "options",
          "content": "Description"
        },
        {
          "heading": "options",
          "content": "`--name <name>`"
        },
        {
          "heading": "options",
          "content": "Override the saved file's name. Defaults to the `name` field in the spec."
        },
        {
          "heading": "options",
          "content": "There is no `--server` flag — `add` doesn't talk to a server at all, it only reads and writes local files."
        },
        {
          "heading": "what-it-does",
          "content": "Reads `alineo.config.json` for `agentsDir` (defaults to `./agents`). If no project-local `alineo.config.json` exists, falls back to a global config at `~/.config/alineo/config.json`, auto-creating it with defaults if that's missing too — `alineo init` is not actually required first."
        },
        {
          "heading": "what-it-does",
          "content": "Fetches the URL (or reads the local file) and validates it as an `AgentSpec`: `name` must be a string, `cli` must be `\"pi\"`. All other fields are unchecked."
        },
        {
          "heading": "what-it-does",
          "content": "Resolves any `registryDependencies` first — each dependency URL is fetched and saved the same way, recursively, depth-first, before the top-level spec is saved. Dependencies are **not** deduplicated across separate `add` invocations, or within recursive resolution of the same tree — a dependency listed twice is fetched and saved twice."
        },
        {
          "heading": "what-it-does",
          "content": "Writes the validated spec to `<agentsDir>/<name>.json`."
        },
        {
          "heading": "what-it-does",
          "content": "`add` never creates a sandbox, runs `setup`, or contacts the OpenSandbox server directly — it's a local file operation. The sandbox work happens later, the first time you call `Alineo.load()` on the saved spec (see Running an agent spec)."
        },
        {
          "heading": "examples",
          "content": "**From a URL:**"
        },
        {
          "heading": "examples",
          "content": "**From a local file:**"
        },
        {
          "heading": "examples",
          "content": "**With a custom name:**"
        },
        {
          "heading": "notes",
          "content": "`alineo add` does not require `alineo init` to have been run first — it falls back to a global config, auto-creating one with defaults if none exists yet."
        },
        {
          "heading": "notes",
          "content": "If a spec's `registryDependencies` chain is large or has cycles, `add` will fetch and save every one of them without any cycle detection — be careful publishing self-referential or deeply nested dependency chains."
        },
        {
          "heading": "notes",
          "content": "Re-running `add` on the same URL overwrites the previously saved spec file with the latest content from the URL."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/agents",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/agents.md",
      "title": "alineo agents",
      "description": "List running agent sessions, cross-checked against the live OpenSandbox control plane.",
      "headings": [
        "Flags",
        "What it does",
        "Example output",
        "Notes"
      ],
      "sections": [
        {
          "heading": "flags",
          "content": "Flag"
        },
        {
          "heading": "flags",
          "content": "Description"
        },
        {
          "heading": "flags",
          "content": "`--json`"
        },
        {
          "heading": "flags",
          "content": "Print the tracked sessions array as JSON instead of a table."
        },
        {
          "heading": "what-it-does",
          "content": "Reads the ledger's \"Running\" entries."
        },
        {
          "heading": "what-it-does",
          "content": "Cross-checks each one against a **live** query to the OpenSandbox control plane, not the ledger alone. The ledger's \"Running\" status only updates on a graceful `agent.close()`/`sb.close()` — a sandbox that crashed, was killed out-of-band, or expired via OpenSandbox's own TTL stays \"Running\" in the ledger forever with nothing to correct it. Entries that no longer exist on the control plane are silently dropped from the table."
        },
        {
          "heading": "what-it-does",
          "content": "Also lists sandboxes actually running on the same OpenSandbox server that **aren't** in this CLI invocation's own ledger at all — labeled \"Untracked\". This is the normal case for a child sandbox created via `alineo fork` from inside another sandbox, since that fork uses the child sandbox's *own* internal ledger, not the host's."
        },
        {
          "heading": "example-output",
          "content": "Column"
        },
        {
          "heading": "example-output",
          "content": "Description"
        },
        {
          "heading": "example-output",
          "content": "`NAME`"
        },
        {
          "heading": "example-output",
          "content": "The session name from the ledger. Truncated to 19 characters."
        },
        {
          "heading": "example-output",
          "content": "`SANDBOX ID`"
        },
        {
          "heading": "example-output",
          "content": "The full sandbox ID — pass this to `alineo prompt`, `alineo kill`, `alineo logs`."
        },
        {
          "heading": "example-output",
          "content": "`STARTED`"
        },
        {
          "heading": "example-output",
          "content": "Relative time since the sandbox was created."
        },
        {
          "heading": "example-output",
          "content": "`EXECS`"
        },
        {
          "heading": "example-output",
          "content": "Number of top-level execs logged against this sandbox in the ledger — **not** the number of individual bash commands a Pi session ran internally, since those happen inside the single long-running bridge process exec, not as separate ledger entries."
        },
        {
          "heading": "example-output",
          "content": "With `--json`, only the tracked (named) sessions are printed — untracked sandbox IDs are omitted from the JSON output."
        },
        {
          "heading": "notes",
          "content": "If nothing is running, prints `(no running alineo-tracked sessions — run 'alineo spawn <spec>' to start one)` instead of an empty table."
        },
        {
          "heading": "notes",
          "content": "The live control-plane check means `alineo agents` makes a real network call on every invocation — it's not a pure local-file read like `alineo list`."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/fork",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/fork.md",
      "title": "alineo fork",
      "description": "Fork a running session's own live sandbox into a brand-new independent child agent.",
      "headings": [
        "Arguments",
        "Flags",
        "What it does",
        "Example",
        "Recursive spawning budgets",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`name`"
        },
        {
          "heading": "arguments",
          "content": "The **caller's own** running session — used only to resolve its sandbox ID, not the child's name. Required."
        },
        {
          "heading": "arguments",
          "content": "`child-spec`"
        },
        {
          "heading": "arguments",
          "content": "Path to the child's agent spec JSON file. Required."
        },
        {
          "heading": "flags",
          "content": "Flag"
        },
        {
          "heading": "flags",
          "content": "Description"
        },
        {
          "heading": "flags",
          "content": "`--prompt <msg>`"
        },
        {
          "heading": "flags",
          "content": "Send one prompt to the child immediately after it's ready, and print the reply before exiting."
        },
        {
          "heading": "flags",
          "content": "`--depth <n>`"
        },
        {
          "heading": "flags",
          "content": "Override the caller's own `spawnDepth` budget instead of reading `ALINEO_SPAWN_DEPTH` from the environment."
        },
        {
          "heading": "flags",
          "content": "`--max <n>`"
        },
        {
          "heading": "flags",
          "content": "Override the caller's own `maxAgents` budget instead of reading `ALINEO_MAX_AGENTS` from the environment."
        },
        {
          "heading": "flags",
          "content": "`--json`"
        },
        {
          "heading": "flags",
          "content": "Print `{ name, sandboxId, reply }` as JSON instead of the default text output."
        },
        {
          "heading": "what-it-does",
          "content": "`alineo fork` is meant to be run **by a session's own Pi bash tool**, not from a fresh host shell — `name` labels the *caller's own* running session, resolved to a sandbox ID two ways:"
        },
        {
          "heading": "what-it-does",
          "content": "If `ALINEO_SANDBOX_ID` is set in this process's own environment (true when running as a descendant of a Pi bridge process inside a alineo-managed sandbox — which is exactly the case when a Pi session's bash tool runs `alineo fork`), that's used directly."
        },
        {
          "heading": "what-it-does",
          "content": "Otherwise, `name` is looked up against the ledger's running sessions."
        },
        {
          "heading": "what-it-does",
          "content": "The caller's sandbox is then resolved via `Alineo.attach()` — **not** `Alineo.resume()`, which would kill the very bridge process making this call — and `agent.spawn(childSpec, { spawnDepth, maxAgents })` forks its live sandbox into the child."
        },
        {
          "heading": "what-it-does",
          "content": "Unlike `alineo spawn` (always starts from a spec's own snapshot), the child sees exactly what the calling sandbox sees right now — filesystem, installed packages, uncommitted work — with no install/setup steps."
        },
        {
          "heading": "recursive-spawning-budgets",
          "content": "A spec's `spawnDepth` is a nesting-depth budget, force-decremented (`current - 1`) into the child's env on every fork. `0` means no budget left — `alineo fork` refuses immediately with an error. `undefined` means forking was never enabled for that spec at all."
        },
        {
          "heading": "recursive-spawning-budgets",
          "content": "`maxAgents` is a separate, optional ceiling on total descendants for **one lineage**, independent of nesting depth. Unset means uncapped. It is **not** coordinated across sibling branches spawned in parallel — it's a per-lineage counter, checked and decremented independently down each branch, not a global budget shared across an entire tree."
        },
        {
          "heading": "notes",
          "content": "The forked child's environment is resolved fresh from **its own** spec — nothing is inherited from the caller except the force-computed spawn-depth counter. Every env var the caller's own spec declares is explicitly unset in the shell command that starts the child's bridge, since the forked container's OS-level env otherwise still carries whatever was baked in at snapshot time."
        },
        {
          "heading": "notes",
          "content": "No install or setup steps run for the child — it inherits whatever is already installed on the caller's sandbox. If the child needs packages the caller doesn't have, add them to a setup step on the spec the **caller** was loaded from, not the child's."
        },
        {
          "heading": "notes",
          "content": "See the Pi extension section on the commands index for how a Pi session gets `alineo fork` syntax injected into its own guidance automatically."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands.md",
      "title": "Commands",
      "description": "All alineo commands and their options.",
      "headings": [
        "SDK — OpenSandbox config and the local spec cache",
        "Alineo — session lifecycle",
        "Recursive spawning (`alineo fork`)",
        "Pi extension"
      ],
      "sections": [
        {
          "heading": "sdk--opensandbox-config-and-the-local-spec-cache",
          "content": "<Card href=\"/docs/alineo/commands/init\" title=\"init\" description=\"Start a local OpenSandbox server via Docker.\" />"
        },
        {
          "heading": "sdk--opensandbox-config-and-the-local-spec-cache",
          "content": "<Card href=\"/docs/alineo/commands/add\" title=\"add\" description=\"Fetch an agent spec from a URL or file and save it locally.\" />"
        },
        {
          "heading": "sdk--opensandbox-config-and-the-local-spec-cache",
          "content": "<Card href=\"/docs/alineo/commands/list\" title=\"list\" description=\"List the agent specs saved in this project.\" />"
        },
        {
          "heading": "sdk--opensandbox-config-and-the-local-spec-cache",
          "content": "<Card href=\"/docs/alineo/commands/remove\" title=\"remove\" description=\"Delete a saved agent spec file.\" />"
        },
        {
          "heading": "sdk--opensandbox-config-and-the-local-spec-cache",
          "content": "<Card href=\"/docs/alineo/commands/telemetry\" title=\"telemetry\" description=\"Show, enable, or disable anonymous CLI usage telemetry.\" />"
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "These wrap `alineo`'s `Alineo.load()`/`Alineo.resume()`/`Alineo.attach()`/`Alineo.spawn()` directly. Sessions are usually addressed by **sandbox ID** — `prompt` and `kill` require it (see alineo prompt for why). `fork` and `logs` are addressed by session **name** instead — see their own pages for why. `spawn` starts a fresh session and takes a spec path, not an identifier."
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "<Card href=\"/docs/alineo/commands/spawn\" title=\"spawn\" description=\"Start a fresh, independent agent sandbox from a spec's own snapshot.\" />"
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "<Card href=\"/docs/alineo/commands/prompt\" title=\"prompt\" description=\"Send one prompt to a running sandbox and print the reply.\" />"
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "<Card href=\"/docs/alineo/commands/fork\" title=\"fork\" description=\"Fork a running session's own live sandbox into a new child agent.\" />"
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "<Card href=\"/docs/alineo/commands/agents\" title=\"agents\" description=\"List running agent sessions, cross-checked against the live control plane.\" />"
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "<Card href=\"/docs/alineo/commands/kill\" title=\"kill\" description=\"Stop a sandbox by ID.\" />"
        },
        {
          "heading": "alineo--session-lifecycle",
          "content": "<Card href=\"/docs/alineo/commands/logs\" title=\"logs\" description=\"Print ledger events for a session.\" />"
        },
        {
          "heading": "recursive-spawning-alineo-fork",
          "content": "A spec's `spawnDepth` is a nesting-depth budget — required for `alineo fork` to be allowed from inside a session at all. Each fork force-decrements it (`current - 1`) into the child's env; `0` means no budget left, `undefined` means forking was never enabled for that spec."
        },
        {
          "heading": "recursive-spawning-alineo-fork",
          "content": "`maxAgents` is a separate, optional ceiling on total descendants for one lineage, independent of nesting depth. Unset means uncapped. **Not** coordinated across sibling branches spawned in parallel — it's a per-lineage counter, not a global one."
        },
        {
          "heading": "recursive-spawning-alineo-fork",
          "content": "See alineo fork for the full mechanics."
        },
        {
          "heading": "pi-extension",
          "content": "`pi install npm:alineo-cli` installs the alineo extension into Pi at user scope. Once installed, any Pi session:"
        },
        {
          "heading": "pi-extension",
          "content": "Bootstraps `alineo` automatically on first use (installs it, runs `alineo init`) — no manual setup."
        },
        {
          "heading": "pi-extension",
          "content": "Gets `alineo spawn`/`alineo fork` CLI syntax injected into its own guidance, dynamically chosen based on whether the current session is itself running inside a alineo-managed sandbox."
        },
        {
          "heading": "pi-extension",
          "content": "The extension source lives at `pi-extension/alineo.ts` in the `alineo` npm package."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/init",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/init.md",
      "title": "alineo init",
      "description": "Start a local OpenSandbox server in Docker and configure the current project.",
      "headings": [
        "What it does",
        "Output files",
        "`alineo.config.json`",
        "Idempotency",
        "Docker requirements",
        "Stopping the server"
      ],
      "sections": [
        {
          "heading": "what-it-does",
          "content": "Checks that Docker is running."
        },
        {
          "heading": "what-it-does",
          "content": "If an OpenSandbox container named `alineo-opensandbox` is already running, exits immediately — nothing changes."
        },
        {
          "heading": "what-it-does",
          "content": "If the container exists but is stopped, restarts it."
        },
        {
          "heading": "what-it-does",
          "content": "Otherwise, pulls `opensandbox/server:latest` and starts it, mounting the Docker socket so the server can manage sandbox containers."
        },
        {
          "heading": "what-it-does",
          "content": "Waits up to 60 seconds for the server to report healthy."
        },
        {
          "heading": "what-it-does",
          "content": "Writes `alineo.config.json` in the current directory (project root) if it doesn't already exist."
        },
        {
          "heading": "what-it-does",
          "content": "The server it starts is configured with credential injection available out of the box — a sandbox that opts in with `networkPolicy`/`credentialProxy` can use `sb.credentials.*` with no extra setup on the server side. See Credentials. Sandboxes that don't ask for it are unaffected."
        },
        {
          "heading": "alineoconfigjson",
          "content": "Written once, in the project root (not under `.alineo/`). Contains the connection details and defaults used by both the `alineo` CLI and the `alineo` SDK package."
        },
        {
          "heading": "alineoconfigjson",
          "content": "Field"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Description"
        },
        {
          "heading": "alineoconfigjson",
          "content": "`serverUrl`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "The OpenSandbox server URL."
        },
        {
          "heading": "alineoconfigjson",
          "content": "`useServerProxy`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Always `true` when started via `alineo init`. The `alineo` SDK package reads this for its `Sandbox` client."
        },
        {
          "heading": "alineoconfigjson",
          "content": "`apiKey`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Empty for local dev. Set this if your server requires authentication."
        },
        {
          "heading": "alineoconfigjson",
          "content": "`adapterPath`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Path to the SQLite ledger database for this project."
        },
        {
          "heading": "alineoconfigjson",
          "content": "`agentsDir`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "Directory `alineo add`/`list`/`remove` read and write agent spec files in."
        },
        {
          "heading": "alineoconfigjson",
          "content": "`defaults.resources`"
        },
        {
          "heading": "alineoconfigjson",
          "content": "CPU/memory used for an agent's sandbox when its spec omits `resources`."
        },
        {
          "heading": "idempotency",
          "content": "`alineo init` is safe to run multiple times. If the server is already running it prints the URL and exits. If `alineo.config.json` already exists it is not overwritten."
        },
        {
          "heading": "docker-requirements",
          "content": "Docker Engine must be running before calling `alineo init`."
        },
        {
          "heading": "docker-requirements",
          "content": "Port `8080` must be free."
        },
        {
          "heading": "docker-requirements",
          "content": "The Docker socket `/var/run/docker.sock` must be accessible (standard on Linux and macOS with Docker Desktop)."
        },
        {
          "heading": "stopping-the-server",
          "content": "`alineo init` starts the server but does not provide a stop command. To stop it:"
        },
        {
          "heading": "stopping-the-server",
          "content": "To remove the container entirely:"
        },
        {
          "heading": "stopping-the-server",
          "content": "The next `alineo init` will start a fresh container."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/kill",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/kill.md",
      "title": "alineo kill",
      "description": "Stop a sandbox by ID.",
      "headings": [
        "Arguments",
        "What it does",
        "Example",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`sandbox-id`"
        },
        {
          "heading": "arguments",
          "content": "The sandbox ID, as shown by `alineo agents` or printed by `alineo spawn`/`alineo fork`. Required."
        },
        {
          "heading": "what-it-does",
          "content": "Connects to the sandbox via a live control-plane check (the same authoritative liveness check `alineo prompt` uses — see Why sandbox ID, not name) and closes it, deleting the container and releasing its resources."
        },
        {
          "heading": "notes",
          "content": "`kill` only stops the sandbox it's given — it does not recursively stop any children that sandbox may have forked via `alineo fork`. Kill each one individually, or check `alineo agents` for the full list including untracked (agent-spawned) sandboxes."
        },
        {
          "heading": "notes",
          "content": "If the sandbox is already stopped or never existed, the underlying live check fails and `kill` errors out rather than silently succeeding."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/list",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/list.md",
      "title": "alineo list",
      "description": "List the agent specs saved in the current project's agents directory.",
      "headings": [
        "Example output",
        "Notes"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Reads `alineo.config.json` for `agentsDir` (defaults to `./agents`), lists every `.json` file there, and prints a table. It does not query the OpenSandbox server or check whether any sandbox exists for a spec — it only reads local files."
        },
        {
          "heading": "example-output",
          "content": "Column"
        },
        {
          "heading": "example-output",
          "content": "Description"
        },
        {
          "heading": "example-output",
          "content": "`NAME`"
        },
        {
          "heading": "example-output",
          "content": "The spec's `name` field, or the filename (without `.json`) as a fallback. Truncated to 19 characters."
        },
        {
          "heading": "example-output",
          "content": "`CLI`"
        },
        {
          "heading": "example-output",
          "content": "The spec's `cli` field (currently always `\"pi\"`). Truncated to 7 characters."
        },
        {
          "heading": "example-output",
          "content": "`DESCRIPTION`"
        },
        {
          "heading": "example-output",
          "content": "The spec's `description` field, falling back to `title`, or blank if neither is set."
        },
        {
          "heading": "example-output",
          "content": "A spec file that fails to parse as JSON is still listed, with `(unreadable)` in place of its columns, instead of being silently skipped."
        },
        {
          "heading": "notes",
          "content": "`list` only reflects what's in `agentsDir` on disk — a spec appearing here does not mean it has ever been `Alineo.load()`ed or has a sandbox snapshot yet."
        },
        {
          "heading": "notes",
          "content": "If `agentsDir` doesn't exist, `list` prints `No agents dir found at '<dir>'. Run 'alineo add <url>' to add an agent spec.` and exits cleanly."
        },
        {
          "heading": "notes",
          "content": "If `agentsDir` exists but is empty, `list` prints `No agent specs found. Run 'alineo add <url>' to add one.` and exits cleanly."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/logs",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/logs.md",
      "title": "alineo logs",
      "description": "Print ledger events for a session.",
      "headings": [
        "Arguments",
        "Flags",
        "What it does",
        "Example",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`name`"
        },
        {
          "heading": "arguments",
          "content": "The session name, as shown by `alineo agents`. Required."
        },
        {
          "heading": "flags",
          "content": "Flag"
        },
        {
          "heading": "flags",
          "content": "Description"
        },
        {
          "heading": "flags",
          "content": "`--json`"
        },
        {
          "heading": "flags",
          "content": "Print the full ledger entry array as JSON."
        },
        {
          "heading": "what-it-does",
          "content": "Looks up the most recent sandbox for `name` in the ledger and prints every ledger event recorded against it — `sandbox_created`, `exec_start`/`exec_event`/`exec_complete`, `checkpoint_created`, `sandbox_closed`, and so on."
        },
        {
          "heading": "what-it-does",
          "content": "Note this reads by **name**, unlike `alineo prompt`/`alineo kill` which read by sandbox ID — `logs` is a read-only audit trail, not a liveness-sensitive operation, so the staleness/non-uniqueness concerns that motivate ID-based addressing elsewhere don't apply the same way. If a name has multiple sandboxes in the ledger, the newest one is used."
        },
        {
          "heading": "example",
          "content": "Long payloads are truncated to 200 characters in text mode. Use `--json` for the full, untruncated payloads."
        },
        {
          "heading": "notes",
          "content": "Only shows what's in **this CLI invocation's own** ledger — a child sandbox forked via `alineo fork` from inside another sandbox writes to that sandbox's own internal ledger, not this one. Use `alineo agents` from inside the child's own project context to inspect it, or connect to it directly."
        },
        {
          "heading": "notes",
          "content": "Errors with `No session named '<name>' found in the ledger.` if nothing matches."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/prompt",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/prompt.md",
      "title": "alineo prompt",
      "description": "Send one prompt to a running agent sandbox and print the reply.",
      "headings": [
        "Arguments",
        "Flags",
        "What it does",
        "Example",
        "Why sandbox ID, not name",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`sandbox-id`"
        },
        {
          "heading": "arguments",
          "content": "The sandbox ID printed by `alineo spawn`/`alineo fork`. Required."
        },
        {
          "heading": "arguments",
          "content": "`message`"
        },
        {
          "heading": "arguments",
          "content": "The prompt text to send. Required."
        },
        {
          "heading": "flags",
          "content": "Flag"
        },
        {
          "heading": "flags",
          "content": "Description"
        },
        {
          "heading": "flags",
          "content": "`--spec <path>`"
        },
        {
          "heading": "flags",
          "content": "Skip the ledger lookup for the spec file — see Why sandbox ID, not name below."
        },
        {
          "heading": "flags",
          "content": "`--json`"
        },
        {
          "heading": "flags",
          "content": "Print `{ name, sandboxId, reply }` as JSON instead of the raw reply text."
        },
        {
          "heading": "what-it-does",
          "content": "Calls `Alineo.resume(sandboxId, { adapter, specPath })`, sends `message`, and prints the full reply once Pi finishes responding. `Alineo.resume()` restarts the sandbox's bridge process — Pi and the workspace are untouched, but any interactive stream a *different* process had open against that sandbox will be interrupted."
        },
        {
          "heading": "why-sandbox-id-not-name",
          "content": "Every other `alineo` command that looks up a session takes a sandbox ID, not a name. Names aren't unique — running `alineo spawn` twice on the same spec produces two sandboxes with the same name — and a name-based ledger lookup can hand back a sandbox that already died ungracefully (crashed before its `close()` ran, expired via OpenSandbox's own TTL) since nothing tells the ledger it stopped. `Alineo.resume()`'s own `connect()` call is the actual authoritative liveness check; addressing by sandbox ID means that's the *only* check, not a second opinion layered on an already-stale one."
        },
        {
          "heading": "why-sandbox-id-not-name",
          "content": "`--spec <path>` skips `Alineo.resume()`'s own ledger lookup for the spec file entirely — needed when the sandbox's own `sandbox_created` event lives in a different ledger than this CLI invocation's own. This happens when prompting a child sandbox created via `alineo fork` from inside another sandbox: the child's creation event is recorded in *that sandbox's own* internal ledger, not the one this CLI invocation reads from."
        },
        {
          "heading": "notes",
          "content": "`prompt` waits for the entire reply before printing anything — there's no streaming/incremental output on the CLI. For streaming, use `alineo`'s `agent.prompt()` directly."
        },
        {
          "heading": "notes",
          "content": "If the sandbox no longer exists (closed, expired, or never existed), `Alineo.resume()`'s `connect()` call fails and the command errors out rather than hanging."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/remove",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/remove.md",
      "title": "alineo remove",
      "description": "Delete a saved agent spec file from the current project.",
      "headings": [
        "Arguments",
        "What it does",
        "Example",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`name`"
        },
        {
          "heading": "arguments",
          "content": "The spec name as shown by `alineo list`. Required."
        },
        {
          "heading": "what-it-does",
          "content": "Looks for `<agentsDir>/<name>.json`. Throws `No agent spec named '<name>' in '<agentsDir>'. Run 'alineo list' to see available specs.` if it doesn't exist."
        },
        {
          "heading": "what-it-does",
          "content": "Deletes the file."
        },
        {
          "heading": "what-it-does",
          "content": "No network calls are made and no sandbox is touched — `remove` is a plain local file delete."
        },
        {
          "heading": "notes",
          "content": "`remove` only deletes the spec JSON file. It does not delete any sandbox snapshot created by a prior `Alineo.load()` — that's tracked separately, in `agent-snapshots.json` next to your ledger — nor does it touch the ledger itself."
        },
        {
          "heading": "notes",
          "content": "To remove a spec that was saved with `--name` on `add`, use that custom name, not the `name` field from inside the spec file."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/spawn",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/spawn.md",
      "title": "alineo spawn",
      "description": "Start a brand-new, independent agent sandbox from a spec's own snapshot.",
      "headings": [
        "Arguments",
        "Flags",
        "What it does",
        "Example",
        "spawn vs. fork",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`spec`"
        },
        {
          "heading": "arguments",
          "content": "Path to an agent spec JSON file. Required."
        },
        {
          "heading": "flags",
          "content": "Flag"
        },
        {
          "heading": "flags",
          "content": "Description"
        },
        {
          "heading": "flags",
          "content": "`--prompt <msg>`"
        },
        {
          "heading": "flags",
          "content": "Send one prompt immediately after the sandbox is ready, and print the reply before exiting."
        },
        {
          "heading": "flags",
          "content": "`--rebuild`"
        },
        {
          "heading": "flags",
          "content": "Force a full reinstall instead of restoring from the cached snapshot. See Snapshotting."
        },
        {
          "heading": "flags",
          "content": "`--depth <n>`"
        },
        {
          "heading": "flags",
          "content": "Override the spec's own `spawnDepth` field."
        },
        {
          "heading": "flags",
          "content": "`--max <n>`"
        },
        {
          "heading": "flags",
          "content": "Override the spec's own `maxAgents` field."
        },
        {
          "heading": "flags",
          "content": "`--json`"
        },
        {
          "heading": "flags",
          "content": "Print `{ name, sandboxId, reply }` as JSON instead of the default text output."
        },
        {
          "heading": "what-it-does",
          "content": "Reads `alineo.config.json` and calls `Alineo.load(spec, { adapter, rebuild, spawnDepth, maxAgents })` — see that page for the full snapshot/install behavior. This always starts from the spec's own snapshot (or builds one on first use); it never inspects the calling process's own sandbox state."
        },
        {
          "heading": "what-it-does",
          "content": "If `--prompt` is given, sends that message and waits for the full reply before printing anything and exiting."
        },
        {
          "heading": "what-it-does",
          "content": "Without `--prompt`, prints the sandbox name and ID and exits immediately — the sandbox keeps running."
        },
        {
          "heading": "spawn-vs-fork",
          "content": "`alineo spawn` is the entry point for a **fresh** session — e.g. a host-level Pi session starting the master of a recursive-agent run. `alineo fork` instead branches an **already-running** session's own live sandbox state. Use `spawn` when nothing exists yet; use `fork` when you're running *inside* a session and want to delegate part of the current work to a child."
        },
        {
          "heading": "notes",
          "content": "The sandbox ID printed here is what `alineo prompt`, `alineo kill`, and `alineo logs` need — it is not saved anywhere else automatically."
        },
        {
          "heading": "notes",
          "content": "`--depth`/`--max` only matter if the spec itself (via `agent.spawn()`) or a later `alineo fork` call from inside that sandbox will go on to fork children."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/commands/telemetry",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/commands/telemetry.md",
      "title": "alineo telemetry",
      "description": "Show, enable, or disable anonymous CLI usage telemetry.",
      "headings": [
        "Arguments",
        "What it does",
        "What's collected",
        "Example",
        "Opting out",
        "Notes"
      ],
      "sections": [
        {
          "heading": "arguments",
          "content": "Argument"
        },
        {
          "heading": "arguments",
          "content": "Description"
        },
        {
          "heading": "arguments",
          "content": "`status` (default if omitted)"
        },
        {
          "heading": "arguments",
          "content": "Print whether telemetry is enabled and this machine's anonymous ID."
        },
        {
          "heading": "arguments",
          "content": "`enable`"
        },
        {
          "heading": "arguments",
          "content": "Turn telemetry on."
        },
        {
          "heading": "arguments",
          "content": "`disable`"
        },
        {
          "heading": "arguments",
          "content": "Turn telemetry off."
        },
        {
          "heading": "what-it-does",
          "content": "`alineo` can send small, anonymous usage events — which subcommand ran, a per-command allowlist of boolean flag presence, success/failure, and timing — to help prioritize development. `alineo telemetry` reads and writes the local config file that controls this (`~/.config/alineo/telemetry.json`), created on first use with a random `anonymousId`."
        },
        {
          "heading": "what-it-does",
          "content": "**Default-on**, sent to `https://telemetry.alineo.tech`. See Opting out below."
        },
        {
          "heading": "whats-collected",
          "content": "Never anything beyond this:"
        },
        {
          "heading": "whats-collected",
          "content": "Field"
        },
        {
          "heading": "whats-collected",
          "content": "Example"
        },
        {
          "heading": "whats-collected",
          "content": "`command`"
        },
        {
          "heading": "whats-collected",
          "content": "`\"spawn\"`"
        },
        {
          "heading": "whats-collected",
          "content": "`flags`"
        },
        {
          "heading": "whats-collected",
          "content": "`{ \"json\": true, \"prompt\": false }` — presence only, never values"
        },
        {
          "heading": "whats-collected",
          "content": "`specProvider`"
        },
        {
          "heading": "whats-collected",
          "content": "`\"nvidia\"` — `spawn`/`fork` only, read from the target spec's own `provider` field"
        },
        {
          "heading": "whats-collected",
          "content": "`outcome`"
        },
        {
          "heading": "whats-collected",
          "content": "`\"success\"` or `\"error\"`"
        },
        {
          "heading": "whats-collected",
          "content": "`errorClass`"
        },
        {
          "heading": "whats-collected",
          "content": "`\"CommandError\"` — the error's constructor name only, never its message"
        },
        {
          "heading": "whats-collected",
          "content": "`durationMs`"
        },
        {
          "heading": "whats-collected",
          "content": "`842`"
        },
        {
          "heading": "whats-collected",
          "content": "`cliVersion`, `osPlatform`, `osArch`, `bunVersion`, `isCI`"
        },
        {
          "heading": "whats-collected",
          "content": "environment metadata"
        },
        {
          "heading": "whats-collected",
          "content": "`anonymousId`"
        },
        {
          "heading": "whats-collected",
          "content": "a random UUID generated once per machine"
        },
        {
          "heading": "whats-collected",
          "content": "**Never collected**: raw `argv`, flag values, file paths, spec contents, prompts, sandbox output, or anything else that could identify you or your code."
        },
        {
          "heading": "opting-out",
          "content": "Telemetry is on by default. Turn it off any time with:"
        },
        {
          "heading": "opting-out",
          "content": "or by setting either env var, which take priority over the persisted config and require no config file read at all:"
        },
        {
          "heading": "notes",
          "content": "A one-time notice is printed to stderr the first time an event would actually send, naming both opt-out mechanisms — never mixed into `--json` output."
        },
        {
          "heading": "notes",
          "content": "Sending is bounded: a 500ms timeout races the request, and a failed or slow send never delays or fails the real command it's attached to."
        },
        {
          "heading": "notes",
          "content": "Scoped to the `alineo` CLI only — `alineo`/`alineo`/`@alineo-labs/workflow` used as libraries in your own code are never instrumented."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/registry",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/registry.md",
      "title": "Registry",
      "description": "Publish an AgentSpec JSON file so others can fetch it with a single command.",
      "headings": [
        "Publishing a spec",
        "Minimal example",
        "Composition with `registryDependencies`"
      ],
      "sections": [
        {
          "heading": null,
          "content": "An alineo \"registry item\" is just a URL that returns an `AgentSpec` JSON object (the same spec type used by `alineo`). Any URL works — a GitHub raw file, a Gist, your own server, or the small curated set of examples alineo hosts at registry.alineo.tech — there's no requirement to publish through a central service."
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/alineo/registry/schema\" title=\"AgentSpec schema\" description=\"All fields, types, and constraints for a valid agent spec.\" />"
        },
        {
          "heading": "publishing-a-spec",
          "content": "An agent spec is a static JSON file. Hosting it is as simple as pushing it to a public GitHub repository and sharing the raw URL:"
        },
        {
          "heading": "publishing-a-spec",
          "content": "Anyone can then fetch it into their own project with:"
        },
        {
          "heading": "publishing-a-spec",
          "content": "This only saves the spec locally — it doesn't run anything. See Running an agent spec for how to actually load and run it with `Alineo.load()`."
        },
        {
          "heading": "minimal-example",
          "content": "Every spec runs inside a `node:22` sandbox (there's no `image` field — the base image is fixed) and requires `cli: \"pi\"`:"
        },
        {
          "heading": "composition-with-registrydependencies",
          "content": "A spec can declare dependencies on other specs by URL. `alineo add` fetches and saves them first, depth-first, before saving the top-level spec — it does not merge or run them, each dependency just ends up as its own separate spec file in `agentsDir`:"
        },
        {
          "heading": "composition-with-registrydependencies",
          "content": "There's no cycle detection — avoid dependency chains that reference each other."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/alineo/registry/schema",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/alineo/registry/schema.md",
      "title": "AgentSpec schema",
      "description": "Complete field reference for the AgentSpec JSON fetched by alineo add and run by alineo's Alineo.load().",
      "headings": [
        "Full example",
        "Fields",
        "Required",
        "Optional",
        "Validation"
      ],
      "sections": [
        {
          "heading": null,
          "content": "An agent spec is a JSON object — the same `AgentSpec` type `alineo` exports. Only `name` and `cli` are required; `alineo add` and `Alineo.load()` both use the same `validateAgentSpec()` function."
        },
        {
          "heading": "full-example",
          "content": "There's no `image` field — every spec runs in a fixed `node:22` sandbox — and no `ports` field."
        },
        {
          "heading": "required",
          "content": "Field"
        },
        {
          "heading": "required",
          "content": "Type"
        },
        {
          "heading": "required",
          "content": "Description"
        },
        {
          "heading": "required",
          "content": "`name`"
        },
        {
          "heading": "required",
          "content": "`string`"
        },
        {
          "heading": "required",
          "content": "Unique identifier. Used as the saved filename (`<name>.json`) and the sandbox session name."
        },
        {
          "heading": "required",
          "content": "`cli`"
        },
        {
          "heading": "required",
          "content": "`\"pi\"`"
        },
        {
          "heading": "required",
          "content": "CLI to run inside the sandbox. Currently only `\"pi\"` is accepted — anything else fails validation."
        },
        {
          "heading": "optional",
          "content": "Field"
        },
        {
          "heading": "optional",
          "content": "Type"
        },
        {
          "heading": "optional",
          "content": "Description"
        },
        {
          "heading": "optional",
          "content": "`title`"
        },
        {
          "heading": "optional",
          "content": "`string`"
        },
        {
          "heading": "optional",
          "content": "Human-readable display name."
        },
        {
          "heading": "optional",
          "content": "`description`"
        },
        {
          "heading": "optional",
          "content": "`string`"
        },
        {
          "heading": "optional",
          "content": "Shown by `alineo list`, falling back from `title` if `description` is unset."
        },
        {
          "heading": "optional",
          "content": "`author`"
        },
        {
          "heading": "optional",
          "content": "`string`"
        },
        {
          "heading": "optional",
          "content": "Author name and optional URL."
        },
        {
          "heading": "optional",
          "content": "`categories`"
        },
        {
          "heading": "optional",
          "content": "`string[]`"
        },
        {
          "heading": "optional",
          "content": "Tags for discovery. Not currently used by any command — informational only."
        },
        {
          "heading": "optional",
          "content": "`cliVersion`"
        },
        {
          "heading": "optional",
          "content": "`string`"
        },
        {
          "heading": "optional",
          "content": "npm version specifier for the Pi CLI (e.g. `\"1.2.3\"`, `\"^1.2.0\"`, or a dist-tag like `\"latest\"`). Passed to `npm install -g @earendil-works/pi-coding-agent@<cliVersion>`. Omit to install whatever npm resolves as latest. Included in the setup-hash cache key, so changing it forces a fresh install."
        },
        {
          "heading": "optional",
          "content": "`provider`"
        },
        {
          "heading": "optional",
          "content": "`string`"
        },
        {
          "heading": "optional",
          "content": "AI provider passed to Pi via `--provider`. Omit for a direct Google API key (Pi's default)."
        },
        {
          "heading": "optional",
          "content": "`model`"
        },
        {
          "heading": "optional",
          "content": "`string`"
        },
        {
          "heading": "optional",
          "content": "Model ID passed to Pi via `--model`."
        },
        {
          "heading": "optional",
          "content": "`packages`"
        },
        {
          "heading": "optional",
          "content": "`string[]`"
        },
        {
          "heading": "optional",
          "content": "APT packages installed before the CLI starts. `nodejs`/`nodejs_22` are silently ignored (the base image already has Node)."
        },
        {
          "heading": "optional",
          "content": "`env`"
        },
        {
          "heading": "optional",
          "content": "`Record<string, string>`"
        },
        {
          "heading": "optional",
          "content": "Environment variables available inside the sandbox. Values may reference host env vars: `\"${MY_API_KEY}\"` → `process.env.MY_API_KEY` at load time."
        },
        {
          "heading": "optional",
          "content": "`resources`"
        },
        {
          "heading": "optional",
          "content": "`{ cpu: string; memory: string; gpu?: string }`"
        },
        {
          "heading": "optional",
          "content": "Falls back to `defaults.resources` in `alineo.config.json` if omitted (`1000m` / `1Gi` by default)."
        },
        {
          "heading": "optional",
          "content": "`metadata`"
        },
        {
          "heading": "optional",
          "content": "`Record<string, string>`"
        },
        {
          "heading": "optional",
          "content": "Not read anywhere in `alineo`; has no effect on the sandbox."
        },
        {
          "heading": "optional",
          "content": "`registryDependencies`"
        },
        {
          "heading": "optional",
          "content": "`string[]`"
        },
        {
          "heading": "optional",
          "content": "URLs of other agent specs. `alineo add` fetches and saves each one first, depth-first, before saving this spec. No cycle detection."
        },
        {
          "heading": "optional",
          "content": "`setup`"
        },
        {
          "heading": "optional",
          "content": "`SetupStep[]`"
        },
        {
          "heading": "optional",
          "content": "Steps run inside the sandbox after CLI install, before the checkpoint. Each is `{ name, run, cwd? }` — `run` is a bash command, `cwd` optionally changes directory first. Any change here invalidates the setup-hash cache, forcing a fresh install+setup on the next `Alineo.load()`."
        },
        {
          "heading": "validation",
          "content": "Both `alineo add` and `Alineo.load()` call the same `validateAgentSpec()`. It checks four things:"
        },
        {
          "heading": "validation",
          "content": "`name` is present and is a string."
        },
        {
          "heading": "validation",
          "content": "`cli` is exactly `\"pi\"`."
        },
        {
          "heading": "validation",
          "content": "If `spawnDepth` is set, it must be a non-negative integer."
        },
        {
          "heading": "validation",
          "content": "If `maxAgents` is set, it must be a non-negative integer."
        },
        {
          "heading": "validation",
          "content": "Every other field is unchecked at validation time — a malformed `resources`, `setup` entry, or `env` value won't be caught until it's actually used (typically inside the sandbox, at load/run time)."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/cancellation",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/cancellation.md",
      "title": "Cancellation & Cleanup",
      "description": "Resource cleanup and error handling when commands fail or time out.",
      "headings": [
        "cancellation",
        "Setup",
        "Run",
        "What it shows"
      ],
      "sections": [
        {
          "heading": "cancellation",
          "content": "Demonstrates resource cleanup and error handling patterns when commands fail or time out."
        },
        {
          "heading": "what-it-shows",
          "content": "Pattern"
        },
        {
          "heading": "what-it-shows",
          "content": "Description"
        },
        {
          "heading": "what-it-shows",
          "content": "`try/finally`"
        },
        {
          "heading": "what-it-shows",
          "content": "Guarantees `sb.close()` runs even when an exec throws"
        },
        {
          "heading": "what-it-shows",
          "content": "Bash `timeout`"
        },
        {
          "heading": "what-it-shows",
          "content": "Limits a command's wall-clock time at the shell level"
        },
        {
          "heading": "what-it-shows",
          "content": "`CommandError`"
        },
        {
          "heading": "what-it-shows",
          "content": "Catching the error thrown by a non-zero exit code"
        },
        {
          "heading": "what-it-shows",
          "content": "Three sandboxes run sequentially, each demonstrating one pattern."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/capture",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/capture.md",
      "title": "Capturing Output",
      "description": "Capture exec stdout and use it as input for subsequent steps.",
      "headings": [
        "capture",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "capture",
          "content": "Demonstrates capturing exec stdout and using it as input for subsequent steps."
        },
        {
          "heading": "what-it-does",
          "content": "Creates a `node:20-slim` sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Runs a Node.js one-liner and captures its stdout (the Node version string)"
        },
        {
          "heading": "what-it-does",
          "content": "Interpolates the captured value into a subsequent exec command"
        },
        {
          "heading": "what-it-does",
          "content": "Writes a JSON file into the sandbox using the captured value"
        },
        {
          "heading": "what-it-does",
          "content": "Reads the file back with `sb.readFile()` and prints it"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/control-flow",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/control-flow.md",
      "title": "Control Flow",
      "description": "retry, when, and forEach — alineo's built-in workflow control-flow primitives.",
      "headings": [
        "control-flow",
        "Setup",
        "Run",
        "What it shows"
      ],
      "sections": [
        {
          "heading": "control-flow",
          "content": "Demonstrates all of alineo's built-in workflow control-flow primitives in a single sandbox."
        },
        {
          "heading": "what-it-shows",
          "content": "Primitive"
        },
        {
          "heading": "what-it-shows",
          "content": "Description"
        },
        {
          "heading": "what-it-shows",
          "content": "`retry`"
        },
        {
          "heading": "what-it-shows",
          "content": "Retries a flaky command up to 5 times with exponential backoff"
        },
        {
          "heading": "what-it-shows",
          "content": "`when`"
        },
        {
          "heading": "what-it-shows",
          "content": "Branches conditionally based on the previous exec's exit code"
        },
        {
          "heading": "what-it-shows",
          "content": "`forEach`"
        },
        {
          "heading": "what-it-shows",
          "content": "Iterates over a list, running a command per item"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/environments",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/environments.md",
      "title": "Reusable Environments",
      "description": "Build a sandbox image once, then restore from snapshot on every run.",
      "headings": [
        "environments",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "environments",
          "content": "Demonstrates sandbox environments: build a reusable sandbox image once, then restore from the snapshot on every subsequent run — skipping the setup entirely."
        },
        {
          "heading": "what-it-does",
          "content": "**First run** — environment not yet cached:"
        },
        {
          "heading": "what-it-does",
          "content": "Installs Python 3 and pip into a `debian:bookworm-slim` container"
        },
        {
          "heading": "what-it-does",
          "content": "Installs `numpy` and `pandas` via pip"
        },
        {
          "heading": "what-it-does",
          "content": "Snapshots the container and saves the snapshot ID to the ledger"
        },
        {
          "heading": "what-it-does",
          "content": "**Subsequent runs** — environment cached:"
        },
        {
          "heading": "what-it-does",
          "content": "Finds the existing snapshot in the ledger"
        },
        {
          "heading": "what-it-does",
          "content": "Boots directly from the snapshot — no apt-get or pip install"
        },
        {
          "heading": "what-it-does",
          "content": "Runs `import numpy, pandas` to prove packages are already present"
        },
        {
          "heading": "what-it-does",
          "content": "The ledger entry is keyed by the environment name (`\"python-data-science\"`), so the snapshot is reused across process restarts."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/error-handling",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/error-handling.md",
      "title": "Error Handling",
      "description": "Strict vs. non-strict exec, and the CommandError/SandboxError/ExecConnectionError types.",
      "headings": [
        "error-handling",
        "Setup",
        "Run",
        "What it shows"
      ],
      "sections": [
        {
          "heading": "error-handling",
          "content": "Demonstrates the two error-handling modes for exec commands, and the error types you may encounter."
        },
        {
          "heading": "what-it-shows",
          "content": "Pattern"
        },
        {
          "heading": "what-it-shows",
          "content": "Description"
        },
        {
          "heading": "what-it-shows",
          "content": "Non-strict exec"
        },
        {
          "heading": "what-it-shows",
          "content": "`exec(\"...\", { strict: false })` returns the result; you inspect `exitCode` yourself"
        },
        {
          "heading": "what-it-shows",
          "content": "Strict exec (default)"
        },
        {
          "heading": "what-it-shows",
          "content": "Non-zero exit throws `CommandError` — catch it to handle the failure"
        },
        {
          "heading": "what-it-shows",
          "content": "Error types"
        },
        {
          "heading": "what-it-shows",
          "content": "`CommandError`, `SandboxError`, `ExecConnectionError` — when each is thrown"
        },
        {
          "heading": "what-it-shows",
          "content": "Two sandboxes run sequentially, one per pattern."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/exec-code",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/exec-code.md",
      "title": "Code Interpreter",
      "description": "Run code through the sandbox's built-in interpreter — stateless and stateful modes.",
      "headings": [
        "exec-code",
        "Setup",
        "Run",
        "What it shows"
      ],
      "sections": [
        {
          "heading": "exec-code",
          "content": "Demonstrates `sb.execCode()` for running code through the sandbox's built-in interpreter — stateless one-shot calls and stateful sessions where variables persist across calls."
        },
        {
          "heading": "what-it-shows",
          "content": "Mode"
        },
        {
          "heading": "what-it-shows",
          "content": "Description"
        },
        {
          "heading": "what-it-shows",
          "content": "Stateless"
        },
        {
          "heading": "what-it-shows",
          "content": "Each `execCode()` call runs in an isolated context; no shared state"
        },
        {
          "heading": "what-it-shows",
          "content": "Stateful"
        },
        {
          "heading": "what-it-shows",
          "content": "Calls sharing the same `context` object see each other's variables"
        },
        {
          "heading": "what-it-shows",
          "content": "Uses the `opensandbox/code-interpreter` image, which ships a Python interpreter accessible via the execd `/code` endpoint."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/file-ops",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/file-ops.md",
      "title": "File Operations",
      "description": "The full sandbox file operations API — read, write, move, search, and transfer.",
      "headings": [
        "file-ops",
        "Setup",
        "Run",
        "What it covers"
      ],
      "sections": [
        {
          "heading": "file-ops",
          "content": "Demonstrates the full sandbox file operations API — no `exec`/`sed` needed for common file tasks."
        },
        {
          "heading": "what-it-covers",
          "content": "Method"
        },
        {
          "heading": "what-it-covers",
          "content": "Description"
        },
        {
          "heading": "what-it-covers",
          "content": "`writeFile` / `readFile`"
        },
        {
          "heading": "what-it-covers",
          "content": "Write and read UTF-8 files"
        },
        {
          "heading": "what-it-covers",
          "content": "`moveFile` / `deleteFile`"
        },
        {
          "heading": "what-it-covers",
          "content": "Move or remove a file"
        },
        {
          "heading": "what-it-covers",
          "content": "`createDirectory` / `deleteDirectory`"
        },
        {
          "heading": "what-it-covers",
          "content": "Create or remove directories"
        },
        {
          "heading": "what-it-covers",
          "content": "`listDirectory`"
        },
        {
          "heading": "what-it-covers",
          "content": "List directory contents"
        },
        {
          "heading": "what-it-covers",
          "content": "`searchFiles`"
        },
        {
          "heading": "what-it-covers",
          "content": "Find files matching a glob pattern"
        },
        {
          "heading": "what-it-covers",
          "content": "`getFileInfo`"
        },
        {
          "heading": "what-it-covers",
          "content": "Stat a file (size, type, mode, timestamps)"
        },
        {
          "heading": "what-it-covers",
          "content": "`replaceInFiles`"
        },
        {
          "heading": "what-it-covers",
          "content": "In-place string substitution across one or more files"
        },
        {
          "heading": "what-it-covers",
          "content": "`transfer`"
        },
        {
          "heading": "what-it-covers",
          "content": "Copy a file from one sandbox to another"
        },
        {
          "heading": "what-it-covers",
          "content": "Two sandboxes are used to demonstrate `transfer()`."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/hello-world",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/hello-world.md",
      "title": "Hello World",
      "description": "Spin up an Ubuntu sandbox, run a command, and stream the output.",
      "headings": [
        "hello-world",
        "Setup",
        "Run",
        "What it does",
        "Notes"
      ],
      "sections": [
        {
          "heading": "hello-world",
          "content": "The simplest alineo example: spin up an Ubuntu sandbox, run `echo \"hello world\"`, and stream the output."
        },
        {
          "heading": "what-it-does",
          "content": "Creates an Ubuntu 22.04 sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Executes `echo \"hello world\"` inside it"
        },
        {
          "heading": "what-it-does",
          "content": "Streams output to stdout"
        },
        {
          "heading": "what-it-does",
          "content": "Deletes the sandbox on completion"
        },
        {
          "heading": "notes",
          "content": "All examples default to `useServerProxy: true` — traffic routes through the OpenSandbox server so Docker bridge IPs don't need to be reachable directly. Set `USE_SERVER_PROXY=false` to disable (e.g. when using `uvx opensandbox-server` on the host)."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples.md",
      "title": "Examples",
      "description": "Runnable examples covering every corner of the alineo SDK — clone, install, and run.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "Every example below is a real, runnable package in the alineo repo — `bunx alineo-cli init`, `bun install`, `bun start`."
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/hello-world\" title=\"Hello World\" description=\"Spin up an Ubuntu sandbox, run a command, and stream the output.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/exec-code\" title=\"Code Interpreter\" description=\"Run code through the sandbox's built-in interpreter — stateless and stateful modes.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/file-ops\" title=\"File Operations\" description=\"The full sandbox file operations API — read, write, move, search, and transfer.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/read-file\" title=\"Reading Files\" description=\"Read a file written inside the sandbox back to the host process.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/capture\" title=\"Capturing Output\" description=\"Capture exec stdout and use it as input for subsequent steps.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/run-bash-script\" title=\"Running Bash Scripts\" description=\"Run a multi-line bash script inside an isolated sandbox and stream its output.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/interactive-exec\" title=\"Interactive Exec\" description=\"A live, bidirectional PTY session that can be checkpointed and resumed mid-session.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/ports\" title=\"Exposing Ports\" description=\"Start an HTTP server inside a sandbox and reach it from the host via sb.proxy().\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/control-flow\" title=\"Control Flow\" description=\"retry, when, and forEach — alineo's built-in workflow control-flow primitives.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/cancellation\" title=\"Cancellation & Cleanup\" description=\"Resource cleanup and error handling when commands fail or time out.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/error-handling\" title=\"Error Handling\" description=\"Strict vs. non-strict exec, and the CommandError/SandboxError/ExecConnectionError types.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/environments\" title=\"Reusable Environments\" description=\"Build a sandbox image once, then restore from snapshot on every run.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/snapshot-replay\" title=\"Checkpoint & Resume\" description=\"Capture a snapshot after install, then resume from it to skip setup on the next run.\" />"
        },
        {
          "heading": null,
          "content": "<Card href=\"/docs/examples/sandbox-fork\" title=\"Forking Sandboxes\" description=\"Install dependencies once, then fork into independent sandboxes that run in parallel.\" />"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/interactive-exec",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/interactive-exec.md",
      "title": "Interactive Exec",
      "description": "A live, bidirectional PTY session that can be checkpointed and resumed mid-session.",
      "headings": [
        "interactive-exec",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "interactive-exec",
          "content": "Demonstrates `sb.exec(cmd, { interactive: true })`: a live, bidirectional PTY session that can be driven like a human — and resumed like one too."
        },
        {
          "heading": "what-it-does",
          "content": "Creates an `ubuntu:22.04` sandbox and opens an interactive `bash` session"
        },
        {
          "heading": "what-it-does",
          "content": "Drives it with several `write()` calls — exports a var, `cd`s into a directory, writes a file — none of which is a single self-contained command"
        },
        {
          "heading": "what-it-does",
          "content": "Checkpoints the sandbox **while the shell is still open** (not after it exits)"
        },
        {
          "heading": "what-it-does",
          "content": "Resumes from that checkpoint into a new sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Opens the same interactive exec again at the same call site — the resume path detects the session was still open, replays its recorded stdin for real against the freshly restored filesystem, then hands control back live"
        },
        {
          "heading": "what-it-does",
          "content": "Asserts the `cd`, the file contents, and the exported variable all survived — reconstructed by re-running the recorded input, not by faking a transcript"
        },
        {
          "heading": "what-it-does",
          "content": "Exits the shell and asserts the interactive exec resolves with the process's real exit code"
        },
        {
          "heading": "what-it-does",
          "content": "OpenSandbox snapshots are rootfs-only (no CRIU) — the original bash process is provably gone after resume. Reconstructing shell state is only possible by replaying the stdin that produced it."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/ports",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/ports.md",
      "title": "Exposing Ports",
      "description": "Start an HTTP server inside a sandbox and reach it from the host via sb.proxy().",
      "headings": [
        "ports",
        "Setup",
        "Run",
        "What it does",
        "Notes"
      ],
      "sections": [
        {
          "heading": "ports",
          "content": "Demonstrates `sb.proxy()`: start an HTTP server inside a sandbox and send requests to it from the host process via the OpenSandbox server proxy."
        },
        {
          "heading": "what-it-does",
          "content": "Creates a `node:22` sandbox and writes a simple HTTP server to `/server.js`"
        },
        {
          "heading": "what-it-does",
          "content": "Starts the server on port 3000 in the background"
        },
        {
          "heading": "what-it-does",
          "content": "Calls `sb.proxy(3000)` to get a proxy URL and auth headers"
        },
        {
          "heading": "what-it-does",
          "content": "Sends two requests from the host process and prints the JSON responses"
        },
        {
          "heading": "notes",
          "content": "With `useServerProxy: true` (the default), `sb.proxy()` returns a URL that routes through the OpenSandbox server (`http://127.0.0.1:8080/sandboxes/{id}/proxy/3000`). This works regardless of Docker networking because the server relays the request to the container on your behalf."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/read-file",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/read-file.md",
      "title": "Reading Files",
      "description": "Read a file written inside the sandbox back to the host process.",
      "headings": [
        "read-file",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "read-file",
          "content": "Demonstrates `sb.readFile()` — reading a file written inside the sandbox back to the host process."
        },
        {
          "heading": "what-it-does",
          "content": "Creates a `node:20-slim` sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Runs a Node.js one-liner that writes the Node version to `/tmp/version.txt`"
        },
        {
          "heading": "what-it-does",
          "content": "Reads the file back using `sb.readFile()`"
        },
        {
          "heading": "what-it-does",
          "content": "Writes a JSON report to the sandbox using `sb.writeFile()` and reads it back"
        },
        {
          "heading": "what-it-does",
          "content": "Prints both captured values to the console"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/run-bash-script",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/run-bash-script.md",
      "title": "Running Bash Scripts",
      "description": "Run a multi-line bash script inside an isolated sandbox and stream its output.",
      "headings": [
        "run-bash-script",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "run-bash-script",
          "content": "Run a multi-line bash script inside an isolated sandbox and stream its output."
        },
        {
          "heading": "what-it-does",
          "content": "Creates an Ubuntu 22.04 sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Runs a bash script that prints system info, disk usage, and writes/reads a file"
        },
        {
          "heading": "what-it-does",
          "content": "Streams output to stdout as it arrives"
        },
        {
          "heading": "what-it-does",
          "content": "Deletes the sandbox on completion"
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/sandbox-fork",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/sandbox-fork.md",
      "title": "Forking Sandboxes",
      "description": "Install dependencies once, then fork into independent sandboxes that run in parallel.",
      "headings": [
        "sandbox-fork",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "sandbox-fork",
          "content": "Demonstrates `sb.fork()`: install dependencies once into a base sandbox, then branch into two independent sandboxes that run different workloads in parallel — without repeating the install."
        },
        {
          "heading": "what-it-does",
          "content": "Creates a `python:3.11-slim` sandbox and installs `numpy`"
        },
        {
          "heading": "what-it-does",
          "content": "Forks into two independent sandboxes (`track-a`, `track-b`) from the post-install state"
        },
        {
          "heading": "what-it-does",
          "content": "Runs a different numpy computation on each fork in parallel"
        },
        {
          "heading": "what-it-does",
          "content": "Lists the checkpoints recorded on the original sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Closes all three sandboxes"
        },
        {
          "heading": "what-it-does",
          "content": "Both forks start with numpy already installed — neither pays the pip install cost again."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/examples/snapshot-replay",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/examples/snapshot-replay.md",
      "title": "Checkpoint & Resume",
      "description": "Capture a snapshot after install, then resume from it to skip setup on the next run.",
      "headings": [
        "snapshot-replay",
        "Setup",
        "Run",
        "What it does"
      ],
      "sections": [
        {
          "heading": "snapshot-replay",
          "content": "Demonstrates alineo's checkpoint and resume feature: run once to install dependencies and capture a snapshot, then resume from that snapshot — skipping the install — to run updated code against the same environment."
        },
        {
          "heading": "what-it-does",
          "content": "**Initial run**"
        },
        {
          "heading": "what-it-does",
          "content": "Creates a Python 3.11 sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "Installs `requests` via pip"
        },
        {
          "heading": "what-it-does",
          "content": "Captures a checkpoint (`after-install`)"
        },
        {
          "heading": "what-it-does",
          "content": "Runs a script and closes the sandbox"
        },
        {
          "heading": "what-it-does",
          "content": "**Resumed run**"
        },
        {
          "heading": "what-it-does",
          "content": "Calls `client.resume(sandboxId)` — boots from the snapshot"
        },
        {
          "heading": "what-it-does",
          "content": "The `pip install` call returns cached output instantly (never re-runs)"
        },
        {
          "heading": "what-it-does",
          "content": "Runs an updated script on the restored container"
        },
        {
          "heading": "what-it-does",
          "content": "Run `bun start` twice to see the difference: first run takes \\~30–60s; the resume takes \\~2–3s."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/ai-agent-bugfix",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/ai-agent-bugfix.md",
      "title": "AI Agent Bugfix",
      "description": "An agent that debugs and fixes a failing test on its own, then gets independently verified.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "An AI agent that debugs a failing test and fixes the bug itself — inside its own sandbox, using\nnothing but bash and a model."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "setup",
          "content": "Get a free API key from build.nvidia.com and export it:"
        },
        {
          "heading": "what-it-does",
          "content": "Plants a deliberate off-by-one bug in `calc.py` and a test that catches it."
        },
        {
          "heading": "what-it-does",
          "content": "Runs `pytest` via `agent.bash()` to show the failure."
        },
        {
          "heading": "what-it-does",
          "content": "Prompts the agent — via `alineo`'s `Alineo.load()` + `agent.prompt()` — to find and fix the bug\nitself, streaming its reasoning and tool calls as they happen."
        },
        {
          "heading": "what-it-does",
          "content": "**Re-runs `pytest` independently of the agent** (via `agent.sandbox.exec()`) to verify the fix,\nrather than trusting the agent's own claim that it passed."
        },
        {
          "heading": "what-it-does",
          "content": "`agents/bugfix-agent.json` configures a Pi agent on `python:3.11-slim` using the NVIDIA NIM API.\nSwap `provider`/`model` for anything in `@alineo-labs/model-providers` to use a different key."
        },
        {
          "heading": "what-it-does",
          "content": "Step 4 is the important part of this recipe: never trust an agent's self-report that a fix worked\n— re-run the check yourself against the sandbox it was working in."
        },
        {
          "heading": "what-it-does",
          "content": "See the Agent SDK docs for the full `alineo` API (`prompt`, `bash`, `steer`,\n`fork`, model switching, and more), and\nexamples/pi-agent for a tour of\nevery command it exposes."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/ci-test-runner",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/ci-test-runner.md",
      "title": "CI Test Runner",
      "description": "Run a repo's test suite in a disposable sandbox and turn the output into a structured pass/fail report.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Run a repo's test suite in a disposable, CI-style sandbox and turn the raw output into a\nstructured pass/fail report — the building block for a PR check, a bot, or an agent that needs to\nknow \"did the tests pass\" without scraping a log."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "what-it-does",
          "content": "Scaffolds a tiny Python project (`calc.py` + `test_calc.py`) directly in the sandbox — one test\ndeliberately fails, so you see a real failure report."
        },
        {
          "heading": "what-it-does",
          "content": "Installs `pytest`."
        },
        {
          "heading": "what-it-does",
          "content": "Runs the suite with `strict: false` so a non-zero exit is data, not a thrown error."
        },
        {
          "heading": "what-it-does",
          "content": "Prints a structured report (`status`, `summary`, and the full output on failure) and sets\n`process.exitCode` accordingly — the same shape a CI step or bot would check."
        },
        {
          "heading": "what-it-does",
          "content": "Swap the \"scaffold a project\" step for a real clone:"
        },
        {
          "heading": "what-it-does",
          "content": "All examples default to `useServerProxy: true` — traffic routes through the OpenSandbox server so\nDocker bridge IPs don't need to be reachable directly. Set `USE_SERVER_PROXY=false` to disable\n(e.g. when using `uvx opensandbox-server` on the host)."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/credential-scoped-agent",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/credential-scoped-agent.md",
      "title": "Credential-Scoped Agent",
      "description": "An agent that calls an authenticated API with a token it can never read — injected at the egress layer.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "An agent that does real work against an authenticated API — GitHub, here — using a token it can\nnever read. The token is registered as a **credential**, not an environment variable: it's\ninjected into matching outbound requests at the egress layer, so the agent can call\n`api.github.com` as you while the value itself never enters the container's filesystem or\nenvironment. Revoking it takes effect immediately, mid-session, without touching the running\nsandbox."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "setup",
          "content": "Export the agent's model key and a GitHub token:"
        },
        {
          "heading": "setup",
          "content": "`GH_TOKEN` is just the raw token — the agent spec (`agents/github-agent.json`) wraps it as\n`Bearer ${GH_TOKEN}` so the injected `Authorization` header is well-formed for GitHub."
        },
        {
          "heading": "setup",
          "content": "Credential injection rides on the same egress layer as network policy, so the server needs\n`egress.image` configured and `egress.mode = \"dns+nft\"` — the default for `alineo init` since\nthis feature landed. On an older local config, add this to `~/.config/alineo/server.toml` and\nrestart the server:"
        },
        {
          "heading": "what-it-does",
          "content": "**`Alineo.load()` reads `agents/github-agent.json`**, whose `env.GITHUB_TOKEN` is a\n`{ credential, host, injection }` binding rather than a string. Because at least one binding is\npresent, `load()` creates the sandbox with `credentialProxy: true` and registers the token with\nthe egress sidecar's Credential Vault — it never becomes a container env var.\n`env.NVIDIA_API_KEY`, an ordinary string, is exported the normal way."
        },
        {
          "heading": "what-it-does",
          "content": "**The agent does authenticated GitHub work** — told to `curl https://api.github.com/user` and\nreport the login, it does — with no token and no `Authorization` header of its own, and the call\ncomes back authenticated as you. The recipe echoes the agent's `bash` command and its output, so\nyou see the real request and the real response."
        },
        {
          "heading": "what-it-does",
          "content": "**Audit** — `env | grep` inside the sandbox turns up nothing, and a bare\n`curl https://api.github.com/user` with no `Authorization` header of its own still returns\n`HTTP 200`: the credential reached the request at the sidecar, not through anything the agent\ncould see."
        },
        {
          "heading": "what-it-does",
          "content": "**Revoke** — `agent.sandbox.credentials.remove(\"GITHUB_TOKEN\")`, and the same bare request now\nreturns `HTTP 401`. The agent's own next call to `api.github.com` would fail the same way."
        },
        {
          "heading": "what-it-does",
          "content": "`AgentSpec.env` is the obvious place to hand an agent a secret, but a plain env var is readable by\nanything running in the sandbox — including code the agent wrote itself, and including a\nprompt-injected instruction to print it. A credential binding gives the agent the *capability*\n(authenticated calls to one host) without the *secret*, and leaves you holding the leash: one\n`remove()` call cuts access at the network layer, with no redeploy and nothing to clean up inside\nthe container."
        },
        {
          "heading": "what-it-does",
          "content": "`examples/credential-injection`\n— the same mechanism at the raw `Sandbox` level (no agent), plus `fork()` carrying a bound\ncredential to the child automatically and the `source` / `resolveCredential` contract for\n`resume()`."
        },
        {
          "heading": "what-it-does",
          "content": "Credentials in the docs for the full\n`sb.credentials.*` API, `pathPrefix` scoping, and how bindings behave across\n`resume()` / `fork()` / spawned children."
        },
        {
          "heading": "what-it-does",
          "content": "The binding here uses `credential: \"Bearer ${GH_TOKEN}\"`, an interpolated string rather than a\nbare `${GH_TOKEN}` — so its `CredentialSource` is `{ type: \"external\" }`, not `{ type: \"env\" }`.\nThat only matters for `resume()` / `fork()` (which would then need an explicit\n`resolveCredential` callback); this recipe loads the agent, uses it, and closes it, so it never\ncomes up. Use a bare `${GH_TOKEN}` (and a token that already includes its scheme) if you want\nenv-based auto-resolution."
        },
        {
          "heading": "what-it-does",
          "content": "Two `injection` types are supported: `{ type: \"header\", name }` (the common case, shown above)\nand `{ type: \"substitution\", placeholder, in }`, which replaces a literal placeholder string in\nthe request's path/query/header/body with the value — use it for APIs that take the key in the\nURL. The request must already contain the placeholder verbatim (e.g. put it in a base URL)."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks.md",
      "title": "Cookbooks",
      "description": "Task-oriented recipes for building with alineo — composed from the SDK's primitives to solve real end-to-end problems.",
      "headings": [],
      "sections": [
        {
          "heading": null,
          "content": "Every recipe below is a real, runnable package in the\nalineo repo — `cd cookbooks/<recipe>`,\n`bun install`, `bun start`. Where Examples demonstrates one SDK primitive at a\ntime, a cookbook recipe composes several of them to solve one real task. Each page below has an\nin-browser preview of what running it looks like, plus a step-by-step walkthrough of what's\nactually happening."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/parallel-test-shards",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/parallel-test-shards.md",
      "title": "Parallel Test Shards",
      "description": "Install dependencies once, then fork into parallel sandboxes to shard work across them.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Install dependencies once, then fork the sandbox into N independent copies that each run a shard\nof the test suite in parallel — cutting wall-clock time roughly by the number of shards, without\nrepeating the install in every shard."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "what-it-does",
          "content": "Creates one base sandbox, installs `pytest`, and writes three small test files to it."
        },
        {
          "heading": "what-it-does",
          "content": "Calls `base.fork()` three times — each fork branches off the base sandbox's state, so none of\nthem repeat the `pip install`."
        },
        {
          "heading": "what-it-does",
          "content": "Runs a different test file in each fork with `Promise.all`, in parallel."
        },
        {
          "heading": "what-it-does",
          "content": "Aggregates each shard's exit code and a one-line summary into an overall pass/fail report."
        },
        {
          "heading": "what-it-does",
          "content": "Install dependencies and discover shard boundaries once in the base sandbox, then fork once per\nshard (or per CPU core) instead of paying setup cost N times. See Forking\nSandboxes for the underlying `sb.fork()` primitive."
        },
        {
          "heading": "what-it-does",
          "content": "All examples default to `useServerProxy: true` — traffic routes through the OpenSandbox server so\nDocker bridge IPs don't need to be reachable directly. Set `USE_SERVER_PROXY=false` to disable\n(e.g. when using `uvx opensandbox-server` on the host)."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/persistent-agent-memory",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/persistent-agent-memory.md",
      "title": "Persistent Agent Memory",
      "description": "An agent that remembers things about a customer across separate sandbox sessions, via @alineo-labs/memory.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "A support agent that remembers things about a customer across sessions — not within one\nconversation (Pi already keeps that), but across separate sandbox sessions entirely, backed by a\nreal, persisted `@alineo-labs/memory` store."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "setup",
          "content": "Get a free API key from build.nvidia.com and export it:"
        },
        {
          "heading": "what-it-does",
          "content": "**Session 1** — loads `agents/support-agent.json` via `Alineo.load()`, wired with a `Memory`\ninstance backed by `@alineo-labs/sqlite-memory` (a real file: `./.alineo/agent-memory.db`). Sets\na working-memory profile (`plan`, `name`), runs a command, and `remember()`s a fact tagged with a\n`sourceRef` pointing at the real ledger entry that command produced — so it comes back\n`verified: true`, not just claimed. Prompts the agent, then closes the sandbox."
        },
        {
          "heading": "what-it-does",
          "content": "**Session 2** — calls `Alineo.load()` again with the **same spec** and the **same `Memory`\ninstance**. This creates a brand-new sandbox (a different `sandboxId` — you'll see it in the\noutput) with none of session 1's container state. Everything recalled — the working-memory\nprofile, the verified fact — comes back purely because `agent.resourceRef` (which defaults to the\nagent's own `name`) is the same resource as session 1's, not because anything about the sandbox\nitself was preserved."
        },
        {
          "heading": "what-it-does",
          "content": "Uses `buildContextSnippet()` to assemble what's known about the customer into a plain-text block,\nprepended to the second session's prompt — so the agent's reply is grounded in real memory, not\nasked to guess."
        },
        {
          "heading": "what-it-does",
          "content": "Sandboxes are already durable — `sb.checkpoint()`/`resume()` preserve container state within one\nlogical session. What this recipe shows is a different kind of durability: memory that survives\npast the sandbox session it was learned in entirely, addressed by a stable identity\n(`resourceRef`) instead of a `sandboxId`."
        },
        {
          "heading": "what-it-does",
          "content": "`examples/memory-basics` —\nevery `@alineo-labs/memory` capability demonstrated standalone, no OpenSandbox needed:\ncompaction, `SchemaWorkingMemory`, `episodicTree()`, `Memory.fork()` (which `Alineo.spawn()`\ncalls automatically — not exercised in this recipe, since it needs a second agent spec and a\n`spawnDepth` budget beyond what this recipe's scope covers), and team access control."
        },
        {
          "heading": "what-it-does",
          "content": "`@alineo-labs/memory`'s own README\nfor the full API."
        },
        {
          "heading": "what-it-does",
          "content": "The embedding call in `index.ts` (`nvidiaEmbeddings()`) is inlined rather than imported from\n`@alineo-labs/model-providers`, since that package is private to this repo's own dashboard app —\nnot meant to be depended on from a cookbook someone copies out of this repo. Swap it for any\n`EmbeddingProvider` (a different provider's API, a local model) with no other changes needed."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/resumable-etl-pipeline",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/resumable-etl-pipeline.md",
      "title": "Resumable ETL Pipeline",
      "description": "A multi-stage pipeline that checkpoints after every stage and resumes without redoing completed work.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "A multi-stage ETL pipeline (extract → transform → load) that checkpoints after each stage, so a\ncrash — or just wanting to re-run \"load\" in isolation — doesn't mean paying for extract and\ntransform again."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "what-it-does",
          "content": "**Extract** — installs `pandas`, writes raw CSV data, then `sb.checkpoint(\"after-extract\")`."
        },
        {
          "heading": "what-it-does",
          "content": "**Transform** — aggregates revenue by region with pandas, writes `transformed.csv`, then\n`sb.checkpoint(\"after-transform\")`."
        },
        {
          "heading": "what-it-does",
          "content": "**Load** — reads back the final output and \"publishes\" it (prints it)."
        },
        {
          "heading": "what-it-does",
          "content": "Simulates picking the pipeline back up later:"
        },
        {
          "heading": "what-it-does",
          "content": "`client.resume(sandboxId)` restores the container from the last checkpoint (`after-transform`)."
        },
        {
          "heading": "what-it-does",
          "content": "The first `exec()` call after resume replays instantly from the ledger — no network call, no\nre-install. This demo re-issues the identical `pip install` command from the extract stage as a\nmatter of good practice; the replay itself is positional (see the note below), not a check that\nthe command matches."
        },
        {
          "heading": "what-it-does",
          "content": "`transformed.csv` is already present on the restored container's filesystem, so the load stage\nreads it straight away — the transform never re-runs."
        },
        {
          "heading": "what-it-does",
          "content": "Every `sb.checkpoint(tag)` is a real container snapshot, not just a ledger bookmark — the restored\ncontainer genuinely has extract and transform's output on disk. The ledger replay on top of that\nis an optimization: the Nth `exec()` call since resume returns the Nth call's original result,\nwhatever command is actually passed — there's no check that it matches what was recorded. Always\nre-issue calls in the same order as the original run. See Checkpoint &\nResume for the primitive this recipe builds on."
        },
        {
          "heading": "what-it-does",
          "content": "All examples default to `useServerProxy: true` — traffic routes through the OpenSandbox server so\nDocker bridge IPs don't need to be reachable directly. Set `USE_SERVER_PROXY=false` to disable\n(e.g. when using `uvx opensandbox-server` on the host)."
        }
      ]
    },
    {
      "url": "https://docs.alineo.tech/docs/cookbooks/untrusted-code-execution",
      "markdownUrl": "https://docs.alineo.tech/llms.mdx/cookbooks/untrusted-code-execution.md",
      "title": "Untrusted Code Execution",
      "description": "Safely run LLM-generated or user-submitted code — per-snippet isolation, resource caps, and timeouts.",
      "headings": [
        "Setup",
        "Run it",
        "What it does"
      ],
      "sections": [
        {
          "heading": null,
          "content": "Safely execute untrusted or LLM-generated Python snippets — each one gets its own throwaway,\nresource-capped sandbox with a wall-clock timeout, and failures are captured as data instead of\ncrashing the batch."
        },
        {
          "heading": "setup",
          "content": "Start OpenSandbox in Docker (one-time setup):"
        },
        {
          "heading": "what-it-does",
          "content": "Runs three Python snippets in parallel, each in its own sandbox:"
        },
        {
          "heading": "what-it-does",
          "content": "**`well-behaved`** — a snippet that just prints a value."
        },
        {
          "heading": "what-it-does",
          "content": "**`raises`** — a snippet that raises an uncaught exception."
        },
        {
          "heading": "what-it-does",
          "content": "**`infinite-loop`** — a snippet that never terminates on its own."
        },
        {
          "heading": "what-it-does",
          "content": "Each sandbox is created with tight `resources` (`250m` CPU / `128Mi` memory), a 30s container\n`timeout`, and a 5s `timeoutMs` on the exec call itself. `strict: false` means a non-zero exit is\nreturned as data (`exitCode`, `stdout`, `stderr`) instead of throwing, so one bad snippet doesn't\nabort the batch. Every sandbox is closed in `finally`, even on timeout or crash."
        },
        {
          "heading": "what-it-does",
          "content": "You're executing code you didn't write yourself — an LLM's generated code, a user-submitted\nscript, a plugin. Swap `runUntrusted()`'s body for `execCode()`/`createCodeContext()` (see Code\nInterpreter) if you want a stateful REPL instead of one-shot scripts."
        },
        {
          "heading": "what-it-does",
          "content": "All examples default to `useServerProxy: true` — traffic routes through the OpenSandbox server so\nDocker bridge IPs don't need to be reachable directly. Set `USE_SERVER_PROXY=false` to disable\n(e.g. when using `uvx opensandbox-server` on the host)."
        }
      ]
    }
  ]
}