Konfigurasi, kustomisasi, pengaturan platform, dan referensi API untuk Pi.

SDK

pi dapat membantu Anda menggunakan SDK. Mintalah untuk membangun integrasi untuk kasus penggunaan Anda.

SDK menyediakan akses terprogram ke kemampuan agen pi. Gunakan untuk menyematkan pi di aplikasi lain, membuat antarmuka khusus, atau berintegrasi dengan alur kerja otomatis.

Contoh kasus penggunaan:

  • Bangun UI khusus (web, desktop, seluler)
  • Integrasikan kemampuan agen ke dalam aplikasi yang ada
  • Buat saluran pipa otomatis dengan alasan agen
  • Bangun alat khusus yang menghasilkan sub-agen
  • Uji perilaku agen secara terprogram

Lihat examples/sdk/ untuk contoh kerja dari kontrol minimal hingga kontrol penuh.

Mulai Cepat

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

Instalasi

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

SDK disertakan dalam paket utama. Tidak diperlukan instalasi terpisah.

Konsep Inti

buatAgentSession()

Fungsi pabrik utama untuk satu AgentSession.

createAgentSession() menggunakan ResourceLoader untuk menyediakan ekstensi, keterampilan, prompt templates, tema, dan context files. Jika Anda tidak menyediakannya, ia akan menggunakan DefaultResourceLoader dengan penemuan standar.

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

AgenSesi

Sesi ini mengelola siklus hidup agen, riwayat pesan, status model, pemadatan, dan streaming peristiwa.

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

Penggantian sesi API seperti sesi baru, resume, fork, dan impor langsung di AgentSessionRuntime, bukan di AgentSession.

createAgentSessionRuntime() dan AgentSessionRuntime

Gunakan runtime API ketika Anda perlu mengganti sesi aktif dan membangun kembali status runtime yang terikat cwd. Ini adalah lapisan yang sama yang digunakan oleh mode interaktif, cetak, dan RPC bawaan.

createAgentSessionRuntime() membutuhkan pabrik runtime ditambah target cwd/sesi awal. Pabrik menutup input tetap global proses, membuat ulang layanan terikat cwd untuk cwd yang efektif, menyelesaikan opsi sesi terhadap layanan tersebut, dan mengembalikan hasil runtime penuh.

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 memiliki penggantian runtime aktif di:

  • newSession()
  • switchSession()
  • fork()
  • aliran klon melalui fork(entryId, { position: "at" })
  • importFromJsonl()

Perilaku penting:

  • runtime.session perubahan setelah operasi tersebut
  • langganan acara terikat pada AgentSession tertentu, jadi berlangganan kembali setelah penggantian
  • jika Anda menggunakan ekstensi, panggil runtime.session.bindExtensions(...) lagi untuk sesi baru
  • kreasi mengembalikan diagnostik pada runtime.diagnostics
  • jika pembuatan atau penggantian runtime gagal, metode akan dilempar dan pemanggil memutuskan bagaimana menanganinya
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});

await runtime.newSession();

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

Anjuran dan Antrian Pesan

PromptOptions mengontrol perluasan cepat, perilaku antrian saat streaming, dan pemberitahuan pra-penerbangan cepat:

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

preflightResult dipanggil sekali per prompt() pemanggilan:

  • true ketika perintah diterima, dimasukkan dalam antrean, atau ditangani dengan segera
  • false ketika preflight cepat ditolak sebelum diterima

Ini menyala sebelum prompt() terselesaikan. prompt() masih terselesaikan hanya setelah proses yang diterima sepenuhnya selesai, termasuk percobaan ulang. Kegagalan setelah penerimaan dilaporkan melalui peristiwa normal dan aliran pesan, bukan melalui preflightResult(false).

Metode prompt() menangani prompt templates, perintah ekstensi, dan pengiriman pesan:

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

Perilaku:

  • Perintah ekstensi (mis., /mycommand): Jalankan segera, bahkan saat streaming. Mereka mengelola interaksi LLM mereka sendiri melalui pi.sendMessage().
  • Berbasis file prompt templates (dari .md file): Diperluas ke kontennya sebelum mengirim atau mengantri.
  • Selama streaming tanpa streamingBehavior: Terjadi kesalahan. Gunakan steer() atau followUp() secara langsung, atau tentukan opsinya.
  • preflightResult(true): Berarti perintah diterima, dimasukkan dalam antrean, atau ditangani dengan segera.
  • preflightResult(false): Berarti preflight ditolak sebelum diterima.

Untuk antrian eksplisit selama 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");

Baik steer() dan followUp() memperluas berbasis file prompt templates tetapi kesalahan pada perintah ekstensi (perintah ekstensi tidak dapat dimasukkan dalam antrean).

Agen dan AgentState

Kelas Agent (dari @earendil-works/pi-agent-core) menangani interaksi inti LLM. Akses melalui 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();

Acara

Berlangganan acara untuk menerima keluaran streaming dan pemberitahuan siklus hidup.

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

Referensi Pilihan

Direktori

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

cwd digunakan oleh DefaultResourceLoader untuk:

  • Ekstensi proyek (.pi/extensions/)
  • Keterampilan proyek:
    • .pi/skills/
    • .agents/skills/ di cwd dan direktori leluhur (hingga git repo root, atau root sistem file jika tidak ada dalam repo)
  • Perintah proyek (.pi/prompts/)
  • File konteks (AGENTS.md berjalan dari cwd)
  • Penamaan direktori sesi

agentDir digunakan oleh DefaultResourceLoader untuk:

  • Ekstensi global (extensions/)
  • Keterampilan global:
    • skills/ di bawah agentDir (misalnya ~/.pi/agent/skills/)
    • ~/.agents/skills/
  • Perintah global (prompts/)
  • File konteks global (AGENTS.md)
  • Pengaturan (settings.json)
  • Model khusus (models.json)
  • Kredensial (auth.json)
  • Sesi (sessions/)

Saat Anda meneruskan ResourceLoader khusus, cwd dan agentDir tidak lagi mengontrol penemuan sumber daya. Mereka masih mempengaruhi penamaan sesi dan resolusi jalur alat.

Model

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

Jika tidak ada model yang disediakan:

  1. Mencoba memulihkan dari sesi (jika melanjutkan)
  2. Menggunakan default dari pengaturan
  3. Kembali ke model pertama yang tersedia

Untuk mencocokkan penguraian model CLI, gunakan bantuan penyelesai yang diekspor:

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() menggunakan semua model terdaftar sehingga pengaturan pertama kali gaya --api-key dapat menyelesaikan model sebelum autentikasi yang disimpan ada. resolveModelScopeWithDiagnostics() cocok dengan semantik --models dan enabledModels sambil mengembalikan peringatan alih-alih mencetaknya.

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

API Kunci dan OAuth

Prioritas resolusi autentikasi (ditangani oleh ModelRuntime):

  1. Penggantian waktu proses (melalui setRuntimeApiKey, tidak dipertahankan)
  2. Kredensial yang disimpan dalam auth.json (API keys atau OAuth token)
  3. Variabel lingkungan (ANTHROPIC_API_KEY, OPENAI_API_KEY, dll.)
  4. Penyelesai cadangan (untuk kunci penyedia khusus dari 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(), dan removeRuntimeApiKey() diselesaikan setelah katalog, komposisi, dan snapshot ketersediaan yang di-cache/bawaan dari penyedia yang terpengaruh konsisten secara lokal. Mereka tidak menunggu kesegaran katalog yang jauh. Jika kredensial telah diterapkan tetapi sinkronisasi lokal gagal, kredensial akan ditolak dengan CredentialSynchronizationError yang diekspor; periksa bidang providerId, operation, credential, dan cause daripada mencoba ulang mutasi kredensial secara membabi buta.

Operasi model publik/autentikasi dan ModelRuntime.create({ signal }) menerima sinyal pembatalan opsional dan tidak dibatasi saat dihilangkan. SDK aplikasi memiliki kebijakan tenggat waktu untuk kesegaran katalog jarak jauh:

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

Penyegaran jaringan yang gagal atau habis waktunya tidak membatalkan operasi kredensial yang berhasil. refresh() memulai generasi penyedia baru, sehingga tidak menunggu penyegaran lama yang terhenti dan generasi lama tidak dapat mempublikasikannya setelahnya.

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

Perintah Sistem

Gunakan ResourceLoader untuk mengganti perintah sistem:

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

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

Peralatan

Tentukan alat bawaan mana yang akan diaktifkan:

  • Nama alat bawaan: read, bash, edit, write, grep, find, ls
  • Bawaan bawaan: read, bash, edit, write
  • noTools: "all" menonaktifkan semua alat
  • noTools: "builtin" menonaktifkan bawaan bawaan sambil tetap mengaktifkan ekstensi dan alat khusus
  • excludeTools menonaktifkan nama alat bawaan, ekstensi, atau khusus tertentu setelah tools daftar yang diizinkan diterapkan

Alat edit mengembalikan details.diff untuk tampilan Pi TUI dan details.patch sebagai patch terpadu standar untuk SDK konsumen.

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

Alat dengan Custom cwd

Saat Anda meneruskan cwd khusus, createAgentSession() membuat alat bawaan yang dipilih untuk cwd tersebut.

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

Lihat examples/sdk/05-tools.ts

Alat Kustom

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

Gunakan defineTool() untuk definisi mandiri dan array seperti customTools: [myTool]. Inline pi.registerTool({... }) sudah menyimpulkan tipe parameter dengan benar.

Alat khusus yang diteruskan melalui customTools digabungkan dengan alat yang terdaftar dengan ekstensi. Extensions yang dimuat oleh ResourceLoader juga dapat mendaftarkan alat melalui pi.registerTool().

Jika Anda meneruskan tools, sertakan setiap nama alat khusus atau ekstensi yang ingin Anda aktifkan, misalnya tools: ["read", "bash", "my_tool"].

Lihat examples/sdk/05-tools.ts

Extensions

Extensions dimuat oleh ResourceLoader. DefaultResourceLoader menemukan ekstensi dari ~/.pi/agent/extensions/, .pi/extensions/, dan sumber ekstensi 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 dapat mendaftarkan alat, berlangganan acara, menambahkan perintah, dan banyak lagi. Lihat extensions.md untuk API selengkapnya.

Ekstensi inline yang diberi nama: Secara default, pabrik inline ditampilkan sebagai <inline:1>, <inline:2>, dll. di daftar startup Extensions. Untuk menampilkan nama deskriptif, bungkus pabriknya:

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

Ini ditampilkan sebagai <inline:my-provider> bukannya <inline:1>. Fungsi pabrik yang kosong masih diterima untuk kompatibilitas ke belakang.

Bus Acara: Extensions dapat berkomunikasi melalui pi.events. Berikan eventBus ke DefaultResourceLoader bersama jika Anda perlu memancarkan atau mendengarkan dari luar:

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

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

Lihat examples/sdk/04-skills.ts

File Konteks

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

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

Perintah Tebas

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

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

Manajemen Sesi

Sesi menggunakan struktur pohon dengan tautan id/parentId, sehingga memungkinkan percabangan di tempat.

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

Pohon Manajer Sesi 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

Lihat examples/sdk/11-sessions.ts dan Session Format

Manajemen Pengaturan

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

Pabrik statis:

  • SettingsManager.create(cwd?, agentDir?) - Muat dari file
  • SettingsManager.inMemory(settings?) - Tidak ada I/O berkas

Setelan spesifik proyek:

Pengaturan dimuat dari dua lokasi dan digabungkan:

  1. Global: ~/.pi/agent/settings.json
  2. Proyek: <cwd>/.pi/settings.json

Proyek menggantikan global. Objek bersarang menggabungkan kunci. Setter mengubah pengaturan global secara default.

Semantik penanganan kesalahan dan persistensi:

  • Pengambil/penyetel pengaturan sinkron untuk status dalam memori.
  • Penyetel membuat persistensi antrean menulis secara asinkron.
  • Panggil await settingsManager.flush() ketika Anda memerlukan batas ketahanan (misalnya, sebelum proses keluar atau sebelum menegaskan konten file dalam pengujian).
  • SettingsManager tidak mencetak kesalahan pengaturan I/O. Gunakan settingsManager.drainErrors() dan laporkan di lapisan aplikasi Anda.

Lihat examples/sdk/10-settings.ts

ResourceLoader

Gunakan DefaultResourceLoader untuk menemukan ekstensi, keterampilan, petunjuk, tema, dan 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;

Nilai Pengembalian

createAgentSession() kembali:

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

Contoh Lengkap

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

Jalankan Mode

Utilitas mode proses ekspor SDK untuk membangun antarmuka khusus di atas createAgentSession():

Mode Interaktif

Mode interaktif TUI penuh dengan editor, riwayat obrolan, dan semua perintah bawaan:

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

jalankanPrintMode

Mode pengambilan tunggal: kirim petunjuk, hasil keluaran, keluar:

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

jalankanRpcMode

Mode JSON-RPC untuk integrasi subproses:

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

Lihat RPC documentation untuk protokol JSON.

RPC Mode Alternatif

Untuk integrasi berbasis subproses tanpa membangun dengan SDK, gunakan CLI secara langsung:

pi --mode rpc --no-session

Lihat RPC documentation untuk protokol JSON.

SDK lebih disukai ketika:

  • Anda ingin mengetik keamanan
  • Anda berada dalam proses Node.js yang sama
  • Anda memerlukan akses langsung ke negara agen
  • Anda ingin menyesuaikan alat/ekstensi secara terprogram

Mode RPC lebih disukai ketika:

  • Anda mengintegrasikan dari bahasa lain
  • Anda ingin proses isolasi
  • Anda sedang membangun klien tanpa bahasa

Ekspor

Ekspor titik masuk utama:

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

Untuk jenis ekstensi, lihat extensions.md untuk API selengkapnya.