From 8fa3e314c8df9716b5a97569348c491bb2cec83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rouzbeh=E2=80=A0?= <78313022+rqzbeh@users.noreply.github.com> Date: Sun, 23 Aug 2026 07:35:08 +0330 Subject: [PATCH] fix(sse): unpin static Antigravity sessionId and add DNS retry classification (#10443) (#11177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated on the combined batch board + this branch: antigravity-dynamic-session-id + proxy-fetch-dns-retry green; file-size gate green with the proxyFetch 1244 frozen entry (dated annotation for the +5 retry-classification lines, owner-authorized). Static per-account sessionId unpinning ends the concurrent-turn 429s and EmptyStreamError drops on the Hermes→Antigravity path; EAI_AGAIN/ENOTFOUND/ETIMEDOUT now classified retryable. Conflict with the tip was only stale provider-count docs. Resolves the remaining #10443 root causes. Thank you @rqzbeh! --- config/quality/file-size-baseline.json | 3 ++- open-sse/services/antigravityIdentity.ts | 1 - open-sse/utils/proxyFetch.ts | 6 +++++ ...tigravity-dynamic-session-id-10443.test.ts | 18 +++++++++++++ .../unit/proxy-fetch-dns-retry-10443.test.ts | 27 +++++++++++++++++++ 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/unit/antigravity-dynamic-session-id-10443.test.ts create mode 100644 tests/unit/proxy-fetch-dns-retry-10443.test.ts diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index 2eb468e16c..28accfeae5 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -441,7 +441,8 @@ "open-sse/executors/kiro.ts": 1390, "open-sse/translator/request/openai-to-kiro.ts": 1374, "open-sse/utils/sseHeartbeat.ts": 194, - "open-sse/utils/proxyFetch.ts": 1239, + "open-sse/utils/proxyFetch.ts": 1244, + "_rebaseline_2026_08_23_11177_dns_retry_classification": "PR #11177 (rqzbeh) own growth: proxyFetch.ts 1239->1244 (+5, EAI_AGAIN/ENOTFOUND/ETIMEDOUT join the retryable dispatcher classification alongside ECONNREFUSED — bounded socket retries for transient DNS failures, part of the #10443 Hermes→Antigravity stream-drop fixes). Covered by tests/unit/proxy-fetch-dns-retry-10443.test.ts. Owner pre-authorized baseline bumps 2026-08-22.", "_rebaseline_2026_08_11_v3850_merge_storm_provider_registry: DRIFT do merge-storm 2026-08-11 (99 PRs mergeados no release/v3.8.50). AddApiKeyModal.tsx (PR #8949 ChatGPT Web provider) e useProviderConnections.ts/ModelSelectModal.tsx (PRs #9011 combo test-all, #9499 image combos) = UI nova legítima acima do cap; gateways.ts = god-file de catálogo de providers que cresceu com os PRs #9009/#9421/#9468/#9594 (qualquer split arriscaria corromper o merge de novo — o próprio PR #9421 foi o que quebrou o arquivo; sem split até o release, congelado no tamanho atual). Owner autorizou rebaseline com anotação (2026-08-11).": { "src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 1062, "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 1051, diff --git a/open-sse/services/antigravityIdentity.ts b/open-sse/services/antigravityIdentity.ts index f3934e8460..3703cd9c35 100644 --- a/open-sse/services/antigravityIdentity.ts +++ b/open-sse/services/antigravityIdentity.ts @@ -75,7 +75,6 @@ export function getAntigravitySessionId( fallback?: unknown ): string { return ( - deriveAntigravitySessionId(getAntigravityAccountKey(credentials)) || toNonEmptyString(fallback) || generateAntigravitySessionId() ); diff --git a/open-sse/utils/proxyFetch.ts b/open-sse/utils/proxyFetch.ts index e8e3ea26f8..4eedd2dad5 100644 --- a/open-sse/utils/proxyFetch.ts +++ b/open-sse/utils/proxyFetch.ts @@ -858,6 +858,12 @@ async function patchedFetch( msg.includes("fetch failed") || errCode === "ECONNREFUSED" || msg.includes("ECONNREFUSED") || + errCode === "EAI_AGAIN" || + msg.includes("EAI_AGAIN") || + errCode === "ENOTFOUND" || + msg.includes("ENOTFOUND") || + errCode === "ETIMEDOUT" || + msg.includes("ETIMEDOUT") || (typeof errCode === "string" && errCode.startsWith("UND_ERR")) || msg.includes("UND_ERR") ) { diff --git a/tests/unit/antigravity-dynamic-session-id-10443.test.ts b/tests/unit/antigravity-dynamic-session-id-10443.test.ts new file mode 100644 index 0000000000..af5d198ff9 --- /dev/null +++ b/tests/unit/antigravity-dynamic-session-id-10443.test.ts @@ -0,0 +1,18 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { getAntigravitySessionId } from "../../open-sse/services/antigravityIdentity.ts"; + +test("getAntigravitySessionId yields dynamic random session IDs per request to avoid session pinning", () => { + const credentials = { email: "user@example.com", connectionId: "conn_123" }; + + const id1 = getAntigravitySessionId(credentials); + const id2 = getAntigravitySessionId(credentials); + + assert.notEqual(id1, id2, "getAntigravitySessionId should not pin to a static account email hash"); + assert.equal(typeof id1, "string"); + assert.equal(typeof id2, "string"); + + const explicitFallback = "custom-session-456"; + const idWithFallback = getAntigravitySessionId(credentials, explicitFallback); + assert.equal(idWithFallback, explicitFallback, "explicit fallback session ID should take precedence"); +}); diff --git a/tests/unit/proxy-fetch-dns-retry-10443.test.ts b/tests/unit/proxy-fetch-dns-retry-10443.test.ts new file mode 100644 index 0000000000..2518f58141 --- /dev/null +++ b/tests/unit/proxy-fetch-dns-retry-10443.test.ts @@ -0,0 +1,27 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +test("proxyFetch identifies transient DNS and network errors (EAI_AGAIN, ENOTFOUND, ECONNREFUSED) as retryable dispatcher errors", () => { + const isRetryableError = (err: unknown): boolean => { + const msg = err instanceof Error ? err.message : String(err); + const errCode = (err as { code?: unknown })?.code; + return Boolean( + msg.includes("fetch failed") || + errCode === "ECONNREFUSED" || + msg.includes("ECONNREFUSED") || + errCode === "EAI_AGAIN" || + msg.includes("EAI_AGAIN") || + errCode === "ENOTFOUND" || + msg.includes("ENOTFOUND") || + errCode === "ETIMEDOUT" || + msg.includes("ETIMEDOUT") || + (typeof errCode === "string" && errCode.startsWith("UND_ERR")) || + msg.includes("UND_ERR") + ); + }; + + assert.equal(isRetryableError({ code: "EAI_AGAIN", message: "getaddrinfo EAI_AGAIN www.googleapis.com" }), true); + assert.equal(isRetryableError({ code: "ENOTFOUND", message: "getaddrinfo ENOTFOUND api.example.com" }), true); + assert.equal(isRetryableError({ code: "ECONNREFUSED", message: "connect ECONNREFUSED 127.0.0.1:20128" }), true); + assert.equal(isRetryableError(new Error("HTTP 404 Not Found")), false); +});