# SDK

> Embed pi with the package main export: minimal agent construction, full control hooks, custom models, tools, and settings.

- Repository: earendil-works/pi
- GitHub: https://github.com/earendil-works/pi
- Human docs: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1
- Complete Markdown: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1/llms-full.txt

## Source Files

- `packages/coding-agent/examples/sdk/README.md`
- `packages/coding-agent/examples/sdk/01-minimal.ts`
- `packages/coding-agent/examples/sdk/12-full-control.ts`
- `packages/coding-agent/package.json`
- `packages/coding-agent/src/core/agent-session.ts`
- `packages/coding-agent/examples/sdk/02-custom-model.ts`

---

---
title: "SDK"
description: "Embed pi with the package main export: minimal agent construction, full control hooks, custom models, tools, and settings."
---

The `@earendil-works/pi-coding-agent` main export is the embed surface for programmatic agents. Call `createAgentSession()` to construct an `AgentSession`, then drive turns with `session.prompt()`, observe progress through `session.subscribe()`, and release resources with `session.dispose()`. The same session abstraction powers interactive, print, and RPC run modes; SDK mode is the library path that wires model runtime, tools, settings, resources, and persistence without the TUI.

## Package surface

| Export | Path / field | Role |
|--------|----------------|------|
| Main | `@earendil-works/pi-coding-agent` (`exports["."]`, `main`: `./dist/index.js`) | SDK entry: `createAgentSession`, `ModelRuntime`, loaders, managers |
| RPC | `@earendil-works/pi-coding-agent/rpc-entry` | Process JSON RPC integration (separate from in-process SDK) |
| Client | `@earendil-works/pi-coding-agent/client` | Client package surface |
| Bin | `pi` → `dist/cli.js` | CLI entry (not required for SDK embeds) |
| Config dir | `piConfig.configDir`: `.pi` | Package-declared config directory name |
| Engine | `node` `>=22.19.0` | Runtime requirement |

Primary construction APIs used by the SDK examples:

| Symbol | Purpose |
|--------|---------|
| `createAgentSession` | Build a session with optional overrides |
| `createAgentSessionRuntime` | Runtime-backed session replacement (see session runtime docs) |
| `ModelRuntime` | Auth + models (`create`, `getModel`, `getAvailable`, `setRuntimeApiKey`) |
| `DefaultResourceLoader` | Default discovery for extensions, skills, prompts, themes, context files |
| `SessionManager` | Session persistence (`create`, `inMemory`) |
| `SettingsManager` | Settings (`create`, `inMemory`) |
| `createExtensionRuntime` | Empty extension runtime for custom loaders |

Model helpers may also come from `@earendil-works/pi-ai` or `@earendil-works/pi-ai/compat` (`getModel`).

## Minimal construction

With no options, `createAgentSession()` uses discovery defaults: skills, extensions, tools, and context files from the current working directory and `~/.pi/agent`. The model is taken from settings or the first available model with valid credentials.

```typescript
import { createAgentSession } from "@earendil-works/pi-coding-agent";

const { session } = await createAgentSession();

try {
  session.subscribe((event) => {
    if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
      process.stdout.write(event.assistantMessageEvent.delta);
    }
  });

  await session.prompt("What files are in the current directory?");
  session.state.messages.forEach((msg) => {
    console.log(msg);
  });
} finally {
  session.dispose();
}
```

Run the package example with:

```bash
cd packages/coding-agent
npx tsx examples/sdk/01-minimal.ts
```

## `createAgentSession` options

| Option | Default | Description |
|--------|---------|-------------|
| `modelRuntime` | Runtime using `agentDir/auth.json` and `models.json` | Canonical model and authentication runtime |
| `cwd` | `process.cwd()` | Working directory |
| `agentDir` | `~/.pi/agent` | Config directory |
| `model` | From settings / first available | Model instance to use |
| `thinkingLevel` | From settings / `"off"` | `off`, `low`, `medium`, `high` |
| `tools` | `["read", "bash", "edit", "write"]` built-ins | Allowlist across built-in, extension, and custom tool names |
| `customTools` | `[]` | Additional tool definitions |
| `resourceLoader` | `DefaultResourceLoader` | Extensions, skills, prompts, themes, context files |
| `sessionManager` | `SessionManager.create(cwd)` | Persistence |
| `settingsManager` | `SettingsManager.create(cwd, agentDir)` | Settings (compaction, retry, and related options) |

Return shape used in examples: `{ session }` where `session` is an `AgentSession`.

### Common option patterns

```typescript
import { getModel } from "@earendil-works/pi-ai";
import {
  createAgentSession,
  DefaultResourceLoader,
  ModelRuntime,
  SessionManager,
  SettingsManager,
} from "@earendil-works/pi-coding-agent";

const modelRuntime = await ModelRuntime.create();

// Explicit runtime only
const { session } = await createAgentSession({ modelRuntime });

// Model + thinking level
const model = getModel("anthropic", "claude-opus-4-5");
const { session: s2 } = await createAgentSession({
  model,
  thinkingLevel: "high",
  modelRuntime,
});

// Prompt override via DefaultResourceLoader
const loader = new DefaultResourceLoader({
  systemPromptOverride: (base) => `${base}\n\nBe concise.`,
});
await loader.reload();
const { session: s3 } = await createAgentSession({ resourceLoader: loader, modelRuntime });

// Built-in tool allowlist (read-oriented)
const { session: s4 } = await createAgentSession({
  tools: ["read", "grep", "find", "ls"],
  modelRuntime,
});

// Ephemeral session
const { session: s5 } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
  modelRuntime,
});
```

## Model selection

`ModelRuntime` is the SDK-facing registry for built-in models, custom entries from `models.json`, availability (valid API keys), and runtime API keys.

```typescript
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";

const modelRuntime = await ModelRuntime.create();

// Built-in by provider / id
const opus = modelRuntime.getModel("anthropic", "claude-opus-4-5");

// Custom provider/model from models.json
const customModel = modelRuntime.getModel("my-provider", "my-model");

// Models with valid API keys
const available = await modelRuntime.getAvailable();

if (available.length > 0) {
  const { session } = await createAgentSession({
    model: available[0],
    thinkingLevel: "medium", // off | low | medium | high
    modelRuntime,
  });
  try {
    await session.prompt("Say hello in one sentence.");
  } finally {
    session.dispose();
  }
}
```

Custom auth and models paths:

```typescript
const customRuntime = await ModelRuntime.create({
  authPath: "/my/app/auth.json",
  modelsPath: "/my/app/models.json",
});
await customRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
```

<Note>
Examples use concrete provider/model ids (for example `anthropic` / `claude-opus-4-5`) as illustrations. Any provider/model pair available through `ModelRuntime` or `getModel` can be passed; credentials and availability determine what actually runs.
</Note>

## Full control (no discovery)

Replace discovery with explicit loaders, settings, tools, and in-memory session state.

```typescript
import { getModel } from "@earendil-works/pi-ai/compat";
import {
  createAgentSession,
  createExtensionRuntime,
  ModelRuntime,
  type ResourceLoader,
  SessionManager,
  SettingsManager,
} from "@earendil-works/pi-coding-agent";

const modelRuntime = await ModelRuntime.create({
  authPath: "/tmp/my-agent/auth.json",
  modelsPath: "/tmp/my-agent/models.json",
});
if (process.env.MY_ANTHROPIC_KEY) {
  await modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_ANTHROPIC_KEY);
}

const model = getModel("anthropic", "claude-sonnet-4-5");
if (!model) throw new Error("Model not found");

const settingsManager = SettingsManager.inMemory({
  compaction: { enabled: false },
  retry: { enabled: true, maxRetries: 2 },
});

const cwd = process.cwd();

const resourceLoader: ResourceLoader = {
  getExtensions: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
  getSkills: () => ({ skills: [], diagnostics: [] }),
  getPrompts: () => ({ prompts: [], diagnostics: [] }),
  getThemes: () => ({ themes: [], diagnostics: [] }),
  getAgentsFiles: () => ({ agentsFiles: [] }),
  getSystemPrompt: () => `You are a minimal assistant.
Available: read, bash. Be concise.`,
  getSystemPromptSource: () => undefined,
  getAppendSystemPrompt: () => [],
  getAppendSystemPromptSources: () => [],
  extendResources: () => {},
  reload: async () => {},
};

const { session } = await createAgentSession({
  cwd,
  agentDir: "/tmp/my-agent",
  model,
  thinkingLevel: "off",
  modelRuntime,
  resourceLoader,
  tools: ["read", "bash"],
  sessionManager: SessionManager.inMemory(cwd),
  settingsManager,
});
```

Equivalent full-control shape with `DefaultResourceLoader` overrides (from the SDK examples README):

```typescript
const resourceLoader = new DefaultResourceLoader({
  systemPromptOverride: () => "You are helpful.",
  extensionFactories: [myExtension],
  skillsOverride: () => ({ skills: [], diagnostics: [] }),
  agentsFilesOverride: () => ({ agentsFiles: [] }),
  promptsOverride: () => ({ prompts: [], diagnostics: [] }),
});
await resourceLoader.reload();

const { session } = await createAgentSession({
  model,
  modelRuntime: customRuntime,
  resourceLoader,
  tools: ["read", "bash", "my_tool"],
  customTools: [myTool],
  sessionManager: SessionManager.inMemory(),
  settingsManager: SettingsManager.inMemory(),
});
```

### `ResourceLoader` methods used for full control

| Method | Role in full-control example |
|--------|------------------------------|
| `getExtensions` | Return extensions list, errors, and runtime |
| `getSkills` / `getPrompts` / `getThemes` | Resource lists + diagnostics |
| `getAgentsFiles` | Agent context files |
| `getSystemPrompt` | Full system prompt string |
| `getSystemPromptSource` / `getAppendSystemPrompt` / `getAppendSystemPromptSources` | Prompt provenance and append list |
| `extendResources` | Mutation hook (noop in minimal loader) |
| `reload` | Async refresh |

## Session API surface used by embeds

| Member | Usage |
|--------|--------|
| `session.subscribe(listener)` | Register for `AgentSessionEvent` stream |
| `session.prompt(text)` | Queue/run a user turn (async) |
| `session.state.messages` | Read message history after a turn |
| `session.dispose()` | Tear down; always call in `finally` |

### Event subscription

Core event types documented in the SDK examples:

```typescript
session.subscribe((event) => {
  switch (event.type) {
    case "message_update":
      if (event.assistantMessageEvent.type === "text_delta") {
        process.stdout.write(event.assistantMessageEvent.delta);
      }
      break;
    case "tool_execution_start":
      console.log(`Tool: ${event.toolName}`);
      break;
    case "tool_execution_end":
      console.log(`Result: ${event.result}`);
      break;
    case "agent_end":
      console.log("Done");
      break;
  }
});
await session.prompt("Hello");
```

`AgentSession` also emits session-specific events beyond core agent events, including:

| `event.type` | Notable fields |
|--------------|----------------|
| `agent_end` | `messages`, `willRetry` |
| `agent_settled` | — |
| `queue_update` | `steering`, `followUp` |
| `compaction_start` / `compaction_end` | `reason`: `manual` \| `threshold` \| `overflow`; end adds `result`, `aborted`, `willRetry`, optional `errorMessage` |
| `entry_appended` | `entry` |
| `session_info_changed` | `name` |
| `thinking_level_changed` | `level` |
| `auto_retry_start` / `auto_retry_end` | attempt metadata and errors |
| `summarization_retry_scheduled` / `summarization_retry_attempt_start` / `summarization_retry_finished` | Compaction / branch-summary retries |
| `bash_execution_update` | optional `id`, `delta` |

Streaming assistant text uses nested `assistantMessageEvent.type === "text_delta"` with `delta`.

## What `AgentSession` owns

`AgentSession` is the shared core across interactive, print, and RPC modes. Modes add their own I/O; the session encapsulates:

- Agent state access
- Event subscription with automatic session persistence
- Model and thinking-level management
- Compaction (manual and auto)
- Bash execution
- Session switching and branching

Embedders therefore get the same lifecycle semantics as CLI modes, without the interactive UI layer.

```text
  createAgentSession(options)
            │
            ▼
  ┌─────────────────────┐     subscribe()      ┌──────────────────┐
  │    AgentSession     │ ───────────────────► │  Event consumer  │
  │  (shared core)      │                      └──────────────────┘
  │                     │     prompt()
  │  modelRuntime       │ ◄──────────────────  host application
  │  resourceLoader     │
  │  sessionManager     │     dispose()
  │  settingsManager    │ ◄──────────────────  finally / shutdown
  │  tools / customTools│
  └─────────────────────┘
```

## Example catalog

Package path: `packages/coding-agent/examples/sdk/`.

| File | Focus |
|------|--------|
| `01-minimal.ts` | Defaults only |
| `02-custom-model.ts` | Model + thinking level |
| `03-custom-prompt.ts` | System prompt replace/modify |
| `04-skills.ts` | Discover, filter, or replace skills |
| `05-tools.ts` | Built-in tool allowlists |
| `06-extensions.ts` | Logging, blocking, result modification |
| `07-context-files.ts` | AGENTS.md context files |
| `08-slash-commands.ts` | File-based slash commands |
| `09-api-keys-and-oauth.ts` | API key resolution, OAuth config |
| `10-settings.ts` | Compaction, retry, terminal settings |
| `11-sessions.ts` | In-memory, persistent, continue, list |
| `12-full-control.ts` | Replace everything, no discovery |
| `13-session-runtime.ts` | Runtime-backed session replacement |

The runtime example builds a recreate function that closes over process-global fixed inputs and recreates cwd-bound services and sessions as the active session cwd changes. Prefer `createAgentSessionRuntime` when the host needs that lifecycle; use plain `createAgentSession` for single-session embeds.

## Constraints and verification

| Constraint | Detail |
|------------|--------|
| Node version | `>=22.19.0` |
| Defaults discovery roots | `cwd` and `~/.pi/agent` when using default loaders |
| Default tools | `read`, `bash`, `edit`, `write` unless `tools` overrides |
| Thinking levels | `off`, `low`, `medium`, `high` |
| Disposal | Call `session.dispose()` in `finally` after construction |
| Model availability | Prefer `modelRuntime.getAvailable()` or explicit keys via `setRuntimeApiKey` |
| Full control | Supply `resourceLoader` (and usually in-memory managers) to avoid filesystem discovery |

**Verification signals**

- `createAgentSession()` resolves without throw
- `message_update` / `text_delta` events stream during `prompt`
- `agent_end` (and later `agent_settled` when applicable) after the turn
- `session.state.messages` non-empty after a successful prompt
- Full-control path: only allowlisted tools appear in tool events

## Related pages

<CardGroup cols={2}>
  <Card title="SDK examples" href="/sdk-examples">
    Copy-paste recipes for models, skills, tools, extensions, sessions, and full control.
  </Card>
  <Card title="Session runtime" href="/session-runtime">
    `AgentSessionRuntime`, settled events, and embedding without the interactive TUI.
  </Card>
  <Card title="Package exports" href="/package-exports">
    Main, rpc-entry, client exports, bin name, and `piConfig.configDir`.
  </Card>
  <Card title="Run modes" href="/run-modes">
    Interactive, print/JSON, RPC, and SDK modes: when to choose each.
  </Card>
  <Card title="Tools and allowlists" href="/tools">
    Default tools, extension tools, allowlist filters, and blocked-tool behavior.
  </Card>
  <Card title="Settings" href="/settings">
    Settings load/reload and merge rules for compaction, retry, and session options.
  </Card>
  <Card title="Providers and models" href="/providers-and-models">
    Built-in and dynamic providers, model order, and `models.json` behavior.
  </Card>
  <Card title="Authentication" href="/authentication">
    API keys, OAuth, credential storage, and ambient auth.
  </Card>
</CardGroup>
