Port Antigravity payload enhancements from CLIProxyAPI v6.6.89:
- Add systemInstruction with role 'user' and Antigravity identity text
- Add requestType: 'agent' to wrapped request body
- Add ANTIGRAVITY_SYSTEM_INSTRUCTION constant with full identity/guidelines
Reference: router-for-me/CLIProxyAPI@67985d8
- Wrap tools in functionDeclarations format for Gemini 3 API
- Flatten incoming functionDeclarations and convert parameters to proper schema
- Re-implement session-level thinking deduplication after partial revert
- Update README model names for clarity (Antigravity vs Gemini CLI)
- Fix model resolver to append default tier for Antigravity Gemini 3 models
The cross-request deduplication was hashing PARTIAL streaming chunks,
not complete thinking blocks. This caused 'Failed to process error
response' errors during streaming.
Within-request deduplication (delta extraction) still works correctly.
Merge conflict resolution and defaults per official API docs.
**Gemini 3** (https://cloud.google.com/vertex-ai/generative-ai/docs/thinking):
> HIGH: Allows the model to use more tokens for thinking... This is the
> default level for Gemini 3 Pro and Gemini 3 Flash.
- Default thinkingLevel: 'high' for both Pro and Flash
- Model-specific levels: Flash (minimal/low/medium/high), Pro (low/high only)
**Claude** (https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking):
> To turn on extended thinking, add a thinking object... and the
> budget_tokens to a specified token budget.
- budget_tokens is required when enabling extended thinking (no default)
- Default thinkingBudget: 32768 (max) for Claude thinking models without variant
**Additional changes per maintainer feedback:**
- Simplify Claude variants to 'low' and 'max' only (medium/high not much different)
- Update all README examples and config snippets
- Add 'minimal' to GEMINI_3_THINKING_LEVELS constant
- Change Pro default from 'medium' to 'high' (per Google API docs)
- Document model-specific level availability:
- Flash: minimal, low, medium, high
- Pro: low, high only
- Update README variant examples with correct levels per model
Addresses CodeRabbit review feedback on #131
Fixes#130
## Changes
### Model resolver (already in PR)
- Default thinkingLevel for base Gemini 3 models: Pro → 'medium', Flash → 'minimal'
### Native thinkingLevel support
- extractVariantThinkingConfig now extracts thinkingLevel string for Gemini 3
- Prefer native thinkingLevel when present
- Fall back to budget→level conversion with deprecation warning
### Correct variant config format (README)
- Remove providerOptions.google wrapper from all examples
- Gemini 3: use thinkingLevel string
- Claude: use thinkingConfig.thinkingBudget number
### Code cleanup
- Remove dead Anthropic/OpenRouter checks (all Antigravity routes through Google)
- Add deprecation warning for legacy thinkingBudget on Gemini 3
## Backward Compatibility
- Legacy thinkingBudget for Gemini 3 still works (deprecated)
- Tier-suffixed model names still work
Fixes#130
The skipAlias logic for Antigravity Gemini 3 models was bypassing
thinkingLevel assignment, causing empty responses from the API.
Changes:
- Remove skipAlias branch that skipped thinkingLevel
- All Gemini 3 models now get a default thinkingLevel:
- Pro models: 'medium'
- Flash models: 'minimal'
- Models with explicit tier suffix use the specified tier
Added delta-tracking to streaming transformer to extract only new portions
of thinking text, preventing duplicated blocks in UI.
- Added deduplicateThinkingText() function
- Added sentThinkingBuffer parameter to transformSseLine()
- Added 6 unit tests for deduplication logic
- File locking via proper-lockfile for concurrent write safety
- Atomic writes with temp file + rename pattern
- Merge-on-write pattern to preserve concurrent changes
- PID-based offset so different sessions start at different accounts
- Remove unused import (extractVariantThinkingConfig)
Fixes#70 - Gemini thoughtSignature persisting when switching to Claude
Root cause: sanitizeCrossModelPayloadInPlace() was only called for batched
requests, not for single requests. Gemini's thoughtSignature in tool metadata
persisted and caused Claude to reject with 'Invalid signature in thinking block'.
Bumps version to 1.2.8-beta.0
Add gemini-3 pattern matching to shouldCacheThinkingSignatures() to
enable proper thought signature handling for multi-turn conversations
with function calling on Gemini 3 models, alongside existing Claude support.
Fixes 'Invalid signature in thinking block' error when switching models mid-session.
Root cause: Gemini stores thoughtSignature in metadata.google on tool call parts,
but existing strippers only checked top-level signatures. When switching to Claude
with a tool call, the foreign signature caused validation errors.
Changes:
- Add cross-model-sanitizer module for bi-directional sanitization (Gemini<->Claude)
- Integrate sanitizer into request pipeline for Claude models
- Add 42 new tests (28 unit + 14 integration)
- Add E2E test scripts for 5-model verification
Tested with: Gemini, Claude (Anthropic), Claude (Google), OpenAI, all passing.
- Fix IPv4/IPv6 mismatch by binding server to all interfaces
- Add WSL/SSH/remote environment detection to skip unreachable local server
- Add 30s timeout fallback with manual URL input prompt
- Add --no-browser flag support for headless environments
- Add fetch timeout (10s) to fetchProjectID() to prevent indefinite hangs
- Improve openBrowser() with WSL wslview support
- Deleted the RATE_LIMIT_ROUTING_ANALYSIS.md document as it is no longer needed.
- Enhanced the regression test to provide detailed failure information, including the first failure's stderr output.
- Updated the plugin to handle 400 errors ("Prompt too long") with a synthetic response instead of returning a session-locking error.
- Introduced createSyntheticErrorResponse function to generate a synthetic SSE response for error messages, allowing continued session usage.
- Added tests for createSyntheticErrorResponse to ensure correct behavior and structure of the synthetic SSE events.
- Add 2s deduplication window to prevent rate limit counter inflation from concurrent 429s
- Separate cooldown system from rate limits for non-429 errors (auth failures, 5xx)
- Add quota_fallback config option for automatic quota switching on rate limit
- Add toast notification for 400 'Prompt is too long' errors guiding users to /compact
- Add 5 new cooldown unit tests
- Enhance regression test suite with concurrent test infrastructure
- Add comprehensive rate limit analysis documentation
- Add test-models.ts for validating all supported model endpoints
- Add test-regression.ts for multi-turn regression testing (Issue #50)
- Consolidate Gemini 3 Flash variants (low/medium/high) into single model
- Fix schema structure by flattening nested signature_cache properties
- Extract streaming transformer utilities to dedicated module
- Add tool hardening for Claude models with parameter signature injection
and system instruction prepending (configurable via claude_tool_hardening)
- Add context error detection (prompt_too_long, tool_pairing) with toast
notifications to guide users on recovery actions
- Improve session recovery to handle cases where messageID isn't provided
by fetching and finding the latest assistant message
- Change empty schema placeholder from reason (string) to _placeholder
(boolean) to reduce token usage
- Add duplicate injection prevention for parameter signatures and tool
hardening instructions
- Fix cache key to strip tier suffix from model name (e.g., -high, -low)
preventing cache misses on tier change
- Add thoughtsTokenCount to usage metadata extraction
- Extract and export applyToolPairingFixes helper for centralized tool
pairing logic
- Add comprehensive tests for recovery error detection and request helpers
FixesNoeFabris/opencode-antigravity-auth#62 - WebFetch tool failing with
"Invalid option: expected one of 'text'|'markdown'|'html'" error.
Root cause: Two bugs in JSON schema cleaning for Antigravity API:
1. flattenAnyOfOneOf lost enum values when anyOf/oneOf used const pattern
- Pattern like anyOf: [{const: "text"}, {const: "markdown"}] was
flattened to just the first option, losing other valid values
- Fix: Added tryMergeEnumFromUnion() to detect enum patterns and
merge all values into a single enum array
2. removeUnsupportedKeywords incorrectly removed property NAMES
- The "format" JSON Schema keyword was in UNSUPPORTED_KEYWORDS
- This also removed properties NAMED "format" inside schemas
- Fix: Added isInsideProperties flag to preserve property names
while still removing JSON Schema keywords
Added comprehensive test coverage for enum merging from anyOf/oneOf.