Skip to content

delegate_task can overwrite delegated custom endpoint with parent custom credential pool #7833

@bailob

Description

@bailob

Summary

delegate_task can misroute subagents that target one custom endpoint when the parent agent runs on a different custom endpoint. The child is initialized with the correct delegation base_url, but its credential pool selection later rebinds it to the parent's custom pool because both runtimes collapse to provider="custom".

Reported by Hermes Agent.

Reproduction

  1. Configure the main Hermes runtime to use one custom endpoint (for example a Codex-compatible endpoint).
  2. Configure delegation to use a different custom endpoint and model.
  3. Run a minimal delegate_task that should only produce a tiny text response.
  4. Observe that the delegated child can fail against the parent's endpoint instead of the delegated endpoint.

Observed Behavior

The child is initially built with the delegated custom endpoint, but before the delegated run starts it receives a credential pool derived from the parent custom runtime. When the child leases that pool, _swap_credential() overwrites the delegated base_url with the parent's custom endpoint. This makes the child send the delegated model name to the wrong endpoint.

Expected Behavior

Delegated children using one custom endpoint should never inherit a different custom endpoint's credential pool. Pool selection must distinguish custom runtimes by endpoint identity, not just by the normalized provider name custom.

Code Evidence

  • Delegation config is loaded in tools/delegate_tool.py via _load_config() and _resolve_delegation_credentials().
  • Child agents are built in tools/delegate_tool.py with the correct delegated base_url and api_mode.
  • The bug happens in _resolve_child_credential_pool() in tools/delegate_tool.py, which currently shares the parent's pool whenever effective_provider == parent_provider.
  • For custom endpoints, both parent and child often normalize to provider="custom", even when their base_url values differ.
  • Later, _run_single_child() acquires a lease from that pool and calls child._swap_credential(...), which overwrites the child's base_url with the leased pool entry.

Root Cause

_resolve_child_credential_pool() treats all custom runtimes as interchangeable because it compares only the provider string. It does not account for custom endpoint identity. Named custom providers and explicit delegation.base_url configurations can therefore collapse into the same provider family and incorrectly share pools.

Suggested Fix

When the effective provider is custom, resolve the pool by the delegated endpoint identity (for example via the custom pool key derived from the delegated base_url) instead of sharing the parent pool based only on provider equality. Only reuse the parent's pool when both the provider and the effective custom endpoint match.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundtool/delegateSubagent delegationtype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions