feat(delegation): honor delegation.request_overrides on all three resolution branches with explicit-over-runtime merge precedence

Completes the #90953 salvage on post-#98237 main:

- New _merge_request_overrides helper defines the precedence contract:
  explicit delegation.request_overrides merges OVER runtime/parent-derived
  overrides — explicit top-level keys win; extra_body is deep-merged one
  level so runtime extra_body keys survive unless redefined. Inputs are
  copy.deepcopy'd so transport-side mutation can't leak into config or the
  provider runtime cache.
- Direct base_url branch: explicit key now merges over the #98237
  provider-alongside-base_url runtime overrides instead of being a separate
  return shape; max_output_tokens preserved.
- Named-provider branch and parent-inherit branch now honor the key too, so
  delegation.request_overrides never silently no-ops.
- _build_child_agent honors override_request_overrides whenever set
  (previously only when override_provider was set), enabling the inherit
  branch's merged value to reach the child.
- DEFAULT_CONFIG: delegation.request_overrides entry with comment.
- Tests: expanded tests/tools/test_delegate_request_overrides.py — deep-copy
  proofs, explicit-over-runtime precedence on the provider-alongside-base_url
  path, named-provider branch, inherit branch, and merge-helper unit tests.
- Docs: configuration.md delegation section + features/delegation.md document
  the key, precedence, and example YAML (OpenRouter extra_body.provider.sort).
This commit is contained in:
Teknium
2026-08-29 18:38:24 -07:00
parent d3bfd2e9b1
commit bacb90fe20
5 changed files with 353 additions and 33 deletions

View File

@@ -2077,6 +2077,15 @@ DEFAULT_CONFIG = {
# "codex_responses", or "anthropic_messages". Empty = auto-detect
# from URL (e.g. /anthropic suffix → anthropic_messages). Set this
# explicitly for non-standard endpoints the heuristic can't detect.
# Per-child request settings sent on every delegation API call, on all
# three resolution branches (direct base_url, named provider, and
# parent-inherit). Top-level keys are API kwargs (e.g. service_tier);
# an "extra_body" sub-dict is merged into the request's extra_body —
# e.g. {"extra_body": {"provider": {"sort": "throughput"}}} routes
# OpenRouter delegation children to the fastest provider. Precedence:
# these explicit values merge OVER runtime/parent-derived overrides
# (explicit keys win; extra_body deep-merged one level).
"request_overrides": {},
# When delegate_task narrows child toolsets explicitly, preserve any
# MCP toolsets the parent already has enabled. On by default so
# narrowing (e.g. toolsets=["web","browser"]) expresses "I want these

View File

@@ -1,17 +1,26 @@
"""Regression tests for delegation.request_overrides on the direct-endpoint branch.
"""Regression tests for the delegation.request_overrides config key.
The direct base_url branch of _resolve_delegation_credentials (delegation.base_url
set, provider=custom) used to drop delegation.request_overrides on the floor —
the named-provider branch forwards runtime request_overrides (delegate_tool.py
"request_overrides": dict(runtime.get("request_overrides") or {})), but the
direct branch returned no key. That made it impossible to give delegation
children OpenRouter routing hints (extra_body.provider = {"sort": "throughput"})
when delegating straight to openrouter.ai/api/v1 via base_url+api_key.
PR #90953 (salvage): ``delegation.request_overrides`` is an explicit dict of
per-child request settings that must be honored on EVERY resolution branch of
``_resolve_delegation_credentials``:
1. direct base_url (provider=custom) — the branch the original PR fixed,
2. named provider (delegation.provider set, no base_url),
3. parent-inherit (neither provider nor base_url).
Precedence contract (post-#98237): explicit config values merge OVER
runtime/parent-derived overrides — top-level explicit keys win; the
``extra_body`` sub-dict is deep-merged one level so runtime extra_body keys
survive unless the explicit key redefines them. Nested values are deep-copied
so transport-side mutation cannot leak back into config.
"""
import pytest
from unittest.mock import MagicMock, patch
from tools.delegate_tool import _resolve_delegation_credentials
from tools.delegate_tool import (
_merge_request_overrides,
_resolve_delegation_credentials,
)
def _cfg(**overrides):
@@ -24,6 +33,20 @@ def _cfg(**overrides):
return cfg
def _parent(**attrs):
parent = MagicMock()
parent._delegate_depth = 0
# MagicMock attributes are MagicMocks (non-dict) by default; set real
# values for the ones the resolution path inspects.
parent.request_overrides = attrs.pop("request_overrides", None)
for k, v in attrs.items():
setattr(parent, k, v)
return parent
# ── Branch 1: direct base_url ──────────────────────────────────────────────
def test_direct_branch_forwards_request_overrides():
"""delegation.request_overrides flows through the direct-endpoint branch."""
cfg = _cfg(
@@ -35,6 +58,8 @@ def test_direct_branch_forwards_request_overrides():
assert creds["request_overrides"] == {
"extra_body": {"provider": {"sort": "throughput"}},
}
# Shape parity with the named-provider branch: max_output_tokens present.
assert "max_output_tokens" in creds
def test_direct_branch_absent_request_overrides_stays_none():
@@ -50,3 +75,182 @@ def test_direct_branch_non_dict_request_overrides_stays_none():
_cfg(request_overrides=bad), parent_agent=None
)
assert creds["request_overrides"] is None
def test_direct_branch_deep_copies_nested_extra_body():
"""Transport-side mutation of the child's overrides must not leak back
into the config dict (copy.deepcopy, not a shallow dict())."""
source = {"extra_body": {"provider": {"sort": "throughput"}}}
cfg = _cfg(request_overrides=source)
creds = _resolve_delegation_credentials(cfg, parent_agent=None)
creds["request_overrides"]["extra_body"]["provider"]["sort"] = "mutated"
creds["request_overrides"]["extra_body"]["injected"] = True
assert source == {"extra_body": {"provider": {"sort": "throughput"}}}
@patch("hermes_cli.runtime_provider.resolve_runtime_provider")
def test_explicit_merges_over_runtime_on_provider_alongside_base_url(mock_resolve):
"""Precedence on the provider-alongside-base_url path (#98237 interplay):
explicit delegation.request_overrides merges OVER the named provider's
runtime overrides — runtime extra_body keys survive unless redefined,
explicit top-level keys win, and max_output_tokens is preserved."""
mock_resolve.return_value = {
"provider": "custom",
"base_url": "https://provider-default.example/v1",
"api_key": "provider-key",
"api_mode": "chat_completions",
"request_overrides": {
"service_tier": "default",
"extra_body": {"thinking": {"type": "disabled"}, "provider": {"sort": "price"}},
},
"max_output_tokens": 8192,
}
cfg = _cfg(
provider="mimo",
request_overrides={
"service_tier": "flex",
"extra_body": {"provider": {"sort": "throughput"}},
},
)
creds = _resolve_delegation_credentials(cfg, parent_agent=None)
assert creds["request_overrides"] == {
# explicit top-level key wins
"service_tier": "flex",
"extra_body": {
# runtime extra_body key survives (not redefined)
"thinking": {"type": "disabled"},
# explicit extra_body key wins over runtime's
"provider": {"sort": "throughput"},
},
}
assert creds["max_output_tokens"] == 8192
# ── Branch 2: named provider (no base_url) ─────────────────────────────────
@patch("hermes_cli.runtime_provider.resolve_runtime_provider")
def test_named_provider_branch_honors_explicit_key(mock_resolve):
"""The named-provider branch merges the explicit key over the provider's
runtime overrides — the config key never silently no-ops."""
mock_resolve.return_value = {
"provider": "openrouter",
"base_url": "https://openrouter.ai/api/v1",
"api_key": "runtime-key",
"api_mode": "chat_completions",
"request_overrides": {"extra_body": {"reasoning": {"enabled": True}}},
"max_output_tokens": 4096,
}
cfg = {
"model": "deepseek/deepseek-v4-flash-0731",
"provider": "openrouter",
"request_overrides": {"extra_body": {"provider": {"sort": "throughput"}}},
}
creds = _resolve_delegation_credentials(cfg, parent_agent=None)
assert creds["request_overrides"] == {
"extra_body": {
"reasoning": {"enabled": True},
"provider": {"sort": "throughput"},
}
}
@patch("hermes_cli.runtime_provider.resolve_runtime_provider")
def test_named_provider_branch_without_explicit_key_unchanged(mock_resolve):
"""Without the config key the named-provider branch behaves as before."""
mock_resolve.return_value = {
"provider": "openrouter",
"base_url": "https://openrouter.ai/api/v1",
"api_key": "runtime-key",
"api_mode": "chat_completions",
"request_overrides": {"extra_body": {"reasoning": {"enabled": True}}},
"max_output_tokens": 4096,
}
cfg = {"model": "m", "provider": "openrouter"}
creds = _resolve_delegation_credentials(cfg, parent_agent=None)
assert creds["request_overrides"] == {"extra_body": {"reasoning": {"enabled": True}}}
# ── Branch 3: parent-inherit (no provider, no base_url) ────────────────────
def test_inherit_branch_honors_explicit_key_over_parent():
"""Pure-inherit setups still apply delegation.request_overrides, merged
over the parent agent's own request_overrides."""
parent = _parent(
request_overrides={
"service_tier": "default",
"extra_body": {"thinking": {"type": "disabled"}},
}
)
cfg = {
"model": "",
"provider": "",
"request_overrides": {"extra_body": {"provider": {"sort": "throughput"}}},
}
creds = _resolve_delegation_credentials(cfg, parent)
assert creds["request_overrides"] == {
"service_tier": "default",
"extra_body": {
"thinking": {"type": "disabled"},
"provider": {"sort": "throughput"},
},
}
def test_inherit_branch_without_key_stays_none():
"""No explicit key and no parent overrides → None (old contract: the
child's construction path falls back to the parent's request_overrides)."""
parent = _parent(request_overrides=None)
creds = _resolve_delegation_credentials({"model": "", "provider": ""}, parent)
assert creds["request_overrides"] is None
def test_inherit_branch_deep_copies_parent_overrides():
"""Parent's nested overrides must be deep-copied on the inherit branch."""
parent_overrides = {"extra_body": {"thinking": {"type": "disabled"}}}
parent = _parent(request_overrides=parent_overrides)
cfg = {
"model": "",
"provider": "",
"request_overrides": {"extra_body": {"provider": {"sort": "throughput"}}},
}
creds = _resolve_delegation_credentials(cfg, parent)
creds["request_overrides"]["extra_body"]["thinking"]["type"] = "mutated"
assert parent_overrides == {"extra_body": {"thinking": {"type": "disabled"}}}
# ── Merge helper unit tests ────────────────────────────────────────────────
def test_merge_helper_both_none():
assert _merge_request_overrides(None, None) is None
assert _merge_request_overrides({}, {}) is None
assert _merge_request_overrides("junk", 42) is None
def test_merge_helper_explicit_only():
assert _merge_request_overrides(None, {"a": 1}) == {"a": 1}
def test_merge_helper_runtime_only():
assert _merge_request_overrides({"a": 1}, None) == {"a": 1}
def test_merge_helper_explicit_top_level_wins():
assert _merge_request_overrides({"a": 1, "b": 2}, {"a": 9}) == {"a": 9, "b": 2}
def test_merge_helper_extra_body_one_level_merge():
merged = _merge_request_overrides(
{"extra_body": {"keep": 1, "clash": "runtime"}},
{"extra_body": {"clash": "explicit", "new": 2}},
)
assert merged == {"extra_body": {"keep": 1, "clash": "explicit", "new": 2}}
def test_merge_helper_non_dict_runtime_extra_body_replaced():
merged = _merge_request_overrides(
{"extra_body": "junk"}, {"extra_body": {"a": 1}}
)
assert merged == {"extra_body": {"a": 1}}

View File

@@ -1994,9 +1994,17 @@ def _build_child_agent(
provider_require_parameters=child_provider_require_parameters,
provider_data_collection=child_provider_data_collection,
request_overrides=(
dict(override_request_overrides or {})
if override_provider
else dict(getattr(parent_agent, "request_overrides", {}) or {})
# override_request_overrides is honored whenever set —
# including the inherit branch (override_provider=None),
# where _resolve_delegation_credentials already merged
# delegation.request_overrides OVER the parent's values.
dict(override_request_overrides)
if override_request_overrides is not None
else (
{}
if override_provider
else dict(getattr(parent_agent, "request_overrides", {}) or {})
)
),
openrouter_min_coding_score=child_openrouter_min_coding_score,
tool_progress_callback=child_progress_cb,
@@ -4456,6 +4464,43 @@ def _resolve_child_credential_pool(
return None
def _merge_request_overrides(runtime_overrides, explicit_overrides):
"""Merge explicit ``delegation.request_overrides`` over runtime-derived ones.
Precedence contract: the explicit config key WINS over runtime-derived
(provider-catalog or parent-inherited) overrides. Top-level keys from the
explicit dict replace same-named runtime keys; the ``extra_body`` sub-dict
is deep-merged ONE level — runtime ``extra_body`` keys survive unless the
explicit dict redefines that exact key. This keeps provider personality
(e.g. ``thinking: {type: disabled}``) intact while letting users layer
routing hints (e.g. ``extra_body.provider = {"sort": "throughput"}``) on
top.
Both inputs are deep-copied (``copy.deepcopy``) so transport-side mutation
of the child's request kwargs can never leak back into the loaded config
dict or the provider runtime cache.
Returns ``None`` when both sides are empty/non-dict.
"""
import copy as _copy
runtime_overrides = runtime_overrides if isinstance(runtime_overrides, dict) else None
explicit_overrides = explicit_overrides if isinstance(explicit_overrides, dict) else None
if not runtime_overrides and not explicit_overrides:
return None
merged = _copy.deepcopy(runtime_overrides) if runtime_overrides else {}
explicit = _copy.deepcopy(explicit_overrides) if explicit_overrides else {}
runtime_extra = merged.get("extra_body")
explicit_extra = explicit.pop("extra_body", None)
merged.update(explicit)
if isinstance(runtime_extra, dict) and isinstance(explicit_extra, dict):
runtime_extra.update(explicit_extra)
merged["extra_body"] = runtime_extra
elif explicit_extra is not None:
merged["extra_body"] = explicit_extra
return merged or None
def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
"""Resolve credentials for subagent delegation.
@@ -4483,6 +4528,18 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
configured_api_key = str(cfg.get("api_key") or "").strip() or None
configured_api_mode = str(cfg.get("api_mode") or "").strip().lower() or None
# delegation.request_overrides: explicit per-child request settings from
# config. Honored on EVERY resolution branch (direct base_url, named
# provider, and parent-inherit) so the key never silently no-ops.
# Precedence: explicit merges OVER runtime/parent-derived overrides via
# _merge_request_overrides (top-level explicit keys win; extra_body is
# deep-merged one level). Non-dict values are ignored.
explicit_request_overrides = (
cfg.get("request_overrides")
if isinstance(cfg.get("request_overrides"), dict)
else None
)
# Native-SDK providers (Bedrock, Vertex, Google GenAI) speak their own
# wire protocol — they cannot be reached via OpenAI chat_completions against
# a base_url. For these, always fall through to resolve_runtime_provider()
@@ -4494,23 +4551,23 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
_is_native_sdk_provider = _provider_lower in _NATIVE_SDK_PROVIDERS
if configured_base_url and not _is_native_sdk_provider:
# delegation.request_overrides: same semantics as the named-provider
# branch below (runtime.get("request_overrides")) — a dict merged into
# the child's API kwargs by the transport's profile path. Keys are
# top-level kwargs (e.g. service_tier); an "extra_body" sub-dict is
# merged into extra_body. This is how a direct-endpoint delegation
# (provider=custom) forwards OpenRouter routing hints such as
# extra_body.provider = {"sort": "throughput"} to its children —
# the child's CustomProfile does not emit provider preferences, and
# the parent-inheritance path is deliberately cleared when
# delegation.provider/base_url overrides the parent (see the
# delegation.request_overrides: an explicit dict of per-child request
# settings merged into the child's API kwargs by the transport's
# profile path. Keys are top-level kwargs (e.g. service_tier); an
# "extra_body" sub-dict is merged into extra_body. This is how a
# direct-endpoint delegation (provider=custom) forwards OpenRouter
# routing hints such as extra_body.provider = {"sort": "throughput"}
# to its children — the child's CustomProfile does not emit provider
# preferences, and the parent-inheritance path is deliberately cleared
# when delegation.provider/base_url overrides the parent (see the
# provider-preference clearing in _build_child_agent).
configured_request_overrides = cfg.get("request_overrides")
request_overrides = (
dict(configured_request_overrides)
if isinstance(configured_request_overrides, dict)
else None
)
#
# Precedence: explicit delegation.request_overrides MERGES OVER any
# runtime-derived overrides (see _merge_request_overrides) — top-level
# explicit keys win; extra_body is deep-merged one level so runtime
# extra_body keys survive unless the explicit key redefines them.
# (explicit_request_overrides is parsed once at the top of this
# function and applied to every branch.)
# When delegation.api_key is not set, return None so _build_child_agent
# falls back to the parent agent's API key via the credential inheritance
@@ -4577,6 +4634,12 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
exc,
)
# Explicit delegation.request_overrides merges OVER the runtime-derived
# overrides (explicit wins; extra_body deep-merged one level).
request_overrides = _merge_request_overrides(
request_overrides, explicit_request_overrides
)
return {
"model": configured_model,
"provider": provider,
@@ -4588,14 +4651,22 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
}
if not configured_provider:
# No provider override — child inherits everything from parent
# No provider override — child inherits everything from parent.
# delegation.request_overrides still applies: merge the explicit key
# OVER the parent's own request_overrides so the config key works even
# in pure-inherit setups (never a silent no-op). None when neither
# side has values → _build_child_agent falls back to the parent's
# request_overrides unchanged.
return {
"model": configured_model,
"provider": None,
"base_url": None,
"api_key": None,
"api_mode": None,
"request_overrides": None,
"request_overrides": _merge_request_overrides(
getattr(parent_agent, "request_overrides", None),
explicit_request_overrides,
),
"max_output_tokens": None,
}
@@ -4639,7 +4710,13 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
"base_url": runtime.get("base_url"),
"api_key": api_key,
"api_mode": runtime.get("api_mode"),
"request_overrides": dict(runtime.get("request_overrides") or {}),
# Explicit delegation.request_overrides merges OVER the named
# provider's runtime overrides (explicit wins; extra_body deep-merged
# one level) — same precedence as the direct-base_url branch above.
"request_overrides": _merge_request_overrides(
runtime.get("request_overrides"), explicit_request_overrides
)
or {},
"max_output_tokens": runtime.get("max_output_tokens"),
"command": runtime.get("command"),
"args": list(runtime.get("args") or []),

View File

@@ -2568,6 +2568,10 @@ delegation:
# base_url: "http://localhost:1234/v1" # Direct OpenAI-compatible endpoint (takes precedence over provider)
# api_key: "local-key" # API key for base_url (falls back to OPENAI_API_KEY)
# api_mode: "" # Wire protocol for base_url: "chat_completions", "codex_responses", or "anthropic_messages". Empty = auto-detect from URL (e.g. /anthropic suffix → anthropic_messages). Set explicitly for non-standard endpoints the heuristic can't detect.
# request_overrides: # Per-child request settings sent on every subagent API call (all resolution branches).
# extra_body: # Merged into the request's extra_body — e.g. OpenRouter routing hints:
# provider:
# sort: throughput
max_concurrent_children: 3 # Parallel children per batch (floor 1, no ceiling). Also via DELEGATION_MAX_CONCURRENT_CHILDREN env var.
worktree_isolation: false # Give each child its own git worktree branched from HEAD (local backend + git repos only; inspired by Muse Code). See Subagent Delegation → Worktree Isolation.
max_spawn_depth: 1 # Delegation tree depth cap (1-3, clamped). 1 = flat (default): parent spawns leaves that cannot delegate. 2 = orchestrator children can spawn leaf grandchildren. 3 = three levels.
@@ -2578,6 +2582,19 @@ delegation:
**Direct endpoint override:** If you want the obvious custom-endpoint path, set `delegation.base_url`, `delegation.api_key`, and `delegation.model`. That sends subagents directly to that OpenAI-compatible endpoint and takes precedence over `delegation.provider`. If `delegation.api_key` is omitted, Hermes falls back to `OPENAI_API_KEY` only. When `delegation.provider` is set alongside `delegation.base_url`, the explicit endpoint and key still win, but that provider's request settings (`extra_body` overrides and max output tokens from your `custom_providers` entry) are carried into the subagent.
**Per-child request settings (`request_overrides`):** `delegation.request_overrides` is a dict of request settings sent on every subagent API call. Top-level keys are API kwargs (e.g. `service_tier`); an `extra_body` sub-dict is merged into the request's `extra_body`. It is honored on **all three** resolution branches — direct `base_url`, named `provider`, and pure inherit — so the key always takes effect. Precedence: explicit `request_overrides` values merge **over** any runtime- or parent-derived overrides — top-level explicit keys win, and `extra_body` is deep-merged one level so runtime `extra_body` keys (e.g. a provider's `thinking: {type: disabled}` personality) survive unless your key redefines them. The canonical use case is OpenRouter routing hints for delegation children:
```yaml
delegation:
model: "deepseek/deepseek-v4-flash-0731"
base_url: "https://openrouter.ai/api/v1"
api_key: "sk-or-..."
request_overrides:
extra_body:
provider:
sort: throughput # route children to the fastest OpenRouter provider
```
**Wire protocol (`api_mode`):** Hermes auto-detects the wire protocol from `delegation.base_url` (e.g. paths ending in `/anthropic` → `anthropic_messages`; Codex / native Anthropic / Kimi-coding hostnames keep their existing detection). For endpoints the heuristic can't classify — for example Azure AI Foundry, MiniMax, Zhipu GLM, or LiteLLM proxies fronting an Anthropic-shaped backend — set `delegation.api_mode` explicitly to one of `chat_completions`, `codex_responses`, or `anthropic_messages`. Leave it empty (the default) to keep auto-detection.
The delegation provider uses the same credential resolution as CLI/gateway startup. All configured providers are supported: `openrouter`, `nous`, `copilot`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`. When a provider is set, the system automatically resolves the correct base URL, API key, and API mode — no manual credential wiring needed.

View File

@@ -188,7 +188,7 @@ delegation:
provider: "openrouter" # optional: route children to a different provider
```
Resolution order: `delegation.base_url` (direct endpoint) takes precedence, then `delegation.provider` (full credential bundle resolved via the runtime provider system), and when neither is set children inherit the parent's provider and credentials; `delegation.model` applies in all cases, and when it is empty children inherit the parent's model. Setting `delegation.provider` alongside `delegation.base_url` keeps the explicit endpoint but carries that provider's request overrides and max output tokens into the child.
Resolution order: `delegation.base_url` (direct endpoint) takes precedence, then `delegation.provider` (full credential bundle resolved via the runtime provider system), and when neither is set children inherit the parent's provider and credentials; `delegation.model` applies in all cases, and when it is empty children inherit the parent's model. Setting `delegation.provider` alongside `delegation.base_url` keeps the explicit endpoint but carries that provider's request overrides and max output tokens into the child. An explicit `delegation.request_overrides` dict is honored on every branch and merges over those runtime-derived values (see [Configuration](#configuration) below).
Note that the pin is global: `delegate_task` has no per-task model parameter, so every child in a batch runs on the configured delegation model. For quality-sensitive subtasks that need a stronger model, either leave `delegation.model` unset for that session or hand the task to the [kanban board](kanban.md#per-task-model-override), which does support a per-task model override.
@@ -512,10 +512,23 @@ delegation:
base_url: "http://localhost:1234/v1"
api_key: "local-key"
# api_mode: "anthropic_messages" # Optional. Wire protocol override for base_url ("chat_completions", "codex_responses", or "anthropic_messages"). Empty = auto-detect from URL (e.g. /anthropic suffix). Set explicitly for endpoints the heuristic can't classify (Azure AI Foundry, MiniMax, Zhipu GLM, LiteLLM proxies, …).
# Send per-child request settings on every subagent API call — e.g. OpenRouter
# routing hints when delegating straight to openrouter.ai via base_url:
delegation:
model: "deepseek/deepseek-v4-flash-0731"
base_url: "https://openrouter.ai/api/v1"
api_key: "sk-or-..."
request_overrides:
extra_body:
provider:
sort: throughput # children route to the fastest OpenRouter provider
```
When `base_url` points at an Anthropic-compatible endpoint — for example a path ending in `/anthropic`, an Azure Foundry Claude route, or a MiniMax `/anthropic` proxy — `api_mode` is auto-detected as `anthropic_messages` so the subagent uses the right wire format without you setting anything. Set `api_mode` explicitly when the auto-detection guess is wrong (rare).
`delegation.request_overrides` works on **all three** resolution branches — direct `base_url`, named `provider`, and pure inherit — so it always takes effect. Top-level keys are API kwargs (e.g. `service_tier`); an `extra_body` sub-dict is merged into the request's `extra_body`. Explicit values merge **over** runtime- or parent-derived overrides: explicit top-level keys win, and `extra_body` is deep-merged one level, so a provider's own request personality (e.g. `thinking: {type: disabled}`) survives unless your key redefines it. See [Configuration → Delegation](../configuration.md#delegation) for details.
:::tip
The agent handles delegation automatically based on the task complexity. You don't need to explicitly ask it to delegate — it will do so when it makes sense.
:::