Claude requires max_tokens > thinking.budget_tokens. When caller specifies
a small maxTokens (e.g. compaction with ~13k tokens) and reasoning is enabled
with high budget (16k tokens), the constraint was violated.
Fix: In mapOptionsForApi, add thinkingBudget on top of caller's maxTokens
(capped at model.maxTokens). If still not enough room, reduce thinkingBudget
to leave space for output.
Applied to both anthropic-messages and google-gemini-cli APIs.
Also adds test utilities for OAuth credential resolution and tests for
compaction with thinking models.
fixes#413
Add doubleEscapeAction setting to choose whether double-escape with an
empty editor opens /tree (default) or /branch.
- Add setting to Settings interface and SettingsManager
- Add to /settings UI for easy toggling
- Update interactive-mode to respect the setting
- Document in README.md settings table
fixes#404
getAvailable() now uses hasAuth() which checks if auth is configured
without triggering OAuth token refresh. Refresh happens later when
the model is actually used.
- Implement google-vertex provider in packages/ai
- Support ADC (Application Default Credentials) via @google/generative-ai
- Add Gemini model catalog for Vertex AI
- Update packages/coding-agent to handle google-vertex provider
Builds on #406 to support simpler proxy use case:
- Override just baseUrl to route built-in provider through proxy
- All built-in models preserved, no need to redefine them
- Full replacement still works when models array is provided
* Allow models.json to override built-in providers
When a provider is defined in models.json with the same name as a
built-in provider (e.g., 'anthropic', 'google'), the built-in models
for that provider are completely replaced by the custom definition.
This enables users to:
- Use custom base URLs (proxies, self-hosted endpoints)
- Define a subset of models they want available
- Customize model configurations for built-in providers
Example usage in ~/.pi/agent/models.json:
{
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1",
"apiKey": "ANTHROPIC_API_KEY",
"api": "anthropic-messages",
"models": [...]
}
}
}
* Refactor model-registry for readability
- Extract CustomModelsResult type and emptyCustomModelsResult helper
- Extract loadBuiltInModels method with clear skip logic
- Simplify loadModels with destructuring and ternary
- Reduce repetition in error handling paths
* Refactor model-registry tests for readability
- Extract providerConfig() helper to hide irrelevant model fields
- Extract writeModelsJson() helper for file writing
- Extract getModelsForProvider() helper for filtering
- Move modelsJsonPath to beforeEach
Reduces test file from 262 to 130 lines while maintaining same coverage.
- Fix settings-selector descriptions to explain one-at-a-time vs all
- Update README.md message queuing section, settings example, and table
- Update hooks.md: hasPendingMessages, sendMessage options, triggerTurn example
- Add Theme/ThemeColor export and hasPendingMessages rename to CHANGELOG
- pi.sendMessage(msg, options?) now accepts { triggerTurn?, deliverAs? }
- deliverAs: 'steer' (default) or 'followUp' controls delivery timing
- Update all mode handlers to pass options through
- Update file-trigger example to use new API
- Update CHANGELOG
- Update settings-manager with steeringMode/followUpMode (migrates old queueMode)
- Update sdk.ts to use new mode options
- Update settings-selector UI to show both modes
- Add Alt+Enter keybind for follow-up messages
- Update RPC API: steer/follow_up commands, set_steering_mode/set_follow_up_mode
- Update rpc-client with new methods
- Delete dead code: queue-mode-selector.ts
- Update tests for new API
- Update mom/context.ts stubs
- Update web-ui example
- Rename queueMessage to steer(), add followUp()
- Split _pendingMessages into _steeringMessages and _followUpMessages
- Update sendHookMessage to accept deliverAs option
- Rename hasQueuedMessages to hasPendingMessages
- Rename queuedMessageCount to pendingMessageCount
- Update clearQueue() return type to { steering, followUp }
- Update UI to show steering vs follow-up messages differently
WIP: settings-manager, sdk, interactive-mode, rpc-mode still need updates
- /todos command displays all todos on current branch with custom UI
- Update hooks.md to clarify components must not be wrapped in Box/Container
- Cross-reference tool and hook in example READMEs
Breaking change: replaces queueMessage() with two separate methods:
- steer(msg): interrupt mid-run, delivered after current tool execution
- followUp(msg): wait until agent finishes before delivery
Also renames:
- queueMode -> steeringMode/followUpMode
- getQueuedMessages -> getSteeringMessages/getFollowUpMessages
Refs #403
Agent.prompt() and Agent.continue() now throw if called while already
streaming, preventing race conditions and corrupted state. Use
queueMessage() to queue messages during streaming, or await the
previous call.
AgentSession.prompt() has the same guard with a message directing
users to queueMessage().
Ref #403
Add setTitle() method to Terminal interface for setting window title.
Uses standard OSC escape sequence \x1b]0;...\x07 for broad terminal
compatibility (macOS Terminal, iTerm2, Kitty, WezTerm, Ghostty, etc.).
Changes:
- Add setTitle(title: string) to Terminal interface
- Implement in ProcessTerminal using OSC sequence
- Implement no-op in VirtualTerminal for testing
- Use in interactive mode to set title as "pi - <dirname>"
Kitty and other smart terminals send a specific CSI u control code for
these shifted special keys, which are interpreted as a no-op by the
editor component. These should send the underlying key instead, matching
the behaviour of other TUI programs (e.g. readline, vim insert mode).
On less smart terminals, these key combinations are the same as the
non-shifted versions, and so already work with the existing code.