Summary
Kai should keep backend and provider registration explicit, but model availability should come from the backend or provider source of truth wherever a reliable source exists. New upstream models should become selectable without source edits, while Kai defaults remain curated and stable.
This is an epic. Each backend or provider surface should be handled in a focused child issue so the implementation can respect the different discovery mechanics instead of forcing one abstraction across incompatible sources.
Current state
Kai currently keeps several model surfaces in source: PROVIDER_MODELS, PROVIDER_DEFAULTS, CODEX_MODELS, OPEN_ENDED_PROVIDERS, and the per-role MODEL_REGISTRY. /models shows a curated keyboard when models_for_backend() returns a static dict. Open-ended surfaces fall back to free-text /model <id> input. OpenCode accepts free-text provider/model IDs and lets OpenCode resolve them at runtime. Goose accepts provider-native model IDs through GOOSE_MODEL and goose run --model.
The existing python -m kai.refresh_models helper queries some curated provider model endpoints and prints diffs against PROVIDER_MODELS. That remains useful as an operator audit tool, but it does not make /models current at runtime and does not address backend-specific catalogs such as Codex, OpenCode, or Goose.
Kai has static backend/provider support and a runtime user/default cascade, but the epic needs a derived active-provider inventory so discovery knows which provider surfaces are relevant for each user and installation.
Problem
When a provider or backend releases a new model, Kai may already be able to use it before it is visible or accepted in the model picker. For curated static surfaces, a source change is required before the user can select the model. For open-ended surfaces, direct /model <id> may work, but /models cannot help the user discover the model.
The maintenance burden differs by backend. Some surfaces use curated aliases, some use provider API model IDs, some use backend-owned CLI catalogs, some use full provider/model IDs, and some are intentionally open-ended. Treating all of these as one generic provider list is the failure mode: it hides the source of truth that actually governs each runtime path.
Provider discovery can also start from the wrong set. A provider can be supported by code, configured somewhere in the environment, or documented by an upstream backend without being active for a Kai user. Discovery should start from derived active provider inventory, not from every theoretically supported provider.
Design constraints
Provider and backend registration must remain explicit. Providers affect auth, environment variables, config validation, sudo preservation, documentation, runtime failure modes, and operator setup.
Discovered availability must not change curated defaults. Discovery can make a model selectable, but it must not silently change DEFAULT_MODEL, PROVIDER_DEFAULTS, CODEX_DEFAULT_MODEL, or per-role MODEL_REGISTRY choices.
Discovery must be scoped to the surface that owns model availability. Codex should use the Codex CLI catalog, not OpenAI /v1/models. Claude should follow Claude Code's alias and full-ID behavior, not assume the Anthropic API catalog is the same surface. OpenCode should use opencode models [provider] under the effective Kai user, while preserving free-text provider/model input. Goose should not scrape interactive goose configure; until Goose exposes a stable non-interactive model catalog command, Goose discovery should be provider-native and preserve Goose's bare model ID shape.
Discovery should operate over active provider inventory derived from effective user routing. Supported provider means Kai can use a provider in principle. Configured provider means setup appears present. Active provider means a user can actually reach that backend/provider pair through Kai after inheritance is applied.
Provider-specific follow-ups should be created only when that provider is an active Kai target or has a concrete source-of-truth decision to evaluate. A provider documented by an upstream backend is not automatically a Kai provider issue.
Multi-user installs must not share availability across identities when the underlying backend's catalog can differ by user auth, plan, provider config, or local installation. Codex, OpenCode, and Goose are clear examples: model availability can depend on the effective OS user, backend auth, provider credentials, installed CLI version, and provider-specific config, so a global cache is unsafe.
Fallbacks must be explicit. If discovery fails, Kai should continue with the previous static or free-text behavior where that behavior was already accepted, and the user-visible model surface should not collapse just because a refresh failed.
Child issues
Additional child issues should be created per active backend or provider as the source of truth is evaluated.
Acceptance criteria
- Each supported backend or provider surface has a documented decision: discovered dynamically, static curated list, structurally validated free-text, or explicitly unsupported for discovery.
/models uses discovered model lists where a reliable source exists and falls back clearly when discovery is unavailable.
- Manual
/model <id> remains available as an escape hatch for surfaces that can validate at runtime or are intentionally open-ended.
- Discovery caches include freshness metadata and are scoped correctly for user-specific or installation-specific catalogs.
- Startup and
/model validation preserve backend-specific rules, including Codex using the Codex CLI surface, OpenCode requiring provider/model shape, and Goose preserving provider-native model IDs.
- Discovery and refresh paths consume active provider inventory instead of every provider in static support tables or upstream documentation.
- Defaults remain curated and are not changed automatically by discovery.
python -m kai.refresh_models is either folded into the relevant discovery path or documented as an operator audit tool rather than the primary freshness mechanism.
- Documentation explains the distinction: backends and providers are registered, model availability is discovered where possible, active providers are derived from effective Kai user routing, and defaults are curated.
Implementation guidance
Work one child issue at a time. Codex, Claude, OpenCode, Goose, and active-provider inventory are intentionally separate because their model surfaces have different owners, command shapes, cache semantics, and validation rules.
Avoid a single broad PR that changes every provider, all model UI behavior, cache semantics, refresh tooling, and documentation at once. The safe path is to land the active provider inventory and one backend or provider source with tests, then generalize only after two or more sources prove that a shared abstraction removes real duplication.
Summary
Kai should keep backend and provider registration explicit, but model availability should come from the backend or provider source of truth wherever a reliable source exists. New upstream models should become selectable without source edits, while Kai defaults remain curated and stable.
This is an epic. Each backend or provider surface should be handled in a focused child issue so the implementation can respect the different discovery mechanics instead of forcing one abstraction across incompatible sources.
Current state
Kai currently keeps several model surfaces in source:
PROVIDER_MODELS,PROVIDER_DEFAULTS,CODEX_MODELS,OPEN_ENDED_PROVIDERS, and the per-roleMODEL_REGISTRY./modelsshows a curated keyboard whenmodels_for_backend()returns a static dict. Open-ended surfaces fall back to free-text/model <id>input. OpenCode accepts free-textprovider/modelIDs and lets OpenCode resolve them at runtime. Goose accepts provider-native model IDs throughGOOSE_MODELandgoose run --model.The existing
python -m kai.refresh_modelshelper queries some curated provider model endpoints and prints diffs againstPROVIDER_MODELS. That remains useful as an operator audit tool, but it does not make/modelscurrent at runtime and does not address backend-specific catalogs such as Codex, OpenCode, or Goose.Kai has static backend/provider support and a runtime user/default cascade, but the epic needs a derived active-provider inventory so discovery knows which provider surfaces are relevant for each user and installation.
Problem
When a provider or backend releases a new model, Kai may already be able to use it before it is visible or accepted in the model picker. For curated static surfaces, a source change is required before the user can select the model. For open-ended surfaces, direct
/model <id>may work, but/modelscannot help the user discover the model.The maintenance burden differs by backend. Some surfaces use curated aliases, some use provider API model IDs, some use backend-owned CLI catalogs, some use full
provider/modelIDs, and some are intentionally open-ended. Treating all of these as one generic provider list is the failure mode: it hides the source of truth that actually governs each runtime path.Provider discovery can also start from the wrong set. A provider can be supported by code, configured somewhere in the environment, or documented by an upstream backend without being active for a Kai user. Discovery should start from derived active provider inventory, not from every theoretically supported provider.
Design constraints
Provider and backend registration must remain explicit. Providers affect auth, environment variables, config validation, sudo preservation, documentation, runtime failure modes, and operator setup.
Discovered availability must not change curated defaults. Discovery can make a model selectable, but it must not silently change
DEFAULT_MODEL,PROVIDER_DEFAULTS,CODEX_DEFAULT_MODEL, or per-roleMODEL_REGISTRYchoices.Discovery must be scoped to the surface that owns model availability. Codex should use the Codex CLI catalog, not OpenAI
/v1/models. Claude should follow Claude Code's alias and full-ID behavior, not assume the Anthropic API catalog is the same surface. OpenCode should useopencode models [provider]under the effective Kai user, while preserving free-textprovider/modelinput. Goose should not scrape interactivegoose configure; until Goose exposes a stable non-interactive model catalog command, Goose discovery should be provider-native and preserve Goose's bare model ID shape.Discovery should operate over active provider inventory derived from effective user routing. Supported provider means Kai can use a provider in principle. Configured provider means setup appears present. Active provider means a user can actually reach that backend/provider pair through Kai after inheritance is applied.
Provider-specific follow-ups should be created only when that provider is an active Kai target or has a concrete source-of-truth decision to evaluate. A provider documented by an upstream backend is not automatically a Kai provider issue.
Multi-user installs must not share availability across identities when the underlying backend's catalog can differ by user auth, plan, provider config, or local installation. Codex, OpenCode, and Goose are clear examples: model availability can depend on the effective OS user, backend auth, provider credentials, installed CLI version, and provider-specific config, so a global cache is unsafe.
Fallbacks must be explicit. If discovery fails, Kai should continue with the previous static or free-text behavior where that behavior was already accepted, and the user-visible model surface should not collapse just because a refresh failed.
Child issues
codex debug models.opencode models.Additional child issues should be created per active backend or provider as the source of truth is evaluated.
Acceptance criteria
/modelsuses discovered model lists where a reliable source exists and falls back clearly when discovery is unavailable./model <id>remains available as an escape hatch for surfaces that can validate at runtime or are intentionally open-ended./modelvalidation preserve backend-specific rules, including Codex using the Codex CLI surface, OpenCode requiringprovider/modelshape, and Goose preserving provider-native model IDs.python -m kai.refresh_modelsis either folded into the relevant discovery path or documented as an operator audit tool rather than the primary freshness mechanism.Implementation guidance
Work one child issue at a time. Codex, Claude, OpenCode, Goose, and active-provider inventory are intentionally separate because their model surfaces have different owners, command shapes, cache semantics, and validation rules.
Avoid a single broad PR that changes every provider, all model UI behavior, cache semantics, refresh tooling, and documentation at once. The safe path is to land the active provider inventory and one backend or provider source with tests, then generalize only after two or more sources prove that a shared abstraction removes real duplication.