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
The main factory function:
const { session } = await createAgentSession({
model: myModel,
tools: [readTool, bashTool],
sessionManager: SessionManager.inMemory(),
});
Receive streaming output:
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;
}
});
// 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
Built-in Models
Available Models
Custom Models
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,
});
// Get only models with valid API keys
const available = await modelRegistry.getAvailable();
const { session } = await createAgentSession({
model: available[0],
authStorage,
modelRegistry,
});
// Find custom models from models.json
const customModel = modelRegistry.find("my-provider", "my-model");
const { session } = await createAgentSession({
model: customModel,
authStorage,
modelRegistry,
});
import {
codingTools, // read, bash, edit, write (default)
readOnlyTools, // read, grep, find, ls
} from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({
tools: readOnlyTools,
});
import {
readTool,
bashTool,
grepTool,
} from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({
tools: [readTool, bashTool, grepTool],
});
import {
createCodingTools,
createReadOnlyTools,
} from "@mariozechner/pi-coding-agent";
const cwd = "/path/to/project";
const { session } = await createAgentSession({
cwd,
tools: createCodingTools(cwd),
});
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
In-Memory
New Session
Continue Recent
Open Specific
List Sessions
// No persistence
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
});
// New persistent session
const { session } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd()),
});
// Continue most recent session
const { session, modelFallbackMessage } = await createAgentSession({
sessionManager: SessionManager.continueRecent(process.cwd()),
});
if (modelFallbackMessage) {
console.log("Note:", modelFallbackMessage);
}
// Open specific session file
const { session } = await createAgentSession({
sessionManager: SessionManager.open("/path/to/session.jsonl"),
});
// 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}...`);
});
Extensions and Resources
Automatic discovery from standard locations:
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 });
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 });
const loader = new DefaultResourceLoader({
systemPromptOverride: () => "You are a helpful assistant.",
});
await loader.reload();
const { session } = await createAgentSession({ resourceLoader: loader });
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
From Files
With Overrides
In-Memory
import { SettingsManager } from "@mariozechner/pi-coding-agent";
// Loads from ~/.pi/agent/settings.json and .pi/settings.json
const { session } = await createAgentSession({
settingsManager: SettingsManager.create(),
});
const settingsManager = SettingsManager.create();
settingsManager.applyOverrides({
compaction: { enabled: false },
retry: { enabled: true, maxRetries: 5 },
});
const { session } = await createAgentSession({ settingsManager });
// No file I/O, for testing
const { session } = await createAgentSession({
settingsManager: SettingsManager.inMemory({
compaction: { enabled: false }
}),
sessionManager: SessionManager.inMemory(),
});
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:
Interactive Mode
Print Mode
RPC Mode
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
Single-shot output: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"],
});
JSON-RPC for subprocess integration:import { createAgentSession, runRpcMode } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({ /* ... */ });
await runRpcMode(session); // Reads stdin, writes stdout
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