Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ Providers can expose a built-in shorthand, such as `agy` for `google-antigravity
| `project?` | `string` | Vertex or Antigravity Cloud Code Assist project id. |
| — | — | Antigravity account quota probes (`retrieveUserQuota` and `retrieveUserQuotaSummary`) always go to Google's own Cloud Code host through the pinned outbound transport, regardless of a configured `baseUrl`; the account bearer is never sent to an operator-configured endpoint and a redirect aborts the probe. Only the model-list fallback still honors `baseUrl`. |
| — | — | If Antigravity quota summary returns 403 for a valid OAuth account, OpenCodex retries that endpoint once with the legacy `antigravity/1.0` User-Agent and the same token and project. A 401 is not retried. Inference and model discovery retain the IDE User-Agent. |
| — | — | Antigravity quota shows Gemini 4 Argon as separate `Argon` / `Argon (Weekly)` windows next to Gem and Cla. They are display-only: account ranking, quota exhaustion, reset ordering and reset notifications keep using the Gemini and Claude windows, and Argon requests are still judged by the Gemini window. |
| `location?` | `string` | Vertex location; environment fallback is `GOOGLE_CLOUD_LOCATION`. |
| `mcpServers?` | `Record<string, CursorMcpServerConfig>` | Cursor only: stdio or Streamable HTTP MCP servers. |
| `desktopExecutor?` | `DesktopExecutorConfig` | Cursor only: external computer-use and record-screen commands. |
Expand Down
3 changes: 2 additions & 1 deletion src/oauth/account-quota-rank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ export function accountResetTimestamp(
const quota = getCachedProviderAccountQuota(provider, accountId);
if (!quota) return null;
const family = classifyModelFamilyForQuota(provider, requestedModelId);
const windows = (quota.customWindows ?? []).filter(w => !family || windowMatchesFamily(w.label, family));
// Model-attributed rows are diagnostics; one must never become the account's reset time.
const windows = (quota.customWindows ?? []).filter(w => w.modelId === undefined && (!family || windowMatchesFamily(w.label, family)));
const weekly = windows.find(w => /week/i.test(w.label));
let rawReset = weekly?.resetAt ?? quota.weeklyResetAt;
if (rawReset === undefined && provider !== "google-antigravity" && provider !== "anthropic") {
Expand Down
7 changes: 6 additions & 1 deletion src/providers/quota-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ export interface ProviderQuotaWindow {
label: string;
percent: number;
resetAt?: number;
/** Exact CCA wire model measured by this window; display evidence only, not eligibility. */
/**
* Model this window is attributed to: the exact CCA wire model of a fallback measurement, or
* the canonical model of a quota-summary bucket dedicated to one model (Antigravity's Gemini 4
* Argon pool). Display evidence only, never eligibility: routing, exhaustion, reset ordering
* and reset notifications ignore every window that carries it.
*/
modelId?: string;
/**
* Set only when the PRODUCER proved this window covers one model family, structurally rather
Expand Down
45 changes: 39 additions & 6 deletions src/providers/quota/antigravity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,31 @@ function quotaInfoEntries(modelInfo: Record<string, unknown>): Record<string, un
return entries;
}

/**
* Gemini 4 Argon has its own quota pool. Its names also contain "gemini", so every family
* decision below checks Argon first; otherwise Argon readings fill or replace the Gem family.
*/
const ARGON_TOKEN = /(?:^|[^a-z0-9])argon(?:[^a-z0-9]|$)/i;
const GEMINI_TOKEN = /(?:^|[^a-z0-9])gemini(?:[^a-z0-9]|$)/i;
const CLAUDE_FAMILY_TOKEN = /(?:^|[^a-z0-9])(?:claude|3p|gpt|opus|sonnet)(?:[^a-z0-9]|$)/i;
/** Canonical model of the Argon summary pool; tagging keeps the window out of routing. */
const ARGON_POOL_MODEL_ID = "gemini-4-argon";

type AntigravitySummaryFamily = "argon" | "gem" | "cla";

/** A bucket's own id and name decide its family when they name one; the group decides otherwise. */
function antigravitySummaryBucketFamily(bucket: Record<string, unknown>): AntigravitySummaryFamily | undefined {
const identity = `${typeof bucket.bucketId === "string" ? bucket.bucketId : ""} ${typeof bucket.displayName === "string" ? bucket.displayName : ""}`;
if (ARGON_TOKEN.test(identity)) return "argon";
if (GEMINI_TOKEN.test(identity)) return "gem";
if (CLAUDE_FAMILY_TOKEN.test(identity)) return "cla";
return undefined;
}

function classifyAntigravityFamily(modelId: string, modelInfo: Record<string, unknown>, quotaInfo: Record<string, unknown>): "Gem" | "Cla" | null {
const displayName = typeof modelInfo.displayName === "string" ? modelInfo.displayName : "";
// Argon models keep their exact model rows but never seed the shared Gem family bar.
if (ARGON_TOKEN.test(`${modelId} ${displayName}`)) return null;
const tier = typeof quotaInfo.tier === "string" ? quotaInfo.tier : "";
const haystack = `${modelId} ${displayName} ${tier}`.toLowerCase();
if (haystack.includes("gemini")) return "Gem";
Expand Down Expand Up @@ -128,9 +151,13 @@ function parseAntigravityQuotaSummary(body: Record<string, unknown> | null): Pro
for (const rawGroup of groups) {
const group = asRecord(rawGroup);
if (!group) continue;
const groupName = `${typeof group.displayName === "string" ? group.displayName : ""} ${typeof group.description === "string" ? group.description : ""}`.toLowerCase();
const isGemini = groupName.includes("gemini");
const isClaude = groupName.includes("claude") || groupName.includes("3p") || groupName.includes("gpt");
const groupDisplayName = typeof group.displayName === "string" ? group.displayName : "";
const groupName = `${groupDisplayName} ${typeof group.description === "string" ? group.description : ""}`.toLowerCase();
// A description that merely mentions Argon must not move a whole Gemini group.
const groupFamily: AntigravitySummaryFamily | undefined = ARGON_TOKEN.test(groupDisplayName) ? "argon"
: groupName.includes("gemini") ? "gem"
: groupName.includes("claude") || groupName.includes("3p") || groupName.includes("gpt") ? "cla"
: undefined;

const buckets = Array.isArray(group.buckets) ? (group.buckets as unknown[]) : [];
for (const rawBucket of buckets) {
Expand All @@ -143,13 +170,19 @@ function parseAntigravityQuotaSummary(body: Record<string, unknown> | null): Pro

const isWeekly = windowStr.includes("week");
const is5h = windowStr.includes("5h") || windowStr.includes("five");
const family = antigravitySummaryBucketFamily(bucket) ?? groupFamily;

if (isGemini) {
if (family === "argon") {
const label = is5h ? "Argon" : isWeekly ? "Argon (Weekly)" : "";
if (label && !customWindowsMap.has(label)) {
customWindowsMap.set(label, { label, modelId: ARGON_POOL_MODEL_ID, percent, ...(resetAt !== undefined ? { resetAt } : {}) });
}
} else if (family === "gem") {
const label = is5h ? "Gem" : isWeekly ? "Gem (Weekly)" : "";
if (label && !customWindowsMap.has(label)) {
customWindowsMap.set(label, { label, percent, ...(resetAt !== undefined ? { resetAt } : {}) });
}
} else if (isClaude) {
} else if (family === "cla") {
const label = is5h ? "Cla" : isWeekly ? "Cla (Weekly)" : "";
if (label && !customWindowsMap.has(label)) {
customWindowsMap.set(label, { label, percent, ...(resetAt !== undefined ? { resetAt } : {}) });
Expand All @@ -164,7 +197,7 @@ function parseAntigravityQuotaSummary(body: Record<string, unknown> | null): Pro
}
}

const PREFERRED_ORDER = ["Gem", "Gem (Weekly)", "Cla", "Cla (Weekly)"];
const PREFERRED_ORDER = ["Gem", "Gem (Weekly)", "Cla", "Cla (Weekly)", "Argon", "Argon (Weekly)"];
const customWindows = Array.from(customWindowsMap.values()).sort((a, b) => {
const ia = PREFERRED_ORDER.indexOf(a.label);
const ib = PREFERRED_ORDER.indexOf(b.label);
Expand Down
2 changes: 1 addition & 1 deletion structure/providers-and-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ Antigravity discovery in `src/providers/antigravity-models.ts` collapses complet
Antigravity catalog rows carry the observed complete effort map from `src/providers/antigravity-models.ts` through the in-memory cache. `src/providers/antigravity-effort-families.ts` uses that evidence to group public rows and project suffix allowlists into the base selection, including both final catalog merge callers. Internal retained suffix rows remain available to combo resolution. Authoritative new-model reconciliation normalizes both the prior baseline and policy input before detecting arrivals: an existing suffix family is not a new model. All-disabled tiers transfer a base disable once; a known base keeps the operator's later choice. Genuinely new families still follow new-model policy. Saved defaults, combo targets, suffix disables and provider selections remain unchanged. A discovered ladder advertises only its wire-backed efforts unless explicitly overridden; unknown single-wire models gain no invented ladder.

Known Claude Sonnet and Opus 5.5 base and low/medium/high IDs share a deterministic usage identity in `src/providers/antigravity-models.ts`, independently of live discovery. This accounting map does not migrate saved routing selections; Claude 4.6 and unknown suffix identities remain unchanged. Antigravity model entitlement is per account: one account may list Claude 4.6, another only the Claude 5.5 suffix families, and Gemini 4 Argon only on Ultra/Enterprise. `ANTIGRAVITY_MODELS` is therefore a static superset (Gemini 3.8 Flash, Gemini 4 Argon, Claude Opus 5.5, Claude Sonnet 5.5), and `ANTIGRAVITY_KNOWN_PICKER_MODELS` keeps dropped bases known to routing, discovery collapse and usage identity. The catalog exposes only what the active account's authoritative discovery returns. `src/providers/antigravity-account-roster.ts` records each account's discovered roster (in memory, six-hour freshness, scoped to config home); `src/oauth/generic-account-failover.ts` ranks Antigravity accounts by it the way it ranks Kiro listings: a listing account wins, a known non-listing account is skipped while another remains, and an account without a fresh roster is unknown and stays usable. Gemini 4 Argon requests and Antigravity model discovery send the Antigravity CLI User-Agent; every other model keeps the IDE User-Agent.
When the `retrieveUserQuotaSummary` endpoint has no usable result, `src/providers/quota/antigravity.ts` preserves bounded per-model `quotaInfo` measurements from `fetchAvailableModels` alongside the existing first-seen Gem/Cla family summaries. Model windows carry the exact CCA wire ID and distinct labels of at most 128 characters for display and diagnostics; GUI summaries and native compact rows preserve the discriminator, retain details, and exclude model readings from headline and severity selection; account and provider routing ignore these rows and continue to use family summaries, and `src/quota/window-mapping.ts` keeps them out of quota-reset observation so one pool reset notifies once. A missing model row or failed probe is unknown evidence, not a model eligibility verdict. Per-account cache entries are bound to the current credential and project identity. Paused reads retain only current last-good measurements without probing; `src/providers/quota/account-row.ts` preserves private currentness callbacks through account-row projection until management publication.
`retrieveUserQuotaSummary` reports Gemini 4 Argon as its own group; `src/providers/quota/antigravity.ts` decides each bucket's family from its own id/name (Argon before Gemini, then Claude/3P/GPT), and only for an inconclusive bucket falls back to the group: Argon from the group display name alone (never its description), Gemini and Claude/3P/GPT from the existing display-name-plus-description rules. Argon buckets become `Argon`/`Argon (Weekly)` windows tagged with the canonical pool model `gemini-4-argon`, so they never fill or replace Gem/Cla in either group order and stay diagnostic like the model windows below; an Argon-only summary is a display report with no routing evidence, and the compact GUI summary and tray aggregation treat it like other diagnostic-only data. When the summary endpoint has no usable result, the same file preserves bounded per-model `quotaInfo` measurements from `fetchAvailableModels` alongside the existing first-seen Gem/Cla family summaries, where an Argon model never seeds Gem. Model windows carry the exact CCA wire ID and distinct labels of at most 128 characters for display and diagnostics; GUI summaries and native compact rows preserve the discriminator, retain details, and exclude model readings from headline and severity selection; account and provider routing ignore these rows and continue to use family summaries, and `src/quota/window-mapping.ts` keeps them out of quota-reset observation so one pool reset notifies once. A missing model row or failed probe is unknown evidence, not a model eligibility verdict. Per-account cache entries are bound to the current credential and project identity. Paused reads retain only current last-good measurements without probing; `src/providers/quota/account-row.ts` preserves private currentness callbacks through account-row projection until management publication.

Account-row projection is loaded with `import()` after each account probe in `src/providers/quota.ts`; its cache dependencies do not create a static return edge into the quota facade.

Expand Down
2 changes: 2 additions & 0 deletions structure/transports/inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ Unicode pattern normalization uses [copy-on-write traversal](byte-accounting.md#
`src/oauth/account-quota-rank.ts` ranks Antigravity custom windows for the requested
Gemini or Claude family, including GPT-OSS in the Claude family. An unknown model
retains all-window ranking; absent matching evidence retains the existing unranked behavior.
Windows carrying `modelId` (exact model rows and the Argon summary pool) are excluded
from headroom and from reset-time selection, including family-less requests.
`src/server/responses/request-transport.ts` passes the routed model at initial selection.
The passthrough, adapter, continuation, sidecar and run-turn execution owners pass
the same routed model during account rotation, without bypassing their send-budget
Expand Down
Loading
Loading