Port of tai.py 6957-8650 (backends, ModelHealth, ChatClient) plus the helpers they need. Namespace `detz::llm` (`detz::py` for Python semantics, `detz::json` for JSON). The old placeholder API (openai/anthropic providers) is removed: tai.py has no such providers.
| `unicode_data.cpp` (generated) | case maps, whitespace, digits from Python |
`--update-models` is `agent::update_models_command(config)`.
Gaps: see docs/agent.md. Unit tests never touch the network; the libcurl transport itself is only exercised by compile/link, not against a live server.
- Each failed candidate prints `x provider/model failed after N.Ns: reason` on stderr immediately (`ChatClient::announce_failure`), in addition to the debug log event.
-`muse_argv` always passes `--reasoning-effort low` (`kMuseReasoningEffort`), `--preset empty`, `--no-foreign-personal-context`. Measured on a 16 KB tai prompt: 36.5 s default, 2.8 s low, 2.4 s low + empty preset. Gap: only one prompt was measured, on one machine.
-`ordered_candidates` drops CLI candidates whose circuit is open (3 consecutive failures, 300 s) unless none remain.
- opencode runs are capped at `kCliFallbackTimeout` (30 s).