SDK
pi kann Ihnen bei der Verwendung von SDK helfen. Bitten Sie es, eine Integration für Ihren Anwendungsfall zu erstellen.
Das SDK bietet programmgesteuerten Zugriff auf die Agentenfunktionen von pi. Verwenden Sie es, um Pi in andere Anwendungen einzubetten, benutzerdefinierte Schnittstellen zu erstellen oder in automatisierte Arbeitsabläufe zu integrieren.
Beispielhafte Anwendungsfälle:
- Erstellen Sie eine benutzerdefinierte Benutzeroberfläche (Web, Desktop, Mobilgerät)
- Integrieren Sie Agentenfunktionen in bestehende Anwendungen
- Erstellen Sie automatisierte Pipelines mit Agent Reasoning
- Erstellen Sie benutzerdefinierte Tools, die Subagenten erzeugen
- Testen Sie das Agentenverhalten programmgesteuert
Unter examples/sdk/ finden Sie Arbeitsbeispiele von minimaler bis vollständiger Kontrolle.
Schnellstart
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-agentDas SDK ist im Hauptpaket enthalten. Keine separate Installation erforderlich.
Kernkonzepte
createAgentSession()
Die Hauptfabrikfunktion für ein einzelnes AgentSession.
createAgentSession() verwendet eine ResourceLoader, um Erweiterungen, Fähigkeiten, prompt templates, Themen und context files bereitzustellen. Wenn Sie keines bereitstellen, wird DefaultResourceLoader mit Standarderkennung verwendet.
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(),
});AgentSession
Die Sitzung verwaltet den Agentenlebenszyklus, den Nachrichtenverlauf, den Modellstatus, die Komprimierung und das Ereignis-Streaming.
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;
}Sitzungsersetzungs-APIs wie „Neue Sitzung“, „Fortsetzen“, „Fork“ und „Live-Import“ am AgentSessionRuntime, nicht am AgentSession.
createAgentSessionRuntime() und AgentSessionRuntime
Verwenden Sie die Laufzeit API, wenn Sie die aktive Sitzung ersetzen und den cwd-gebundenen Laufzeitstatus neu erstellen müssen. Dies ist dieselbe Ebene, die von den integrierten Modi „Interaktiv“, „Drucken“ und „RPC“ verwendet wird.
createAgentSessionRuntime() benötigt eine Laufzeitfabrik plus das anfängliche CWD-/Sitzungsziel. Die Factory wird über prozessglobale feste Eingaben geschlossen, erstellt cwd-gebundene Dienste für den effektiven cwd neu, löst Sitzungsoptionen für diese Dienste auf und gibt ein vollständiges Laufzeitergebnis zurück.
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 besitzt den Ersatz der aktiven Laufzeit über:
newSession()switchSession()fork()- Flows über
fork(entryId, { position: "at" })klonen importFromJsonl()
Wichtiges Verhalten:
runtime.sessionändert sich nach diesen Vorgängen- Veranstaltungsabonnements sind an eine bestimmte
AgentSessiongebunden, also abonnieren Sie sie nach dem Austausch erneut - Wenn Sie Erweiterungen verwenden, rufen Sie
runtime.session.bindExtensions(...)für die neue Sitzung erneut auf - Erstellung gibt Diagnose am
runtime.diagnosticszurück - Wenn das Erstellen oder Ersetzen zur Laufzeit fehlschlägt, löst die Methode aus und der Aufrufer entscheidet, wie damit umgegangen werden soll
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});
await runtime.newSession();
unsubscribe();
session = runtime.session;
unsubscribe = session.subscribe(() => {});Eingabeaufforderung und Nachrichtenwarteschlange
PromptOptions steuert die Aufforderungserweiterung, das Warteschlangenverhalten beim Streaming und Aufforderungs-Preflight-Benachrichtigungen:
interface PromptOptions {
expandPromptTemplates?: boolean;
images?: ImageContent[];
streamingBehavior?: "steer" | "followUp";
source?: InputSource;
preflightResult?: (success: boolean) => void;
}preflightResult wird einmal pro prompt()-Aufruf aufgerufen:
truewenn die Eingabeaufforderung angenommen, in die Warteschlange gestellt oder sofort bearbeitet wurdefalsewenn der sofortige Preflight vor der Annahme abgelehnt wird
Es wird ausgelöst, bevor prompt() verrechnet wird. prompt() wird immer noch erst aufgelöst, nachdem der gesamte akzeptierte Lauf abgeschlossen ist, einschließlich Wiederholungsversuchen. Fehler nach der Abnahme werden über den normalen Ereignis- und Nachrichtenstrom gemeldet, nicht über preflightResult(false).
Die prompt()-Methode verarbeitet prompt templates, Erweiterungsbefehle und das Senden von Nachrichten:
// 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" });Verhalten:
- Erweiterungsbefehle (z. B.
/mycommand): Sofort ausführen, auch während des Streamings. Sie verwalten ihre eigene LLM-Interaktion überpi.sendMessage(). - Dateibasiert prompt templates (aus
.mdDateien): Vor dem Senden oder Einreihen in die Warteschlange erweitert. - Beim Streaming ohne
streamingBehavior: Löst einen Fehler aus. Verwenden Siesteer()oderfollowUp()direkt oder geben Sie die Option an. preflightResult(true): Bedeutet, dass die Eingabeaufforderung angenommen, in die Warteschlange gestellt oder sofort bearbeitet wurde.preflightResult(false): Bedeutet, dass der Preflight vor der Annahme abgelehnt wurde.
Für explizite Warteschlangen während des Streamings:
// 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");Sowohl steer() als auch followUp() erweitern dateibasiert prompt templates, aber Fehler bei Erweiterungsbefehlen (Erweiterungsbefehle können nicht in die Warteschlange gestellt werden).
Agent und AgentState
Die Klasse Agent (von @earendil-works/pi-agent-core) übernimmt die Kern-LLM-Interaktion. Greifen Sie über session.agent darauf zu.
// 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();Veranstaltungen
Abonnieren Sie Ereignisse, um Streaming-Ausgaben und Lebenszyklusbenachrichtigungen zu erhalten.
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;
}
});Optionsreferenz
Verzeichnisse
const { session } = await createAgentSession({
// Working directory for DefaultResourceLoader discovery
cwd: process.cwd(), // default
// Global config directory
agentDir: "~/.pi/agent", // default (expands ~)
});cwd wird von DefaultResourceLoader verwendet für:
- Projekterweiterungen (
.pi/extensions/) - Projektkompetenzen:
.pi/skills/.agents/skills/incwdund Vorgängerverzeichnissen (bis zum Git-Repo-Root oder Dateisystem-Root, wenn nicht in einem Repo)
- Projektaufforderungen (
.pi/prompts/) - Kontextdateien (
AGENTS.mdbeim Aufsteigen von cwd) - Benennung des Sitzungsverzeichnisses
agentDir wird von DefaultResourceLoader verwendet für:
- Globale Erweiterungen (
extensions/) - Globale Kompetenzen:
skills/unteragentDir(zum Beispiel~/.pi/agent/skills/)~/.agents/skills/
- Globale Eingabeaufforderungen (
prompts/) - Globale Kontextdatei (
AGENTS.md) - Einstellungen (
settings.json) - Benutzerdefinierte Modelle (
models.json) - Anmeldeinformationen (
auth.json) - Sitzungen (
sessions/)
Wenn Sie eine benutzerdefinierte ResourceLoader übergeben, steuern cwd und agentDir die Ressourcenerkennung nicht mehr. Sie beeinflussen weiterhin die Benennung der Sitzung und die Auflösung des Werkzeugwegs.
Modell
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,
});Wenn kein Modell angegeben ist:
- Versucht eine Wiederherstellung aus der Sitzung (falls fortgesetzt)
- Verwendet die Standardeinstellungen
- Fällt auf das erste verfügbare Modell zurück
Um die Modellanalyse mit CLI abzugleichen, verwenden Sie die exportierten Resolver-Helfer:
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() verwendet alle registrierten Modelle, sodass die erstmalige Einrichtung des Stils --api-key ein Modell auflösen kann, bevor eine gespeicherte Authentifizierung vorhanden ist. resolveModelScopeWithDiagnostics() stimmt mit der Semantik von --models und enabledModels überein und gibt Warnungen zurück, anstatt sie zu drucken.
API Tasten und OAuth
Priorität der Authentifizierungsauflösung (verwaltet von ModelRuntime):
- Laufzeitüberschreibungen (über
setRuntimeApiKey, nicht persistent) - Gespeicherte Anmeldeinformationen in
auth.json(API keys oder OAuth Tokens) - Umgebungsvariablen (
ANTHROPIC_API_KEY,OPENAI_API_KEYusw.) - Fallback-Resolver (für benutzerdefinierte Anbieterschlüssel von
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() und removeRuntimeApiKey() werden aufgelöst, nachdem der zwischengespeicherte/integrierte Katalog, die Zusammensetzung und der Verfügbarkeits-Snapshot des betroffenen Anbieters lokal konsistent sind. Sie warten nicht auf die Aktualität des Remote-Katalogs. Wenn Anmeldeinformationen festgeschrieben wurden, die lokale Synchronisierung jedoch fehlschlägt, werden sie mit dem exportierten CredentialSynchronizationError abgelehnt; Überprüfen Sie die Felder providerId, operation, credential und cause, anstatt die Anmeldeinformationsmutation blind zu wiederholen.
Öffentliche Modell-/Authentifizierungsoperationen und ModelRuntime.create({ signal }) akzeptieren optionale Abbruchsignale und sind unbegrenzt, wenn sie weggelassen werden. SDK Anwendungseigene Fristenrichtlinie für die Aktualität des Remote-Katalogs:
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);
}Eine fehlgeschlagene oder abgelaufene Netzwerkaktualisierung macht einen erfolgreichen Anmeldeinformationsvorgang nicht rückgängig. refresh() startet eine neue Anbietergeneration, damit nicht hinter einer älteren, blockierten Aktualisierung gewartet wird und veraltete Generationen danach nicht veröffentlicht werden können.
Systemaufforderung
Verwenden Sie eine ResourceLoader, um die Systemaufforderung zu überschreiben:
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 });Werkzeuge
Geben Sie an, welche integrierten Tools aktiviert werden sollen:
- Integrierte Werkzeugnamen:
read,bash,edit,write,grep,find,ls - Standardintegrierte Funktionen:
read,bash,edit,write noTools: "all"deaktiviert alle ToolsnoTools: "builtin"deaktiviert standardmäßige integrierte Funktionen, während Erweiterungen und benutzerdefinierte Tools aktiviert bleibenexcludeToolsdeaktiviert bestimmte integrierte, erweiterte oder benutzerdefinierte Toolnamen, nachdem einetools-Zulassungsliste angewendet wurde
Das edit-Tool gibt details.diff für die TUI-Anzeige von Pi und details.patch als einheitlichen Standardpatch für SDK-Verbraucher zurück.
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"],
});Werkzeuge mit benutzerdefiniertem cwd
Wenn Sie ein benutzerdefiniertes cwd übergeben, erstellt createAgentSession() ausgewählte integrierte Tools für dieses 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),
});Siehe examples/sdk/05-tools.ts
Benutzerdefinierte Werkzeuge
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],
});Verwenden Sie defineTool() für eigenständige Definitionen und Arrays wie customTools: [myTool]. Inline pi.registerTool({... }) leitet Parametertypen bereits korrekt ab.
Über customTools übergebene benutzerdefinierte Tools werden mit erweiterungsregistrierten Tools kombiniert. Extensions, das vom ResourceLoader geladen wird, kann Werkzeuge auch über pi.registerTool() registrieren.
Wenn Sie tools übergeben, geben Sie alle benutzerdefinierten oder Erweiterungstoolnamen an, die aktiviert werden sollen, zum Beispiel tools: ["read", "bash", "my_tool"].
Siehe examples/sdk/05-tools.ts
Extensions
Extensions werden von der ResourceLoader geladen. DefaultResourceLoader erkennt Erweiterungen aus ~/.pi/agent/extensions/, .pi/extensions/ und den Erweiterungsquellen „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 kann Werkzeuge registrieren, Ereignisse abonnieren, Befehle hinzufügen und mehr. Siehe extensions.md für die vollständige API.
Benannte Inline-Erweiterungen: Standardmäßig werden Inline-Factorys als <inline:1>, <inline:2> usw. in der Startliste Extensions angezeigt. Um stattdessen einen beschreibenden Namen anzuzeigen, schließen Sie die Factory ein:
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],
});Dies wird als <inline:my-provider> anstelle von <inline:1> angezeigt. Aus Gründen der Abwärtskompatibilität werden weiterhin reine Werksfunktionen akzeptiert.
Ereignisbus: Extensions kann über pi.events kommunizieren. Übergeben Sie eine gemeinsame eventBus an DefaultResourceLoader, wenn Sie von außen etwas senden oder abhören müssen:
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 });Kontextdateien
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 });Slash-Befehle
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 });Sitzungsverwaltung
Sitzungen verwenden eine Baumstruktur mit id/parentId-Verknüpfung, die eine direkte Verzweigung ermöglicht.
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" });SessionManager-Baum 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 fileSiehe examples/sdk/11-sessions.ts und Session Format
Einstellungsverwaltung
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"),
});Statische Fabriken:
SettingsManager.create(cwd?, agentDir?)– Aus Dateien ladenSettingsManager.inMemory(settings?)– Keine Datei-E/A
Projektspezifische Einstellungen:
Einstellungen werden von zwei Speicherorten geladen und zusammengeführt:
- Global:
~/.pi/agent/settings.json - Projekt:
<cwd>/.pi/settings.json
Projekt überschreibt global. Verschachtelte Objekte führen Schlüssel zusammen. Setter ändern standardmäßig globale Einstellungen.
Persistenz und Fehlerbehandlungssemantik:
- Einstellungs-Getter/Setter sind für den In-Memory-Status synchron.
- Setter stellen Persistenzschreibvorgänge asynchron in die Warteschlange.
- Rufen Sie
await settingsManager.flush()auf, wenn Sie eine Haltbarkeitsgrenze benötigen (z. B. vor dem Beenden des Prozesses oder vor der Bestätigung von Dateiinhalten in Tests). SettingsManagerdruckt keine Einstellungen. E/A-Fehler. Verwenden SiesettingsManager.drainErrors()und melden Sie sie in Ihrer App-Ebene.
ResourceLoader
Verwenden Sie DefaultResourceLoader, um Erweiterungen, Fähigkeiten, Eingabeaufforderungen, Themen und context files zu entdecken.
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;Rückgabewert
createAgentSession() gibt Folgendes zurück:
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;
}Vollständiges Beispiel
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.");Laufmodi
Die SDK exportiert Dienstprogramme im Ausführungsmodus zum Erstellen benutzerdefinierter Schnittstellen zusätzlich zu createAgentSession():
Interaktiver Modus
Vollständiger TUI interaktiver Modus mit Editor, Chat-Verlauf und allen integrierten Befehlen:
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();runPrintMode
Single-Shot-Modus: Eingabeaufforderungen senden, Ergebnis ausgeben, beenden:
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
JSON-RPC Modus für die Teilprozessintegration:
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);Siehe RPC documentation für das JSON-Protokoll.
RPC Modusalternative
Für eine unterprozessbasierte Integration ohne Erstellung mit SDK verwenden Sie CLI direkt:
pi --mode rpc --no-sessionSiehe RPC documentation für das JSON-Protokoll.
Die SDK wird bevorzugt, wenn:
- Sie wollen Typsicherheit
- Sie befinden sich im selben Node.js Prozess
- Sie benötigen direkten Zugriff auf den Agentenstatus
- Sie möchten Tools/Erweiterungen programmgesteuert anpassen
Der Modus RPC wird bevorzugt, wenn:
- Sie integrieren eine andere Sprache
- Sie möchten eine Prozessisolation
- Sie erstellen einen sprachunabhängigen Client
Exporte
Der Haupteinstiegspunkt exportiert:
// 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 ToolInformationen zu Erweiterungstypen finden Sie unter extensions.md für die vollständige API.