12 KiB
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 inferenceFetchAvailableModels— list available modelsCountTokens— token countingRetrieveUserQuota— 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 fromsettings.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
grpcioPython 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(also1071006060591-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/aicodescope — it usescloud-platforminstead
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:
{
"project": "<gcp-project-id>",
"model": "<rest-model-id>",
"requestType": "agent",
"userAgent": "antigravity",
"requestId": "agent-<uuid>",
"request": {
"contents": [...],
"systemInstruction": {...},
"generationConfig": {...},
"tools": [...]
}
}
5.2 Required Headers
Content-Type: application/json
Authorization: Bearer <access_token>
User-Agent: antigravity/<version> 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-flashclaude-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
- GUI shows display names (matching agy CLI):
Gemini 3.5 Flash (High) - Codex CLI sends whatever model ID the user selected
- Proxy
alias_maptranslates:"Gemini 3.5 Flash (High)" → "gemini-3-flash" - 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
{
"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
"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-<session>.json
10.2 Quick API Test
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
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:
# 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:
alias_mapintranslate-proxy.py— add display name → REST slug mapping_ANTIGRAVITY_MODELSincodex-launcher-gui— add display name to list- Preset in
codex-launcher-gui— add display name to"Google Antigravity (OAuth)"models - Context sizes in
translate-proxy.py— add model ID to_MODEL_CTXdict