feat(mitm): translate Antigravity cloudcode end-to-end (Gap B) - #4299
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request introduces support for the Antigravity/cloudcode-compatible inbound endpoint. It adds path-based format detection for 'antigravity', a new /v1/antigravity API route, and MITM proxy forwarding logic to route cloudcode envelopes to this endpoint. Unit tests are also added to verify format detection and forwarding target resolution. The review feedback suggests making the translator initialization synchronous to prevent potential race conditions under concurrent startup requests.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| async function ensureInitialized() { | ||
| if (!initialized) { | ||
| await initTranslators(); | ||
| initialized = true; | ||
| console.log("[SSE] Translators initialized for /v1/antigravity"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Since initTranslators is a synchronous function, wrapping it in an async function and using await introduces an unnecessary asynchronous gap (microtask yield) before initialized = true is set. If multiple requests hit this endpoint concurrently at startup, they can all pass the if (!initialized) check and call initTranslators() multiple times. Removing async/await makes the initialization completely synchronous and race-condition free.
| async function ensureInitialized() { | |
| if (!initialized) { | |
| await initTranslators(); | |
| initialized = true; | |
| console.log("[SSE] Translators initialized for /v1/antigravity"); | |
| } | |
| } | |
| function ensureInitialized() { | |
| if (!initialized) { | |
| initTranslators(); | |
| initialized = true; | |
| console.log("[SSE] Translators initialized for /v1/antigravity"); | |
| } | |
| } |
| export async function POST(request: Request): Promise<Response> { | ||
| await ensureInitialized(); | ||
| return await handleChat(request); | ||
| } |
There was a problem hiding this comment.
Since ensureInitialized is now synchronous, we can remove the await keyword when calling it.
| export async function POST(request: Request): Promise<Response> { | |
| await ensureInitialized(); | |
| return await handleChat(request); | |
| } | |
| export async function POST(request: Request): Promise<Response> { | |
| ensureInitialized(); | |
| return await handleChat(request); | |
| } |
The Antigravity IDE speaks cloudcode (the Gemini payload wrapped under
`request`) and expects a cloudcode reply ({response:{candidates}}). The
AgentBridge proxy forwarded that envelope verbatim to /v1/chat/completions
(OpenAI), which 400s on the missing `messages` field — so the IDE could be
decrypted/intercepted but never actually routed to a provider.
Wire the inbound cloudcode path, reusing the already-registered bidirectional
translators (no new translators needed):
- provider.ts: detectFormatFromEndpoint classifies the /antigravity path as
sourceFormat "antigravity" (mirrors /messages -> claude), so the pipeline
translates request antigravity->openai and response openai->antigravity.
- /v1/antigravity route (new): cloudcode-compatible endpoint — just calls
handleChat (mirrors /v1/messages).
- server.cjs: routes cloudcode envelopes to /v1/antigravity (translates both
ways) and plain OpenAI bodies to /v1/chat/completions, via a testable shim.
Tests: forward-target shim (cloudcode vs openai routing) + endpoint format
detection. The antigravity<->openai translators are already covered by
translator-antigravity-to-openai / translator-resp-openai-to-antigravity.
Stacked on #4285 (Gap A). Full Antigravity-IDE e2e validates on the next
standalone deploy (provider.ts + the route compile into .next).
2f9ff38 to
96a939e
Compare
…souzapw#4299) The Antigravity IDE speaks cloudcode (the Gemini payload wrapped under `request`) and expects a cloudcode reply ({response:{candidates}}). The AgentBridge proxy forwarded that envelope verbatim to /v1/chat/completions (OpenAI), which 400s on the missing `messages` field — so the IDE could be decrypted/intercepted but never actually routed to a provider. Wire the inbound cloudcode path, reusing the already-registered bidirectional translators (no new translators needed): - provider.ts: detectFormatFromEndpoint classifies the /antigravity path as sourceFormat "antigravity" (mirrors /messages -> claude), so the pipeline translates request antigravity->openai and response openai->antigravity. - /v1/antigravity route (new): cloudcode-compatible endpoint — just calls handleChat (mirrors /v1/messages). - server.cjs: routes cloudcode envelopes to /v1/antigravity (translates both ways) and plain OpenAI bodies to /v1/chat/completions, via a testable shim. Tests: forward-target shim (cloudcode vs openai routing) + endpoint format detection. The antigravity<->openai translators are already covered by translator-antigravity-to-openai / translator-resp-openai-to-antigravity. Stacked on diegosouzapw#4285 (Gap A). Full Antigravity-IDE e2e validates on the next standalone deploy (provider.ts + the route compile into .next).
Summary
Closes Gap B from the live MITM validation. The Antigravity IDE speaks cloudcode (the Gemini payload wrapped under
request) and expects a cloudcode reply ({response:{candidates}}). The AgentBridge proxy forwarded that envelope verbatim to/v1/chat/completions(OpenAI) →400 'messages', so the IDE could be decrypted/intercepted but never routed to a provider.The fix wires the inbound cloudcode path, reusing the already-registered bidirectional translators — no new translators:
provider.ts:detectFormatFromEndpointclassifies the/antigravitypath assourceFormat="antigravity"(mirrors/messages→ claude). The pipeline then translates requestantigravity→openaiand responseopenai→antigravity(clientResponseFormat = sourceFormat)./v1/antigravityroute (new): cloudcode-compatible endpoint — just callshandleChat(mirrors/v1/messages).server.cjs: routes cloudcode envelopes →/v1/antigravityand plain OpenAI bodies →/v1/chat/completions, via a testable shim.Validation
mitm-forward-target.test.ts— cloudcode envelope →/v1/antigravity, OpenAI →/chat/completions, non-envelope guard — 4antigravity-format-detection.test.ts—/antigravitypath →antigravity, no disturbance to/messages//responses//chat/completions— 2translator-antigravity-to-openai,translator-resp-openai-to-antigravitytypecheck:coreclean, lint cleanprovider.ts+ route compile into.next; onlyserver.cjs/forwardTarget.cjsare hot-deployable). This is the Rule fix(ci): add environment for npm token access #18 fire-test for the full flow.Stacking
Stacked on #4285 (Gap A) —
server.cjshere builds on theintercept()rewrite from Gap A. Base will retarget torelease/v3.8.30once #4285 merges.