# Managing Models

Every model and credential task, with its TUI command and HTTP endpoint.

Everything is managed from the TUI or the local HTTP API — there is no `agen model` CLI subcommand:

| Task | TUI | HTTP |
|---|---|---|
| Add / remove a provider or model | `/model` → `add`; `d` on a model row removes it. Since v1.1.2 the model list shows each model's context window as input/output tokens (`200K/64K`) when the window table knows it, and already-registered models are only preselected — the `(added)` suffix is gone. Since v1.2.0 the model multi-select has a search field that filters the list as you type | `POST` `/v1/models`, `DELETE` `/v1/models/*name` |
| Reorder fallback priority | `/model` → `model`, `w` / `s` move the model under the cursor. Since v1.2.0 each moved row shows its offset (`(+1)` / `(-1)`) and the order is saved only when the popup is confirmed with Enter; leaving with Esc discards it | `GET` `POST` `/v1/model/priority` — `{models}` |
| Set a model's tier | `/model`, `t` on a model row | `POST` `/v1/model/tier` — `{model, tier}` |
| Pick the dispatcher model | `/model` → `dispatch` | `GET` `POST` `/v1/model` — `{dispatcher}` |
| Pick a session's reasoning level | `/model` → `reasoning` (or `Shift+A` / `Shift+D`) | `POST` `/v1/session/:id` — `{reasoning}` |
| Pick the summary model | `/model` → `summary` | `GET` `POST` `/v1/model` — `{summary}` |
| Pick the image generator | `/model` → `image` | `GET` `POST` `/v1/model` — `{image}`, a provider endpoint rather than a model name |
| Pick the speech-to-text model | `/model` → `stt` | `GET` `POST` `/v1/model` — `{stt}`, chosen from `GET /v1/model/audio` |
| Pick the text-to-speech model | `/model` → `tts` | `GET` `POST` `/v1/model` — `{tts}`, chosen from `GET /v1/model/audio` |
| Pick a session's model | `/model` (or `Shift+W` / `Shift+S` to cycle `auto` and registered models) | `POST` `/v1/session/:id` — `{model, reasoning}` |
| Store a credential | `/key` | `POST` `/v1/provider/:provider/key` |
| OAuth login | `/model` | `GET` `/v1/provider/:provider/oauth` (SSE: device code for `copilot`, browser URL for `codex` / `grok-oauth`, CLI check for `claude-code`) |
| Clear an OAuth login | `/model` | `DELETE` `/v1/provider/:provider/oauth` |
| Check quota / balance | `Shift+U` | `GET` `/v1/providers/quota`; one model's provider: `GET` `/v1/model/quota?model=<name>` |

Model routing is one object: `GET` / `POST` `/v1/model` reads and partially updates `dispatcher`, `summary`, `image`, `stt`, and `tts` together. A field left out is untouched, `""` clears it. In `config.json` these land as `dispatcher_model`, `summary_model`, `image_generator`, `stt_model`, and `tts_model`.

Audio routing is separate from the model registry. `stt` and `tts` are not picked from the models you registered with `/model add` — they are queried live from whichever of OpenAI, Gemini and OpenRouter currently hold a credential, and only those three providers back audio today (OpenRouter was added in v1.0.17). Selecting `disable` (listed first) for `tts` also removes `generate_audio` from the tool set; selecting `disable` for `stt` stops `read_files` from transcribing audio and video, and makes inbound Telegram / Discord voice messages refuse with a hint to pick a model.

Credentials (API keys, OAuth tokens) live in the OS keychain under service `agenvoy`, never in plain JSON. `config.json` keeps only the list of stored key names under `keys`.

The daemon watches `config.json`; a write reloads the agent registry (and reconnects Telegram / Discord) without a restart.

Since v1.0.13 a registry entry holds only the model name and resolves its provider config on every `Send` instead of building the client once at startup, so an OAuth token refreshed in the keychain takes effect on the next request rather than at the next registry reload.
