From 52598de07339ea26c4d3d5531e00dc0bbdb79b1b Mon Sep 17 00:00:00 2001 From: Hanbin Noh <282618027+hanbinnoh@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:33:12 +0900 Subject: [PATCH 1/3] fix(antigravity): align User-Agent token order and auth_method with 2.5.5 decompilation - Align token sequence in antigravityUserAgent to match decompiled Go Language Server setHeaders (0x1018fbe00): os_type -> arch -> aidev_client -> auth_method=oauth. - Result: antigravity/ide/2.5.5 (os_type=windows; arch=amd64; aidev_client; auth_method=oauth). - Update unit and wire tests to assert exact decompiled token sequence. --- src/adapters/client-fingerprint.ts | 13 +++++++++---- tests/client-fingerprint.test.ts | 4 ++-- tests/google-antigravity-wire.test.ts | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/adapters/client-fingerprint.ts b/src/adapters/client-fingerprint.ts index 91a51ea39a..0b0b224e5d 100644 --- a/src/adapters/client-fingerprint.ts +++ b/src/adapters/client-fingerprint.ts @@ -49,15 +49,20 @@ const ANTIGRAVITY_IDE_PLATFORM = "windows/amd64"; export const ANTIGRAVITY_GOOG_API_CLIENT_UA = "google-api-nodejs-client/10.3.0"; /** - * The real Antigravity IDE User-Agent, e.g. - * `antigravity/ide/2.5.5 (aidev_client; os_type=windows; arch=amd64)`. + * Real Antigravity IDE User-Agent format, decompiled from 2.5.5 Go LS (`setHeaders` @ `0x1018fbe00`): + * `antigravity/ide/${version} (os_type=${osType}; arch=${arch}; aidev_client; auth_method=oauth)` * - * Must be the IDE client family, NOT `antigravity/cli/...`: the Cloud Code Assist backend gates + * Token ordering from decompiled binary: `os_type` -> `arch` -> `aidev_client` -> `auth_method=oauth`. + * + * Must be the IDE client family (`antigravity/ide/...`): Cloud Code Assist backend gates * newer agent models (e.g. `gemini-3.7-flash`) by User-Agent and answers 404 NOT_FOUND to * CLI-shaped UAs even with a valid OAuth token. Only `antigravity/ide/` unlocks them. * A `GOOGLE_ANTIGRAVITY_USER_AGENT` override (set by the caller) takes precedence upstream. */ export function antigravityUserAgent(version = ANTIGRAVITY_IDE_VERSION): string { + const ov = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT?.trim() + || process.env.PI_AI_ANTIGRAVITY_USER_AGENT?.trim(); + if (ov) return ov; const [osType, arch] = ANTIGRAVITY_IDE_PLATFORM.split("/"); - return `antigravity/ide/${version} (${ANTIGRAVITY_IDE_CLIENT_NAME}; os_type=${osType}; arch=${arch})`; + return `antigravity/ide/${version} (os_type=${osType}; arch=${arch}; ${ANTIGRAVITY_IDE_CLIENT_NAME}; auth_method=oauth)`; } diff --git a/tests/client-fingerprint.test.ts b/tests/client-fingerprint.test.ts index 9e30d494ef..47a6570f71 100644 --- a/tests/client-fingerprint.test.ts +++ b/tests/client-fingerprint.test.ts @@ -21,12 +21,12 @@ function parsed(): OcxParsedRequest { describe("client fingerprint — helpers", () => { test("antigravity UA has the real IDE shape, never the literal giveaway", async () => { const ua = antigravityUserAgent(); - expect(ua).toBe(`antigravity/ide/${ANTIGRAVITY_IDE_VERSION} (aidev_client; os_type=windows; arch=amd64)`); + expect(ua).toBe(`antigravity/ide/${ANTIGRAVITY_IDE_VERSION} (os_type=windows; arch=amd64; aidev_client; auth_method=oauth)`); expect(ua).not.toBe("antigravity"); }); test("antigravity UA honors an explicit version override", async () => { - expect(antigravityUserAgent("9.9.9")).toBe("antigravity/ide/9.9.9 (aidev_client; os_type=windows; arch=amd64)"); + expect(antigravityUserAgent("9.9.9")).toBe("antigravity/ide/9.9.9 (os_type=windows; arch=amd64; aidev_client; auth_method=oauth)"); }); test("GOOGLE_ANTIGRAVITY_USER_AGENT env override wins over the default UA", async () => { diff --git a/tests/google-antigravity-wire.test.ts b/tests/google-antigravity-wire.test.ts index cc3b0d458f..b7274aed31 100644 --- a/tests/google-antigravity-wire.test.ts +++ b/tests/google-antigravity-wire.test.ts @@ -59,7 +59,7 @@ describe("antigravity CCA envelope", () => { // The exact default must not drift: Google gates models by family AND version, // so any change to version/platform could silently re-lock gemini-3.7-flash. expect(req.headers["User-Agent"]).toBe( - "antigravity/ide/2.5.5 (aidev_client; os_type=windows; arch=amd64)", + "antigravity/ide/2.5.5 (os_type=windows; arch=amd64; aidev_client; auth_method=oauth)", ); // The literal "antigravity" giveaway UA must no longer be sent. expect(req.headers["User-Agent"]).not.toBe("antigravity"); From cb14736c0f879044b349282ab0b748077cb1e171 Mon Sep 17 00:00:00 2001 From: Hanbin Noh <282618027+hanbinnoh@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:41:07 +0900 Subject: [PATCH 2/3] fix(antigravity): use antigravityUserAgent in wire adapter and add override regression tests - Use antigravityUserAgent() for ANTIGRAVITY_REQUEST_UA to ensure trimmed override handling. - Add focused regression tests for GOOGLE_ANTIGRAVITY_USER_AGENT and PI_AI_ANTIGRAVITY_USER_AGENT (trimming, precedence, whitespace fallback). --- src/adapters/google-antigravity-wire.ts | 2 +- tests/client-fingerprint.test.ts | 51 +++++++++++++++++++++---- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/adapters/google-antigravity-wire.ts b/src/adapters/google-antigravity-wire.ts index e516b64c63..b4b0ca3176 100644 --- a/src/adapters/google-antigravity-wire.ts +++ b/src/adapters/google-antigravity-wire.ts @@ -9,7 +9,7 @@ import { antigravityUserAgent } from "./client-fingerprint"; * sends. The IDE client family is also required to unlock newer agent models (the backend 404s * CLI-shaped UAs for `gemini-3.7-*`). A `GOOGLE_ANTIGRAVITY_USER_AGENT` override still wins. */ -export const ANTIGRAVITY_REQUEST_UA = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT || antigravityUserAgent(); +export const ANTIGRAVITY_REQUEST_UA = antigravityUserAgent(); /** * Whether a stored `OcxToolCall.thoughtSignature` is a REAL upstream Gemini signature versus a diff --git a/tests/client-fingerprint.test.ts b/tests/client-fingerprint.test.ts index 47a6570f71..0c876d7098 100644 --- a/tests/client-fingerprint.test.ts +++ b/tests/client-fingerprint.test.ts @@ -29,16 +29,51 @@ describe("client fingerprint — helpers", () => { expect(antigravityUserAgent("9.9.9")).toBe("antigravity/ide/9.9.9 (os_type=windows; arch=amd64; aidev_client; auth_method=oauth)"); }); - test("GOOGLE_ANTIGRAVITY_USER_AGENT env override wins over the default UA", async () => { - const prev = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = "custom-ua/1.2.3"; + test("GOOGLE_ANTIGRAVITY_USER_AGENT env override trims surrounding whitespace", async () => { + const prevGoogle = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + const prevPi = process.env.PI_AI_ANTIGRAVITY_USER_AGENT; try { - // Fresh module instance so the env-driven constant is re-evaluated at import time. - const mod = await import(`../src/adapters/google-antigravity-wire?override=${Date.now()}`); - expect(mod.ANTIGRAVITY_REQUEST_UA).toBe("custom-ua/1.2.3"); + process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = " custom-ua/1.2.3 "; + delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; + expect(antigravityUserAgent()).toBe("custom-ua/1.2.3"); } finally { - if (prev === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prev; + if (prevGoogle === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prevGoogle; + if (prevPi === undefined) delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; + else process.env.PI_AI_ANTIGRAVITY_USER_AGENT = prevPi; + } + }); + + test("PI_AI_ANTIGRAVITY_USER_AGENT env override works when GOOGLE override is absent", async () => { + const prevGoogle = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + const prevPi = process.env.PI_AI_ANTIGRAVITY_USER_AGENT; + try { + delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + process.env.PI_AI_ANTIGRAVITY_USER_AGENT = " pi-ua/9.9.9 "; + expect(antigravityUserAgent()).toBe("pi-ua/9.9.9"); + } finally { + if (prevGoogle === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prevGoogle; + if (prevPi === undefined) delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; + else process.env.PI_AI_ANTIGRAVITY_USER_AGENT = prevPi; + } + }); + + test("GOOGLE_ANTIGRAVITY_USER_AGENT takes precedence over PI_AI override, whitespace-only falls back", async () => { + const prevGoogle = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + const prevPi = process.env.PI_AI_ANTIGRAVITY_USER_AGENT; + try { + process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = "google-wins/1.0"; + process.env.PI_AI_ANTIGRAVITY_USER_AGENT = "pi-secondary/2.0"; + expect(antigravityUserAgent()).toBe("google-wins/1.0"); + + process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = " "; + expect(antigravityUserAgent()).toBe("pi-secondary/2.0"); + } finally { + if (prevGoogle === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; + else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prevGoogle; + if (prevPi === undefined) delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; + else process.env.PI_AI_ANTIGRAVITY_USER_AGENT = prevPi; } }); From 81236807f8a06bc6d776abc485d50fb2445ec384 Mon Sep 17 00:00:00 2001 From: Hanbin Noh <282618027+hanbinnoh@users.noreply.github.com> Date: Tue, 18 Aug 2026 11:03:16 +0900 Subject: [PATCH 3/3] fix(antigravity): parameterize authMethod and drop undocumented PI_AI env var - Allow optional authMethod parameter on antigravityUserAgent (defaults to oauth). - Drop undocumented PI_AI_ANTIGRAVITY_USER_AGENT override to keep changes minimal. --- src/adapters/client-fingerprint.ts | 7 +++--- tests/client-fingerprint.test.ts | 37 +++++++----------------------- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/src/adapters/client-fingerprint.ts b/src/adapters/client-fingerprint.ts index 0b0b224e5d..e01880a938 100644 --- a/src/adapters/client-fingerprint.ts +++ b/src/adapters/client-fingerprint.ts @@ -59,10 +59,9 @@ export const ANTIGRAVITY_GOOG_API_CLIENT_UA = "google-api-nodejs-client/10.3.0"; * CLI-shaped UAs even with a valid OAuth token. Only `antigravity/ide/` unlocks them. * A `GOOGLE_ANTIGRAVITY_USER_AGENT` override (set by the caller) takes precedence upstream. */ -export function antigravityUserAgent(version = ANTIGRAVITY_IDE_VERSION): string { - const ov = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT?.trim() - || process.env.PI_AI_ANTIGRAVITY_USER_AGENT?.trim(); +export function antigravityUserAgent(version = ANTIGRAVITY_IDE_VERSION, authMethod = "oauth"): string { + const ov = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT?.trim(); if (ov) return ov; const [osType, arch] = ANTIGRAVITY_IDE_PLATFORM.split("/"); - return `antigravity/ide/${version} (os_type=${osType}; arch=${arch}; ${ANTIGRAVITY_IDE_CLIENT_NAME}; auth_method=oauth)`; + return `antigravity/ide/${version} (os_type=${osType}; arch=${arch}; ${ANTIGRAVITY_IDE_CLIENT_NAME}; auth_method=${authMethod})`; } diff --git a/tests/client-fingerprint.test.ts b/tests/client-fingerprint.test.ts index 0c876d7098..9f681fdb51 100644 --- a/tests/client-fingerprint.test.ts +++ b/tests/client-fingerprint.test.ts @@ -25,55 +25,34 @@ describe("client fingerprint — helpers", () => { expect(ua).not.toBe("antigravity"); }); - test("antigravity UA honors an explicit version override", async () => { + test("antigravity UA honors explicit version and authMethod overrides", async () => { expect(antigravityUserAgent("9.9.9")).toBe("antigravity/ide/9.9.9 (os_type=windows; arch=amd64; aidev_client; auth_method=oauth)"); + expect(antigravityUserAgent(ANTIGRAVITY_IDE_VERSION, "api_key")).toBe( + `antigravity/ide/${ANTIGRAVITY_IDE_VERSION} (os_type=windows; arch=amd64; aidev_client; auth_method=api_key)`, + ); }); test("GOOGLE_ANTIGRAVITY_USER_AGENT env override trims surrounding whitespace", async () => { const prevGoogle = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - const prevPi = process.env.PI_AI_ANTIGRAVITY_USER_AGENT; try { process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = " custom-ua/1.2.3 "; - delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; expect(antigravityUserAgent()).toBe("custom-ua/1.2.3"); } finally { if (prevGoogle === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prevGoogle; - if (prevPi === undefined) delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; - else process.env.PI_AI_ANTIGRAVITY_USER_AGENT = prevPi; } }); - test("PI_AI_ANTIGRAVITY_USER_AGENT env override works when GOOGLE override is absent", async () => { + test("whitespace-only GOOGLE_ANTIGRAVITY_USER_AGENT falls back to default UA", async () => { const prevGoogle = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - const prevPi = process.env.PI_AI_ANTIGRAVITY_USER_AGENT; try { - delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - process.env.PI_AI_ANTIGRAVITY_USER_AGENT = " pi-ua/9.9.9 "; - expect(antigravityUserAgent()).toBe("pi-ua/9.9.9"); - } finally { - if (prevGoogle === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prevGoogle; - if (prevPi === undefined) delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; - else process.env.PI_AI_ANTIGRAVITY_USER_AGENT = prevPi; - } - }); - - test("GOOGLE_ANTIGRAVITY_USER_AGENT takes precedence over PI_AI override, whitespace-only falls back", async () => { - const prevGoogle = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; - const prevPi = process.env.PI_AI_ANTIGRAVITY_USER_AGENT; - try { - process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = "google-wins/1.0"; - process.env.PI_AI_ANTIGRAVITY_USER_AGENT = "pi-secondary/2.0"; - expect(antigravityUserAgent()).toBe("google-wins/1.0"); - process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = " "; - expect(antigravityUserAgent()).toBe("pi-secondary/2.0"); + expect(antigravityUserAgent()).toBe( + `antigravity/ide/${ANTIGRAVITY_IDE_VERSION} (os_type=windows; arch=amd64; aidev_client; auth_method=oauth)`, + ); } finally { if (prevGoogle === undefined) delete process.env.GOOGLE_ANTIGRAVITY_USER_AGENT; else process.env.GOOGLE_ANTIGRAVITY_USER_AGENT = prevGoogle; - if (prevPi === undefined) delete process.env.PI_AI_ANTIGRAVITY_USER_AGENT; - else process.env.PI_AI_ANTIGRAVITY_USER_AGENT = prevPi; } });