docs: Antigravity technical reference — REST API, model IDs, auth, debugging

This commit is contained in:
Roman
2026-05-25 13:24:09 +04:00
Unverified
parent 99d386f030
commit b0bbf6034a

336
docs/ANTIGRAVITY.md Normal file
View File

@@ -0,0 +1,336 @@
# 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**: `REDACTED_ANTIGRAVITY_CLIENT_ID_2`
(also `REDACTED_ANTIGRAVITY_CLIENT_ID`)
- **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