Skip to content
OmniRoute source

Model Exposure Allow/Deny List

Opt-in, off by default (modelVisibilityAllowlist/modelVisibilityDenylist both empty leaves the /v1/models catalog AND every auto/* candidate pool byte-identical). A sibling of hidePaidModels/hideAutoCombos (src/lib/db/settings.ts) for operators who want a curated model list for reasons that have nothing to do with cost.

hidePaidModels answers “is this model free?” and hideAutoCombos answers “should auto/* virtual ids be advertised at all?” — neither lets an operator curate an arbitrary subset of models (e.g. presenting exactly the models a given Claude Code / OpenCode client should see, independent of pricing). #11481 adds that as two independent, opt-in string-array settings.

Key Type Default Meaning
modelVisibilityDenylist string[] [] Entries matching a candidate hide it from the catalog/candidate pool.
modelVisibilityAllowlist string[] [] When non-empty, ONLY entries matching a candidate stay exposed.

Both accept up to 500 entries of up to 200 characters each (Zod-validated in src/shared/validation/settingsSchemas.ts). An entry is either:

  • an exact catalog id — "gpt-4o" (bare model id) or "openai/gpt-4o" (provider-prefixed), or
  • a glob pattern using */? — e.g. "openai/gpt-4*" or "anthropic/*" — resolved via the same shared globToRegex() matcher (src/shared/utils/globPattern.ts) already used by ModelRoutingSection’s per-model combo mappings and freeModels.ts::matchesOnlyPaidModels.

Precedence: the denylist is checked first (a denied entry is always hidden, even if it also matches the allowlist); when the allowlist is non-empty, only entries it matches survive.

The lesson from #6512 (a hidePaidModels-only catalog filter still let auto/* route to a paid model, since the combo candidate pool was built independently) applies identically here. The matching predicate isModelExposureAllowed() (src/shared/utils/modelExposureList.ts) is called from BOTH:

  • src/app/api/v1/models/catalog.ts — the /v1/models listing itself, at the same 5 per-source chokepoints shouldHidePaid() already gates (static PROVIDER_MODELS, synced provider rows, custom rows, alias-backed rows, managed-fallback rows).
  • open-sse/services/autoCombo/modelExposureFilter.ts::filterModelExposureCandidates() — called from virtualFactory.ts::buildPreparedPool, immediately after the equivalent filterPaidOnlyCandidates() call, so a denied model can never be selected into an auto/* candidate pool either.

Mirrors hideAutoCombos’s existing behaviour: a model id sent explicitly (not via auto/*, and not discovered through the catalog listing) is never blocked at dispatch — only advertisement/candidate-pool membership is filtered. This is independent of hidePaidModels; an operator may want a curated set for reasons that have nothing to do with cost, so both settings compose as independent AND-ed filters, same as the existing multi-flag composition in catalog.ts.

Settings export (GET /api/settings/export-json) includes both arrays verbatim, like any other settings field — unlike hidePaidModels’s combo-step export filter, there is no re-hydration risk here: a denied id embedded in an exported combo step is the operator’s own explicit routing choice, not something the export boundary needs to strip.


OmniRoute source repository (a58000c7685f)

HagiCode

HagiCode is an agentic coding workspace: structured workflows, multi-agent execution, and Hero Dungeon views turn ideas into shipped software.

Turn ideas into polished, usable software with a smarter, faster, and more enjoyable agentic coding workflow.

HagiCode light theme main interface screenshot
  • SmartStructured workflows turn intent into an executable path from idea to shipped change.
  • EfficientMulti-agent workflows keep research, implementation, and review moving in parallel.
  • FunHero Dungeon interfaces make long coding sessions visual, collaborative, and rewarding.
Visit HagiCode