hermes - 💡(How to fix) Fix `_load_config()` in delegate_tool.py never reads persistent config — breaks all delegation.* settings in v0.14.0

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

def _load_config() -> dict: try: from cli import CLI_CONFIG cfg = CLI_CONFIG.get("delegation") or {} if cfg: # ← BUG: always truthy return cfg # ← Always returns here except Exception: pass # Persistent config fallback — NEVER REACHED ...

Root Cause

tools/delegate_tool.py lines 2459–2481:

def _load_config() -> dict:
    try:
        from cli import CLI_CONFIG
        cfg = CLI_CONFIG.get("delegation") or {}
        if cfg:                    # ← BUG: always truthy
            return cfg             # ← Always returns here
    except Exception:
        pass
    # Persistent config fallback — NEVER REACHED
    ...

CLI_CONFIG is loaded at gateway startup with "delegation" always present (default keys with empty-string values, set in cli.py lines 389–395). The check if cfg: passes because the dict is non-empty, so the persistent config.yaml is never consulted.

This is the exact bug described in #11999, which was marked as closed in v0.11.x. The commit 9423fda added _resolve_delegation_credentials() and credential plumbing, but _load_config() itself was never patched — the fix was incomplete or regressed.

Fix Action

Fix / Workaround

This is the exact bug described in #11999, which was marked as closed in v0.11.x. The commit 9423fda added _resolve_delegation_credentials() and credential plumbing, but _load_config() itself was never patched — the fix was incomplete or regressed.

IssueStatusRelation
#11999Closed (v0.11.x)Same root bug. Supposedly fixed but _load_config was never patched.
#26482Open (P2)Master tracking issue. This _load_config bug is the reason config settings are ignored.
#26486OpenFixes ACP transport leak + provider override. Does not touch _load_config.
#17685Open (P3)Per-task model override in batch mode. Covered by the schema/handler gap.
#17718OpenPer-task model override (1-line fix). Covered by our proposal.
#5012OpenFeature request for model/provider params. Covered by our proposal.
#31155ClosedDuplicate of #26482.

Code Example

def _load_config() -> dict:
    try:
        from cli import CLI_CONFIG
        cfg = CLI_CONFIG.get("delegation") or {}
        if cfg:                    # ← BUG: always truthy
            return cfg             # ← Always returns here
    except Exception:
        pass
    # Persistent config fallback — NEVER REACHED
    ...

---

delegation:
  model: deepseek/deepseek-v4-pro
  provider: kilocode
  base_url: https://api.kilo.ai/api/gateway

---

child = _build_child_agent(
    ...
    model=creds["model"],  # ← no per-call or per-task override
    ...
)

---

def _load_config() -> dict:
    """Load delegation config by merging persistent config with in-memory defaults."""
    # Start with CLI_CONFIG defaults (always present, may be empty values)
    result = {}
    try:
        from cli import CLI_CONFIG
        result = dict(CLI_CONFIG.get("delegation") or {})
    except Exception:
        pass

    # Overlay persistent config — non-empty values win
    try:
        from hermes_cli.config import load_config
        full = load_config()
        persisted = full.get("delegation") or {}
        for key, value in persisted.items():
            if value or isinstance(value, (bool, int)):
                result[key] = value
    except Exception:
        pass

    return result
RAW_BUFFERClick to expand / collapse

_load_config() in delegate_tool.py never reads persistent config — breaks all delegation.* settings in v0.14.0

Problem

delegation.model, delegation.provider, delegation.base_url, and all other delegation.* settings in config.yaml are silently ignored on v0.14.0. Subagents always inherit the parent agent's model/provider regardless of what the user configured.

This was supposedly fixed in v0.11.x (commit 9423fda, March 11, 2026), but the _load_config() function in tools/delegate_tool.py still contains the original bug.

Root Cause

tools/delegate_tool.py lines 2459–2481:

def _load_config() -> dict:
    try:
        from cli import CLI_CONFIG
        cfg = CLI_CONFIG.get("delegation") or {}
        if cfg:                    # ← BUG: always truthy
            return cfg             # ← Always returns here
    except Exception:
        pass
    # Persistent config fallback — NEVER REACHED
    ...

CLI_CONFIG is loaded at gateway startup with "delegation" always present (default keys with empty-string values, set in cli.py lines 389–395). The check if cfg: passes because the dict is non-empty, so the persistent config.yaml is never consulted.

This is the exact bug described in #11999, which was marked as closed in v0.11.x. The commit 9423fda added _resolve_delegation_credentials() and credential plumbing, but _load_config() itself was never patched — the fix was incomplete or regressed.

Reproduction

  1. Set delegation config:
delegation:
  model: deepseek/deepseek-v4-pro
  provider: kilocode
  base_url: https://api.kilo.ai/api/gateway
  1. Start parent agent on a different model (e.g., kilo-auto/balanced)

  2. Call delegate_task(goal="Report your model name")

  3. Expected: Subagent uses deepseek/deepseek-v4-pro via kilocode

  4. Actual: Subagent inherits parent model (kilo-auto/balanced)

The config file is correctly written, parseable, and contains the right values — _load_config() simply never reads it.

Additional Gap: No per-call model/provider parameters

Beyond the _load_config bug, delegate_task() also lacks:

  • Top-level model and provider function parameters
  • model and provider fields in the per-task schema (batch mode)

The handler at line 2066 hardcodes model=creds["model"] with no extraction from args:

child = _build_child_agent(
    ...
    model=creds["model"],  # ← no per-call or per-task override
    ...
)

The OpenAI function-calling schema (DELEGATE_TASK_SCHEMA) also omits model and provider properties, so the JSON validator strips them even if the LLM emits them.

Relation to Existing Issues

IssueStatusRelation
#11999Closed (v0.11.x)Same root bug. Supposedly fixed but _load_config was never patched.
#26482Open (P2)Master tracking issue. This _load_config bug is the reason config settings are ignored.
#26486OpenFixes ACP transport leak + provider override. Does not touch _load_config.
#17685Open (P3)Per-task model override in batch mode. Covered by the schema/handler gap.
#17718OpenPer-task model override (1-line fix). Covered by our proposal.
#5012OpenFeature request for model/provider params. Covered by our proposal.
#31155ClosedDuplicate of #26482.

Proposed Fix

1. Fix _load_config() — merge strategy

def _load_config() -> dict:
    """Load delegation config by merging persistent config with in-memory defaults."""
    # Start with CLI_CONFIG defaults (always present, may be empty values)
    result = {}
    try:
        from cli import CLI_CONFIG
        result = dict(CLI_CONFIG.get("delegation") or {})
    except Exception:
        pass

    # Overlay persistent config — non-empty values win
    try:
        from hermes_cli.config import load_config
        full = load_config()
        persisted = full.get("delegation") or {}
        for key, value in persisted.items():
            if value or isinstance(value, (bool, int)):
                result[key] = value
    except Exception:
        pass

    return result

This ensures:

  • CLI_CONFIG provides safe defaults (empty strings, iteration counts)
  • Persistent config values override defaults when non-empty
  • Boolean/int fields like max_iterations: 50 are preserved
  • Both entry points (CLI and gateway) respect user config

2. Add model and provider parameters to delegate_task()

  • Add model: Optional[str] = None and provider: Optional[str] = None to the function signature
  • Wire into credential resolution: if model: creds["model"] = model, if provider: creds["provider"] = provider
  • Update DELEGATE_TASK_SCHEMA to include these properties
  • Support per-task model/provider in the tasks array schema

3. Override Priority (after fix)

  1. Per-tasktasks[i].model / tasks[i].provider (highest)
  2. Per-calldelegate_task(model=..., provider=...)
  3. Configdelegation.model / delegation.provider in config.yaml
  4. Parent inheritance — parent agent's model/provider (lowest)

Impact

  • Breaking: No. Additive change — default values mean existing callers are unaffected.
  • Backwards compatible: Existing config.yaml delegation settings start working immediately.
  • No migration required.

Environment

  • Hermes Agent: v0.14.0 (2026.5.16)
  • Python: 3.11.15
  • OS: Linux 6.12.74+deb13+1-amd64
  • Profile: foundation-curator (profile-scoped config at ~/.hermes/profiles/foundation-curator/config.yaml)

Willing to Contribute

I have a tested diagnosis, verified the bug against source, and can submit a PR consolidating both the _load_config fix and the model/provider parameter additions. Please advise if you'd prefer this split across two PRs or if an active PR already covers any of this scope.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix `_load_config()` in delegate_tool.py never reads persistent config — breaks all delegation.* settings in v0.14.0