### Self check - [x] I searched [existing issues](https://github.com/zhayujie/CowAgent/issues) (incl. closed) — no duplicate. ### What's the problem? Model configuration is currently one-model-per-provider: `custom_providers` entries carry a single `model`, and built-in vendors expose fixed preset lists. There is no place to maintain a vendor's model list (add / remove / edit), and no per-model metadata. The Agent's context budget is driven by the global `agent_max_context_tokens`, which is independent of the active model: switching to a model with a smaller real window still trims against the global budget (API 400s), and a larger window is silently capped. Related: #2747 (model routing/fallback — a per-provider catalog is its prerequisite) and #2995 (the session model switcher needs a real list to switch between). ### What would you like? 1. **Per-provider model catalog** — for both built-in and custom providers, maintain a list of models, each with capability tags (`text` / `vision` / `video` / `image` / `embedding` / `asr` / `tts`) plus an optional context window and max output tokens. `text` marks a conversational model: text-tagged entries are selectable as the main model and in the conversation switcher; the other tags route the model into the matching capability card. A model may hold several tags (text + vision for a VL model). Storage: a `provider_model_catalog` key in config.json keyed by provider id (`"zhipu"`, `"custom:<id>"`). A saved catalog replaces the vendor's preset list; no catalog keeps today's behavior (fully backward compatible). Built-in presets ship pre-typed with their real capabilities and official context/output specs. 2. **Derive the context budget from the active model** — remove the global `agent_max_context_tokens`; the trimming budget becomes model window minus output reserve (catalog value wins, falling back to the existing name-based detection). A session that switched models uses that model's window; message channels use the main model's. 3. **Honor max output tokens** — when a model's catalog entry sets `max_output_tokens`, send it as `max_tokens` (otherwise nothing is sent, preserving provider defaults). I have a working implementation ready (backend + desktop + web console) and will open a PR referencing this issue. ### Contribution - [x] I'd be interested in helping implement this.