AgentSessionRuntime owns an AgentSession and its cwd-scoped services; the createAgentSession() SDK entry point from @earendil-works/pi-coding-agent orchestrates resource loading, model selection with fallback, tool setup, and thinking-level clamping to build a ready-to-use session. Session replacement methods rebuild the runtime from scratch when working directory or configuration changes; callers must re-subscribe to runtime.session after any replacement, and streaming sessions require explicit streamingBehavior to accept new prompts.
packages/coding-agent/src/core/index.ts is the barrel export for all core shared modules used across run modes, including AgentSession, AgentSessionRuntime, AgentSessionServices, extension types, EventBus, bash executor, compaction, RLM subagent runtime, and session utilities.[1] packages/coding-agent/src/core/sdk.ts is the primary programmatic entry point for creating agent sessions, exporting createAgentSession, createAgentSessionFromServices, createAgentSessionRuntime, and related types.[2] The packages/coding-agent package exposes two public exports: the root . (index) and ./hooks (hooks module), both shipping types and ES module builds from the dist/ directory.[3] The SDK is distributed under the npm package name @earendil-works/pi-coding-agent, installed via npm install @earendil-works/pi-coding-agent.[4]
createAgentSession() is the primary SDK entry point in @earendil-works/pi-coding-agent; with no arguments it discovers skills, extensions, tools, and context files from cwd and ~/.pi/agent, and chooses the model from settings or the first available provider.[5] createAgentSession() uses a ResourceLoader to supply extensions, skills, prompt templates, themes, and context files; if none is provided it defaults to DefaultResourceLoader with standard discovery, calling reload() immediately so resources are available before the session is built.[4][2] createAgentSession attempts to restore the model from an existing session's stored provider/modelId; if that model is unavailable or unconfigured, it sets modelFallbackMessage and falls back to findInitialModel.[2] The thinkingLevel option defaults to the value stored in the existing session (if any), then to the settings default, then to the DEFAULT_THINKING_LEVEL constant; it is clamped to the chosen model's actual capabilities via clampThinkingLevel, so requesting 'high' on a model that supports only 'medium' silently downgrades.[2] When no model is available at all, createAgentSession sets thinkingLevel to "off" rather than throwing.[2] The serviceTier option automatically downgrades from 'priority' to 'default' when the selected model does not support fast mode (checked via supportsFastMode).[2] The noTools option accepts "all" (no tools enabled) or "builtin" (disable only the default built-in ipython tool, keeping extension/custom tools); when neither tools nor noTools is specified, initialActiveToolNames defaults to ["ipython"], making IPython the sole initially active tool.[2] When settingsManager.getBlockImages() is enabled, createAgentSession replaces all ImageContent items in messages with a { type: 'text', text: 'Image reading is disabled.' } placeholder at the convertToLlm wrapper level — checked dynamically so mid-session setting changes take effect.[2]
CreateAgentSessionResult carries three fields: session (the AgentSession), extensionsResult (for UI context setup), and an optional modelFallbackMessage warning string emitted when the session was restored with a different model than saved.[2] packages/coding-agent/src/core/sdk.ts re-exports the tool factory functions createBashTool, createEditTool, createIpythonTool, and withFileMutationQueue for callers who need custom cwd-scoped tool instances.[2]
AgentSessionRuntime owns the current AgentSession plus its cwd-bound services; session replacement methods tear down the current runtime first, then create and apply the next — if creation fails, the error is propagated to the caller, which is responsible for user-facing error handling.[6] CreateAgentSessionRuntimeFactory closes over process-global fixed inputs, recreates cwd-bound services for the effective cwd, resolves session options against those services, and finally creates the AgentSession. The factory pattern enables the runtime to rebuild state when the working directory changes.[6][4] CreateAgentSessionRuntimeResult extends CreateAgentSessionResult and adds services: AgentSessionServices and diagnostics: AgentSessionRuntimeDiagnostic[].[6] AgentSessionRuntimeMetadata records the runtime's kind ("top-level" or "subagent"), creation timestamp, parent session references, RLM child/parent node IDs, rehydration state, initial prompt, spawn code, and session directory.[6]
The teardownCurrent method flushes agent trace uploads, runs beforeSessionInvalidate, awaits the kernel's final snapshot flush via session.disposeAsync(), and disposes all hosted subagent runtimes — in that order.[6] If building a replacement AgentSession runtime throws, AgentSessionRuntime releases the uncommitted session lease before re-throwing, preventing lease leaks on failed transitions.[6] AgentSessionRuntime.setRuntimeEnvScope installs a host-provided async scope that wraps every runtime rebuild (new/switch/fork/import and subagent creation), during which extensions re-load; the daemon uses it to apply the session's client env for load-time captures.[6] AgentSessionRuntime.setBeforeSessionInvalidate installs a synchronous callback that runs after session_shutdown handlers finish but before the current session is invalidated — used for host-owned TUI teardown that must not yield to the event loop.[6] A session lease is a lock that prevents two runtimes from writing to the same session file simultaneously.
createAgentSessionRuntime() creation failures throw; the caller is responsible for handling them, and diagnostics are exposed on runtime.diagnostics.[4]
Calling session.prompt() during streaming without specifying streamingBehavior throws an error; callers must use steer(), followUp(), or pass streamingBehavior: "steer" | "followUp" in PromptOptions.[4] PromptOptions.preflightResult is a callback called once per prompt() invocation — true when the prompt was accepted, queued, or handled; false when rejected before acceptance. Failures after acceptance are reported through the normal event and message stream, not through preflightResult(false).[4] AgentSession.steer() queues a steering message for delivery after the current assistant turn finishes its tool calls; followUp() queues a message that is only delivered when the agent fully stops.[4] AgentSession.navigateTree() performs in-place tree navigation within the current session file, accepting a targetId and options for summarize, customInstructions, replaceInstructions, and label; it returns { editorText?, cancelled }. Session navigation is covered further on the Sessions and navigation page.[4] AgentSession.agent.state.messages and agent.state.tools support direct array replacement (copies the top-level array), enabling use cases like branching or tool set swapping.[4] Agent.continue in packages/agent/src/agent.ts returns typed result codes instead of throwing or returning generic errors when preconditions are unmet (e.g., compaction required, session not continuable), letting call sites branch on the specific failure reason.
After calling runtime.newSession() (or any session-replacing operation), callers must unsubscribe from the old AgentSession, retrieve runtime.session again, and re-subscribe to the new one.[4]
packages/coding-agent/src/index.ts is the package's top-level barrel file, re-exporting the entire public API surface: session management, auth, compaction, extension system, skills, tools, daemon protocol, interactive mode, UI components, and theme utilities.[7] AgentSession and its associated event/config types (AgentSessionConfig, AgentSessionEvent, AgentSessionEventListener, ModelCycleResult, PromptOptions) are exported from ./core/agent-session.js, forming the core session lifecycle surface.[7] The compaction API — compact, shouldCompact, generateBranchSummary, findCutPoint, calculateContextTokens, estimateTokens, and related types — is exported from ./core/compaction/index.js. Compaction behaviour is covered on the Compaction page.[7] AuthStorage and related credential types (ApiKeyCredential, OAuthCredential, AuthStatus) plus storage backend classes (FileAuthStorageBackend, InMemoryAuthStorageBackend) are exported from ./core/auth-storage.js. Authentication configuration is covered on the Providers and authentication page.[7] getAgentDir and VERSION are exported from ./config.js, providing the config-path and package-version entry points for the coding-agent package.[7] The extension system surface includes createExtensionRuntime, discoverAndLoadExtensions, ExtensionRunner, defineTool, wrapRegisteredTool, and type-guard helpers isBashToolResult, isEditToolResult, isIpythonToolResult, isToolCallEventType, all exported from ./core/extensions/index.js.[7]
Minimal SDK usage: session.subscribe(callback) streams events; session.prompt(text) runs a turn; session.state.messages holds the conversation history after each turn:
import { createAgentSession } from "@earendil-works/pi-coding-agent";
const { session } = await createAgentSession();
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?");
session.state.messages.forEach((msg) => console.log(msg));
Example: continuing a previous session with createAgentSession and handling a model fallback warning.
// Continue previous session
const { session, modelFallbackMessage } = await createAgentSession({
continueSession: true,
});
Example: full-control createAgentSession usage with an explicit DefaultResourceLoader, restricted tool list, and in-memory session manager.
const loader = new DefaultResourceLoader({
cwd: process.cwd(),
agentDir: getAgentDir(),
settingsManager: SettingsManager.create(),
});
await loader.reload();
const { session } = await createAgentSession({
model: myModel,
tools: ["ipython"],
resourceLoader: loader,
sessionManager: SessionManager.inMemory(),
});
The canonical minimal SDK setup — creating AuthStorage and ModelRegistry, calling createAgentSession() with an in-memory SessionManager, subscribing to message_update events, and calling session.prompt():
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@earendil-works/pi-coding-agent";
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
});
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?");
Sources