Four bundled plugins wrapped get_hermes_home() in a try/except that fell back
to ~/.hermes on ImportError (a2a/protocol._hermes_home, photon/auth
._auth_json_path, google_chat adapter inline, openviking done in the previous
commit). A bundled plugin cannot lose hermes_constants -- each already imports
gateway.* / agent.* from the same tree -- so the fallback was dead code that
was also wrong on Windows (%LOCALAPPDATA%/hermes) and under a profile override.
telegram's gmail-triage verb path used Path.home()/".hermes" outright, ignoring
profiles. mem0/_oss_providers baked os.path.expanduser("~/.hermes/mem0_qdrant")
into VECTOR_PROVIDERS at import time, so the Qdrant default landed in the
user's ~/.hermes for every profile; the default is now a lazy callable resolved
by vector_default_config(provider_id) at setup time. a2a/security.py only
changes its import (it borrowed protocol._hermes_home).
Behavior change: on Windows and under profiles these paths now follow the
active HERMES_HOME (they were previously anchored to ~/.hermes in the impossible
fallback / at import time); the default-profile POSIX layout is unchanged.
Test: tests/plugins/test_plugin_paths_follow_profile.py asserts each resolver
(a2a conversations, photon auth.json, mem0 qdrant default, openviking log)
lands inside a HERMES_HOME ContextVar override; sabotage red for the mem0
import-time constant and the photon ~/.hermes fallback.
50 lines
1.7 KiB
Python
50 lines
1.7 KiB
Python
"""Plugin data paths follow the active profile's HERMES_HOME, including the ContextVar override.
|
|
|
|
Several plugins carried a ``~/.hermes`` fallback (guarding an ImportError of ``hermes_constants``
|
|
that cannot happen for a bundled plugin) or resolved the home at import time. Both are wrong on
|
|
Windows and under multiplex profile overrides. Every resolver below must land inside the override.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from hermes_constants import reset_hermes_home_override, set_hermes_home_override
|
|
|
|
|
|
def _a2a_conversation(home):
|
|
from plugins.platforms.a2a import protocol
|
|
return protocol._conv_path("peer-x")
|
|
|
|
|
|
def _photon_auth(home):
|
|
from plugins.platforms.photon import auth
|
|
return auth._auth_json_path()
|
|
|
|
|
|
def _mem0_qdrant(home):
|
|
from plugins.memory.mem0._oss_providers import vector_default_config
|
|
return vector_default_config("qdrant")["path"]
|
|
|
|
|
|
def _openviking_log(home):
|
|
import plugins.memory.openviking as ov
|
|
return ov.get_hermes_home() / ov._OPENVIKING_SERVER_LOG_RELATIVE_PATH
|
|
|
|
|
|
_RESOLVERS = {"a2a": _a2a_conversation, "photon": _photon_auth, "mem0-qdrant": _mem0_qdrant,
|
|
"openviking": _openviking_log}
|
|
|
|
|
|
@pytest.mark.parametrize("name", sorted(_RESOLVERS))
|
|
def test_plugin_path_follows_profile_override(name, tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "default"))
|
|
monkeypatch.setenv("HOME", str(tmp_path / "user-home"))
|
|
profile = tmp_path / "profiles" / "b"
|
|
profile.mkdir(parents=True)
|
|
token = set_hermes_home_override(profile)
|
|
try:
|
|
resolved = str(_RESOLVERS[name](profile))
|
|
finally:
|
|
reset_hermes_home_override(token)
|
|
assert resolved.startswith(str(profile)), f"{name} resolved {resolved!r} outside the active profile"
|