Commit Graph

143 Commits

  • feat: add maxDelayMs setting to cap server-requested retry delays
    When a provider (e.g., Google Gemini CLI) requests a retry delay longer
    than maxDelayMs (default: 60s), the request fails immediately with an
    informative error instead of waiting silently for hours.
    
    The error is then handled by agent-level auto-retry, which shows the
    delay to the user and allows aborting with Escape.
    
    - Add maxRetryDelayMs to StreamOptions (packages/ai)
    - Add maxRetryDelayMs to AgentOptions (packages/agent)
    - Add retry.maxDelayMs to settings (packages/coding-agent)
    - Update _isRetryableError to match 'retry delay' errors
    
    fixes #1123
  • fix(coding-agent): make setThinkingLevel idempotent
    switchSession() was appending spurious thinking_level_change entries
    to session log on resume because setThinkingLevel() unconditionally
    persisted. Now only persists if the level actually changes.
    
    fixes #1118
  • feat(coding-agent): add ctx.getSystemPrompt() to extension context
    Adds a method to access the effective system prompt (after any per-turn
    extension modifications) from the extension context.
    
    Implementation:
    - Add systemPrompt getter to AgentSession reading from agent.state.systemPrompt
    - Wire getSystemPrompt through ExtensionContextActions to ExtensionRunner
    - Add getSystemPrompt to interactive-mode's shortcut context
    - Update docs with ctx.getSystemPrompt() section
    - Add system-prompt-header.ts example
    - Add example to docs reference table
    
    Closes #1098
  • feat(coding-agent): add set_session_name RPC command (#1075)
    - Add set_session_name command with empty name validation
    - Expose sessionName in get_state response
    - Add setSessionName() to AgentSession and RpcClient
    - Document in docs/rpc.md
  • fix(coding-agent): reset retry counter after each successful LLM response
    Previously, within a single tool-use turn, rate limit retries would
    accumulate across separate LLM calls. For example, if each of 3 tool
    calls hit a 429 and retried once, the counter would show '3/3' and fail
    even though each individual retry succeeded.
    
    Now the counter resets immediately when a successful (non-error)
    assistant message arrives, so each LLM call gets a fresh set of retries.
    
    Fixes #1019
  • fix(coding-agent): sync agent state after newSession setup callback
    Move setup callback handling from interactive/rpc modes into AgentSession.newSession().
    After setup() runs, sync agent state via replaceMessages() so the LLM has context
    and the UI renders the messages properly.
    
    fixes #968
  • feat: custom provider support with streamSimple
    - Add resetApiProviders() to clear and re-register built-in providers
    - Add createAssistantMessageEventStream() factory for extensions
    - Add streamSimple support in ProviderConfig for custom API implementations
    - Call resetApiProviders() on /reload to clean up extension providers
    - Add custom-provider.md documentation
    - Add custom-provider.ts example with full Anthropic implementation
    - Update extensions.md with streamSimple config option
  • feat(coding-agent): package deduplication and collision detection
    - Package deduplication: same package in global+project, project wins
    - Collision detection for skills, prompts, and themes with ResourceCollision type
    - PathMetadata tracking with parent directory lookup for file paths
    - Display improvements: section headers, sorted groups, accent colors for packages
    - pi list shows full paths below package names
    - Extension loader discovers files in directories without index.ts
    - In-memory SettingsManager properly tracks project settings
    
    fixes #645
  • feat(coding-agent): make skill invocation messages collapsible
    - Add ParsedSkillBlock interface and parseSkillBlock() function
    - Change skill expansion to use XML-style <skill> tags
    - Add SkillInvocationMessageComponent for collapsible display
    - Collapsed: single line with skill name and expand hint
    - User message rendered separately after skill block
    
    Fixes #894
  • fix(coding-agent): simplify extension error listener to single instance
    There's only ever one bindings instance per session, so the Set/Array
    approach was unnecessary. Changed from Set<ExtensionErrorListener> to
    optional single listener.
  • fix(coding-agent): add 'terminated' to retryable error patterns
    Codex API can send 'terminated' error mid-stream, which should be
    retried like other transient server errors.
  • feat(coding-agent): ResourceLoader, package management, and /reload command (#645)
    - Add ResourceLoader interface and DefaultResourceLoader implementation
    - Add PackageManager for npm/git extension sources with install/remove/update
    - Add session.reload() and session.bindExtensions() APIs
    - Add /reload command in interactive mode
    - Add CLI flags: --skill, --theme, --prompt-template, --no-themes, --no-prompt-templates
    - Add pi install/remove/update commands for extension management
    - Refactor settings.json to use arrays for skills, prompts, themes
    - Remove legacy SkillsSettings source flags and filters
    - Update SDK examples and documentation for ResourceLoader pattern
    - Add theme registration and loadThemeFromPath for dynamic themes
    - Add getShellEnv to include bin dir in PATH for bash commands
  • fix(coding-agent): prevent crash on OAuth authentication failure (#849)
    Detect OAuth authentication failures (expired credentials, offline) and provide helpful error message instead of crashing with generic 'No API key found' error.
    
    Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
  • fix(coding-agent): handle auto-compaction failures gracefully
    When auto-compaction fails (e.g., quota exceeded), emit the error via
    the auto_compaction_end event instead of throwing. The UI now displays
    the error message, allowing users to take action (switch models, wait
    for quota reset, etc.) instead of crashing.
    
    fixes #792
  • fix(ai): filter empty error assistant messages in transformMessages
    When 429/500 errors occur during tool execution, empty assistant messages
    with stopReason='error' get persisted. These break the tool_use -> tool_result
    chain for Claude/Gemini APIs.
    
    Added centralized filtering in transformMessages to skip assistant messages
    with empty content and no tool calls. Provider-level filters remain for
    defense-in-depth.
  • Move skill command handling to AgentSession, update docs
    - Skill commands (/skill:name) now expanded in AgentSession instead of
      interactive mode, enabling them in RPC and print modes
    - Input event can now intercept /skill:name before expansion
    - Updated extensions.md with clearer input event docs and processing order
    - Updated rpc.md: hook -> extension terminology, added skill expansion mentions
    - Added PR attribution to changelog entries for #761
  • feat(coding-agent): add input event for extension input interception (#761)
    * feat(coding-agent): add input event for extension input interception
    
    Extensions can now intercept, transform, or handle user input before the
    agent processes it. Three result types: continue (pass through), transform
    (modify text/images), handled (respond without LLM). Handlers chain
    transforms and short-circuit on handled. Source field identifies origin.
    
    * fix: make source public, use if/else over ternary
    
    * fix: remove response field, extension handles own UI
  • feat(coding-agent): Custom tool export rendering in export (#702)
    * coding-agent: add ANSI-to-HTML converter for export
    
    * coding-agent: add getToolDefinition method to ExtensionRunner
    
    * coding-agent: add tool HTML renderer factory for custom tools
    
    * coding-agent: add custom tool pre-rendering to HTML export
    
    * coding-agent: render pre-rendered custom tools in HTML export
    
    * coding-agent: integrate tool renderer in exportToHtml
  • Improve Google Cloud Code Assist error handling (#665)
    * Improve Cloud Code Assist error messages
    
    - Extract just the message from verbose JSON error responses
    - Extract cause from generic 'fetch failed' errors for better diagnostics
    
    * Make 'other side closed' network error retryable
    
    * Make 'other side closed' network error retryable
  • Change getAllTools() to return ToolInfo[] instead of string[]
    Breaking change: pi.getAllTools() now returns Array<{ name, description }>
    instead of string[]. Extensions needing just names can use .map(t => t.name).
    
    Removes redundant getToolInfo() method added in original PR.
    
    Fixes #647
  • Rename /branch command to /fork
    - RPC: branch -> fork, get_branch_messages -> get_fork_messages
    - SDK: branch() -> fork(), getBranchMessages() -> getForkMessages()
    - AgentSession: branch() -> fork(), getUserMessagesForBranching() -> getUserMessagesForForking()
    - Extension events: session_before_branch -> session_before_fork, session_branch -> session_fork
    - Settings: doubleEscapeAction 'branch' -> 'fork'
    
    fixes #641
  • Add /models command for enabling/disabling Ctrl+P model cycling
    - New /models command with toggle UI for each available model
    - Changes persist to enabledModels in settings.json
    - Updates take effect immediately for Ctrl+P cycling
  • feat(coding-agent): add model_select extension hook
    Fires when model changes via setModel(), cycleModel(), or session restore.
    Includes source field ("set" | "cycle" | "restore") and previous model.
  • Fix --no-skills flag not preventing skills from loading
    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
  • feat(coding-agent): add user_bash event and theme API extensions
    - 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
  • Allow extensions to modify system prompt in before_agent_start
    - 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
  • fix: show retry attempt count when aborting during retry
    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
  • fix: ESC key not interrupting during Working... state
    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.
  • Extensions: add pi.sendUserMessage() for sending user messages
    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
  • feat(ai,agent,coding-agent): add sessionId for provider session-based caching
    - 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
  • fix: clean up Codex thinking level handling
    - 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
  • Merge hooks and custom-tools into unified extensions system (#454)
    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
  • Fix event bus async error handling, clear pending messages on session switch, improve SDK docs
    - 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
  • feat(coding-agent): add event bus for tool/hook communication (#431)
    * feat(coding-agent): add event bus for tool/hook communication
    
    Adds pi.events API enabling custom tools and hooks to communicate via
    pub/sub. Tools can emit events, hooks can listen. Shared EventBus instance
    created per session in createAgentSession().
    
    - EventBus interface with emit() and on() methods
    - on() returns unsubscribe function
    - Threaded through hook and tool loaders
    - Documented in hooks.md and custom-tools.md
    
    * fix(coding-agent): wrap event handlers to catch errors
    
    * docs: note async handler error handling for event bus
    
    * feat(coding-agent): add sendMessage to tools, nextTurn delivery mode
    
    - Custom tools now have pi.sendMessage() for direct agent notifications
    - New deliverAs: 'nextTurn' queues messages for next user prompt
    - Fix: hooks and tools now share the same eventBus (was isolated before)
    
    * fix(coding-agent): nextTurn delivery should always queue, even when streaming
  • feat(hooks): add systemPromptAppend to before_agent_start, full tool registry
    - 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
  • refactor(hooks): address PR feedback
    - Rename getTools/setTools to getActiveTools/setActiveTools
    - Add getAllTools to enumerate all configured tools
    - Remove text_delta event (use turn_end/agent_end instead)
    - Add shortcut conflict detection:
      - Skip shortcuts that conflict with built-in shortcuts (with warning)
      - Log warnings when hooks register same shortcut (last wins)
    - Add note about prompt cache invalidation in setActiveTools
    - Update plan-mode hook to use agent_end for [DONE:id] parsing
  • feat(hooks): add text_delta event for streaming text monitoring
    - New text_delta hook event fires for each chunk of streaming text
    - Enables real-time monitoring of agent output
    - Plan-mode hook now updates todo progress as [DONE:id] tags stream in
    - Each todo item has unique ID for reliable tracking
  • WIP: Add hook API for dynamic tool control with plan-mode hook example
    - Add pi.getTools() and pi.setTools(toolNames) to HookAPI
    - Hooks can now enable/disable tools dynamically
    - Changes take effect on next agent turn
    
    New example hook: plan-mode.ts
    - Claude Code-style read-only exploration mode
    - /plan command toggles plan mode on/off
    - Plan mode tools: read, bash, grep, find, ls
    - Edit/write tools disabled in plan mode
    - Injects context telling agent about restrictions
    - After each response, prompts to execute/stay/refine
    - State persists across sessions