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
8 changes: 8 additions & 0 deletions open-sse/services/antigravity429Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ const FULL_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24 hours
export function classify429(errorMessage: string): Category {
const lower = (errorMessage || "").toLowerCase();

// Cloud Code may report an exhausted-capacity message with a zero reset
// window for a burst/RPM throttle. The explicit zero reset is stronger
// evidence than the generic wording, so retry briefly instead of applying
// the durable quota cooldown.
if (/\breset\s+(?:after|in)\s+0s\b/.test(lower)) {
return "rate_limited";
}

// Check for quota exhaustion first (most specific)
for (const kw of QUOTA_EXHAUSTED_KEYWORDS) {
if (lower.includes(kw)) return "quota_exhausted";
Expand Down
10 changes: 10 additions & 0 deletions tests/unit/antigravity-429-quota-cooldown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ test("classify429: standard Gemini rate limit 'resource has been exhausted' -> r
);
});

test("classify429: exhausted capacity with reset after 0s is rate_limited", () => {
const message = "You have exhausted your capacity on this model. Your quota will reset after 0s.";
const category = classify429(message);
assert.equal(category, "rate_limited");

const decision = decide429(category, 2_000);
assert.equal(decision.kind, "soft_retry");
assert.equal(decision.retryAfterMs, 2_000);
});

// ── DB persistence (the missing wire — Bug #2) ───────────────────────────────

test("markConnectionQuotaExhausted persists 24h cooldown; isConnectionRateLimited returns true", async () => {
Expand Down
Loading