MusePi

SDK

The SDK is the in-process integration surface for @musepi/pi-coding-agent. Use it when you want direct access to agent state, event streaming, tool wiring, and session control from your own Bun/Node process.

If you need cross-language/process isolation, use RPC mode instead.

Installation

bun add @musepi/pi-coding-agent

Entry points

@musepi/pi-coding-agent exports the SDK APIs from the package root (and also via @musepi/pi-coding-agent/sdk).

Core exports for embedders:

Quick start (auto-discovery defaults)

import { createAgentSession } from "@musepi/pi-coding-agent";

const { session, modelFallbackMessage } = await createAgentSession();

if (modelFallbackMessage) {
  process.stderr.write(`${modelFallbackMessage}\n`);
}

const unsubscribe = session.subscribe((event) => {
  if (
    event.type === "message_update" &&
    event.assistantMessageEvent.type === "text_delta"
  ) {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await session.prompt("Summarize this repository in 3 bullets.");
unsubscribe();
await session.dispose();

What createAgentSession() discovers by default

createAgentSession() follows “provide to override, omit to discover”.

If omitted, it resolves:

Required vs optional inputs

Typically you must provide only what you want to control:

Session manager behavior (persistent vs in-memory)

AgentSession always uses a SessionManager; behavior depends on which factory you use.

File-backed (default)

import { createAgentSession, SessionManager } from "@musepi/pi-coding-agent";

const { session } = await createAgentSession({
  sessionManager: SessionManager.create(process.cwd()),
});

console.log(session.sessionFile); // absolute .jsonl path

In-memory

import { createAgentSession, SessionManager } from "@musepi/pi-coding-agent";

const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
});

console.log(session.sessionFile); // undefined

Resume/open/list helpers

import { SessionManager } from "@musepi/pi-coding-agent";

const recent = await SessionManager.continueRecent(process.cwd());
const listed = await SessionManager.list(process.cwd());
const opened = listed[0] ? await SessionManager.open(listed[0].path) : null;

Model and auth wiring

createAgentSession() uses ModelRegistry + AuthStorage for model selection and API key resolution.

Explicit wiring

import {
  createAgentSession,
  discoverAuthStorage,
  ModelRegistry,
  SessionManager,
} from "@musepi/pi-coding-agent";

const authStorage = await discoverAuthStorage();
const modelRegistry = new ModelRegistry(authStorage);
await modelRegistry.refresh();

const available = modelRegistry.getAvailable();
if (available.length === 0)
  throw new Error("No authenticated models available");

const { session } = await createAgentSession({
  authStorage,
  modelRegistry,
  model: available[0],
  thinkingLevel: "medium",
  sessionManager: SessionManager.inMemory(),
});

Selection order when model is omitted

When no explicit model/modelPattern is provided:

  1. restore model from existing session (if restorable + key available)
  2. settings default model role (default)
  3. first available model with valid auth

If restore fails, modelFallbackMessage explains fallback.

Auth priority

AuthStorage.getApiKey(...) resolves in this order:

  1. runtime override (setRuntimeApiKey, used by CLI --api-key)
  2. config-sourced API key override (models.yml provider apiKey)
  3. stored OAuth credential, including refresh when needed
  4. API key persisted by a successful /login
  5. provider environment variables
  6. other stored API-key credential in agent.db / broker-backed storage
  7. custom-provider resolver fallback

Event subscription model

Subscribe with session.subscribe(listener); it returns an unsubscribe function.

const unsubscribe = session.subscribe((event) => {
  switch (event.type) {
    case "agent_start":
    case "turn_start":
    case "tool_execution_start":
      break;
    case "message_update":
      if (event.assistantMessageEvent.type === "text_delta") {
        process.stdout.write(event.assistantMessageEvent.delta);
      }
      break;
  }
});

AgentSessionEvent includes core AgentEvent plus session-level events:

Prompt lifecycle

session.prompt(text, options?) is the primary entry point.

Behavior:

  1. optional command/template expansion (/ commands, custom commands, file slash commands, prompt templates)
  2. if currently streaming:
    • streamingBehavior: "steer" | "followUp" chooses how prompt() queues
    • extension sendUserMessage(content) defaults to steer when deliverAs is omitted
    • queued messages are preserved instead of throwing work away
  3. if idle:
    • validates model + API key
    • appends user message
    • starts agent turn

Related APIs:

Tools and extension integration

Built-ins and filtering

const { session } = await createAgentSession({
  toolNames: ["read", "search", "find", "write"],
  requireYieldTool: true,
});

Extensions

Runtime tool set changes

AgentSession supports runtime activation updates:

System prompt is rebuilt to reflect active tool changes.

Discovery helpers

Use these when you want partial control without recreating internal discovery logic:

Subagent-oriented options

For SDK consumers building orchestrators (similar to task executor flow):

These are optional for normal single-agent embedding.

createAgentSession() return value

type CreateAgentSessionResult = {
  session: AgentSession;
  extensionsResult: LoadExtensionsResult;
  setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
  mcpManager?: MCPManager;
  modelFallbackMessage?: string;
  lspServers?: Array<{
    name: string;
    status: "connecting" | "ready" | "error" | "available";
    fileTypes: string[];
    error?: string;
  }>;
  eventBus: EventBus;
};

Use setToolUIContext(...) only if your embedder provides UI capabilities that tools/extensions should call into.

Startup performance

createAgentSession() runs two background optimizations to overlap I/O with the rest of session setup:

Minimal controlled embed example

import {
  createAgentSession,
  discoverAuthStorage,
  ModelRegistry,
  SessionManager,
  Settings,
} from "@musepi/pi-coding-agent";

const authStorage = await discoverAuthStorage();
const modelRegistry = new ModelRegistry(authStorage);
await modelRegistry.refresh();

const settings = Settings.isolated({
  "compaction.enabled": true,
  "retry.enabled": true,
});

const { session } = await createAgentSession({
  authStorage,
  modelRegistry,
  settings,
  sessionManager: SessionManager.inMemory(),
  toolNames: ["read", "search", "find", "edit", "write"],
  enableMCP: false,
  enableLsp: true,
});

session.subscribe((event) => {
  if (
    event.type === "message_update" &&
    event.assistantMessageEvent.type === "text_delta"
  ) {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await session.prompt("Find all TODO comments in this repo and propose fixes.");
await session.dispose();