SDK
pi pode ajudá-lo a usar o SDK. Peça para criar uma integração para o seu caso de uso.
O SDK fornece acesso programático aos recursos do agente pi. Use-o para incorporar pi em outros aplicativos, criar interfaces personalizadas ou integrar com fluxos de trabalho automatizados.
Exemplos de casos de uso:
- Crie uma UI personalizada (web, desktop, celular)
- Integre recursos de agente em aplicativos existentes
- Crie pipelines automatizados com raciocínio do agente
- Crie ferramentas personalizadas que geram subagentes
- Testar o comportamento do agente programaticamente
Veja examples/sdk/ para exemplos de trabalho desde controle mínimo até controle total.
Início rápido
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?");Instalação
npm install @earendil-works/pi-coding-agentO SDK está incluído no pacote principal. Não é necessária instalação separada.
Conceitos Básicos
createAgentSession()
A principal função de fábrica para um único AgentSession.
createAgentSession() usa ResourceLoader para fornecer extensões, habilidades, prompt templates, temas e context files. Se você não fornecer um, ele usará DefaultResourceLoader com descoberta padrão.
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(),
});Sessão do Agente
A sessão gerencia o ciclo de vida do agente, o histórico de mensagens, o estado do modelo, a compactação e o streaming 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;
}Substituição de sessão APIs, como nova sessão, currículo, bifurcação e importação ao vivo em AgentSessionRuntime, não em AgentSession.
createAgentSessionRuntime() e AgentSessionRuntime
Use o tempo de execução API quando precisar substituir a sessão ativa e reconstruir o estado do tempo de execução vinculado ao cwd. Esta é a mesma camada usada pelos modos interativo, de impressão e RPC integrados.
createAgentSessionRuntime() leva uma fábrica de tempo de execução mais o destino inicial do cwd/sessão. A fábrica fecha as entradas fixas globais do processo, recria os serviços vinculados ao cwd para o cwd efetivo, resolve as opções de sessão nesses serviços e retorna um resultado de tempo de execução 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 possui substituição do tempo de execução ativo em:
newSession()switchSession()fork()- clonar fluxos via
fork(entryId, { position: "at" }) importFromJsonl()
Comportamento importante:
runtime.sessionalterações após essas operações- assinaturas de eventos são anexadas a um
AgentSessionespecífico, então inscreva-se novamente após a substituição - se você usa extensões, ligue
runtime.session.bindExtensions(...)novamente para a nova sessão - criação retorna diagnóstico em
runtime.diagnostics - se a criação ou substituição do tempo de execução falhar, o método será lançado e o chamador decidirá como lidar com isso
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});
await runtime.newSession();
unsubscribe();
session = runtime.session;
unsubscribe = session.subscribe(() => {});Solicitação e enfileiramento de mensagens
PromptOptions controla a expansão de prompt, comportamento de fila durante a transmissão e notificações de simulação de prompt:
interface PromptOptions {
expandPromptTemplates?: boolean;
images?: ImageContent[];
streamingBehavior?: "steer" | "followUp";
source?: InputSource;
preflightResult?: (success: boolean) => void;
}preflightResult é chamado uma vez por invocação de prompt():
truequando o prompt foi aceito, colocado na fila ou tratado imediatamentefalsequando o comprovante imediato é rejeitado antes da aceitação
Ele é acionado antes de prompt() ser resolvido. prompt() ainda é resolvido somente após o término da execução completa aceita, incluindo novas tentativas. As falhas após a aceitação são relatadas através do evento normal e do fluxo de mensagens, não através de preflightResult(false).
O método prompt() lida com prompt templates, comandos de extensão e envio de mensagens:
// 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" });Comportamento:
- Comandos de extensão (por exemplo,
/mycommand): Execute imediatamente, mesmo durante o streaming. Eles gerenciam sua própria interação LLM viapi.sendMessage(). - Baseado em arquivo prompt templates (de arquivos
.md): Expandido para seu conteúdo antes de enviar ou enfileirar. - Durante streaming sem
streamingBehavior: Gera um erro. Usesteer()oufollowUp()diretamente ou especifique a opção. preflightResult(true): Significa que o prompt foi aceito, colocado na fila ou tratado imediatamente.preflightResult(false): Significa que o comprovante foi rejeitado antes da aceitação.
Para enfileiramento explícito durante o 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");Ambos steer() e followUp() expandem prompt templates baseado em arquivo, mas erro nos comandos de extensão (comandos de extensão não podem ser enfileirados).
Agente e AgentState
A classe Agent (de @earendil-works/pi-agent-core) lida com a interação principal do LLM. Acesse-o via 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();Eventos
Assine eventos para receber resultados de streaming e notificações de 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":
case "summarization_retry_scheduled":
case "summarization_retry_attempt_start":
case "summarization_retry_finished":
break;
}
});Referência de opções
Diretórios
const { session } = await createAgentSession({
// Working directory for DefaultResourceLoader discovery
cwd: process.cwd(), // default
// Global config directory
agentDir: "~/.pi/agent", // default (expands ~)
});cwd é usado por DefaultResourceLoader para:
- Extensões do projeto (
.pi/extensions/) - Habilidades de projeto:
.pi/skills/.agents/skills/emcwde diretórios ancestrais (até git repo root ou filesystem root quando não estiver em um repo)
- Solicitações do projeto (
.pi/prompts/) - Arquivos de contexto (
AGENTS.mdsubindo do cwd) - Nomenclatura do diretório de sessão
agentDir é usado por DefaultResourceLoader para:
- Extensões globais (
extensions/) - Habilidades globais:
skills/emagentDir(por exemplo~/.pi/agent/skills/)~/.agents/skills/
- Solicitações globais (
prompts/) - Arquivo de contexto global (
AGENTS.md) - Configurações (
settings.json) - Modelos personalizados (
models.json) - Credenciais (
auth.json) - Sessões (
sessions/)
Quando você passa um ResourceLoader personalizado, cwd e agentDir não controlam mais a descoberta de recursos. Eles ainda influenciam a nomenclatura da sessão e a resolução do caminho da ferramenta.
Modelo
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,
});Se nenhum modelo for fornecido:
- Tenta restaurar da sessão (se continuar)
- Usa o padrão das configurações
- Volta ao primeiro modelo disponível
Para corresponder à análise do modelo CLI, use os auxiliares do resolvedor exportados:
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() usa todos os modelos registrados, portanto, a configuração inicial do estilo --api-key pode resolver um modelo antes que a autenticação armazenada exista. resolveModelScopeWithDiagnostics() corresponde à semântica --models e enabledModels enquanto retorna avisos em vez de imprimi-los.
API Chaves e OAuth
Prioridade de resolução de autenticação (tratada por ModelRuntime):
- Substituições de tempo de execução (via
setRuntimeApiKey, não persistentes) - Credenciais armazenadas em
auth.json(API keys ou OAuth tokens) - Variáveis de ambiente (
ANTHROPIC_API_KEY,OPENAI_API_KEY, etc.) - Resolvedor substituto (para chaves de provedor personalizadas de
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() e removeRuntimeApiKey() são resolvidos depois que o catálogo em cache/integrado, a composição e o instantâneo de disponibilidade do provedor afetado são localmente consistentes. Eles não esperam pela atualização remota do catálogo. Se as credenciais foram confirmadas, mas a sincronização local falhar, elas serão rejeitadas com o CredentialSynchronizationError exportado; inspecione seus campos providerId, operation, credential e cause em vez de tentar novamente a mutação da credencial às cegas.
As operações de modelo público/autenticação e ModelRuntime.create({ signal }) aceitam sinais de interrupção opcionais e são ilimitadas quando omitidas. SDK os aplicativos possuem política de prazo para atualização remota do catálogo:
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);
}Uma atualização de rede com falha ou expirado não desfaz uma operação de credencial bem-sucedida. refresh() inicia uma nova geração de provedor, portanto, ele não espera por uma atualização antiga e paralisada e as gerações obsoletas não podem publicar depois.
Alerta do sistema
Use um ResourceLoader para substituir o prompt do 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 });Ferramentas
Especifique quais ferramentas integradas ativar:
- Nomes de ferramentas integradas:
read,bash,edit,write,grep,find,ls - Integrados padrão:
read,bash,edit,write noTools: "all"desativa todas as ferramentasnoTools: "builtin"desativa os recursos integrados padrão, mantendo as extensões e as ferramentas personalizadas ativadasexcludeToolsdesativa nomes específicos de ferramentas integradas, de extensão ou personalizadas após qualquer lista de permissõestoolsser aplicada
A ferramenta edit retorna details.diff para a exibição TUI de Pi e details.patch como um patch unificado padrão para consumidores 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"],
});Ferramentas com cwd personalizado
Quando você passa um cwd personalizado, createAgentSession() cria ferramentas integradas selecionadas para esse 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),
});Ferramentas personalizadas
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],
});Use defineTool() para definições independentes e matrizes como customTools: [myTool]. Inline pi.registerTool({... }) já infere os tipos de parâmetros corretamente.
Ferramentas personalizadas passadas por customTools são combinadas com ferramentas registradas em extensão. Extensions carregado pelo ResourceLoader também pode registrar ferramentas via pi.registerTool().
Se você passar tools, inclua cada nome de ferramenta personalizada ou de extensão que deseja ativar, por exemplo tools: ["read", "bash", "my_tool"].
Extensions
Extensions são carregados pelo ResourceLoader. DefaultResourceLoader descobre extensões das fontes de extensão ~/.pi/agent/extensions/, .pi/extensions/ e 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 pode registrar ferramentas, assinar eventos, adicionar comandos e muito mais. Veja extensions.md para o API completo.
Extensões inline nomeadas: Por padrão, as fábricas inline são exibidas como <inline:1>, <inline:2>, etc. na lista de inicialização Extensions. Para mostrar um nome descritivo, envolva a fábrica:
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],
});Isso é exibido como <inline:my-provider> em vez de <inline:1>. Funções básicas de fábrica ainda são aceitas para compatibilidade com versões anteriores.
Barramento de Evento: Extensions pode se comunicar via pi.events. Passe um eventBus para DefaultResourceLoader compartilhado se precisar emitir ou ouvir de fora:
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 });Arquivos de Contexto
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 });Comandos de barra
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 });Gerenciamento de sessão
As sessões usam uma estrutura em árvore com vinculação id/parentId, permitindo ramificação no local.
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" });Árvore do 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 fileGerenciamento de configurações
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?)- Carregar de arquivosSettingsManager.inMemory(settings?)- Sem E/S de arquivo
Configurações específicas do projeto:
As configurações são carregadas de dois locais e mescladas:
- Globais:
~/.pi/agent/settings.json - Projeto:
<cwd>/.pi/settings.json
O projeto substitui global. Objetos aninhados mesclam chaves. Os setters modificam as configurações globais por padrão.
Semântica de persistência e tratamento de erros:
- Os getters/setters de configurações são síncronos para o estado na memória.
- Os setters enfileiram gravações de persistência de forma assíncrona.
- Chame
await settingsManager.flush()quando precisar de um limite de durabilidade (por exemplo, antes da saída do processo ou antes de declarar o conteúdo do arquivo em testes). SettingsManagernão imprime erros de E/S de configurações. UsesettingsManager.drainErrors()e relate-os na camada do seu aplicativo.
Carregador de recursos
Use DefaultResourceLoader para descobrir extensões, habilidades, prompts, temas e 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;Valor de retorno
createAgentSession() retorna:
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;
}Exemplo completo
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.");Modos de execução
O SDK exporta utilitários em modo de execução para construir interfaces personalizadas sobre createAgentSession():
Modo interativo
Modo interativo TUI completo com editor, histórico de bate-papo e todos os 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();executarPrintMode
Modo de disparo único: enviar prompts, resultado de saída, sair:
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
Modo JSON-RPC para integração de subprocessos:
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);Consulte RPC documentation para o protocolo JSON.
RPC Alternativa de modo
Para integração baseada em subprocessos sem construir com SDK, use CLI diretamente:
pi --mode rpc --no-sessionConsulte RPC documentation para o protocolo JSON.
O SDK é preferido quando:
- Você quer segurança de tipo
- Você está no mesmo processo Node.js
- Você precisa de acesso direto ao estado do agente
- Você deseja personalizar ferramentas/extensões programaticamente
O modo RPC é preferido quando:
- Você está integrando de outro idioma
- Você quer isolamento de processos
- Você está construindo um cliente independente de idioma
Exportações
O principal ponto de entrada das exportações:
// 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 ToolPara tipos de extensão, consulte extensions.md para o API completo.