Pi 구성, 확장, 플랫폼 설정 및 API 참조.

SDK

pi는 SDK를 사용하는 데 도움이 될 수 있습니다. 사용 사례에 맞는 통합 구축을 요청하세요.

SDK는 pi의 에이전트 기능에 대한 프로그래밍 방식의 액세스를 제공합니다. 이를 사용하여 다른 애플리케이션에 pi를 내장하거나 사용자 정의 인터페이스를 구축하거나 자동화된 워크플로와 통합할 수 있습니다.

사용 사례 예시:

  • 맞춤형 UI 구축(웹, 데스크톱, 모바일)
  • 에이전트 기능을 기존 애플리케이션에 통합
  • 에이전트 추론을 통해 자동화된 파이프라인 생성
  • 하위 에이전트를 생성하는 사용자 정의 도구 구축
  • 프로그래밍 방식으로 에이전트 동작 테스트

최소 제어부터 전체 제어까지의 작업 예는 examples/sdk/를 참조하세요.

빠른 시작

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

설치

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

SDK은 기본 패키지에 포함되어 있습니다. 별도의 설치가 필요하지 않습니다.

핵심 개념

createAgentSession()

단일 AgentSession에 대한 기본 팩토리 기능입니다.

createAgentSession()ResourceLoader을 사용하여 확장 기능, 기술, prompt templates, 테마 및 context files를 제공합니다. 제공하지 않으면 표준 검색과 함께 DefaultResourceLoader를 사용합니다.

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

에이전트 세션

세션은 에이전트 수명 주기, 메시지 기록, 모델 상태, 압축 및 이벤트 스트리밍을 관리합니다.

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

새 세션, 재개, 포크, 가져오기 등의 세션 교체 API는 AgentSession이 아닌 AgentSessionRuntime에서 라이브로 수행됩니다.

createAgentSessionRuntime() 및 AgentSessionRuntime

활성 세션을 교체하고 cwd 바인딩 런타임 상태를 다시 빌드해야 하는 경우 런타임 API을 사용하세요. 이는 내장된 대화형, 인쇄 및 RPC 모드에서 사용되는 것과 동일한 레이어입니다.

createAgentSessionRuntime()는 런타임 팩토리와 초기 cwd/세션 대상을 사용합니다. 팩토리는 프로세스 전역 고정 입력을 닫고, 유효 cwd에 대해 cwd 바인딩 서비스를 다시 생성하고, 해당 서비스에 대한 세션 옵션을 확인하고, 전체 런타임 결과를 반환합니다.

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는 다음에서 활성 런타임을 대체합니다.

  • newSession()
  • switchSession()
  • fork()
  • fork(entryId, { position: "at" })를 통한 클론 흐름
  • importFromJsonl()

중요한 동작:

  • runtime.session 해당 작업 후 변경 사항
  • 이벤트 구독은 특정 AgentSession에 첨부되므로 교체 후 다시 구독하세요.
  • 확장 기능을 사용하는 경우 새 세션을 위해 runtime.session.bindExtensions(...)를 다시 호출하세요.
  • 생성은 runtime.diagnostics에 대한 진단을 반환합니다.
  • 런타임 생성 또는 교체가 실패하면 메서드가 발생하고 호출자가 이를 처리하는 방법을 결정합니다.
let session = runtime.session;
let unsubscribe = session.subscribe(() => {});

await runtime.newSession();

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

프롬프트 및 메시지 큐

PromptOptions 프롬프트 확장, 스트리밍 중 대기열 동작 및 프롬프트 실행 전 알림을 제어합니다.

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

preflightResultprompt() 호출마다 한 번씩 호출됩니다.

  • true 프롬프트가 수락되거나 대기열에 추가되거나 즉시 처리된 경우
  • false 승인 전에 프롬프트 프리플라이트가 거부된 경우

prompt() 해결되기 전에 실행됩니다. prompt() 재시도를 포함하여 허용된 전체 실행이 완료된 후에만 문제가 해결됩니다. 수락 후 실패는 preflightResult(false)를 통하지 않고 일반 이벤트 및 메시지 스트림을 통해 보고됩니다.

prompt() 메소드는 prompt templates, 확장 명령 및 메시지 전송을 처리합니다.

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

행동:

  • 확장 명령(예: /mycommand): 스트리밍 중에도 즉시 실행됩니다. 그들은 pi.sendMessage()를 통해 자체 LLM 상호 작용을 관리합니다.
  • 파일 기반 prompt templates(.md 파일에서): 전송하거나 대기열에 넣기 전의 콘텐츠로 확장됩니다.
  • streamingBehavior 없이 스트리밍하는 동안: 오류가 발생합니다. steer() 또는 followUp()를 직접 사용하거나 옵션을 지정하세요.
  • preflightResult(true): 프롬프트가 즉시 수락, 대기 또는 처리되었음을 의미합니다.
  • preflightResult(false): 수락 전 비행 전 거부를 의미합니다.

스트리밍 중 명시적 대기열의 경우:

// 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()followUp() 모두 파일 기반 prompt templates을 확장하지만 확장 명령에 오류가 발생합니다(확장 명령을 대기열에 추가할 수 없음).

에이전트 및 AgentState

Agent 클래스(@earendil-works/pi-agent-core의)는 핵심 LLM 상호 작용을 처리합니다. 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();

이벤트

스트리밍 출력 및 수명 주기 알림을 받으려면 이벤트를 구독하세요.

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

옵션 참조

디렉토리

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

cwdDefaultResourceLoader에서 다음 용도로 사용됩니다.

  • 프로젝트 확장(.pi/extensions/)
  • 프로젝트 기술:
    • .pi/skills/
    • .agents/skills/ cwd 및 상위 디렉터리(최대 git repo 루트 또는 repo에 없는 경우 파일 시스템 루트)
  • 프로젝트 프롬프트(.pi/prompts/)
  • 컨텍스트 파일(AGENTS.md cwd에서 이동)
  • 세션 디렉터리 이름 지정

agentDirDefaultResourceLoader에서 다음 용도로 사용됩니다.

  • 전역 확장(extensions/)
  • 글로벌 기술:
    • skills/ 아래 agentDir(예: ~/.pi/agent/skills/)
    • ~/.agents/skills/
  • 전역 프롬프트(prompts/)
  • 전역 컨텍스트 파일(AGENTS.md)
  • 설정(settings.json)
  • 맞춤 모델(models.json)
  • 자격 증명(auth.json)
  • 세션(sessions/)

사용자 정의 ResourceLoader를 전달하면 cwdagentDir가 더 이상 리소스 검색을 제어하지 않습니다. 이는 여전히 세션 이름 지정 및 도구 경로 해결에 영향을 미칩니다.

모델

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

모델이 제공되지 않은 경우:

  1. 세션에서 복원을 시도합니다(계속하는 경우).
  2. 설정의 기본값을 사용합니다.
  3. 첫 번째 사용 가능한 모델로 돌아갑니다.

CLI 모델 구문 분석을 일치시키려면 내보낸 확인 도우미를 사용하세요.

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()는 등록된 모든 모델을 사용하므로 --api-key 스타일 최초 설정은 저장된 인증이 존재하기 전에 모델을 해결할 수 있습니다. resolveModelScopeWithDiagnostics()--modelsenabledModels 의미와 일치하며 경고를 인쇄하는 대신 반환합니다.

examples/sdk/02-custom-model.ts 참조

API 키 및 OAuth

인증 해결 우선순위(ModelRuntime에서 처리):

  1. 런타임 재정의(setRuntimeApiKey를 통해, 지속되지 않음)
  2. auth.json(API keys 또는 OAuth 토큰)에 저장된 자격 증명
  3. 환경 변수(ANTHROPIC_API_KEY, OPENAI_API_KEY 등)
  4. 대체 확인자(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()removeRuntimeApiKey()는 영향을 받는 제공업체의 캐시/내장 카탈로그, 구성 및 가용성 스냅샷이 로컬에서 일관된 후에 해결됩니다. 원격 카탈로그가 최신 상태가 될 때까지 기다리지 않습니다. 자격 증명이 커밋되었지만 로컬 동기화에 실패하면 내보낸 CredentialSynchronizationError; 맹목적으로 자격 증명 변형을 재시도하는 대신 providerId, operation, credentialcause 필드를 검사하세요.

공개 모델/인증 작업 및 ModelRuntime.create({ signal })는 선택적 중단 신호를 허용하며 생략 시 제한이 없습니다. SDK 원격 카탈로그 업데이트에 대한 애플리케이션 자체 마감일 정책:

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

실패하거나 시간이 초과된 네트워크 새로 고침은 성공적인 자격 증명 작업을 취소하지 않습니다. refresh() 새로운 공급자 생성을 시작하므로 이전에 지연된 새로 고침 뒤에 기다리지 않으며 오래된 세대는 나중에 게시할 수 없습니다.

examples/sdk/09-api-keys-and-oauth.ts 참조

시스템 프롬프트

시스템 프롬프트를 재정의하려면 ResourceLoader를 사용하세요.

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

examples/sdk/03-custom-prompt.ts 참조

도구

활성화할 내장 도구를 지정합니다.

  • 내장 도구 이름: read, bash, edit, write, grep, find, ls
  • 기본 내장: read, bash, edit, write
  • noTools: "all" 모든 도구를 비활성화합니다.
  • noTools: "builtin" 확장 기능과 맞춤 도구는 활성화된 상태로 유지하면서 기본 내장 기능을 비활성화합니다.
  • excludeTools tools 허용 목록이 적용된 후 특정 내장, 확장 또는 맞춤 도구 이름을 비활성화합니다.

edit 도구는 Pi의 TUI 디스플레이에 대해 details.diff를 반환하고 SDK 소비자를 위한 표준 통합 패치로 details.patch를 반환합니다.

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

사용자 정의 cwd가 포함된 도구

사용자 정의 cwd를 전달하면 createAgentSession()는 해당 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),
});

examples/sdk/05-tools.ts 참조

맞춤형 도구

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

customTools: [myTool]와 같은 독립형 정의 및 배열에는 defineTool()를 사용하세요. 인라인 pi.registerTool({... })는 이미 매개변수 유형을 올바르게 추론합니다.

customTools를 통해 전달된 사용자 정의 도구는 확장 프로그램에 등록된 도구와 결합됩니다. ResourceLoader에 의해 로드된 Extensions는 pi.registerTool()를 통해 도구를 등록할 수도 있습니다.

tools를 전달하는 경우 활성화하려는 각 사용자 정의 또는 확장 도구 이름을 포함합니다(예: tools: ["read", "bash", "my_tool"]).

examples/sdk/05-tools.ts 참조

Extensions

Extensions는 ResourceLoader에 의해 로드됩니다. DefaultResourceLoader~/.pi/agent/extensions/, .pi/extensions/ 및 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 도구 등록, 이벤트 구독, 명령 추가 등을 할 수 있습니다. 전체 API를 보려면 extensions.md를 참조하세요.

명명된 인라인 확장: 기본적으로 인라인 팩토리는 시작 Extensions 목록에 <inline:1>, <inline:2> 등으로 표시됩니다. 대신 설명적인 이름을 표시하려면 팩토리를 래핑하세요.

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

<inline:1> 대신 <inline:my-provider>로 표시됩니다. 이전 버전과의 호환성을 위해 베어 팩토리 기능은 여전히 ​​허용됩니다.

이벤트 버스: Extensions는 pi.events를 통해 통신할 수 있습니다. 외부에서 내보내거나 들어야 하는 경우 공유 eventBusDefaultResourceLoader로 전달합니다.

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

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

examples/sdk/04-skills.ts 참조

컨텍스트 파일

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

examples/sdk/07-context-files.ts 참조

슬래시 명령

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

examples/sdk/08-prompt-templates.ts 참조

세션 관리

세션은 id/parentId 링크가 포함된 트리 구조를 사용하여 내부 분기가 가능합니다.

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 트리 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

examples/sdk/11-sessions.tsSession Format를 참조하세요.

설정 관리

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

정적 팩토리:

  • SettingsManager.create(cwd?, agentDir?) - 파일에서 로드
  • SettingsManager.inMemory(settings?) - 파일 I/O 없음

프로젝트별 설정:

설정은 두 위치에서 로드되어 병합됩니다.

  1. 글로벌: ~/.pi/agent/settings.json
  2. 프로젝트: <cwd>/.pi/settings.json

프로젝트가 전역을 재정의합니다. 중첩된 객체는 키를 병합합니다. Setter는 기본적으로 전역 설정을 수정합니다.

지속성 및 오류 처리 의미:

  • 설정 getter/setter는 메모리 내 상태에 대해 동기식입니다.
  • Setter는 대기열에 지속성을 비동기적으로 씁니다.
  • 내구성 경계가 필요한 경우(예: 프로세스 종료 전 또는 테스트에서 파일 내용을 어설션하기 전) await settingsManager.flush()를 호출하세요.
  • SettingsManager는 설정 I/O 오류를 인쇄하지 않습니다. settingsManager.drainErrors()를 사용하여 앱 레이어에 보고하세요.

examples/sdk/10-settings.ts 참조

리소스로더

확장 프로그램, 기술, 프롬프트, 테마 및 context files를 찾으려면 DefaultResourceLoader를 사용하세요.

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;

반환 값

createAgentSession() 반환:

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

완전한 예

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

실행 모드

SDK는 createAgentSession() 위에 사용자 정의 인터페이스를 구축하기 위한 실행 모드 유틸리티를 내보냅니다.

대화형 모드

편집기, 채팅 기록 및 모든 내장 명령이 포함된 완전한 TUI 대화형 모드:

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

싱글샷 모드: 프롬프트 보내기, 결과 출력, 종료:

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

runRpc모드

하위 프로세스 통합을 위한 JSON-RPC 모드:

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

JSON 프로토콜은 RPC documentation를 참조하세요.

RPC 모드 대안

SDK를 사용하여 빌드하지 않고 하위 프로세스 기반 통합을 수행하려면 CLI를 직접 사용하세요.

pi --mode rpc --no-session

JSON 프로토콜은 RPC documentation를 참조하세요.

SDK는 다음과 같은 경우에 선호됩니다.

  • 유형 안전성을 원합니다
  • 당신도 같은 Node.js 과정을 밟고 있습니다
  • 에이전트 상태에 직접 액세스해야 합니다.
  • 도구/확장 프로그램을 프로그래밍 방식으로 사용자 정의하려는 경우

RPC 모드는 다음과 같은 경우에 선호됩니다:

  • 다른 언어에서 통합하고 있습니다.
  • 프로세스 격리를 원합니다
  • 언어에 구애받지 않는 클라이언트를 구축하고 있습니다.

수출

주요 진입점은 다음을 내보냅니다.

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

확장 유형에 대해서는 extensions.md 전체 API를 참조하세요.