fix(sse): pause failover dispatch after repeated transient upstream failures (#13615)

Behind the new `OPENCODE_TRANSIENT_FAILOVER_BACKOFF` flag (default off), after two consecutive transient upstream failures the opencode rotation pauses before each later account (1.5s, 3s, 6s, capped at 10s per request) instead of hammering the upstream.

Maintainer rework before merge (kept the idea, no default behavior change):
- The pause honors the client abort signal (no dispatch after a disconnect), the failed attempt's body is cancelled before sleeping, `transientRetryDelayMs` now uses its arguments, and the sleep is injectable so the tests run without real timers.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
This commit is contained in:
Dizzle
2026-09-16 01:58:12 +02:00
committed by GitHub
parent e325888d78
commit 13f44af6e5
9 changed files with 449 additions and 9 deletions

View File

@@ -255,6 +255,23 @@ export function isOpencodeUserBlockedRotationEnabled(): boolean {
}
}
/**
* OpenCode transient-failure failover pause (#13615). Opt-in: when off, failover to the next
* account stays immediate exactly as before.
* Fail closed: an unreadable flag store keeps the pre-flag behavior (disabled).
*/
export function isOpencodeTransientFailoverBackoffEnabled(): boolean {
try {
return isFeatureFlagEnabled("OPENCODE_TRANSIENT_FAILOVER_BACKOFF");
} catch (error) {
console.error(
"[featureFlags] Failed to resolve OPENCODE_TRANSIENT_FAILOVER_BACKOFF, defaulting to disabled:",
error instanceof Error ? error.message : error
);
return false;
}
}
export function isServerOwnedToolLoopEnabled(
reader: (key: string) => boolean = isFeatureFlagEnabled
): boolean {