SDK
pi 可以說明你使用 SDK。可以讓它為你的使用情境建置整合。
SDK 提供對 pi Agent 能力的程式化存取。使用它可以將 pi 嵌入其他應用、建置自訂介面,或接入自動化工作流程。
使用情境範例:
- 建置自訂 UI(Web、桌面、移動)
- 將 Agent 能力整合到現有應用程式中
- 使用 Agent 推理建立自動化流水線
- 建置可產生子 Agent 的自訂工具
- 以程式開發方式測試 Agent 行為
從最小範例到完全控制的可執行範例,見 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?");安裝
npm install @earendil-works/pi-coding-agentSDK 包含在主包中。無需個別安裝。
核心概念
createAgentSession()
用於建立單一 AgentSession 的主要工廠函式。
createAgentSession() 使用 ResourceLoader 提供擴充、Skills、Prompt Templates、主題和 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
工作階段管理 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;
}新工作階段、恢復、分叉和匯入等工作階段替換 API 位於 AgentSessionRuntime 上,而不是 AgentSession 上。
createAgentSessionRuntime() 和 AgentSessionRuntime
當需要替換活動工作階段並重建綁定到 cwd 的執行階段狀態時,請使用執行階段 API。 這與內建互動、列印和 RPC 模式使用的層相同。
createAgentSessionRuntime() 接收執行階段工廠以及初始 cwd/session 目標。工廠會閉包捕獲程序全域的固定輸入,為有效 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" })實作的 clone 流程 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(() => {});Prompt 和訊息佇列
PromptOptions 控制 Prompt 展開、串流傳輸時的排隊行為,以及 Prompt 預檢通知:
interface PromptOptions {
expandPromptTemplates?: boolean;
images?: ImageContent[];
streamingBehavior?: "steer" | "followUp";
source?: InputSource;
preflightResult?: (success: boolean) => void;
}每次 prompt() 呼叫都會呼叫一次 preflightResult:
true表示 Prompt 已被接受、排隊或立即處理false表示 Prompt 在接受之前被預檢拒絕
它會在 prompt() resolve 之前觸發。prompt() 仍然只會在完整的已接受執行結束後(包括重試)resolve。接受之後的失敗會透過正常事件和訊息流報告,而不是透過 preflightResult(false)。
prompt() 方法處理 Prompt Templates、擴充指令和訊息傳送:
// 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" });行為:
- 擴充指令(例如
/mycommand):即使在串流傳輸期間也會立即執行。它們透過pi.sendMessage()管理自己的 LLM 互動。 - 基於檔案的 Prompt Templates(來自
.md檔案):傳送或排隊前展開為其內容。 - 串流傳輸期間未指定
streamingBehavior:拋出錯誤。直接使用steer()或followUp(),或指定該選項。 preflightResult(true):表示提示已被接受、排隊或立即處理。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,但遇到擴充指令會報錯(擴充指令不能排隊)。
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();Events
訂閱事件以接收流輸出和生命週期通知。
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":
case "summarization_retry_scheduled":
case "summarization_retry_attempt_start":
case "summarization_retry_finished":
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/) - 專案技能:
.pi/skills/cwd和祖先目錄中的.agents/skills/(直至 git repo 根目錄,或不在repository 中時的檔案系統根目錄)
- 專案 Prompt(
.pi/prompts/) - context files(從 cwd 向上尋找
AGENTS.md) - 工作階段目錄命名
agentDir 由 DefaultResourceLoader 用於:
- 全域擴充 (
extensions/) - 全域 Skills:
skills/位於agentDir之下(例如~/.pi/agent/skills/)~/.agents/skills/
- 全域 Prompt(
prompts/) - 全域context files (
AGENTS.md) - 設定(
settings.json) - 自訂模型 (
models.json) - 憑證 (
auth.json) - 工作階段 (
sessions/)
傳入自訂 ResourceLoader 後,cwd 和 agentDir 不再控制資源探索。它們仍會影響工作階段命名和工具路徑解析。
模型
import { getModel } from "@earendil-works/pi-ai";
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create();
// 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 = modelRuntime.getModel("my-provider", "my-model");
// Get only models that have valid authentication configured
const available = await modelRuntime.getAvailable();
const { session } = await createAgentSession({
model: opus,
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh, max
// Models for cycling (Ctrl+P in interactive mode)
scopedModels: [
{ model: opus, thinkingLevel: "high" },
{ model: haiku, thinkingLevel: "off" },
],
modelRuntime,
});如果沒有提供模型:
- 嘗試從工作階段中恢復(如果繼續)
- 使用設定中的預設值
- fallback 到第一個可用模型
要比對 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() 比對 --models 和 enabledModels 語義,同時傳回警告而不是列印警告。
API Key 和 OAuth
身分驗證解析優先級(由 ModelRuntime 處理):
- 執行階段覆蓋(透過
setRuntimeApiKey,不持久) - 儲存在
auth.json中的憑證(API Key 或 OAuth token) - 環境變數(
ANTHROPIC_API_KEY、OPENAI_API_KEY等) - 後備解析器(用於來自
models.json的自訂 Provider key)
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
const modelRuntime = await ModelRuntime.create();
// Provider-owned auth methods and current status
for (const provider of modelRuntime.getProviders()) {
const status = await modelRuntime.checkAuth(provider.id);
console.log(provider.name, provider.auth, status);
}
// Runtime API key override (not persisted to disk)
await modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
// Custom credential and model locations
const customRuntime = await ModelRuntime.create({
authPath: "/my/app/auth.json",
modelsPath: "/my/app/models.json",
});
// Or inject any pi-ai CredentialStore
const credentials = new InMemoryCredentialStore();
const inMemoryRuntime = await ModelRuntime.create({ credentials });
const { session } = await createAgentSession({
modelRuntime: customRuntime,
});在受影響的 Provider 的快取/內建目錄、組合和可用狀態快照本機一致後,login()、logout()、setRuntimeApiKey() 和 removeRuntimeApiKey() 即可解決。他們不會等待遠端目錄的新鮮度。如果憑證已commit但本機同步失敗,它們會拒絕匯出的 CredentialSynchronizationError;檢查其 providerId、operation、credential 和 cause 欄位,而不是盲目地重試憑證突變。
公共模型/驗證操作和 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() 啟動新的 Provider 產生,因此它不會等待舊的停滯重新整理,並且過時的產生之後無法發佈。
系統提示
使用 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"停用預設內建程式,同時保持擴充和自訂工具啟用- 應用任何
tools允許清單後,excludeTools停用特定的內建、擴充或自訂工具名稱
edit 工具為 Pi 的 TUI 顯示傳回 details.diff,並為 SDK 消費者傳回 details.patch 作為標準統一patch。
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 載入的 Extensions 也可以透過 pi.registerTool() 註冊工具。
如果你傳遞 tools,請包含你想要啟用的每個自訂或擴充工具名稱,例如 tools: ["read", "bash", "my_tool"]。
Extensions
Extensions 由 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 });Extensions 可以註冊工具、訂閱事件、新增指令等。完整的 API 請參見extensions.md。
命名內聯擴充: 預設情況下,內聯工廠在啟動 Extensions 清單中顯示為 <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>。為了向後相容,裸工廠函式仍然被接受。
事件總線: Extensions 可以透過 pi.events 進行通信。如果你需要從外部發出或監聽,請將共享的 eventBus 傳遞給 DefaultResourceLoader:
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 });context files
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
專案特定設定:
設定從兩個位置載入並合併:
- 全域:
~/.pi/agent/settings.json - 專案:
<cwd>/.pi/settings.json
專案設定會覆蓋全域設定。巢狀物件按鍵合併。setter 預設修改全域設定。
持久性和錯誤處理語義:
- 設定 getter/setter 對於記憶體狀態是同步的。
- Setters 將持久化寫入佇列async寫入。
- 當你需要持久性邊界時(例如,在程序退出之前或在測試中斷言檔案內容之前),請呼叫
await settingsManager.flush()。 SettingsManager不列印設定 I/O 錯誤。使用settingsManager.drainErrors()並在你的應用程式層中報告它們。
資源載入器
使用 DefaultResourceLoader 探索擴充、技能、提示、主題和 context files。
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 {
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);
}
// 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",
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();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);請參閱RPC documentation瞭解 JSON 協議。
RPC 模式替代方案
對於不使用 SDK 建置的基於子流程的整合,請直接使用 CLI:
pi --mode rpc --no-session請參閱RPC documentation瞭解 JSON 協議。
在以下情況下,偏好 SDK:
- 你想要類型安全
- 你們處於同一個 Node.js 流程中
- 需要直接存取 Agent 狀態
- 需要以程式開發方式自訂工具或擴充
在以下情況下,偏好 RPC 模式:
- 需要從另一種語言整合
- 需要程序隔離
- 正在建置與語言無關的客戶端
匯出項
主要入口點匯出:
// Factory
createAgentSession
createAgentSessionRuntime
AgentSessionRuntime
// Auth and Models
ModelRuntime // implements pi-ai Models and owns credential storage
ModelRegistry // synchronous extension compatibility facade
CredentialSynchronizationError
resolveCliModel
resolveModelScopeWithDiagnostics
// 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 InlineExtension
type ExtensionAPI
type ToolDefinition
type Skill
type PromptTemplate
type Tool對於擴充類型,請參閱 extensions.md 瞭解完整的 API。