---
title: Pi
description: Learn how to use the Pi harness adapter.
url: "https://ai-sdk.dev/providers/ai-sdk-harnesses/pi"
docs_index: /llms.txt
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

The Pi harness adapter connects `HarnessAgent` to
`@earendil-works/pi-coding-agent`. Pi runs in the host Node.js process and uses
the sandbox as a remote filesystem and shell. It does not install a bridge
inside the sandbox.

Harness packages are **experimental**. Expect breaking changes between
releases as this early API gets further refined.

## Setup

```bash
pnpm add @ai-sdk/harness @ai-sdk/harness-pi @ai-sdk/sandbox-vercel
```

## Import

```ts
import { pi, createPi } from '@ai-sdk/harness-pi';
```

`pi` is equivalent to `createPi()` with its default configuration.

## Basic Usage

```ts
import { HarnessAgent } from '@ai-sdk/harness/agent';
import { pi } from '@ai-sdk/harness-pi';
import { createVercelNetworkSandboxSession } from '@ai-sdk/sandbox-vercel';

const agent = new HarnessAgent({
  harness: pi,
  model: 'anthropic/claude-sonnet-4.6',
});

const sandboxSession = await createVercelNetworkSandboxSession({
  runtime: 'node24',
  template: await agent.getSandboxTemplate(),
});
const session = await agent.createSession({ sandboxSession });

let exitCode = 0;
try {
  const result = await agent.stream({
    session,
    prompt: 'Check the test failures and fix the production code.',
  });

  for await (const part of result.stream) {
    if (part.type === 'text-delta') {
      process.stdout.write(part.text);
    }
  }
} catch (err) {
  exitCode = 1;
  console.error(err);
} finally {
  await session.destroy();
  await sandboxSession.destroy();
  process.exit(exitCode);
}
```

To use this agent, ensure environment variables include `VERCEL_OIDC_TOKEN` for
Vercel Sandbox, and one of the variables listed under [authentication](#authentication)
for Pi.

## Adapter Settings

Use `createPi()` to configure the runtime:

```ts
const harness = createPi({
  thinkingLevel: 'medium',
});
```

Settings:

- `auth`: authentication mode (`auto`, `openai`, `anthropic`, `custom`, or
  `ai-gateway`) or an isolated authentication environment.
- `cacheRetention`: default prompt cache retention for model requests (`none`,
  `short`, or `long`). For Anthropic models, `long` uses the 1-hour cache
  instead of the 5-minute default.
- `credentials`: application-owned Pi credential storage. This replaces the
  file-backed `auth.json` store and supports persistent database-backed
  credentials.
- `extensionFactories`: trusted inline Pi extension factories that run in the
  host Node.js process.
- `fileToolPathPolicy`: extra sandbox roots Pi's native file tools can read or
  write, and roots they must refuse. See [File Tool Paths](#file-tool-paths).
- `mcpServers`: MCP servers keyed by server name, served by Pi's native MCP
  support. Each entry is Pi's MCP server configuration, either stdio
  (`command`, `args`, `env`, `cwd`) or streamable HTTP (`url`, `headers`).
  Tools are named `mcp__<server>__<tool>`, with `-` in either name replaced by
  `_`. `exposure` defaults to `direct`, which declares a server's tools to the
  model; with `deferred`, the model finds them through Pi's `tool_search` tool
  first. Pi's `codemode` exposure is not supported. An entry that uses it, or
  that Pi rejects, fails session start. Pi truncates text results over 20 KB
  and writes the full text to the host temp directory. The harness has no
  sign-in flow, so an HTTP server that requires authentication must carry an
  `Authorization` header or `auth.provider`.
- `mcpSettings`: deprecated. Pi's native MCP has no tool prefix or output
  guard settings, so a `toolPrefix` other than `mcp`, or `outputGuard: false`,
  fails session start.
- `providers`: explicit Pi provider configurations for custom models.
- `reattachInProcess`: whether a suspended turn can reuse its live Pi session
  in the current process. Defaults to `true`.
- `thinkingLevel`: Pi thinking level (`off`, `minimal`, `low`, `medium`,
  `high`, `xhigh`, or `max`).

## File Tool Paths

By default, Pi's native file tools (`read`, `write`, `edit`, `ls`, `find`, and
`grep`) are limited to the session workspace, and the read-only tools can also
read the harness skill directory. Use `fileToolPathPolicy` to change what they
can reach:

```ts
const harness = createPi({
  fileToolPathPolicy: {
    readableRoots: ['/home/vercel-sandbox'],
    writableRoots: ['/tmp', '/mnt/artifacts'],
    deniedRoots: ['/home/vercel-sandbox/.credentials'],
  },
});
```

- `readableRoots`: absolute sandbox directories that `read`, `ls`, `find`, and
  `grep` can reach in addition to the defaults. They do not become writable.
- `writableRoots`: absolute sandbox directories that `write` and `edit` can
  reach in addition to the workspace. Writable roots are also readable.
- `deniedRoots`: absolute sandbox directories that every native file tool
  refuses, even inside the workspace or a readable root. Both requested paths
  and denied roots are resolved in the sandbox, so symlinks pointing to or from
  a denied root are refused. Configured roots are resolved again for each file
  operation; canonical denied roots captured at session startup remain denied.
  `find` and `grep` prune exact denied paths without following directory symlinks,
  and `ls` hides directly denied entries. Other directories with the same name
  remain accessible.

Every configured root must be an absolute sandbox path. Denied roots take
precedence over the workspace, harness skill directory, and allowed roots.
Writes outside the workspace do not emit workspace `file-change` events.

When the setting is present, a leading `~` in a tool path expands to the
sandbox home directory.

The policy applies to Pi's native file tools only. The `bash` tool is not
restricted by it, so do not treat `deniedRoots` as a security boundary for
commands the agent runs.

## Inline Extensions

Use `extensionFactories` to load trusted inline Pi extensions for each harness
session:

```ts
const harness = createPi({
  extensionFactories: [
    pi => {
      pi.on('agent_start', () => {
        console.log('Pi agent started');
      });
    },
  ],
});
```

Each factory also receives the harness session it runs in as a second argument:

- `sandboxSession`: the session's sandbox, restricted to the operations harness
  tools may use.
- `sessionWorkDir`: the sandbox directory the session works in.
- `instructions()`: the instructions of the current turn. Instructions arrive
  with each turn, so call it while a turn runs, not when the factory runs.

```ts
const harness = createPi({
  extensionFactories: [
    (pi, session) => {
      pi.on('agent_start', async () => {
        const { stdout } = await session.sandboxSession.run({
          command: `git -C ${session.sessionWorkDir} status --short`,
        });
        console.log(session.instructions(), stdout);
      });
    },
  ],
});
```

Factories that take only the Pi API keep working unchanged.

Routine resource refreshes between turns reuse the active extension runtime and
do not reinitialize factories. If the underlying Pi session is rebuilt, the
factories initialize again for the new Pi runtime.

Extension factories execute in the host Node.js process with access to the host
environment, so only pass factories you trust. This setting enables only the
factories you explicitly provide. Filesystem extension discovery remains
disabled, including user, project, and settings-based extensions. Themes and
prompt templates also remain disabled.

## Session Reattachment

By default, Pi keeps a suspended turn alive for an efficient continuation in
the same process when the incoming request has compatible settings and runtime
resources. Changed request-scoped settings or sandbox handles cause a cold
restore from persisted lifecycle state.

For stateless or multi-replica applications, disable in-process reattachment so
every continuation restores the persisted lifecycle state with the current
request's settings:

```ts
const harness = createPi({
  reattachInProcess: false,
});
```

An incoming completed `resume-session` state always takes precedence over an
older live suspended turn in the process.

## Authentication

The `auth` setting selects which credentials Pi reads from the host environment:

- `auto` (default): use AI Gateway credentials when available, then fall back
  to all provider credentials found in the environment.
- `openai`: use `OPENAI_API_KEY` and the optional `OPENAI_BASE_URL`.
- `anthropic`: use `ANTHROPIC_API_KEY` and the optional
  `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL`.
- `custom`: register all providers configured through environment variables.
- `ai-gateway`: use `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` and the
  optional `AI_GATEWAY_BASE_URL`.

If no applicable credential environment variable is set, the adapter attempts
to resolve a native subscription from the host system unless AI Gateway
authentication is selected.

```ts
const harness = createPi({ auth: 'ai-gateway' });
```

Pass an authentication environment to use programmatically resolved
credentials without reading `process.env`:

```ts
const harness = createPi({
  auth: {
    MISTRAL_API_KEY: await resolveMistralToken(),
    MISTRAL_BASE_URL: 'https://api.mistral.ai',
  },
});
```

The supplied record replaces the host environment for authentication
discovery. Pi parses provider API keys and base URLs from that record.

For credentials managed outside environment variables or files, inject an
application-owned credential store. Pi performs credential reads, refreshes,
and updates through this interface:

```ts
import { createPi, type PiCredentialStore } from '@ai-sdk/harness-pi';

declare const credentials: PiCredentialStore;

const harness = createPi({
  credentials,
  reattachInProcess: false,
});
```

`credentials` replaces Pi's `auth.json` storage. In request-scoped or
multi-replica deployments, pair it with `reattachInProcess: false` so each
continuation constructs a model runtime from the current request.

With `custom`, standard providers use environment variables such as
`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `ANTHROPIC_API_KEY`, and
`ANTHROPIC_BASE_URL`. Other providers use a `<PREFIX>_API_KEY` and matching
`<PREFIX>_BASE_URL` pair.

Authentication variables do not identify a provider's API protocol or models.
Register that metadata explicitly when using a custom model:

```ts
const harness = createPi({
  auth: {
    MYPROVIDER_API_KEY: await resolveMyProviderToken(),
    MYPROVIDER_BASE_URL: 'https://api.example.com/v1',
  },
  providers: {
    myprovider: {
      api: 'openai-completions',
      models: [
        {
          id: 'my-custom-model',
          name: 'My Custom Model',
          reasoning: false,
          input: ['text'],
          cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
          contextWindow: 128_000,
          maxTokens: 16_384,
        },
      ],
    },
  },
});
```

## Sandbox

Pi needs a sandbox session but does not require exposed ports. You can create a
Vercel network session or a local just-bash session:

```ts
const sandboxSession = await createVercelNetworkSandboxSession({
  runtime: 'node24',
  template: await agent.getSandboxTemplate(),
});
```

## Built-in Tools

The adapter exposes these common Pi built-ins through `agent.tools`:

- `read`
- `write`
- `edit`
- `bash`
- `grep`
- `glob`
- `ls`

Additional Pi built-ins may also appear in `agent.tools` when they do not fit a
common tool shape.

Pi supports built-in tool approval requests when `permissionMode` is
`allow-reads` or `allow-edits`.

## Known Limitations

Pi does not support structured output. Supplying `output` to `HarnessAgent`
causes the turn to throw `HarnessCapabilityUnsupportedError`.

Pi does not support user messages with text parts after image parts; all text
must appear before the first image.

## Related

- [HarnessAgent](/docs/ai-sdk-harnesses/harness-agent)
- [Harness tools](/docs/ai-sdk-harnesses/tools)
- [Harness adapters](/docs/ai-sdk-harnesses/harness-adapters)

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)