From 16a53bb3a9d39860c1d1981edd0d0f3bd6705c21 Mon Sep 17 00:00:00 2001 From: camjac251 Date: Wed, 15 Jul 2026 14:52:57 -0400 Subject: [PATCH] fix(gateway): preserve local oauth provider base url Runtime identity normalization previously ran before local OAuth provider detection, so display-name matching could no longer select the managed backend endpoint. Defer identity normalization until after detection and cover the ordering with a regression test. --- .../gateway/core-runtime/config-compiler.ts | 18 +++++------ .../provider-plugin-runtime-identity.test.mjs | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/core/src/gateway/core-runtime/config-compiler.ts b/packages/core/src/gateway/core-runtime/config-compiler.ts index 274eda8f..12bdcf63 100644 --- a/packages/core/src/gateway/core-runtime/config-compiler.ts +++ b/packages/core/src/gateway/core-runtime/config-compiler.ts @@ -40,20 +40,16 @@ export async function compileCoreGatewayConfig( ) : []; const pluginBillingConfig = isRecord(pluginCoreGatewayConfig.billing) ? pluginCoreGatewayConfig.billing : {}; - const configuredProviderPlugins = normalizeClaudeCodeOauthProviderPlugins( - normalizeCoreProviderPluginNames( - [ - ...(config.providerPlugins ?? []).filter(providerPluginEnabled), - ...pluginService.getCoreProviderPlugins().filter(providerPluginEnabled) - ], - config.Providers - ) - ); - const providerPlugins = await withKimiOauthRuntimeDefaults( + const configuredProviderPlugins = normalizeClaudeCodeOauthProviderPlugins([ + ...(config.providerPlugins ?? []).filter(providerPluginEnabled), + ...pluginService.getCoreProviderPlugins().filter(providerPluginEnabled) + ]); + const providerPluginsWithRuntimeDefaults = await withKimiOauthRuntimeDefaults( await withGrokOauthRuntimeDefaults(withCodexOauthRuntimeDefaults(configuredProviderPlugins)) ); + const codexOauthProviderNames = codexOauthLocalProviderNames(providerPluginsWithRuntimeDefaults); + const providerPlugins = normalizeCoreProviderPluginNames(providerPluginsWithRuntimeDefaults, config.Providers); const providerPluginsWithCapabilityAliases = withProviderCapabilityPluginAliases(providerPlugins, config.Providers); - const codexOauthProviderNames = codexOauthLocalProviderNames(providerPlugins); const virtualModelProfiles = coreGatewayVirtualModelProfiles(config); const coreEndpoint = endpoint(config.gateway.coreHost, config.gateway.corePort); const proxyPreloadFile = upstreamProxyUrl ? writeGatewayProxyPreloadFile(config, upstreamProxyUrl) : undefined; diff --git a/packages/core/test/unit/agents/provider-plugin-runtime-identity.test.mjs b/packages/core/test/unit/agents/provider-plugin-runtime-identity.test.mjs index 00c683f7..279f2aec 100644 --- a/packages/core/test/unit/agents/provider-plugin-runtime-identity.test.mjs +++ b/packages/core/test/unit/agents/provider-plugin-runtime-identity.test.mjs @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; +import { codexDefaultBaseUrl } from "@ccr/core/agents/local-providers/service.ts"; import { createDefaultAppConfig } from "@ccr/core/config/default-config.ts"; import { compileCoreGatewayConfig } from "@ccr/core/gateway/core-runtime/config-compiler.ts"; import { prepareGatewayUpstreamAttemptForTest } from "@ccr/core/gateway/upstream/executor.ts"; @@ -62,6 +63,36 @@ test("provider plugins use compiled runtime and capability identities", async () assert.equal(unscopedPlugin, unchangedPlugin); }); +test("Codex OAuth plugins retain the default base URL after runtime identity normalization", async () => { + const config = createDefaultAppConfig({ generatedConfigFile: "/tmp/ccr-codex-oauth-runtime-identity.json" }); + config.providerPlugins = [ + { + codexOauth: {}, + key: "ccr-local-agent-codex-api-codex-oauth", + providerName: "Codex API" + } + ]; + config.Providers = [ + { + api_base_url: "https://configured.example.test/v1", + id: "codex-api", + models: ["gpt-5-codex"], + name: "Codex API", + type: "openai_responses" + } + ]; + + const compiled = await compileCoreGatewayConfig( + config, + "raw-trace-token", + "billing-usage-token", + "core-auth-token" + ); + + assert.equal(compiled.providerPlugins[0].providerName, "codex-api"); + assert.equal(compiled.providers[0].baseurl, codexDefaultBaseUrl); +}); + test("credential-free fallback headers use the provider runtime identity", () => { const provider = { api_base_url: "https://api.example.test",