Skip to content

fix(moa): scope the per-slot runtime cache to the active profile - #78185

Open
pierrenode wants to merge 1 commit into
NousResearch:mainfrom
pierrenode:fix/moa-runtime-cache-profile-scoping
Open

fix(moa): scope the per-slot runtime cache to the active profile#78185
pierrenode wants to merge 1 commit into
NousResearch:mainfrom
pierrenode:fix/moa-runtime-cache-profile-scoping

Conversation

@pierrenode

Copy link
Copy Markdown
Contributor

Summary

_slot_runtime()'s per-slot runtime cache (added in #77868, salvage of #66811, to cut MoA cold-start latency) keys the resolved (api_key, base_url, api_mode) runtime only on (provider, model). A multiplex gateway resolves multiple profiles' agents in the same OS process (see #77865, #77872 for the same anti-pattern in other modules). If two profiles both use MoA with a slot referencing the same (provider, model) pair but have different real credentials configured for that provider name, the second profile silently receives the first profile's resolved api_key/base_url for up to _RUNTIME_CACHE_TTL_SECONDS (300s).

Reproduced empirically before writing the fix: resolving a slot under profile A's HERMES_HOME override, then under profile B's, returned profile A's cached credentials to profile B.

Fix

Add the active hermes_home to the cache key ((hermes_home, provider, model) instead of (provider, model)), so each profile resolves and caches its own runtime independently. _preset_cache (the sibling cache in the same module) was checked and does not have this issue — its key already includes the config file's st_mtime_ns, and different profiles have different config file paths, so it self-invalidates correctly across profiles (at the cost of some cache-thrashing under interleaved multi-profile access, which is a missed optimization, not a correctness bug — left untouched to keep this fix minimal).

Test plan

  • Updated the existing TTL-expiry test (tests/agent/test_moa_cold_start_cache_66793.py) for the new 3-tuple cache key shape.
  • Added TestMoASlotRuntimeCacheIsolation to tests/test_profile_isolation_runtime.py (the existing dedicated home for this exact bug class), reproducing the cross-profile leak against two real HERMES_HOME overrides.
  • Mutation-verified: reverting the fix makes the new regression test fail with the exact leaked-credential assertion.
  • Full neighboring test sweep (all *moa* + profile-isolation test files, 106 tests) passes.
  • ruff check clean on all changed files.

_slot_runtime()'s runtime cache (added in NousResearch#77868/NousResearch#66811 to cut MoA
cold-start latency) keys resolved (api_key, base_url, api_mode) only
on (provider, model). A multiplex gateway resolves multiple profiles'
agents in the same process; a MoA slot referencing the same
(provider, model) pair in two profiles with different real
credentials for that provider name gets the first profile's resolved
runtime silently served to the second for up to
_RUNTIME_CACHE_TTL_SECONDS (300s).

Add hermes_home to the cache key so each profile resolves and caches
its own runtime. Existing cold-start cache tests updated for the new
key shape; new cross-profile regression test in
tests/test_profile_isolation_runtime.py, alongside the existing
profile-isolation suite for this exact bug class.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/profiles Multi-profile isolation, HERMES_HOME scoping P2 Medium — degraded but workaround exists labels Aug 4, 2026
spfcraze added a commit to spfcraze/hermes-agent that referenced this pull request Aug 9, 2026
The per-message memo in _memoized_resolve_runtime() keyed on config/auth
file (mtime_ns, size) signatures only. A multiplex gateway resolves
multiple profiles' agents in the same OS process (the desktop tui_gateway
switches profiles per request via set_hermes_home_override), and
'hermes profile create --clone-all' copies the profile tree with
mtime-preserving shutil.copy2 — so a cloned profile's config.yaml/auth.json
carry the source's identical (mtime_ns, size) and compute the same memo
signature, letting one profile receive the other's cached api_key/base_url
for up to the 300s TTL.

Add str(get_hermes_home()) to the memo key, the same profile-boundary fix
open PR NousResearch#78185 applies to agent/moa_loop.py's sibling _runtime_cache.

Tests: new tests/gateway/test_runtime_resolve_fastpath_profile_isolation.py
drives _memoized_resolve_runtime under two profile overrides and asserts
each profile gets its own credentials; both tests fail without the fix and
pass with it. Same-profile memo hit preserved (2 invocations, 1 resolve).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint 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.

2 participants