跳转到内容

SDK

pi 可以帮助你使用 SDK。让它为你的用例构建集成。

SDK 提供对 pi 智能体能力的编程式访问。可以用它把 pi 嵌入其他应用、构建自定义界面,或与自动化工作流集成。

示例用例:

  • 构建自定义 UI(Web、桌面、移动端)
  • 把智能体能力集成到现有应用
  • 创建带智能体推理的自动化流水线
  • 构建能生成子智能体的自定义工具
  • 用编程方式测试智能体行为

从最小化到完全控制的可用示例,见 examples/sdk/

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?");
Terminal window
npm install @earendil-works/pi-coding-agent

SDK 已包含在主包中,无需单独安装。

单个 AgentSession 的主要工厂函数。

createAgentSession() 使用 ResourceLoader 提供扩展、技能、提示词模板、主题和上下文文件。如果不传入,则使用带标准发现的 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(),
});

会话管理智能体生命周期、消息历史、模型状态、上下文压缩和事件流。

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?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>;
// 上下文压缩
compact(customInstructions?: string): Promise<CompactionResult>;
abortCompaction(): void;
// 中止当前操作
abort(): Promise<void>;
// 清理
dispose(): void;
}

会话替换 API(如 new-session、resume、fork 和 import)位于 AgentSessionRuntime 上,而非 AgentSession 上。

createAgentSessionRuntime() 和 AgentSessionRuntime

Section titled “createAgentSessionRuntime() 和 AgentSessionRuntime”

当需要替换当前会话并重建绑定 cwd 的运行时状态时,使用运行时 API。这与内置的交互式、print 和 RPC 模式使用的层相同。

createAgentSessionRuntime() 接收一个运行时工厂,外加初始的 cwd/会话目标。该工厂闭包捕获进程级的固定输入,为有效 cwd 重建绑定 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()
  • 通过 fork(entryId, { position: "at" }) 克隆流程
  • importFromJsonl()

重要行为:

  • 上述操作之后 runtime.session 会变化
  • 事件订阅绑定到特定的 AgentSession,替换后需重新订阅
  • 如果使用扩展,请对新会话再次调用 runtime.session.bindExtensions(...)
  • 创建时会在 runtime.diagnostics 上返回诊断信息
  • 如果运行时创建或替换失败,方法会抛出异常,由调用方决定如何处理
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});
await runtime.newSession();
unsubscribe();
session = runtime.session;
unsubscribe = session.subscribe(() => {});

PromptOptions 控制提示词模板展开、流式传输期间的排队行为以及提示词预检通知:

interface PromptOptions {
expandPromptTemplates?: boolean;
images?: ImageContent[];
streamingBehavior?: "steer" | "followUp";
source?: InputSource;
preflightResult?: (success: boolean) => void;
}

preflightResult 每次调用 prompt() 时调用一次:

  • true:提示词已被接受、排队或立即处理
  • false:提示词预检在接受前被拒绝

它在 prompt() 解析之前触发。prompt() 仍会在整个被接受的运行完成(包括重试)后才解析。接受后的失败通过常规事件和消息流上报,而不是通过 preflightResult(false)

prompt() 方法处理提示词模板、扩展命令和消息发送:

// 基本提示(未在流式传输时)
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" });

行为:

  • 扩展命令(如 /mycommand):立即执行,即使在流式传输期间也一样。它们通过 pi.sendMessage() 自行管理 LLM 交互。
  • 基于文件的提示词模板(来自 .md 文件):在发送或排队前展开为内容。
  • 流式传输期间未指定 streamingBehavior:抛出错误。请直接使用 steer()followUp(),或指定该选项。
  • preflightResult(true):表示提示词已被接受、排队或立即处理。
  • preflightResult(false):表示预检在接受前被拒绝。

在流式传输期间显式排队:

// 排队一条转向消息,在当前的助手回合完成工具调用后投递
await session.steer("New instruction");
// 等待智能体完成(仅在智能体停止后投递)
await session.followUp("After you're done, also do this");

steer()followUp() 都会展开基于文件的提示词模板,但对扩展命令会报错(扩展命令无法排队)。

Agent 类(来自 @earendil-works/pi-agent-core)处理核心 LLM 交互。通过 session.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; // 复制顶层数组
// 等待智能体处理完成
await session.agent.waitForIdle();

订阅事件以接收流式输出和生命周期通知。

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;
// 智能体生命周期
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;
}
});
const { session } = await createAgentSession({
// DefaultResourceLoader 发现的工作目录
cwd: process.cwd(), // 默认值
// 全局配置目录
agentDir: "~/.pi/agent", // 默认值(展开 ~)
});

cwdDefaultResourceLoader 用于:

  • 项目扩展(.pi/extensions/
  • 项目技能:
    • .pi/skills/
    • cwd 及其祖先目录中的 .agents/skills/(向上直到 git 仓库根目录;不在仓库内时则到文件系统根目录)
  • 项目提示词(.pi/prompts/
  • 上下文文件(从 cwd 向上遍历的 AGENTS.md
  • 会话目录命名

agentDirDefaultResourceLoader 用于:

  • 全局扩展(extensions/
  • 全局技能:
    • agentDir 下的 skills/(例如 ~/.pi/agent/skills/
    • ~/.agents/skills/
  • 全局提示词(prompts/
  • 全局上下文文件(AGENTS.md
  • 设置(settings.json
  • 自定义模型(models.json
  • 凭据(auth.json
  • 会话(sessions/

传入自定义 ResourceLoader 时,cwdagentDir 不再控制资源发现。它们仍会影响会话命名和工具路径解析。

import { getModel } from "@earendil-works/pi-ai";
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create();
// 查找特定的内置模型(不检查 API 密钥是否存在)
const opus = getModel("anthropic", "claude-opus-4-5");
if (!opus) throw new Error("Model not found");
// 按 provider/id 查找任意模型,包括来自 models.json 的自定义模型
//(不检查 API 密钥是否存在)
const customModel = modelRuntime.getModel("my-provider", "my-model");
// 仅获取已配置有效身份验证的模型
const available = await modelRuntime.getAvailable();
const { session } = await createAgentSession({
model: opus,
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh, max
// 用于循环切换的模型(交互模式下为 Ctrl+P)
scopedModels: [
{ model: opus, thinkingLevel: "high" },
{ model: haiku, thinkingLevel: "off" },
],
modelRuntime,
});

如果未提供模型:

  1. 尝试从会话恢复(如果是继续会话)
  2. 使用设置中的默认模型
  3. 回退到第一个可用模型

要与 CLI 的模型解析保持一致,请使用导出的解析辅助函数:

import {
resolveCliModel,
resolveModelScopeWithDiagnostics,
} from "@earendil-works/pi-coding-agent";
const cliModel = resolveCliModel({
cliModel: "anthropic/claude-opus-4-5:high",
modelRuntime,
});
if (cliModel.error) throw new Error(cliModel.error);
if (cliModel.warning) console.warn(cliModel.warning);
const { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics(
["anthropic/*:high", "gpt-5"],
modelRuntime,
);
for (const diagnostic of diagnostics) {
console.warn(diagnostic.message);
}

resolveCliModel() 使用所有已注册模型,因此 --api-key 风格的首次设置可以在已存储认证存在之前解析模型。resolveModelScopeWithDiagnostics() 匹配 --modelsenabledModels 语义,同时返回警告而不是打印它们。

参见 examples/sdk/02-custom-model.ts

身份验证解析优先级(由 ModelRuntime 处理):

  1. 运行时覆盖(通过 setRuntimeApiKey,不持久化)
  2. auth.json 中存储的凭据(API 密钥或 OAuth 令牌)
  3. 环境变量(ANTHROPIC_API_KEYOPENAI_API_KEY 等)
  4. 回退解析器(用于 models.json 中的自定义提供方密钥)
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
// 默认:使用 ~/.pi/agent/auth.json 和 ~/.pi/agent/models.json
const modelRuntime = await ModelRuntime.create();
// 提供方自有的认证方法及当前状态
for (const provider of modelRuntime.getProviders()) {
const status = await modelRuntime.checkAuth(provider.id);
console.log(provider.name, provider.auth, status);
}
// 运行时 API 密钥覆盖(不持久化到磁盘)
await modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
// 自定义凭据和模型位置
const customRuntime = await ModelRuntime.create({
authPath: "/my/app/auth.json",
modelsPath: "/my/app/models.json",
});
// 或注入任意 pi-ai CredentialStore
const credentials = new InMemoryCredentialStore();
const inMemoryRuntime = await ModelRuntime.create({ credentials });
const { session } = await createAgentSession({
modelRuntime: customRuntime,
});

login()logout()setRuntimeApiKey()removeRuntimeApiKey() 会在受影响的提供方的缓存/内置目录、组合和可用性快照在本地保持一致后解析。它们不等待远程目录的新鲜度。如果凭据已提交但本地同步失败,它们会用导出的 CredentialSynchronizationError 拒绝;检查其 providerIdoperationcredentialcause 字段,而不是盲目重试凭据变更。

公开的模型/认证操作和 ModelRuntime.create({ signal }) 接受可选的中止信号,省略时无界。远程目录新鲜度的截止时间策略由 SDK 应用负责:

const signal = AbortSignal.timeout(15_000);
const result = await modelRuntime.refresh({
providers: ["anthropic"],
signal,
});
if (result.aborted) console.warn("Catalog refresh timed out; using cached models");
for (const [providerId, error] of result.errors) {
console.warn(`Could not refresh ${providerId}:`, error);
}

失败或超时的网络刷新不会撤销成功的凭据操作。refresh() 会启动新的一代提供方,因此它不会等待旧的停滞刷新,过时的世代之后也无法发布。

参见 examples/sdk/09-api-keys-and-oauth.ts

使用 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 });

参见 examples/sdk/03-custom-prompt.ts

指定要启用的内置工具:

  • 内置工具名称:readbasheditwritegrepfindls
  • 默认内置:readbasheditwrite
  • noTools: "all" 禁用所有工具
  • noTools: "builtin" 禁用默认内置工具,同时保持扩展和自定义工具启用
  • excludeTools 在应用任何 tools 白名单之后,禁用特定的内置、扩展或自定义工具名称

edit 工具为 Pi 的 TUI 显示返回 details.diff,并为 SDK 使用方返回标准统一补丁格式的 details.patch

import { createAgentSession } from "@earendil-works/pi-coding-agent";
// 只读模式
const { session } = await createAgentSession({
tools: ["read", "grep", "find", "ls"],
});
// 选择特定工具
const { session } = await createAgentSession({
tools: ["read", "bash", "grep"],
});
// 禁用某个工具,同时保持其余工具可用
const { session } = await createAgentSession({
excludeTools: ["ask_question"],
});

传入自定义 cwd 时,createAgentSession() 会为该校验工作目录构建选中的内置工具。

import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
const cwd = "/path/to/project";
// 为自定义 cwd 使用默认工具
const { session } = await createAgentSession({
cwd,
sessionManager: SessionManager.inMemory(cwd),
});
// 或为自定义 cwd 选择特定工具
const { session } = await createAgentSession({
cwd,
tools: ["read", "bash", "grep"],
sessionManager: SessionManager.inMemory(cwd),
});

参见 examples/sdk/05-tools.ts

import { Type } from "typebox";
import { createAgentSession, defineTool } from "@earendil-works/pi-coding-agent";
// 内联自定义工具
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: {},
}),
});
// 直接传入自定义工具
const { session } = await createAgentSession({
customTools: [myTool],
});

使用 defineTool() 定义独立工具及 customTools: [myTool] 之类的数组。内联的 pi.registerTool({ ... }) 已能正确推断参数类型。

通过 customTools 传入的自定义工具会与扩展注册的工具合并。由 ResourceLoader 加载的扩展也可以通过 pi.registerTool() 注册工具。

如果传入 tools,请把每个要启用的自定义或扩展工具名称包含在内,例如 tools: ["read", "bash", "my_tool"]

参见 examples/sdk/05-tools.ts

扩展由 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 见 扩展

命名内联扩展: 默认情况下,内联工厂在启动时的扩展列表中显示为 <inline:1><inline:2> 等。要显示描述性名称,请包装该工厂:

import type { InlineExtension } from "@earendil-works/pi-coding-agent";
const myProvider: InlineExtension = {
name: "my-provider",
factory: (pi) => {
pi.on("agent_start", () => {
console.log("[my-provider] Agent starting");
});
},
};
const loader = new DefaultResourceLoader({
extensionFactories: [myProvider],
});

这会显示为 <inline:my-provider> 而不是 <inline:1>。为向后兼容,仍然接受裸工厂函数。

事件总线: 扩展可以通过 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));

参见 examples/sdk/06-extensions.ts扩展

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 });

参见 examples/sdk/04-skills.ts

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 });

参见 examples/sdk/07-context-files.ts

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 });

参见 examples/sdk/08-prompt-templates.ts

会话使用带 id/parentId 链接的树结构,支持原地分支。

import {
type CreateAgentSessionRuntimeFactory,
createAgentSession,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
SessionManager,
} from "@earendil-works/pi-coding-agent";
// 内存中(无持久化)
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
});
// 新建持久化会话
const { session: persisted } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd()),
});
// 继续最近的会话
const { session: continued, modelFallbackMessage } = await createAgentSession({
sessionManager: SessionManager.continueRecent(process.cwd()),
});
if (modelFallbackMessage) {
console.log("Note:", modelFallbackMessage);
}
// 打开指定文件
const { session: opened } = await createAgentSession({
sessionManager: SessionManager.open("/path/to/session.jsonl"),
});
// 列出会话
const currentProjectSessions = await SessionManager.list(process.cwd());
const allSessions = await SessionManager.listAll(process.cwd());
// 用于 /new、/resume、/fork、/clone 和导入流程的会话替换 API。
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 runtime.newSession();
// 用另一个已保存的会话替换当前会话
await runtime.switchSession("/path/to/session.jsonl");
// 用来自特定用户条目的分支替换当前会话
await runtime.fork("entry-id");
// 克隆经过特定条目的当前路径
await runtime.fork("entry-id", { position: "at" });

SessionManager 树 API:

const sm = SessionManager.open("/path/to/session.jsonl");
// 会话列表
const currentProjectSessions = await SessionManager.list(process.cwd());
const allSessions = await SessionManager.listAll(process.cwd());
// 树遍历
const entries = sm.getEntries(); // 所有条目(不含头部)
const tree = sm.getTree(); // 完整树结构
const path = sm.getPath(); // 从根到当前叶子节点的路径
const leaf = sm.getLeafEntry(); // 当前叶子条目
const entry = sm.getEntry(id); // 按 ID 获取条目
const children = sm.getChildren(id); // 条目的直接子节点
// 标签
const label = sm.getLabel(id); // 获取条目的标签
sm.appendLabelChange(id, "checkpoint"); // 设置标签
// 分支
sm.branch(entryId); // 将叶子节点移动到更早的条目
sm.branchWithSummary(id, "Summary..."); // 带上下文摘要的分支
sm.createBranchedSession(leafId); // 将路径提取到新文件

参见 examples/sdk/11-sessions.ts会话格式

import { createAgentSession, SettingsManager, SessionManager } from "@earendil-works/pi-coding-agent";
// 默认:从文件加载(全局 + 项目合并)
const { session } = await createAgentSession({
settingsManager: SettingsManager.create(),
});
// 带覆盖
const settingsManager = SettingsManager.create();
settingsManager.applyOverrides({
compaction: { enabled: false },
retry: { enabled: true, maxRetries: 5 },
});
const { session } = await createAgentSession({ settingsManager });
// 内存中(无文件 I/O,用于测试)
const { session } = await createAgentSession({
settingsManager: SettingsManager.inMemory({ compaction: { enabled: false } }),
sessionManager: SessionManager.inMemory(),
});
// 自定义目录
const { session } = await createAgentSession({
settingsManager: SettingsManager.create("/custom/cwd", "/custom/agent"),
});

静态工厂:

  • SettingsManager.create(cwd?, agentDir?) - 从文件加载
  • SettingsManager.inMemory(settings?) - 无文件 I/O

项目特定设置:

设置从两个位置加载并合并:

  1. 全局:~/.pi/agent/settings.json
  2. 项目:<cwd>/.pi/settings.json

项目覆盖全局。嵌套对象按键合并。默认情况下,setter 修改全局设置。

持久化与错误处理语义:

  • 设置 getter/setter 对内存状态是同步的。
  • Setter 异步排队持久化写入。
  • 在需要持久性边界时调用 await settingsManager.flush()(例如在进程退出前,或在测试中断言文件内容之前)。
  • SettingsManager 不打印设置 I/O 错误。使用 settingsManager.drainErrors() 并在应用层上报。

参见 examples/sdk/10-settings.ts

使用 DefaultResourceLoader 发现扩展、技能、提示词、主题和上下文文件。

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 {
// 会话
session: AgentSession;
// 扩展结果(用于运行器设置)
extensionsResult: LoadExtensionsResult;
// 如果无法恢复会话模型时的警告
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 {
createAgentSession,
DefaultResourceLoader,
defineTool,
ModelRuntime,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create({
authPath: "/custom/agent/auth.json",
modelsPath: "/custom/agent/models.json",
});
if (process.env.MY_KEY) {
await modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY);
}
// 内联工具
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");
// 带覆盖的内存中设置
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",
modelRuntime,
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() 之上的运行模式工具,用于构建自定义界面:

带编辑器、聊天历史以及所有内置命令的完整 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();

单次模式:发送提示词、输出结果、退出:

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"],
});

用于子进程集成的 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 文档

对于不基于 SDK 构建的子进程集成,请直接使用 CLI:

Terminal window
pi --mode rpc --no-session

JSON 协议见 RPC 文档

以下情况首选 SDK:

  • 需要类型安全
  • 处于同一个 Node.js 进程中
  • 需要直接访问智能体状态
  • 想以编程方式自定义工具/扩展

以下情况首选 RPC 模式:

  • 从其他语言集成
  • 需要进程隔离
  • 构建语言无关的客户端

主入口导出:

// 工厂
createAgentSession
createAgentSessionRuntime
AgentSessionRuntime
// 认证与模型
ModelRuntime // 实现 pi-ai Models 并拥有凭据存储
ModelRegistry // 同步扩展兼容性门面
CredentialSynchronizationError
resolveCliModel
resolveModelScopeWithDiagnostics
// 资源加载
DefaultResourceLoader
type ResourceLoader
createEventBus
// 常量与辅助函数
CONFIG_DIR_NAME
defineTool
getAgentDir
getPackageDir
getReadmePath
getDocsPath
getExamplesPath
// 会话管理
SessionManager
SettingsManager
// 工具工厂
createCodingTools
createReadOnlyTools
createReadTool, createBashTool, createEditTool, createWriteTool
createGrepTool, createFindTool, createLsTool
// 类型
type CreateAgentSessionOptions
type CreateAgentSessionResult
type ExtensionFactory
type InlineExtension
type ExtensionAPI
type ToolDefinition
type Skill
type PromptTemplate
type Tool

扩展类型的完整 API 见 扩展