diff --git a/website/docs/developer-guide/adding-platform-adapters.md b/website/docs/developer-guide/adding-platform-adapters.md
index 5bab2fc4bee3e..e89b2aefe956c 100644
--- a/website/docs/developer-guide/adding-platform-adapters.md
+++ b/website/docs/developer-guide/adding-platform-adapters.md
@@ -9,7 +9,7 @@ This guide covers adding a new messaging platform to the Hermes gateway. A platf
:::tip
There are two ways to add a platform:
- **Plugin** (recommended for community/third-party): Drop a plugin directory into `~/.hermes/plugins/` — zero core code changes needed. See [Plugin Path](#plugin-path-recommended) below.
-- **Built-in**: Modify 20+ files across code, config, and docs. Use the [Built-in Checklist](#step-by-step-checklist) below.
+- **Built-in**: Modify 20+ files across code, config, and docs. Use the [Built-in Checklist](#step-by-step-checklist-built-in-path) below.
:::
## Architecture Overview
diff --git a/website/docs/guides/cron-script-only.md b/website/docs/guides/cron-script-only.md
index 06fa28800674a..963c57599031d 100644
--- a/website/docs/guides/cron-script-only.md
+++ b/website/docs/guides/cron-script-only.md
@@ -10,18 +10,10 @@ Sometimes you already know exactly what message you want to send. You don't need
Hermes calls this **no-agent mode**. It's the cron system minus the LLM.
-```
- ┌──────────────────┐ ┌──────────────────┐
- │ scheduler tick │ every │ run script │
- │ (every N minutes)│ ──────▶ │ (bash or python) │
- └──────────────────┘ └──────────────────┘
- │
- │ stdout
- ▼
- ┌──────────────────┐
- │ delivery router │
- │ (telegram/disc…) │
- └──────────────────┘
+```mermaid
+flowchart LR
+ tick["scheduler tick
(every N minutes)"] -->|every| script["run script
(bash or python)"]
+ script -->|stdout| router["delivery router
(telegram / discord / ...)"]
```
- **No LLM call.** Zero tokens, zero agent loop, zero model spend.
@@ -242,5 +234,4 @@ For critical system-health watchdogs that must fire *even when the gateway is do
- [Automate Anything with Cron](/docs/guides/automate-with-cron) — LLM-driven cron patterns.
- [Scheduled Tasks (Cron) reference](/docs/user-guide/features/cron) — full schedule syntax, lifecycle, delivery routing.
-- [Pipe Script Output with `hermes send`](/docs/guides/pipe-script-output) — the one-shot counterpart for ad-hoc scripts.
- [Gateway Internals](/docs/developer-guide/gateway-internals) — delivery-router internals.
diff --git a/website/docs/integrations/providers.md b/website/docs/integrations/providers.md
index 1f7d0b403a19f..b9d8c46c755ac 100644
--- a/website/docs/integrations/providers.md
+++ b/website/docs/integrations/providers.md
@@ -1372,24 +1372,24 @@ provider_routing:
**Shortcuts:** Append `:nitro` to any model name for throughput sorting (e.g., `anthropic/claude-sonnet-4:nitro`), or `:floor` for price sorting.
-## Fallback Model
+## Fallback Providers
-Configure a backup provider:model that Hermes switches to automatically when your primary model fails (rate limits, server errors, auth failures):
+Configure a backup provider:model chain that Hermes switches to automatically when your primary model fails (rate limits, server errors, auth failures):
```yaml
-fallback_model:
- provider: openrouter # required
- model: anthropic/claude-sonnet-4 # required
- # base_url: http://localhost:8000/v1 # optional, for custom endpoints
- # key_env: MY_CUSTOM_KEY # optional, env var name for custom endpoint API key
+fallback_providers:
+ - provider: openrouter # required
+ model: anthropic/claude-sonnet-4 # required
+ # base_url: http://localhost:8000/v1 # optional, for custom endpoints
+ # key_env: MY_CUSTOM_KEY # optional, env var name for custom endpoint API key
```
-When activated, the fallback swaps the model and provider mid-session without losing your conversation. It fires **at most once** per session.
+When activated, the fallback swaps the model and provider mid-turn without losing your conversation. The primary model is restored for the next user message.
Supported providers: `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `gemini`, `google-gemini-cli`, `qwen-oauth`, `huggingface`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `minimax-oauth`, `deepseek`, `nvidia`, `xai`, `ollama-cloud`, `bedrock`, `ai-gateway`, `opencode-zen`, `opencode-go`, `kilocode`, `xiaomi`, `arcee`, `gmi`, `stepfun`, `alibaba`, `tencent-tokenhub`, `custom`.
:::tip
-Fallback is configured exclusively through `config.yaml` — there are no environment variables for it. For full details on when it triggers, supported providers, and how it interacts with auxiliary tasks and delegation, see [Fallback Providers](/docs/user-guide/features/fallback-providers).
+Fallback is configured exclusively through `config.yaml` — there are no environment variables for it. The older top-level `fallback_model` object is still read for backward compatibility, but new configs should use `fallback_providers`. For full details on when it triggers, supported providers, and how it interacts with auxiliary tasks and delegation, see [Fallback Providers](/docs/user-guide/features/fallback-providers).
:::
---
diff --git a/website/docs/user-guide/configuration.md b/website/docs/user-guide/configuration.md
index b370c628e2aed..eff6d98773e7b 100644
--- a/website/docs/user-guide/configuration.md
+++ b/website/docs/user-guide/configuration.md
@@ -819,7 +819,7 @@ Available providers for auxiliary tasks: `auto`, `main`, plus any provider in th
:::
:::warning `"main"` is for auxiliary tasks only
-The `"main"` provider option means "use whatever provider my main agent uses" — it's only valid inside `auxiliary:`, `compression:`, and `fallback_model:` configs. It is **not** a valid value for your top-level `model.provider` setting. If you use a custom OpenAI-compatible endpoint, set `provider: custom` in your `model:` section. See [AI Providers](/docs/integrations/providers) for all main model provider options.
+The `"main"` provider option means "use whatever provider my main agent uses" — it's only valid inside `auxiliary:`, `compression:`, and fallback-provider entries. It is **not** a valid value for your top-level `model.provider` setting. If you use a custom OpenAI-compatible endpoint, set `provider: custom` in your `model:` section. See [AI Providers](/docs/integrations/providers) for all main model provider options.
:::
### Full auxiliary config reference
@@ -887,7 +887,7 @@ Each auxiliary task has a configurable `timeout` (in seconds). Defaults: vision
:::
:::info
-Context compression has its own `compression:` block for thresholds and an `auxiliary.compression:` block for model/provider settings — see [Context Compression](#context-compression) above. The fallback model uses a `fallback_model:` block — see [Fallback Model](/docs/integrations/providers#fallback-model). All three follow the same provider/model/base_url pattern.
+Context compression has its own `compression:` block for thresholds and an `auxiliary.compression:` block for model/provider settings — see [Context Compression](#context-compression) above. The primary fallback chain uses top-level `fallback_providers:` — see [Fallback Providers](/docs/integrations/providers#fallback-providers). All three follow the same provider/model/base_url pattern.
:::
### Session Search Tuning
@@ -936,7 +936,7 @@ AUXILIARY_VISION_MODEL=openai/gpt-4o
### Provider Options
-These options apply to **auxiliary task configs** (`auxiliary:`, `compression:`, `fallback_model:`), not to your main `model.provider` setting.
+These options apply to **auxiliary task configs** (`auxiliary:`, `compression:`) and fallback-provider entries, not to your main `model.provider` setting.
| Provider | Description | Requirements |
|----------|-------------|-------------|
diff --git a/website/docs/user-guide/features/fallback-providers.md b/website/docs/user-guide/features/fallback-providers.md
index df52eb1a66799..a75d1d3862de1 100644
--- a/website/docs/user-guide/features/fallback-providers.md
+++ b/website/docs/user-guide/features/fallback-providers.md
@@ -27,20 +27,20 @@ The easiest path is the interactive manager:
hermes fallback
```
-`hermes fallback` reuses the provider picker from `hermes model` — same provider list, same credential prompts, same validation. Press `a` to add a fallback, `↑`/`↓` to reorder, `d` to remove, `q` to save and exit. Changes persist under `model.fallback_providers` in `config.yaml`.
+`hermes fallback` reuses the provider picker from `hermes model` — same provider list, same credential prompts, same validation. Press `a` to add a fallback, `↑`/`↓` to reorder, `d` to remove, `q` to save and exit. Changes persist under top-level `fallback_providers` in `config.yaml`.
-If you'd rather edit the YAML directly, add a `fallback_model` section to `~/.hermes/config.yaml`:
+If you'd rather edit the YAML directly, add a top-level `fallback_providers` list to `~/.hermes/config.yaml`:
```yaml
-fallback_model:
- provider: openrouter
- model: anthropic/claude-sonnet-4
+fallback_providers:
+ - provider: openrouter
+ model: anthropic/claude-sonnet-4
```
-Both `provider` and `model` are **required**. If either is missing, the fallback is disabled.
+Each entry requires both `provider` and `model`. Entries missing either value are ignored.
:::note `fallback_model` vs `fallback_providers`
-`fallback_model` (singular) is the legacy single-fallback key — Hermes still honors it for back-compat. `fallback_providers` (plural, list) supports multiple fallbacks tried in order; `hermes fallback` writes to this key. When both are set, Hermes merges them with `fallback_providers` taking priority.
+`fallback_model` (singular) is the legacy single-fallback key — Hermes still honors it for back-compat. `fallback_providers` (plural, list) is the current format and supports multiple fallbacks tried in order; `hermes fallback` writes to this top-level key. When both are set, `fallback_providers` takes priority.
:::
### Supported Providers
@@ -90,11 +90,11 @@ Both `provider` and `model` are **required**. If either is missing, the fallback
For a custom OpenAI-compatible endpoint, add `base_url` and optionally `key_env`:
```yaml
-fallback_model:
- provider: custom
- model: my-local-model
- base_url: http://localhost:8000/v1
- key_env: MY_LOCAL_KEY # env var name containing the API key
+fallback_providers:
+ - provider: custom
+ model: my-local-model
+ base_url: http://localhost:8000/v1
+ key_env: MY_LOCAL_KEY # env var name containing the API key
```
### When Fallback Triggers
@@ -128,9 +128,9 @@ model:
provider: anthropic
default: claude-sonnet-4-6
-fallback_model:
- provider: openrouter
- model: anthropic/claude-sonnet-4
+fallback_providers:
+ - provider: openrouter
+ model: anthropic/claude-sonnet-4
```
**Nous Portal as fallback for OpenRouter:**
@@ -139,25 +139,25 @@ model:
provider: openrouter
default: anthropic/claude-opus-4
-fallback_model:
- provider: nous
- model: nous-hermes-3
+fallback_providers:
+ - provider: nous
+ model: nous-hermes-3
```
**Local model as fallback for cloud:**
```yaml
-fallback_model:
- provider: custom
- model: llama-3.1-70b
- base_url: http://localhost:8000/v1
- key_env: LOCAL_API_KEY
+fallback_providers:
+ - provider: custom
+ model: llama-3.1-70b
+ base_url: http://localhost:8000/v1
+ key_env: LOCAL_API_KEY
```
**Codex OAuth as fallback:**
```yaml
-fallback_model:
- provider: openai-codex
- model: gpt-5.3-codex
+fallback_providers:
+ - provider: openai-codex
+ model: gpt-5.3-codex
```
### Where Fallback Works
@@ -171,7 +171,7 @@ fallback_model:
| Auxiliary tasks (vision, compression) | ✘ (use their own provider chain — see below) |
:::tip
-There are no environment variables for `fallback_model` — it is configured exclusively through `config.yaml`. This is intentional: fallback configuration is a deliberate choice, not something a stale shell export should override.
+There are no environment variables for `fallback_providers` — it is configured exclusively through `config.yaml`. This is intentional: fallback configuration is a deliberate choice, not something a stale shell export should override.
:::
---
@@ -259,13 +259,13 @@ auxiliary:
base_url: null # Custom OpenAI-compatible endpoint
```
-And the fallback model uses:
+And the primary fallback chain uses:
```yaml
-fallback_model:
- provider: openrouter
- model: anthropic/claude-sonnet-4
- # base_url: http://localhost:8000/v1 # Optional custom endpoint
+fallback_providers:
+ - provider: openrouter
+ model: anthropic/claude-sonnet-4
+ # base_url: http://localhost:8000/v1 # Optional custom endpoint
```
For `auxiliary.session_search`, Hermes also supports:
@@ -287,11 +287,11 @@ auxiliary:
If your provider does not support a native OpenAI-compatible reasoning-control field, `extra_body` will not help for that part; in that case `max_concurrency` is still useful for reducing request-burst 429s.
-All three — auxiliary, compression, fallback — work the same way: set `provider` to pick who handles the request, `model` to pick which model, and `base_url` to point at a custom endpoint (overrides provider).
+Auxiliary, compression, and each fallback entry use the same provider fields: set `provider` to pick who handles the request, `model` to pick which model, and `base_url` to point at a custom endpoint (overrides provider).
### Provider Options for Auxiliary Tasks
-These options apply to `auxiliary:`, `compression:`, and `fallback_model:` configs only — `"main"` is **not** a valid value for your top-level `model.provider`. For custom endpoints, use `provider: custom` in your `model:` section (see [AI Providers](/docs/integrations/providers)).
+These options apply to `auxiliary:`, `compression:`, and fallback-provider entries only — `"main"` is **not** a valid value for your top-level `model.provider`. For custom endpoints, use `provider: custom` in your `model:` section (see [AI Providers](/docs/integrations/providers)).
| Provider | Description | Requirements |
|----------|-------------|-------------|
@@ -375,7 +375,7 @@ See [Scheduled Tasks (Cron)](/docs/user-guide/features/cron) for full configurat
| Feature | Fallback Mechanism | Config Location |
|---------|-------------------|----------------|
-| Main agent model | `fallback_model` in config.yaml — per-turn failover on errors (primary restored each turn) | `fallback_model:` (top-level) |
+| Main agent model | `fallback_providers` in config.yaml — per-turn failover on errors (primary restored each turn) | `fallback_providers:` (top-level) |
| Vision | Auto-detection chain + internal OpenRouter retry | `auxiliary.vision` |
| Web extraction | Auto-detection chain + internal OpenRouter retry | `auxiliary.web_extract` |
| Context compression | Auto-detection chain, degrades to no-summary if unavailable | `auxiliary.compression` |
diff --git a/website/docs/user-guide/features/kanban.md b/website/docs/user-guide/features/kanban.md
index c82311538de6a..a48f037039714 100644
--- a/website/docs/user-guide/features/kanban.md
+++ b/website/docs/user-guide/features/kanban.md
@@ -447,25 +447,17 @@ Visually the target is the familiar Linear / Fusion layout: dark theme, column h
The GUI is strictly a **read-through-the-DB + write-through-kanban_db** layer with no domain logic of its own:
+```mermaid
+flowchart TD
+ UI["React SPA plugin
HTML5 drag-and-drop"]
+ API["FastAPI router
plugins/kanban/dashboard/plugin_api.py"]
+ DB["~/.hermes/kanban.db
WAL, shared"]
+
+ UI -->|REST over fetchJSON| API
+ API -->|writes call kanban_db.*
same code path CLI /kanban verbs use| DB
+ DB -->|append task_events| UI
```
-┌────────────────────────┐ WebSocket (tails task_events)
-│ React SPA (plugin) │ ◀──────────────────────────────────┐
-│ HTML5 drag-and-drop │ │
-└──────────┬─────────────┘ │
- │ REST over fetchJSON │
- ▼ │
-┌────────────────────────┐ writes call kanban_db.* │
-│ FastAPI router │ directly — same code path │
-│ plugins/kanban/ │ the CLI /kanban verbs use │
-│ dashboard/plugin_api.py │
-└──────────┬─────────────┘ │
- │ │
- ▼ │
-┌────────────────────────┐ │
-│ ~/.hermes/kanban.db │ ───── append task_events ──────────┘
-│ (WAL, shared) │
-└────────────────────────┘
-```
+
### REST surface