> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/badlogic/pi-mono/llms.txt
> Use this file to discover all available pages before exploring further.

# Using Pi SDK Programmatically

> Embed Pi's agent capabilities in your applications with the TypeScript SDK

The Pi SDK provides programmatic access to Pi's agent capabilities. Use it to embed Pi in other applications, build custom interfaces, or integrate with automated workflows.

## Installation

```bash theme={null}
npm install @mariozechner/pi-coding-agent
```

## Quick Start

```typescript theme={null}
import {
  AuthStorage,
  createAgentSession,
  ModelRegistry,
  SessionManager
} from "@mariozechner/pi-coding-agent";

const authStorage = AuthStorage.create();
const modelRegistry = new ModelRegistry(authStorage);

const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
  authStorage,
  modelRegistry,
});

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?");
```

## Core Concepts

<Steps>
  ### Create Agent Session

  The main factory function:

  ```typescript theme={null}
  const { session } = await createAgentSession({
    model: myModel,
    tools: [readTool, bashTool],
    sessionManager: SessionManager.inMemory(),
  });
  ```

  ### Subscribe to Events

  Receive streaming output:

  ```typescript theme={null}
  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 "agent_end":
        console.log("Agent finished");
        break;
    }
  });
  ```

  ### Send Prompts

  Interact with the agent:

  ```typescript theme={null}
  // Basic prompt
  await session.prompt("List files");

  // With images
  await session.prompt("What's in this image?", {
    images: [{ type: "image", source: { type: "base64", mediaType: "image/png", data: "..." } }]
  });

  // During streaming
  await session.steer("Stop and do this instead");
  await session.followUp("After you're done, also check X");
  ```
</Steps>

## Model Selection

<Tabs>
  <Tab title="Built-in Models">
    ```typescript theme={null}
    import { getModel } from "@mariozechner/pi-ai";
    import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";

    const authStorage = AuthStorage.create();
    const modelRegistry = new ModelRegistry(authStorage);

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

    const { session } = await createAgentSession({
      model: opus,
      thinkingLevel: "medium",
      authStorage,
      modelRegistry,
    });
    ```
  </Tab>

  <Tab title="Available Models">
    ```typescript theme={null}
    // Get only models with valid API keys
    const available = await modelRegistry.getAvailable();

    const { session } = await createAgentSession({
      model: available[0],
      authStorage,
      modelRegistry,
    });
    ```
  </Tab>

  <Tab title="Custom Models">
    ```typescript theme={null}
    // Find custom models from models.json
    const customModel = modelRegistry.find("my-provider", "my-model");

    const { session } = await createAgentSession({
      model: customModel,
      authStorage,
      modelRegistry,
    });
    ```
  </Tab>
</Tabs>

## Tools Configuration

<Steps>
  ### Use Built-in Tool Sets

  ```typescript theme={null}
  import {
    codingTools,   // read, bash, edit, write (default)
    readOnlyTools, // read, grep, find, ls
  } from "@mariozechner/pi-coding-agent";

  const { session } = await createAgentSession({
    tools: readOnlyTools,
  });
  ```

  ### Pick Specific Tools

  ```typescript theme={null}
  import {
    readTool,
    bashTool,
    grepTool,
  } from "@mariozechner/pi-coding-agent";

  const { session } = await createAgentSession({
    tools: [readTool, bashTool, grepTool],
  });
  ```

  ### Custom Working Directory

  ```typescript theme={null}
  import {
    createCodingTools,
    createReadOnlyTools,
  } from "@mariozechner/pi-coding-agent";

  const cwd = "/path/to/project";

  const { session } = await createAgentSession({
    cwd,
    tools: createCodingTools(cwd),
  });
  ```

  ### Add Custom Tools

  ```typescript theme={null}
  import { Type } from "@sinclair/typebox";
  import type { ToolDefinition } from "@mariozechner/pi-coding-agent";

  const myTool: ToolDefinition = {
    name: "my_tool",
    label: "My Tool",
    description: "Does something useful",
    parameters: Type.Object({
      input: Type.String({ description: "Input value" }),
    }),
    execute: async (toolCallId, params, onUpdate, ctx, signal) => ({
      content: [{ type: "text", text: `Result: ${params.input}` }],
      details: {},
    }),
  };

  const { session } = await createAgentSession({
    customTools: [myTool],
  });
  ```
</Steps>

## Session Management

<Tabs>
  <Tab title="In-Memory">
    ```typescript theme={null}
    // No persistence
    const { session } = await createAgentSession({
      sessionManager: SessionManager.inMemory(),
    });
    ```
  </Tab>

  <Tab title="New Session">
    ```typescript theme={null}
    // New persistent session
    const { session } = await createAgentSession({
      sessionManager: SessionManager.create(process.cwd()),
    });
    ```
  </Tab>

  <Tab title="Continue Recent">
    ```typescript theme={null}
    // Continue most recent session
    const { session, modelFallbackMessage } = await createAgentSession({
      sessionManager: SessionManager.continueRecent(process.cwd()),
    });
    if (modelFallbackMessage) {
      console.log("Note:", modelFallbackMessage);
    }
    ```
  </Tab>

  <Tab title="Open Specific">
    ```typescript theme={null}
    // Open specific session file
    const { session } = await createAgentSession({
      sessionManager: SessionManager.open("/path/to/session.jsonl"),
    });
    ```
  </Tab>

  <Tab title="List Sessions">
    ```typescript theme={null}
    // List available sessions
    const sessions = await SessionManager.list(process.cwd());
    for (const info of sessions) {
      console.log(`${info.id}: ${info.firstMessage}`);
    }

    // List all sessions across all projects
    const allSessions = await SessionManager.listAll((loaded, total) => {
      console.log(`Loading ${loaded}/${total}...`);
    });
    ```
  </Tab>
</Tabs>

## Extensions and Resources

<Steps>
  ### Use Default Discovery

  Automatic discovery from standard locations:

  ```typescript theme={null}
  import { DefaultResourceLoader } from "@mariozechner/pi-coding-agent";

  const loader = new DefaultResourceLoader({
    cwd: process.cwd(),
    agentDir: "~/.pi/agent",
  });
  await loader.reload();

  const { session } = await createAgentSession({ resourceLoader: loader });
  ```

  ### Add Custom Extensions

  ```typescript theme={null}
  const loader = new DefaultResourceLoader({
    additionalExtensionPaths: ["/path/to/my-extension.ts"],
    extensionFactories: [
      (pi) => {
        pi.on("agent_start", () => {
          console.log("Agent starting");
        });
      },
    ],
  });
  await loader.reload();

  const { session } = await createAgentSession({ resourceLoader: loader });
  ```

  ### Override System Prompt

  ```typescript theme={null}
  const loader = new DefaultResourceLoader({
    systemPromptOverride: () => "You are a helpful assistant.",
  });
  await loader.reload();

  const { session } = await createAgentSession({ resourceLoader: loader });
  ```

  ### Add Custom Skills

  ```typescript theme={null}
  import type { Skill } from "@mariozechner/pi-coding-agent";

  const customSkill: Skill = {
    name: "my-skill",
    description: "Custom instructions",
    filePath: "/path/to/SKILL.md",
    baseDir: "/path/to",
    source: "custom",
  };

  const loader = new DefaultResourceLoader({
    skillsOverride: (current) => ({
      skills: [...current.skills, customSkill],
      diagnostics: current.diagnostics,
    }),
  });
  await loader.reload();

  const { session } = await createAgentSession({ resourceLoader: loader });
  ```
</Steps>

## Settings Management

<Tabs>
  <Tab title="From Files">
    ```typescript theme={null}
    import { SettingsManager } from "@mariozechner/pi-coding-agent";

    // Loads from ~/.pi/agent/settings.json and .pi/settings.json
    const { session } = await createAgentSession({
      settingsManager: SettingsManager.create(),
    });
    ```
  </Tab>

  <Tab title="With Overrides">
    ```typescript theme={null}
    const settingsManager = SettingsManager.create();
    settingsManager.applyOverrides({
      compaction: { enabled: false },
      retry: { enabled: true, maxRetries: 5 },
    });

    const { session } = await createAgentSession({ settingsManager });
    ```
  </Tab>

  <Tab title="In-Memory">
    ```typescript theme={null}
    // No file I/O, for testing
    const { session } = await createAgentSession({
      settingsManager: SettingsManager.inMemory({
        compaction: { enabled: false }
      }),
      sessionManager: SessionManager.inMemory(),
    });
    ```
  </Tab>
</Tabs>

## Complete Example

Here's a complete working example:

```typescript theme={null}
import { getModel } from "@mariozechner/pi-ai";
import { Type } from "@sinclair/typebox";
import {
  AuthStorage,
  createAgentSession,
  DefaultResourceLoader,
  ModelRegistry,
  SessionManager,
  SettingsManager,
  readTool,
  bashTool,
  type ToolDefinition,
} from "@mariozechner/pi-coding-agent";

// Set up auth storage
const authStorage = AuthStorage.create("/custom/agent/auth.json");

// Runtime API key override (not persisted)
if (process.env.MY_KEY) {
  authStorage.setRuntimeApiKey("anthropic", process.env.MY_KEY);
}

// Model registry
const modelRegistry = new ModelRegistry(authStorage);

// Custom tool
const statusTool: ToolDefinition = {
  name: "status",
  label: "Status",
  description: "Get system status",
  parameters: Type.Object({}),
  execute: async () => ({
    content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
    details: {},
  }),
};

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

// In-memory settings with overrides
const settingsManager = SettingsManager.inMemory({
  compaction: { enabled: false },
  retry: { enabled: true, maxRetries: 2 },
});

const loader = new DefaultResourceLoader({
  cwd: process.cwd(),
  agentDir: "/custom/agent",
  settingsManager,
  systemPromptOverride: () => "You are a minimal assistant. Be concise.",
});
await loader.reload();

const { session } = await createAgentSession({
  cwd: process.cwd(),
  agentDir: "/custom/agent",

  model,
  thinkingLevel: "off",
  authStorage,
  modelRegistry,

  tools: [readTool, bashTool],
  customTools: [statusTool],
  resourceLoader: loader,

  sessionManager: SessionManager.inMemory(),
  settingsManager,
});

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

await session.prompt("Get status and list files.");
```

## Run Modes

Build custom interfaces on top of the SDK:

<Tabs>
  <Tab title="Interactive Mode">
    Full TUI with editor and commands:

    ```typescript theme={null}
    import { createAgentSession, InteractiveMode } from "@mariozechner/pi-coding-agent";

    const { session } = await createAgentSession({ /* ... */ });

    const mode = new InteractiveMode(session, {
      initialMessage: "Hello",
      initialImages: [],
      initialMessages: [],
    });

    await mode.run();  // Blocks until exit
    ```
  </Tab>

  <Tab title="Print Mode">
    Single-shot output:

    ```typescript theme={null}
    import { createAgentSession, runPrintMode } from "@mariozechner/pi-coding-agent";

    const { session } = await createAgentSession({ /* ... */ });

    await runPrintMode(session, {
      mode: "text",              // "text" or "json"
      initialMessage: "Hello",
      initialImages: [],
      messages: ["Follow up"],
    });
    ```
  </Tab>

  <Tab title="RPC Mode">
    JSON-RPC for subprocess integration:

    ```typescript theme={null}
    import { createAgentSession, runRpcMode } from "@mariozechner/pi-coding-agent";

    const { session } = await createAgentSession({ /* ... */ });

    await runRpcMode(session);  // Reads stdin, writes stdout
    ```
  </Tab>
</Tabs>

## API Reference

### AgentSession

```typescript theme={null}
interface AgentSession {
  // Prompting
  prompt(text: string, options?: PromptOptions): Promise<void>;
  steer(text: string): Promise<void>;
  followUp(text: string): Promise<void>;

  // Events
  subscribe(listener: (event: AgentSessionEvent) => void): () => void;

  // State
  sessionFile: string | undefined;
  sessionId: string;
  agent: Agent;
  model: Model | undefined;
  thinkingLevel: ThinkingLevel;
  messages: AgentMessage[];
  isStreaming: boolean;

  // Model control
  setModel(model: Model): Promise<void>;
  setThinkingLevel(level: ThinkingLevel): void;
  cycleModel(): Promise<ModelCycleResult | undefined>;
  cycleThinkingLevel(): ThinkingLevel | undefined;

  // Session management
  newSession(options?: { parentSession?: string }): Promise<boolean>;
  switchSession(sessionPath: string): Promise<boolean>;
  fork(entryId: string): Promise<{ selectedText: string; cancelled: boolean }>;
  navigateTree(targetId: string, options?: NavigateTreeOptions): Promise<NavigateTreeResult>;

  // Compaction
  compact(customInstructions?: string): Promise<CompactionResult>;
  abortCompaction(): void;

  // Control
  abort(): Promise<void>;
  dispose(): void;
}
```

## Next Steps

* See [Building Extensions](/guides/building-extensions) for custom tools and event handlers
* See [Creating Skills](/guides/creating-skills) for specialized workflows
* See [Custom Providers](/guides/custom-providers) for adding LLM providers
* Check `packages/coding-agent/examples/sdk/` for more examples
