fix(cpa): isolate credential-pool failures (#8308)

* fix(cpa): isolate credential pool failures

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(cpa): forward transport through the chatCore key-health wrapper

The local recordKeyHealthStatus wrapper in handleChatCore only declared
(status, creds), so the transport argument added for CPA credential-pool
isolation was silently dropped at the call site (TS2554 "Expected 2
arguments, but got 3" once chatCore.ts is typechecked with tsc directly —
this file is not in tsconfig.typecheck-core.json's file list, so `npm run
typecheck:core` did not surface it). The CPA isolation guard in
keyHealth.ts never received `transport`, so it never fired.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
Ravi Tharuma
2026-07-24 14:35:43 +02:00
committed by GitHub
parent cbe49f6929
commit 1f7ec2c321
5 changed files with 30 additions and 6 deletions

View File

@@ -428,7 +428,7 @@ export class CliproxyapiExecutor extends BaseExecutor {
input.log?.warn?.("CPA", `CLIProxyAPI rate limited: ${response.status}`);
}
return { response, url, headers, transformedBody };
return { response, url, headers, transformedBody, transport: "cliproxyapi" as const };
}
/**

View File

@@ -549,8 +549,9 @@ export async function handleChatCore({
// and delegate so the existing call sites stay byte-identical.
const recordKeyHealthStatus = (
status: number,
creds: Record<string, unknown> | null | undefined
): void => recordKeyHealthStatusFor(status, creds, log);
creds: Record<string, unknown> | null | undefined,
transport?: string
): void => recordKeyHealthStatusFor(status, creds, log, transport);
const persistCodexQuotaState = async (headers: Record<string, string> | null, status = 0) => {
const currentConnectionId = getCurrentConnectionId();
@@ -2988,7 +2989,11 @@ export async function handleChatCore({
rawResult._executionCredentials?.connectionId &&
rawResult._executionCredentials?.apiKey
) {
recordKeyHealthStatus(status, rawResult._executionCredentials);
recordKeyHealthStatus(
status,
rawResult._executionCredentials,
rawResult.transport
);
}
releaseRawResultAccountSemaphore =
typeof rawResult._accountSemaphoreRelease === "function"

View File

@@ -31,8 +31,13 @@ type KeyHealthLog = {
export function recordKeyHealthStatus(
status: number,
creds: Record<string, unknown> | null | undefined,
log?: KeyHealthLog
log?: KeyHealthLog,
transport?: string
): void {
// CLIProxyAPI owns a shared external credential pool. Its auth failures cannot be
// attributed to the native OmniRoute connection selected before proxy dispatch.
if (transport === "cliproxyapi") return;
const connId = creds?.connectionId as string | undefined;
if (!connId) return;

View File

@@ -106,8 +106,15 @@ export function normalizeExecutorResult(
url?: string;
headers?: Record<string, string>;
transformedBody?: unknown;
transport?: string;
}
): { response: Response; url: string; headers: Record<string, string>; transformedBody: unknown } {
): {
response: Response;
url: string;
headers: Record<string, string>;
transformedBody: unknown;
transport?: string;
} {
if (result instanceof Response) {
return { response: result, url: "", headers: {}, transformedBody: null };
}
@@ -116,6 +123,7 @@ export function normalizeExecutorResult(
url: result.url || "",
headers: result.headers || {},
transformedBody: result.transformedBody ?? null,
transport: result.transport,
};
}

View File

@@ -68,3 +68,9 @@ test("non-401 / non-2xx status does not touch key health", () => {
recordKeyHealthStatus(500, creds(conn), noopLog);
assert.equal(getAllKeyHealth()[`${conn}:primary`], undefined);
});
test("CPA pool failures do not poison the selected native connection key", () => {
const conn = "kh-cpa-pool-isolation";
recordKeyHealthStatus(401, creds(conn), noopLog, "cliproxyapi");
assert.equal(getAllKeyHealth()[`${conn}:primary`], undefined);
});