Skip to content

Commit cfcc84d

Browse files
alltomatosclaude
andcommitted
feat(providers): remove Antigravity IDE (AGY), keep AGY CLI only
Confirmed against OmniRoute's own source (antigravityProjectGate.ts, anomalyco#11284) that the "ide" profile hits a real, unfixable Google requirement: consumer accounts must bring their own Google Cloud project with a paid Gemini Code Assist license attached (BYOP) — Google's onboardUser returns done:true with an empty cloudaicompanionProject otherwise, no matter the metadata/header/tier_id shape sent. OmniRoute doesn't solve this either; it just marks those connections "degraded" and prefers healthier ones. AGY CLI has no such requirement and covers the same models, so cutting the IDE profile removes an OAuth path that can silently connect but never actually serve a chat request. Simplified discoverProject back to CLI-only metadata now that it's the only registered profile. Co-Authored-By: Claude Sonnet 5 <[email protected]>
1 parent 6231310 commit cfcc84d

5 files changed

Lines changed: 31 additions & 98 deletions

File tree

‎packages/app/src/components/dialog-connect-provider.tsx‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ function ProviderPicker(props: {
194194
return [
195195
{ id: CUSTOM_ID, name: customLabel() },
196196
...(connected.has(OMNIROUTE_PROVIDER_ID) ? [] : [{ id: OMNIROUTE_PROVIDER_ID, name: "Omniroute" }]),
197-
...(connected.has("google-antigravity") ? [] : [{ id: "google-antigravity", name: "AGY" }]),
198197
...(connected.has("google-antigravity-cli") ? [] : [{ id: "google-antigravity-cli", name: "AGY CLI" }]),
199198
...connected.values(),
200199
]
@@ -260,7 +259,6 @@ function ProviderPickerV2(props: {
260259
"opencode-go",
261260
"anthropic",
262261
"openai",
263-
"google-antigravity",
264262
"google-antigravity-cli",
265263
"openrouter",
266264
]
@@ -273,7 +271,6 @@ function ProviderPickerV2(props: {
273271
const values = [
274272
custom(),
275273
...(connected.has(OMNIROUTE_PROVIDER_ID) ? [] : [omniroutePlaceholder()]),
276-
...(connected.has("google-antigravity") ? [] : [{ id: "google-antigravity", name: "AGY" }]),
277274
...(connected.has("google-antigravity-cli") ? [] : [{ id: "google-antigravity-cli", name: "AGY CLI" }]),
278275
...connected.values(),
279276
]

‎packages/app/src/components/settings-v2/providers.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ export const SettingsProvidersV2: Component<{
253253
.filter((id) => !connectedIDs.has(id))
254254
.map((id) => {
255255
if (id === OMNIROUTE_PROVIDER_ID) return { id: OMNIROUTE_PROVIDER_ID, name: "Omniroute" } as ProviderItem
256-
if (id === "google-antigravity") return (allProviders.get(id) ?? { id: "google-antigravity", name: "AGY" }) as ProviderItem
257256
if (id === "google-antigravity-cli") return (allProviders.get(id) ?? { id: "google-antigravity-cli", name: "AGY CLI" }) as ProviderItem
258257
return allProviders.get(id)
259258
})
@@ -279,7 +278,7 @@ export const SettingsProvidersV2: Component<{
279278
}
280279

281280
const type = (item: ProviderItem) => {
282-
if (item.id === "google-antigravity" || item.id === "google-antigravity-cli" || item.id === "kiro" || item.id === "kilo") {
281+
if (item.id === "google-antigravity-cli" || item.id === "kiro" || item.id === "kilo") {
283282
return "OAuth"
284283
}
285284
const current = source(item)

‎packages/app/src/hooks/use-providers.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export const popularProviders = [
1111
"opencode-go",
1212
"anthropic",
1313
"openai",
14-
"google-antigravity",
1514
"google-antigravity-cli",
1615
"openrouter",
1716
]

‎packages/core/src/plugin/provider/google-antigravity.ts‎

Lines changed: 25 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,6 @@ type Profile = {
5050

5151
function profiles(): Profile[] {
5252
return [
53-
{
54-
integrationID: Integration.ID.make("google-antigravity"),
55-
methodID: Integration.MethodID.make("oauth"),
56-
label: "Google account",
57-
providerName: "AGY",
58-
clientProfile: "ide",
59-
clientID:
60-
process.env.ANTIGRAVITY_OAUTH_CLIENT_ID ?? "884354919052-36trc1jjb3tguiac32ov6cod268c5blh.apps.googleusercontent.com",
61-
clientSecret: process.env.ANTIGRAVITY_OAUTH_CLIENT_SECRET ?? "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",
62-
},
6353
{
6454
integrationID: Integration.ID.make("google-antigravity-cli"),
6555
methodID: Integration.MethodID.make("oauth"),
@@ -87,52 +77,16 @@ const OnboardUserResponse = Schema.Struct({
8777
response: Schema.optional(Schema.Struct({ cloudaicompanionProject: Schema.optional(Schema.String) })),
8878
})
8979

90-
function post<S extends Schema.Top>(
91-
http: HttpClient.HttpClient,
92-
url: string,
93-
token: string,
94-
body: unknown,
95-
schema: S,
96-
headers?: Record<string, string>,
97-
) {
98-
return http
80+
function post<S extends Schema.Top>(http: HttpClient.HttpClient, url: string, token: string, body: unknown, schema: S) {
81+
return HttpClient.filterStatusOk(http)
9982
.execute(
10083
HttpClientRequest.post(url).pipe(
10184
HttpClientRequest.acceptJson,
10285
HttpClientRequest.bearerToken(token),
10386
HttpClientRequest.bodyJsonUnsafe(body),
104-
HttpClientRequest.setHeaders(headers ?? {}),
105-
),
106-
)
107-
.pipe(
108-
Effect.tap((response) =>
109-
response.status >= 400
110-
? response.text.pipe(
111-
Effect.tap((text) =>
112-
Effect.sync(() =>
113-
console.error(
114-
`[antigravity-debug] POST ${url} sentBody=${JSON.stringify(body)} sentHeaders=${JSON.stringify(headers ?? {})} -> ${response.status}: ${text}`,
115-
),
116-
),
117-
),
118-
)
119-
: Effect.void,
12087
),
121-
Effect.flatMap(HttpClientResponse.filterStatusOk),
122-
Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)),
12388
)
124-
}
125-
126-
// Google's onboarding endpoints (unlike generateContent) appear to gate on
127-
// client identity headers, not just the request body: OmniRoute's
128-
// reverse-engineered client sends this exact "IDE-Node" fingerprint —
129-
// distinct from both the CLI User-Agent and the native IDE Electron
130-
// User-Agent — specifically for loadCodeAssist/onboardUser on the "ide"
131-
// profile. Confirmed live: onboardUser 403s without it even with correct
132-
// body metadata.
133-
const ideNodeOnboardingHeaders = {
134-
"User-Agent": "antigravity/2.1.1 darwin/arm64 google-api-nodejs-client/10.3.0",
135-
"X-Goog-Api-Client": "gl-node/22.21.1",
89+
.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)))
13690
}
13791

13892
function get<S extends Schema.Top>(http: HttpClient.HttpClient, url: string, token: string, schema: S) {
@@ -145,43 +99,37 @@ function get<S extends Schema.Top>(http: HttpClient.HttpClient, url: string, tok
14599
// backing this account. Required on every Code Assist call — the token
146100
// alone isn't enough. Best-effort: a failure here still leaves the account
147101
// usable, since onboarding can complete lazily on the first real request.
148-
function discoverProject(http: HttpClient.HttpClient, accessToken: string, clientProfile: ClientProfile) {
149-
// pluginType: "GEMINI" is the CLI's onboarding metadata; ideType:
150-
// "ANTIGRAVITY" (OmniRoute's reverse-engineered shape) is what the real
151-
// IDE app sends. Scoped to "ide" only — "cli" is confirmed working today.
152-
const metadata = clientProfile === "ide" ? { ideType: "ANTIGRAVITY" } : { pluginType: "GEMINI" }
153-
const headers = clientProfile === "ide" ? ideNodeOnboardingHeaders : undefined
102+
//
103+
// NOTE: only the CLI profile is registered (see profiles() below) — the
104+
// Antigravity IDE profile was removed. Its onboardUser call kept returning
105+
// Google's real, documented "BYOP" (bring-your-own-project) response for
106+
// consumer accounts: `done: true` but an empty `cloudaicompanionProject`,
107+
// because Google requires those accounts to link a real Google Cloud
108+
// project with a paid Gemini Code Assist license attached — confirmed by
109+
// reading OmniRoute's own source (antigravityProjectGate.ts, #11284), which
110+
// hits the identical wall and just marks those connections "degraded"
111+
// rather than actually resolving it. Not fixable from this side; CLI has no
112+
// such requirement and covers the same models.
113+
function discoverProject(http: HttpClient.HttpClient, accessToken: string) {
154114
return Effect.gen(function* () {
155-
const loaded = yield* post(http, loadCodeAssistUrl, accessToken, { metadata }, LoadCodeAssistResponse, headers)
115+
const loaded = yield* post(
116+
http,
117+
loadCodeAssistUrl,
118+
accessToken,
119+
{ metadata: { pluginType: "GEMINI" } },
120+
LoadCodeAssistResponse,
121+
)
156122
if (loaded.cloudaicompanionProject) {
157123
return { projectID: loaded.cloudaicompanionProject, tier: loaded.currentTier?.id }
158124
}
159-
// onboardUser's request body uses snake_case tier_id, unlike every other
160-
// camelCase field in these APIs — confirmed against OmniRoute's working
161-
// client. Sending `tierId` here silently fails onboarding (Google's
162-
// request validation just ignores/rejects the unrecognized field).
163-
// Google returns the specific error FREE_TIER_USER_NOT_ELIGIBLE for
164-
// paid-plan accounts onboarded with tier_id "free-tier" — confirmed
165-
// live on a Google AI Pro account. loadCodeAssist's own response is `{}`
166-
// for accounts with no existing project (no currentTier to read), so a
167-
// fallback tier is unavoidable; OmniRoute's client defaults to
168-
// "legacy-tier" rather than "free-tier" and works across paid plans.
169125
const onboarded = yield* post(
170126
http,
171127
onboardUserUrl,
172128
accessToken,
173-
{ tier_id: loaded.currentTier?.id ?? "legacy-tier", metadata },
129+
{ tier_id: loaded.currentTier?.id ?? "legacy-tier", metadata: { pluginType: "GEMINI" } },
174130
OnboardUserResponse,
175-
headers,
176131
)
177-
if (onboarded.response?.cloudaicompanionProject) {
178-
return { projectID: onboarded.response.cloudaicompanionProject, tier: loaded.currentTier?.id }
179-
}
180-
// onboardUser's own response doesn't reliably carry the project once
181-
// onboarding completes — OmniRoute re-fetches loadCodeAssist afterward
182-
// to pick it up instead of trusting onboardUser's response shape.
183-
const reloaded = yield* post(http, loadCodeAssistUrl, accessToken, { metadata }, LoadCodeAssistResponse, headers)
184-
return { projectID: reloaded.cloudaicompanionProject, tier: reloaded.currentTier?.id ?? loaded.currentTier?.id }
132+
return { projectID: onboarded.response?.cloudaicompanionProject, tier: loaded.currentTier?.id }
185133
}).pipe(Effect.catch(() => Effect.succeed({ projectID: undefined, tier: undefined })))
186134
}
187135

@@ -214,7 +162,7 @@ function exchange(
214162
get(http, userInfoUrl, token.access_token, UserInfo).pipe(
215163
Effect.catch(() => Effect.succeed({ email: undefined as string | undefined })),
216164
),
217-
discoverProject(http, token.access_token, profile.clientProfile),
165+
discoverProject(http, token.access_token),
218166
],
219167
{ concurrency: 2 },
220168
)

‎packages/opencode/src/provider/provider.ts‎

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -220,16 +220,6 @@ function agentRouterModel(id: string, name: string, baseURL: string): Model {
220220

221221
function custom(dep: CustomDep): Record<string, CustomLoader> {
222222
return {
223-
"google-antigravity": Effect.fnUntraced(function* (input: Info) {
224-
return {
225-
autoload: true,
226-
options: {
227-
apiKey: input.options?.apiKey ?? input.key ?? "antigravity-oauth",
228-
baseURL: "https://daily-cloudcode-pa.googleapis.com/v1internal",
229-
fetch: createAntigravityFetch("ide", () => input.options ?? {}),
230-
},
231-
}
232-
}),
233223
"google-antigravity-cli": Effect.fnUntraced(function* (input: Info) {
234224
return {
235225
autoload: true,
@@ -2096,21 +2086,21 @@ const layer = Layer.effect(
20962086
if (credential?.type === "oauth") {
20972087
info.key = credential.access
20982088
const meta = (credential as any).metadata
2099-
const clientProfile: "ide" | "cli" = meta?.clientProfile || (providerID.endsWith("-cli") ? "cli" : "ide")
21002089
info.options = {
21012090
...info.options,
21022091
apiKey: credential.access,
21032092
accessToken: credential.access,
21042093
projectID: meta?.projectID || "aicode-consumers",
2105-
clientProfile,
2094+
clientProfile: "cli",
21062095
}
21072096
// fromCatalog() only carries over the catalog's static api.url/apiKey —
21082097
// it can't know about the Code Assist wire protocol, so the fetch
21092098
// interceptor that speaks it (see antigravity-adapter.ts) has to be
21102099
// injected here, on every sync, since a fresh access token means a
2111-
// fresh options object each time.
2112-
if (providerID.startsWith("google-antigravity")) {
2113-
info.options.fetch = createAntigravityFetch(clientProfile, () => info.options)
2100+
// fresh options object each time. Only the CLI profile is registered
2101+
// (see google-antigravity.ts for why the IDE profile was removed).
2102+
if (providerID === "google-antigravity-cli") {
2103+
info.options.fetch = createAntigravityFetch("cli", () => info.options)
21142104
}
21152105
// Same bridge for Kiro: the v2 catalog registration (kiro.ts) only
21162106
// carries OAuth + model names, it knows nothing about the AWS

0 commit comments

Comments
 (0)