From f20bab56057ac5fde30b4338a3b7d143d2a7e1ac Mon Sep 17 00:00:00 2001 From: Andre Date: Mon, 3 Aug 2026 07:27:43 -0700 Subject: [PATCH] fix(failover): rotate all GO keys before switching to Zen --- packages/core/src/integration.ts | 4 +++- packages/opencode/src/session/processor.ts | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index 5df2884f64b3..b4a02fadccb7 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -420,10 +420,12 @@ export const locationLayer = Layer.effect( return connection }), cooldown: Effect.fn("Integration.connection.cooldown")(function* (id, owner, retryAfterMs = 0) { - const selected = selection.get(`${id}:${owner}`) + const ownerKey = `${id}:${owner}` + const selected = selection.get(ownerKey) if (!selected) return console.log(`[credential-pool] ${id} owner=${owner}: penalizing ${selected.slice(0, 12)}... retryAfterMs=${retryAfterMs}`) pools.get(id)?.pool.penalize(selected, retryAfterMs, yield* Clock.currentTimeMillis) + selection.delete(ownerKey) }), release: Effect.fn("Integration.connection.release")(function* (id, owner) { const key = `${id}:${owner}` diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 262990de332e..6ad19dfbaae1 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -121,6 +121,7 @@ const layer = Layer.effect( let aborted = false let currentStreamInput: LLM.StreamInput | undefined let failoverAttempted = false + let retryKeyCount = 0 const switchProvider = Effect.fnUntraced(function* (error: SessionRetry.Err) { if (failoverAttempted) return false @@ -128,6 +129,19 @@ const layer = Layer.effect( return false if (!CredentialFailover.eligible(error)) return false if (ctx.model.providerID !== "opencode" && ctx.model.providerID !== "opencode-go") return false + + const allAuth = yield* auth.all().pipe(Effect.orDie) + const currentAuth = allAuth[ctx.model.providerID] + if (currentAuth && currentAuth.type === "api") { + const keys = currentAuth.metadata?.keys ? JSON.parse(currentAuth.metadata.keys) : [currentAuth.key] + if (Array.isArray(keys) && retryKeyCount < keys.length - 1) { + retryKeyCount++ + console.log(`[failover-v1] ${ctx.model.providerID} key ${retryKeyCount}/${keys.length} failed, trying next key before switching provider`) + return false + } + console.log(`[failover-v1] ${ctx.model.providerID} all ${keys.length} keys exhausted, switching provider`) + } + const destination = ctx.model.providerID === "opencode" ? "opencode-go" : "opencode" const all = (yield* providers.list()) as Record const destinationProvider = Object.values(all).find((item) => String(item.id) === destination)