Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions docs-site/src/content/docs/reference/configuration/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,16 @@ Per-request route-decision traces are recorded when a policy profile executes.
A combo remains directly routable even when it cannot be listed. `ocx sync`, `/v1/models`, and the
Codex picker list it only when every target exposes capabilities that can be intersected:

- a positive `contextWindow`, from live metadata, registry hints, or provider
`modelContextWindows` / `contextWindow`; and
- a positive `contextWindow`, from live metadata, registry hints, provider
`modelContextWindows` / `contextWindow`, a known positive `maxInputTokens` on the member row,
or — when the provider is known and enabled but every source still omits a window — a
conservative 128,000-token fallback (clamped by `providerContextCaps` when set); and
Comment on lines +185 to +188

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep translated routing docs aligned

This updates only the English and zh-cn routing references with the new combo catalog fallback, but the ru, ja, and ko versions of the same section still say combos are omitted when context metadata is absent (for example docs-site/src/content/docs/ru/reference/configuration/routing.md:93-100, .../ja/...:82-86, and .../ko/...:81-84). That leaves non-English users with the old eligibility rule and contradicts this new documented behavior, so the remaining locale pages should be updated or made non-contradictory.

AGENTS.md reference: docs-site/AGENTS.md:L7-L10

Useful? React with 👍 / 👎.

- a non-empty `inputModalities` intersection, treating an omitted member value as `["text"]`.

A bare relay id with no context metadata or targets with disjoint modalities removes the combo from
the catalog. Sync emits a summary warning and the dashboard marks it **Needs attention**. Add context
metadata, align modalities, or target models with discoverable compatible capabilities.
A target on a disabled provider (even with a complete discovery row), on an unknown provider with
no discovery row, or targets with disjoint modalities, removes the combo from the catalog. Sync
emits a summary warning and the dashboard marks it **Needs attention**. Add context metadata,
align modalities, or target models with discoverable compatible capabilities.

## Request history and routing analytics

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ selector 校验、冲突规则和隐私说明见[提供方配置](/reference/con

即使某个 combo 不能被列出,它仍然可以直接路由。只有当所有目标都暴露出可以交集的能力时,`ocx sync`、`/v1/models` 和 Codex 选择器才会列出它:

- 一个正的 `contextWindow`,来源可以是实时元数据、注册表提示,或提供方的
`modelContextWindows` / `contextWindow`;以及
- 一个正的 `contextWindow`,来源可以是实时元数据、注册表提示、提供方的
`modelContextWindows` / `contextWindow`、成员行上已知的正 `maxInputTokens`,或者——当提供方已知且启用但所有来源仍未给出窗口时——
保守的 128,000 token 回退(若配置了 `providerContextCaps` 则会按上限夹紧);以及
- 非空的 `inputModalities` 交集,其中省略的成员值按 `["text"]` 处理。

如果是一个没有上下文元数据的裸 relay id,或者目标之间的模态互不相交,combo 就会从
目标位于已禁用提供方(即使有完整 discovery 行)、未知且无 discovery 行的提供方,或目标之间的模态互不相交时,combo 会从
目录中移除。同步时会输出一条汇总警告,仪表板会将其标记为 **Needs attention**。
补充上下文元数据、对齐模态,或者把目标模型切换为可发现且兼容的能力。

Expand Down
2 changes: 1 addition & 1 deletion src/codex/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export type { CatalogModel, MultiAgentMode } from "./catalog/parsing";
export { NATIVE_OPENAI_MODELS, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi } from "./catalog/metadata";
export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled";
export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort";
export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithJawcodeMetadata } from "./catalog/provider-fetch";
export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithJawcodeMetadata, resolveComboCatalogMember } from "./catalog/provider-fetch";
export { deriveComboCatalogModel, exactComboCatalogSlugs, getLastComboCatalogOmissions, resetOpenAiApiCatalogWarningStateForTests, uniqueCatalogModelsForPublicList, uniqueCatalogModelsForRawPublicList, buildComboCatalogOmission, comboCatalogOmissionReason, summarizeComboCatalogOmissions } from "./catalog/aggregation";
export type { ComboCatalogOmission, ComboCatalogOmissionReason } from "./catalog/aggregation";
export { MAX_SPAWN_AGENT_MODEL_OVERRIDES, effectiveSubagentRoster, buildCatalogEntries, resetCatalogRuntimeStateForTests, orderForSubagents, mergeCatalogEntriesForSync, syncCatalogModels, restoreCodexCatalog, invalidateCodexModelsCache } from "./catalog/sync";
Expand Down
11 changes: 8 additions & 3 deletions src/codex/catalog/aggregation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,14 @@ export function deriveComboCatalogModel(
const inputModalities = intersectStrings(
members.map(member => member.inputModalities ?? ["text"]),
);
const reasoningEfforts = intersectStrings(
members.map(member => member.reasoningEfforts ?? []),
);
// Unknown ladders (`undefined`) are wildcards for catalog derivation — same
// boundary as the GUI picker. An explicit empty ladder still constrains.
const advertisedLadders = members
.map(member => member.reasoningEfforts)
.filter((ladder): ladder is string[] => ladder !== undefined);
const reasoningEfforts = advertisedLadders.length === 0
? []
: intersectStrings(advertisedLadders);
const contextWindow = Math.min(...members.map(member => member.contextWindow!));
const maxInputTokens = Math.min(
...members.map(member => member.maxInputTokens ?? member.contextWindow!),
Expand Down
111 changes: 107 additions & 4 deletions src/codex/catalog/provider-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,104 @@ export function applyConfigHintsToCachedModels(name: string, prov: OcxProviderCo
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap));
}


/**
* Last-resort context window for combo member synthesis when discovery and
* provider config both omit one. Matches the catalog entry default in
* `normalizeRoutedCatalogEntry` so incomplete live rows still catalog.
*/
const COMBO_MEMBER_CONTEXT_FALLBACK = 128_000;

/**
* Resolve a combo target to a catalog member for derivation.
* Prefer discovery metadata; when the target is missing from the gather map or
* lacks a positive contextWindow, synthesize from the (registry-enriched)
* provider config so combos remain catalogued when targets are configured but
* discovery metadata is incomplete. Disabled providers stay unresolved.
* When hints still omit contextWindow, prefer known maxInputTokens, else
* COMBO_MEMBER_CONTEXT_FALLBACK so a live row without ctx does not drop the
* whole combo from the public catalog.
*/
export function resolveComboCatalogMember(
target: { provider: string; model: string },
memberByKey: ReadonlyMap<string, CatalogModel>,
providers: ReadonlyMap<string, OcxProviderConfig>,
contextCap?: number,
): CatalogModel | undefined {
const existing = memberByKey.get(targetKey(target));
const prov = providers.get(target.provider);
// Disabled providers never contribute members — even a complete discovery row
// is unusable for catalog derivation while the provider is off.
if (prov?.disabled === true) return undefined;

// Complete live/configured rows still honor providerContextCaps so a high
// discovery window cannot outrun an operator-configured cap.
if (
existing
&& typeof existing.contextWindow === "number"
&& existing.contextWindow > 0
) {
const capped = applyProviderContextCap(existing.contextWindow, contextCap);
if (capped === undefined || capped === existing.contextWindow) return existing;
Comment on lines +647 to +648

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject disabled native OpenAI combo members

When a combo target is the canonical openai provider and that provider is disabled, activeProviders omits the provider from enrichedByName, but the earlier native-slug injection still puts an openai/<slug> entry in memberByKey; this branch then returns that existing member because prov is undefined. In that configuration the combo is still derived and picker-visible even though this change is meant to omit targets on disabled providers, so pass disabled provider state into the resolver or skip native injection when providers.openai.disabled is true.

Useful? React with 👍 / 👎.

const maxInput = typeof existing.maxInputTokens === "number" && existing.maxInputTokens > 0
? Math.min(existing.maxInputTokens, capped)
: capped;
return {
...existing,
contextWindow: capped,
maxInputTokens: maxInput,
contextCap,
contextCapped: true as const,
};
}

const base: CatalogModel = existing ?? {
id: target.model,
provider: target.provider,
};
const hinted = prov
? applyProviderConfigHints(target.provider, prov, base, contextCap)
: base;
const hintedContext = typeof hinted.contextWindow === "number" && hinted.contextWindow > 0
? hinted.contextWindow
: undefined;
// Prefer a known positive maxInputTokens over inventing 128k when discovery
// advertised an input limit but no context window (common thin /models rows).
const knownMaxInput = typeof hinted.maxInputTokens === "number" && hinted.maxInputTokens > 0
? hinted.maxInputTokens
: (typeof base.maxInputTokens === "number" && base.maxInputTokens > 0
? base.maxInputTokens
: undefined);
const uncappedContext = hintedContext
?? knownMaxInput
?? (existing || prov ? COMBO_MEMBER_CONTEXT_FALLBACK : undefined);
Comment on lines +678 to +680

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not synthesize excluded combo targets

When a combo target is a row the catalog intentionally filtered out, such as opencode-go/hy3-preview from the exact compatibility exclusion or a media-generation id hidden by shouldExposeRoutedModel, memberByKey has no entry but prov is still present, so this fallback recreates the member with a 128k stub and the combo becomes picker-visible again. That bypasses the existing catalog exclusion path and lets users select combos whose target was already known to be uncallable or unsupported; check the same visibility/exclusion predicate before synthesizing a ghost member.

AGENTS.md reference: src/AGENTS.md:L15-L18

Useful? React with 👍 / 👎.

if (uncappedContext === undefined) return undefined;
const usedFallback = hintedContext === undefined;
const cappedContext = applyProviderContextCap(uncappedContext, contextCap);
const contextWindow = cappedContext ?? uncappedContext;
const fallbackCapped = usedFallback
&& contextCap !== undefined
&& cappedContext !== undefined
&& cappedContext !== uncappedContext;

const inputModalities = hinted.inputModalities ?? base.inputModalities ?? ["text"];
const reasoningEfforts = hinted.reasoningEfforts
?? (prov ? configuredReasoningEfforts(prov, target.model) : undefined)
?? base.reasoningEfforts;
const maxInputTokens = knownMaxInput !== undefined
? Math.min(knownMaxInput, contextWindow)
: contextWindow;

return {
...hinted,
inputModalities,
...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
contextWindow,
maxInputTokens,
...(fallbackCapped ? { contextCap, contextCapped: true as const } : {}),
};
}

export function isDatedVariantId(liveId: string, configuredId: string): boolean {
if (!liveId.startsWith(`${configuredId}-`)) return false;
return /^\d{8}$/.test(liveId.slice(configuredId.length + 1));
Expand Down Expand Up @@ -1273,21 +1371,26 @@ async function gatherRoutedModelsUncached(
if (!memberByKey.has(key)) memberByKey.set(key, synthetic);
}
}
// Enriched (registry-hydrated) provider clones — shared by combo member synthesis and
// custom-model vision-sidecar inheritance so both see the same merged registry view.
const enrichedByName = new Map(activeProviders.map(provider => [provider.name, provider.provider]));
for (const id of listComboIds(config)) {
const combo = getCombo(config, id);
if (!combo) continue;
const members = combo.targets
.map(target => memberByKey.get(targetKey(target)))
.map(target => resolveComboCatalogMember(
target,
memberByKey,
enrichedByName,
providerContextCap(config, target.provider),
Comment on lines +1381 to +1385

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use custom model metadata for combo members

When a combo target is only registered through config.customModels (for example added with a smaller --context-window) and the provider does not also expose that model in discovery or provider hints, this loop resolves the combo before custom rows are merged and the resolver falls back to the hard-coded 128k text member. That makes the combo picker-visible with capabilities that contradict the custom model row and can let Codex send requests above the configured custom context, so include custom model metadata in member resolution before applying the 128k fallback.

Useful? React with 👍 / 👎.

))
Comment on lines +1374 to +1386

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify the disabled-provider signal reaching resolveComboCatalogMember.
set -euo pipefail

echo "=== disabledNativeSlugs definition (does it consider providers.openai.disabled?) ==="
ast-grep run --pattern 'export function disabledNativeSlugs($$$) { $$$ }' --lang typescript src

echo
echo "=== all call sites of resolveComboCatalogMember (which map is passed?) ==="
rg -nP -C 8 '\bresolveComboCatalogMember\s*\(' --type=ts

echo
echo "=== construction of capture.providers (confirm the disabled filter) ==="
rg -nP -C 4 'disabled\s*!==\s*true' --type=ts -g 'src/**'

echo
echo "=== any integration test that disables a provider AND configures a combo ==="
rg -nP -C 10 'disabled:\s*true' --type=ts -g 'tests/**' | rg -n -C 10 'combos'

Repository: lidge-jun/opencodex

Length of output: 242


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== files containing resolveComboCatalogMember/disabledNativeSlugs ==="
rg -n 'resolveComboCatalogMember|disabledNativeSlugs|disabledNative|activeProviders|capture\.providers|disabledNativeSlugs' --type=ts -g 'src/**' -g 'tests/**'

echo
echo "=== provider-fetch relevant sections ==="
python3 - <<'PY'
from pathlib import Path
p = Path('src/codex/catalog/provider-fetch.ts')
lines = p.read_text().splitlines()
for start,end in [(1320,1405),(600,695)]:
    print(f"\n--- {p}:{start}-{end} ---")
    for i in range(start,end+1):
        if i <= len(lines):
            print(f"{i:5}: {lines[i-1]}")
PY

echo
echo "=== tests likely relevant ==="
rg -n -C 12 'disabled:\s*true|openai|combos|resolveComboCatalogMember|catalog' tests --type=ts -g 'tests/**' | head -n 300

Repository: lidge-jun/opencodex

Length of output: 354


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== locate provider-fetch file ==="
fd -a 'provider-fetch\.ts$' . || true

echo
echo "=== symbols across tracked TypeScript files ==="
git ls-files '*.ts' | xargs rg -n 'resolveComboCatalogMember|disabledNativeSlugs|activeProviders|capture\.providers|disabled\s*!==\s*true' || true

echo
echo "=== locate test files ==="
git ls-files 'tests/**/*.ts' | xargs rg -n -C 10 'disabled:\s*true|combos|catalog|resolveComboCatalogMember|enrichedByName|capture\.providers' || true

echo
echo "=== provider-fetch key ranges if present ==="
if [ -f src/codex/catalog/provider-fetch.ts ]; then
  python3 - <<'PY'
from pathlib import Path
lines = Path('src/codex/catalog/provider-fetch.ts').read_text().splitlines()
for range in [(620,690),(1340,1400),(400,425)]:
    print(f"\n--- ranges {range} ---")
    for i in range(*range):
        if i <= len(lines):
            print(f"{i:5}: {lines[i-1]}")
PY
fi

Repository: lidge-jun/opencodex

Length of output: 17252


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== provider-fetch key ranges ==="
python3 - <<'PY'
from pathlib import Path
p = Path('src/codex/catalog/provider-fetch.ts')
lines = p.read_text().splitlines()
ranges = [(620,690),(1280,1415),(395,425)]
for start,end in ranges:
    print(f"\n--- {p}:{start}-{end} ---")
    for i in range(start, min(end, len(lines)) + 1):
        print(f"{i:5}: {lines[i-1]}")
PY

echo
echo "=== metadata definitions ==="
python3 - <<'PY'
from pathlib import Path
p = Path('src/codex/catalog/metadata.ts')
lines = p.read_text().splitlines()
ranges = [(90,170)]
for start,end in ranges:
    print(f"\n--- {p}:{start}-{end} ---")
    for i in range(start, min(end, len(lines)) + 1):
        print(f"{i:5}: {lines[i-1]}")
PY

echo
echo "=== unit tests around resolveComboCatalogMember ==="
python3 - <<'PY'
from pathlib import Path
p = Path('tests/codex-catalog.test.ts')
lines = p.read_text().splitlines()
ranges = [(740,835)]
for start,end in ranges:
    print(f"\n--- {p}:{start}-{end} ---")
    for i in range(start, min(end, len(lines)) + 1):
        print(f"{i:5}: {lines[i-1]}")
PY

echo
echo "=== native-model-toggle tests ==="
python3 - <<'PY'
from pathlib import Path
p = Path('tests/native-model-toggle.test.ts')
lines = p.read_text().splitlines()
for i, line in enumerate(lines, 1):
    if 'disabledNativeSlugs' in line:
        print(f"{i}: {line}")
PY
cat -n tests/native-model-toggle.test.ts | sed -n '1,90p'

echo
echo "=== exact resolveComboCatalogMember call sites ==="
rg -n -C 12 '\bresolveComboCatalogMember\s*\(' --type=ts -g 'src/codex/catalog/provider-fetch.ts' || true

echo
echo "=== exact activeProviders usage around catalog build ==="
rg -n -C 8 '\bactiveProviders\b' --type=ts -g 'src/codex/catalog/provider-fetch.ts' || true

echo
echo "=== deterministic behavior probe from source extraction ==="
python3 - <<'PY'
import re
from pathlib import Path

src = Path('src/codex/catalog/provider-fetch.ts')
txt = src.read_text()

m = re.search(r'buildCatalogEntries[\s\S]*?const enrichedByName = new Map\([\s\S]*?\);\s*for \(const id of listComboIds\([\s\S]*?resolveComboCatalogMember\([\s\S]*?\)\)\s*\]', txt)
print("buildCatalogEntries block extracted:", bool(m))
if m:
    code = m.group(0)
    print("includes activeProviders.map provider.disabled filter:", ".filter(([, provider]) => provider.disabled !== true)" in code or "provider.disabled !== true" in code.split("const activeProviders = capture.providers;")[-1].split("const enrichedByName")[0])
    print("enrichedByName source:", [x.strip() for x in re.findall(r'const enrichedByName = new Map\([^;]+;\n', code)])
    print("comboProviderView construction mentioned:", "comboProviderView" in code or "Object.entries(config.providers)" in code)
    print("native injection call present:", "disabledNativeSlugs(config)" in code)

metadata = Path('src/codex/catalog/metadata.ts').read_text()
m2 = re.search(r'export function disabledNativeSlugs\([\s\S]*?\n\}', metadata)
print("\ndisabledNativeSlugs definition:")
print(m2.group(0) if m2 else "not found")
PY

Repository: lidge-jun/opencodex

Length of output: 27361


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== doc references ==="
rg -n -C 4 'disabled provider|disabled providers|catolog|catalog' docs-site/src/content/docs/reference/configuration/routing.md docs-site/src/content/docs/zh-cn/reference/configuration/routing.md 2>/dev/null || true

echo
echo "=== combo disable/route checks around providers.disabled ==="
rg -n -C 6 'target\.provider.*disabled|disabled.*target\.provider|combos.*disabled|disabled.*combos|disabledModels' src --type=ts -g 'src/combos/**' -g 'src/codex/**' -g 'src/router.ts' || true

echo
echo "=== unit tests: provider disabled with combos/native slugs ==="
python3 - <<'PY'
from pathlib import Path
text = Path('tests/codex-catalog.test.ts').read_text()
print('has disabled=true in provider map:', 'disabled: true' in text)
print('has openai provider in tests:', 'openai' in Path('tests/codex-catalog.test.ts').read_text())
print('has combos in tests:', 'combos' in text)
PY

Repository: lidge-jun/opencodex

Length of output: 25622


Pass a combo provider view that keeps disabled providers visible.

src/codex/catalog/provider-fetch.ts:414-416 filters disabled providers before capture.providers is created, and enrichedByName is built from that filtered list at line 1376. In resolveComboCatalogMember, prov = providers.get(target.provider) can only be true-disabled when the input map contains the provider entry, so a disabled provider is treated as missing rather than explicitly disabled. Native OpenAI rows are still synthesized from disabledNativeSlugs(config) alone, which only checks config.disabledModels, so openai/<native-slug> can satisfy the complete-row branch despite providers.openai.disabled = true. This contradicts the documented behavior: docs-site/src/content/docs/reference/configuration/routing.md:191-192 removes combos when a target is on a disabled provider even with a complete discovery row.

Fix the combo-member lookup by passing the full configured provider map, preferring enriched clones where present, without changing the custom-model inheritance that needs enrichedByName.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/codex/catalog/provider-fetch.ts` around lines 1374 - 1386, Update
combo-member synthesis around resolveComboCatalogMember to use a provider map
containing every configured provider, including disabled entries, while
preferring the corresponding enriched clone when available. Keep enrichedByName
unchanged for custom-model vision-sidecar inheritance, and ensure native OpenAI
disabled status still reflects the provider’s disabled flag so disabled-provider
combo targets are excluded.

Source: Path instructions

.filter((member): member is CatalogModel => member !== undefined);
const derived = deriveComboCatalogModel(id, combo, members);
if (derived) all.push(derived);
else warnUncataloguedComboOnce(id, combo, members, localOmissions);
}
replaceLastComboCatalogOmissions(localOmissions);
all.sort((a, b) => (a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider)));
// Enriched (registry-hydrated) provider clones, keyed by name — the same view used above so
// custom rows get the same noVisionModels / inputModalities treatment as discovered rows.
const enrichedByName = new Map(activeProviders.map(provider => [provider.name, provider.provider]));
// Provider-derived rows keyed by their Codex-facing slug: a custom override replaces the row
// with the same slug below, so that row's provider capability metadata is the inheritance source.
const replacedByRoutedSlug = new Map(all.map(model => [routedSlug(model.provider, model.id), model]));
Expand Down
Loading
Loading