353 lines
9.8 KiB
Markdown
353 lines
9.8 KiB
Markdown
# 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();
|
|
```
|