hermes - ✅(Solved) Fix Profile config fallback_providers not inherited by kanban worker agents [2 pull requests, 1 participants]

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…
GitHub stats
NousResearch/hermes-agent#23362Fetched 2026-05-11 03:29:52
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×3cross-referenced ×2referenced ×1

Root Cause

In hermes_cli/kanban_db.py:_default_spawn(), workers are spawned as:

hermes -p <profile> --skills kanban-worker chat -q work kanban task <id>

The profile's config.yaml is loaded but only model.provider / model.base_url / model.default are extracted. fallback_providers is never read from the profile config and is not passed to AIAgent.__init__(fallback_model=...).

Additionally, run_agent.py:AIAgent.__init__() accepts fallback_model as a constructor argument, but callers (the hermes CLI entry points) never extract fallback_providers from the active profile config to pass it through.

Fix Action

Fix / Workaround

However, when kanban dispatcher spawns worker agents via _default_spawn(), those workers (AIAgent instances) are initialized with fallback_model=None, regardless of what is configured in the worker's profile config. The fallback_providers field in a profile config is never read — only gateway/run.py:_try_resolve_fallback_provider() reads fallback_providers from the main config, not from profile-specific configs.

In hermes_cli/ where a profile config is loaded and an AIAgent is constructed (e.g. the CLI entry point that handles -p <profile> chat or the kanban dispatcher path), extract fallback_providers from the profile config and pass it as fallback_model=fallback_providers to AIAgent.__init__().

PR fix notes

PR #23368: fix(oneshot): pass fallback_providers from profile config to AIAgent

Description (problem / solution / changelog)

What does this PR do?

Kanban workers are spawned as hermes -p <profile> chat -q ..., which goes through the oneshot path in hermes_cli/oneshot.py. The AIAgent was constructed without a fallback_model argument, so any fallback_providers configured in a profile's config.yaml was silently ignored — workers had no fallback chain even when one was explicitly set.

Fix: reads fallback_providers / fallback_model from the already-loaded profile cfg and passes it to AIAgent as fallback_model, mirroring the same normalization already used in the interactive CLI path (cli.py:2478).

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

References

Fixes #23362

Checklist

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • My PR contains only changes related to this fix

Changed files

  • hermes_cli/oneshot.py (modified, +26/-4)
  • tools/process_registry.py (modified, +1/-1)

PR #23421: refactor(agent): decompose AIAgent constructor into config dataclasses (Phase 1)

Description (problem / solution / changelog)

Summary

Phase 1 of the agent core decomposition proposed in the architecture audit (#17154) and aligned with the middleware direction from #626.

The 60-parameter AIAgent.__init__ is the root cause of several recurring bugs where subagents and background agents miss critical config (see #15882, #11811, #18961, #23362). This PR introduces four focused dataclasses that group those parameters by responsibility:

ClassConcernReplaces
ProviderConfigAPI routing, credentials, provider selectionbase_url, api_key, provider, api_mode, model, fallback_model, credential_pool, ...
SessionConfigSession identity, platform contextsession_id, platform, user_id, chat_id, thread_id, gateway_session_key, ...
BudgetConfigIteration/token limits, trajectory settingsmax_iterations, save_trajectories, max_tokens, reasoning_config, checkpoints, ...
CallbackConfigAll callback hookstool_progress_callback, stream_delta_callback, clarify_callback, ...

Design principles

  • SOLID: Single responsibility per class, Open/Closed via new fields, Interface Segregation (consumers depend only on what they need), Dependency Inversion (AIAgent depends on abstract config)
  • DRY: Validation, serialization, defaults live once per class
  • TDD: RED-GREEN-RED-GREEN cycle, 25 tests written before implementation
  • Zero regressions: 3974 existing tests pass

Backward compatibility

Old-style positional params still work. Config objects take precedence when both are provided:

# Old way (still works)
agent = AIAgent(model="claude-sonnet-4", provider="openrouter", max_iterations=30)

# New way (config objects)
agent = AIAgent(
    provider_config=ProviderConfig(model="claude-sonnet-4", provider="openrouter"),
    budget_config=BudgetConfig(max_iterations=30),
)

Test plan

  • 19 dataclass contract tests (creation, validation, serialization, roundtrip, auto-detection)
  • 6 AIAgent integration tests (config objects accepted, backward compat, precedence)
  • 3974 existing tests pass with zero regressions (1 pre-existing flaky failure excluded: test_same_key_replaces_stale_loop_entry)

Future phases

This is Phase 1. Subsequent phases (separate PRs):

  • Phase 2: Extract AgentLoop from run_conversation() into agent/loop.py
  • Phase 3: Provider-as-strategy (push all provider routing into adapters)
  • Phase 4: Event-driven lifecycle (EventBus replacing scattered callback invocations)
  • Phase 5: Decompose cli.py (11k LOC → focused modules)
  • Phase 6: AgentHarness stable interface for plugins

Relates to #626, #17154, #3823, #21172, #15882, #11811, #18961, #23362

Changed files

  • agent/config.py (added, +263/-0)
  • run_agent.py (modified, +120/-0)
  • tests/agent/test_config.py (added, +337/-0)

Code Example

model:
  provider: minimax-cn
  base_url: https://api.minimaxi.com/anthropic
  default: MiniMax-M2.7

---

fallback_providers:
- provider: deepseek
  model: deepseek-v4-flash
  base_url: https://api.deepseek.com/v1
- provider: openrouter
  model: tencent/hy3-preview:free
  base_url: https://openrouter.ai/api/v1

---

hermes -p <profile> --skills kanban-worker chat -q work kanban task <id>
RAW_BUFFERClick to expand / collapse

Problem

Profile-specific config.yaml files (e.g. ~/.hermes/profiles/scraper/config.yaml) support a model section:

model:
  provider: minimax-cn
  base_url: https://api.minimaxi.com/anthropic
  default: MiniMax-M2.7

The main agent config (~/.hermes/config.yaml) supports fallback_providers as a top-level list:

fallback_providers:
- provider: deepseek
  model: deepseek-v4-flash
  base_url: https://api.deepseek.com/v1
- provider: openrouter
  model: tencent/hy3-preview:free
  base_url: https://openrouter.ai/api/v1

However, when kanban dispatcher spawns worker agents via _default_spawn(), those workers (AIAgent instances) are initialized with fallback_model=None, regardless of what is configured in the worker's profile config. The fallback_providers field in a profile config is never read — only gateway/run.py:_try_resolve_fallback_provider() reads fallback_providers from the main config, not from profile-specific configs.

Impact

Workers (e.g. scraper profile agents) have no fallback if their primary provider fails (rate limit, timeout, etc.). The agent simply fails instead of failing over to a backup provider.

Root Cause

In hermes_cli/kanban_db.py:_default_spawn(), workers are spawned as:

hermes -p <profile> --skills kanban-worker chat -q work kanban task <id>

The profile's config.yaml is loaded but only model.provider / model.base_url / model.default are extracted. fallback_providers is never read from the profile config and is not passed to AIAgent.__init__(fallback_model=...).

Additionally, run_agent.py:AIAgent.__init__() accepts fallback_model as a constructor argument, but callers (the hermes CLI entry points) never extract fallback_providers from the active profile config to pass it through.

Expected Behavior

When a worker agent is spawned with hermes -p <profile>, the profile's config.yaml fallback_providers (if present) should be used as the worker's fallback chain, just as the main config's fallback_providers is used for the main agent.

Suggested Fix

In hermes_cli/ where a profile config is loaded and an AIAgent is constructed (e.g. the CLI entry point that handles -p <profile> chat or the kanban dispatcher path), extract fallback_providers from the profile config and pass it as fallback_model=fallback_providers to AIAgent.__init__().

Specifically:

  1. Profile config loading in the CLI path should also extract config.get("fallback_providers")
  2. That value should be passed to the AIAgent constructor as fallback_model
  3. This applies to both the interactive CLI path and the kanban worker spawn path

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 - ✅(Solved) Fix Profile config fallback_providers not inherited by kanban worker agents [2 pull requests, 1 participants]