Pi

Learn how to use the Pi harness adapter.

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

$ pnpm add @ai-sdk/harness @ai-sdk/harness-pi @ai-sdk/sandbox-vercel

Import

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

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

Basic Usage

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 for Pi.

Adapter Settings

Use createPi() to configure the runtime:

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

Settings:

  • auth: authentication mode (auto, openai, anthropic, custom, or ai-gateway) or an isolated authentication environment.
  • 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.
  • mcpServers: MCP server definitions keyed by server name.
  • 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).

Inline Extensions

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

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

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:

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.

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

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

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:

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:

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:

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.