Expose data that extensions cannot otherwise access: git branch and
extension statuses from setStatus(). Token stats, model info, etc.
remain computable via ctx.sessionManager and ctx.model.
The --no-skills flag set options.skills = [] in main.ts, but the
interactive mode UI would rediscover skills anyway because it called
loadSkills() directly instead of using the already-loaded skills.
Changes:
- Add AgentSession.skills and AgentSession.skillWarnings properties
- discoverSkills() now returns { skills, warnings } instead of Skill[]
- Interactive mode uses session.skills instead of calling loadSkills()
- Update SDK docs and examples for new return type
Fixes#577
The --no-skills flag set options.skills = [] in main.ts, but the interactive mode UI would rediscover skills anyway because it called loadSkills() directly.
Changes:
- Add AgentSession.skills and AgentSession.skillWarnings properties
- discoverSkills() now returns { skills, warnings } instead of Skill[]
- Interactive mode uses session.skills instead of calling loadSkills()
Co-authored-by: Carlos Villela <cv@lixo.org>
Adds overlay rendering capability to the TUI, enabling floating modal
components that render on top of existing content without clearing the screen.
- Add showOverlay(), hideOverlay(), hasOverlay() methods to TUI
- Implement ANSI-aware line compositing via extractSegments()
- Support overlay stack (multiple overlays, later on top)
- Add { overlay: true } option to ctx.ui.custom()
- Add overlay-test.ts example extension
Also fixes pre-existing bug where bash tool output cached visual lines
at fixed terminal width, causing crashes on terminal resize.
Co-authored-by: Nico Bailon <nico.bailon@gmail.com>
- user_bash event for intercepting ! and !! commands (#528)
- Extensions can return { operations } or { result } to redirect/replace
- executeBashWithOperations() for custom BashOperations execution
- session.recordBashResult() for extensions handling bash themselves
- Theme API: getAllThemes(), getTheme(), setTheme() on ctx.ui
- mac-system-theme.ts example: sync with macOS dark/light mode
- Updated ssh.ts to use user_bash event
When passing a string systemPrompt to createAgentSession(), it is now
used as-is without appending context files and skills. This matches the
documented behavior: 'String replaces default, function receives default
and returns final.'
Previously, string systemPrompt would have context files and skills
appended, causing duplication if they were already in the string.
fixes#543
- Add systemPrompt to BeforeAgentStartEvent so extensions can see current prompt
- Change systemPromptAppend to systemPrompt in BeforeAgentStartEventResult for full replacement
- Extensions can now chain modifications (each sees the result of previous)
- Update ssh.ts to replace local cwd with remote cwd in system prompt
- Update pirate.ts, claude-rules.ts, preset.ts to use new API
fixes#575
- Add setActiveTools() to ExtensionAPI for dynamic tool management
- Extensions can now override, wrap, or disable built-in tools
- Add tool-override.ts example demonstrating the pattern
- Update documentation for tool override capabilities
When aborting a retry attempt, surface the retry-aware abort message
in the assistant output and tool results instead of a generic "Aborted".
- Set errorMessage for aborted streaming messages
- Render abort message without forcing a leading newline when no content
Three related fixes:
1. google-gemini-cli: Handle abort signal in stream reading loop
- Add abort event listener to cancel reader immediately when signal fires
- Fix AbortError detection in retry catch block (fetch throws AbortError,
not our custom message)
- Swallow reader.cancel() rejection to avoid unhandled promise
2. agent-session: Fix retry attempt counter showing 0 on cancel
- abortRetry() was resetting _retryAttempt before the catch block could
read it for the error message
3. interactive-mode: Restore main escape handler on agent_start
- When auto-retry starts, onEscape is replaced with retry-specific handler
- auto_retry_end (which restores it) fires on turn_end, after streaming begins
- Now restore immediately on agent_start if retry handler is still active
Amended: suppress reader.cancel() rejection on abort.
Add --no-tools flag that allows starting pi without any built-in tools,
enabling extension-only tool setups (e.g., pi-ssh-remote).
- Add --no-tools flag to CLI args parsing
- Handle --tools '' (empty string) as equivalent to no tools
- Fix system prompt to not show READ-ONLY mode when no tools (extensions may provide write capabilities)
- Add tests for new flag and system prompt behavior
fixes#555
Tools now use ExtensionRunner.createContext() instead of a separate
inline context factory. This ensures tools and event handlers share
the same context, fixing ctx.shutdown() and other context methods.
- Made ExtensionRunner.createContext() public
- Changed wrapRegisteredTools to accept ExtensionRunner instead of getContext callback
- Create ExtensionRunner when SDK custom tools are present (not just extensions)
- Removed redundant inline context factory from sdk.ts
The tool execution context was created with a no-op shutdown handler.
Now it delegates to ExtensionRunner.shutdown() which uses the handler
set by the mode via initialize().
- Replace per-extension closures with shared ExtensionRuntime
- Split context actions: ExtensionContextActions (required) + ExtensionCommandContextActions (optional)
- Rename LoadedExtension to Extension, remove setter methods
- Change runner.initialize() from options object to positional params
- Derive hasUI from uiContext presence (no separate param)
- Add warning when extensions override built-in tools
- RPC and print modes now provide full command context actions
BREAKING CHANGE: Extension system types and initialization API changed.
See CHANGELOG.md for migration details.
When enabledModels is configured without thinking level suffixes (e.g.,
'claude-opus-4-5' instead of 'claude-opus-4-5:high'), the scoped model's
default 'off' thinking level was overriding defaultThinkingLevel from
settings.
Now thinkingLevel in ScopedModel is optional (undefined means 'not
explicitly specified'). When passing to SDK, undefined values are filled
with defaultThinkingLevel from settings.
When a user edits settings.json while pi is running (e.g., adding
enabledModels), those settings would be lost when pi saved other
changes (e.g., changing thinking level via Shift+Tab).
The fix re-reads the file before saving and merges the current file
contents with in-memory changes, so external edits are preserved.
Adds test coverage for SettingsManager.
Extension UI dialogs (select, confirm, input) now support a timeout option
that auto-dismisses with a live countdown display. Simpler alternative to
manually managing AbortSignal for timed dialogs.
Also adds ExtensionUIDialogOptions type export and updates RPC mode to
forward timeout to clients.
Extensions can now use async initialization, enabling:
- Dynamic imports (e.g., loading tools from external packages)
- Async setup (config fetching, service connections)
- Lazy-loaded dependencies
Changes:
- ExtensionFactory type now returns void | Promise<void>
- loadExtensionFromFactory is now async, returns Promise<LoadedExtension>
- All factory(api) calls are now awaited
Backwards compatible: sync extensions continue to work unchanged.
- ExtensionAPI: setModel(), getThinkingLevel(), setThinkingLevel() methods
- New preset.ts example with plan/implement presets for model/thinking/tools switching
- Export all UI components from pi-coding-agent for extension use
- docs/tui.md: Common Patterns section with copy-paste code for SelectList, BorderedLoader, SettingsList, setStatus, setWidget, setFooter
- docs/tui.md: Key Rules section 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 development
Fixes#509, relates to #347
When OAuth refresh fails during model discovery, getApiKey() now returns
undefined instead of throwing. This allows the app to start and fall back
to other providers, so the user can /login to re-authenticate.
fixes#498
Adds sendUserMessage() to the extension API, allowing extensions to send
actual user messages (role: user) rather than custom messages. Unlike
sendMessage(), this always triggers a turn and behaves as if the user
typed the message.
- Add SendUserMessageHandler type and sendUserMessage() to ExtensionAPI
- Wire handler through loader, runner, and all modes
- Implement via prompt() with expandPromptTemplates: false
- Add send-user-message.ts example with /ask, /steer, /followup commands
- Document in extensions.md
fixes#483
Extensions can now replace the built-in footer with a custom component:
- setFooter(factory) replaces with custom component
- setFooter(undefined) restores built-in footer
Includes example extension demonstrating context usage display.
Closes#481
- Setting controls filtering at convertToLlm layer
- Images are always stored in session, filtered dynamically based on current setting
- Toggle mid-session works: LLM sees/doesn't see images already in session
- Fixed SettingsManager.save() to handle inMemory mode for all setters
Closes#492
- Setting controls filtering at convertToLlm layer (defense-in-depth)
- Images are always stored in session, filtered dynamically based on current setting
- Toggle mid-session works: LLM sees/doesn't see images already in session
- Fixed SettingsManager.save() to handle inMemory mode for all setters
Closes#492
- Add sessionId to StreamOptions for providers that support session-based caching
- OpenAI Codex provider uses sessionId for prompt_cache_key and routing headers
- Agent class now accepts and forwards sessionId to stream functions
- coding-agent passes session ID from SessionManager and updates on session changes
- Update ai package README with table of contents, OpenAI Codex OAuth docs, and env vars table
- Increase Codex instructions cache TTL from 15 minutes to 24 hours
- Add tests for sessionId forwarding in ai and agent packages
- Validate working directory exists before spawning to provide clear error message
- Add spawn error handler to prevent uncaught exceptions when shell not found or cwd invalid
- Add tests for both error scenarios
Without these fixes, spawn errors (e.g., ENOENT from missing cwd or shell) would
cause uncaught exceptions that crash the entire agent session instead of being
returned as clean tool errors.
Co-authored-by: robinwander <robinwander@users.noreply.github.com>