Skip to content

fix(#29872): cross-check custom provider pool key against requested name - #30107

Closed
zombi3butt wants to merge 13 commits into
NousResearch:mainfrom
zombi3butt:fix/profile-env-var-pidfile-collision
Closed

fix(#29872): cross-check custom provider pool key against requested name#30107
zombi3butt wants to merge 13 commits into
NousResearch:mainfrom
zombi3butt:fix/profile-env-var-pidfile-collision

Conversation

@zombi3butt

Copy link
Copy Markdown

Fix for #29872

Problem

When a named custom provider (e.g. custom:bobapi-deepseek) is resolved,
_try_resolve_from_custom_pool() calls get_custom_provider_pool_key(base_url, provider_name) which may fall back to base_url matching and pick a DIFFERENT
provider's pool if multiple entries share the same endpoint URL. This causes
the wrong API key to be loaded from auth.json pools.

Root Cause

_try_resolve_from_custom_pool() calls get_custom_provider_pool_key(base_url, provider_name). Inside GPCPK, when provider_name is given, it iterates through
config entries to find a name match. But if multiple custom providers share the
same base_url with different names, iteration order and matching logic may cause
a fallback match against the wrong provider entry — returning wrong credentials
from auth.json.

Fix

Add a cross-check in _try_resolve_from_custom_pool(): before calling GPCPK for
the actual URL-based lookup, iterate _iter_custom_providers() to find the expected
pool key for the given provider_name. Then compare against what GPCPK actually
returns. If they differ (URL fallback picked a different provider), reject and
return None instead of loading wrong API key.

Changes

  • hermes_cli/runtime_provider.py: Added cross-check logic in _try_resolve_from_custom_pool()

    • Imports _iter_custom_providers from agent.credential_pool
    • Cross-checks expected pool key (from name-only iteration) against actual pool key (from URL-based lookup)
    • Rejects resolution if keys don't match
  • tests/hermes_cli/test_runtime_provider_cross_check.py: New regression tests

    • Rejects on key mismatch, passes on match, no cross-check for provider_name=None, early exit for missing config

Testing

  • All 181 targeted tests pass (runtime_provider_resolution + credential_pool + cross_check)
  • No regressions in existing custom provider resolution paths

3ighty2O and others added 13 commits May 21, 2026 21:17
…me dict (issue NousResearch#29872)

When resolving named custom providers (custom:<name>), the returned
runtime dict previously collapsed provider to bare 'custom', losing
the specific sub-provider identity. This caused TUI display and
credential pool lookups to show only 'custom' instead of
'custom:bobapi-deepseek' etc.

Changes:
- _try_resolve_from_custom_pool: preserve sub-provider name in
  returned provider field as 'custom:<name>'
- _resolve_named_custom_runtime: non-pool return path uses
  'custom:<name>' for the provider field
- Bare 'custom' with explicit base_url remains unchanged

Updated tests to reflect new expected provider values.
fix: route cron job scripts through remote terminal backend (issue NousResearch#29849)
When provider hooks silently strip the role="system" message (known with
some Ollama Cloud variants), re-inject from original input so SOUL.md is
never lost mid-flight. Adds defensive verification in _build_kwargs_from_profile.
…esearch#29920)

Two-layer fix for `HTTP 400: invalid message content type: map[string]interface{}`.

1. `_tool_result_content_for_active_model` in run_agent.py — serializes
   non-string, non-list results (Python dicts/lists from MCP tools or memory
   helpers) as JSON before appending to messages. Falls back to repr() on
   serialization failure.

2. `sanitize_api_messages` in agent_runtime_helpers.py — coerces tool role
   `content` to JSON string as a safety-net before every API call. Catches
   any tool results that bypass the first layer (e.g. from session restore
   or manual message manipulation).

Fixes the 'model provider failed after retries' loop caused by a single bad
tool result poisoning the entire message history.
Three-layer fix for Discord bot-to-bot silence token handling:

1. Entry filter (_handle_message): Drop Discord bot messages with content
   exactly "NO_REPLY" before they enter the agent loop. When
   DISCORD_ALLOW_BOTS=mentions, other bots' NO_REPLY must be ignored.

2. Backfill exclusion (_fetch_channel_context): Exclude NO_REPLY sentinel
   messages from channel history backfill so they don't contaminate session
   context.

3. Delivery suppression (send): Suppress literal NO_REPLY responses from
   being sent to Discord channels — it's a control/silence token, not
   user-facing content.

Fixes noisy bot-to-bot loops caused by agent silence being surfaced as
empty-response retries.
…onversation (issue NousResearch#29926)

When auto-compression rotates the session mid-run, result["messages"]
contains the inflated post-turn list (compressed baseline + this turn's
growth). The CLI was overwriting conversation_history with this inflated
list, causing the next turn to start from 130K+ tokens instead of the
compressed ~24K baseline — wasting VRAM and API time.

Fix: detect session rotation via _cli_last_run_old_session_id (captured
before run_conversation in the agent thread) and use agent._session_messages
instead of result["messages"] when rotation occurred. Falls back to
result["messages"] for normal (non-rotated) runs.

Mirrors the gateway path fix in PR NousResearch#29505.
…usResearch#29948)

When users start multiple Telegram gateways via
  HERMES_PROFILE=alice hermes gateway --replace &
  HERMES_PROFILE=bob hermes gateway --replace &
the _apply_profile_override() function now resolves HERMES_PROFILE
to the correct profile-scoped PID file instead of colliding on the
default ~/.hermes/gateway.pid.

Root cause: _apply_profile_override() read --profile/-p argv flags and
active_profile file but completely ignored HERMES_PROFILE env var.
Both gateways resolved to the same default path → profile B's --replace
SIGKILL'd profile A's gateway.

Priority order (highest to lowest):
  1. --profile/-p argv flag (explicit)
  2. HERMES_PROFILE env var (session-level)
  3. active_profile file (sticky default)
  4. No override — falls back to default ~/.hermes

Only honours HERMES_PROFILE if the profile directory exists, preventing
crashes on stale/typos env vars. Falls through gracefully to active_profile.

Fixes issue NousResearch#29948.
… requested name

When  is called with a named custom
provider (e.g. ), it calls
 which may
fall back to base_url matching and pick a DIFFERENT provider's pool if
multiple entries share the same endpoint.

This fix adds a cross-check: iterate  in the
caller to find the expected pool key for the given name, then compare
against what GPCPK actually returns. If they differ (URL fallback picked
wrong provider), reject and return None instead of loading wrong API key.

Also added regression tests verifying rejection on mismatch and pass on
match, plus no-cross-check path for provider_name=None.

Fixes NousResearch#29872
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists area/auth Authentication, OAuth, credential pools comp/cli CLI entry point, hermes_cli/, setup wizard labels May 21, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This PR bundles many unrelated changes across 16 files (sanitize_api_messages, model_metadata llama.cpp context, chat_completions kwargs, etc.) beyond the described runtime_provider.py cross-check. The core fix looks correct for #29872. Recommend splitting unrelated changes into separate PRs. Also see #29893 which addresses the same issue more narrowly.

@zombi3butt

Copy link
Copy Markdown
Author

Closing per alt-glitch review. This PR bundles unrelated changes (16 files, 997 additions) and overlaps with #29893 which addresses #29872 with a cleaner 4-file fix that directly extracts the sub-name from custom: and passes it to _try_resolve_from_custom_pool(). Vanhci's approach fixes the root cause rather than adding defensive validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants