Skip to content

Antigravity

HTTP를 통해 Google Antigravity 백엔드에 도달하기 — 로그인, 프로젝트 디스커버리, 모델 슬러그, thinking, 어댑터가 전달하는 것.

Updated View as Markdown

내장 antigravity 프로바이더는 HTTP를 통해 daily-cloudcode-pa.googleapis.com에 있는 Google의 Antigravity 백엔드에 도달합니다. 이 호스트는 Antigravity 클라이언트 자신이 디스커버리와 추론 양쪽에 사용하는 daily- 컨트롤 플레인입니다. gemini 프로바이더와 동일한 Code Assist 프로토콜을 사용하며 — shunt는 Anthropic Messages를 generateContent / streamGenerateContent로 변환합니다 — 다만 Antigravity 구독 토큰으로 인증하고 프로젝트 디스커버리 과정에서 자신을 ideType: ANTIGRAVITY로 식별합니다.

Antigravity에는 두 번째의 더 오래된 전송이 있습니다. kind = "antigravity_cli"는 로컬 agy 바이너리를 서브프로세스로 실행하며 더 이상 사용되지 않습니다더 이상 사용되지 않는 antigravity-cli 전송을 참고하세요. 이 페이지는 그런 장치가 전혀 필요 없는 HTTP 프로바이더를 다룹니다.

빠른 시작

코딩 에이전트가 대신 구성하도록 하세요 — shunt add는 내장된 설정 블루프린트를 출력합니다 (오프라인·읽기 전용이며, 구성은 에이전트가 편집하고 이 명령은 절대 편집하지 않습니다):

shunt add upstream antigravity --print | claude

또는 아래의 수동 단계를 따르세요.

1. 로그인

shunt login antigravity

이는 Antigravity 자체의 OAuth 클라이언트를 사용하는 Google authorization-code 플로우를 실행하고 ~/.shunt/antigravity-auth.json을 기록하며, shunt는 이 파일을 읽고 자동 갱신합니다. 이 플로우는 다섯 개의 스코프를 요청합니다:

https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/cclog
https://www.googleapis.com/auth/experimentsandconfigs

마지막 두 개가 바로 Gemini CLI 로그인을 여기서 재사용할 수 없고, 그 반대도 마찬가지인 이유입니다: ~/.gemini/oauth_creds.json 토큰은 결코 cclogexperimentsandconfigs를 담지 않습니다. google_oauthantigravity_oauth는 서로 교체할 수 없습니다.

로그인은 Code Assist 프로젝트도 함께 해석하며 — 먼저 loadCodeAssist, 그다음 처음 사용하는 계정을 프로비저닝하기 위한 onboardUser — 프로젝트 id를 자격 증명 파일에 저장합니다. 첫 요청 앞에 디스커버리가 놓이지 않습니다. 디스커버리가 실패해도 로그인은 토큰을 저장하고 첫 요청에서 재시도합니다.

2. 업스트림 구성

프로바이더는 내장이므로, provider = "antigravity"를 지정한 [[routes]] 항목만으로 자체 프로바이더 테이블 없이 동작합니다. 순서가 있는 업스트림으로 선언하는 것은 다릅니다: Antigravity 종류에는 [[upstreams]] 프리셋이 없으므로, 거기서는 provider = "antigravity"를 쓸 수 없고 kind, base_url, auth를 직접 설정해야 합니다:

[[upstreams]]
name = "anthropic"
provider = "anthropic"   # 라우트가 없는 모델(예: claude-*)의 기본값으로 Anthropic을 유지

[[upstreams]]
name = "antigravity"
kind = "antigravity"
base_url = "https://daily-cloudcode-pa.googleapis.com"
auth = "antigravity_oauth"

순서가 있는 [[upstreams]]는 shunt의 내장 프로바이더를 대체하므로, 명시적 구성은 여전히 폴백 대상인 anthropic 기본값도 함께 선언해야 합니다(server.default_provider의 기본값은 anthropic입니다).

레거시 [providers.antigravity] 테이블 형식도 계속 지원됩니다 — 다만 한 파일에서 [[upstreams]][providers.*]를 섞지 마세요.

3. 모델 라우팅

shunt는 이 프로바이더에 대해 모델 허용 목록을 두지 않습니다: 해석된 upstream_model이 적힌 그대로 백엔드로 전송되고, 판단은 백엔드가 합니다. Antigravity는 Code Assist gemini 프로바이더가 받아들이지 않는 Gemini 계열 슬러그를 제공합니다 — 다만 effort를 id에 담아 두 가지 형태 중 하나로 게시합니다: -low / -medium / -high 접미사(Pro에는 -low-high만 있습니다), 또는 effort를 요청 필드로 받는 단일 -tiered id입니다. 어느 형태가 오는지는 계정마다 다르고 시간이 지나면 바뀝니다 — 어떤 계정은 gemini-3.8-flash-tiered를, 다른 계정은 gemini-3.8-flash-medium을 받습니다. 현재 카탈로그는 agy models로 확인할 수 있으며, 이 글을 쓰는 시점에는 gemini-3.8-flash-*, gemini-3.7-flash-*, gemini-3.6-flash-*, gemini-3.1-pro-low / -high, claude-sonnet-4-6, claude-opus-4-6-thinking, gpt-oss-120b-medium이 들어 있습니다.

접미사가 없는 슬러그는 제공되지 않습니다: daily-cloudcode-pa.googleapis.com404로 답합니다. 프로덕션 호스트에서는 이전의 shunt 요청 — 접미사 없는 id와 평범한 Code Assist 엔벨로프 — 이 요청량 제한처럼 보이는 429 RESOURCE_EXHAUSTED(“check quota”)로 돌아왔습니다. 다만 탐침에서 두 입력을 함께 바꾸었기 때문에 둘 중 무엇이 그 429를 만들었는지는 확인되지 않았습니다.

두 형태 중 어느 쪽인지 추측하지 않도록, shunt는 요청 경로에서 계정 자신의 카탈로그 (fetchAvailableModels)를 읽어 — 계정별 10분 캐시, 시간 제한, 실패 시 통과 — 그 계정이 실제로 게시하는 id를 고릅니다.

로컬 별칭을 정확한 Antigravity 슬러그에 매핑하세요:

[[models]]
id = "claude-gemini-3.6-flash-via-antigravity"
display_name = "[AGY ] Gemini-3.6-Flash"

[[routes]]
model = "claude-gemini-3.6-flash-via-antigravity"
provider = "antigravity"
upstream_model = "gemini-3.6-flash-medium"

접미사를 직접 쓸 필요는 없습니다. 접미사가 없는 gemini-* id에 대해서는 shunt가 등급을 직접 해석해 붙이며, 다음 신호 중 먼저 적용되는 것을 따릅니다:

  1. [[routes]] 항목이나 프로바이더의 effort — 명시적 지정입니다. 여기서도 xhighmaxhigh로 접히며, 대소문자와 앞뒤 공백은 무시됩니다. shunt가 모르는 등급은 적힌 대로(공백을 제거하고 소문자로) 통과하므로 카탈로그에 나중에 추가될 등급을 미리 지정할 수 있습니다.
  2. 요청의 output_config.effort(Claude Code는 low/medium/high/xhigh/max를 보내며, xhighmaxhigh로 접힙니다).
  3. thinking.type = "enabled"budget_tokens가 2048 이하이면 low, 8192 이하이면 medium, 그보다 크면 high입니다. budget을 명시하지 않은 활성 블록은 변환된 요청이 싣는 것과 같은 기본값 1024를 사용하므로 low가 됩니다.
  4. 그 외에는 medium입니다.

그 등급이 무엇이 될지는 카탈로그가 정합니다. 계정이 {id}-{등급}을 게시하면 그것이 나갑니다. {id}-tiered만 게시하면 shunt는 tiered id를 보내고 등급은 generationConfig.thinkingConfig.thinkingLevel에 실어 보냅니다(백엔드가 함께 받아들이므로 기존 thinkingBudget은 그대로 둡니다). 둘 다 없지만 같은 모델의 다른 등급을 게시한다면 가장 가까운 게시 등급이 이기고, 동점이면 더 높은 쪽이 됩니다 — -pro id의 medium-high가 되는 것은 Pro 전용 규칙이 아니라 이 규칙 때문입니다.

직접 적어 넣은 접미사도 똑같이 다뤄집니다. 계정이 gemini-3.6-flash-medium을 더 이상 게시하지 않는다면, 그 접미사는 여전히 원하는 등급을 말해 주므로 shunt는 카탈로그가 404임을 방금 확인한 id를 그대로 보내는 대신 위 규칙으로 다시 해석합니다. 카탈로그가 그 계열을 전혀 모른다면 적어 넣은 값을 그대로 둡니다. 직접 지정한 -tiered id는 id를 유지한 채 effortthinkingLevel로 계속 싣기 때문에, 실제로 게시된 id를 그대로 적어도 effort 제어를 잃지 않습니다. 계정이 나중에 접미사 id로 되돌아가면 그 pin도 같은 방식으로 다시 해석됩니다. id가 사라졌다는 증거는 방금 가져온 카탈로그뿐이므로, 갱신에 실패해 마지막으로 알던 카탈로그를 쓰는 동안에는 모든 pin이 적힌 그대로 나갑니다.

카탈로그를 쓸 수 없을 때(디스커버리 장애, fetchAvailableModels에 답하지 않는 백엔드)에는 Pro 조정을 포함한 -{등급} 덧붙이기 — 0.40.0 동작 — 로 되돌아가므로, 조회 실패는 추측만 잃을 뿐 요청을 잃지 않습니다. 실패한 조회는 1분 동안 기억한 뒤 다시 시도하므로, 닿지 않는 컨트롤 플레인의 비용은 메시지마다가 아니라 1분에 한 번으로 제한되며, 조회 자체도 Gemini id에 대해서만 일어납니다. 설정에 적힌 인식되지 않는 effort는 카탈로그가 없을 때 1번 규칙대로 적힌 그대로 덧붙여지며 조정되지 않습니다. 카탈로그가 있으면 그 등급이 제공되지 않는다고 방금 확인한 셈이므로, 계정이 게시하는 등급으로 접힙니다. -tiered 경로에서는 백엔드가 모르는 thinkingLevel을 거부하므로 medium으로 접힙니다. 카탈로그가 그대로 게시하는 id, 그리고 Gemini가 아닌 id(claude-sonnet-4-6, gpt-oss-120b-medium)는 적힌 그대로 전송됩니다. 인식되지 않는 output_config.effort는 모델 id에 반영되지 않고 medium으로 되돌아갑니다. thinking을 명시적으로 끈 요청(thinking.type = "disabled")에는 thinkingLevel이 붙지 않습니다. 변환된 thinkingBudget: 0이 그대로 유지됩니다.

Antigravity는 Claude 모델도 제공하지만, shunt는 아직 그 모델들이 필요로 하는 요청 재작성을 구현하지 않았습니다(#368). 로컬에서 그런 슬러그를 거부하는 것은 아무것도 없으며 — 적힌 그대로 백엔드에 도달합니다 — 그러니 당분간은 Gemini 계열 슬러그만 라우팅하세요.

어댑터가 전달하는 것

어댑터는 어시스턴트의 텍스트thinking을 Anthropic SSE로 스트리밍하고, 클라이언트의 도구를 네이티브로 브리지합니다: tool_use는 Gemini functionCall이 되고, tool_resultfunctionResponse가 되며, 도구 선언과 도구 선택도 함께 변환됩니다. 시스템 프롬프트systemInstruction이 됩니다. 토큰 사용량은 Google이 보고한 수치입니다 — promptTokenCountcandidatesTokenCountinput_tokensoutput_tokens에 매핑됩니다.

Thinking은 요청을 따릅니다: thinking.type = "enabled"budget_tokens(기본값 1024)로부터 thinkingConfig.thinkingBudget을 설정하고, "disabled"는 이를 0으로 설정합니다. 활성화된 thinking 블록은 위 모델 라우팅에서 설명한 대로 모델의 effort 등급을 고르는 신호이기도 합니다.

또한 모든 요청은 Antigravity 클라이언트가 보내는 에이전트 신원을 함께 전달합니다: userAgent: "antigravity", requestType: "agent", 요청마다 생성되는 requestId, 그리고 후속 턴이 같은 세션에 도달하도록 대화에서 가장 먼저 나오는 사용자 텍스트에서 유도한 sessionId입니다. gemini 프로바이더의 Code Assist 요청은 이 중 어느 것도 보내지 않습니다.

도구 스키마는 Gemini의 Schema 방언에 맞게 조정됩니다 — 이 프로바이더와, 같은 어댑터를 쓰는 gemini 프로바이더 모두에서요. $schema, $id, $comment, propertyNames, patternProperties, exclusiveMinimum, exclusiveMaximum, const는 제거되고, 그 외의 키워드는 작성된 그대로 전달됩니다. type 목록은 첫 번째 non-null 항목을 유지하며, 목록에 null이 있으면 nullable: true가 설정되고, null만 있는 목록은 nullable string이 됩니다. 배열이 아닌 모든 스키마 — 다른 멤버로 정해진 유니언이든, 평범한 스칼라든 — 는 prefixItems와 스키마 객체가 아닌 items를 버리고, 객체 값 items는 그대로 유지합니다. 타입 없는 스키마는 prefixItems나 배열 값 또는 객체 값 items를 가지면 배열로 취급되고, 그렇지 않으면 홀로 남은 불리언 items만 버립니다. 모든 배열은 단일 items 스키마로 전송됩니다: 튜플 — prefixItems, 또는 draft-07의 배열 값 items — 은 하나로 접히는데, 동일한 위치들은 그 스키마를 유지하고, 배열이 아닌 타입만 일치하는 위치들은 그 타입을 유지하며, 그 외에는 첫 위치의 스키마가 남습니다. prefixItems 옆에 타입이 선언된 items 스키마가 이미 있으면 그 스키마가 그대로 유지되고 위치들은 버려집니다. 타입이 anyOf/oneOf/allOf 분기 안에 있는 요소도 같은 방식으로 접힙니다. 요소에 대해 아무것도 말하지 않는 배열이나 요소 스키마가 타입을 선언하지 않는 배열은, 요소가 enum이나 properties로 타입을 암시하지 않는 한 items: {"type": "string"}으로 전송됩니다. 이 폴백은 요청을 거부하는 대신 요소 타입을 좁히므로, 튜플이 string 타입으로 도착하는 도구는 라우팅 오류가 아니라 이 조정이 동작한 결과입니다.

프로덕션 트래픽을 라우팅하기 전에 알아둘 만한 제한이 두 가지 있습니다:

  • 이미지는 인라인이어야 합니다. base64 image 블록은 Gemini inlineData가 됩니다. URL 이미지 소스는 400으로 거부됩니다(URL image sources are not supported by the Gemini adapter).
  • 도구 결과는 리치 미디어를 담을 수 없습니다. 내용에 imagedocument 블록이 포함된 tool_result는 거부됩니다.

검증

shunt check    # -> config ok
shunt run
curl -sS http://127.0.0.1:3001/v1/messages \
  -H 'anthropic-version: 2023-06-01' \
  -H 'content-type: application/json' \
  -d '{"model":"claude-gemini-3.6-flash-via-antigravity","max_tokens":16,"messages":[{"role":"user","content":"Reply with OK."}]}'

응답의 x-gateway-upstream 헤더가 antigravity를 가리키는지 확인한 다음, Claude Code를 shunt로 지정하세요.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close