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
Messages submitted during compaction are queued and delivered after
compaction completes, preserving steer vs follow-up behavior. Extension
commands execute immediately during compaction.
Co-authored-by: Thomas Mustier <tmustier@users.noreply.github.com>
- Remove per-thinking-level model variants (gpt-5.2-codex-high, etc.)
- Remove thinkingLevels from Model type
- Provider clamps reasoning effort internally
- Omit reasoning field when thinking is off
fixes#472
Only fall back to 256color for truly limited terminals (dumb, empty, linux).
Virtually all modern terminals support truecolor, no need to be conservative.
- Add LoginDialogComponent with proper borders (top/bottom DynamicBorder)
- Refactor all OAuth providers to use racing approach (browser callback vs manual paste)
- Add onEscape handler to Input component for cancellation
- Add abortable sleep for GitHub Copilot polling (instant cancel on Escape)
- Show OS-specific click hint (Cmd+click on macOS, Ctrl+click elsewhere)
- Clear content between login phases (fixes GitHub Copilot two-phase flow)
- Use InteractiveMode's showStatus/showError for result messages
- Reorder providers: Anthropic, ChatGPT, GitHub Copilot, Gemini CLI, Antigravity
Extensions defined in settings.json were being ignored because SettingsManager was created after the extension loading step.
The fix moves SettingsManager.create() before discoverAndLoadExtensions() and merges settings extensions with CLI --extension args. CLI args take precedence (appended after settings).
Also clarifies SDK documentation about extension discovery behavior.
Co-authored-by: melihmucuk <melih@mucuk.co>
Previously the system prompt was converted to an input message in convertMessages,
then stripped out by filterPiSystemPrompts. Now the system prompt is passed directly
to transformRequestBody and appended after CODEX_PI_BRIDGE in the bridge message.
- Accept ExtensionFactory[] for inline extensions (merged with discovery)
- Mark preloadedExtensions as @internal (CLI implementation detail)
- Update sdk.md with inline extension example
- Update CHANGELOG
- Auto-migrate commands/ to prompts/ on startup
- Warn if hooks/ or tools/ directories contain custom extensions
- Show deprecation warnings in interactive mode with keypress to continue
- Update CHANGELOG and docs with full migration guide
Breaking changes:
- Settings: 'hooks' and 'customTools' arrays replaced with 'extensions'
- CLI: '--hook' and '--tool' flags replaced with '--extension' / '-e'
- API: HookMessage renamed to CustomMessage, role 'hookMessage' to 'custom'
- API: FileSlashCommand renamed to PromptTemplate
- API: discoverSlashCommands() renamed to discoverPromptTemplates()
- Directories: commands/ renamed to prompts/ for prompt templates
Migration:
- Session version bumped to 3 (auto-migrates v2 sessions)
- Old 'hookMessage' role entries converted to 'custom'
Structural changes:
- src/core/hooks/ and src/core/custom-tools/ merged into src/core/extensions/
- src/core/slash-commands.ts renamed to src/core/prompt-templates.ts
- examples/hooks/ and examples/custom-tools/ merged into examples/extensions/
- docs/hooks.md and docs/custom-tools.md merged into docs/extensions.md
New test coverage:
- test/extensions-runner.test.ts (10 tests)
- test/extensions-discovery.test.ts (26 tests)
- test/prompt-templates.test.ts
Discovery rules:
1. extensions/*.ts or *.js - direct files
2. extensions/*/index.ts or index.js - subdirectory with index
3. extensions/*/package.json with pi field - load declared paths
No recursion beyond one level. Complex packages use package.json manifest.
Added PiManifest type for future theme/skill bundling support.
17 tests covering all discovery scenarios.
refs #454
New src/core/extensions/ directory with:
- types.ts: merged types from hooks and custom-tools
- loader.ts: single loader for extensions
- runner.ts: ExtensionRunner for event emission
- wrapper.ts: tool wrapping utilities
- index.ts: exports
Key changes from old system:
- Single ExtensionAPI with registerTool() for LLM-callable tools
- Tools use ExtensionContext (has UI access)
- No onSession callback on tools (use pi.on events instead)
refs #454
- event-bus.ts: await async handlers to catch errors properly
- agent-session.ts: clear _pendingNextTurnMessages on newSession/switchSession/branch
- sdk.ts: make eventBus first (required) param for discoverHooks/discoverCustomTools
- docs/sdk.md: document eventBus sharing pattern for hook/tool communication
The Hook API features (registerFlag, registerShortcut, setWidget, getActiveTools,
plan-mode.ts) were incorrectly listed under 0.32.2 but actually shipped in 0.34.0.
- createAllTools() populates registry with all 7 built-in tools
- --tools flag only sets initially active tools (default: read/bash/edit/write)
- Hooks can enable any tool from registry via setActiveTools()
- System prompt rebuilds with correct tool guidelines when tools change
- Document tsx module resolution workaround in README
When hooks are loaded via jiti, they get a separate module instance from
the main app. This means the global 'theme' variable in the hook's module
is never initialized. Adding an optional theme parameter allows hooks to
pass the theme from ctx.ui.custom() callback.
Usage in hooks:
getSettingsListTheme(theme) // theme from ctx.ui.custom callback
- HookError now includes optional stack field
- Hook error display shows stack trace in dim color below error message
- tools.ts: create SettingsListTheme using the theme passed to ctx.ui.custom()
instead of using getSettingsListTheme() which depends on global theme
- before_agent_start handlers can return systemPromptAppend to dynamically
append text to the system prompt for that turn
- Multiple hooks' systemPromptAppend strings are concatenated
- Multiple hooks' messages are now all injected (not just first)
- Tool registry now contains ALL built-in tools (read, bash, edit, write,
grep, find, ls) regardless of --tools flag
- --tools only sets initially active tools, hooks can enable any via
setActiveTools()
- System prompt automatically rebuilds when tools change, updating tool
descriptions and guidelines
- Add pirate.ts example hook demonstrating systemPromptAppend
- Update hooks.md with systemPromptAppend documentation
- /tools command opens SettingsList-based selector for all loaded tools
- Space/Enter toggles individual tools between enabled/disabled
- Changes apply immediately on toggle (like /settings)
- Tool selection persisted to session via appendEntry()
- State restored from current branch on session_start, session_tree, session_branch
- Uses getBranch() to respect branch-specific tool configuration
- Export getSettingsListTheme and getSelectListTheme for hooks to use
The context event handler documentation promised a deep copy but the
implementation passed the original array reference. This could cause
hooks to accidentally mutate session messages.
Uses structuredClone() for fast native deep copying.
The context event handler documentation promised a deep copy but the
implementation passed the original array reference. This could cause
hooks to accidentally mutate session messages.
Uses structuredClone() for fast native deep copying.