# config.json

Every key in `~/.config/agenvoy/config.json`, its default and what reads it.

Model, routing, and channel settings:

| Key | Description |
|---|---|
| `models` | Registered models as `"<provider>@<model>"` strings; the order is the fallback priority (`pass`-tier models are skipped since v1.1.0) |
| `model_tag` | `{model: tier}` with tiers `S` `A` `B` `C` `pass`; written as `{}` when missing. Since v1.1.0 a `pass` model is never picked by auto routing, subagents or fallback, even when a request names it; only a session whose own model is set to it uses it |
| `dispatcher_model` / `summary_model` | Dispatcher and summary roles |
| `dispatcher_beta` | Added in v1.0.18. `true` routes through the TypeSafe dispatcher instead of `dispatcher_model`; requires `TYPESAFE_API_KEY` in the keychain |
| `image_generator` / `stt_model` / `tts_model` | Image provider endpoint and audio models |
| `compats` | Custom OpenAI-compatible endpoints, `[{provider, url}]` |
| `keys` | Names of credentials stored in the keychain (never the values) |
| `telegram_enabled` / `discord_enabled` | Channel flags; `telegram_username` / `discord_username` are filled in by the daemon |
| `admin_channel` | Verification-code relay target (set from `/config` → **Admin Channel** in the TUI since v1.1.0) |
| `reply_lang` | Reply language, default `"auto"` (match the user). Accepts a code from `configs/jsons/reply_lang.json` (`en`, `zh-TW`, `zh-HK`, `zh-CN`, `ja`, `ko`, `es`, `fr`, `de`, `pt`, `it`, `ru`, `vi`, `th`, `id`, `ar`) or any language name |
| `output_dir` | Where files made for the user land when no location is named; default `""` means `~/Downloads`, or `~/.config/agenvoy/download` when that folder does not exist. `~` is expanded; an unusable path falls back to the default |

`reply_lang` and `output_dir` are set from `/config` in the TUI or `GET` / `POST` `/v1/config/system` and `/v1/config/output_dir`; `/config` also toggles **Startup on login** (`/v1/config/startup`). The separate startup, reply-language, and output-dir TUI commands were removed; use `/config`. The daemon watches `config.json` and reloads the agent registry and Telegram / Discord on every write.

`auto_reasoning` (added in v1.0.18) was removed in v1.1.0: reasoning is now a per-session setting, and a session whose `reasoning` is `auto` gets its level from the model selector per request. An `auto_reasoning` entry left in `config.json` is ignored.

Beyond model and channel settings, `config.json` carries the runtime limits and optional policy overrides. Missing limit fields, `reply_lang`, `output_dir`, and `model_tag` are filled with defaults and written back on startup.

| Key | Default | Description |
|---|---:|---|
| `limits.max_tool_iterations` | `128` | Maximum tool iterations per run |
| `limits.agent_send_timeout_seconds` | `600` | Model-request timeout |
| `limits.max_history_messages` | `24` | Recent history messages retained |
| `limits.max_history_bytes` | `4194304` | History-size ceiling. Changed from `5242880` in v1.0.12, where it became `DocumentMaxBytes * 4` — four times the 1 MiB single-document ceiling |

Package defaults that are **not** read from `config.json`:

| Constant | Default | Description |
|---|---:|---|
| `MaxSessionTasks` | `NumCPU × 4` | Concurrent tasks per session; further tasks queue rather than fail |
| `MaxSubagentTimeoutMin` | `30` | Subagent timeout in minutes |
| `MaxResumeWaitMin` | `60` | How long a pending resume waits for answers |
| `maxConcurrentTools` | `5` | Concurrent tool calls within one model turn; the rest queue (v1.0.20) |

The daemon port is **not** configurable: `17989` is a package constant, and a `limits.port` entry is ignored.

Policy keys in `config.json`. The first two merge with values embedded in the binary (user entries add to, never replace, the defaults); the rest exist only as user config:

| Key | Embedded source | Purpose |
|---|---|---|
| `sensitive_path` | `configs/jsons/sensitive_path.json` | Credential and key-material paths — reachable only after a password-backed per-session grant. Buckets: `dirs`, `files`, `prefixes`, `extensions` |
| `read_only_command` | `configs/jsons/read_only_command.json` | `run_command` calls that skip the confirm gate (`git status`, `ls`, `cat`, ...). Since v1.0.25 an entry is matched against the binary plus at most its first two arguments (`git config --get`, `docker compose ps`), a call with `network: true` or a sensitive-path argument never matches, and the dedicated read-only command tool is gone |
| `denied_command` | — | Binaries `run_command` refuses outright, inside `sh -c` too |
| `denied_path` | — | Paths permanently off limits for reads and writes; no prompt can approve them. Entries must be absolute or start with `~/`; the filesystem root is refused |
| `net_white_list` | — | Hosts exempt from the `http_request` SSRF guard |

Three keys are no longer read and produce a startup warning if present: `sensitive_map` (renamed `sensitive_path`), `white_list` (removed — commands run unless listed in `denied_command`), and `path_white_list` (removed — paths outside `$HOME` are approved per session).

v0.35.0 dropped the last pre-v0.28.9 compatibility paths: `limits.max_skill_iterations` (superseded by `limits.max_tool_iterations`) and `planner_model` (superseded by `dispatcher_model`, and deleted from `config.json` on the next save) are no longer honoured, and the legacy `api_tools/` / `script_tools/` directories are no longer read — tools live under `tools/api/` and `tools/script/`.

There are **no environment variables** for runtime limits — env-based overrides were removed; `config.json` is the only knob.
