From a11930ec4445fc2c9f4390129779107ca93f7c95 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 25 Aug 2026 19:34:49 -0300 Subject: [PATCH] fix(sse): honor per-step connection pins instead of rotating accounts on fallback A combo step pinned with an explicit `connectionId` (or a request pinned via `x-omniroute-connection`) is an operator instruction, not a hint. The generic account-fallback branch in handleSingleModelChat excluded the pinned connection after an upstream failure and re-selected a sibling account of the same provider, so a priority combo repeating one provider/model with two different fixed accounts ran both attempts under the FIRST step: the second step, with its own pin, never executed and per-step attribution (comboStepId / comboExecutionKey) was wrong. Gate the rotation on `!hasForcedConnection`, matching the antigravity stream-readiness, pre-response-timeout and account-semaphore branches that already let pinned steps fall through to combo orchestration. Cooldown recording via markAccountUnavailable is unchanged, and unpinned selection still excludes burned connections. Refs tests/integration/combo-routing-e2e.test.ts --- src/sse/handlers/chat.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sse/handlers/chat.ts b/src/sse/handlers/chat.ts index aadf7d29d3..4c4d589a0c 100644 --- a/src/sse/handlers/chat.ts +++ b/src/sse/handlers/chat.ts @@ -2294,7 +2294,14 @@ async function handleSingleModelChat( } ); - if (shouldFallback) { + // An explicit pin (combo step `connectionId` / `x-omniroute-connection`) is an + // operator instruction, not a suggestion: the account cooldown above is still + // recorded, but selection must NOT silently rotate to a sibling account of the + // same provider. Pinned steps fall through to combo orchestration, which moves + // to the next target — with ITS own pin. Same rule the antigravity + // stream-readiness / pre-response-timeout and account-semaphore paths above + // already apply. + if (shouldFallback && !hasForcedConnection) { if (Number.isFinite(cooldownMs) && cooldownMs > 0) { lastCooldownMs = cooldownMs; requestRetryLastCooldownMs = cooldownMs;