Files
Codex-Launcher---Any-AI-Por…/docs/ANTIGRAVITY.md

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 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:

{
  "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-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

{
  "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:

  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