Files
oh-my-pi/packages/coding-agent/docs/sdk.md
T
can1357 1eaf40b784 feat(coding-agent): added temperature configuration setting for LLM sampling control
- Added `temperature` configuration setting to control LLM sampling temperature with values from 0 (deterministic) to 1 (creative) to -1 (provider default).
- Added temperature option selector in settings UI with preset values: Default, 0, 0.2, 0.5, 0.7, 1.
- Renamed `settingsInstance` parameter to `settings` in `CreateAgentSessionOptions` for consistency.
- Updated all internal references from `settingsInstance` to `settings` throughout SDK and components.
- Integrated temperature setting into agent configuration and selector controller.
2026-02-10 15:25:10 +01:00

1040 lines
28 KiB
Markdown

> 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<void>;
sendUserMessage(
content: string | (TextContent | ImageContent)[],
options?: { deliverAs?: "steer" | "followUp" }
): Promise<void>;
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<void>;
setModelTemporary(model: Model): Promise<void>;
setThinkingLevel(level: ThinkingLevel): void;
cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
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<boolean>; // Returns false if cancelled by extension
fork(): Promise<boolean>; // 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<T>(
message: { customType: string; content: T; display?: boolean; details?: unknown },
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" }
): Promise<void>;
// Compaction
compact(
customInstructions?: string,
options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void }
): Promise<CompactionResult>;
abortCompaction(): void;
// Utilities
getSessionStats(): SessionStats;
formatSessionAsText(): string;
formatCompactContext(): string;
exportToHtml(outputPath?: string): Promise<string>;
handoff(customInstructions?: string): Promise<{ document: string } | undefined>;
// Abort current operation
abort(): Promise<void>;
// Cleanup
dispose(): Promise<void>;
}
```
### 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: `<agentDir>/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(); // <agentDir>/agent.db
const modelRegistry = discoverModels(authStorage); // + <agentDir>/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";
```