From 22073c79b64df838595a11cd6b962809622b545c Mon Sep 17 00:00:00 2001 From: Probe Test Date: Thu, 23 Jul 2026 03:29:21 -0300 Subject: [PATCH] fix(resilience,translator): three release/v3.8.49 base-red regressions + eslint baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The release branch accumulated deterministic unit-test failures (fast-path red on every open PR). These are the ones with a clear, surgical root cause: 1. #6863 combo model-lockout — the #7940/#7980 "cap exactCooldownMs against maxCooldownMs" clamp was also clamping an AUTHORITATIVE parsed upstream quota reset (e.g. "Resets in 92h27m28s") down to maxCooldownMs, so an exhausted model was retried far too early. recordModelLockoutFailure now takes exactCooldownIsUpstreamReset — set by the combo callers when the exact cooldown is a real upstream reset — which exempts it from the cap. The #7980 computed until-midnight cap is unchanged (flag absent → still capped). 2. #5786 streaming claude←codex — stripInternalReasoningPlaceholder (#8081/#8162) unconditionally .trim()'d every value. On the per-delta streaming path this ate the meaningful edge spaces of each delta ("Hello, " + "world." + " Bye." glued to "Hello,world.Bye."). It now only collapses to "" when whitespace is all that remains after removing the placeholder, preserving real content verbatim. 3. SPAWN_CAPABLE_PREFIXES test — #7892 added /api/vnc-session (11th spawn-capable prefix, spawns Docker) but the client-safe guard test still expected 10 and did not list it. Aligned to 11 + added the entry to the checklist. 4. ESLint baseline — #8008/#8062 merged new test files with no-explicit-any without refreshing the frozen suppressions, so "No new ESLint warnings" went red for the whole branch. Regenerated the two affected entries (combo-routing-engine.test.ts 269→271, oauth-refresh-connection-dedup-8059.test.ts +1). Validated: the three failing tests now pass; the sibling guards they interact with stay green (#7980 exact-cooldown-cap 4/4, #8162 placeholder suites 17+12+41, account-fallback 77); typecheck:core clean; lint:json --max-warnings 0 exits 0. NOTE: the release branch has ~20 further real base-red failures (compression-engine catalog, handleChat fallback, provider candidate transparency, i18n, misc). Those are tracked separately, one focused PR per root-cause cluster; this PR is the first slice. --- config/quality/eslint-suppressions.json | 9 +++++++-- open-sse/services/accountFallback.ts | 15 +++++++++++---- open-sse/services/combo.ts | 7 +++++++ open-sse/utils/reasoningPlaceholder.ts | 12 +++++++++--- .../spawn-capable-prefixes-client-safe.test.ts | 3 ++- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/config/quality/eslint-suppressions.json b/config/quality/eslint-suppressions.json index 174969573b..21d269d137 100644 --- a/config/quality/eslint-suppressions.json +++ b/config/quality/eslint-suppressions.json @@ -1182,7 +1182,7 @@ }, "tests/unit/combo-routing-engine.test.ts": { "@typescript-eslint/no-explicit-any": { - "count": 269 + "count": 271 } }, "tests/unit/combo-same-provider-cascade.test.ts": { @@ -1750,6 +1750,11 @@ "count": 2 } }, + "tests/unit/oauth-refresh-connection-dedup-8059.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, "tests/unit/observability-fase04.test.ts": { "@typescript-eslint/no-explicit-any": { "count": 1 @@ -2445,4 +2450,4 @@ "count": 5 } } -} \ No newline at end of file +} diff --git a/open-sse/services/accountFallback.ts b/open-sse/services/accountFallback.ts index c24c836e45..e0f3f5a797 100644 --- a/open-sse/services/accountFallback.ts +++ b/open-sse/services/accountFallback.ts @@ -573,7 +573,11 @@ export function recordModelLockoutFailure( status: number, fallbackCooldownMs: number, profile: ProviderProfile | null = null, - options: { exactCooldownMs?: number | null; maxCooldownMs?: number } = {} + options: { + exactCooldownMs?: number | null; + maxCooldownMs?: number; + exactCooldownIsUpstreamReset?: boolean; + } = {} ) { ensureCleanupTimer(); const key = getModelLockKey(provider, connectionId, model, reason, status); @@ -596,15 +600,18 @@ export function recordModelLockoutFailure( const failureCount = withinWindow ? previous.failureCount + 1 : 1; const baseCooldownMs = getModelLockBaseCooldown(status, fallbackCooldownMs, profile); - // Cap both exponential backoff and exact cooldowns (e.g. daily-quota - // until-midnight) against maxCooldownMs so user-configured caps are honored. + // Cap both exponential backoff and computed exact cooldowns (e.g. daily-quota + // until-midnight, #7940/#7980) against maxCooldownMs so user-configured caps are + // honored — EXCEPT an authoritative parsed upstream reset (#6863, e.g. Antigravity + // "Resets in 92h27m28s"), which the upstream told us to wait and must be honored + // exactly, never clamped down to maxCooldownMs. const maxCooldownMs = typeof options.maxCooldownMs === "number" && options.maxCooldownMs > 0 ? options.maxCooldownMs : null; const cooldownMs = typeof options.exactCooldownMs === "number" && options.exactCooldownMs > 0 - ? maxCooldownMs !== null + ? maxCooldownMs !== null && !options.exactCooldownIsUpstreamReset ? Math.min(options.exactCooldownMs, maxCooldownMs) : options.exactCooldownMs : Math.min( diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index 1a4b88de30..f5e207b719 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -2370,6 +2370,10 @@ export async function handleComboChat({ // the short base cooldown / exponential backoff when present. exactCooldownMs: selectLockoutCooldownMs(lockoutHintMs, mlSettings), maxCooldownMs: mlSettings.maxCooldownMs, + // #6863: a parsed upstream quota reset is authoritative — the upstream + // told us exactly when it resets, so honor it in full instead of + // clamping to maxCooldownMs (which only bounds computed backoff). + exactCooldownIsUpstreamReset: lockoutHintMs > mlSettings.baseCooldownMs, } ); lockoutRecorded = true; @@ -2417,6 +2421,9 @@ export async function handleComboChat({ // #1308/#6863: honor a long upstream reset over base/exponential cooldown. exactCooldownMs: selectLockoutCooldownMs(lockoutHintMs, mlSettings), maxCooldownMs: mlSettings.maxCooldownMs, + // #6863: an authoritative parsed upstream reset must be honored in full, + // never clamped to maxCooldownMs (which only bounds computed backoff). + exactCooldownIsUpstreamReset: lockoutHintMs > mlSettings.baseCooldownMs, } ); } diff --git a/open-sse/utils/reasoningPlaceholder.ts b/open-sse/utils/reasoningPlaceholder.ts index 1531c9cb00..915af48c92 100644 --- a/open-sse/utils/reasoningPlaceholder.ts +++ b/open-sse/utils/reasoningPlaceholder.ts @@ -12,9 +12,15 @@ export function isInternalReasoningPlaceholder(value: unknown): boolean { /** * Strip the internal placeholder from user-visible content. Models sometimes * echo the sentinel through ordinary `message.content` / `delta.content` - * (#8081). Removes all occurrences and trims; returns "" when nothing - * meaningful remains so callers can skip emission entirely. + * (#8081). Removes all occurrences; returns "" when only whitespace remains so + * callers can skip emission entirely. + * + * IMPORTANT (#5786): this runs per-delta on the streaming path, where a delta's + * leading/trailing spaces are meaningful (e.g. "Hello, " + "world." + " Bye."). + * Only collapse to "" when the placeholder WAS the whole content — never trim + * real content, or streamed deltas glue together with their spaces eaten. */ export function stripInternalReasoningPlaceholder(value: string): string { - return value.replaceAll(NON_ANTHROPIC_THINKING_PLACEHOLDER, "").trim(); + const stripped = value.replaceAll(NON_ANTHROPIC_THINKING_PLACEHOLDER, ""); + return stripped.trim() === "" ? "" : stripped; } diff --git a/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts b/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts index 2522c659a1..8b1ee022eb 100644 --- a/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts +++ b/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts @@ -80,11 +80,12 @@ test("SPAWN_CAPABLE_PREFIXES is defined in the server-free constants leaf with t "/api/skills/collect/", "/api/headroom/start", "/api/headroom/stop", + "/api/vnc-session", ]) { assert.ok( SPAWN_CAPABLE_PREFIXES.includes(prefix), `SPAWN_CAPABLE_PREFIXES lost the spawn-capable prefix "${prefix}" during extraction` ); } - assert.equal(SPAWN_CAPABLE_PREFIXES.length, 10); + assert.equal(SPAWN_CAPABLE_PREFIXES.length, 11); });