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-agentLe 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.sessionchangements après ces opérations- les abonnements aux événements sont attachés à un
AgentSessionspé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():
truelorsque l'invite a été acceptée, mise en file d'attente ou traitée immédiatementfalselorsque 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 viapi.sendMessage(). - Basé sur des fichiers prompt templates (à partir de
.mdfichiers): étendu à leur contenu avant l'envoi ou la mise en file d'attente. - Pendant la diffusion sans
streamingBehavior: génère une erreur. Utilisezsteer()oufollowUp()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/danscwdet 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.mden remontant de cwd) - Dénomination du répertoire de session
agentDir est utilisé par DefaultResourceLoader pour:
- Extensions globales (
extensions/) - Compétences globales:
skills/sousagentDir(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:
- Essaie de restaurer à partir de la session (si vous continuez)
- Utilise les paramètres par défaut
- 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.
Touches API et OAuth
Priorité de résolution d'authentification (gérée par ModelRuntime):
- Remplacements d'exécution (via
setRuntimeApiKey, non persistant) - Informations d'identification stockées dans
auth.json(API keys ou OAuth jetons) - Variables d'environnement (
ANTHROPIC_API_KEY,OPENAI_API_KEY, etc.) - 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.
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 });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 outilsnoTools: "builtin"désactive les éléments intégrés par défaut tout en gardant les extensions et les outils personnalisés activésexcludeToolsdésactive les noms spécifiques d'outils intégrés, d'extension ou personnalisés après l'application d'une liste autoriséetools
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),
});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"].
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));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 });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 });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 });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 fileGestion 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 fichiersSettingsManager.inMemory(settings?)- Aucune E/S de fichier
Paramètres spécifiques au projet:
Les paramètres se chargent à partir de deux emplacements et fusionnent:
- Mondial:
~/.pi/agent/settings.json - 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). SettingsManagern'imprime pas les erreurs d'E/S des paramètres. UtilisezsettingsManager.drainErrors()et signalez-les dans votre couche d'application.
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-sessionVoir 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 ToolPour les types d'extensions, voir extensions.md pour le API complet.