github badlogic/pi-mono v0.31.0

latest releases: v0.54.2, v0.54.1, v0.54.0...
one month ago

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.branch(entryId) - branch from a specific entry
  • 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/theme.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 shittycodingagent.ai (#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

Fixed

  • HTML export now properly sanitizes user messages containing HTML tags like <style> that could break DOM rendering
  • Crash when displaying bash output containing Unicode format characters like U+0600-U+0604 (#372 by @HACKE-RC)
  • Footer shows full session stats: Token usage and cost now include all messages, not just those after compaction. (#322)
  • Status messages spam chat log: Rapidly changing settings (e.g., thinking level via Shift+Tab) would add multiple status lines. Sequential status updates now coalesce into a single line. (#365 by @paulbettner)
  • Toggling thinking blocks during streaming shows nothing: Pressing Ctrl+T while streaming would hide the current message until streaming completed.
  • Resuming session resets thinking level to off: Initial model and thinking level were not saved to session file, causing --resume/--continue to default to off. (#342 by @aliou)
  • Hook tool_result event ignores errors from custom tools: The tool_result hook event was never emitted when tools threw errors, and always had isError: false for successful executions. Now emits the event with correct isError value in both success and error cases. (#374 by @nicobailon)
  • Edit tool fails on Windows due to CRLF line endings: Files with CRLF line endings now match correctly when LLMs send LF-only text. Line endings are normalized before matching and restored to original style on write. (#355 by @Pratham-Dubey)
  • Edit tool fails on files with UTF-8 BOM: Files with UTF-8 BOM marker could cause "text not found" errors since the LLM doesn't include the invisible BOM character. BOM is now stripped before matching and restored on write. (#394 by @prathamdby)
  • Use bash instead of sh on Unix: Fixed shell commands using /bin/sh instead of /bin/bash on Unix systems. (#328 by @dnouri)
  • OAuth login URL clickable: Made OAuth login URLs clickable in terminal. (#349 by @Cursivez)
  • Improved error messages: Better error messages when apiKey or model are missing. (#346 by @ronyrus)
  • Session file validation: findMostRecentSession() now validates session headers before returning, preventing non-session JSONL files from being loaded
  • Compaction error handling: generateSummary() and generateTurnPrefixSummary() now throw on LLM errors instead of returning empty strings
  • Compaction with branched sessions: Fixed compaction incorrectly including entries from abandoned branches, causing token overflow errors. Compaction now uses sessionManager.getPath() to work only on the current branch path, eliminating 80+ lines of duplicate entry collection logic between prepareCompaction() and compact()
  • enabledModels glob patterns: --models and enabledModels now support glob patterns like github-copilot/* or *sonnet*. Previously, patterns were only matched literally or via substring search. (#337)

Don't miss a new pi-mono release

NewReleases is sending notifications on new releases.