Changelog

Release notes and changes from the Pi changelog.

Release notes

Pi 0.37.5

New version of pi. Download from npm or view release on GitHub.

Added

  • ExtensionAPI: setModel(), getThinkingLevel(), setThinkingLevel() methods for extensions to change model and thinking level at runtime (#509)
  • Exported truncation utilities for custom tools: truncateHead, truncateTail, truncateLine, formatSize, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, TruncationOptions, TruncationResult
  • New example truncated-tool.ts demonstrating proper output truncation with custom rendering for extensions
  • New example preset.ts demonstrating preset configurations with model/thinking/tools switching (#347)
  • Documentation for output truncation best practices in docs/extensions.md
  • Exported all UI components for extensions: ArminComponent, AssistantMessageComponent, BashExecutionComponent, BorderedLoader, BranchSummaryMessageComponent, CompactionSummaryMessageComponent, CustomEditor, CustomMessageComponent, DynamicBorder, ExtensionEditorComponent, ExtensionInputComponent, ExtensionSelectorComponent, FooterComponent, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, SessionSelectorComponent, SettingsSelectorComponent, ShowImagesSelectorComponent, ThemeSelectorComponent, ThinkingSelectorComponent, ToolExecutionComponent, TreeSelectorComponent, UserMessageComponent, UserMessageSelectorComponent, plus utilities renderDiff, truncateToVisualLines
  • docs/tui.md: Common Patterns section with copy-paste code for SelectList, BorderedLoader, SettingsList, setStatus, setWidget, setFooter
  • docs/tui.md: Key Rules section documenting critical patterns for extension UI development
  • docs/extensions.md: Exhaustive example links for all ExtensionAPI methods and events
  • System prompt now references docs/tui.md for TUI component development

Read more

Release notes

Pi 0.37.4

New version of pi. Download from npm or view release on GitHub.

Added

  • Session picker (pi -r) and --session flag now support searching/resuming by session ID (UUID prefix) (#495 by @arunsathiya)
  • Extensions can now replace the startup header with ctx.ui.setHeader(), see examples/extensions/custom-header.ts (#500 by @tudoroancea)

Changed

  • Startup help text: fixed misleading "ctrl+k to delete line" to "ctrl+k to delete to end"
  • Startup help text and /hotkeys: added !! shortcut for running bash without adding output to context

Read more

Release notes

Pi 0.37.3

New version of pi. Download from npm or view release on GitHub.

Added

  • Extensions can now replace the footer with ctx.ui.setFooter(), see examples/extensions/custom-footer.ts (#481)
  • Session ID is now forwarded to LLM providers for session-based caching (used by OpenAI Codex for prompt caching).
  • Added blockImages setting to prevent images from being sent to LLM providers (#492 by @jsinge97)
  • Extensions can now send user messages via pi.sendUserMessage() (#483)

Read more

Release notes

Pi 0.37.2

New version of pi. Download from npm or view release on GitHub.

Fixed

  • Extension directories in settings.json now respect package.json manifests, matching global extension behavior (#480 by @prateekmedia)
  • Share viewer: deep links now scroll to the target message when opened via /share
  • Bash tool now handles spawn errors gracefully instead of crashing the agent (missing cwd, invalid shell path) (#479 by @robinwander)

Read more

Release notes

Pi 0.37.0

New version of pi. Download from npm or view release on GitHub.

Added

  • Share viewer: copy-link button on messages to share URLs that navigate directly to a specific message (#477 by @lockmeister)
  • Extension example: add claude-rules to load .claude/rules/ entries into the system prompt (#461 by @vaayne)
  • Headless OAuth login: all providers now show paste input for manual URL/code entry, works over SSH without DISPLAY (#428 by @ben-vargas, #468 by @crcatala)

Changed

  • OAuth login UI now uses dedicated dialog component with consistent borders
  • Assume truecolor support for all terminals except dumb, empty, or linux (fixes colors over SSH)
  • OpenAI Codex clean-up: removed per-thinking-level model variants, thinking level is now set separately and the provider clamps to what each model supports internally (initial implementation in #472 by @ben-vargas)

Read more

Release notes

Pi 0.35.0

New version of pi. Download from npm or view release on GitHub.

Changes

This release unifies hooks and custom tools into a single "extensions" system and renames "slash commands" to "prompt templates". (#454)

Before migrating, read:

Extensions Migration

Hooks and custom tools are now unified as extensions. Both were TypeScript modules exporting a factory function that receives an API object. Now there's one concept, one discovery location, one CLI flag, one settings.json entry.

Automatic migration:

  • commands/ directories are automatically renamed to prompts/ on startup (both ~/.pi/agent/commands/ and .pi/commands/)

Manual migration required:

  1. Move files from hooks/ and tools/ directories to extensions/ (deprecation warnings shown on startup)
  2. Update imports and type names in your extension code
  3. Update settings.json if you have explicit hook and custom tool paths configured

Directory changes:

# Before
~/.pi/agent/hooks/*.ts       →  ~/.pi/agent/extensions/*.ts
~/.pi/agent/tools/*.ts       →  ~/.pi/agent/extensions/*.ts
.pi/hooks/*.ts               →  .pi/extensions/*.ts
.pi/tools/*.ts               →  .pi/extensions/*.ts

Extension discovery rules (in extensions/ directories):

  1. Direct files: extensions/*.ts or *.js → loaded directly
  2. Subdirectory with index: extensions/myext/index.ts → loaded as single extension
  3. Subdirectory with package.json: extensions/myext/package.json with "pi" field → loads declared paths
// extensions/my-package/package.json
{
  "name": "my-extension-package",
  "dependencies": { "zod": "^3.0.0" },
  "pi": {
    "extensions": ["./src/main.ts", "./src/tools.ts"]
  }
}

No recursion beyond one level. Complex packages must use the package.json manifest. Dependencies are resolved via jiti, and extensions can be published to and installed from npm.

Type renames:

  • HookAPIExtensionAPI
  • HookContextExtensionContext
  • HookCommandContextExtensionCommandContext
  • HookUIContextExtensionUIContext
  • CustomToolAPIExtensionAPI (merged)
  • CustomToolContextExtensionContext (merged)
  • CustomToolUIContextExtensionUIContext
  • CustomToolToolDefinition
  • CustomToolFactoryExtensionFactory
  • HookMessageCustomMessage

Import changes:

// Before (hook)
import type { HookAPI, HookContext } from "@mariozechner/pi-coding-agent";
export default function (pi: HookAPI) { ... }

// Before (custom tool)
import type { CustomToolFactory } from "@mariozechner/pi-coding-agent";
const factory: CustomToolFactory = (pi) => ({ name: "my_tool", ... });
export default factory;

// After (both are now extensions)
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
export default function (pi: ExtensionAPI) {
  pi.on("tool_call", async (event, ctx) => { ... });
  pi.registerTool({ name: "my_tool", ... });
}

Custom tools now have full context access. Tools registered via pi.registerTool() now receive the same ctx object that event handlers receive. Previously, custom tools had limited context. Now all extension code shares the same capabilities:

  • pi.registerTool() - Register tools the LLM can call
  • pi.registerCommand() - Register commands like /mycommand
  • pi.registerShortcut() - Register keyboard shortcuts (shown in /hotkeys)
  • pi.registerFlag() - Register CLI flags (shown in --help)
  • pi.registerMessageRenderer() - Custom TUI rendering for message types
  • pi.on() - Subscribe to lifecycle events (tool_call, session_start, etc.)
  • pi.sendMessage() - Inject messages into the conversation
  • pi.appendEntry() - Persist custom data in session (survives restart/branch)
  • pi.exec() - Run shell commands
  • pi.getActiveTools() / pi.setActiveTools() - Dynamic tool enable/disable
  • pi.getAllTools() - List all available tools
  • pi.events - Event bus for cross-extension communication
  • ctx.ui.confirm() / select() / input() - User prompts
  • ctx.ui.notify() - Toast notifications
  • ctx.ui.setStatus() - Persistent status in footer (multiple extensions can set their own)
  • ctx.ui.setWidget() - Widget display above editor
  • ctx.ui.setTitle() - Set terminal window title
  • ctx.ui.custom() - Full TUI component with keyboard handling
  • ctx.ui.editor() - Multi-line text editor with external editor support
  • ctx.sessionManager - Read session entries, get branch history

Settings changes:

// Before
{
  "hooks": ["./my-hook.ts"],
  "customTools": ["./my-tool.ts"]
}

// After
{
  "extensions": ["./my-extension.ts"]
}

CLI changes:

# Before
pi --hook ./safety.ts --tool ./todo.ts

# After
pi --extension ./safety.ts -e ./todo.ts

Prompt Templates Migration

"Slash commands" (markdown files defining reusable prompts invoked via /name) are renamed to "prompt templates" to avoid confusion with extension-registered commands.

Automatic migration: The commands/ directory is automatically renamed to prompts/ on startup (if prompts/ doesn't exist). Works for both regular directories and symlinks.

Directory changes:

~/.pi/agent/commands/*.md    →  ~/.pi/agent/prompts/*.md
.pi/commands/*.md            →  .pi/prompts/*.md

SDK type renames:

  • FileSlashCommandPromptTemplate
  • LoadSlashCommandsOptionsLoadPromptTemplatesOptions

SDK function renames:

  • discoverSlashCommands()discoverPromptTemplates()
  • loadSlashCommands()loadPromptTemplates()
  • expandSlashCommand()expandPromptTemplate()
  • getCommandsDir()getPromptsDir()

SDK option renames:

  • CreateAgentSessionOptions.slashCommands.promptTemplates
  • AgentSession.fileCommands.promptTemplates
  • PromptOptions.expandSlashCommands.expandPromptTemplates

SDK Migration

Discovery functions:

  • discoverAndLoadHooks()discoverAndLoadExtensions()
  • discoverAndLoadCustomTools() → merged into discoverAndLoadExtensions()
  • loadHooks()loadExtensions()
  • loadCustomTools() → merged into loadExtensions()

Runner and wrapper:

  • HookRunnerExtensionRunner
  • wrapToolsWithHooks()wrapToolsWithExtensions()
  • wrapToolWithHooks()wrapToolWithExtensions()

CreateAgentSessionOptions:

  • .hooks → removed (use .additionalExtensionPaths for paths)
  • .additionalHookPaths.additionalExtensionPaths
  • .preloadedHooks.preloadedExtensions
  • .customTools type changed: Array<{ path?; tool: CustomTool }>ToolDefinition[]
  • .additionalCustomToolPaths → merged into .additionalExtensionPaths
  • .slashCommands.promptTemplates

AgentSession:

  • .hookRunner.extensionRunner
  • .fileCommands.promptTemplates
  • .sendHookMessage().sendCustomMessage()

Session Migration

Automatic. Session version bumped from 2 to 3. Existing sessions are migrated on first load:

  • Message role "hookMessage""custom"

Breaking Changes

  • Settings: hooks and customTools arrays replaced with single extensions array
  • CLI: --hook and --tool flags replaced with --extension / -e
  • Directories: hooks/, tools/extensions/; commands/prompts/
  • Types: See type renames above
  • SDK: See SDK migration above

Changed

  • Extensions can have their own package.json with dependencies (resolved via jiti)
  • Documentation: docs/hooks.md and docs/custom-tools.md merged into docs/extensions.md
  • Examples: examples/hooks/ and examples/custom-tools/ merged into examples/extensions/
  • README: Extensions section expanded with custom tools, commands, events, state persistence, shortcuts, flags, and UI examples
  • SDK: customTools option now accepts ToolDefinition[] directly (simplified from Array<{ path?, tool }>)
  • SDK: extensions option accepts ExtensionFactory[] for inline extensions
  • SDK: additionalExtensionPaths replaces both additionalHookPaths and additionalCustomToolPaths

Read more

Release notes

Pi 0.34.0

New version of pi. Download from npm or view release on GitHub.

Added

  • Hook API: pi.getActiveTools() and pi.setActiveTools(toolNames) for dynamically enabling/disabling tools from hooks
  • Hook API: pi.getAllTools() to enumerate all configured tools (built-in via --tools or default, plus custom tools)
  • Hook API: pi.registerFlag(name, options) and pi.getFlag(name) for hooks to register custom CLI flags (parsed automatically)
  • Hook API: pi.registerShortcut(shortcut, options) for hooks to register custom keyboard shortcuts using KeyId (e.g., Key.shift("p")). Conflicts with built-in shortcuts are skipped, conflicts between hooks logged as warnings.
  • Hook API: ctx.ui.setWidget(key, content) for status displays above the editor. Accepts either a string array or a component factory function.
  • Hook API: theme.strikethrough(text) for strikethrough text styling
  • Hook API: before_agent_start handlers can now return systemPromptAppend to dynamically append text to the system prompt for that turn. Multiple hooks' appends are concatenated.
  • Hook API: before_agent_start handlers can now return multiple messages (all are injected, not just the first)
  • /hotkeys command now shows hook-registered shortcuts in a separate "Hooks" section
  • New example hook: plan-mode.ts - Claude Code-style read-only exploration mode:
    • Toggle via /plan command, Shift+P shortcut, or --plan CLI flag
    • Read-only tools: read, bash, grep, find, ls (no edit/write)
    • Bash commands restricted to non-destructive operations (blocks rm, mv, git commit, npm install, etc.)
    • Interactive prompt after each response: execute plan, stay in plan mode, or refine
    • Todo list widget showing progress with checkboxes and strikethrough for completed items
    • Each todo has a unique ID; agent marks items done by outputting [DONE:id]
    • Progress updates via agent_end hook (parses completed items from final message)
    • /todos command to view current plan progress
    • Shows ⏸ plan indicator in footer when in plan mode, 📋 2/5 when executing
    • State persists across sessions (including todo progress)
  • New example hook: tools.ts - Interactive /tools command to enable/disable tools with session persistence
  • New example hook: pirate.ts - Demonstrates systemPromptAppend to make the agent speak like a pirate
  • Tool registry now contains all built-in tools (read, bash, edit, write, grep, find, ls) even when --tools limits the initially active set. Hooks can enable any tool from the registry via pi.setActiveTools().
  • System prompt now automatically rebuilds when tools change via setActiveTools(), updating tool descriptions and guidelines to match the new tool set
  • Hook errors now display full stack traces for easier debugging
  • Event bus (pi.events) for tool/hook communication: shared pub/sub between custom tools and hooks
  • Custom tools now have pi.sendMessage() to send messages directly to the agent session without needing the event bus
  • sendMessage() supports deliverAs: "nextTurn" to queue messages for the next user prompt

Changed

  • Removed image placeholders after copy & paste, replaced with inserting image file paths directly. (#442 by @mitsuhiko)

Read more

Release notes

Pi 0.33.0

New version of pi. Download from npm or view release on GitHub.

Breaking Changes

  • Key detection functions removed from @mariozechner/pi-tui: All isXxx() key detection functions (isEnter(), isEscape(), isCtrlC(), etc.) have been removed. Use matchesKey(data, keyId) instead (e.g., matchesKey(data, "enter"), matchesKey(data, "ctrl+c")). This affects hooks and custom tools that use ctx.ui.custom() with keyboard input handling. (#405)

Added

  • Clipboard image paste support via Ctrl+V. Images are saved to a temp file and attached to the message. Works on macOS, Windows, and Linux (X11). (#419)
  • Configurable keybindings via ~/.pi/agent/keybindings.json. All keyboard shortcuts (editor navigation, deletion, app actions like model cycling, etc.) can now be customized. Supports multiple bindings per action. (#405 by @hjanuschka)
  • /quit and /exit slash commands to gracefully exit the application. Unlike double Ctrl+C, these properly await hook and custom tool cleanup handlers before exiting. (#426 by @ben-vargas)

Read more

Release notes

Pi 0.32.3

New version of pi. Download from npm or view release on GitHub.

Fixed

  • --list-models no longer shows Google Vertex AI models without explicit authentication configured
  • JPEG/GIF/WebP images not displaying in terminals using Kitty graphics protocol (Kitty, Ghostty, WezTerm). The protocol requires PNG format, so non-PNG images are now converted before display.
  • Version check URL typo preventing update notifications from working (#423 by @skuridin)
  • Large images exceeding Anthropic's 5MB limit now retry with progressive quality/size reduction (#424 by @mitsuhiko)

Read more

Release notes

Pi 0.32.2

New version of pi. Download from npm or view release on GitHub.

Added

  • $ARGUMENTS syntax for custom slash commands as alternative to $@ for all arguments joined. Aligns with patterns used by Claude, Codex, and OpenCode. Both syntaxes remain fully supported. (#418 by @skuridin)

Changed

  • Slash commands and hook commands now work during streaming: Previously, using a slash command or hook command while the agent was streaming would crash with "Agent is already processing". Now:
    • Hook commands execute immediately (they manage their own LLM interaction via pi.sendMessage())
    • File-based slash commands are expanded and queued via steer/followUp
    • steer() and followUp() now expand file-based slash commands and error on hook commands (hook commands cannot be queued)
    • prompt() accepts new streamingBehavior option ("steer" or "followUp") to specify queueing behavior during streaming
    • RPC prompt command now accepts optional streamingBehavior field (#420)

Read more

Release notes

Pi 0.32.1

New version of pi. Download from npm or view release on GitHub.

Added

  • Shell commands without context contribution: use !!command to execute a bash command that is shown in the TUI and saved to session history but excluded from LLM context. Useful for running commands you don't want the AI to see. (#414)

Read more

Release notes

Pi 0.32.0

New version of pi. Download from npm or view release on GitHub.

Breaking Changes

  • Queue API replaced with steer/followUp: The queueMessage() method has been split into two methods with different delivery semantics (#403):
    • steer(text): Interrupts the agent mid-run (Enter while streaming). Delivered after current tool execution.
    • followUp(text): Waits until the agent finishes (Alt+Enter while streaming). Delivered only when agent stops.
  • Settings renamed: queueMode setting renamed to steeringMode. Added new followUpMode setting. Old settings.json files are migrated automatically.
  • AgentSession methods renamed:
    • queueMessage()steer() and followUp()
    • queueMode getter → steeringMode and followUpMode getters
    • setQueueMode()setSteeringMode() and setFollowUpMode()
    • queuedMessageCountpendingMessageCount
    • getQueuedMessages()getSteeringMessages() and getFollowUpMessages()
    • clearQueue() now returns { steering: string[], followUp: string[] }
    • hasQueuedMessages()hasPendingMessages()
  • Hook API signature changed: pi.sendMessage() second parameter changed from triggerTurn?: boolean to options?: { triggerTurn?, deliverAs? }. Use deliverAs: "followUp" for follow-up delivery. Affects both hooks and internal sendHookMessage() method.
  • RPC API changes:
    • queue_message command → steer and follow_up commands
    • set_queue_mode command → set_steering_mode and set_follow_up_mode commands
    • RpcSessionState.queueModesteeringMode and followUpMode
  • Settings UI: "Queue mode" setting split into "Steering mode" and "Follow-up mode"

Added

  • Configurable double-escape action: choose whether double-escape with empty editor opens /tree (default) or /branch. Configure via /settings or doubleEscapeAction in settings.json (#404)
  • Vertex AI provider (google-vertex): access Gemini models via Google Cloud Vertex AI using Application Default Credentials (#300 by @default-anton)
  • Built-in provider overrides in models.json: override just baseUrl to route a built-in provider through a proxy while keeping all its models, or define models to fully replace the provider (#406 by @yevhen)
  • Automatic image resizing: images larger than 2000x2000 are resized for better model compatibility. Original dimensions are injected into the prompt. Controlled via /settings or images.autoResize in settings.json. (#402 by @mitsuhiko)
  • Alt+Enter keybind to queue follow-up messages while agent is streaming
  • Theme and ThemeColor types now exported for hooks using ctx.ui.custom()
  • Terminal window title now displays "pi - dirname" to identify which project session you're in (#407 by @kaofelix)

Changed

  • Editor component now uses word wrapping instead of character-level wrapping for better readability (#382 by @nickseelert)

Read more

Release notes

Pi 0.31.1

New version of pi. Download from npm or view release on GitHub.

Fixed

  • Model selector no longer allows negative index when pressing arrow keys before models finish loading (#398 by @mitsuhiko)
  • Type guard functions (isBashToolResult, etc.) now exported at runtime, not just in type declarations (#397)

Read more

Release notes

Pi 0.31.0

New version of pi. Download from npm or view release on GitHub.

Changes

This release introduces session trees for in-place branching, major API changes to hooks and custom tools, and structured compaction with file tracking.

Session Tree

Sessions now use a tree structure with id/parentId fields. This enables in-place branching: navigate to any previous point with /tree, continue from there, and switch between branches while preserving all history in a single file.

Existing sessions are automatically migrated (v1 → v2) on first load. No manual action required.

New entry types: BranchSummaryEntry (context from abandoned branches), CustomEntry (hook state), CustomMessageEntry (hook-injected messages), LabelEntry (bookmarks).

See docs/session.md for the file format and SessionManager API.

Hooks Migration

The hooks API has been restructured with more granular events and better session access.

Type renames:

  • HookEventContextHookContext
  • HookCommandContext is now a new interface extending HookContext with session control methods

Event changes:

  • The monolithic session event is now split into granular events: session_start, session_before_switch, session_switch, session_before_branch, session_branch, session_before_compact, session_compact, session_shutdown
  • session_before_switch and session_switch events now include reason: "new" | "resume" to distinguish between /new and /resume
  • New session_before_tree and session_tree events for /tree navigation (hook can provide custom branch summary)
  • New before_agent_start event: inject messages before the agent loop starts
  • New context event: modify messages non-destructively before each LLM call
  • Session entries are no longer passed in events. Use ctx.sessionManager.getEntries() or ctx.sessionManager.getBranch() instead

API changes:

  • pi.send(text, attachments?)pi.sendMessage(message, triggerTurn?) (creates CustomMessageEntry)
  • New pi.appendEntry(customType, data?) for hook state persistence (not in LLM context)
  • New pi.registerCommand(name, options) for custom slash commands (handler receives HookCommandContext)
  • New pi.registerMessageRenderer(customType, renderer) for custom TUI rendering
  • New ctx.isIdle(), ctx.abort(), ctx.hasQueuedMessages() for agent state (available in all events)
  • New ctx.ui.editor(title, prefill?) for multi-line text editing with Ctrl+G external editor support
  • New ctx.ui.custom(component) for full TUI component rendering with keyboard focus
  • New ctx.ui.setStatus(key, text) for persistent status text in footer (multiple hooks can set their own)
  • New ctx.ui.theme getter for styling text with theme colors
  • ctx.exec() moved to pi.exec()
  • ctx.sessionFilectx.sessionManager.getSessionFile()
  • New ctx.modelRegistry and ctx.model for API key resolution

HookCommandContext (slash commands only):

  • ctx.waitForIdle() - wait for agent to finish streaming
  • ctx.newSession(options?) - create new sessions with optional setup callback
  • `ctx.fork(entryId) - fork from a specific entry, creating a new session file
  • ctx.navigateTree(targetId, options?) - navigate the session tree

These methods are only on HookCommandContext (not HookContext) because they can deadlock if called from event handlers that run inside the agent loop.

Removed:

  • hookTimeout setting (hooks no longer have timeouts; use Ctrl+C to abort)
  • resolveApiKey parameter (use ctx.modelRegistry.getApiKey(model))

See docs/hooks.md and examples/hooks/ for the current API.

Custom Tools Migration

The custom tools API has been restructured to mirror the hooks pattern with a context object.

Type renames:

  • CustomAgentToolCustomTool
  • ToolAPICustomToolAPI
  • ToolContextCustomToolContext
  • ToolSessionEventCustomToolSessionEvent

Execute signature changed:

// Before (v0.30.2)
execute(toolCallId, params, signal, onUpdate)

// After
execute(toolCallId, params, onUpdate, ctx, signal?)

The new ctx: CustomToolContext provides sessionManager, modelRegistry, model, and agent state methods:

  • ctx.isIdle() - check if agent is streaming
  • ctx.hasQueuedMessages() - check if user has queued messages (skip interactive prompts)
  • ctx.abort() - abort current operation (fire-and-forget)

Session event changes:

  • CustomToolSessionEvent now only has reason and previousSessionFile
  • Session entries are no longer in the event. Use ctx.sessionManager.getBranch() or ctx.sessionManager.getEntries() to reconstruct state
  • Reasons: "start" | "switch" | "branch" | "tree" | "shutdown" (no separate "new" reason; /new triggers "switch")
  • dispose() method removed. Use onSession with reason: "shutdown" for cleanup

See docs/custom-tools.md and examples/custom-tools/ for the current API.

SDK Migration

Type changes:

  • CustomAgentToolCustomTool
  • AppMessageAgentMessage
  • sessionFile returns string | undefined (was string | null)
  • model returns Model | undefined (was Model | null)
  • Attachment type removed. Use ImageContent from @mariozechner/pi-ai instead. Add images directly to message content arrays.

AgentSession API:

  • branch(entryIndex: number)branch(entryId: string)
  • getUserMessagesForBranching() returns { entryId, text } instead of { entryIndex, text }
  • reset()newSession(options?) where options has optional parentSession for lineage tracking
  • newSession() and switchSession() now return Promise<boolean> (false if cancelled by hook)
  • New navigateTree(targetId, options?) for in-place tree navigation

Hook integration:

  • New sendHookMessage(message, triggerTurn?) for hook message injection

SessionManager API:

  • Method renames: saveXXX()appendXXX() (e.g., appendMessage, appendCompaction)
  • branchInPlace()branch()
  • reset()newSession(options?) with optional parentSession for lineage tracking
  • createBranchedSessionFromEntries(entries, index)createBranchedSession(leafId)
  • SessionHeader.branchedFromSessionHeader.parentSession
  • saveCompaction(entry)appendCompaction(summary, firstKeptEntryId, tokensBefore, details?)
  • getEntries() now excludes the session header (use getHeader() separately)
  • getSessionFile() returns string | undefined (undefined for in-memory sessions)
  • New tree methods: getTree(), getBranch(), getLeafId(), getLeafEntry(), getEntry(), getChildren(), getLabel()
  • New append methods: appendCustomEntry(), appendCustomMessageEntry(), appendLabelChange()
  • New branch methods: branch(entryId), branchWithSummary()

ModelRegistry (new):

ModelRegistry is a new class that manages model discovery and API key resolution. It combines built-in models with custom models from models.json and resolves API keys via AuthStorage.

import {
  discoverAuthStorage,
  discoverModels,
} from "@mariozechner/pi-coding-agent";

const authStorage = discoverAuthStorage(); // ~/.pi/agent/auth.json
const modelRegistry = discoverModels(authStorage); // + ~/.pi/agent/models.json

// Get all models (built-in + custom)
const allModels = modelRegistry.getAll();

// Get only models with valid API keys
const available = await modelRegistry.getAvailable();

// Find specific model
const model = modelRegistry.find("anthropic", "claude-sonnet-4-20250514");

// Get API key for a model
const apiKey = await modelRegistry.getApiKey(model);

This replaces the old resolveApiKey callback pattern. Hooks and custom tools access it via ctx.modelRegistry.

Renamed exports:

  • messageTransformerconvertToLlm
  • SessionContext alias LoadedSession removed

See docs/sdk.md and examples/sdk/ for the current API.

RPC Migration

Session commands:

  • reset command → new_session command with optional parentSession field

Branching commands:

  • branch command: entryIndexentryId
  • get_branch_messages response: entryIndexentryId

Type changes:

  • Messages are now AgentMessage (was AppMessage)
  • prompt command: attachments field replaced with images field using ImageContent format

Compaction events:

  • auto_compaction_start now includes reason field ("threshold" or "overflow")
  • auto_compaction_end now includes willRetry field
  • compact response includes full CompactionResult (summary, firstKeptEntryId, tokensBefore, details)

See docs/rpc.md for the current protocol.

Structured Compaction

Compaction and branch summarization now use a structured output format:

  • Clear sections: Goal, Progress, Key Information, File Operations
  • File tracking: readFiles and modifiedFiles arrays in details, accumulated across compactions
  • Conversations are serialized to text before summarization to prevent the model from "continuing" them

The before_compact and before_tree hook events allow custom compaction implementations. See docs/compaction.md.

Interactive Mode

/tree command:

  • Navigate the full session tree in-place
  • Search by typing, page with ←/→
  • Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
  • Press l to label entries as bookmarks
  • Selecting a branch switches context and optionally injects a summary of the abandoned branch

Entry labels:

  • Bookmark any entry via /tree → select → l
  • Labels appear in tree view and persist as LabelEntry

Theme changes (breaking for custom themes):

Custom themes must add these new color tokens or they will fail to load:

  • selectedBg: background for selected/highlighted items in tree selector and other components
  • customMessageBg: background for hook-injected messages (CustomMessageEntry)
  • customMessageText: text color for hook messages
  • customMessageLabel: label color for hook messages (the [customType] prefix)

Total color count increased from 46 to 50. See docs/themes.md for the full color list and copy values from the built-in dark/light themes.

Settings:

  • enabledModels: allowlist models in settings.json (same format as --models CLI)

Added

  • ctx.ui.setStatus(key, text) for hooks to display persistent status text in the footer (#385 by @prateekmedia)
  • ctx.ui.theme getter for styling status text and other output with theme colors
  • /share command to upload session as a secret GitHub gist and get a shareable URL via pi.dev (#380)
  • HTML export now includes a tree visualization sidebar for navigating session branches (#375)
  • HTML export supports keyboard shortcuts: Ctrl+T to toggle thinking blocks, Ctrl+O to toggle tool outputs
  • HTML export supports theme-configurable background colors via optional export section in theme JSON (#387 by @mitsuhiko)
  • HTML export syntax highlighting now uses theme colors and matches TUI rendering
  • Snake game example hook: Demonstrates ui.custom(), registerCommand(), and session persistence. See examples/hooks/snake.ts.
  • thinkingText theme token: Configurable color for thinking block text. (#366 by @paulbettner)

Changed

  • Entry IDs: Session entries now use short 8-character hex IDs instead of full UUIDs
  • API key priority: ANTHROPIC_OAUTH_TOKEN now takes precedence over ANTHROPIC_API_KEY
  • HTML export template split into separate files (template.html, template.css, template.js) for easier maintenance

Read more

Release notes

Pi 0.30.1

New version of pi. Download from npm or view release on GitHub.

Fixed

  • Sessions saved to wrong directory: In v0.30.0, sessions were being saved to ~/.pi/agent/ instead of ~/.pi/agent/sessions/<encoded-cwd>/, breaking --resume and /resume. Misplaced sessions are automatically migrated on startup. (#320 by @aliou)
  • Custom system prompts missing context: When using a custom system prompt string, project context files (AGENTS.md), skills, date/time, and working directory were not appended. (#321)

Read more

Release notes

Pi 0.30.0

New version of pi. Download from npm or view release on GitHub.

Breaking Changes

  • SessionManager API: The second parameter of create(), continueRecent(), and list() changed from agentDir to sessionDir. When provided, it specifies the session directory directly (no cwd encoding). When omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/). open() no longer takes agentDir. (#313)

Added

  • --session-dir flag: Use a custom directory for sessions instead of the default ~/.pi/agent/sessions/<encoded-cwd>/. Works with -c (continue) and -r (resume) flags. (#313 by @scutifer)
  • Reverse model cycling and model selector: Shift+Ctrl+P cycles models backward, Ctrl+L opens model selector (retaining text in editor). (#315 by @mitsuhiko)

Read more