Skip to content
5 changes: 0 additions & 5 deletions config/quality/eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@
"count": 1
}
},
"open-sse/executors/antigravity.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 2
}
},
"open-sse/executors/awsPollyTts.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
Expand Down
30 changes: 30 additions & 0 deletions open-sse/config/agyModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,36 @@
// they are not chat-callable.

export const AGY_PUBLIC_MODELS = Object.freeze([
// Gemini 3.8 Flash tiers. Per-effort upstream ids (`--effort low|medium|high` on the
// CLI); they are dispatched verbatim, unlike the 3.7 tiers below which share one
// `-tiered` upstream id.
{
id: "gemini-3.8-flash-high",
name: "Gemini 3.8 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.8-flash-medium",
name: "Gemini 3.8 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.8-flash-low",
name: "Gemini 3.8 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// Gemini 3.7 Flash tiers. The live endpoint selects High by default and advertises
// all three ids to both the IDE 2.5.5 and CLI 1.1.x clients.
{
Expand Down
35 changes: 35 additions & 0 deletions open-sse/config/antigravityModelAliases.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
// Gemini 3.8 Flash tiers. Unlike 3.7 these are per-effort upstream ids: each tier is a
// real runtime model, so they are NOT aliased onto a shared `-tiered` id and the effort
// is not steered through generationConfig.thinkingConfig.thinkingBudget.
{
id: "gemini-3.8-flash-high",
name: "Gemini 3.8 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.8-flash-medium",
name: "Gemini 3.8 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.8-flash-low",
name: "Gemini 3.8 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// Gemini 3.7 Flash tiers listed by the current official Antigravity model catalog.
// Keep the upstream model ids unchanged so discovery and execution address the same
// models selected by the native client.
Expand Down Expand Up @@ -100,6 +130,11 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
]);

export const ANTIGRAVITY_MODEL_ALIASES = Object.freeze({
// Gemini 3.8 Flash: only the bare id is aliased, onto the upstream default effort
// (medium). The three tier ids are real upstream models and must reach the backend
// verbatim — mapping them onto a shared `-tiered` id (the 3.7 mechanism below) would
// send an id the 3.8 backend does not serve.
"gemini-3.8-flash": "gemini-3.8-flash-medium",
// Gemini 3.7 Flash tiers map to the upstream tiered endpoint model; the thinking
// budget is steered via generationConfig.thinkingConfig.thinkingBudget.
"gemini-3.7-flash": "gemini-3.7-flash-tiered",
Expand Down
3 changes: 3 additions & 0 deletions open-sse/config/freeModelCatalog.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [
{ provider: "agentrouter", modelId: "claude-opus-4-8", displayName: "Claude Opus 4.8", monthlyTokens: 0, creditTokens: 200000000, freeType: "one-time-initial", poolKey: "agentrouter", tos: "caution" },
{ provider: "agentrouter", modelId: "claude-opus-5", displayName: "Claude Opus 5", monthlyTokens: 0, creditTokens: 200000000, freeType: "one-time-initial", poolKey: "agentrouter", tos: "caution" },
{ provider: "agentrouter", modelId: "gpt-5.6-sol", displayName: "GPT-5.6 Sol", monthlyTokens: 0, creditTokens: 200000000, freeType: "one-time-initial", poolKey: "agentrouter", tos: "caution" },
{ provider: "agy", modelId: "gemini-3.8-flash-high", displayName: "Gemini 3.8 Flash (High)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" },
{ provider: "agy", modelId: "gemini-3.8-flash-medium", displayName: "Gemini 3.8 Flash (Medium)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" },
{ provider: "agy", modelId: "gemini-3.8-flash-low", displayName: "Gemini 3.8 Flash (Low)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" },
{ provider: "agy", modelId: "gemini-3.7-flash-high", displayName: "Gemini 3.7 Flash (High)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" },
{ provider: "agy", modelId: "gemini-3.7-flash-medium", displayName: "Gemini 3.7 Flash (Medium)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" },
{ provider: "agy", modelId: "gemini-3.7-flash-low", displayName: "Gemini 3.7 Flash (Low)", monthlyTokens: 0, creditTokens: 0, freeType: "keyless", poolKey: "agy", tos: "avoid" },
Expand Down
55 changes: 34 additions & 21 deletions open-sse/executors/antigravity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ import {
getAntigravityOAuthUserAgent,
} from "../services/antigravityHeaders.ts";
import { classify429, decide429, type Decision } from "../services/antigravity429Engine.ts";
import {
parseRetryFromErrorText,
type RetryHintProvenance,
} from "../services/accountFallback.ts";
import { parseRetryFromErrorText, type RetryHintProvenance } from "../services/accountFallback.ts";
import { parseDetailedRetryHintFromJsonBody } from "../services/retryAfterJson.ts";
import {
shouldRetryWithCredits,
Expand All @@ -28,10 +25,7 @@ import { persistCreditBalance, getAllPersistedCreditBalances } from "@/lib/db/cr
import { setConnectionRateLimitUntil } from "@/lib/db/providers";
import { markAntigravityModelQuotaExhausted } from "../services/antigravityFamilyCooldown.ts";
import { getMitmAlias } from "@/lib/db/models";
import {
MAX_ANTIGRAVITY_OUTPUT_TOKENS,
resolveAntigravityOutputCap,
} from "./antigravityOutputCap.ts";
import { resolveAntigravityOutputCap } from "./antigravityOutputCap.ts";
export { MAX_ANTIGRAVITY_OUTPUT_TOKENS } from "./antigravityOutputCap.ts";
import {
ensureAntigravityProjectAssigned,
Expand Down Expand Up @@ -270,7 +264,11 @@ export function markConnectionQuotaExhausted(
* specific upstream id, pass it here. It is an ALREADY-RESOLVED upstream id, so it bypasses
* the MITM/static alias resolution and is used verbatim (after prefix stripping).
*/
async function cleanModelName(model: string, modelIdOverride?: string): Promise<string> {
export async function cleanModelName(
model: string,
modelIdOverride?: string,
provider = "antigravity"
): Promise<string> {
if (modelIdOverride) {
return modelIdOverride.includes("/") ? modelIdOverride.split("/").pop()! : modelIdOverride;
}
Expand All @@ -282,16 +280,16 @@ async function cleanModelName(model: string, modelIdOverride?: string): Promise<
// Built during model sync — contains ONLY currently-available models.
// Obsolete/removed models are automatically excluded.
try {
const mitmAliases = await getMitmAlias("antigravity");
const mitmAliases = await getMitmAlias(provider);
if (mitmAliases && typeof mitmAliases === "object") {
const aliases = mitmAliases as Record<string, unknown>;
const raw = aliases[stripped];
// Only honor string aliases; corrupted/non-string DB values fall through
// to the static alias resolution below (never return undefined here).
if (typeof raw === "string" && raw) {
// Strip the "antigravity/" prefix if present; use the raw model ID otherwise.
const PREFIX = "antigravity/";
clean = raw.startsWith(PREFIX) ? raw.slice(PREFIX.length) : raw;
// Strip the provider prefix if present; use the raw model ID otherwise.
const prefix = `${provider}/`;
clean = raw.startsWith(prefix) ? raw.slice(prefix.length) : raw;
}
}
} catch {
Expand All @@ -315,11 +313,20 @@ function applyAntigravityGenerationDefaults(
? (request.generationConfig as Record<string, unknown>)
: {};

if (generationConfig.topK === undefined) {
generationConfig.topK = 40;
}
if (generationConfig.topP === undefined) {
generationConfig.topP = 1.0;
const isGemini38 =
typeof modelId === "string" && modelId.toLowerCase().includes("gemini-3.8-flash");

if (isGemini38) {
delete generationConfig.topK;
delete generationConfig.topP;
delete generationConfig.temperature;
} else {
if (generationConfig.topK === undefined) {
generationConfig.topK = 40;
}
if (generationConfig.topP === undefined) {
generationConfig.topP = 1.0;
}
}

const thinkingConfig =
Expand All @@ -329,6 +336,7 @@ function applyAntigravityGenerationDefaults(
const thinkingBudget = Number(thinkingConfig?.thinkingBudget);
const maxOutputTokens = Number(generationConfig.maxOutputTokens);
if (
!isGemini38 &&
Number.isFinite(thinkingBudget) &&
thinkingBudget > 0 &&
(!Number.isFinite(maxOutputTokens) || maxOutputTokens <= thinkingBudget)
Expand Down Expand Up @@ -379,15 +387,20 @@ const COMPETITIVE_AGENT_PROMPT_PATTERNS: RegExp[] = [
*/
export function stripCompetitiveAgentPrompts(systemInstruction: unknown): unknown {
const record = asRecord(systemInstruction);
const parts = Array.isArray(record?.parts) ? (record.parts as Array<Record<string, unknown>>) : [];
const parts = Array.isArray(record?.parts)
? (record.parts as Array<Record<string, unknown>>)
: [];
if (parts.length === 0) return systemInstruction;

let changed = false;
const newParts = parts.map((part) => {
if (typeof part.text !== "string" || part.text.length === 0) return part;
let text = part.text;
for (const pattern of COMPETITIVE_AGENT_PROMPT_PATTERNS) {
const stripped = text.replace(pattern, "").replace(/\n{3,}/g, "\n\n").trimStart();
const stripped = text
.replace(pattern, "")
.replace(/\n{3,}/g, "\n\n")
.trimStart();
if (stripped !== text) {
changed = true;
text = stripped;
Expand Down Expand Up @@ -1352,7 +1365,7 @@ export class AntigravityExecutor extends BaseExecutor {
accountId,
urlIndex,
retryAttemptsByUrl,
fallbackCount,
fallbackCount: _fallbackCount,
} = ctx;

const { response, finalHeaders } = await sendAntigravityRequest(
Expand Down
40 changes: 33 additions & 7 deletions open-sse/executors/antigravity/executeAttempt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { mergeAbortSignals, type ExecutorLog } from "../base.ts";
import { applyFingerprint, isCliCompatEnabled } from "../../config/cliFingerprints.ts";
import { buildAntigravityUpstreamError } from "../antigravityUpstreamError.ts";
import { maybeTriggerReactiveModelSync } from "@/lib/providerModels/reactiveModelSync.ts";
import { awaitReactiveModelSync } from "@/lib/providerModels/reactiveModelSync.ts";
import {
HTTP_STATUS,
STREAM_READINESS_TIMEOUT_MS,
Expand All @@ -21,12 +21,13 @@ import {
removeHeaderCaseInsensitive,
} from "../../services/antigravityClientProfile.ts";
import * as prl from "../../utils/providerRequestLogging.ts";
import { generateAntigravityRequestId } from "../../services/antigravityIdentity.ts";
import {
createCreditsExtractionTransform as createCreditsExtractionTransformImpl,
buildSsePassthroughResult,
type SsePassthroughResult,
} from "./streamingPassthrough.ts";
import type { AntigravityCredentials } from "../antigravity.ts";
import { cleanModelName, type AntigravityCredentials } from "../antigravity.ts";

const LONG_RETRY_THRESHOLD_MS = 60_000;
const CREDITS_EXHAUSTED_TTL_MS = 5 * 60 * 60 * 1000; // 5 hours
Expand Down Expand Up @@ -387,12 +388,37 @@ export async function sendAntigravityRequest(
if (response.status === HTTP_STATUS.NOT_FOUND) {
// The backend may have shipped/renamed models the synced catalog does not
// know yet (pinned-catalog staleness). Kick a discovery sync for this
// connection so the fresh list lands in the synced catalog and the next
// request can resolve. Cooldown + in-flight dedup live in the trigger.
// credentials.connectionId is optional (base.ts): no connection row means
// there is no synced catalog to refresh, so skip rather than pass undefined.
// connection so the fresh list lands in the synced catalog. If sync succeeds,
// re-resolve the model with the updated catalog, re-serialize the request envelope,
// and transparently retry once so the first request for a freshly shipped model
// or alias succeeds on the first try.
if (credentials.connectionId) {
maybeTriggerReactiveModelSync(provider, credentials.connectionId);
const synced = await awaitReactiveModelSync(provider, credentials.connectionId);
if (synced) {
const reResolvedModel = await cleanModelName(model, undefined, provider);
const retryBody: Record<string, unknown> = {
...transformedBody,
model: reResolvedModel,
requestId: generateAntigravityRequestId(),
};
const reSerialized = serializeAntigravityRequest(provider, headers, retryBody);
const retryHeaders = reSerialized.headers;
applyAntigravityClientProfileHeaders(retryHeaders, credentials, retryBody);

log.info(
"RETRY",
`[Antigravity] Discovery sync succeeded after 404 for ${model} (re-resolved to ${reResolvedModel}), retrying request with rebuilt envelope`
);
await prl.captureCurrentProviderBody(url, retryHeaders, reSerialized.bodyString, log);
response = await fetchAntigravityWithReadinessTimeout(url, {
method: "POST",
headers: retryHeaders,
body: getChunkedOrFixedBody(reSerialized.bodyString, stream),
...(stream ? { duplex: "half" } : {}),
signal,
});
finalHeaders = retryHeaders;
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions open-sse/services/antigravityQuotaFamily.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ export function selectAntigravityQuotaWindowNames(

const familyAggregates =
requestedFamily === "gemini"
? ["gemini_weekly"]
? ["gemini_session", "gemini_weekly"]
: requestedFamily === "claude"
? ["claude_gpt_weekly"]
? ["claude_gpt_session", "claude_gpt_weekly"]
: [];

const exactWindows = quotaNames.filter((windowName) => {
Expand Down
Loading
Loading