Skip to content

Commit 5bb1bcb

Browse files
committed
fix: enforce user-ending Antigravity turns
1 parent 5cd0eba commit 5bb1bcb

4 files changed

Lines changed: 41 additions & 12 deletions

File tree

ARCHITECTURE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
**Payload Transformation:**
3737
- Purpose: Resolves client-supplied model names into logical Antigravity identifiers, injects tool description hardening rules, strips Claude thinking blocks, and sanitizes payload fields during model family swaps.
3838
- Location: `packages/core/src/transform/` and `packages/core/src/model-registry.ts`
39-
- Contains: `resolveModelWithTier`, `applyClaudeTransforms`, `applyGeminiTransforms`, `sanitizeCrossModelPayload`.
39+
- Contains: `resolveModelWithTier`, `applyClaudeTransforms`, `applyGeminiTransforms`, `sanitizeCrossModelPayload`, `getGemini36FlashAntigravityModel`.
4040
- Depends on: `packages/core/src/constants.ts`
4141
- Used by: Host request interceptors and streaming parsers.
4242

@@ -89,9 +89,9 @@
8989
- Pattern: Raw socket read-write buffer stream with custom chunked-transfer decoding.
9090

9191
**`ModelResolver` (`resolveModelWithTier`):**
92-
- Purpose: Maps external AI model tags (e.g. `claude-3-7-sonnet`) into internal Google Antigravity identifiers, specifying thinking budgets and custom header styles.
92+
- Purpose: Maps external AI model tags (e.g. `claude-3-7-sonnet`, `gemini-3.6-flash`) into internal Google Antigravity identifiers, specifying thinking budgets and custom header styles.
9393
- Location: `packages/core/src/transform/model-resolver.ts`
94-
- Pattern: Regular expression and alias lookup maps.
94+
- Pattern: Regular expression and alias lookup maps backed by tiered route definitions in `packages/core/src/model-registry.ts`.
9595

9696
**`ensureProjectContext`:**
9797
- Purpose: Automatically initializes, caches, and maintains valid Google Cloud project mappings for standard or enterprise accounts.

STRUCTURE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ antigravity-auth/
1717
**`packages/core`:**
1818
- Purpose: Provides harness-agnostic utilities for Google Antigravity integrations.
1919
- Contains: Direct TCP/TLS socket transport, Google OAuth PKCE auth flow, Google Cloud project bootstrapping, Claude and Gemini request/response transforms, device fingerprint generators, and centralized model registries.
20-
- Key files: `packages/core/src/agy-transport.ts` (TCP/TLS socket), `packages/core/src/project.ts` (project resolution), `packages/core/src/transform/model-resolver.ts` (model mapping).
20+
- Key files: `packages/core/src/agy-transport.ts` (TCP/TLS socket), `packages/core/src/project.ts` (project resolution), `packages/core/src/transform/model-resolver.ts` (model mapping), `packages/core/src/model-registry.ts` (model definitions & route maps).
2121

2222
**`packages/opencode`:**
2323
- Purpose: Integrates the Antigravity authentication and request transformation logic into the OpenCode host environment.
@@ -42,6 +42,7 @@ antigravity-auth/
4242
**Core Logic:**
4343
- `packages/core/src/agy-transport.ts`: Custom TCP/TLS transport socket implementation.
4444
- `packages/core/src/project.ts`: Project resolution, context loading, and GCP project provisioning.
45+
- `packages/core/src/model-registry.ts`: Model definitions, quota groups, and Gemini Flash tier routing maps.
4546
- `packages/core/src/transform/cross-model-sanitizer.ts`: Payload cleanup when switching model families.
4647
- `packages/opencode/src/plugin/accounts.ts`: Multi-account selection, rotation, metrics, and health scores.
4748

packages/opencode/src/plugin/request.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,32 @@ it("removes x-api-key header", () => {
13131313
expect(wrapped.request.labels.model_enum).toBe(modelEnum);
13141314
});
13151315

1316+
it("appends a user continuation when a Gemini AGY request ends with a model turn", () => {
1317+
const result = prepareAntigravityRequest(
1318+
"https://generativelanguage.googleapis.com/v1beta/models/antigravity-gemini-3.6-flash:generateContent",
1319+
{
1320+
method: "POST",
1321+
body: JSON.stringify({
1322+
contents: [
1323+
{ role: "user", parts: [{ text: "Summarize this context" }] },
1324+
{ role: "model", parts: [{ text: "Summary complete" }] },
1325+
],
1326+
}),
1327+
},
1328+
mockAccessToken,
1329+
mockProjectId,
1330+
undefined,
1331+
"antigravity",
1332+
);
1333+
1334+
const wrapped = JSON.parse(result.init.body as string);
1335+
expect(wrapped.request.contents).toEqual([
1336+
{ role: "user", parts: [{ text: "Summarize this context" }] },
1337+
{ role: "model", parts: [{ text: "Summary complete" }] },
1338+
{ role: "user", parts: [{ text: "[Continue]" }] },
1339+
]);
1340+
});
1341+
13161342
it("preserves uppercase Gemini schemas for properties named thinking", () => {
13171343
const result = prepareAntigravityRequest(
13181344
"https://generativelanguage.googleapis.com/v1beta/models/antigravity-gemini-3.6-flash:generateContent",

packages/opencode/src/plugin/request.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,10 +1243,11 @@ export function prepareAntigravityRequest(
12431243
}
12441244
}
12451245

1246-
// Guard against assistant prefill: Claude rejects conversations ending
1247-
// with model/assistant messages. After context compaction, the conversation
1248-
// can end with a model message — append synthetic user message to fix.
1249-
if (isClaude) {
1246+
// AGY rejects every request that ends with a model turn. Enforce this at
1247+
// the final wire boundary because host races, recovery, or sanitization can
1248+
// otherwise leave a model/assistant entry last. Claude fallback transports
1249+
// have the same assistant-prefill restriction.
1250+
if (headerStyle === "antigravity" || isClaude) {
12501251
for (const req of requestObjects) {
12511252
if (Array.isArray((req as any).contents)) {
12521253
const contents = (req as any).contents;
@@ -1819,10 +1820,11 @@ export function prepareAntigravityRequest(
18191820
}
18201821
}
18211822

1822-
// Guard against assistant prefill: Claude rejects conversations ending
1823-
// with model/assistant messages. After context compaction, the conversation
1824-
// can end with a model message — append synthetic user message to fix.
1825-
if (isClaude) {
1823+
// AGY rejects every request that ends with a model turn. Enforce this at
1824+
// the final wire boundary because host races, recovery, or sanitization can
1825+
// otherwise leave a model/assistant entry last. Claude fallback transports
1826+
// have the same assistant-prefill restriction.
1827+
if (headerStyle === "antigravity" || isClaude) {
18261828
if (Array.isArray(requestPayload.contents)) {
18271829
const lastContent = requestPayload.contents[requestPayload.contents.length - 1] as any;
18281830
if (lastContent?.role === "model" || lastContent?.role === "assistant") {

0 commit comments

Comments
 (0)