# 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 IDs**: Stored locally in `~/.config/codex-launcher/oauth-secrets.json` (not in repo)
- **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": "<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

```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-<session>.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
