# Antigravity (Google CloudCode) — Technical Reference Everything needed to understand, maintain, and debug the Antigravity OAuth provider integration in Codex Launcher. --- ## 1. What Is Antigravity? Antigravity is Google's internal codename for **Google CloudCode** — a cloud-based AI coding agent powered by Gemini and other models. The CLI tool (`agy`) is a native Go binary that uses gRPC to communicate with Google's CloudCode backend. - **Official CLI binary**: `~/.local/bin/agy-core` (ELF x86-64 Go binary, ~183MB) - **Wrapper script**: `~/.local/bin/agy` (Python, manages provider switching) - **CLI settings**: `~/.gemini/antigravity-cli/settings.json` - **Provider state**: `~/.gemini/antigravity-cli/agy_provider.json` --- ## 2. Two API Protocols — REST vs gRPC ### 2.1 What the agy CLI uses (gRPC) The native `agy-core` binary uses **gRPC** to communicate with the CloudCode backend: - **Service**: `google.internal.cloud.code.v1internal.PredictionService` - **Methods**: - `GenerateContent` — main inference - `FetchAvailableModels` — list available models - `CountTokens` — token counting - `RetrieveUserQuota` — quota check - **Other services**: `CloudCode`, `JetskiService` (settings, plugins, etc.) - **Proto files**: `google/internal/cloud/code/v1internal/prediction_service.proto`, `cloudcode.proto` - **Model IDs in gRPC**: Display names like `"Gemini 3.5 Flash (High)"` — verified from `settings.json` ### 2.2 What our proxy uses (REST) Our Codex Launcher proxy does NOT use gRPC. It uses the **REST API** that the CloudCode backend also exposes: - **Endpoint path**: `v1internal:generateContent` (non-streaming) / `v1internal:streamGenerateContent?alt=sse` (streaming SSE) - **This is NOT the standard Gemini REST API** — it's the CloudCode-internal REST gateway - **Model IDs in REST**: Slug-style IDs like `gemini-3-flash` — NOT display names - **The REST API is more limited** — fewer model variants available than gRPC ### 2.3 Why not gRPC? The agy binary uses gRPC with protobuf serialization. Using gRPC from the proxy would require: - Maintaining proto definitions (compiled from the binary) - More complex streaming - The `grpcio` Python library (not installed by default) The REST API works well enough for our use case. --- ## 3. Endpoints The proxy tries these endpoints in order for Antigravity: ``` 1. https://daily-cloudcode-pa.sandbox.googleapis.com (primary) 2. https://autopush-cloudcode-pa.sandbox.googleapis.com (fallback) 3. https://cloudcode-pa.googleapis.com (production fallback) ``` For regular Gemini CLI OAuth, only `cloudcode-pa.googleapis.com` is used. --- ## 4. Authentication ### 4.1 OAuth Flow - **Client ID**: `884354919052-36trc1jjb3tguiac32ov6cod268c5blh.apps.googleusercontent.com` (also `1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com`) - **OAuth callback**: `https://antigravity.google/oauth-callback` - **Token storage**: `~/.cache/codex-proxy/google-antigravity-oauth-token.json` - **Token refresh**: via `https://oauth2.googleapis.com/token` - **Scopes**: `email profile openid cloud-platform cclog experimentsandconfigs userinfo.email userinfo.profile` - **Note**: The token does NOT have `auth/aicode` scope — it uses `cloud-platform` instead ### 4.2 Multi-Account Support - `GoogleAccountPool("antigravity")` manages multiple Google accounts - Token files: `google-antigravity-oauth-token.json`, `google-antigravity-oauth-token-2.json`, etc. - Round-robin rotation across accounts --- ## 5. Request Format ### 5.1 REST Request Wrapper The proxy wraps the Gemini-format request body in an outer envelope: ```json { "project": "", "model": "", "requestType": "agent", "userAgent": "antigravity", "requestId": "agent-", "request": { "contents": [...], "systemInstruction": {...}, "generationConfig": {...}, "tools": [...] } } ``` ### 5.2 Required Headers ``` Content-Type: application/json Authorization: Bearer User-Agent: antigravity/ darwin/arm64 ``` The User-Agent version is auto-fetched from: - `https://antigravity-auto-updater-974169037036.us-central1.run.app` - Fallback: `https://antigravity.google/changelog` - Cached in `~/.cache/codex-proxy/antigravity-version.json` - Default: `1.18.3` --- ## 6. Model ID Mapping (CRITICAL) ### 6.1 The Problem The agy CLI shows models with display names: - `Gemini 3.5 Flash (High)` - `Claude Sonnet 4.6 (Thinking)` But the **REST API only accepts slug IDs**: - `gemini-3-flash` - `claude-sonnet-4-6` Sending display names to the REST API returns **HTTP 404 "Requested entity was not found"**. ### 6.2 Verified Working Model IDs All tested with live API calls to `daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent` on 2026-05-25: | Display Name (agy CLI / GUI) | REST API Model ID | Status | |---|---|---| | Gemini 3.5 Flash (High) | `gemini-3-flash` | OK | | Gemini 3.5 Flash (Medium) | `gemini-3-flash` | OK | | Gemini 3.5 Flash (Low) | `gemini-3.5-flash-low` | OK | | Gemini 3.1 Pro (High) | `gemini-3.1-pro-low` | OK (only low tier works via REST) | | Gemini 3.1 Pro (Low) | `gemini-3.1-pro-low` | OK | | Claude Sonnet 4.6 (Thinking) | `claude-sonnet-4-6` | OK | | Claude Opus 4.6 (Thinking) | `claude-opus-4-6-thinking` | OK | | GPT-OSS 120B (Medium) | `gpt-oss-120b-medium` | OK | | Gemini 2.5 Flash | `gemini-2.5-flash` | OK | | Gemini 2.5 Flash Lite | `gemini-2.5-flash-lite` | OK | | Gemini 2.5 Pro | `gemini-2.5-pro` | 503 (exists, no capacity) | ### 6.3 Models That Return 404 via REST These exist in gRPC but NOT in the REST API: ``` gemini-3-flash-high, gemini-3-flash-medium, gemini-3-flash-low gemini-3.5-flash, gemini-3.5-flash-high, gemini-3.5-flash-medium gemini-3.1-pro-high (400, not 404, but doesn't work) gemini-3-pro, gemini-3-pro-high, gemini-3-pro-low (500) gemini-3.1-flash, gemini-3.1-flash-high claude-sonnet-4, claude-sonnet-4-5, claude-sonnet-4-6-thinking claude-opus-4, claude-opus-4-5 claude-haiku-4-5 gpt-oss-120b, gpt-oss-120b-maas, gpt-oss-20b-maas ``` ### 6.4 How the Mapping Works 1. GUI shows display names (matching agy CLI): `Gemini 3.5 Flash (High)` 2. Codex CLI sends whatever model ID the user selected 3. Proxy `alias_map` translates: `"Gemini 3.5 Flash (High)" → "gemini-3-flash"` 4. Proxy sends REST request with `"model": "gemini-3-flash"` The alias map is in `_handle_gemini_oauth()` around line 4316 of `translate-proxy.py`. --- ## 7. Response Format ### 7.1 Non-Streaming ```json { "response": { "candidates": [{ "content": { "role": "model", "parts": [{"text": "..."}] }, "finishReason": "STOP" }] } } ``` ### 7.2 Streaming (SSE) Content-Type: `text/event-stream` Each SSE event contains a JSON chunk with the same structure. The proxy converts these to OpenAI Responses API format for Codex CLI. --- ## 8. Context Sizes ```python "Gemini 3.5 Flash": 1000000, "Gemini 3.1 Pro": 2000000, "gemini-3-flash": 1000000, "gemini-3.1-pro-low": 2000000, "gemini-3.5-flash-low": 1000000, "Claude Sonnet 4.6": 200000, "Claude Opus 4.6": 200000, "claude-sonnet-4-6": 200000, "claude-opus-4-6-thinking": 200000, "GPT-OSS 120B": 128000, "gpt-oss-120b-medium": 128000, "gemini-2.5-flash": 1000000, "gemini-2.5-pro": 2000000, ``` --- ## 9. Key Proxy Code Locations | Component | File | Line (approx) | |---|---|---| | Antigravity version | translate-proxy.py | 287-288 | | Version fetcher | translate-proxy.py | 705-748 | | Model alias map | translate-proxy.py | ~4316 | | REST request building | translate-proxy.py | ~4563-4602 | | Endpoint fallback loop | translate-proxy.py | ~4610 | | SSE streaming handler | translate-proxy.py | `_forward_gemini_sse()` | | Auto-continue for MAX_TOKENS | translate-proxy.py | `_auto_continue_gemini()` | | OAuth token refresh | translate-proxy.py | `_refresh_oauth_token_for()` | | Google account pool | translate-proxy.py | `_google_antigravity_pool` | | GUI preset models | codex-launcher-gui | ~358 | | GUI static model list | codex-launcher-gui | ~760 `_ANTIGRAVITY_MODELS` | | GUI fetch_models shortcut | codex-launcher-gui | ~770 `fetch_models_for_endpoint()` | --- ## 10. Debugging ### 10.1 Debug Logs - **Proxy stderr**: Shows model mapping, request details, errors - **400 error dump**: `~/.cache/codex-proxy/gemini-last-400-request.json` - **Long context dump**: `~/.cache/codex-proxy/gemini-long-ctx-.json` ### 10.2 Quick API Test ```bash TOKEN=$(python3 -c "import json; print(json.load(open('$HOME/.cache/codex-proxy/google-antigravity-oauth-token.json'))['access_token'])") curl -s "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "User-Agent: antigravity/2.0.1 darwin/arm64" \ -d '{ "project": "voltaic-hangout-z1qhf", "model": "gemini-3-flash", "requestType": "agent", "userAgent": "antigravity", "requestId": "test-123", "request": { "contents": [{"role": "user", "parts": [{"text": "say hi"}]}] } }' ``` ### 10.3 Token Info ```bash TOKEN=$(python3 -c "import json; print(json.load(open('$HOME/.cache/codex-proxy/google-antigravity-oauth-token.json'))['access_token'])") curl -s "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=$TOKEN" | python3 -m json.tool ``` ### 10.4 Common Errors | Error | Cause | Fix | |---|---|---| | 404 "Requested entity was not found" | Wrong model ID (display name instead of slug) | Check alias_map | | 404 on `/v1/models` | Antigravity has no models REST endpoint | Proxy returns static list | | 404 on POST /responses | Codex CLI routing issue, not Antigravity | Check proxy is running | | 503 "No capacity" | Model exists but overloaded | Try another model or endpoint | | 500 "Unknown Error" | Model ID exists but broken on server | Known for gemini-3-pro-low | | PERMISSION_DENIED (gRPC) | Token lacks scope or empty request body | Use REST API instead | --- ## 11. Version History (Antigravity-specific) | Version | Date | Change | |---|---|---| | v3.10.3 | 2026-05-25 | **Fix 404**: Verified REST model IDs, display→slug mapping | | v3.10.2 | 2026-05-25 | Wrong fix: tried display names (didn't work) | | v3.10.0 | 2026-05-25 | Provider model editor, static Antigravity model list | | v3.9.9 | 2026-05-25 | Refreshed Antigravity models (slugs were wrong) | | v3.3.0 | Earlier | Initial Antigravity OAuth + tool calls + SSE streaming | --- ## 12. Testing a New Model ID If new models appear in the agy CLI, verify them against the REST API before adding: ```python # Test a candidate model ID import urllib.request, json, os token = json.load(open(os.path.expanduser("~/.cache/codex-proxy/google-antigravity-oauth-token.json")))["access_token"] wrapped = { "project": "voltaic-hangout-z1qhf", "model": "NEW-MODEL-ID", "requestType": "agent", "userAgent": "antigravity", "requestId": "test-123", "request": {"contents": [{"role": "user", "parts": [{"text": "say hi"}]}]}, } req = urllib.request.Request( "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent", data=json.dumps(wrapped).encode(), headers={"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "antigravity/2.0.1 darwin/arm64"}, ) try: resp = urllib.request.urlopen(req, timeout=15) print("OK:", resp.read().decode()[:200]) except urllib.error.HTTPError as e: print(f"{e.code}:", e.read().decode()[:200]) ``` Then update: 1. `alias_map` in `translate-proxy.py` — add display name → REST slug mapping 2. `_ANTIGRAVITY_MODELS` in `codex-launcher-gui` — add display name to list 3. Preset in `codex-launcher-gui` — add display name to `"Google Antigravity (OAuth)"` models 4. Context sizes in `translate-proxy.py` — add model ID to `_MODEL_CTX` dict