The hybrid strategy now considers account quota levels when selecting
accounts, preventing any single account from being drained to 0%.
- Add QuotaTracker class to track per-account quota levels
- Exclude accounts with critical quota (<5%) from selection
- Add quota component to scoring formula (weight: 3)
- Fall back to critical accounts when no alternatives exist
- Add 18 new tests for quota-aware selection
Scoring formula: Health×2 + Tokens×5 + Quota×3 + LRU×0.1
An attempt at resolving badrisnarayanan/antigravity-claude-proxy#171
Adds `maxAccounts` configuration parameter to control the maximum number of Google accounts.
**Changes:**
- New config field `maxAccounts` (default: 10, range: 1-100)
- Settings page: slider control for adjusting limit
- Accounts page: counter badge (e.g., "8/10") with visual feedback
- Add button disabled when limit reached
- Server-side validation on account creation
**Breaking Changes:** None
Adds logging when 403/404 errors trigger endpoint fallback (daily → prod).
The retry behavior was already working but silently - now it's visible.
Matches opencode-antigravity-auth error handling behavior.
Co-Authored-By: Claude <noreply@anthropic.com>
The version was stuck at "1.0.0" because fetchVersion() was only called
when initialLoad was true, but loadFromCache() set initialLoad to false
before fetchData() ran. Now version is fetched unconditionally in the
global store's init().
Fixes#144
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes regression where /v1/models returned 500 error because
pickNext() method was removed in v2.2.x refactor.
Fixes#164
Co-Authored-By: Claude <noreply@anthropic.com>
paidTier values like g1-pro-tier and g1-ultra-tier are rejected by
the onboardUser API with 400 INVALID_ARGUMENT. This matches the
opencode-antigravity-auth reference which only uses allowedTiers.
Co-Authored-By: Claude <noreply@anthropic.com>
- Store project IDs in composite refresh token format (refreshToken|projectId|managedProjectId)
- Add parseRefreshParts() and formatRefreshParts() for token handling
- Extract and persist subscription tier during project discovery
- Fetch subscription in blocking mode when missing from cached accounts
- Fix conditional duetProject setting to match reference implementation
- Export parseTierId() for reuse across modules
Co-Authored-By: Claude <noreply@anthropic.com>
Consolidate strategy configuration by moving STRATEGY_LABELS from
strategies/index.js to constants.js. Update startup banner to
dynamically display strategy options from SELECTION_STRATEGIES.
Co-Authored-By: Claude <noreply@anthropic.com>
Refactor account selection into a strategy pattern with three options:
- Sticky: cache-optimized, stays on same account until rate-limited
- Round-robin: load-balanced, rotates every request
- Hybrid (default): smart distribution using health scores, token buckets, and LRU
The hybrid strategy uses multiple signals for optimal account selection:
health tracking for reliability, client-side token buckets for rate limiting,
and LRU freshness to prefer rested accounts.
Includes WebUI settings for strategy selection and unit tests.
Co-Authored-By: Claude <noreply@anthropic.com>
Set functionCallingConfig.mode = 'VALIDATED' when using Claude models
to ensure strict parameter validation, matching opencode-antigravity-auth.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: preserve whitespace-only chunks in SSE stream
Fixes issue #138 where Claude models would swallow spaces between words because whitespace-only chunks (e.g., " ") were being filtered out as empty.
Changes:
- Modified sse-streamer.js to only skip truly empty strings (""), preserving strings that contain only whitespace.
- Added regression test case in tests/test-streaming-whitespace.cjs to verify whitespace preservation.
* test: add streaming whitespace regression test to main suite
---------
Co-authored-by: walczak <walczak@ial.ruhr>
- Add pt.js translation file with complete PT-BR translations
- Add Portuguese option to language selector in settings
- Load pt.js translation file in index.html
Based on PR #108
Co-authored-by: Pedro Farias <feliperodriguesf62@gmail.com>
Co-authored-by: Wha1eChai <whaleora@gmail.com>
Some accounts have paidTier.id = "free-tier" but allowedTiers includes
"standard-tier", meaning they can use Pro-level quotas even though their
Google One subscription doesn't grant Antigravity benefits.
New priority:
1. paidTier - if Pro/Ultra, use immediately
2. allowedTiers - if paidTier is free, check for higher tiers
3. currentTier - fallback if still unknown
4. allowedTiers default - final fallback
This fixes accounts that show as "free" but actually have Pro access
through programs other than Google One AI.
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: add i18n support with separate translation files
- Extract translations from store.js to separate files for easier management
- Add translation files for English (en.js), Indonesian (id.js), Turkish (tr.js), and Chinese (zh.js)
- Load translations via window.translations object before Alpine store initialization
- Add Bahasa Indonesia option to language selector
* feat: translate remaining hardcoded UI strings
- Update index.html to use t() for Menu and GitHub labels
- Update views to translate Tier, Quota, Live, tier badges, and close button
- Update components to use translated error messages and confirmation dialogs
- Update utils to use translated validation and error messages
- Update app-init.js to use translated OAuth success/error messages
- Use raw API tier IDs (e.g., 'free-tier', 'g1-pro-tier') consistently
- Pass DEFAULT_PROJECT_ID for non-free tiers during onboarding
- Fix free tier detection to use .includes('free') instead of exact match
- Upgrade onboarding error logs from debug to warn for visibility
- Add debug logging for onboarding request parameters
Fixes onboarding failures for Pro/Ultra accounts that were previously
failing because the API requires a project ID for paid tier onboarding.
Co-Authored-By: Claude <noreply@anthropic.com>
- Use parseTierId() consistently for all tier sources (paidTier, currentTier, allowedTiers)
- Add tierSource tracking to identify which field tier was detected from
- Add verbose debug logging to show all tier-related fields from loadCodeAssist API
- Fix onboarding to prioritize paidTier > currentTier > allowedTiers (consistent with model-api.js)
This helps diagnose issues where Pro/Ultra accounts are incorrectly detected as free.
Fixes#128
Co-Authored-By: Claude <noreply@anthropic.com>
When all accounts are rate-limited for > 2 minutes, now attempts
model fallback (if enabled) before throwing the error. This allows
quota-exhausted accounts to gracefully fall back to alternate models.
Co-Authored-By: Claude <noreply@anthropic.com>
- Pass project ID to fetchAvailableModels for accurate per-project quota
- Treat missing remainingFraction with resetTime as 0% (exhausted)
- Fix double-escaped regex in rate-limit-parser.js (\\d -> \d)
- Use ANTIGRAVITY_HEADERS for loadCodeAssist consistency
- Store actual reset time from API instead of capping at default
- Add getRateLimitInfo() for detailed rate limit state
- Handle disabled accounts in rate limit checks
Fixes issue where free tier accounts showed 100% quota but were actually exhausted.
Co-Authored-By: Claude <noreply@anthropic.com>
When loadCodeAssist returns no project, automatically call onboardUser API
to provision a managed project. This handles first-time setup for new accounts.
Co-Authored-By: Claude <noreply@anthropic.com>
When Claude Code strips thinking signatures it doesn't recognize,
the proxy would drop unsigned thinking blocks, causing the error
"Expected thinking but found text". This fix detects unsigned
thinking blocks and triggers recovery to close the tool loop.
Co-Authored-By: Claude <noreply@anthropic.com>
The tier detection was incorrectly showing Pro accounts as "free" due to:
1. paidTier field being flaky/missing from some API responses
2. standard-tier not being recognized as a Pro tier
3. No fallback to allowedTiers when currentTier is missing
Changes:
- Add parseTierId() helper to centralize tier ID parsing
- Recognize "standard-tier" as Pro (Gemini Code Assist paid tier)
- Add fallback chain: paidTier > currentTier > allowedTiers
- Return "unknown" instead of incorrectly defaulting to "free"
Fixes#121
Co-Authored-By: Claude <noreply@anthropic.com>
When multiple tool_results contain images, the inlineData parts were
being interleaved between functionResponse parts, breaking Claude's API
requirement that functionResponse parts be consecutive.
Co-Authored-By: Claude <noreply@anthropic.com>
Replace gpt-tokenizer with model-specific official tokenizers:
- Claude models: @anthropic-ai/tokenizer (official Anthropic tokenizer)
- Gemini models: @lenml/tokenizer-gemini (GemmaTokenizer)
Changes:
- Add @anthropic-ai/tokenizer and @lenml/tokenizer-gemini dependencies
- Remove gpt-tokenizer dependency
- Update count-tokens.js with model-aware tokenization
- Use getModelFamily() to select appropriate tokenizer
- Lazy-load Gemini tokenizer (138MB) on first use
- Default to local estimation for all content types (no API calls)
Tested with all supported models:
- claude-sonnet-4-5, claude-opus-4-5-thinking, claude-sonnet-4-5-thinking
- gemini-3-flash, gemini-3-pro-low, gemini-3-pro-high
- Add ensureInitialized() call before count_tokens handler
- Use hybrid approach: local estimation for text, API for images/docs
- This prevents "No accounts available" error on first request
Switch from local estimation (gpt-tokenizer) to API-based counting
via Google Cloud Code API for accurate token counts. Falls back to
local estimation if API call fails.
Add comprehensive test suite for /v1/messages/count_tokens endpoint:
- Simple text messages
- Multi-turn conversations
- System prompts (string and array format)
- Tool definitions and tool use/result blocks
- Thinking blocks
- Content arrays with text blocks
- Error handling for invalid requests
- Long text tokenization
Also adds npm script test:counttokens for running tests individually.
Add Anthropic-compatible token counting endpoint using hybrid approach:
- Local estimation with gpt-tokenizer for text content (~95% accuracy)
- API-based counting for complex content (images, documents)
- Automatic fallback to local estimation on API errors
This resolves warnings in LiteLLM and other clients that rely on
pre-request token counting.
Feat(ui): add Turkish language support and UI enhancements
Introduces Turkish language support and several UI/UX improvements to the web management interface.
- Remove nested x-data from save preset modal to fix scope access
- Add newPresetName to parent component state
- Ensure savingPreset state is correctly accessed from the button
- Clear input field on modal open and after successful save