Configuração, personalização, ajustes de plataforma e referências de API para Pi.

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

O 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.session alterações após essas operações
  • assinaturas de eventos são anexadas a um AgentSession especí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():

  • true quando o prompt foi aceito, colocado na fila ou tratado imediatamente
  • false quando 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 via pi.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. Use steer() ou followUp() 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/ em cwd e 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.md subindo do cwd)
  • Nomenclatura do diretório de sessão

agentDir é usado por DefaultResourceLoader para:

  • Extensões globais (extensions/)
  • Habilidades globais:
    • skills/ em agentDir (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:

  1. Tenta restaurar da sessão (se continuar)
  2. Usa o padrão das configurações
  3. 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.

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

API Chaves e OAuth

Prioridade de resolução de autenticação (tratada por ModelRuntime):

  1. Substituições de tempo de execução (via setRuntimeApiKey, não persistentes)
  2. Credenciais armazenadas em auth.json (API keys ou OAuth tokens)
  3. Variáveis ​​de ambiente (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
  4. 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.

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

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

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

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 ferramentas
  • noTools: "builtin" desativa os recursos integrados padrão, mantendo as extensões e as ferramentas personalizadas ativadas
  • excludeTools desativa nomes específicos de ferramentas integradas, de extensão ou personalizadas após qualquer lista de permissões tools ser 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),
});

Veja examples/sdk/05-tools.ts

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"].

Veja examples/sdk/05-tools.ts

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

Veja examples/sdk/06-extensions.ts e 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 });

Veja examples/sdk/04-skills.ts

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

Veja examples/sdk/07-context-files.ts

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

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

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 file

Veja examples/sdk/11-sessions.ts e Session Format

Gerenciamento 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 arquivos
  • SettingsManager.inMemory(settings?) - Sem E/S de arquivo

Configurações específicas do projeto:

As configurações são carregadas de dois locais e mescladas:

  1. Globais: ~/.pi/agent/settings.json
  2. 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).
  • SettingsManager não imprime erros de E/S de configurações. Use settingsManager.drainErrors() e relate-os na camada do seu aplicativo.

Veja examples/sdk/10-settings.ts

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

Consulte 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 Tool

Para tipos de extensão, consulte extensions.md para o API completo.