From 6f809bb970cc466d7d8ca2f53b49aca87997c480 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 25 Sep 2026 17:29:39 +0000 Subject: [PATCH] feat(gateway): recognise Antigravity CLI requests as antigravity-cli Antigravity CLI (agy) has no user agent of its own. Pointed at a custom Gemini endpoint (GOOGLE_GEMINI_BASE_URL + GEMINI_API_KEY with modelProvider "gemini"), agy 1.2.11 sends the Go genai SDK default in both User-Agent and X-Goog-Api-Client: google-genai-sdk/1.71.0 gl-go/go1.28-20260721-RC03 cl/951519500 +3ebc191975 X:fieldtrack,... Matching on `google-genai-sdk` alone would label every Go program built on the SDK as agy. What sets agy apart is the Google-internal Go toolchain version after `gl-go/` (it carries `cl/`); a program built with a public Go release sends `gl-go/go1.25.1`. The hint now maps that combination to `antigravity-cli`, the client id ThinkWatch Lite uses for it. Captured by running agy against a local fake server with a fake key and all outbound traffic blocked; no account was signed in. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012JfMJSMbfEW5nzkn9vro9A --- crates/tw-gateway/src/hint.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/tw-gateway/src/hint.rs b/crates/tw-gateway/src/hint.rs index ae93fa7d..18d3f0f5 100644 --- a/crates/tw-gateway/src/hint.rs +++ b/crates/tw-gateway/src/hint.rs @@ -51,6 +51,14 @@ pub fn client_hint(headers: &HeaderMap) -> Option { return Some(id.into()); } } + // 四、Antigravity CLI(agy)。它没有自己的 UA,发的是 Go 版 genai SDK 的默认值。 + // 实测 1.2.11:`google-genai-sdk/1.71.0 gl-go/go1.28-20260721-RC03 cl/951519500 +3ebc191975 X:…` + // 只看 `google-genai-sdk` 会把任何用 Go SDK 的程序都算成 agy;能分开的是 + // `gl-go/` 后面那段 Google 内部 Go 工具链的版本号(带 `cl/`),外面编出来的只有 + // `gl-go/go1.25.1`。 + if ua.starts_with("google-genai-sdk/") && ua.contains(" gl-go/") && ua.contains(" cl/") { + return Some("antigravity-cli".into()); + } None } @@ -120,6 +128,28 @@ mod tests { ); } + #[test] + fn antigravity_cli_is_recognised_by_the_toolchain_in_its_user_agent() { + // agy 1.2.11 指向假服务器时抓到的原样;X-Goog-Api-Client 和 UA 相同。 + let agy = "google-genai-sdk/1.71.0 gl-go/go1.28-20260721-RC03 cl/951519500 +3ebc191975 X:fieldtrack,boringcrypto,simd,mapsplitgroup"; + assert_eq!( + client_hint(&h(&[ + ("user-agent", agy), + ("x-goog-api-client", agy), + ("x-goog-api-key", "k"), + ])), + Some("antigravity-cli".into()) + ); + // 外面的人用 Go SDK 写的程序:同一个 UA 前缀,普通的 Go 版本号。不是 agy。 + assert_eq!( + client_hint(&h(&[( + "user-agent", + "google-genai-sdk/1.71.0 gl-go/go1.25.1" + )])), + None + ); + } + #[test] fn an_unknown_client_is_none_rather_than_a_guess() { // 猜错比不猜更糟:观察窗口会因此宣布「已生效」。