Pi 官方文档
SDK
pi 可以帮你使用 SDK。让它为你的使用场景构建一个集成方案。
SDK
SDK 提供对 pi 的 agent 能力的编程访问。你可以把它嵌入到其他应用中,构建自定义界面,或者接入自动化工作流。
示例用法:
- 构建自定义 UI(web、desktop、mobile)
- 将 agent 能力集成到现有应用中
- 利用 agent 推理创建自动化流水线
- 构建会生成子 agent 的自定义工具
- 以编程方式测试 agent 行为
查看 examples/sdk/ 中的可运行示例,从最小配置到完全控制都有覆盖。
快速开始
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@earendil-works/pi-coding-agent";
// Set up credential storage and model registry
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(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?");
安装
npm install @earendil-works/pi-coding-agent
SDK 已包含在主 Package 中,无需单独安装。
核心概念
createAgentSession()
用于创建单个 AgentSession 的主工厂函数。
createAgentSession() 会使用 ResourceLoader 来提供 extensions、skills、prompt templates、themes 和 context files。如果你没有传入,它会使用带标准发现机制的 DefaultResourceLoader。
import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
// Minimal: defaults with DefaultResourceLoader
const { session } = await createAgentSession();
// Custom: override specific options
const { session } = await createAgentSession({
model: myModel,
tools: ["read", "bash"],
sessionManager: SessionManager.inMemory(),
});
AgentSession
session 负责管理 agent 生命周期、消息历史、模型状态、上下文压缩和事件流式传输。
interface AgentSession {
// Send a prompt and wait for completion
prompt(text: string, options?: PromptOptions): Promise<void>;
// Queue messages during streaming
steer(text: string): Promise<void>;
followUp(text: string): Promise<void>;
// Subscribe to events (returns unsubscribe function)
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
// Session info
sessionFile: string | undefined;
sessionId: string;
// Model control
setModel(model: Model): Promise<void>;
setThinkingLevel(level: ThinkingLevel): void;
cycleModel(): Promise<ModelCycleResult | undefined>;
cycleThinkingLevel(): ThinkingLevel | undefined;
// State access
agent: Agent;
model: Model | undefined;
thinkingLevel: ThinkingLevel;
messages: AgentMessage[];
isStreaming: boolean;
// In-place tree navigation within the current session file
navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>;
// Compaction
compact(customInstructions?: string): Promise<CompactionResult>;
abortCompaction(): void;
// Abort current operation
abort(): Promise<void>;
// Cleanup
dispose(): void;
}
像 new-session、resume、fork 和 import 这类 session 替换 API,定义在 AgentSessionRuntime 上,而不是 AgentSession 上。
createAgentSessionRuntime() 和 AgentSessionRuntime
当你需要替换当前 session,并重建与 cwd 绑定的 runtime 状态时,请使用 runtime API。 这也是内置交互模式、print 模式和 RPC 模式所使用的同一层。
createAgentSessionRuntime() 接收一个 runtime 工厂,以及初始 cwd/session 目标。这个工厂会闭包捕获进程全局的固定输入,为有效 cwd 重建与 cwd 绑定的服务,基于这些服务解析 session 选项,然后返回完整的 runtime 结果。
import {
type CreateAgentSessionRuntimeFactory,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
SessionManager,
} from "@earendil-works/pi-coding-agent";
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({
services,
sessionManager,
sessionStartEvent,
})),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.create(process.cwd()),
});
AgentSessionRuntime 负责在以下操作中替换当前 runtime:
newSession()switchSession()fork()- 通过
fork(entryId, { position: "at" })的 clone 流程 importFromJsonl()
重要行为:
- 执行这些操作后,
runtime.session会发生变化 - 事件订阅是绑定到具体
AgentSession上的,所以替换后需要重新订阅 - 如果你使用 extensions,需要针对新 session 再次调用
runtime.session.bindExtensions(...) - 创建结果会把诊断信息返回到
runtime.diagnostics - 如果 runtime 创建或替换失败,方法会抛出异常,由调用方决定如何处理
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});
await runtime.newSession();
unsubscribe();
session = runtime.session;
unsubscribe = session.subscribe(() => {});
提示与消息队列
PromptOptions 用来控制 prompt 展开、流式传输期间的排队行为,以及 prompt 预检通知:
interface PromptOptions {
expandPromptTemplates?: boolean;
images?: ImageContent[];
streamingBehavior?: "steer" | "followUp";
source?: InputSource;
preflightResult?: (success: boolean) => void;
}
preflightResult 会在每次调用 prompt() 时执行一次:
- 当 prompt 被接受、进入队列,或被立即处理时,返回
true - 当 prompt 在接受前就被预检拒绝时,返回
false
它会在 prompt() resolve 之前触发。prompt() 只有在完整的已接受运行结束后才会 resolve,包括重试在内。接受后的失败会通过正常的事件流和消息流上报,而不是通过 preflightResult(false)。
prompt() 方法会处理 prompt templates、extension commands 和消息发送:
// Basic prompt (when not streaming)
await session.prompt("What files are here?");
// With images
await session.prompt("What's in this image?", {
images: [{ type: "image", source: { type: "base64", mediaType: "image/png", data: "..." } }]
});
// During streaming: must specify how to queue the message
await session.prompt("Stop and do this instead", { streamingBehavior: "steer" });
await session.prompt("After you're done, also check X", { streamingBehavior: "followUp" });
行为:
- extension commands(例如
/mycommand):会立即执行,即使在流式传输过程中也是如此。它们通过pi.sendMessage()自行管理 LLM 交互。 - 基于文件的 prompt templates(来自
.md文件):在发送或排队之前,会先展开为其内容。 - 在没有
streamingBehavior的流式传输期间:会抛出错误。请直接使用steer()或followUp(),或者显式指定该选项。 preflightResult(true):表示 prompt 已被接受、进入队列,或被立即处理。preflightResult(false):表示在接受前就被预检拒绝。
如果要在流式传输期间显式排队:
// Queue a steering message for delivery after the current assistant turn finishes its tool calls
await session.steer("New instruction");
// Wait for agent to finish (delivered only when agent stops)
await session.followUp("After you're done, also do this");
steer() 和 followUp() 都会展开基于文件的 prompt templates,但遇到 extension commands 时会报错(extension commands 不能排队)。
Agent 和 AgentState
Agent 类(来自 @earendil-works/pi-agent-core)负责处理核心 LLM 交互。通过 session.agent 访问它。
// 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: AgentTool[] - available tools
// state.streamingMessage?: AgentMessage - current partial assistant message
// state.errorMessage?: string - latest assistant error
// Replace messages (useful for branching or restoration)
session.agent.state.messages = messages; // copies the top-level array
// Replace tools
session.agent.state.tools = tools; // copies the top-level array
// Wait for agent to finish processing
await session.agent.waitForIdle();
事件
订阅这些事件,可以接收流式输出和生命周期通知。
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 (queue, compaction, retry)
case "queue_update":
console.log(event.steering, event.followUp);
break;
case "compaction_start":
case "compaction_end":
case "auto_retry_start":
case "auto_retry_end":
break;
}
});
选项参考
目录
const { session } = await createAgentSession({
// Working directory for DefaultResourceLoader discovery
cwd: process.cwd(), // default
// Global config directory
agentDir: "~/.pi/agent", // default (expands ~)
});
cwd 会被 DefaultResourceLoader 用于:
- 项目扩展(
.pi/extensions/) - 项目 Skill:
.pi/skills/cwd及其祖先目录中的.agents/skills/(直到 git 仓库根目录;如果不在仓库中,则直到文件系统根目录)
- 项目提示词(
.pi/prompts/) - 上下文文件(从
cwd向上查找AGENTS.md) - 会话目录命名
agentDir 会被 DefaultResourceLoader 用于:
- 全局扩展(
extensions/) - 全局 Skill:
agentDir下的skills/(例如~/.pi/agent/skills/)~/.agents/skills/
- 全局提示词(
prompts/) - 全局上下文文件(
AGENTS.md) - 设置(
settings.json) - 自定义模型(
models.json) - 凭据(
auth.json) - 会话(
sessions/)
当你传入自定义 ResourceLoader 时,cwd 和 agentDir 将不再控制资源发现。它们仍会影响会话命名和工具路径解析。
模型
import { getModel } from "@earendil-works/pi-ai";
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(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.json
// (doesn't check if API key exists)
const customModel = modelRegistry.find("my-provider", "my-model");
// Get only models that have valid API keys configured
const available = await 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,
});
如果没有提供模型:
- 尝试从会话中恢复(如果是继续之前的会话)
- 使用 settings 中的默认值
- 回退到第一个可用模型
API Key 和 OAuth
API key 的解析优先级(由 AuthStorage 处理):
- 运行时覆盖(通过
setRuntimeApiKey,不会持久化) - 存储在
auth.json中的凭据(API key 或 OAuth token) - 环境变量(
ANTHROPIC_API_KEY、OPENAI_API_KEY等) - 回退解析器(用于
models.json中自定义 Provider 的 key)
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(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
const customAuth = AuthStorage.create("/my/app/auth.json");
const customRegistry = ModelRegistry.create(customAuth, "/my/app/models.json");
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage: customAuth,
modelRegistry: customRegistry,
});
// No custom models.json (built-in models only)
const simpleRegistry = ModelRegistry.inMemory(authStorage);
系统提示词
使用 ResourceLoader 覆盖系统提示词:
import { createAgentSession, DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
const loader = new DefaultResourceLoader({
systemPromptOverride: () => "You are a helpful assistant.",
});
await loader.reload();
const { session } = await createAgentSession({ resourceLoader: loader });
工具
指定要启用哪些内置工具:
- 内置工具名称:
read、bash、edit、write、grep、find、ls - 默认内置工具:
read、bash、edit、write noTools: "all"会禁用所有工具noTools: "builtin"会禁用默认内置工具,但保留扩展和自定义工具可用- 在应用任何
toolsallowlist 之后,excludeTools会禁用指定的内置、扩展或自定义工具名称
edit 工具会返回 details.diff,供 Pi 的 TUI 显示;也会返回 details.patch,作为标准 unified patch 供 SDK 使用方使用。
import { createAgentSession } from "@earendil-works/pi-coding-agent";
// Read-only mode
const { session } = await createAgentSession({
tools: ["read", "grep", "find", "ls"],
});
// Pick specific tools
const { session } = await createAgentSession({
tools: ["read", "bash", "grep"],
});
// Disable one tool while keeping the rest available
const { session } = await createAgentSession({
excludeTools: ["ask_question"],
});
带自定义 cwd 的工具
当你传入自定义 cwd 时,createAgentSession() 会针对该 cwd 构建所选的内置工具。
import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
const cwd = "/path/to/project";
// Use default tools for custom cwd
const { session } = await createAgentSession({
cwd,
sessionManager: SessionManager.inMemory(cwd),
});
// Or pick specific tools for custom cwd
const { session } = await createAgentSession({
cwd,
tools: ["read", "bash", "grep"],
sessionManager: SessionManager.inMemory(cwd),
});
自定义工具
import { Type } from "typebox";
import { createAgentSession, defineTool } from "@earendil-works/pi-coding-agent";
// Inline custom tool
const myTool = defineTool({
name: "my_tool",
label: "My Tool",
description: "Does something useful",
parameters: Type.Object({
input: Type.String({ description: "Input value" }),
}),
execute: async (_toolCallId, params) => ({
content: [{ type: "text", text: `Result: ${params.input}` }],
details: {},
}),
});
// Pass custom tools directly
const { session } = await createAgentSession({
customTools: [myTool],
});
使用 defineTool() 定义独立工具,并配合 customTools: [myTool] 这类数组使用。内联的 pi.registerTool({ ... }) 已经能正确推断参数类型。
通过 customTools 传入的自定义工具,会和扩展注册的工具合并。ResourceLoader 加载的扩展也可以通过 pi.registerTool() 注册工具。
如果你传入 tools,请把要启用的每个自定义或扩展工具名称都包含进去,例如 tools: ["read", "bash", "my_tool"]。
扩展
扩展由 ResourceLoader 加载。DefaultResourceLoader 会从 ~/.pi/agent/extensions/、.pi/extensions/ 以及 settings.json 的扩展来源中发现扩展。
import { createAgentSession, DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
const loader = new DefaultResourceLoader({
additionalExtensionPaths: ["/path/to/my-extension.ts"],
extensionFactories: [
(pi) => {
pi.on("agent_start", () => {
console.log("[Inline Extension] Agent starting");
});
},
],
});
await loader.reload();
const { session } = await createAgentSession({ resourceLoader: loader });
扩展可以注册工具、订阅事件、添加命令等。完整 API 见 extensions.md。
事件总线: 扩展可以通过 pi.events 进行通信。如果你需要从外部发出或监听事件,请向 DefaultResourceLoader 传入共享的 eventBus:
import { createEventBus, DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
const eventBus = createEventBus();
const loader = new DefaultResourceLoader({
eventBus,
});
await loader.reload();
eventBus.on("my-extension:status", (data) => console.log(data));
Skills
import {
createAgentSession,
DefaultResourceLoader,
type Skill,
} from "@earendil-works/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 });
上下文文件
import { createAgentSession, DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
const loader = new DefaultResourceLoader({
agentsFilesOverride: (current) => ({
agentsFiles: [
...current.agentsFiles,
{ path: "/virtual/AGENTS.md", content: "# Guidelines\n\n- Be concise" },
],
}),
});
await loader.reload();
const { session } = await createAgentSession({ resourceLoader: loader });
斜杠命令
import {
createAgentSession,
DefaultResourceLoader,
type PromptTemplate,
} from "@earendil-works/pi-coding-agent";
const customCommand: PromptTemplate = {
name: "deploy",
description: "Deploy the application",
source: "(custom)",
content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
};
const loader = new DefaultResourceLoader({
promptsOverride: (current) => ({
prompts: [...current.prompts, customCommand],
diagnostics: current.diagnostics,
}),
});
await loader.reload();
const { session } = await createAgentSession({ resourceLoader: loader });
会话管理
会话使用树结构,通过 id/parentId 关联,因此可以在原地分支。
import {
type CreateAgentSessionRuntimeFactory,
createAgentSession,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
SessionManager,
} from "@earendil-works/pi-coding-agent";
// In-memory (no persistence)
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
});
// New persistent session
const { session: persisted } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd()),
});
// Continue most recent
const { session: continued, modelFallbackMessage } = await createAgentSession({
sessionManager: SessionManager.continueRecent(process.cwd()),
});
if (modelFallbackMessage) {
console.log("Note:", modelFallbackMessage);
}
// Open specific file
const { session: opened } = await createAgentSession({
sessionManager: SessionManager.open("/path/to/session.jsonl"),
});
// List sessions
const currentProjectSessions = await SessionManager.list(process.cwd());
const allSessions = await SessionManager.listAll(process.cwd());
// Session replacement API for /new, /resume, /fork, /clone, and import flows.
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({
services,
sessionManager,
sessionStartEvent,
})),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.create(process.cwd()),
});
// Replace the active session with a fresh one
await runtime.newSession();
// Replace the active session with another saved session
await runtime.switchSession("/path/to/session.jsonl");
// Replace the active session with a fork from a specific user entry
await runtime.fork("entry-id");
// Clone the active path through a specific entry
await runtime.fork("entry-id", { position: "at" });
SessionManager 树 API:
const sm = SessionManager.open("/path/to/session.jsonl");
// Session listing
const currentProjectSessions = await SessionManager.list(process.cwd());
const allSessions = await SessionManager.listAll(process.cwd());
// Tree traversal
const entries = sm.getEntries(); // All entries (excludes header)
const tree = sm.getTree(); // Full tree structure
const path = sm.getPath(); // 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
设置管理
import { createAgentSession, SettingsManager, SessionManager } from "@earendil-works/pi-coding-agent";
// Default: loads from files (global + project merged)
const { session } = await createAgentSession({
settingsManager: SettingsManager.create(),
});
// With overrides
const settingsManager = SettingsManager.create();
settingsManager.applyOverrides({
compaction: { enabled: false },
retry: { enabled: true, maxRetries: 5 },
});
const { session } = await createAgentSession({ settingsManager });
// In-memory (no file I/O, for testing)
const { session } = await createAgentSession({
settingsManager: SettingsManager.inMemory({ compaction: { enabled: false } }),
sessionManager: SessionManager.inMemory(),
});
// Custom directories
const { session } = await createAgentSession({
settingsManager: SettingsManager.create("/custom/cwd", "/custom/agent"),
});
静态工厂:
SettingsManager.create(cwd?, agentDir?)- 从文件加载SettingsManager.inMemory(settings?)- 不进行文件 I/O
项目级设置:
Settings 会从两个位置加载并合并:
- 全局:
~/.pi/agent/settings.json - 项目:
<cwd>/.pi/settings.json
项目配置会覆盖全局配置。嵌套对象会合并 key。setter 默认修改全局 Settings。
持久化与错误处理语义:
- 对于内存中的状态,Settings 的 getter/setter 是同步的。
- setter 会异步排队写入持久化。
- 当你需要一个持久化边界时,调用
await settingsManager.flush()(例如,在进程退出前,或在测试中断言文件内容前)。 - SettingsManager 不会打印 Settings I/O 错误。请使用
settingsManager.drainErrors(),并在你的应用层里处理和上报这些错误。
ResourceLoader
使用 DefaultResourceLoader 来发现扩展、Skill、提示词、主题和上下文文件。
import {
DefaultResourceLoader,
getAgentDir,
} from "@earendil-works/pi-coding-agent";
const loader = new DefaultResourceLoader({
cwd,
agentDir: getAgentDir(),
});
await loader.reload();
const extensions = loader.getExtensions();
const skills = loader.getSkills();
const prompts = loader.getPrompts();
const themes = loader.getThemes();
const contextFiles = loader.getAgentsFiles().agentsFiles;
返回值
createAgentSession() 返回:
interface CreateAgentSessionResult {
// The session
session: AgentSession;
// Extensions result (for runner setup)
extensionsResult: LoadExtensionsResult;
// Warning if session model couldn't be restored
modelFallbackMessage?: string;
}
interface LoadExtensionsResult {
extensions: Extension[];
errors: Array<{ path: string; error: string }>;
runtime: ExtensionRuntime;
}
完整示例
import { getModel } from "@earendil-works/pi-ai";
import { Type } from "typebox";
import {
AuthStorage,
createAgentSession,
DefaultResourceLoader,
defineTool,
ModelRegistry,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
// Set up auth storage (custom location)
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 (no custom models.json)
const modelRegistry = ModelRegistry.create(authStorage);
// Inline tool
const statusTool = defineTool({
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: ["read", "bash", "status"],
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.");
运行模式
SDK 导出了一组运行模式工具,用来在 createAgentSession() 之上构建自定义界面:
InteractiveMode
完整的 TUI 交互模式,包含编辑器、聊天历史和所有内置命令:
import {
type CreateAgentSessionRuntimeFactory,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
InteractiveMode,
SessionManager,
} from "@earendil-works/pi-coding-agent";
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.create(process.cwd()),
});
const mode = new InteractiveMode(runtime, {
migratedProviders: [],
modelFallbackMessage: undefined,
initialMessage: "Hello",
initialImages: [],
initialMessages: [],
});
await mode.run();
runPrintMode
单次模式:发送提示词,输出结果,然后退出:
import {
type CreateAgentSessionRuntimeFactory,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
runPrintMode,
SessionManager,
} from "@earendil-works/pi-coding-agent";
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.create(process.cwd()),
});
await runPrintMode(runtime, {
mode: "text",
initialMessage: "Hello",
initialImages: [],
messages: ["Follow up"],
});
runRpcMode
用于子进程集成的 JSON-RPC 模式:
import {
type CreateAgentSessionRuntimeFactory,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
runRpcMode,
SessionManager,
} from "@earendil-works/pi-coding-agent";
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.create(process.cwd()),
});
await runRpcMode(runtime);
JSON 协议请参见 RPC documentation。
RPC 模式替代方案
如果要做基于子进程的集成,但不想通过 SDK 构建,可以直接使用 CLI:
pi --mode rpc --no-session
JSON 协议请参见 RPC documentation。
以下场景更适合使用 SDK:
- 你希望有类型安全
- 你和 agent 运行在同一个 Node.js 进程中
- 你需要直接访问 agent 状态
- 你想通过程序化方式自定义 tools/extensions
以下场景更适合使用 RPC 模式:
- 你要从另一种语言进行集成
- 你希望进程隔离
- 你在构建与语言无关的客户端
导出内容
主入口导出如下内容:
// Factory
createAgentSession
createAgentSessionRuntime
AgentSessionRuntime
// Auth and Models
AuthStorage
ModelRegistry
// Resource loading
DefaultResourceLoader
type ResourceLoader
createEventBus
// Constants and helpers
CONFIG_DIR_NAME
defineTool
getAgentDir
getPackageDir
getReadmePath
getDocsPath
getExamplesPath
// Session management
SessionManager
SettingsManager
// Tool factories
createCodingTools
createReadOnlyTools
createReadTool, createBashTool, createEditTool, createWriteTool
createGrepTool, createFindTool, createLsTool
// Types
type CreateAgentSessionOptions
type CreateAgentSessionResult
type ExtensionFactory
type ExtensionAPI
type ToolDefinition
type Skill
type PromptTemplate
type Tool
关于 extension 类型,请参见 extensions.md 了解完整 API。