Skip to main content

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.

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

npm install @mariozechner/pi-coding-agent

Quick Start

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

1
Create Agent Session
2
The main factory function:
3
const { session } = await createAgentSession({
  model: myModel,
  tools: [readTool, bashTool],
  sessionManager: SessionManager.inMemory(),
});
4
Subscribe to Events
5
Receive streaming output:
6
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;
  }
});
7
Send Prompts
8
Interact with the agent:
9
// 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");

Model Selection

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,
});

Tools Configuration

1
Use Built-in Tool Sets
2
import {
  codingTools,   // read, bash, edit, write (default)
  readOnlyTools, // read, grep, find, ls
} from "@mariozechner/pi-coding-agent";

const { session } = await createAgentSession({
  tools: readOnlyTools,
});
3
Pick Specific Tools
4
import {
  readTool,
  bashTool,
  grepTool,
} from "@mariozechner/pi-coding-agent";

const { session } = await createAgentSession({
  tools: [readTool, bashTool, grepTool],
});
5
Custom Working Directory
6
import {
  createCodingTools,
  createReadOnlyTools,
} from "@mariozechner/pi-coding-agent";

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

const { session } = await createAgentSession({
  cwd,
  tools: createCodingTools(cwd),
});
7
Add Custom Tools
8
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],
});

Session Management

// No persistence
const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
});

Extensions and Resources

1
Use Default Discovery
2
Automatic discovery from standard locations:
3
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 });
4
Add Custom Extensions
5
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 });
6
Override System Prompt
7
const loader = new DefaultResourceLoader({
  systemPromptOverride: () => "You are a helpful assistant.",
});
await loader.reload();

const { session } = await createAgentSession({ resourceLoader: loader });
8
Add Custom Skills
9
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 });

Settings Management

import { SettingsManager } from "@mariozechner/pi-coding-agent";

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

Complete Example

Here’s a complete working example:
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:
Full TUI with editor and commands:
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

API Reference

AgentSession

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