From 2e533dc8c7864c4f2ab2ae0cfb89dbae1aa8f69c Mon Sep 17 00:00:00 2001 From: NoRain211 <170875756+NoRain211@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:48:34 -0700 Subject: [PATCH 1/5] fix(router): decode routed slugs via modelDisplayNames ids --- src/router.ts | 3 +++ tests/codex-integration/slug-codec.test.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/router.ts b/src/router.ts index d3939298e05..11901318556 100644 --- a/src/router.ts +++ b/src/router.ts @@ -189,6 +189,9 @@ export function knownModelIdsForProvider( .digest("hex"); }); for (const cached of cachedModels ?? []) ids.add(cached.id); + // Display-name keys are operator-declared native ids; they keep a routed slug decodable while + // the live roster is cold or its discovery timed out. + for (const id of Object.keys(prov.modelDisplayNames ?? {})) ids.add(id); for (const model of config?.customModels ?? []) { if (model.provider === provName && model.modelId) ids.add(model.modelId); } diff --git a/tests/codex-integration/slug-codec.test.ts b/tests/codex-integration/slug-codec.test.ts index 3a07001def8..4706502f5d0 100644 --- a/tests/codex-integration/slug-codec.test.ts +++ b/tests/codex-integration/slug-codec.test.ts @@ -301,6 +301,24 @@ describe("routeModel decode (proxy layer)", () => { expect(route.modelId).toBe("vendor/m-2"); }); + test("modelDisplayNames keys decode a provider-qualified slug with a cold live cache", () => { + const config: OcxConfig = { + port: 10100, + defaultProvider: "other", + providers: { + other: { + adapter: "openai-chat", + baseUrl: "https://example.com/v1", + apiKey: "k", + modelDisplayNames: { "vendor/m-3": "Vendor M3" }, + }, + }, + }; + const route = routeModel(config, "other/vendor-m-3"); + expect(route.providerName).toBe("other"); + expect(route.modelId).toBe("vendor/m-3"); + }); + test("knownModelIdsForProvider unions config, registry, and hint-map ids", () => { const ids = knownModelIdsForProvider("zenmux", zenmuxConfig().providers.zenmux!); expect(ids).toContain("moonshotai/kimi-k3-free"); From 719b4ffcdd7334bcf587fa7161d74ea5b04a3ebd Mon Sep 17 00:00:00 2001 From: NoRain211 <170875756+NoRain211@users.noreply.github.com> Date: Thu, 29 Oct 2026 19:12:12 -0700 Subject: [PATCH 2/5] fix(router): use display names only as a last-resort slug decode --- src/router.ts | 10 +++---- tests/codex-integration/slug-codec.test.ts | 35 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/router.ts b/src/router.ts index 11901318556..2936301b995 100644 --- a/src/router.ts +++ b/src/router.ts @@ -30,7 +30,7 @@ import { LEGACY_OPENAI_MULTI_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID, } from "./providers/openai-tiers"; -import { decodeRoutedModelIdOrThrow, encodeRoutedModelId } from "./providers/slug-codec"; +import { decodeRoutedModelId, decodeRoutedModelIdOrThrow, encodeRoutedModelId } from "./providers/slug-codec"; import { effectiveProviderAliasDecision, resolveModelAlias } from "./providers/default-aliases"; import { resolveBlockedModelRedirect } from "./lib/shadow-call"; import { workBuddyCatalogIdentity } from "./providers/workbuddy"; @@ -189,9 +189,6 @@ export function knownModelIdsForProvider( .digest("hex"); }); for (const cached of cachedModels ?? []) ids.add(cached.id); - // Display-name keys are operator-declared native ids; they keep a routed slug decodable while - // the live roster is cold or its discovery timed out. - for (const id of Object.keys(prov.modelDisplayNames ?? {})) ids.add(id); for (const model of config?.customModels ?? []) { if (model.provider === provName && model.modelId) ids.add(model.modelId); } @@ -586,7 +583,10 @@ function routeModelInternal( const decoded = decodeRoutedModelIdOrThrow(requestedModel, known); const nativeModel = known.includes(decoded) ? decoded - : resolveModelAlias(config, prov, known, requestedModel) ?? decoded; + : resolveModelAlias(config, prov, known, requestedModel) + // Last resort while the live roster is cold: display-name keys are operator-declared + // native ids, consulted only here so they never claim aliases or shadow known ids. + ?? decodeRoutedModelId(decoded, Object.keys(prov.modelDisplayNames ?? {})); return routeResult( config, provName, diff --git a/tests/codex-integration/slug-codec.test.ts b/tests/codex-integration/slug-codec.test.ts index 4706502f5d0..1c5033e9058 100644 --- a/tests/codex-integration/slug-codec.test.ts +++ b/tests/codex-integration/slug-codec.test.ts @@ -319,6 +319,41 @@ describe("routeModel decode (proxy layer)", () => { expect(route.modelId).toBe("vendor/m-3"); }); + test("a stale modelDisplayNames key does not shadow a configured id", () => { + const config: OcxConfig = { + port: 10100, + defaultProvider: "other", + providers: { + other: { + adapter: "openai-chat", + baseUrl: "https://example.com/v1", + apiKey: "k", + models: ["a-b"], + modelDisplayNames: { "a/b": "Stale A/B" }, + }, + }, + }; + expect(routeModel(config, "other/a-b").modelId).toBe("a-b"); + }); + + test("a stale modelDisplayNames key does not break an existing default alias", () => { + const config: OcxConfig = { + port: 10100, + defaultProvider: "other", + providers: { + other: { + adapter: "openai-chat", + baseUrl: "https://example.com/v1", + apiKey: "k", + defaultAliases: true, + models: ["vendor/claude-opus-5"], + modelDisplayNames: { "vendor/claude-opus-5-old": "Stale Opus" }, + }, + }, + }; + expect(routeModel(config, "other/opus").modelId).toBe("vendor/claude-opus-5"); + }); + test("knownModelIdsForProvider unions config, registry, and hint-map ids", () => { const ids = knownModelIdsForProvider("zenmux", zenmuxConfig().providers.zenmux!); expect(ids).toContain("moonshotai/kimi-k3-free"); From 8b6fa8def6f51a64cfdc57f43f9bcea136b3e315 Mon Sep 17 00:00:00 2001 From: NoRain211 <170875756+NoRain211@users.noreply.github.com> Date: Thu, 29 Oct 2026 20:47:55 -0700 Subject: [PATCH 3/5] fix(router): keep concrete policy and combo targets exact --- src/router.ts | 8 ++++--- tests/routing/policy-execution.test.ts | 29 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/router.ts b/src/router.ts index 2936301b995..7338f84c514 100644 --- a/src/router.ts +++ b/src/router.ts @@ -584,9 +584,11 @@ function routeModelInternal( const nativeModel = known.includes(decoded) ? decoded : resolveModelAlias(config, prov, known, requestedModel) - // Last resort while the live roster is cold: display-name keys are operator-declared - // native ids, consulted only here so they never claim aliases or shadow known ids. - ?? decodeRoutedModelId(decoded, Object.keys(prov.modelDisplayNames ?? {})); + // Last resort for direct requests while the live roster is cold: display-name keys are + // operator-declared native ids, consulted only here so they never claim aliases or shadow + // known ids. Concrete policy/combo/redirect targets (bypassCombos) keep the exact model + // their evaluator selected, so evidence and eligibility describe what is sent. + ?? (bypassCombos ? decoded : decodeRoutedModelId(decoded, Object.keys(prov.modelDisplayNames ?? {}))); return routeResult( config, provName, diff --git a/tests/routing/policy-execution.test.ts b/tests/routing/policy-execution.test.ts index 96b6405a5b5..3508c5a2594 100644 --- a/tests/routing/policy-execution.test.ts +++ b/tests/routing/policy-execution.test.ts @@ -134,6 +134,35 @@ describe("policy execution (RI-05)", () => { expect(route.modelId).toBe("m1"); }); + test("policy and combo targets keep their exact model when a display name could re-decode it", () => { + const base = baseConfig(); + const config = baseConfig({ + providers: { + ...base.providers, + other: { + adapter: "openai-chat", + baseUrl: "https://other.example/v1", + apiKey: "ko", + modelDisplayNames: { "a/b": "Display-only A/B" }, + }, + }, + combos: { exactcombo: { strategy: "failover", targets: [{ provider: "other", model: "a-b" }] } }, + routingProfiles: { + exact: { + candidates: [{ provider: "other", model: "a-b" }], + unknownEvidence: { capability: "penalize", health: "penalize", quota: "penalize", cost: "penalize" }, + }, + }, + }); + expect(routeModel(config, "other/a-b").modelId).toBe("a/b"); + const policy = routeModel(config, "policy/exact"); + expect(policy.modelId).toBe("a-b"); + expect(policy.routeDecision!.selected.model).toBe("a-b"); + const combo = routeModel(config, "combo/exactcombo"); + expect(combo.modelId).toBe("a-b"); + expect(combo.combo!.target.model).toBe("a-b"); + }); + test("existing explicit, combo, native and default routes are unchanged", () => { const config = baseConfig(); expect(routeModel(config, "a/m1")).toMatchObject({ routeKind: "explicit-provider", providerName: "a", modelId: "m1" }); From a33745a53c8c5bde42a23927cac76d6fad7b10e9 Mon Sep 17 00:00:00 2001 From: NoRain211 <170875756+NoRain211@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:00:57 -0700 Subject: [PATCH 4/5] fix(router): keep retained ids ahead of display-name decode --- src/router.ts | 8 +++++--- tests/codex-integration/slug-codec.test.ts | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/router.ts b/src/router.ts index 7338f84c514..68ae9ca013e 100644 --- a/src/router.ts +++ b/src/router.ts @@ -586,9 +586,11 @@ function routeModelInternal( : resolveModelAlias(config, prov, known, requestedModel) // Last resort for direct requests while the live roster is cold: display-name keys are // operator-declared native ids, consulted only here so they never claim aliases or shadow - // known ids. Concrete policy/combo/redirect targets (bypassCombos) keep the exact model - // their evaluator selected, so evidence and eligibility describe what is sent. - ?? (bypassCombos ? decoded : decodeRoutedModelId(decoded, Object.keys(prov.modelDisplayNames ?? {}))); + // known or retained ids. Concrete policy/combo/redirect targets (bypassCombos) keep the + // exact model their evaluator selected, so evidence and eligibility describe what is sent. + ?? (bypassCombos || prov.retainModels?.includes(decoded) + ? decoded + : decodeRoutedModelId(decoded, Object.keys(prov.modelDisplayNames ?? {}))); return routeResult( config, provName, diff --git a/tests/codex-integration/slug-codec.test.ts b/tests/codex-integration/slug-codec.test.ts index 1c5033e9058..a5e0ae79319 100644 --- a/tests/codex-integration/slug-codec.test.ts +++ b/tests/codex-integration/slug-codec.test.ts @@ -336,6 +336,23 @@ describe("routeModel decode (proxy layer)", () => { expect(routeModel(config, "other/a-b").modelId).toBe("a-b"); }); + test("a stale modelDisplayNames key does not shadow a retained id", () => { + const config: OcxConfig = { + port: 10100, + defaultProvider: "other", + providers: { + other: { + adapter: "openai-chat", + baseUrl: "https://example.com/v1", + apiKey: "k", + retainModels: ["a-b"], + modelDisplayNames: { "a/b": "Stale A/B" }, + }, + }, + }; + expect(routeModel(config, "other/a-b").modelId).toBe("a-b"); + }); + test("a stale modelDisplayNames key does not break an existing default alias", () => { const config: OcxConfig = { port: 10100, From a2cc0d9149db23eb264ac7e57498316c3834c077 Mon Sep 17 00:00:00 2001 From: JUN Date: Fri, 30 Oct 2026 20:10:41 +0900 Subject: [PATCH 5/5] docs(structure): record the modelDisplayNames slug-decode fallback --- structure/runtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure/runtime.md b/structure/runtime.md index e40e2fc0ed0..5621876406b 100644 --- a/structure/runtime.md +++ b/structure/runtime.md @@ -18,7 +18,7 @@ Release packaging, Docker, installed-artifact and service smoke jobs, desktop sh `src/router.ts` attaches one frozen `ResolvedModelPolicy` to every `RouteResult`. Fast observation, persistence and cost provenance follow the [response-tier authority contract](transports/responses.md#response-tier-observation-authority); outbound Fast policy is unchanged. Policy/combo route spreads retain that object. Every initial, fallback, and recovery route is recaptured for the request's original inbound protocol before route-dependent normalization, and all adapter rebuilds consume its recorded adapter. A translated Chat or Anthropic replay therefore cannot inherit a Responses-only default. Credential, account, quota, health, cooldown, and observed transport evidence remain late and cannot widen a captured static limit. -Virtual models are the sole model-identity transition: the ordinary and compact paths preserve the selected public id in diagnostics, rewrite `route.modelId` to the upstream wire id, and atomically replace `route.staticPolicy` before adapter or capability decisions continue. Model aliases are resolved before the route result is built, so their policy is already keyed by the native wire id. `src/router.ts` applies bare `blockedModelRedirects` keys after this resolution; ordinary targets substitute one upstream model id on the selected provider/account, including slash-valued targets, while only targets explicitly naming a different configured provider reroute. Qualified source keys take precedence only for those cross-provider targets. A cross-provider redirect sets `RouteResult.credentialDomainRewrite`, which Responses and Chat header scoping treat like combo/policy routes, so a caller credential for the source route is never forwarded. `src/cli/alias.ts` consumes the JSON output flag before choosing its default list action. `ocx alias --json` and both flag positions around an explicit action preserve the selected read or mutation. Live selector hints obey the [credential-scoped cache contract](catalog.md): a selection change cannot reuse the previous credential's roster to choose an alias target. Passive OAuth observation neither refreshes credentials nor repairs their storage. Cross-provider redirects require an enabled destination with a resolved key, usable stored OAuth account, or local/keyless auth (never source-caller forward auth), and share one visited set and five-edge budget across alias, provider, combo and policy resolution; every policy redirect must end at an eligible declared candidate, including same-provider substitutions and candidate fallback retries, and pinned account selectors reject provider changes. Decision traces name the final provider/model while policy candidates retain selection evidence and combos retain their route kind and redirect reason. +Virtual models are the sole model-identity transition: the ordinary and compact paths preserve the selected public id in diagnostics, rewrite `route.modelId` to the upstream wire id, and atomically replace `route.staticPolicy` before adapter or capability decisions continue. Model aliases are resolved before the route result is built, so their policy is already keyed by the native wire id. `src/router.ts` applies bare `blockedModelRedirects` keys after this resolution; ordinary targets substitute one upstream model id on the selected provider/account, including slash-valued targets, while only targets explicitly naming a different configured provider reroute. Qualified source keys take precedence only for those cross-provider targets. A cross-provider redirect sets `RouteResult.credentialDomainRewrite`, which Responses and Chat header scoping treat like combo/policy routes, so a caller credential for the source route is never forwarded. `src/cli/alias.ts` consumes the JSON output flag before choosing its default list action. `ocx alias --json` and both flag positions around an explicit action preserve the selected read or mutation. Live selector hints obey the [credential-scoped cache contract](catalog.md): a selection change cannot reuse the previous credential's roster to choose an alias target. Passive OAuth observation neither refreshes credentials nor repairs their storage. Cross-provider redirects require an enabled destination with a resolved key, usable stored OAuth account, or local/keyless auth (never source-caller forward auth), and share one visited set and five-edge budget across alias, provider, combo and policy resolution; every policy redirect must end at an eligible declared candidate, including same-provider substitutions and candidate fallback retries, and pinned account selectors reject provider changes. Decision traces name the final provider/model while policy candidates retain selection evidence and combos retain their route kind and redirect reason. An explicit provider-qualified slug decodes against `knownModelIdsForProvider` first, then model aliases. Only when both miss on a direct request does `src/router.ts` decode once more against the provider's `modelDisplayNames` keys, which are operator-declared native ids; this keeps a live-discovery provider such as Baseten routable while its roster cache is cold. The fallback never feeds `knownModelIdsForProvider`, so display-name keys claim no aliases and never shadow a configured or `retainModels` id, and concrete policy, combo and blocked-model redirect targets keep the exact model their evaluator selected (`tests/codex-integration/slug-codec.test.ts`, `tests/routing/policy-execution.test.ts`). Routed Meta Muse requests use the registry-owned [Muse effort and header contract](providers-and-adapters.md); `max` reaches the provider through the existing reasoning mapper.