# SDK The SDK is the in-process integration surface for `@oh-my-pi/pi-coding-agent`. Use it when you want direct access to agent state, event streaming, tool wiring, and session control from your own Bun/Node process. If you need cross-language/process isolation, use RPC mode instead. ## Installation ```bash bun add @oh-my-pi/pi-coding-agent ``` ## Entry points `@oh-my-pi/pi-coding-agent` exports the SDK APIs from the package root (and also via `@oh-my-pi/pi-coding-agent/sdk`). Core exports for embedders: - `createAgentSession` - `SessionManager` - `Settings` - `AuthStorage` - `ModelRegistry` - `discoverAuthStorage` - Discovery helpers (`discoverExtensions`, `discoverSkills`, `discoverContextFiles`, `discoverPromptTemplates`, `discoverSlashCommands`, `discoverCustomTSCommands`, `discoverMCPServers`) - Tool factory surface (`createTools`, `BUILTIN_TOOLS`, tool classes) ## Quick start (auto-discovery defaults) ```ts import { createAgentSession } from "@oh-my-pi/pi-coding-agent"; const { session, modelFallbackMessage } = await createAgentSession(); if (modelFallbackMessage) { process.stderr.write(`${modelFallbackMessage}\n`); } const unsubscribe = session.subscribe((event) => { if ( event.type === "message_update" && event.assistantMessageEvent.type === "text_delta" ) { process.stdout.write(event.assistantMessageEvent.delta); } }); await session.prompt("Summarize this repository in 3 bullets."); unsubscribe(); await session.dispose(); ``` ## What `createAgentSession()` discovers by default `createAgentSession()` follows “provide to override, omit to discover”. If omitted, it resolves: - `cwd`: `getProjectDir()` - `agentDir`: `~/.omp/agent` (via `getAgentDir()`) - `authStorage`: `discoverAuthStorage(agentDir)` - `modelRegistry`: `new ModelRegistry(authStorage)` + background `refreshInBackground()` when the registry is not provided - `settings`: `await Settings.init({ cwd, agentDir })` - `sessionManager`: `SessionManager.create(cwd)` (file-backed) - skills/context files/prompt templates/slash commands/extensions/custom TS commands - built-in tools via `createTools(...)` - MCP tools (enabled by default) - LSP integration (enabled by default) - `eventBus`: new `EventBus()` unless supplied ### Required vs optional inputs Typically you must provide only what you want to control: - **Must provide**: nothing for a minimal session - **Usually provide explicitly** in embedders: - `sessionManager` (if you need in-memory or custom location) - `authStorage` + `modelRegistry` (if you own credential/model lifecycle) - `model` or `modelPattern` (if deterministic model selection matters) - `settings` (if you need isolated/test config) ## Session manager behavior (persistent vs in-memory) `AgentSession` always uses a `SessionManager`; behavior depends on which factory you use. ### File-backed (default) ```ts import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent"; const { session } = await createAgentSession({ sessionManager: SessionManager.create(process.cwd()), }); console.log(session.sessionFile); // absolute .jsonl path ``` - Persists conversation/messages/state deltas to session files. - Supports resume/open/list/fork workflows. - `session.sessionFile` is defined. ### In-memory ```ts import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent"; const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(), }); console.log(session.sessionFile); // undefined ``` - No filesystem persistence. - Useful for tests, ephemeral workers, request-scoped agents. - Session methods still work, but persistence-specific behaviors (file resume/fork paths) are naturally limited. ### Resume/open/list helpers ```ts import { SessionManager } from "@oh-my-pi/pi-coding-agent"; const recent = await SessionManager.continueRecent(process.cwd()); const listed = await SessionManager.list(process.cwd()); const opened = listed[0] ? await SessionManager.open(listed[0].path) : null; ``` ## Model and auth wiring `createAgentSession()` uses `ModelRegistry` + `AuthStorage` for model selection and API key resolution. ### Explicit wiring ```ts import { createAgentSession, discoverAuthStorage, ModelRegistry, SessionManager, } from "@oh-my-pi/pi-coding-agent"; const authStorage = await discoverAuthStorage(); const modelRegistry = new ModelRegistry(authStorage); await modelRegistry.refresh(); const available = modelRegistry.getAvailable(); if (available.length === 0) throw new Error("No authenticated models available"); const { session } = await createAgentSession({ authStorage, modelRegistry, model: available[0], thinkingLevel: "medium", sessionManager: SessionManager.inMemory(), }); ``` ### Selection order when `model` is omitted When no explicit `model`/`modelPattern` is provided: 1. restore model from existing session (if restorable + key available) 2. settings default model role (`default`) 3. first available model with valid auth If restore fails, `modelFallbackMessage` explains fallback. ### Auth priority `AuthStorage.getApiKey(...)` resolves in this order: 1. runtime override (`setRuntimeApiKey`) 2. stored credentials in `agent.db` 3. provider environment variables 4. custom-provider resolver fallback (if configured) ## Event subscription model Subscribe with `session.subscribe(listener)`; it returns an unsubscribe function. ```ts const unsubscribe = session.subscribe((event) => { switch (event.type) { case "agent_start": case "turn_start": case "tool_execution_start": break; case "message_update": if (event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } break; } }); ``` `AgentSessionEvent` includes core `AgentEvent` plus session-level events: - `auto_compaction_start` / `auto_compaction_end` - `auto_retry_start` / `auto_retry_end` - `retry_fallback_applied` / `retry_fallback_succeeded` - `ttsr_triggered` - `todo_reminder` / `todo_auto_clear` - `irc_message` ## Prompt lifecycle `session.prompt(text, options?)` is the primary entry point. Behavior: 1. optional command/template expansion (`/` commands, custom commands, file slash commands, prompt templates) 2. if currently streaming: - requires `streamingBehavior: "steer" | "followUp"` - queues instead of throwing work away 3. if idle: - validates model + API key - appends user message - starts agent turn Related APIs: - `sendUserMessage(content, { deliverAs? })` - `steer(text, images?)` - `followUp(text, images?)` - `sendCustomMessage({ customType, content, ... }, { deliverAs?, triggerTurn? })` - `abort()` ## Tools and extension integration ### Built-ins and filtering - Built-ins come from `createTools(...)` and `BUILTIN_TOOLS`. - `toolNames` acts as an allowlist for built-ins. - `customTools` and extension-registered tools are still included. - Hidden tools (for example `yield`) are opt-in unless required by options. ```ts const { session } = await createAgentSession({ toolNames: ["read", "grep", "find", "write"], requireYieldTool: true, }); ``` ### Extensions - `extensions`: inline `ExtensionFactory[]` - `additionalExtensionPaths`: load extra extension files - `disableExtensionDiscovery`: disable automatic extension scanning - `preloadedExtensions`: reuse already loaded extension set ### Runtime tool set changes `AgentSession` supports runtime activation updates: - `getActiveToolNames()` - `getAllToolNames()` - `setActiveToolsByName(names)` - `refreshMCPTools(mcpTools)` System prompt is rebuilt to reflect active tool changes. ## Discovery helpers Use these when you want partial control without recreating internal discovery logic: - `discoverAuthStorage(agentDir?)` - `discoverExtensions(cwd?)` - `discoverSkills(cwd?, _agentDir?, settings?)` - `discoverContextFiles(cwd?, _agentDir?)` - `discoverPromptTemplates(cwd?, agentDir?)` - `discoverSlashCommands(cwd?)` - `discoverCustomTSCommands(cwd?, agentDir?)` - `discoverMCPServers(cwd?)` - `buildSystemPrompt(options?)` ## Subagent-oriented options For SDK consumers building orchestrators (similar to task executor flow): - `outputSchema`: passes structured output expectation into tool context - `requireYieldTool`: forces `yield` tool inclusion - `taskDepth`: recursion-depth context for nested task sessions - `parentTaskPrefix`: artifact naming prefix for nested task outputs These are optional for normal single-agent embedding. ## `createAgentSession()` return value ```ts type CreateAgentSessionResult = { session: AgentSession; extensionsResult: LoadExtensionsResult; setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void; mcpManager?: MCPManager; modelFallbackMessage?: string; lspServers?: Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string; }>; eventBus: EventBus; }; ``` Use `setToolUIContext(...)` only if your embedder provides UI capabilities that tools/extensions should call into. ## Minimal controlled embed example ```ts import { createAgentSession, discoverAuthStorage, ModelRegistry, SessionManager, Settings, } from "@oh-my-pi/pi-coding-agent"; const authStorage = await discoverAuthStorage(); const modelRegistry = new ModelRegistry(authStorage); await modelRegistry.refresh(); const settings = Settings.isolated({ "compaction.enabled": true, "retry.enabled": true, }); const { session } = await createAgentSession({ authStorage, modelRegistry, settings, sessionManager: SessionManager.inMemory(), toolNames: ["read", "grep", "find", "edit", "write"], enableMCP: false, enableLsp: true, }); session.subscribe((event) => { if ( event.type === "message_update" && event.assistantMessageEvent.type === "text_delta" ) { process.stdout.write(event.assistantMessageEvent.delta); } }); await session.prompt("Find all TODO comments in this repo and propose fixes."); await session.dispose(); ```