Saltearse al contenido

SDK

pi puede ayudarte a usar el SDK. Pídele que construya una integración para tu caso de uso.

El SDK proporciona acceso programático a las capacidades del agente de pi. Úsalo para integrar pi en otras aplicaciones, crear interfaces personalizadas o integrarlo en flujos de trabajo automatizados.

Casos de uso de ejemplo:

  • Construir una UI personalizada (web, escritorio, móvil)
  • Integrar capacidades del agente en aplicaciones existentes
  • Crear pipelines automatizados con razonamiento del agente
  • Construir herramientas personalizadas que generen subagentes
  • Probar el comportamiento del agente de forma programática

Consulta examples/sdk/ para ejemplos funcionales, desde configuraciones mínimas hasta control total.

import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@earendil-works/pi-coding-agent";
// Set up credential storage and model registry
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
});
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?");
Ventana de terminal
npm install @earendil-works/pi-coding-agent

El SDK está incluido en el paquete principal. No se necesita instalación por separado.

La función de fábrica principal para una única AgentSession.

createAgentSession() usa un ResourceLoader para proporcionar extensiones, skills, plantillas de prompt, temas y archivos de contexto. Si no proporcionas uno, usa DefaultResourceLoader con descubrimiento estándar.

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

La sesión gestiona el ciclo de vida del agente, el historial de mensajes, el estado del modelo, la compactación y la transmisión de eventos.

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

Las API de reemplazo de sesión como new-session, resume, fork e import están en AgentSessionRuntime, no en AgentSession.

createAgentSessionRuntime() y AgentSessionRuntime

Sección titulada «createAgentSessionRuntime() y AgentSessionRuntime»

Usa la API de runtime cuando necesites reemplazar la sesión activa y reconstruir el estado de runtime vinculado al cwd. Es la misma capa que usan los modos interactivo, print y RPC integrados.

createAgentSessionRuntime() recibe una fábrica de runtime más el cwd/sesión inicial. La fábrica cierra sobre entradas fijas globales del proceso, recrea servicios vinculados al cwd para el cwd efectivo, resuelve opciones de sesión contra esos servicios y devuelve un resultado de runtime completo.

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 gestiona el reemplazo del runtime activo en:

  • newSession()
  • switchSession()
  • fork()
  • flujos de clonación mediante fork(entryId, { position: "at" })
  • importFromJsonl()

Comportamiento importante:

  • runtime.session cambia después de esas operaciones
  • las suscripciones a eventos están adjuntas a una AgentSession específica; vuelve a suscribirte tras el reemplazo
  • si usas extensiones, llama de nuevo a runtime.session.bindExtensions(...) para la nueva sesión
  • la creación devuelve diagnósticos en runtime.diagnostics
  • si la creación o el reemplazo del runtime falla, el método lanza una excepción y el llamador decide cómo manejarla
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});
await runtime.newSession();
unsubscribe();
session = runtime.session;
unsubscribe = session.subscribe(() => {});

PromptOptions controla la expansión de prompts, el comportamiento de cola durante el streaming y las notificaciones de preflight del prompt:

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

preflightResult se llama una vez por invocación de prompt():

  • true cuando el prompt fue aceptado, encolado o manejado de inmediato
  • false cuando el preflight del prompt rechazó antes de la aceptación

Se dispara antes de que prompt() se resuelva. prompt() solo se resuelve después de que termine la ejecución aceptada completa, incluidos los reintentos. Los fallos posteriores a la aceptación se reportan mediante el flujo normal de eventos y mensajes, no mediante preflightResult(false).

El método prompt() gestiona plantillas de prompt, comandos de extensión y envío de mensajes:

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

Comportamiento:

  • Comandos de extensión (p. ej., /mycommand): se ejecutan de inmediato, incluso durante el streaming. Gestionan su propia interacción con el LLM mediante pi.sendMessage().
  • Plantillas de prompt basadas en archivos (desde archivos .md): se expanden a su contenido antes de enviarse o encolarse.
  • Durante el streaming sin streamingBehavior: lanza un error. Usa steer() o followUp() directamente, o especifica la opción.
  • preflightResult(true): significa que el prompt fue aceptado, encolado o manejado de inmediato.
  • preflightResult(false): significa que el preflight rechazó antes de la aceptación.

Para encolar explícitamente durante el streaming:

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

Tanto steer() como followUp() expanden plantillas de prompt basadas en archivos, pero fallan con comandos de extensión (los comandos de extensión no se pueden encolar).

La clase Agent (de @earendil-works/pi-agent-core) gestiona la interacción principal con el LLM. Accede mediante 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();

Suscríbete a eventos para recibir salida en streaming y notificaciones del ciclo de vida.

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":
break;
}
});
const { session } = await createAgentSession({
// Working directory for DefaultResourceLoader discovery
cwd: process.cwd(), // default
// Global config directory
agentDir: "~/.pi/agent", // default (expands ~)
});

cwd lo usa DefaultResourceLoader para:

  • Extensiones del proyecto (.pi/extensions/)
  • Skills del proyecto:
    • .pi/skills/
    • .agents/skills/ en cwd y directorios ancestros (hasta la raíz del repositorio git, o la raíz del sistema de archivos si no hay repo)
  • Prompts del proyecto (.pi/prompts/)
  • Archivos de contexto (AGENTS.md recorriendo hacia arriba desde cwd)
  • Nomenclatura del directorio de sesiones

agentDir lo usa DefaultResourceLoader para:

  • Extensiones globales (extensions/)
  • Skills globales:
    • skills/ bajo agentDir (por ejemplo ~/.pi/agent/skills/)
    • ~/.agents/skills/
  • Prompts globales (prompts/)
  • Archivo de contexto global (AGENTS.md)
  • Configuración (settings.json)
  • Modelos personalizados (models.json)
  • Credenciales (auth.json)
  • Sesiones (sessions/)

Cuando pasas un ResourceLoader personalizado, cwd y agentDir ya no controlan el descubrimiento de recursos. Siguen influyendo en el nombre de sesión y la resolución de rutas de herramientas.

import { getModel } from "@earendil-works/pi-ai";
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
// 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 = modelRegistry.find("my-provider", "my-model");
// Get only models that have valid API keys configured
const available = await modelRegistry.getAvailable();
const { session } = await createAgentSession({
model: opus,
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh
// Models for cycling (Ctrl+P in interactive mode)
scopedModels: [
{ model: opus, thinkingLevel: "high" },
{ model: haiku, thinkingLevel: "off" },
],
authStorage,
modelRegistry,
});

Si no se proporciona modelo:

  1. Intenta restaurar desde la sesión (si continúas)
  2. Usa el valor predeterminado de la configuración
  3. Recurre al primer modelo disponible

Consulta examples/sdk/02-custom-model.ts

Prioridad de resolución de claves API (gestionada por AuthStorage):

  1. Sobrescrituras en runtime (mediante setRuntimeApiKey, no persistidas)
  2. Credenciales almacenadas en auth.json (claves API o tokens OAuth)
  3. Variables de entorno (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
  4. Resolvedor de respaldo (para claves de proveedores personalizados de models.json)
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
});
// Runtime API key override (not persisted to disk)
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
// Custom auth storage location
const customAuth = AuthStorage.create("/my/app/auth.json");
const customRegistry = ModelRegistry.create(customAuth, "/my/app/models.json");
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage: customAuth,
modelRegistry: customRegistry,
});
// No custom models.json (built-in models only)
const simpleRegistry = ModelRegistry.inMemory(authStorage);

Consulta examples/sdk/09-api-keys-and-oauth.ts

Usa un ResourceLoader para sobrescribir el prompt del sistema:

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

Consulta examples/sdk/03-custom-prompt.ts

Especifica qué herramientas integradas habilitar:

  • Nombres de herramientas integradas: read, bash, edit, write, grep, find, ls
  • Integradas predeterminadas: read, bash, edit, write
  • noTools: "all" deshabilita todas las herramientas
  • noTools: "builtin" deshabilita las integradas predeterminadas manteniendo habilitadas las de extensión y personalizadas
  • excludeTools deshabilita nombres específicos de herramientas integradas, de extensión o personalizadas después de aplicar cualquier lista permitida de tools

La herramienta edit devuelve details.diff para la visualización TUI de Pi y details.patch como parche unificado estándar para consumidores del 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"],
});

Cuando pasas un cwd personalizado, createAgentSession() construye las herramientas integradas seleccionadas para ese 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),
});

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

Usa defineTool() para definiciones independientes y arrays como customTools: [myTool]. El pi.registerTool({ ... }) en línea ya infiere correctamente los tipos de parámetros.

Las herramientas personalizadas pasadas mediante customTools se combinan con las registradas por extensiones. Las extensiones cargadas por el ResourceLoader también pueden registrar herramientas mediante pi.registerTool().

Si pasas tools, incluye cada nombre de herramienta personalizada o de extensión que quieras habilitar, por ejemplo tools: ["read", "bash", "my_tool"].

Consulta examples/sdk/05-tools.ts

Las extensiones las carga el ResourceLoader. DefaultResourceLoader descubre extensiones desde ~/.pi/agent/extensions/, .pi/extensions/ y fuentes de extensión en 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 });

Las extensiones pueden registrar herramientas, suscribirse a eventos, añadir comandos y más. Consulta extensions.md para la API completa.

Bus de eventos: las extensiones pueden comunicarse mediante pi.events. Pasa un eventBus compartido a DefaultResourceLoader si necesitas emitir o escuchar desde fuera:

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

Consulta examples/sdk/06-extensions.ts y docs/extensions.md

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

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

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

Consulta examples/sdk/08-prompt-templates.ts

Las sesiones usan una estructura de árbol con enlaces id/parentId, lo que permite ramificación in situ.

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

API de árbol de SessionManager:

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

Consulta examples/sdk/11-sessions.ts y Formato de sesión

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

Fábricas estáticas:

  • SettingsManager.create(cwd?, agentDir?) - Cargar desde archivos
  • SettingsManager.inMemory(settings?) - Sin E/S de archivos

Configuración específica del proyecto:

La configuración se carga desde dos ubicaciones y se fusiona:

  1. Global: ~/.pi/agent/settings.json
  2. Proyecto: <cwd>/.pi/settings.json

El proyecto sobrescribe lo global. Los objetos anidados fusionan claves. Los setters modifican la configuración global por defecto.

Semántica de persistencia y manejo de errores:

  • Los getters/setters de configuración son síncronos para el estado en memoria.
  • Los setters encolan escrituras de persistencia de forma asíncrona.
  • Llama a await settingsManager.flush() cuando necesites un límite de durabilidad (por ejemplo, antes de salir del proceso o antes de comprobar contenidos de archivos en pruebas).
  • SettingsManager no imprime errores de E/S de configuración. Usa settingsManager.drainErrors() y repórtalos en tu capa de aplicación.

Consulta examples/sdk/10-settings.ts

Usa DefaultResourceLoader para descubrir extensiones, skills, prompts, temas y archivos de contexto.

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() devuelve:

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 {
AuthStorage,
createAgentSession,
DefaultResourceLoader,
defineTool,
ModelRegistry,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
// Set up auth storage (custom location)
const authStorage = AuthStorage.create("/custom/agent/auth.json");
// Runtime API key override (not persisted)
if (process.env.MY_KEY) {
authStorage.setRuntimeApiKey("anthropic", process.env.MY_KEY);
}
// Model registry (no custom models.json)
const modelRegistry = ModelRegistry.create(authStorage);
// 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",
authStorage,
modelRegistry,
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.");

El SDK exporta utilidades de modo de ejecución para construir interfaces personalizadas sobre createAgentSession():

Modo interactivo TUI completo con editor, historial de chat y todos los comandos integrados:

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

Modo de una sola ejecución: enviar prompts, mostrar resultado y salir:

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

Modo JSON-RPC para integración con subprocesos:

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

Consulta la documentación RPC para el protocolo JSON.

Para integración basada en subprocesos sin compilar con el SDK, usa el CLI directamente:

Ventana de terminal
pi --mode rpc --no-session

Consulta la documentación RPC para el protocolo JSON.

Se prefiere el SDK cuando:

  • Quieres seguridad de tipos
  • Estás en el mismo proceso de Node.js
  • Necesitas acceso directo al estado del agente
  • Quieres personalizar herramientas/extensiones de forma programática

Se prefiere el modo RPC cuando:

  • Integras desde otro lenguaje
  • Quieres aislamiento de procesos
  • Construyes un cliente independiente del lenguaje

El punto de entrada principal exporta:

// Factory
createAgentSession
createAgentSessionRuntime
AgentSessionRuntime
// Auth and Models
AuthStorage
ModelRegistry
// 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 ExtensionAPI
type ToolDefinition
type Skill
type PromptTemplate
type Tool

Para tipos de extensión, consulta extensions.md para la API completa.