Настройка, расширение, параметры платформы и справочник API для Pi.

SDK

pi может помочь вам использовать SDK. Попросите его построить интеграцию для вашего варианта использования.

SDK обеспечивает программный доступ к возможностям агента pi. Используйте его для встраивания pi в другие приложения, создания пользовательских интерфейсов или интеграции с автоматизированными рабочими процессами.

Примеры использования:

  • Создайте собственный пользовательский интерфейс (веб, настольный компьютер, мобильный телефон)
  • Интегрируйте возможности агента в существующие приложения
  • Создавайте автоматизированные конвейеры с аргументацией агента
  • Создавайте собственные инструменты, которые создают субагенты.
  • Программное тестирование поведения агента

См. 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-agent

SDK включен в основной пакет. Никакой отдельной установки не требуется.

Основные понятия

создатьАгентСессион()

Основная заводская функция для одного AgentSession.

createAgentSession() использует ResourceLoader для предоставления расширений, навыков, 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(),
});

Агентсессия

Сеанс управляет жизненным циклом агента, историей сообщений, состоянием модели, сжатием и потоковой передачей событий.

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

Используйте среду выполнения API, когда вам нужно заменить активный сеанс и перестроить состояние среды выполнения, связанное с cwd. Это тот же слой, который используется во встроенных интерактивных режимах, режимах печати и 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" })
  • 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() обрабатывает 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): выполняются немедленно, даже во время потоковой передачи. Они управляют своим собственным взаимодействием с LLM через pi.sendMessage().
  • На основе файлов 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 (из @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":
    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/
    • .agents/skills/ в cwd и каталогах предков (до корня репозитория git или корня файловой системы, если он не находится в репозитории)
  • Подсказки проекта (.pi/prompts/)
  • Контекстные файлы (AGENTS.md при переходе от cwd)
  • Именование каталога сеанса

agentDir используется DefaultResourceLoader для:

  • Глобальные расширения (extensions/)
  • Глобальные навыки:
    • skills/ под agentDir (например, ~/.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 { 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,
});

Если модель не указана:

  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() соответствует семантике --models и enabledModels, возвращая предупреждения вместо их печати.

См. examples/sdk/02-custom-model.ts

API Клавиши и OAuth

Приоритет разрешения аутентификации (обрабатывается ModelRuntime):

  1. Переопределения во время выполнения (через setRuntimeApiKey, не сохраняются)
  2. Хранимые учетные данные в auth.json (API keys или OAuth токенах)
  3. Переменные среды (ANTHROPIC_API_KEY, OPENAI_API_KEY и т. д.)
  4. Резервный преобразователь (для пользовательских ключей поставщика от models.json)
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,
});

login(), logout(), setRuntimeApiKey() и removeRuntimeApiKey() разрешаются после того, как кэшированный/встроенный каталог, состав и снимок доступности затронутого поставщика становятся локально согласованными. Они не ждут свежести удаленного каталога. Если учетные данные были зафиксированы, но локальная синхронизация не удалась, они отклоняются вместе с экспортированным 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() запускает новое поколение поставщика, поэтому оно не ожидает более старого остановленного обновления, а устаревшие поколения не могут публиковаться позже.

См. 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

Инструменты

Укажите, какие встроенные инструменты включить:

  • Имена встроенных инструментов: read, bash, edit, write, grep, find, ls
  • Встроенные модули по умолчанию: read, bash, edit, write.
  • noTools: "all" отключает все инструменты
  • noTools: "builtin" отключает встроенные модули по умолчанию, сохраняя при этом расширение и пользовательские инструменты.
  • excludeTools отключает определенные имена встроенных, расширенных или пользовательских инструментов после применения любого белого списка tools.

Инструмент edit возвращает details.diff для дисплея TUI Pi и details.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),
});

См. examples/sdk/05-tools.ts

Пользовательские инструменты

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, объединяются с инструментами, зарегистрированными в расширении. Extensions, загруженный ResourceLoader, также может регистрировать инструменты через pi.registerTool().

Если вы передадите tools, укажите имя каждого пользовательского инструмента или инструмента расширения, который вы хотите включить, например tools: ["read", "bash", "my_tool"].

См. examples/sdk/05-tools.ts

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.

Именованные встроенные расширения. По умолчанию встроенные фабрики отображаются как <inline:1>, <inline:2> и т. д. в списке запуска Extensions. Чтобы вместо этого отображать описательное имя, оберните фабрику:

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

См. examples/sdk/06-extensions.ts и docs/extensions.md.

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

См. 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";

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

См. examples/sdk/11-sessions.ts и Session Format.

Управление настройками

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?) — Нет файлового ввода-вывода

Настройки для конкретного проекта:

Настройки загружаются из двух мест и объединяются:

  1. Глобально: ~/.pi/agent/settings.json
  2. Проект: <cwd>/.pi/settings.json

Проект переопределяет глобальный. Вложенные объекты объединяют ключи. Сеттеры по умолчанию изменяют глобальные настройки.

Семантика постоянства и обработки ошибок:

  • Геттеры/сеттеры настроек синхронны для состояния в памяти.
  • Сеттеры ставят в очередь постоянную запись асинхронно.
  • Вызовите await settingsManager.flush(), когда вам нужна граница устойчивости (например, перед завершением процесса или перед утверждением содержимого файла в тестах).
  • SettingsManager не выводит ошибки ввода-вывода настроек. Используйте settingsManager.drainErrors() и сообщите о них на уровне вашего приложения.

См. examples/sdk/10-settings.ts

Ресурслоадер

Используйте 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();

запуститьPrintMode

Однократный режим: отправка подсказок, вывод результата, выход:

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

запуститьRpcMode

Режим 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
  • Вам нужен прямой доступ к состоянию агента
  • Вы хотите программно настроить инструменты/расширения.

Режим 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.