Commit Graph

91 Commits

  • fix(ai): classify Google thoughtSignature as thinking
    Google streaming may emit thoughtSignature without thought=true (including empty-text signature-only parts). Treat non-empty thoughtSignature as thinking to avoid leaking reasoning into normal text and retain signature across streaming deltas. Add unit test coverage.
  • 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(ai): append system prompt to codex bridge message instead of converting to input
    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.
  • 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
  • Add Vertex AI provider with ADC support
    - 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
  • Update READMEs: remove agent section from pi-ai, rewrite pi-agent-core
    - Removed Agent API section from pi-ai README (moved to agent package)
    - Rewrote agent package README for new architecture:
      - No more transports (ProviderTransport, AppTransport removed)
      - Uses streamFn directly with streamProxy for proxy usage
      - Documents convertToLlm and transformContext
      - Documents low-level agentLoop/agentLoopContinue API
      - Updated custom message types documentation
  • WIP: Refactor agent package - not compiling
    - Renamed AppMessage to AgentMessage throughout
    - New agent-loop.ts with AgentLoopContext, AgentLoopConfig
    - Removed transport abstraction, Agent now takes streamFn directly
    - Extracted streamProxy to proxy.ts utility
    - Removed agent-loop from pi-ai (now in agent package)
    - Updated consumers (coding-agent, mom) for AgentMessage rename
    - Tests updated but some consumers still need migration
    
    Known issues:
    - AgentTool, AgentToolResult not exported from pi-ai
    - Attachment not exported from pi-agent-core
    - ProviderTransport removed but still referenced
    - messageTransformer -> convertToLlm migration incomplete
    - CustomMessages declaration merging not working properly
  • Migrate zai provider from Anthropic to OpenAI-compatible API
    - Migrate glm-4.5, glm-4.5-air, glm-4.5-flash, glm-4.6, glm-4.7 from anthropic-messages to openai-completions API
    - Updated baseUrl from https://api.z.ai/api/anthropic to https://api.z.ai/api/coding/paas/v4
    - Added compat setting to disable developer role for zai models
    - Filter empty text blocks in openai-completions to avoid zai API validation errors
    - Fixed zai provider tests to use OpenAI-style options (reasoningEffort)
  • Migrate zai provider from Anthropic to OpenAI-compatible API
    - Change all zai models from anthropic-messages to openai-completions API
    - Update baseUrl from https://api.z.ai/api/anthropic to https://api.z.ai/api/coding/paas/v4
    - Add compat setting to disable developer role for zai
    - Update zai provider tests to use OpenAI-style options (reasoningEffort instead of thinkingEnabled/thinkingBudgetTokens)
    - Enable previously disabled thinking and image input tests for zai models
  • WIP: Remove global state from pi-ai OAuth/API key handling
    - Remove setApiKey, resolveApiKey, and global apiKeys Map from stream.ts
    - Rename getApiKey to getApiKeyFromEnv (only checks env vars)
    - Remove OAuth storage layer (storage.ts deleted)
    - OAuth login/refresh functions now return credentials instead of saving
    - getOAuthApiKey/refreshOAuthToken now take credentials as params
    - Add test/oauth.ts helper for ai package tests
    - Simplify root npm run check (single biome + tsgo pass)
    - Remove redundant check scripts from most packages
    - Add web-ui and coding-agent examples to biome/tsgo includes
    
    coding-agent still has compile errors - needs refactoring for new API
  • Add OAuth providers to test suite and improve test coverage
    Tests updated:
    - abort.test.ts: Add Google Gemini CLI, add retries
    - agent.test.ts: Add OAuth providers (Anthropic, GitHub Copilot, Gemini CLI, Antigravity), add retries, remove timeouts
    - context-overflow.test.ts: Handle Cerebras 429 status code
    - image-tool-result.test.ts: Add OAuth providers
    - overflow.ts: Detect 429 as overflow for Cerebras
    
    Removed obsolete debug/one-off tests:
    - copilot-initiator.test.ts
    - gemini-3-flash-tool-calling.test.ts
    - google-thought-signature.test.ts
    - mistral-debug.test.ts
    - mistral-empty-assistant.test.ts
    - mistral-sdk.test.ts
  • Fix orphaned tool calls by inserting synthetic empty results
    When a user interrupts a tool call flow (sends a message without providing
    tool results), APIs like OpenAI Responses and Anthropic fail because:
    - OpenAI requires tool outputs for function calls
    - OpenAI requires reasoning items to have their following items
    - Anthropic requires non-empty content for error tool results
    
    Instead of filtering out orphaned tool calls (which breaks thinking signatures),
    we now insert synthetic empty tool results with isError: true and content
    'No result provided'. This preserves the conversation structure and satisfies
    all API requirements.
  • Add Google Gemini CLI and Antigravity OAuth providers
    - Add google-gemini-cli provider: free Gemini 2.0/2.5 via Cloud Code Assist
    - Add google-antigravity provider: free Gemini 3, Claude, GPT-OSS via sandbox
    - Move OAuth infrastructure from coding-agent to ai package
    - Fix thinking signature handling for cross-model handoff
    - Fix OpenAI message ID length limit (max 64 chars)
    - Add GitHub Copilot overflow pattern detection
    - Add OAuth provider tests for context overflow and streaming
  • Fix X-Initiator header logic for GitHub Copilot
    Check last message role instead of any message in history.
    This matches the original correct implementation from PR #200.
    
    fixes #209
  • fix(ai): correct Gemini tool result format and improve type safety
    - Fix tool result format for Gemini 3 Flash Preview compatibility
      - Use 'output' key for successful results (not 'result')
      - Use 'error' key for error results (not 'isError')
      - Per Google SDK documentation for FunctionResponse.response
    
    - Improve type safety in google.ts provider
      - Add ImageContent import and use proper type guards
      - Replace 'as any' casts with proper typing
      - Import and use Schema type for tool parameters
      - Add proper typing for index deletion in error handler
    
    - Add comprehensive test for Gemini 3 Flash tool calling
      - Tests successful tool call and result handling
      - Tests error tool result handling
      - Verifies fix for issue #213
    
    Fixes #213
  • Release v0.23.2
    Fixed Claude models via GitHub Copilot re-answering all previous prompts.
    
    fixes #209
  • Fix image limits test to use realistic payload sizes
    Previous test used compressed 8k images (0.01MB) which was meaningless.
    Now tests with actual large noise images that don't compress.
    
    Realistic payload limits discovered:
    - Anthropic: 6 x 3MB = ~18MB total (not 32MB as documented)
    - OpenAI: 2 x 15MB = ~30MB total
    - Gemini: 10 x 20MB = ~200MB total (very permissive)
    - Mistral: 4 x 10MB = ~40MB total
    - xAI: 1 x 20MB (strict request size limit)
    - Groq: 5 x 5760px images (5 image + pixel limit)
    - zAI: 2 x 15MB = ~30MB (50MB request limit)
    - OpenRouter: 2 x 5MB = ~10MB total
    
    Also fixed GEMINI_API_KEY env var (was GOOGLE_API_KEY).
    
    Related to #120
  • Update image limits test with comprehensive 8k stress test results
    Tested max 8kx8k images per provider:
    - Anthropic: 100 (explicit limit, fails at 101)
    - OpenAI: 100-200 (100 works, 200 times out)
    - Mistral: 8 (explicit limit, fails at 9)
    - xAI: 100-150 (100 works, 150 times out)
    - Groq: 0 (8k exceeds 33M pixel limit)
    - zAI: 400 (context window limited at 500)
    - OpenRouter: 40 (context window limited at 50)
    - Gemini: untested (no API key in test env)
    
    Key finding: Anthropic's 'many images' rule does NOT cause API errors.
    100 x 8kx8k images work fine. Anthropic likely auto-resizes internally.
    
    Related to #120
  • Add comprehensive image limits test suite for all vision-capable providers
    Tests max image count, size, dimensions, and 8k stress test for:
    - Anthropic, OpenAI, Gemini, Mistral, OpenRouter, xAI, Groq, zAI
    
    Key finding: Anthropic's 'many images' rule (>20 images = 2000px max)
    does NOT cause API errors. 100 x 8k images work fine. Anthropic likely
    auto-resizes internally.
    
    Related to #120
  • ai: add image limits test suite
    Tests provider-specific image limitations across all supported providers:
    - Maximum number of images in context
    - Maximum image size (bytes)
    - Maximum image dimensions
    
    Discovered limits (Dec 2025):
    - Anthropic: 100 images, 5MB per image, 8000px max dimension
    - OpenAI: 500 images, >=25MB per image
    - Gemini: ~2500 images, >=40MB per image
    - Mistral: 8 images, ~15MB per image
    - OpenRouter: ~40 images (context limited), ~15MB per image
  • Fix branch selector for single message and --no-session mode
    - Allow branch selector to open with single user message (changed <= 1 to === 0 check)
    - Support in-memory branching for --no-session mode (no files created)
    - Add isEnabled() getter to SessionManager
    - Update sessionFile getter to return null when sessions disabled
    - Update SessionSwitchEvent types to allow null session files
    - Add branching tests for single message and --no-session scenarios
    
    fixes #163
  • Fix Mistral 400 errors after aborted assistant messages
    - Skip empty assistant messages (no content, no tool calls) to avoid
      Mistral's 'Assistant message must have either content or tool_calls'
      error
    - Remove synthetic assistant bridge message after tool results (Mistral
      no longer requires this as of Dec 2024)
    - Add test for empty assistant message handling
    
    Follow-up to #165
  • Add Mistral as AI provider
    - Add Mistral to KnownProvider type and model generation
    - Implement Mistral-specific compat handling in openai-completions:
      - requiresToolResultName: tool results need name field
      - requiresAssistantAfterToolResult: synthetic assistant message between tool/user
      - requiresThinkingAsText: thinking blocks as <thinking> text
      - requiresMistralToolIds: tool IDs must be exactly 9 alphanumeric chars
    - Add MISTRAL_API_KEY environment variable support
    - Add Mistral tests across all test files
    - Update documentation (README, CHANGELOG) for both ai and coding-agent packages
    - Remove client IDs from gemini.md, reference upstream source instead
    
    Closes #165
  • Simplify compaction: remove proactive abort, use Agent.continue() for retry
    - Add agentLoopContinue() to pi-ai for resuming from existing context
    - Add Agent.continue() method and transport.continue() interface
    - Simplify AgentSession compaction to two cases: overflow (auto-retry) and threshold (no retry)
    - Remove proactive mid-turn compaction abort
    - Merge turn prefix summary into main summary
    - Add isCompacting property to AgentSession and RPC state
    - Block input during compaction in interactive mode
    - Show compaction count on session resume
    - Rename RPC.md to rpc.md for consistency
    
    Related to #128
  • Add xhigh thinking level for OpenAI codex-max models
    - Add 'xhigh' to ThinkingLevel type in ai and agent packages
    - Map xhigh to reasoning_effort: 'max' for OpenAI providers
    - Add thinkingXhigh color token to theme schema and built-in themes
    - Show xhigh option only when using codex-max models
    - Update CHANGELOG for both ai and coding-agent packages
    
    closes #143
  • Implement tool result truncation with actionable notices (#134)
    - read: actionable notices with offset for continuation
      - First line > 30KB: return empty + bash command suggestion
      - Hit limit: '[Showing lines X-Y of Z. Use offset=N to continue]'
    
    - bash: tail truncation with temp file
      - Notice includes line range + temp file path
      - Edge case: last line > 30KB shows partial
    
    - grep: pre-truncate match lines to 500 chars
      - '[... truncated]' suffix on long lines
      - Notice for match limit and line truncation
    
    - find/ls: result/entry limit notices
      - '[N results limit reached. Use limit=M for more]'
    
    - All notices now in text content (LLM sees them)
    - TUI simplified (notices render as part of output)
    - Never return partial lines (except bash edge case)
  • Add totalTokens field to Usage type
    - Added totalTokens field to Usage interface in pi-ai
    - Anthropic: computed as input + output + cacheRead + cacheWrite
    - OpenAI/Google: uses native total_tokens/totalTokenCount
    - Fixed openai-completions to compute totalTokens when reasoning tokens present
    - Updated calculateContextTokens() to use totalTokens field
    - Added comprehensive test covering 13 providers
    
    fixes #130
  • Add context overflow detection utilities
    Extract overflow detection logic into reusable utilities:
    - isContextOverflowError() to detect overflow from error messages
    - isContextOverflowFromUsage() to detect overflow from token usage
    - Patterns for Anthropic, OpenAI, Google, xAI, Groq, OpenRouter, llama.cpp, LM Studio
    
    Fixes #129
  • Add image support in tool results across all providers
    Tool results now use content blocks and can include both text and images.
    All providers (Anthropic, Google, OpenAI Completions, OpenAI Responses)
    correctly pass images from tool results to LLMs.
    
    - Update ToolResultMessage type to use content blocks
    - Add placeholder text for image-only tool results in Google/Anthropic
    - OpenAI providers send tool result + follow-up user message with images
    - Fix Anthropic JSON parsing for empty tool arguments
    - Add comprehensive tests for image-only and text+image tool results
    - Update README with tool result content blocks API
  • Fix token statistics on abort for Anthropic provider
    - Add handling for message_start event to capture initial token usage
    - Fix message_delta to use assignment (=) instead of addition (+=)
      since Anthropic sends cumulative token counts, not incremental
    - Add comprehensive tests for all providers (Google, OpenAI Completions,
      OpenAI Responses, Anthropic)
    - Document OpenAI limitation: token stats only available at stream end
    
    Fixes issue where aborted streams had zero token counts despite
    Anthropic sending input tokens in the initial message_start event.