Skip to content

Commit a8e4e28

Browse files
committed
feat: add Gemini 3.6 Flash support
1 parent d1453c9 commit a8e4e28

28 files changed

Lines changed: 354 additions & 54 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Antigravity Auth
22

33
Google Antigravity OAuth for coding agents. Authenticate with your Google
4-
account and access **Gemini 3**, **Claude 4.6**, and **GPT-OSS 120B** models through
4+
account and access **Gemini 3.6**, **Claude 4.6**, and **GPT-OSS 120B** models through
55
Antigravity's endpoints.
66

77
This is a monorepo publishing three packages:

packages/core/src/agy-request-metadata.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ type ModelMetadataFixture = {
2020
}>
2121
}
2222

23-
const MODEL_METADATA_FIXTURE = JSON.parse(
24-
readFileSync(new URL("../../../test-fixtures/agy-cli-1.1.3-model-metadata.json", import.meta.url), "utf8"),
25-
) as ModelMetadataFixture
23+
const MODEL_METADATA_FIXTURES = ["1.1.3", "1.1.5"].map((version) => JSON.parse(
24+
readFileSync(new URL(`../../../test-fixtures/agy-cli-${version}-model-metadata.json`, import.meta.url), "utf8"),
25+
) as ModelMetadataFixture)
2626

2727
describe("agy request metadata", () => {
2828
it("matches the captured signed FNV-1a session ID for an empty workspace URI", () => {
@@ -54,7 +54,7 @@ describe("agy request metadata", () => {
5454
})
5555
})
5656

57-
it("orders request fields like captured agy 1.1.3 payloads", () => {
57+
it("orders request fields like captured agy 1.1.5 payloads", () => {
5858
const payload: Record<string, unknown> = {
5959
generationConfig: {},
6060
sessionId: "session",
@@ -132,9 +132,11 @@ describe("agy request metadata", () => {
132132
})
133133
})
134134

135-
it("matches every captured agy 1.1.3 model enum fixture", () => {
136-
for (const [model, expected] of Object.entries(MODEL_METADATA_FIXTURE.models)) {
137-
expect(getAgyModelEnum(model), model).toBe(expected.modelEnum)
135+
it("matches every captured agy model enum fixture", () => {
136+
for (const fixture of MODEL_METADATA_FIXTURES) {
137+
for (const [model, expected] of Object.entries(fixture.models)) {
138+
expect(getAgyModelEnum(model), model).toBe(expected.modelEnum)
139+
}
138140
}
139141
})
140142

packages/core/src/agy-request-metadata.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const AGY_MODEL_ENUM_BY_WIRE_MODEL: Readonly<Record<string, string>> = {
2020
"gemini-3.5-flash-extra-low": "MODEL_PLACEHOLDER_M187",
2121
"gemini-3.5-flash-low": "MODEL_PLACEHOLDER_M20",
2222
"gemini-3-flash-agent": "MODEL_PLACEHOLDER_M84",
23+
"gemini-3.6-flash-low": "MODEL_PLACEHOLDER_M266",
24+
"gemini-3.6-flash-medium": "MODEL_PLACEHOLDER_M265",
25+
"gemini-3.6-flash-high": "MODEL_PLACEHOLDER_M264",
2326
"gemini-3.1-pro-low": "MODEL_PLACEHOLDER_M36",
2427
"gemini-pro-agent": "MODEL_PLACEHOLDER_M16",
2528
"claude-sonnet-4-6": "MODEL_PLACEHOLDER_M35",

packages/core/src/agy-transport.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ type AgyWireFixture = {
2222
requestKeys: string[]
2323
}
2424

25-
const AGY_1_1_3_WIRE_FIXTURE = JSON.parse(
26-
readFileSync(new URL("../../../test-fixtures/agy-cli-1.1.3-stream-request.json", import.meta.url), "utf8"),
25+
const AGY_1_1_5_WIRE_FIXTURE = JSON.parse(
26+
readFileSync(new URL("../../../test-fixtures/agy-cli-1.1.5-stream-request.json", import.meta.url), "utf8"),
2727
) as AgyWireFixture
2828

2929
const savedProxyEnv = {
@@ -65,13 +65,13 @@ describe("agy transport", () => {
6565
expect(DEFAULT_AGY_IDLE_TIMEOUT_MS).toBe(180_000)
6666
})
6767

68-
it("serializes the captured agy CLI 1.1.3 stream header contract", () => {
69-
const pairs = buildAgyCliHeaderPairs(AGY_1_1_3_WIRE_FIXTURE.capture.endpoint, {
68+
it("serializes the captured agy CLI 1.1.5 stream header contract", () => {
69+
const pairs = buildAgyCliHeaderPairs(AGY_1_1_5_WIRE_FIXTURE.capture.endpoint, {
7070
method: "POST",
7171
headers: {
7272
Authorization: "Bearer token",
7373
"Content-Type": "application/json",
74-
"User-Agent": "antigravity/cli/1.1.3 (aidev_client; os_type=darwin; arch=arm64; auth_method=consumer)",
74+
"User-Agent": "antigravity/cli/1.1.5 (aidev_client; os_type=darwin; arch=arm64; auth_method=consumer)",
7575
"Accept-Encoding": "gzip",
7676
},
7777
body: JSON.stringify({ request: { contents: [] } }),
@@ -80,11 +80,11 @@ describe("agy transport", () => {
8080
name === "Authorization" ? "<redacted>" : value,
8181
])
8282

83-
expect(AGY_1_1_3_WIRE_FIXTURE.capture).toMatchObject({
84-
version: "1.1.3",
83+
expect(AGY_1_1_5_WIRE_FIXTURE.capture).toMatchObject({
84+
version: "1.1.5",
8585
httpVersion: "HTTP/1.1",
8686
})
87-
expect(pairs).toEqual(AGY_1_1_3_WIRE_FIXTURE.headers)
87+
expect(pairs).toEqual(AGY_1_1_5_WIRE_FIXTURE.headers)
8888
})
8989

9090
it("rejects immediately when the signal is already aborted", async () => {
@@ -117,7 +117,7 @@ describe("agy transport", () => {
117117
await expect(fetchWithAgyCliTransport("https://example.com/v1internal:streamGenerateContent", {
118118
method: "POST",
119119
headers: {
120-
"User-Agent": "antigravity/cli/1.1.3 (aidev_client; os_type=darwin; arch=arm64; auth_method=consumer)",
120+
"User-Agent": "antigravity/cli/1.1.5 (aidev_client; os_type=darwin; arch=arm64; auth_method=consumer)",
121121
},
122122
body: JSON.stringify({ x: 1 }),
123123
}, {

packages/core/src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const ANTIGRAVITY_REDIRECT_URI = "http://localhost:51121/oauth-callback";
2929

3030
/**
3131
* Root endpoints for the Antigravity API (in fallback order).
32-
* Live agy CLI 1.1.3 traffic uses daily-cloudcode-pa.googleapis.com.
32+
* Live agy CLI 1.1.5 traffic uses daily-cloudcode-pa.googleapis.com.
3333
*/
3434
export const ANTIGRAVITY_ENDPOINT_DAILY = "https://daily-cloudcode-pa.googleapis.com";
3535
export const ANTIGRAVITY_ENDPOINT_AUTOPUSH = "https://autopush-cloudcode-pa.sandbox.googleapis.com";

packages/core/src/fingerprint.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
/**
22
* Device Fingerprint Generator for Rate Limit Mitigation
33
*
4-
* Uses the agy CLI 1.1.3 content-request identity captured with mitmproxy:
4+
* Uses the agy CLI 1.1.5 content-request identity captured with mitmproxy:
55
* an Antigravity CLI User-Agent with explicit client, OS, architecture, and auth metadata.
66
* The stored deviceId/sessionToken fields are
77
* retained for account history, but content requests only send User-Agent.
88
*/
99

1010
import * as crypto from "node:crypto";
1111

12-
export const AGY_CLI_VERSION = "1.1.3";
12+
export const AGY_CLI_VERSION = "1.1.5";
1313
const ANTIGRAVITY_API_CLIENT = "antigravity-cli";
1414

1515
export interface ClientMetadata {

packages/core/src/model-registry.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest"
33
import {
44
getGemini35FlashAntigravityModel,
55
getGemini35FlashGeminiCliFallbackModel,
6+
getGemini36FlashAntigravityModel,
67
getPublicModelDefinitions,
78
getResolverAliasMap,
89
} from "./model-registry.ts"
@@ -31,6 +32,7 @@ describe("model registry", () => {
3132
"antigravity-gemini-3.1-flash-image",
3233
"antigravity-gemini-3.1-pro",
3334
"antigravity-gemini-3.5-flash",
35+
"antigravity-gemini-3.6-flash",
3436
"antigravity-gpt-oss-120b-medium",
3537
])
3638

@@ -49,10 +51,18 @@ describe("model registry", () => {
4951
expect(getGemini35FlashGeminiCliFallbackModel()).toBe("gemini-3-flash-preview")
5052
})
5153

54+
it("preserves live Gemini 3.6 Flash route mappings", () => {
55+
expect(getGemini36FlashAntigravityModel()).toBe("gemini-3.6-flash-medium")
56+
expect(getGemini36FlashAntigravityModel("high")).toBe("gemini-3.6-flash-high")
57+
expect(getGemini36FlashAntigravityModel("medium")).toBe("gemini-3.6-flash-medium")
58+
expect(getGemini36FlashAntigravityModel("low")).toBe("gemini-3.6-flash-low")
59+
})
60+
5261
it("keeps resolver aliases for supported agy CLI variants", () => {
5362
const aliases = getResolverAliasMap()
5463

5564
expect(aliases["gemini-3.5-flash-medium"]).toBe("gemini-3.5-flash")
65+
expect(aliases["gemini-3.6-flash-medium"]).toBe("gemini-3.6-flash")
5666
expect(aliases["gemini-claude-opus-4-6-thinking-medium"]).toBe("claude-opus-4-6-thinking")
5767
expect(aliases["gemini-claude-sonnet-4-6-thinking-high"]).toBe("claude-sonnet-4-6")
5868
expect(aliases["gpt-oss-120b"]).toBe("gpt-oss-120b-medium")

packages/core/src/model-registry.ts

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ interface OpencodeModelDefinitionInput {
5454
variants?: Record<string, ModelVariant>
5555
}
5656

57-
interface Gemini35FlashRouteMetadata {
58-
antigravity: {
59-
defaultModel: string
60-
byTier: Partial<Record<ThinkingTier, string>>
61-
}
57+
interface AntigravityTieredRouteMetadata {
58+
defaultModel: string
59+
byTier: Partial<Record<ThinkingTier, string>>
60+
}
61+
62+
interface GeminiFlashRouteMetadata {
63+
antigravity: AntigravityTieredRouteMetadata
6264
geminiCliFallbackModel: string
6365
}
6466

@@ -98,6 +100,16 @@ const ALL_MODEL_DEFINITIONS: OpencodeModelDefinitions = {
98100
high: { thinkingLevel: "high" },
99101
},
100102
}),
103+
"antigravity-gemini-3.6-flash": defineModel("antigravity-gemini-3.6-flash", {
104+
name: "Gemini 3.6 Flash (Antigravity)",
105+
reasoning: true,
106+
limit: { context: 1048576, output: 65536 },
107+
modalities: DEFAULT_MODALITIES,
108+
variants: {
109+
low: { thinkingLevel: "low" },
110+
high: { thinkingLevel: "high" },
111+
},
112+
}),
101113
"antigravity-gemini-3.5-flash": defineModel("antigravity-gemini-3.5-flash", {
102114
name: "Gemini 3.5 Flash (Antigravity)",
103115
reasoning: true,
@@ -208,6 +220,9 @@ const RESOLVER_ALIASES: Record<string, string> = {
208220
"gemini-3.5-flash-low": "gemini-3.5-flash",
209221
"gemini-3.5-flash-medium": "gemini-3.5-flash",
210222
"gemini-3.5-flash-high": "gemini-3.5-flash",
223+
"gemini-3.6-flash-low": "gemini-3.6-flash",
224+
"gemini-3.6-flash-medium": "gemini-3.6-flash",
225+
"gemini-3.6-flash-high": "gemini-3.6-flash",
211226
"gemini-claude-opus-4-6-thinking-low": "claude-opus-4-6-thinking",
212227
"gemini-claude-opus-4-6-thinking-medium": "claude-opus-4-6-thinking",
213228
"gemini-claude-opus-4-6-thinking-high": "claude-opus-4-6-thinking",
@@ -222,7 +237,7 @@ const RESOLVER_ALIASES: Record<string, string> = {
222237
"gpt-oss-120b": "gpt-oss-120b-medium",
223238
}
224239

225-
const GEMINI_35_FLASH_ROUTES: Gemini35FlashRouteMetadata = {
240+
const GEMINI_35_FLASH_ROUTES: GeminiFlashRouteMetadata = {
226241
antigravity: {
227242
defaultModel: "gemini-3-flash-agent",
228243
byTier: {
@@ -234,6 +249,15 @@ const GEMINI_35_FLASH_ROUTES: Gemini35FlashRouteMetadata = {
234249
geminiCliFallbackModel: "gemini-3-flash-preview",
235250
}
236251

252+
const GEMINI_36_FLASH_ROUTES: AntigravityTieredRouteMetadata = {
253+
defaultModel: "gemini-3.6-flash-medium",
254+
byTier: {
255+
low: "gemini-3.6-flash-low",
256+
medium: "gemini-3.6-flash-medium",
257+
high: "gemini-3.6-flash-high",
258+
},
259+
}
260+
237261
const QUOTA_GROUP_BY_MODEL_ID: Record<string, ModelQuotaGroup> = {
238262
"claude-opus-4-6-thinking": "claude",
239263
"claude-opus-4-6": "claude",
@@ -247,12 +271,17 @@ const QUOTA_GROUP_BY_MODEL_ID: Record<string, ModelQuotaGroup> = {
247271
"gemini-3-flash-agent": "gemini-flash",
248272
"gemini-3.5-flash-low": "gemini-flash",
249273
"gemini-3.5-flash-extra-low": "gemini-flash",
274+
"gemini-3.6-flash-low": "gemini-flash",
275+
"gemini-3.6-flash-medium": "gemini-flash",
276+
"gemini-3.6-flash-high": "gemini-flash",
277+
"gemini-3.6-flash-tiered": "gemini-flash",
250278
"gemini-3.1-flash-image": "gemini-flash",
251279
"gpt-oss-120b": "gpt-oss",
252280
"gpt-oss-120b-medium": "gpt-oss",
253281
}
254282

255283
const ANTIGRAVITY_OPENCODE_MODEL_IDS = [
284+
"antigravity-gemini-3.6-flash",
256285
"antigravity-gemini-3.5-flash",
257286
"antigravity-gemini-3.1-pro",
258287
"antigravity-claude-sonnet-4-6-thinking",
@@ -290,6 +319,13 @@ export function getGemini35FlashGeminiCliFallbackModel(): string {
290319
return GEMINI_35_FLASH_ROUTES.geminiCliFallbackModel
291320
}
292321

322+
export function getGemini36FlashAntigravityModel(tier?: ThinkingTier): string {
323+
if (!tier) {
324+
return GEMINI_36_FLASH_ROUTES.defaultModel
325+
}
326+
return GEMINI_36_FLASH_ROUTES.byTier[tier] ?? GEMINI_36_FLASH_ROUTES.defaultModel
327+
}
328+
293329
export function getQuotaGroupForModel(modelId: string): ModelQuotaGroup | undefined {
294330
return QUOTA_GROUP_BY_MODEL_ID[modelId.toLowerCase()]
295331
}

packages/core/src/project.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe("project bootstrap", () => {
4343

4444
expect(headers).toEqual({
4545
"User-Agent": expect.stringMatching(
46-
/^antigravity\/cli\/1\.1\.3 \(aidev_client; os_type=.+; arch=.+; auth_method=consumer\)$/,
46+
/^antigravity\/cli\/1\.1\.5 \(aidev_client; os_type=.+; arch=.+; auth_method=consumer\)$/,
4747
),
4848
Authorization: "Bearer token",
4949
"Content-Type": "application/json",

packages/core/src/transform/model-resolver.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ describe("resolveModelWithTier", () => {
6969
});
7070
});
7171

72+
describe("Gemini 3.6 Flash Antigravity routes", () => {
73+
it.each([
74+
["antigravity-gemini-3.6-flash", "gemini-3.6-flash-medium", 4000, "medium"],
75+
["antigravity-gemini-3.6-flash-low", "gemini-3.6-flash-low", 1000, "low"],
76+
["antigravity-gemini-3.6-flash-medium", "gemini-3.6-flash-medium", 4000, "medium"],
77+
["antigravity-gemini-3.6-flash-high", "gemini-3.6-flash-high", 10000, "high"],
78+
])("maps %s to the captured AGY route", (requestedModel, actualModel, thinkingBudget, tier) => {
79+
const result = resolveModelWithTier(requestedModel);
80+
expect(result.actualModel).toBe(actualModel);
81+
expect(result.thinkingBudget).toBe(thinkingBudget);
82+
expect(result.thinkingLevel).toBeUndefined();
83+
expect(result.tier).toBe(tier);
84+
expect(result.quotaPreference).toBe("antigravity");
85+
});
86+
});
87+
7288
describe("All Gemini models default to antigravity quota", () => {
7389
it("gemini-2.5-flash defaults to antigravity", () => {
7490
const result = resolveModelWithTier("gemini-2.5-flash");
@@ -342,6 +358,14 @@ describe("Issue #103: resolveModelForHeaderStyle", () => {
342358
expect(result.thinkingLevel).toBeUndefined();
343359
expect(result.quotaPreference).toBe("antigravity");
344360
});
361+
362+
it("maps gemini-3.6-flash to the captured agy medium Antigravity model", () => {
363+
const result = resolveModelForHeaderStyle("gemini-3.6-flash", "antigravity");
364+
expect(result.actualModel).toBe("gemini-3.6-flash-medium");
365+
expect(result.thinkingBudget).toBe(4000);
366+
expect(result.thinkingLevel).toBeUndefined();
367+
expect(result.quotaPreference).toBe("antigravity");
368+
});
345369
});
346370

347371
describe("quota fallback from antigravity to gemini-cli", () => {

0 commit comments

Comments
 (0)