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-agent

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

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