Changelog

Release notes, project updates, and announcements from the Pi team.

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()
    • queuedMessageCount → pendingMessageCount
    • 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.queueMode → steeringMode 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:

  • HookEventContext → HookContext
  • 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.sessionFile → ctx.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:

  • CustomAgentTool → CustomTool
  • ToolAPI → CustomToolAPI
  • ToolContext → CustomToolContext
  • ToolSessionEvent → CustomToolSessionEvent

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:

  • CustomAgentTool → CustomTool
  • AppMessage → AgentMessage
  • 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.branchedFrom → SessionHeader.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:

  • messageTransformer → convertToLlm
  • 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: entryIndex → entryId
  • get_branch_messages response: entryIndex → entryId

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.2

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

Changed

  • Consolidated migrations: Moved auth migration from AuthStorage.migrateLegacy() to new migrations.ts module.

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

Release notes

Pi 0.29.1

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

Added

  • Automatic custom system prompt loading: Pi now auto-loads SYSTEM.md files to replace the default system prompt. Project-local .pi/SYSTEM.md takes precedence over global ~/.pi/agent/SYSTEM.md. CLI --system-prompt flag overrides both. (#309)
  • Unified /settings command: New settings menu consolidating thinking level, theme, queue mode, auto-compact, show images, hide thinking, and collapse changelog. Replaces individual /thinking, /queue, /theme, /autocompact, and /show-images commands. (#310)

Read more

Release notes

Pi 0.29.0

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

Breaking Changes

  • Renamed /clear to /new: The command to start a fresh session is now /new. Hook event reasons before_clear/clear are now before_new/new. Merry Christmas @mitsuhiko! (#305)

Added

  • Auto-space before pasted file paths: When pasting a file path (starting with /, ~, or .) after a word character, a space is automatically prepended. (#307 by @mitsuhiko)
  • Word navigation in input fields: Added Ctrl+Left/Right and Alt+Left/Right for word-by-word cursor movement. (#306 by @kim0)
  • Full Unicode input: Input fields now accept Unicode characters beyond ASCII. (#306 by @kim0)

Read more

Release notes

Pi 0.28.0

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

Changed

  • Credential storage refactored: API keys and OAuth tokens are now stored in ~/.pi/agent/auth.json instead of oauth.json and settings.json. Existing credentials are automatically migrated on first run. (#296)

  • SDK API changes (#296):

    • Added AuthStorage class for credential management (API keys and OAuth tokens)
    • Added ModelRegistry class for model discovery and API key resolution
    • Added discoverAuthStorage() and discoverModels() discovery functions
    • createAgentSession() now accepts authStorage and modelRegistry options
    • Removed configureOAuthStorage(), defaultGetApiKey(), findModel(), discoverAvailableModels()
    • Removed getApiKey callback option (use AuthStorage.setRuntimeApiKey() for runtime overrides)
    • Use getModel() from @mariozechner/pi-ai for built-in models, modelRegistry.find() for custom models + built-in models
    • See updated SDK documentation and README
  • Settings changes: Removed apiKeys from settings.json. Use auth.json instead. (#296)

Read more

Release notes

Pi 0.27.9

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

Fixed

  • Model selector and --list-models with settings.json API keys: Models with API keys configured in settings.json (but not in environment variables) now properly appear in the /model selector and --list-models output. (#295)

Read more

Release notes

Pi 0.27.8

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

Fixed

  • API key priority: OAuth tokens now take priority over settings.json API keys. Previously, an API key in settings.json would trump OAuth, causing users logged in with a plan (unlimited tokens) to be billed via PAYG instead.

Read more

Release notes

Pi 0.27.7

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

Fixed

  • Thinking tag leakage: Fixed Claude mimicking literal </thinking> tags in responses. Unsigned thinking blocks (from aborted streams) are now converted to plain text without <thinking> tags. The TUI still displays them as thinking blocks. (#302 by @nicobailon)

Read more

Release notes

Pi 0.27.6

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

Added

  • Compaction hook improvements: The before_compact session event now includes:

    • previousSummary: Summary from the last compaction (if any), so hooks can preserve accumulated context
    • messagesToKeep: Messages that will be kept after the summary (recent turns), in addition to messagesToSummarize
    • resolveApiKey: Function to resolve API keys for any model (checks settings, OAuth, env vars)
    • Removed apiKey string in favor of resolveApiKey for more flexibility
  • SessionManager API cleanup:

    • Renamed loadSessionFromEntries() to buildSessionContext() (builds LLM context from entries, handling compaction)
    • Renamed loadEntries() to getEntries() (returns defensive copy of all session entries)
    • Added buildSessionContext() method to SessionManager

Read more

Release notes

Pi 0.27.5

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

Added

  • HTML export syntax highlighting: Code blocks in markdown and tool outputs (read, write) now have syntax highlighting using highlight.js with theme-aware colors matching the TUI.
  • HTML export improvements: Render markdown server-side using marked (tables, headings, code blocks, etc.), honor user's chosen theme (light/dark), add image rendering for user messages, and style code blocks with TUI-like language markers. (@scutifer)

Read more

Release notes

Pi 0.27.4

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

Fixed

  • Symlinked skill directories: Skills in symlinked directories (e.g., ~/.pi/agent/skills/my-skills -> /path/to/skills) are now correctly discovered and loaded.

Read more

Release notes

Pi 0.27.3

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

Added

  • API keys in settings.json: Store API keys in ~/.pi/agent/settings.json under the apiKeys field (e.g., { "apiKeys": { "anthropic": "sk-..." } }). Settings keys take priority over environment variables. (#295)

Read more

Release notes

Pi 0.27.2

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

Added

  • Skip conversation restore on branch: Hooks can return { skipConversationRestore: true } from before_branch to create the branched session file without restoring conversation messages. Useful for checkpoint hooks that restore files separately. (#286 by @nicobarray)

Read more