Configuration, personnalisation, paramètres de plateforme et références API pour Pi.

SDK

pi peut vous aider à utiliser le SDK. Demandez-lui de créer une intégration pour votre cas d'utilisation.

Le SDK fournit un accès programmatique aux capacités de l'agent de pi. Utilisez-le pour intégrer pi dans d'autres applications, créer des interfaces personnalisées ou intégrer des flux de travail automatisés.

Exemples de cas d'utilisation:

  • Créez une interface utilisateur personnalisée (Web, ordinateur de bureau, mobile)
  • Intégrer les capacités des agents dans les applications existantes
  • Créez des pipelines automatisés avec le raisonnement des agents
  • Créez des outils personnalisés qui génèrent des sous-agents
  • Tester le comportement de l'agent par programmation

Voir examples/sdk/ pour des exemples de travail allant du contrôle minimal au contrôle total.

Démarrage rapide

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

Installation

npm install @earendil-works/pi-coding-agent

Le SDK est inclus dans le package principal. Aucune installation séparée n'est nécessaire.

Concepts de base

créerAgentSession()

La fonction d'usine principale pour un seul AgentSession.

createAgentSession() utilise un ResourceLoader pour fournir des extensions, des compétences, un prompt templates, des thèmes et un context files. Si vous n'en fournissez pas, il utilise DefaultResourceLoader avec la découverte standard.

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

SessionAgent

La session gère le cycle de vie des agents, l'historique des messages, l'état du modèle, le compactage et le streaming des événements.

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

Remplacement de session API tels que nouvelle session, reprise, fork et importation en direct sur AgentSessionRuntime, pas sur AgentSession.

createAgentSessionRuntime() et AgentSessionRuntime

Utilisez le runtime API lorsque vous devez remplacer la session active et reconstruire l'état d'exécution lié au cwd. Il s'agit du même calque utilisé par les modes interactif, d'impression et RPC intégrés.

createAgentSessionRuntime() prend une usine d'exécution plus la cible initiale cwd/session. L'usine se ferme sur les entrées fixes globales du processus, recrée les services liés au cwd pour le cwd effectif, résout les options de session par rapport à ces services et renvoie un résultat d'exécution complet.

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 possède le remplacement du runtime actif sur:

  • newSession()
  • switchSession()
  • fork()
  • le clonage circule via fork(entryId, { position: "at" })
  • importFromJsonl()

Comportement important:

  • runtime.session changements après ces opérations
  • les abonnements aux événements sont attachés à un AgentSession spécifique, alors réabonnez-vous après le remplacement
  • si vous utilisez des extensions, appelez à nouveau le runtime.session.bindExtensions(...) pour la nouvelle session
  • la création renvoie un diagnostic sur runtime.diagnostics
  • si la création ou le remplacement du runtime échoue, la méthode est lancée et l'appelant décide comment le gérer
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});

await runtime.newSession();

unsubscribe();
session = runtime.session;
unsubscribe = session.subscribe(() => {});

Invites et mise en file d'attente des messages

PromptOptions contrôle l'expansion rapide, le comportement de la file d'attente pendant la diffusion et les notifications rapides de contrôle en amont:

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

preflightResult est appelé une fois par invocation prompt():

  • true lorsque l'invite a été acceptée, mise en file d'attente ou traitée immédiatement
  • false lorsque le contrôle en amont rapide est rejeté avant l'acceptation

Il se déclenche avant que prompt() ne soit résolu. prompt() n'est toujours résolu qu'une fois l'exécution complète acceptée terminée, y compris les tentatives. Les échecs après acceptation sont signalés via le flux normal d'événements et de messages, et non via preflightResult(false).

La méthode prompt() gère prompt templates, les commandes d'extension et l'envoi de messages:

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

Comportement:

  • Commandes d'extension (par exemple, /mycommand): exécutez-les immédiatement, même pendant la diffusion. Ils gèrent leur propre interaction LLM via pi.sendMessage().
  • Basé sur des fichiers prompt templates (à partir de .md fichiers): étendu à leur contenu avant l'envoi ou la mise en file d'attente.
  • Pendant la diffusion sans streamingBehavior: génère une erreur. Utilisez steer() ou followUp() directement, ou spécifiez l'option.
  • preflightResult(true): signifie que l'invite a été acceptée, mise en file d'attente ou traitée immédiatement.
  • preflightResult(false): signifie que le contrôle en amont est rejeté avant l'acceptation.

Pour une mise en file d'attente explicite pendant le 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");

steer() et followUp() développent tous deux prompt templates basé sur un fichier, mais erreur sur les commandes d'extension (les commandes d'extension ne peuvent pas être mises en file d'attente).

Agent et état de l'agent

La classe Agent (à partir de @earendil-works/pi-agent-core) gère l'interaction principale du LLM. Accédez-y 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();

Événements

Abonnez-vous aux événements pour recevoir des sorties en streaming et des notifications de cycle de vie.

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

Référence des options

Annuaires

const { session } = await createAgentSession({
  // Working directory for DefaultResourceLoader discovery
  cwd: process.cwd(), // default
  
  // Global config directory
  agentDir: "~/.pi/agent", // default (expands ~)
});

cwd est utilisé par DefaultResourceLoader pour:

  • Extensions de projet (.pi/extensions/)
  • Compétences projet:
    • .pi/skills/
    • .agents/skills/ dans cwd et les répertoires ancêtres (jusqu'à la racine du dépôt git ou la racine du système de fichiers lorsqu'il n'est pas dans un dépôt)
  • Invites du projet (.pi/prompts/)
  • Fichiers de contexte (AGENTS.md en remontant de cwd)
  • Dénomination du répertoire de session

agentDir est utilisé par DefaultResourceLoader pour:

  • Extensions globales (extensions/)
  • Compétences globales:
    • skills/ sous agentDir (par exemple ~/.pi/agent/skills/)
    • ~/.agents/skills/
  • Invites globales (prompts/)
  • Fichier de contexte global (AGENTS.md)
  • Paramètres (settings.json)
  • Modèles personnalisés (models.json)
  • Identifiants (auth.json)
  • Séances (sessions/)

Lorsque vous transmettez un ResourceLoader personnalisé, cwd et agentDir ne contrôlent plus la découverte des ressources. Ils influencent toujours la dénomination des sessions et la résolution du chemin d'outil.

Modèle

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

Si aucun modèle n'est fourni:

  1. Essaie de restaurer à partir de la session (si vous continuez)
  2. Utilise les paramètres par défaut
  3. Revient au premier modèle disponible

Pour faire correspondre l'analyse du modèle CLI, utilisez les assistants de résolution exportés:

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() utilise tous les modèles enregistrés, donc la première configuration de style --api-key peut résoudre un modèle avant que l'authentification stockée n'existe. resolveModelScopeWithDiagnostics() correspond à la sémantique --models et enabledModels tout en renvoyant les avertissements au lieu de les imprimer.

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

Touches API et OAuth

Priorité de résolution d'authentification (gérée par ModelRuntime):

  1. Remplacements d'exécution (via setRuntimeApiKey, non persistant)
  2. Informations d'identification stockées dans auth.json (API keys ou OAuth jetons)
  3. Variables d'environnement (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
  4. Résolveur de secours (pour les clés de fournisseur personnalisées à partir 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() et removeRuntimeApiKey() sont résolus une fois que le catalogue mis en cache/intégré, la composition et l'instantané de disponibilité du fournisseur concerné sont cohérents localement. Ils n’attendent pas la fraîcheur du catalogue distant. Si les informations d'identification ont été validées mais que la synchronisation locale échoue, elles sont rejetées avec le CredentialSynchronizationError exporté; inspectez ses champs providerId, operation, credential et cause au lieu de réessayer aveuglément la mutation des informations d'identification.

Les opérations publiques de modèle/d'authentification et ModelRuntime.create({ signal }) acceptent les signaux d'abandon facultatifs et sont illimitées lorsqu'elles sont omises. SDK les applications ont leur propre politique de délai pour la fraîcheur du catalogue à distance:

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

Un échec ou un délai d'actualisation du réseau n'annule pas une opération d'identification réussie. refresh() démarre une nouvelle génération de fournisseur, il n'attend donc pas une ancienne actualisation bloquée et les générations obsolètes ne peuvent pas publier par la suite.

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

Invite système

Utilisez un ResourceLoader pour remplacer l'invite du système:

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

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

Outils

Spécifiez les outils intégrés à activer:

  • Noms des outils intégrés: read, bash, edit, write, grep, find, ls
  • Intégrés par défaut: read, bash, edit, write
  • noTools: "all" désactive tous les outils
  • noTools: "builtin" désactive les éléments intégrés par défaut tout en gardant les extensions et les outils personnalisés activés
  • excludeTools désactive les noms spécifiques d'outils intégrés, d'extension ou personnalisés après l'application d'une liste autorisée tools

L'outil edit renvoie details.diff pour l'affichage TUI de Pi et details.patch en tant que correctif unifié standard pour les consommateurs 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"],
});

Outils avec cwd personnalisé

Lorsque vous transmettez un cwd personnalisé, createAgentSession() crée les outils intégrés sélectionnés pour ce 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),
});

Voir examples/sdk/05-tools.ts

Outils personnalisés

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

Utilisez defineTool() pour les définitions autonomes et les tableaux comme customTools: [myTool]. Inline pi.registerTool({... }) déduit déjà correctement les types de paramètres.

Les outils personnalisés transmis via customTools sont combinés avec des outils enregistrés par extension. Extensions chargé par le ResourceLoader peut également enregistrer des outils via pi.registerTool().

Si vous transmettez tools, incluez chaque nom d'outil personnalisé ou d'extension que vous souhaitez activer, par exemple tools: ["read", "bash", "my_tool"].

Voir examples/sdk/05-tools.ts

Extensions

Les Extensions sont chargés par les ResourceLoader. DefaultResourceLoader découvre les extensions des sources d'extension ~/.pi/agent/extensions/, .pi/extensions/ et 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 peut enregistrer des outils, s'abonner à des événements, ajouter des commandes, etc. Voir extensions.md pour le API complet.

Extensions en ligne nommées: Par défaut, les usines en ligne s'affichent sous la forme <inline:1>, <inline:2>, etc. dans la liste de démarrage Extensions. Pour afficher un nom descriptif à la place, enveloppez la fabrique:

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

Cela s'affiche sous la forme <inline:my-provider> au lieu de <inline:1>. Les fonctions d'usine nues sont toujours acceptées pour des raisons de compatibilité ascendante.

Event Bus: Extensions peut communiquer via pi.events. Passez un eventBus partagé à un DefaultResourceLoader si vous avez besoin d'émettre ou d'écouter de l'extérieur:

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

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

Voir examples/sdk/04-skills.ts

Fichiers contextuels

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

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

Commandes barre oblique

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

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

Gestion des sessions

Les sessions utilisent une structure arborescente avec des liens id/parentId, permettant un branchement sur place.

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

Arborescence 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

Voir examples/sdk/11-sessions.ts et Session Format

Gestion des paramètres

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

Usines statiques:

  • SettingsManager.create(cwd?, agentDir?) - Charger à partir de fichiers
  • SettingsManager.inMemory(settings?) - Aucune E/S de fichier

Paramètres spécifiques au projet:

Les paramètres se chargent à partir de deux emplacements et fusionnent:

  1. Mondial: ~/.pi/agent/settings.json
  2. Projet: <cwd>/.pi/settings.json

Le projet remplace le global. Les objets imbriqués fusionnent les clés. Les setters modifient les paramètres globaux par défaut.

Sémantique de persistance et de gestion des erreurs:

  • Les getters/setters de paramètres sont synchrones pour l’état en mémoire.
  • Les setters mettent en file d'attente les écritures persistantes de manière asynchrone.
  • Appelez await settingsManager.flush() lorsque vous avez besoin d'une limite de durabilité (par exemple, avant la sortie du processus ou avant d'affirmer le contenu du fichier dans les tests).
  • SettingsManager n'imprime pas les erreurs d'E/S des paramètres. Utilisez settingsManager.drainErrors() et signalez-les dans votre couche d'application.

Voir examples/sdk/10-settings.ts

Chargeur de ressources

Utilisez DefaultResourceLoader pour découvrir des extensions, des compétences, des invites, des thèmes et 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;

Valeur de retour

createAgentSession() renvoie:

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

Exemple complet

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

Modes d'exécution

Le SDK exporte les utilitaires en mode exécution pour créer des interfaces personnalisées au-dessus de createAgentSession():

Mode interactif

Mode interactif complet TUI avec éditeur, historique des discussions et toutes les commandes intégrées:

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

ModeImpression

Mode mono-coup: envoyer des invites, afficher le résultat, quitter:

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

exécuterRpcMode

Mode JSON-RPC pour l'intégration des sous-processus:

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

Voir RPC documentation pour le protocole JSON.

RPC Mode alternatif

Pour une intégration basée sur des sous-processus sans construire avec le SDK, utilisez directement le CLI:

pi --mode rpc --no-session

Voir RPC documentation pour le protocole JSON.

Le SDK est préféré lorsque:

  • Vous voulez la sécurité du type
  • Vous êtes dans le même processus Node.js
  • Vous avez besoin d'un accès direct à l'état de l'agent
  • Vous souhaitez personnaliser les outils/extensions par programme

Le mode RPC est préféré lorsque:

  • Vous intégrez depuis une autre langue
  • Vous souhaitez une isolation des processus
  • Vous créez un client indépendant de la langue

Exportations

Le principal point d’entrée exporte:

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

Pour les types d'extensions, voir extensions.md pour le API complet.