> omp can help you use the SDK. Ask it to build an integration for your use case. # SDK The SDK provides programmatic access to omp's agent capabilities. Use it to embed omp in other applications, build custom interfaces, or integrate with automated workflows. **Example use cases:** - Build a custom UI (web, desktop, mobile) - Integrate agent capabilities into existing applications - Create automated pipelines with agent reasoning - Build custom tools that spawn sub-agents - Test agent behavior programmatically See [examples/sdk/](../examples/sdk/) for working examples from minimal to full control. ## Quick Start ```typescript import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@oh-my-pi/pi-coding-agent"; // Set up credential storage and model registry const authStorage = await discoverAuthStorage(); const modelRegistry = discoverModels(authStorage); const { session, modelFallbackMessage } = await createAgentSession({ sessionManager: SessionManager.inMemory(), authStorage, modelRegistry, }); if (modelFallbackMessage) { process.stderr.write(`${modelFallbackMessage}\n`); } 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?"); ``` ## Installation ```bash bun add @oh-my-pi/pi-coding-agent ``` The SDK is included in the main package. No separate installation needed. ## Core Concepts ### createAgentSession() The main factory function. Creates an `AgentSession` with configurable options. **Philosophy:** "Omit to discover, provide to override." - Omit an option → omp discovers/loads from standard locations - Provide an option → your value is used, discovery skipped for that option ```typescript import { createAgentSession } from "@oh-my-pi/pi-coding-agent"; import systemPrompt from "./SYSTEM.md" with { type: "text" }; // Minimal: all defaults (discovers from cwd + config dirs and ~/.omp/agent) const { session } = await createAgentSession(); // Custom: override specific options const { session } = await createAgentSession({ model: myModel, systemPrompt, toolNames: ["read", "bash", "edit"], // Filter to specific tools sessionManager: SessionManager.inMemory(), }); ``` ### AgentSession The session manages the agent lifecycle, message history, and event streaming. ```typescript interface AgentSession { // Prompting prompt(text: string, options?: PromptOptions): Promise; sendUserMessage( content: string | (TextContent | ImageContent)[], options?: { deliverAs?: "steer" | "followUp" } ): Promise; steer(text: string): void; followUp(text: string): void; // Subscribe to events (returns unsubscribe function) subscribe(listener: (event: AgentSessionEvent) => void): () => void; // Session info sessionFile: string | undefined; // undefined for in-memory sessionId: string; sessionName: string | undefined; // Model control setModel(model: Model, role?: ModelRole): Promise; setModelTemporary(model: Model): Promise; setThinkingLevel(level: ThinkingLevel): void; cycleModel(direction?: "forward" | "backward"): Promise; cycleRoleModels( direction?: "forward" | "backward" ): Promise<{ model: Model; thinkingLevel: ThinkingLevel; role: ModelRole } | undefined>; cycleThinkingLevel(): ThinkingLevel | undefined; // State access agent: Agent; sessionManager: SessionManager; settings: Settings; model: Model | undefined; thinkingLevel: ThinkingLevel; messages: AgentMessage[]; isStreaming: boolean; isCompacting: boolean; isRetrying: boolean; // Session management newSession(options?: NewSessionOptions): Promise; // Returns false if cancelled by extension fork(): Promise; // Creates a new session file // Branching branch(entryId: string): Promise<{ selectedText: string; cancelled: boolean }>; navigateTree( targetId: string, options?: { summarize?: boolean; customInstructions?: string } ): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean; summaryEntry?: BranchSummaryEntry }>; // Custom message injection sendCustomMessage( message: { customType: string; content: T; display?: boolean; details?: unknown }, options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" } ): Promise; // Compaction compact( customInstructions?: string, options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void } ): Promise; abortCompaction(): void; // Utilities getSessionStats(): SessionStats; formatSessionAsText(): string; formatCompactContext(): string; exportToHtml(outputPath?: string): Promise; handoff(customInstructions?: string): Promise<{ document: string } | undefined>; // Abort current operation abort(): Promise; // Cleanup dispose(): Promise; } ``` ### Agent and AgentState The `Agent` class (from `@oh-my-pi/pi-agent-core`) handles the core LLM interaction. Access it via `session.agent`. ```typescript // Access current state const state = session.agent.state; // state.messages: AgentMessage[] - conversation history // state.model: Model - current model // state.thinkingLevel: ThinkingLevel - current thinking level // state.systemPrompt: string - system prompt // state.tools: Tool[] - available tools // Replace messages (useful for branching, restoration) session.agent.replaceMessages(messages); // Wait for agent to finish processing await session.agent.waitForIdle(); ``` ### Events Subscribe to events to receive streaming output and lifecycle notifications. ```typescript session.subscribe((event) => { switch (event.type) { // Streaming text from assistant case "message_update": if (event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } if (event.assistantMessageEvent.type === "thinking_delta") { // Thinking output (if thinking enabled) } break; // Tool execution case "tool_execution_start": console.log(`Tool: ${event.toolName}`); break; case "tool_execution_update": // Streaming tool output break; case "tool_execution_end": console.log(`Result: ${event.isError ? "error" : "success"}`); break; // Message lifecycle case "message_start": // New message starting break; case "message_end": // Message complete break; // Agent lifecycle case "agent_start": // Agent started processing prompt break; case "agent_end": // Agent finished (event.messages contains new messages) break; // Turn lifecycle (one LLM response + tool calls) case "turn_start": break; case "turn_end": // event.message: assistant response // event.toolResults: tool results from this turn break; // Session events (auto-compaction, retry, TTSR, todo reminders) case "auto_compaction_start": case "auto_compaction_end": case "auto_retry_start": case "auto_retry_end": case "ttsr_triggered": // event.rules break; case "todo_reminder": // event.todos break; } }); ``` ## Options Reference ### Directories ```typescript const { session } = await createAgentSession({ // Working directory for project-local discovery cwd: process.cwd(), // default // Global config directory agentDir: "~/.omp/agent", // default (expands ~) }); ``` `cwd` is used for: - Project config discovery (`.omp/`, `.pi/`, `.claude/`, `.codex/`, `.gemini/`) - Project extensions/tools/skills/commands (via config dirs) - Context files (`AGENTS.md` walking up from cwd) - Session directory naming (via `SessionManager.create(cwd)`) `agentDir` is used for: - Global settings (`config.yml` + `agent.db`) - Primary auth/models locations (`agent.db`, `models.yml`, `models.json`) - Prompt templates (`prompts/`) - Custom TS commands (`commands/`) ### Model ```typescript import { getModel } from "@oh-my-pi/pi-ai"; import { discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent"; const authStorage = await discoverAuthStorage(); const modelRegistry = discoverModels(authStorage); // Find specific built-in model (doesn't check if API key exists) const opus = getModel("anthropic", "claude-opus-4-5"); if (!opus) throw new Error("Model not found"); // Find any model by provider/id, including custom models from models.yml // (doesn't check if API key exists) const customModel = modelRegistry.find("my-provider", "my-model"); // Get all models that have valid API keys configured const available = modelRegistry.getAvailable(); const { session } = await createAgentSession({ model: opus, thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh // Models for cycling (Ctrl+P in interactive mode) scopedModels: [ { model: opus, thinkingLevel: "high" }, { model: haiku, thinkingLevel: "off" }, ], authStorage, modelRegistry, }); ``` If no model is provided: 1. Tries to restore from session (if continuing) 2. Uses default from settings 3. Falls back to first available model > See [examples/sdk/02-custom-model.ts](../examples/sdk/02-custom-model.ts) ### API Keys and OAuth API key resolution priority (handled by AuthStorage): 1. Runtime overrides (via `setRuntimeApiKey`, not persisted) 2. Stored credentials in `agent.db` (API keys or OAuth tokens) 3. Environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.) 4. Fallback resolver (for custom provider keys from `models.yml`) `discoverAuthStorage` opens the `agent.db` SQLite database in the agent directory. ```typescript import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent"; // Default: uses agentDir/agent.db and agentDir/models.yml const authStorage = await discoverAuthStorage(); const modelRegistry = discoverModels(authStorage); const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(), authStorage, modelRegistry, }); // Runtime API key override (not persisted to disk) authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key"); // Custom auth storage location (use create(), constructor is private) const customAuth = await AuthStorage.create("/my/app/agent.db"); const customRegistry = new ModelRegistry(customAuth, "/my/app/models.yml"); const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(), authStorage: customAuth, modelRegistry: customRegistry, }); // No custom models.yml (built-in models only) const simpleRegistry = new ModelRegistry(authStorage); ``` > See [examples/sdk/09-api-keys-and-oauth.ts](../examples/sdk/09-api-keys-and-oauth.ts) ### System Prompt ```typescript import systemPrompt from "./SYSTEM.md" with { type: "text" }; const { session } = await createAgentSession({ // Replace entirely with a static prompt systemPrompt, }); const { session: modified } = await createAgentSession({ // Or modify default (receives default, returns modified) systemPrompt: (defaultPrompt) => { return `${defaultPrompt}\n\n## Additional Rules\n- Be concise`; }, }); ``` > See [examples/sdk/03-custom-prompt.ts](../examples/sdk/03-custom-prompt.ts) ### Tools By default, `createAgentSession` creates all built-in tools automatically. You can filter which tools are available using `toolNames`: ```typescript // Use all built-in tools (default) const { session } = await createAgentSession(); // Filter to specific tools const { session } = await createAgentSession({ toolNames: ["read", "grep", "find"], // Read-only tools }); `toolNames` is an allowlist for built-ins; custom tools are always included even if not listed. ``` #### Available Built-in Tools All tools are defined in `BUILTIN_TOOLS`: - `ask` - Interactive user prompts (requires UI) - `bash` - Shell command execution - `python` - Python REPL execution - `calc` - Calculator - `ssh` - Remote SSH execution - `edit` - Surgical file editing - `find` - File search by glob patterns - `grep` - Content search with regex - `lsp` - Language server protocol integration - `notebook` - Jupyter notebook editing - `read` - File reading (text and images) - `browser` - Puppeteer-based web browser - `task` - Subagent spawning - `todo_write` - Todo file management - `fetch` - URL fetching - `web_search` - Web search - `write` - File writing Hidden tools (not in `BUILTIN_TOOLS`) are available but excluded unless requested: - `submit_result` - Required for subagent structured output (use `requireSubmitResultTool` or include in `toolNames`) - `report_finding` - Security review reporting - `exit_plan_mode` - Plan mode control #### Creating Tools Manually For advanced use cases, you can create tools directly using `createTools`: ```typescript import { createTools, Settings, type ToolSession } from "@oh-my-pi/pi-coding-agent"; const settings = await Settings.init({ cwd: "/path/to/project" }); const session: ToolSession = { cwd: "/path/to/project", hasUI: false, getSessionFile: () => null, getSessionSpawns: () => "*", settings, }; const tools = await createTools(session); ``` **When you don't need factories:** - If you omit `toolNames`, omp automatically creates them with the correct `cwd` - If you use `process.cwd()` as your `cwd`, the pre-built instances work fine **When you must use factories:** - When you specify both `cwd` (different from `process.cwd()`) AND custom tools ### Custom Tools ```typescript import { Type } from "@sinclair/typebox"; import { createAgentSession, type CustomTool } from "@oh-my-pi/pi-coding-agent"; // Inline custom tool const myTool: CustomTool = { 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}` }], }), // Optional session lifecycle handler onSession: async (event, ctx) => { if (event.reason === "shutdown") { // cleanup } }, }; // Add custom tools (merged with built-in tools) const { session } = await createAgentSession({ customTools: [myTool], }); ``` ### Extensions Extensions intercept agent events and can register custom tools/commands. Hooks remain for legacy compatibility. ```typescript import { createAgentSession, discoverExtensions, type ExtensionFactory } from "@oh-my-pi/pi-coding-agent"; // Inline extension const loggingExtension: ExtensionFactory = (api) => { // Log tool calls api.on("tool_call", async (event) => { console.log(`Tool: ${event.toolName}`); return undefined; // Don't block }); // Block dangerous commands api.on("tool_call", async (event) => { if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) { return { block: true, reason: "Dangerous command" }; } return undefined; }); // Register custom slash command api.registerCommand("stats", { description: "Show session stats", handler: async (args, ctx) => { const entries = ctx.sessionManager.getEntries(); ctx.ui.notify(`${entries.length} entries`, "info"); }, }); }; // Merge with discovery (default behavior) const { session } = await createAgentSession({ extensions: [loggingExtension], }); // Replace discovery const { session } = await createAgentSession({ extensions: [loggingExtension], disableExtensionDiscovery: true, }); // Disable all extensions const { session } = await createAgentSession({ extensions: [], disableExtensionDiscovery: true, }); // Use preloaded extensions (skip discovery I/O) const discovered = await discoverExtensions(); const { session } = await createAgentSession({ preloadedExtensions: discovered, extensions: [loggingExtension], }); // Add paths without replacing discovery const { session } = await createAgentSession({ additionalExtensionPaths: ["/extra/extensions"], }); ``` Extension API methods: - `api.on(event, handler)` - Subscribe to events - `api.registerTool(definition)` - Register a custom tool - `api.registerCommand(name, options)` - Register custom slash command - `api.registerMessageRenderer(customType, renderer)` - Custom TUI rendering - `api.exec(command, args, options?)` - Execute shell commands > See [examples/sdk/06-extensions.ts](../examples/sdk/06-extensions.ts) and [docs/extensions.md](extensions.md) ### Skills ```typescript import { createAgentSession, discoverSkills, type Skill } from "@oh-my-pi/pi-coding-agent"; // Discover and filter const { skills: allSkills, warnings } = await discoverSkills(); const filtered = allSkills.filter((s) => s.name.includes("search")); // Custom skill const mySkill: Skill = { name: "my-skill", description: "Custom instructions", filePath: "/path/to/SKILL.md", baseDir: "/path/to", source: "custom", }; const { session } = await createAgentSession({ skills: [...filtered, mySkill], }); // Disable skills const { session } = await createAgentSession({ skills: [], }); ``` > See [examples/sdk/04-skills.ts](../examples/sdk/04-skills.ts) ### Context Files ```typescript import { createAgentSession, discoverContextFiles } from "@oh-my-pi/pi-coding-agent"; // Discover AGENTS.md files const discovered = await discoverContextFiles(); // Add custom context const { session } = await createAgentSession({ contextFiles: [ ...discovered, { path: "/virtual/AGENTS.md", content: "# Guidelines\n\n- Be concise\n- Use TypeScript", }, ], }); // Disable context files const { session } = await createAgentSession({ contextFiles: [], }); ``` > See [examples/sdk/07-context-files.ts](../examples/sdk/07-context-files.ts) ### Slash Commands ```typescript import { createAgentSession, discoverSlashCommands, type FileSlashCommand } from "@oh-my-pi/pi-coding-agent"; const discovered = await discoverSlashCommands(); const customCommand: FileSlashCommand = { name: "deploy", description: "Deploy the application", source: "(custom)", content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy", }; const { session } = await createAgentSession({ slashCommands: [...discovered, customCommand], }); ``` > See [examples/sdk/08-slash-commands.ts](../examples/sdk/08-slash-commands.ts) ### Session Management Sessions use a tree structure with `id`/`parentId` linking, enabling in-place branching. ```typescript import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent"; // In-memory (no persistence) const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(), }); // New persistent session const { session } = await createAgentSession({ sessionManager: SessionManager.create(process.cwd()), }); // Continue most recent (async) const { session, modelFallbackMessage } = await createAgentSession({ sessionManager: await SessionManager.continueRecent(process.cwd()), }); if (modelFallbackMessage) { console.log("Note:", modelFallbackMessage); } // Open specific file (async) const { session } = await createAgentSession({ sessionManager: await SessionManager.open("/path/to/session.jsonl"), }); // List available sessions (async) const sessions = await SessionManager.list(process.cwd()); for (const info of sessions) { console.log(`${info.id}: ${info.firstMessage} (${info.messageCount} messages)`); } // Custom session directory (no cwd encoding) const customDir = "/path/to/my-sessions"; const { session } = await createAgentSession({ sessionManager: SessionManager.create(process.cwd(), customDir), }); ``` **SessionManager static factories:** - `SessionManager.create(cwd, sessionDir?)` - New persistent session (sync) - `SessionManager.inMemory(cwd?)` - In-memory session (sync) - `SessionManager.open(filePath, sessionDir?)` - Open existing file (async) - `SessionManager.continueRecent(cwd, sessionDir?)` - Most recent session (async) - `SessionManager.list(cwd, sessionDir?)` - List sessions (async) - `SessionManager.listAll()` - List all sessions across cwds (async) - `SessionManager.forkFrom(sourcePath, cwd, sessionDir?)` - Fork from existing (async) **SessionManager tree API:** ```typescript const sm = await SessionManager.open("/path/to/session.jsonl"); // Tree traversal const entries = sm.getEntries(); // All entries (excludes header) const tree = sm.getTree(); // Full tree structure const branch = sm.getBranch(); // Path from root to current leaf const leaf = sm.getLeafEntry(); // Current leaf entry const entry = sm.getEntry(id); // Get entry by ID const children = sm.getChildren(id); // Direct children of entry // Labels const label = sm.getLabel(id); // Get label for entry sm.appendLabelChange(id, "checkpoint"); // Set label // Branching sm.branch(entryId); // Move leaf to earlier entry sm.branchWithSummary(id, "Summary..."); // Branch with context summary sm.createBranchedSession(leafId); // Extract path to new file ``` > See [examples/sdk/11-sessions.ts](../examples/sdk/11-sessions.ts) and [docs/session.md](session.md) ### Settings Management ```typescript import { createAgentSession, Settings, SessionManager } from "@oh-my-pi/pi-coding-agent"; // Default: loads from files (global config.yml + project settings.json merged) const settings = await Settings.init(); const { session } = await createAgentSession({ settings, }); // Read/write settings const enabled = settings.get("compaction.enabled"); settings.set("compaction.enabled", false); // In-memory (no file I/O, for testing) const isolated = Settings.isolated({ "compaction.enabled": false, "retry.enabled": true, }); const { session } = await createAgentSession({ settings: isolated, sessionManager: SessionManager.inMemory(), }); // Custom directories const { session } = await createAgentSession({ cwd: "/custom/cwd", agentDir: "/custom/agent", }); ``` **Settings static factories:** - `Settings.init(options?)` - Load from files (async) - `Settings.isolated(overrides?)` - In-memory, no file I/O (sync) - `Settings.instance` - Global singleton (throws if not initialized) **Settings file locations:** Settings load from two locations and merge: 1. Global: `/config.yml` (default `~/.omp/agent/config.yml`) 2. Project: `settings.json` from the first matching config dir (`.omp/`, `.pi/`, `.claude/`, `.codex/`, `.gemini/`) Project overrides global. Nested objects merge keys. ## Discovery Functions Discovery functions accept optional `cwd` and `agentDir` parameters where applicable. ```typescript import { getModel } from "@oh-my-pi/pi-ai"; import appendPrompt from "./APPEND_SYSTEM.md" with { type: "text" }; import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels, discoverSkills, discoverExtensions, discoverContextFiles, discoverSlashCommands, discoverPromptTemplates, discoverCustomTSCommands, discoverMCPServers, buildSystemPrompt, Settings, } from "@oh-my-pi/pi-coding-agent"; // Auth and Models const authStorage = await discoverAuthStorage(); // /agent.db const modelRegistry = discoverModels(authStorage); // + /models.yml (or models.json) const allModels = modelRegistry.getAll(); // All models (built-in + custom) const available = modelRegistry.getAvailable(); // Only models with API keys const model = modelRegistry.find("provider", "id"); // Find specific model const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only // Skills (async) const { skills, warnings } = await discoverSkills(cwd, agentDir, skillsSettings); // Extensions (async - loads TypeScript) const extensionsResult = await discoverExtensions(cwd); // Custom TS commands (async - loads TypeScript) const customCommands = await discoverCustomTSCommands(cwd, agentDir); // Context files (async) const contextFiles = await discoverContextFiles(cwd, agentDir); // Slash commands (async) const commands = await discoverSlashCommands(cwd); // Prompt templates (async) const promptTemplates = await discoverPromptTemplates(cwd, agentDir); // MCP servers (async) const mcp = await discoverMCPServers(cwd); // Settings (async - global + project merged) const settings = await Settings.init({ cwd, agentDir }); // Build system prompt manually const prompt = await buildSystemPrompt({ skills, contextFiles, appendPrompt, cwd, }); ``` ## Return Value `createAgentSession()` returns: ```typescript interface CreateAgentSessionResult { // The session session: AgentSession; // Extensions result (loaded extensions + runtime) extensionsResult: LoadExtensionsResult; // Update tool UI context (interactive mode) setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void; // MCP manager for server lifecycle management (undefined if MCP disabled) mcpManager?: MCPManager; // Warning if session model couldn't be restored modelFallbackMessage?: string; // LSP servers that were warmed up at startup lspServers?: Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string }>; } ``` ## Complete Example ```typescript import { getModel } from "@oh-my-pi/pi-ai"; import { Type } from "@sinclair/typebox"; import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager, Settings, type ExtensionFactory, type CustomTool, } from "@oh-my-pi/pi-coding-agent"; import systemPrompt from "./SYSTEM.md" with { type: "text" }; // Set up auth storage const authStorage = await discoverAuthStorage(); // Runtime API key override (not persisted) if (Bun.env.MY_KEY) { authStorage.setRuntimeApiKey("anthropic", Bun.env.MY_KEY); } // Model registry const modelRegistry = discoverModels(authStorage); // Inline extension const auditExtension: ExtensionFactory = (api) => { api.on("tool_call", async (event) => { console.log(`[Audit] ${event.toolName}`); return undefined; }); }; // Inline tool const statusTool: CustomTool = { name: "status", label: "Status", description: "Get system status", parameters: Type.Object({}), execute: async (toolCallId, params, onUpdate, ctx, signal) => ({ content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }], }), }; const model = getModel("anthropic", "claude-opus-4-5"); if (!model) throw new Error("Model not found"); // In-memory settings with overrides const settings = Settings.isolated({ "compaction.enabled": false, "retry.enabled": true, }); const { session } = await createAgentSession({ cwd: process.cwd(), agentDir: "/custom/agent", model, thinkingLevel: "off", authStorage, modelRegistry, systemPrompt, toolNames: ["read", "bash"], customTools: [statusTool], extensions: [auditExtension], skills: [], contextFiles: [], slashCommands: [], sessionManager: SessionManager.inMemory(), settings, }); 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."); ``` ## RPC Mode Alternative For subprocess-based integration, use RPC mode instead of the SDK: ```bash omp --mode rpc --no-session ``` See [RPC documentation](rpc.md) for the JSON protocol. The SDK is preferred when: - You want type safety - You're in the same Node.js process - You need direct access to agent state - You want to customize tools/extensions programmatically RPC mode is preferred when: - You're integrating from another language - You want process isolation - You're building a language-agnostic client ## Exports The main entry point exports: ```typescript // Factory createAgentSession // Auth and Models AuthStorage ModelRegistry discoverAuthStorage discoverModels // Discovery discoverSkills discoverExtensions discoverCustomTSCommands discoverContextFiles discoverSlashCommands discoverPromptTemplates discoverMCPServers // Helpers buildSystemPrompt Settings // Session management SessionManager // Tool registry and factory BUILTIN_TOOLS // Map of tool name to factory createTools // Create all tools from ToolSession type ToolSession // Session context for tool creation // Individual tool classes ReadTool, BashTool, EditTool, WriteTool GrepTool, FindTool, PythonTool loadSshTool // Types type CreateAgentSessionOptions type CreateAgentSessionResult type CustomTool type ExtensionFactory type Skill type FileSlashCommand type SkillsSettings type Tool ``` For extension types, import from the main package: ```typescript import type { ExtensionAPI, ExtensionFactory, ExtensionContext, ExtensionCommandContext, ToolDefinition, } from "@oh-my-pi/pi-coding-agent"; ``` For legacy hook types (deprecated, use extensions instead): ```typescript import type { HookAPI, HookFactory, HookContext, HookCommandContext } from "@oh-my-pi/pi-coding-agent/hooks"; ``` For config utilities: ```typescript import { getAgentDir } from "@oh-my-pi/pi-coding-agent"; ```