20 SDK
20.1 SDK 概述
SDK 提供对 Pi Agent 能力的编程式访问。通过 SDK 可以将 Pi 嵌入其他应用、构建自定义界面、集成到自动化工作流中。
典型应用场景:
- 构建 Web/桌面/移动端自定义 UI
- 将 Agent 能力集成到现有应用
- 创建带 Agent 推理的自动化流水线
- 构建能生成子 Agent 的自定义工具
- 以编程方式测试 Agent 行为
20.2 快速开始
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create();
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
modelRuntime,
});
// 订阅事件
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?");20.3 安装
npm install @earendil-works/pi-coding-agentSDK 包含在主包中,无需单独安装。
Tip
完整示例参见 examples/sdk/,从最小化到完全控制的示例都有。
20.4 核心概念
20.4.1 createAgentSession()
创建 AgentSession 的主工厂函数。使用 ResourceLoader 来提供扩展、Skills、Prompt 模板、主题和上下文文件。如果不提供,使用 DefaultResourceLoader 进行标准发现。
import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
// 最小化:使用默认 DefaultResourceLoader
const { session } = await createAgentSession();
// 自定义:覆盖特定选项
const { session } = await createAgentSession({
model: myModel,
tools: ["read", "bash"],
sessionManager: SessionManager.inMemory(),
});20.4.2 AgentSession
会话管理 Agent 生命周期、消息历史、模型状态、压缩和事件流:
interface AgentSession {
// 发送提示并等待完成
prompt(text: string, options?: PromptOptions): Promise<void>;
// 流式传输期间排队消息
steer(text: string): Promise<void>;
followUp(text: string): Promise<void>;
// 订阅事件(返回取消订阅函数)
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
// 会话信息
sessionFile: string | undefined;
sessionId: string;
// 模型控制
setModel(model: Model): Promise<void>;
setThinkingLevel(level: ThinkingLevel): void;
cycleModel(): Promise<ModelCycleResult | undefined>;
cycleThinkingLevel(): ThinkingLevel | undefined;
// 状态访问
agent: Agent;
model: Model | undefined;
thinkingLevel: ThinkingLevel;
messages: AgentMessage[];
isStreaming: boolean;
// 树状导航
navigateTree(targetId: string, options?): Promise<{ editorText?: string; cancelled: boolean }>;
// 压缩
compact(customInstructions?: string): Promise<CompactionResult>;
abortCompaction(): void;
// 中止
abort(): Promise<void>;
// 清理
dispose(): void;
}20.4.3 createAgentSessionRuntime() 和 AgentSessionRuntime
当需要替换活跃会话并重建 cwd 绑定的运行时状态时使用:
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 管理以下会话替换操作:
newSession()— 新建会话switchSession()— 切换会话fork()— 分支会话importFromJsonl()— 导入会话
Warning
会话替换后 runtime.session 会改变。事件订阅绑定到特定 AgentSession,替换后需要重新订阅。如果使用扩展,需要重新调用 runtime.session.bindExtensions(...)。
20.5 提示和消息队列
20.5.1 PromptOptions
interface PromptOptions {
expandPromptTemplates?: boolean;
images?: ImageContent[];
streamingBehavior?: "steer" | "followUp";
source?: InputSource;
preflightResult?: (success: boolean) => void;
}20.5.2 使用方式
// 基本提示(非流式传输时)
await session.prompt("What files are here?");
// 带图片
await session.prompt("What's in this image?", {
images: [{ type: "image", source: { type: "base64", mediaType: "image/png", data: "..." } }]
});
// 流式传输期间:必须指定排队方式
await session.prompt("Stop and do this instead", { streamingBehavior: "steer" });
await session.prompt("After you're done, also check X", { streamingBehavior: "followUp" });20.5.3 行为规则
- 扩展命令(如
/mycommand):立即执行,即使在流式传输期间 - 文件 Prompt 模板:在发送/排队之前展开
- 流式传输中无
streamingBehavior:抛出错误 preflightResult(true):提示被接受/排队/立即处理preflightResult(false):preflight 拒绝
20.5.4 显式排队
// 转向消息:当前助手回合完成工具调用后投递
await session.steer("New instruction");
// 跟进消息:Agent 停止后才投递
await session.followUp("After you're done, also do this");20.6 Agent 和 AgentState
通过 session.agent 访问核心 Agent:
// 当前状态
const state = session.agent.state;
// state.messages: AgentMessage[] - 对话历史
// state.model: Model - 当前模型
// state.thinkingLevel: ThinkingLevel - 当前思考级别
// state.systemPrompt: string - 系统提示
// state.tools: AgentTool[] - 可用工具
// state.streamingMessage?: AgentMessage - 当前部分助手消息
// state.errorMessage?: string - 最新助手错误
// 替换消息(用于分支或恢复)
session.agent.state.messages = messages;
// 替换工具
session.agent.state.tools = tools;
// 等待 Agent 完成处理
await session.agent.waitForIdle();20.7 事件系统
完整的事件类型列表:
session.subscribe((event) => {
switch (event.type) {
// 流式文本
case "message_update":
if (event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
if (event.assistantMessageEvent.type === "thinking_delta") {
// 思考输出
}
break;
// 工具执行
case "tool_execution_start":
console.log(`Tool: ${event.toolName}`);
break;
case "tool_execution_update":
// 流式工具输出
break;
case "tool_execution_end":
console.log(`Result: ${event.isError ? "error" : "success"}`);
break;
// 消息生命周期
case "message_start":
break;
case "message_end":
break;
// Agent 生命周期
case "agent_start":
break;
case "agent_end":
// event.messages 包含新消息
break;
// 回合生命周期(一次 LLM 响应 + 工具调用)
case "turn_start":
break;
case "turn_end":
// event.message: 助手响应
// event.toolResults: 本回合的工具结果
break;
// 队列/压缩/重试
case "queue_update":
console.log(event.steering, event.followUp);
break;
case "compaction_start":
case "compaction_end":
case "auto_retry_start":
case "auto_retry_end":
case "summarization_retry_scheduled":
case "summarization_retry_attempt_start":
case "summarization_retry_finished":
break;
}
});20.8 Options Reference
20.8.1 Directories
const { session } = await createAgentSession({
cwd: process.cwd(), // 工作目录
agentDir: "~/.pi/agent", // 全局配置目录
});cwd 控制项目级扩展、Skills、Prompts、上下文文件和会话目录。agentDir 控制全局资源发现。
20.8.2 Model
import { getModel } from "@earendil-works/pi-ai";
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create();
// 查找特定内置模型
const opus = getModel("anthropic", "claude-opus-4-5");
// 查找任何模型(包括自定义)
const customModel = modelRuntime.getModel("my-provider", "my-model");
// 获取已配置认证的模型
const available = await modelRuntime.getAvailable();
const { session } = await createAgentSession({
model: opus,
thinkingLevel: "medium",
scopedModels: [
{ model: opus, thinkingLevel: "high" },
{ model: haiku, thinkingLevel: "off" },
],
modelRuntime,
});20.8.3 API Keys 和 OAuth
const { session } = await createAgentSession({
apiKeys: {
anthropic: "sk-ant-...",
openai: "sk-...",
},
});20.8.4 System Prompt
const { session } = await createAgentSession({
systemPrompt: "You are a helpful coding assistant.",
systemPromptOptions: {
appendInstructions: "Extra context here",
},
});20.8.5 Tools
const { session } = await createAgentSession({
tools: ["read", "write", "bash"], // 仅启用指定工具
// 或
tools: ["-bash"], // 启用所有工具,除了 bash
});20.8.6 Custom Tools
const { session } = await createAgentSession({
customTools: [
{
name: "my_tool",
description: "Does something",
parameters: Type.Object({ input: Type.String() }),
async execute(toolCallId, params, signal, onUpdate, ctx) {
return { content: [{ type: "text", text: "result" }], details: {} };
},
},
],
});20.8.7 Extensions
const { session } = await createAgentSession({
extensions: [
"./extensions/my-extension.ts",
"./extensions/another.ts",
],
});20.8.8 Skills
const { session } = await createAgentSession({
skills: [
"~/.pi/agent/skills",
"./project-skills",
],
});20.8.9 Context Files
const { session } = await createAgentSession({
contextFiles: ["AGENTS.md", ".pi/context.md"],
});20.8.10 Slash Commands
const { session } = await createAgentSession({
enableSlashCommands: true,
});20.8.11 Session Management
import { SessionManager } from "@earendil-works/pi-coding-agent";
// 内存会话(不持久化)
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
});
// 文件系统会话
const { session } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd()),
});
// 继续已有会话
const { session } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd()),
sessionFile: "existing-session.jsonl",
});20.8.12 Settings Management
const { session } = await createAgentSession({
settings: {
autoCompact: true,
thinkingLevel: "high",
},
});20.8.13 ResourceLoader
自定义资源加载器:
import { type ResourceLoader } from "@earendil-works/pi-coding-agent";
class MyResourceLoader implements ResourceLoader {
async loadExtensions() { /* ... */ }
async loadSkills() { /* ... */ }
async loadPrompts() { /* ... */ }
async loadThemes() { /* ... */ }
async loadContextFiles() { /* ... */ }
}
const { session } = await createAgentSession({
resourceLoader: new MyResourceLoader(),
});20.9 完整示例
import {
createAgentSession,
ModelRuntime,
SessionManager,
} from "@earendil-works/pi-coding-agent";
import { getModel } from "@earendil-works/pi-ai";
import { Type } from "typebox";
async function main() {
const modelRuntime = await ModelRuntime.create();
const model = getModel("anthropic", "claude-sonnet-4-5");
if (!model) throw new Error("Model not found");
const { session } = await createAgentSession({
model,
thinkingLevel: "medium",
modelRuntime,
sessionManager: SessionManager.inMemory(),
tools: ["read", "write", "bash"],
customTools: [
{
name: "calc",
description: "Perform calculations",
parameters: Type.Object({
expression: Type.String(),
}),
async execute(_id, params) {
try {
const result = eval(params.expression);
return {
content: [{ type: "text", text: String(result) }],
details: {},
};
} catch (e) {
return {
content: [{ type: "text", text: `Error: ${e.message}` }],
details: {},
isError: true,
};
}
},
},
],
systemPrompt: "You are a helpful coding assistant with calculation capabilities.",
});
// 订阅事件
session.subscribe((event) => {
switch (event.type) {
case "message_update":
if (event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
break;
case "tool_execution_start":
console.log(`\n[Tool: ${event.toolName}]`);
break;
case "tool_execution_end":
console.log(`[Result: ${event.isError ? "error" : "success"}]`);
break;
case "agent_end":
console.log("\n--- Done ---");
break;
}
});
// 交互循环
await session.prompt("Calculate 123 * 456 and explain the result");
// 清理
session.dispose();
}
main().catch(console.error);20.10 运行模式
20.10.1 InteractiveMode
完整的交互式 TUI 界面:
import { InteractiveMode } from "@earendil-works/pi-coding-agent";
const mode = new InteractiveMode();
await mode.run();20.10.2 runPrintMode
单次提示,输出到 stdout:
import { runPrintMode } from "@earendil-works/pi-coding-agent";
await runPrintMode({
prompt: "List all TypeScript files",
model: "anthropic/claude-sonnet-4-5",
});20.10.3 runRpcMode
RPC 模式,通过 stdin/stdout 的 JSON 协议:
import { runRpcMode } from "@earendil-works/pi-coding-agent";
await runRpcMode({
// 可选配置
});20.11 导出
SDK 的主要导出:
// 核心
export { createAgentSession, type AgentSession };
export { createAgentSessionRuntime, type AgentSessionRuntime };
export { SessionManager };
export { ModelRuntime };
export { type ResourceLoader, DefaultResourceLoader };
export { type AgentSessionEvent };
export { type PromptOptions };
// 运行模式
export { InteractiveMode };
export { runPrintMode };
export { runRpcMode };
// 工具
export { resolveCliModel };
export { resolveModelScopeWithDiagnostics };
// 工具函数
export { getAgentDir };
export { createAgentSessionServices };
export { createAgentSessionFromServices };