feat: Add AntiGravity SDK support & Gemini API key routing, introduce dynamic model discovery#576
feat: Add AntiGravity SDK support & Gemini API key routing, introduce dynamic model discovery#576chrisgeo wants to merge 34 commits into
Conversation
…names Add gemini-3.5-flash, gemini-3.5-pro, and gemini-3.1-flash across both quota pools, and align the Gemini CLI model-name convention with the antigravity (agy) and gemini CLIs: the 3.1+ generation uses bare names, not the legacy -preview suffix used by the 3.0 line. Registry (src/plugin/config/models.ts): - Antigravity: antigravity-gemini-3.1-flash, antigravity-gemini-3.5-flash, antigravity-gemini-3.5-pro - Gemini CLI (bare): gemini-3.1-flash, gemini-3.5-flash, gemini-3.5-pro, and gemini-3.1-pro (renamed from gemini-3.1-pro-preview) - Pro variants low/high; Flash variants minimal/low/medium/high - Kept gemini-3.1-pro-preview-customtools as a legacy entry Resolver (src/plugin/transform/model-resolver.ts): - resolveModelForHeaderStyle appends -preview only for the legacy 3.0 line; dotted-minor generations (gemini-3.1+) use bare names. Previously configured -preview model strings still route correctly via the resolver. Updated tests (models, model-resolver, request), README tables + copy-paste config, and CHANGELOG. Full suite 1014 passing; typecheck clean.
Fix default Gemini 3 thinkingLevel injection
Remove Gemini 3.1 Flash and Gemini 3.5 Pro models
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds Antigravity SDK (AGY) API-key credential discovery and fetch helpers, Gemini 3.5 Flash model definitions and thinking-level mapping, model discovery factories and merging into provider definitions, request preparation and AGY fetch paths with rate-limit tracking and fallbacks, resolver updates for antigravity/gemini-cli/agy-sdk header styles, schema and DEFAULT_CONFIG extensions, debug header/URL redaction helpers, quota helper exports, many unit tests, and README/CHANGELOG documentation updates. Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@emilioarvidisaksson This version might work more cleanly. |
Greptile SummaryThis PR introduces Gemini API-key routing with credential pooling and fallback, runtime model discovery via both the public Gemini API and the Antigravity available-models endpoint, and a new
Confidence Score: 3/5Two correctness issues in quota aggregation and API-key fallback handling need to be addressed before merging. The aggregateQuota inversion from minimum-remaining to maximum-remaining causes the stored resetTime to reflect the model with the most quota rather than the one actually depleted, so getMinWaitTimeForSoftQuota may tell users to wait longer than necessary. Separately, tryFetchWithAgySdkCredentials always returns a non-null Response when credentials exist, so every tryAgySdkFallbackForRequest call site short-circuits the OAuth quota-wait path when all API keys are simultaneously rate-limited. src/plugin/quota.ts (aggregateQuota logic) and src/plugin.ts (tryAgySdkFallbackForRequest return-value handling in the soft-quota and rate-limit wait branches) Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Incoming fetch request] --> B{Is OAuth auth available?}
B -- No OAuth, API keys present --> C[API-key-only loader branch]
C --> D{isAntigravityOnlyGenerativeLanguageRequest?}
D -- Yes --> E[Return synthetic 404 with OAuth guidance]
D -- No --> F{isAgySdkSupportedRequest?}
F -- No --> G[Native fetch passthrough]
F -- Yes --> H[tryFetchWithAgySdkCredentials round-robin key pool]
H --> I{All keys rate-limited?}
I -- Yes --> J[Return 429 All keys exhausted]
I -- No --> K[Return API response with enhanced 404 if needed]
B -- OAuth present --> L[OAuth / AccountManager path]
L --> M{prefer_for_gemini and API keys present?}
M -- Yes --> H
M -- No --> N[Select OAuth account via triedSwitchIndices]
N --> O{Account found?}
O -- No: all tried --> P[tryAgySdkFallbackForRequest]
P --> Q{Keys available and not all 429?}
Q -- Yes --> K
Q -- No --> R[Return last failure or rate-limit error]
O -- Yes --> S[prepareAntigravityRequest await Request body]
S --> T[Fetch via Antigravity/Gemini CLI backend]
T --> U{Success?}
U -- Yes --> V[transformAntigravityResponse]
U -- 429/quota --> W{Retry / switch account?}
W -- Switch --> N
W -- All exhausted --> P
|
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/plugin/request.ts (1)
1787-1857:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
Retry-Afterextraction is unreachable in the normal Google error shape.Any payload with
{ error: ... }returns from the earlier block first, so thisgoogle.rpc.RetryInfoparsing never runs and callers never get the backoff headers. Move thedetailsscan before that return, or set the retry headers inside the sameerrorBody?.errorbranch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugin/request.ts` around lines 1787 - 1857, The Retry-After extraction logic is unreachable because any payload with errorBody.error returns early; update the handling in request.ts by moving the google.rpc.RetryInfo scan (the block that finds detail['`@type`'] === 'type.googleapis.com/google.rpc.RetryInfo' and sets Retry-After/retry-after-ms) into the same branch that processes errorBody.error (the block that builds rawErrorMessage, debugInfo, sets errorBody.error.message, checks detectErrorType, and sets x-antigravity-context-error), or alternatively run that details scan immediately before the existing early return; adjust references to errorBody, headers, and retryInfo accordingly so callers receive the backoff headers.
🧹 Nitpick comments (1)
src/plugin/config/schema.test.ts (1)
72-80: ⚡ Quick winAdd JSON-schema parity test for
model_discoveryas well.
agy_sdkverifies both defaults and schema docs, butmodel_discoverycurrently verifies defaults only. Adding the schema assertion prevents drift.Suggested test addition
describe("model_discovery config", () => { it("includes model_discovery defaults in DEFAULT_CONFIG", () => { expect(DEFAULT_CONFIG.model_discovery).toMatchObject({ enabled: true, gemini_api: true, antigravity: true, }); }); + + it("documents model_discovery in the JSON schema", () => { + const schemaPath = new URL("../../../assets/antigravity.schema.json", import.meta.url); + const schema = JSON.parse(readFileSync(schemaPath, "utf8")) as { + properties?: Record<string, { type?: string; default?: unknown }>; + }; + const modelDiscovery = schema.properties?.model_discovery; + expect(modelDiscovery).toBeDefined(); + expect(modelDiscovery?.type).toBe("object"); + }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugin/config/schema.test.ts` around lines 72 - 80, Add a JSON-schema parity assertion for model_discovery by importing the plugin config JSON schema (e.g., CONFIG_SCHEMA or PLUGIN_CONFIG_SCHEMA) and asserting the schema's default for properties.model_discovery matches DEFAULT_CONFIG.model_discovery; specifically, inside the "model_discovery config" test add an expectation like expect(CONFIG_SCHEMA.properties.model_discovery.default).toMatchObject(DEFAULT_CONFIG.model_discovery) so schema and defaults cannot drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@assets/antigravity.schema.json`:
- Around line 200-203: The JSON Schema marks "api_key" and "enabled" as required
but your runtime uses Zod defaults so these may be omitted; update the schema by
removing "api_key" and "enabled" from the "required" arrays (and the analogous
fields at the other block around lines 208-213) or mark them as
optional/defaulted so the schema matches the Zod runtime defaults; locate the
"required" arrays in antigravity.schema.json and adjust them to reflect the
runtime defaulting behavior rather than forcing presence.
In `@README.md`:
- Line 143: Update the README bullet so the fallback to configured
agy_sdk.cloud_projects is described as conditional: state that this fallback
occurs only when the AGY SDK is enabled, the fallback option is turned on, and
valid AGY SDK credentials (cloud_projects) are available; reference the existing
config key agy_sdk.cloud_projects and the AGY SDK enablement setting when
rewording the sentence to avoid implying unconditional behavior.
In `@src/plugin.ts`:
- Around line 1620-1633: The current discovery uses Promise.all for
modelsFromAgySdkCredentials and modelsFromOAuthAuth so a single rejection forces
returning fallbackModels; change this to a partial-success approach (e.g.,
Promise.allSettled or separate try/catch calls) to collect successful results
and their errors, then call mergeModelDefinitions with fallbackModels plus only
the successfully-resolved model lists before passing to
normalizeProviderHookModels (referencing modelsFromAgySdkCredentials,
modelsFromOAuthAuth, mergeModelDefinitions, normalizeProviderHookModels,
fallbackModels, providerId, provider, client); log individual source errors at
debug/warn and only use fallbackModels for sources that failed rather than
discarding all discovery.
In `@src/plugin/api-key.ts`:
- Around line 61-64: The code currently selects only one of GEMINI_API_KEY or
GOOGLE_API_KEY via `GEMINI_API_KEY || GOOGLE_API_KEY`, causing the other valid
key to be ignored; change the logic in the block that builds
`envKeys`/`singleEnvKey` so both environment single-key vars are collected
(e.g., gather `process.env.GEMINI_API_KEY` and `process.env.GOOGLE_API_KEY` into
an array, filter out empty/undefined values, trim them, deduplicate if needed)
and then iterate over that combined list when pushing into `credentials`
(reference the variables `envKeys`, `GEMINI_API_KEY`, `GOOGLE_API_KEY`, and the
`credentials.push(...)` site to make the change).
- Around line 225-265: prepareAgySdkGeminiRequest currently only rebuilds
headers/body from init and drops Request metadata when input is a Request; fix
it by detecting when input is a Request, cloning/reading it (e.g.,
request.clone().text()), and preserving its method, signal, and original headers
(merge with the new Headers logic) before applying resolveModelForHeaderStyle
and applyAgySdkGeminiBodyTransforms; alternatively make the helper async so it
can await request.text(), then return init that includes method, signal, headers
(merged), and the transformed body so callers of prepareAgySdkGeminiRequest get
the same HTTP method/signal/body as the original Request.
In `@src/plugin/config/schema.ts`:
- Line 46: Update the zod validation for the api_key field so it rejects
whitespace-only strings; replace the current api_key: z.string().min(1) rule
with one that ensures after trimming the value is non-empty (e.g., use a refine
or equivalent check) so values like " " fail validation rather than being
normalized later by project.api_key.trim(); modify the schema entry named
api_key in src/plugin/config/schema.ts accordingly and include a clear
validation message for failure.
In `@src/plugin/debug.ts`:
- Around line 308-310: Update isSensitiveHeader to also treat
Proxy-Authorization as sensitive: inside the isSensitiveHeader(key: string)
function, lower-case the header name as done now and add a check for
"proxy-authorization" (or more generally any header that
endsWith("-authorization") if you prefer wider coverage) in addition to
"authorization" and the existing api-key check so Proxy-Authorization values are
redacted from debug logs.
In `@src/plugin/request.ts`:
- Around line 757-764: isGenerativeLanguageRequest currently does a substring
check and should instead parse the URL and compare the hostname exactly so only
real Google Generative Language API requests are intercepted; update
isGenerativeLanguageRequest to call requestInfoUrl(input), construct a new
URL(...) (wrap in try/catch for invalid URLs), and return parsed.hostname ===
"generativelanguage.googleapis.com" (use the hostname property so ports or
credentials are handled consistently); keep requestInfoUrl as-is to support
string and Request inputs and ensure callers like prepareAntigravityRequest use
the updated function.
- Around line 805-807: The current construction of requestUrl/baseInit/headers
in request.ts drops original Request properties when input is a Request and init
is undefined; update the logic in the code that builds baseInit (and uses
requestInfoUrl/requestUrl and headers) to detect when input is an instance of
Request and merge its method, body, signal, and other fetch-relevant fields into
baseInit (with init taking precedence), and carry over headers from the original
Request so the JSON body-rewrite path still sees the original body; ensure you
use the same symbols (requestInfoUrl, requestUrl, baseInit, headers) and only
override fields when init explicitly provides them.
In `@src/plugin/transform/model-resolver.ts`:
- Around line 410-422: The model-transform logic in transform/model-resolver.ts
incorrectly treats the "-minimal" tier as part of the base name because the
replace chain on requestedModel only strips "-low|-medium|-high" and not
"-minimal", causing transformedModel to become e.g.
"gemini-3-flash-minimal-preview" which is invalid; update the normalization so
that requestedModel has "-minimal" removed alongside the other tier suffixes
(modify the initial replace that produces transformedModel), and ensure the
subsequent hasPreviewSuffix and usesBareName checks (which reference
transformedModel and GEMINI_DOTTED_MINOR_REGEX) still operate on the cleaned
name so that the correct "-preview" fallback logic applies.
---
Outside diff comments:
In `@src/plugin/request.ts`:
- Around line 1787-1857: The Retry-After extraction logic is unreachable because
any payload with errorBody.error returns early; update the handling in
request.ts by moving the google.rpc.RetryInfo scan (the block that finds
detail['`@type`'] === 'type.googleapis.com/google.rpc.RetryInfo' and sets
Retry-After/retry-after-ms) into the same branch that processes errorBody.error
(the block that builds rawErrorMessage, debugInfo, sets errorBody.error.message,
checks detectErrorType, and sets x-antigravity-context-error), or alternatively
run that details scan immediately before the existing early return; adjust
references to errorBody, headers, and retryInfo accordingly so callers receive
the backoff headers.
---
Nitpick comments:
In `@src/plugin/config/schema.test.ts`:
- Around line 72-80: Add a JSON-schema parity assertion for model_discovery by
importing the plugin config JSON schema (e.g., CONFIG_SCHEMA or
PLUGIN_CONFIG_SCHEMA) and asserting the schema's default for
properties.model_discovery matches DEFAULT_CONFIG.model_discovery; specifically,
inside the "model_discovery config" test add an expectation like
expect(CONFIG_SCHEMA.properties.model_discovery.default).toMatchObject(DEFAULT_CONFIG.model_discovery)
so schema and defaults cannot drift.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 585aabb2-0d2b-419e-a92f-24fb060f337f
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (21)
CHANGELOG.mdREADME.mdassets/antigravity.schema.jsonsrc/constants.tssrc/plugin.test.tssrc/plugin.tssrc/plugin/api-key.test.tssrc/plugin/api-key.tssrc/plugin/config/models.test.tssrc/plugin/config/models.tssrc/plugin/config/schema.test.tssrc/plugin/config/schema.tssrc/plugin/debug.test.tssrc/plugin/debug.tssrc/plugin/quota.tssrc/plugin/request.test.tssrc/plugin/request.tssrc/plugin/transform/index.tssrc/plugin/transform/model-resolver.test.tssrc/plugin/transform/model-resolver.tssrc/plugin/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
🧰 Additional context used
🪛 OpenGrep (1.21.0)
src/plugin.ts
[ERROR] 1146-1146: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
[ERROR] 1157-1157: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🔇 Additional comments (12)
CHANGELOG.md (1)
3-12: LGTM!src/plugin/config/models.ts (1)
35-50: LGTM!Also applies to: 86-97, 126-140, 148-241
src/plugin/config/models.test.ts (1)
3-7: LGTM!Also applies to: 27-35, 56-62, 72-119
src/plugin/transform/index.ts (1)
25-25: LGTM!src/plugin/transform/model-resolver.test.ts (1)
45-50: LGTM!Also applies to: 70-87, 145-161, 331-386
src/plugin/config/schema.ts (1)
51-77: LGTM!Also applies to: 332-350, 531-541
src/constants.ts (1)
148-148: LGTM!assets/antigravity.schema.json (1)
216-243: LGTM!src/plugin/config/schema.test.ts (1)
50-70: LGTM!src/plugin/types.ts (1)
12-12: LGTM!Also applies to: 26-44, 109-109
src/plugin/quota.ts (1)
65-76: LGTM!Also applies to: 185-188, 343-343
src/plugin.test.ts (1)
1-100: LGTM!
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/plugin/api-key.test.ts`:
- Around line 101-126: The test is incorrectly asserting Request.body identity;
update the test for prepareAgySdkGeminiRequest to read/parse the
prepared.init.body (or prepared.request body string) and assert the
rewritten/normalized payload contents instead of using toBe(original.body).
Specifically, replace the identity check
expect(prepared.init.body).toBe(original.body) with code that reads
prepared.init.body (or constructs a text/json from prepared.request) and asserts
the payload JSON has the expected contents (e.g., the contents/parts/text value
"hi" and any normalization you expect), while keeping the other header
assertions (x-request-id removed Authorization and x-goog-api-key present)
intact so the test verifies body equality not reference identity.
In `@src/plugin/api-key.ts`:
- Around line 257-275: The current logic only rewrites JSON when originalBody is
a non-empty string, missing cases where requestInput?.body is a ReadableStream;
update the prepare logic around originalBody/requestInput/init so that if the
incoming body is a ReadableStream you read/buffer it to text (clone or
stream-to-string) into originalBody, run
applyAgySdkGeminiBodyTransforms(payload, resolved?.actualModel ?? rawModel ??
"", resolved?.thinkingLevel) then set body = JSON.stringify(payload), and
finally ensure init.duplex = "half" whenever you forward a ReadableStream (or
any stream-like body) to satisfy Undici; keep existing string-path behaviour but
add stream detection and conversion before returning request/init.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 772f34eb-6c20-49e0-b618-9a8fad61ec31
📒 Files selected for processing (2)
src/plugin/api-key.test.tssrc/plugin/api-key.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/plugin/api-key.ts (1)
278-304:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve the original
Requestoptions when rebuildinginit.This new
Requestpath only carries overmethod.signal,redirect,cache,credentials,mode,referrer, and similar options are dropped once the call becomesfetch(prepared.request, prepared.init), so aborts and other request semantics can silently stop working.🔧 Suggested fix
export async function prepareAgySdkGeminiRequest( input: RequestInfo, init: RequestInit | undefined, credential: AgySdkCredential, ): Promise<PreparedAgySdkGeminiRequest> { const requestInput = typeof input === "string" ? undefined : input; + const inheritedInit: RequestInit = requestInput + ? { + method: requestInput.method, + signal: requestInput.signal, + cache: requestInput.cache, + credentials: requestInput.credentials, + integrity: requestInput.integrity, + keepalive: requestInput.keepalive, + mode: requestInput.mode, + redirect: requestInput.redirect, + referrer: requestInput.referrer, + referrerPolicy: requestInput.referrerPolicy, + } + : {}; const urlString = requestInput?.url ?? input.toString(); const url = new URL(urlString); const match = url.pathname.match(/\/models\/([^:]+):(\w+)/); const rawModel = match?.[1] ?? undefined; const action = match?.[2] ?? undefined; @@ return { request: url.toString(), init: { - method: requestInput?.method, + ...inheritedInit, ...init, headers, body, }, model: resolved?.actualModel ?? rawModel, }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugin/api-key.ts` around lines 278 - 304, The rebuilt init currently only preserves method and spreads init, losing Request-specific options (signal, redirect, cache, credentials, mode, referrer, referrerPolicy, integrity, keepalive, duplex, etc.); extract those options from requestInput when it is a Request (e.g., create requestOptions = requestInput instanceof Request ? { signal: requestInput.signal, redirect: requestInput.redirect, cache: requestInput.cache, credentials: requestInput.credentials, mode: requestInput.mode, referrer: requestInput.referrer, referrerPolicy: requestInput.referrerPolicy, integrity: requestInput.integrity, keepalive: requestInput.keepalive, /* duplex if available */ } : {}) and then build the returned init by spreading requestOptions before spreading init and the other overrides (ensuring method and headers/body remain correct) so abort signals and all original request semantics are preserved (reference symbols: requestInput, requestTextBody, originalBody, headers, the returned init).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@script/build-schema.ts`:
- Around line 101-112: The post-processing in alignAgySdkDefaults only strips
required from agy_sdk but misses model_discovery, so update alignAgySdkDefaults
to do the same for props?.model_discovery: if present delete
model_discovery.required and then locate
model_discovery.properties?.cloud_projects?.items and, when items.required is an
array, filter out "enabled" (same logic as for agy_sdk.cloud_projects). Use the
same symbol names (alignAgySdkDefaults, agy_sdk, model_discovery,
cloud_projects, items) so the changes mirror the existing removal behavior.
In `@src/plugin/request.ts`:
- Around line 845-892: The Request normalization drops original Request headers
and other Request-specific fields; update the logic around requestInput,
baseInit and headers so you fully preserve and merge the original Request
semantics: start baseInit by shallow-copying all relevant Request fields from
requestInput (method, body, signal, redirect, credentials, cache, referrer,
referrerPolicy, mode, integrity, keepalive, etc.) and then overlay any
properties from init, and build headers by constructing new
Headers(requestInput?.headers) and then merging/overwriting with init?.headers
(instead of choosing one or the other) so existing Request headers are retained
and only overridden by init headers; keep prepareAntigravityRequest and
requestInfoUrl usages intact.
---
Duplicate comments:
In `@src/plugin/api-key.ts`:
- Around line 278-304: The rebuilt init currently only preserves method and
spreads init, losing Request-specific options (signal, redirect, cache,
credentials, mode, referrer, referrerPolicy, integrity, keepalive, duplex,
etc.); extract those options from requestInput when it is a Request (e.g.,
create requestOptions = requestInput instanceof Request ? { signal:
requestInput.signal, redirect: requestInput.redirect, cache: requestInput.cache,
credentials: requestInput.credentials, mode: requestInput.mode, referrer:
requestInput.referrer, referrerPolicy: requestInput.referrerPolicy, integrity:
requestInput.integrity, keepalive: requestInput.keepalive, /* duplex if
available */ } : {}) and then build the returned init by spreading
requestOptions before spreading init and the other overrides (ensuring method
and headers/body remain correct) so abort signals and all original request
semantics are preserved (reference symbols: requestInput, requestTextBody,
originalBody, headers, the returned init).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 263a5e14-f353-4ca1-a2fe-b984bf1764a0
📒 Files selected for processing (16)
README.mdassets/antigravity.schema.jsonscript/build-schema.tssrc/plugin.tssrc/plugin/api-key.test.tssrc/plugin/api-key.tssrc/plugin/config/models.test.tssrc/plugin/config/models.tssrc/plugin/config/schema.test.tssrc/plugin/config/schema.tssrc/plugin/debug.test.tssrc/plugin/debug.tssrc/plugin/request.test.tssrc/plugin/request.tssrc/plugin/transform/model-resolver.test.tssrc/plugin/transform/model-resolver.ts
✅ Files skipped from review due to trivial changes (1)
- README.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
🔇 Additional comments (10)
src/plugin/debug.test.ts (1)
79-93: LGTM!assets/antigravity.schema.json (1)
200-202: LGTM!src/plugin/config/schema.test.ts (1)
4-4: LGTM!Also applies to: 71-74
src/plugin/config/models.test.ts (1)
97-119: LGTM!src/plugin/config/models.ts (1)
202-202: LGTM!src/plugin/debug.ts (1)
310-312: LGTM!src/plugin/api-key.test.ts (1)
25-28: LGTM!Also applies to: 37-37, 55-55, 62-63, 95-95, 108-108, 110-110, 121-121, 129-137, 143-144, 165-165, 179-180, 192-192, 203-204, 222-222
src/plugin/transform/model-resolver.ts (1)
409-427: LGTM!src/plugin/request.test.ts (1)
80-88: LGTM!Also applies to: 560-588
src/plugin/api-key.ts (1)
19-23: LGTM!Also applies to: 36-43, 84-94, 135-145, 330-330
The interactive auth-login menu uses select() with clearScreen: true, so on every re-render the terminal is cleared. Actions that print results and then continue back to the menu (check quotas, verify, verify-all, manage, configure-models) had their output wiped before the user could read it. Add a pressEnterToContinue() helper in cli.ts and call it at every menu-loop branch that prints user-visible output. No-op when stdin is not a TTY so CI / piped input is unaffected.
|
@thaihoc215 Resolved the previous issue for the plugin: |
|
I installed this on Windows and it's causing several errors: The first error is that it sometimes displays the following message: "" Your account does not have the required IAM permission "cloudaicompanion.companions.generateChat" on the resource "projects/rising-fact-p41fc". Contact your administrator. "" The next error is that it freezes; that is, it stops responding for a long time. |
|
@titet11 feel free to submit a PR against this fork resolving any issues you run across. The error message hasn't changed from upstream. |
|
@chrisgeo tested and model work well on my windows device, thank you |
|
After further analysis, the error I found is not related to this request. The problem is that, with Gemini models, the extension doesn't detect when the quota limit has been reached and continues sending requests to accounts that have already returned error 429. |
It still uses the same fallback methods as the original, with new models. The docs in the fork have one update if you don't want a fallback to Gemini CLI: In {
"agy_sdk": {
"default": {
"enabled": true,
"prefer_for_gemini": false,
"api_key_fallback": true,
"cloud_projects": []
}
} |
Code Review SummaryThe review did not run because the selected model is no longer available. Choose another model in Kilo Code review settings: https://app.kilo.ai/code-reviews |
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
The plugin had isLikelyAntigravityOnlyModel() but only used it post-hoc in enhanceAgySdkErrorResponse to dress up 404s after the public Gemini API rejected the request. isAgySdkSupportedRequest() accepted any gemini-* URL, so bare Antigravity-only ids (gemini-3.1-pro, gemini-3-pro, gemini-3-flash, gemini-3.1-flash) leaked to the public API whenever agy_sdk.api_key_fallback kicked in or auth was API-key only. - isAgySdkSupportedRequest now rejects Antigravity-only ids so the api-key path can never route them. The existing antigravity \u2194 gemini-cli OAuth quota fallback (allowQuotaFallback) handles these models as originally intended. - Add isAntigravityOnlyGenerativeLanguageRequest + extractRequestedGeminiModel + createAntigravityOnlyModelErrorResponse so the api-key-only auth paths short-circuit with a Gemini-shaped 404 carrying the same OAuth guidance message, no wasted round trip. - Refactor enhanceAgySdkErrorResponse to share buildAntigravityModelGuidanceMessage with the pre-flight helper to prevent message drift. - Update isAgySdkSupportedRequest tests for the new contract; add tests for the new helpers.
Two coupled defects surfaced when running alongside oh-my-opencode (which flips the google provider into API-key mode): 1. Disk-stored OAuth accounts were ignored. `auth.loader` only branched on `isOAuthAuth(auth)` from OpenCode's getAuth(), so when the provider was API-key but `antigravity-accounts.json` held usable refresh tokens, the API-key-only interceptor short-circuited `antigravity-*` requests with a synthetic 404. Now `auth.loader` consults `loadAccounts()` first and synthesizes an OAuth auth from the active disk account; the OAuth fetch handler gates on `accountManager.getAccountCount() === 0` so per-request behavior follows real disk state. 2. Antigravity-only Gemini ids had no working api-key fallback. The public Gemini API doesn't serve bare `gemini-3.x-pro/flash` (verified via `GET v1beta/models?key=$GEMINI_API_KEY`), so `isAgySdkSupportedRequest` refused to forward them, leaving OAuth-exhaustion fallback a no-op for exactly the models that needed it. Added `mapAntigravityModelToPublicApi` with the verified mapping (gemini-3-pro -> -preview, gemini-3-flash -> -preview, gemini-3.1-pro -> -preview, gemini-3.1-flash -> -lite). Applied in `resolveModelForHeaderStyle`'s agy-sdk branch; the gate now consults a shared `canRouteAsPublicGeminiApiModel` helper that classifies after stripping the prefix and tier suffix. Mixed-mode safety guards (from two rounds of code review): - `client.auth.set` (invalid_grant cleanup) is wrapped in `if (initialAuthWasOAuth)` so promoted-from-disk OAuth failures don't wipe OpenCode's API-key auth by writing empty OAuth credentials. - OAuth-branch credentials include the captured `apiKeyAuth` so OpenCode's API key counts as a fallback credential alongside env vars and `agy_sdk.cloud_projects`. - Last-OAuth-account `invalid_grant` attempts `tryAgySdkFallbackForRequest` before throwing, giving mixed-mode setups a real response instead of reauth guidance they can't act on. - `canRouteAsPublicGeminiApiModel` requires `antigravity-` prefixed inputs without a known mapping to strip to `^gemini-...` so unknown ids (`antigravity-mystery-model`) still hit the synthetic 404 instead of being raw-forwarded. Tests verify the OAuth promotion path is taken (and `client.auth.set` is NOT called in mixed mode after invalid_grant), the per-id translation table, and the loosened gate contract. 1070 tests pass; typecheck clean.
|
hello! i fixed the installation issue with locally installing the plugin since for some reason using the config you provided it never worked. |
|
@angelo0098 yes. Please read the documentation. |
|
Frankly, it's starting to bother me that the owner of this repository isn't releasing new versions, and even more so that it takes days or weeks to approve a single request. If any of you would like to clone the repository and provide more active support than the creator himself, that would be excellent. |
…usted Background agents using an Antigravity-only Gemini model (e.g. the OMO visual-engineering category -> antigravity-gemini-3.1-pro, plus the per-run gemini-3-flash title generation) could pin a CPU core at 100% and hang opencode indefinitely once the antigravity quota pool was exhausted across all accounts. The request loop spun synchronously (no fetch, no progress), so it was unkillable by SIGTERM and froze the whole process including the parent session. Root cause: hybrid account selection judged availability at family level (isRateLimitedForFamily requires BOTH pools exhausted) and kept re-selecting an antigravity-exhausted account whose gemini-cli pool was still open, while the routing check rejected it per header style and set shouldSwitchAccount without making the next selection exclude it -> select/switch forever. - Track accounts switched-away-from within a request (triedSwitchIndices) and pass them as excludeIndices to getCurrentOrNextForFamily / getNextForFamily so each switch makes forward progress (or selection returns null). Cleared after a rate-limit/quota wait so accounts are retried once resets free them. - Add a loop guard and a fast give-up when every account has been tried, so the request loop provably terminates (returns the real error or the api-key fallback) instead of spinning the event loop. - Make createSyntheticErrorResponse family-aware: emit a Gemini-shaped SSE stream for Gemini requests instead of the Claude-shaped stream, which OpenCode cannot parse for a Gemini model and would leave hanging. Adds regression tests for exclusion-based selection and the Gemini-format synthetic response.
In the discord he said he archived the project and that he doesn't have the time or interest to keep working on it. So we need someone to clone the repository to continue working on it. |
This did not work for me either, on either windows or linux. |
|
@volcán I've tried countless times, but I can't establish a connection. I even tried using a proxy that links to the AMP code, which I've already configured, but without success. To answer the relevant question, I'd be willing to clone the repository and approve the requests. |
…-planning
A Gemini-3 Antigravity agent re-derived the same plan every turn (visible as
an agent looping on one instruction, e.g. repeatedly searching for "Phase 3P").
shouldCacheThinkingSignatures already enabled signature caching for gemini-3 and
the response side cached them, but the input-side restore was gated to Claude
only, so every Gemini-3 turn shipped the skip_thought_signature_validator
sentinel instead of the real signature and the model lost its signed
chain-of-thought.
- Defect 1: sanitizeRequestPayloadForAntigravity now takes optional
{ sessionKey, getSignature } and restores the cached real signature onto the
first functionCall part (looked up by the block's thinking text) before
falling back to the sentinel. Wired for non-Claude gemini-3 requests.
- Defect 2: the signature cache key embedded projectId, so a quota-driven
account/project switch changed the key and missed the cache (the bridge that
turned the old account-switch freeze into a re-plan loop). Add
signatureCacheProjectKey() to drop projectId for gemini-3 at all three
buildSignatureSessionKey sites, so continuity survives account switches. A
live A/B test confirmed gemini-3 thoughtSignatures are accepted across
projects (HTTP 200), so reusing them is safe.
Adds regression tests for the restore and the project-independent cache key.
Standalone tsx probes used to validate the signature-loop fix (manual run; both hit the live Antigravity endpoint and consume quota): - ab-signature-scope.ts: replays a captured thoughtSignature under a different account/project to test cross-project portability. Confirmed signatures are accepted across projects (HTTP 200), so the project-independent cache key is safe. - signature-continuity-probe.ts: A/B-compares real-signature vs sentinel on a multi-step tool task to probe whether the model re-plans.
|
Hi @chrisgeo , I see the latest changes from your fork repo. Do you have a plan to publish as a new plugin? Or do I have to clone your repo to build and run it from my local machine? |
@thaihoc215 I don't have plans to publish separately. Likely plugins need to move to an org to allow better maintenance. You can install it with a clone or using the direct git reference from the opencode docs. |
AGENT_PLAN_ANCHOR ## Original User Request (verbatim) O projeto https://github.com/NoeFabris/opencode-antigravity-auth/ e o nosso projeto atual seguem a mesma base, portanto vamos mesclar NoeFabris/opencode-antigravity-auth#576 no nosso projeto. ## Objective Port upstream PR #576 into the current fork so the project gains AntiGravity SDK support, Gemini API-key routing/pooling, dynamic model discovery, and the later stability fixes included on the PR branch. ## Why This Plan - The current fork and upstream PR share commit 09ccf4b as a merge base, but both sides diverged afterward. - A blind branch replacement would overwrite fork-specific package metadata, repository URLs, Node 25 ESM fixes, and local docs updates. - A normal merge preview reports conflicts in package-lock.json, src/plugin.ts, src/plugin/quota.ts, and model resolver files; resolving those intentionally is safer than copying files wholesale. ## Scope - In scope: upstream PR #576 feature and fix changes, conflict resolution, tests, typecheck/build verification, and a code review loop. - Out of scope: pushing to GitHub, changing container/infrastructure config, broad Desloppify cleanup unrelated to the merge, or changing package identity away from opencode-antigravity-auth-updated. ## Current Context - Working directory: /home/ubuntu/MEGA/WORK/opencode-antigravity-auth-updated. - Current branch is main at e168501. - Remote origin is git@github.com:insign/opencode-antigravity-auth-updated.git. - Upstream PR ref fetched as refs/remotes/upstream/pr-576. - Project has no Makefile and no lib directory; required lib Desloppify scan is not applicable, so src scan was used for evidence. - User clarified this project does not use Docker or Podman; native npm commands must be used. ## Constraints, Preferences, and Biases to Preserve - Preserve fork package name, repository URL, homepage, bug URL, README package references, and build script compatibility. - Preserve local Node 25 ESM import fix behavior unless the PR explicitly supersedes it. - Do not use Docker or Podman. - Do not stage generated Desloppify artifacts unless intentionally part of the merge. - Prefer minimal conflict resolutions that retain upstream behavior and fork-specific metadata. ## Assumptions to Validate - The PR branch can be merged with manual conflict resolution. - Validation: git merge --no-commit succeeds after resolving conflicts and tests pass. - If false: manually apply the PR diff by file and discard unrelated upstream metadata changes. - Existing local tests cover the merge sufficiently once PR tests are included. - Validation: npm test and npm run typecheck pass. - If false: fix merge regressions and rerun checks. ## Options Considered - Accepted: no-commit merge followed by targeted conflict resolution, because it preserves history relationships and applies the full PR surface. - Rejected: replacing current tree with upstream PR branch, because it would discard fork-specific package/docs/CI details. - Rejected: cherry-picking 34 commits one by one, because conflict repetition would be high and final behavior matters more than preserving upstream micro-commits. ## Execution Plan - [ ] Step 1: Merge PR #576 into the worktree without committing and resolve conflicts. - Why now: this captures the complete upstream feature surface while preserving the chance to inspect all conflicts before committing. - Validation: no unresolved conflict markers remain and git status shows only intended files. - [ ] Step 2: Adapt fork-specific metadata and ESM/build compatibility. - Why now: package identity and Node compatibility must be correct before tests/build. - Validation: package.json/package-lock/README/package references remain fork-specific and imports resolve under Node ESM. - [ ] Step 3: Run native verification. - Why now: the merge touches core request routing, auth, quota, model resolution, and tests. - Validation: npm test, npm run typecheck, and npm run build where feasible pass. - [ ] Step 4: Commit the port and run reviewer loop. - Why now: code review should evaluate the complete merged code commit. - Validation: reviewers report no judge-approved remaining fixes, or approved fixes are committed and re-reviewed. - [ ] Step 5: Final docs/context check and report. - Why now: ensure user receives merge state, verification, and review decisions. - Validation: final response includes tests and Code Review section. ## Do - Use native npm commands for tests/checks. - Keep upstream PR behavior where it does not conflict with fork-specific identity or existing fixes. - Resolve conflicts with source inspection, not assumptions. - Avoid unrelated cleanup. ## Do Not - Do not use Docker or Podman. - Do not push unless explicitly requested. - Do not revert unrelated user changes. - Do not let upstream package metadata overwrite the fork identity. ## References - NoeFabris/opencode-antigravity-auth#576 - refs/remotes/upstream/pr-576 - package.json - README.md - src/plugin.ts - src/plugin/api-key.ts - src/plugin/transform/model-resolver.ts ## Risks and Dependencies - Risk: PR #576 is large and touches core routing behavior. - Mitigation: run full tests/typecheck/build and independent reviewer loop. - Risk: local fork has changes not present upstream. - Mitigation: preserve local metadata and inspect conflicts manually. - Risk: generated Desloppify artifacts could pollute the commit. - Mitigation: stage only intended merge files. ## Handoff Notes - The latest plan anchor should be used for any resumed work. - Upstream PR #576 includes later fixes for quota aggregation, OAuth/API-key fallback, infinite account-switch loop, and Gemini-3 thought-signature continuity; do not stop at the early feature commits. - User explicitly stated the project does not use Docker nor Podman. ## Definition of Done - PR #576 feature/fix changes are present in this fork. - Fork package identity remains opencode-antigravity-auth-updated. - Native tests and typecheck pass, with build attempted if feasible. - Code changes are committed with PLAN_REF. - Reviewer loop completes with no judge-approved unresolved findings.
PLAN_REF: 374c922 PREVIOUS_STEP: 374c922 — plan anchor for PR 576 port ## Completed in This Step - Merged upstream PR #576 into the fork with manual conflict resolution. - Preserved fork package identity as opencode-antigravity-auth-updated. - Added API-key routing, agy-sdk fallback, dynamic model discovery, model-resolution updates, quota fixes, and PR regression coverage. - Preserved local stale-provider reload behavior and proactive quota refresh while adding PR test exports. - Restored AccountManager.updateQuotaCache index-or-token support needed by PR tests. - Updated auto-update checker tests to the fork package name. ## References Used - NoeFabris/opencode-antigravity-auth#576 - refs/remotes/upstream/pr-576 - package.json - src/plugin.ts - src/plugin/accounts.ts - src/plugin/quota.ts - src/plugin/transform/model-resolver.ts ## Debts and Warnings - Desloppify generated unstaged artifacts during required scan attempts; these were intentionally not staged. - Desloppify scan --path lib is not applicable because this repo has no lib directory, and a later scan was blocked by Desloppify's active queue state. - Live diagnostic scripts from upstream are included but should consume quota only when run manually. ## Progress (ref: 374c922) - [x] Step 1: Merge PR #576 into the worktree without committing and resolve conflicts. - [x] Step 2: Adapt fork-specific metadata and ESM/build compatibility. - [x] Step 3: Run native verification. - [ ] Step 4: Commit the port and run reviewer loop. - [ ] Step 5: Final docs/context check and report. ## Verification - npm test: passed via tester subagent - npm run typecheck: passed via tester subagent - npm run build: passed via tester subagent ## Next Step chore(agent): [Step 2/5] run reviewer loop for PR 576 port

Summary
Verification
Closes #573 #572 #571 #549