Files
hermes-agent/tests/tools/test_multiplex_tool_memo_scope.py

109 lines
4.6 KiB
Python

"""Under ``gateway.multiplex_profiles`` one process serves every profile; each routed turn runs with
a context-local HERMES_HOME override. Tool-side state resolved once at import, or cached in a
single unkeyed slot, would hand the launch profile's paths/limits to every other profile.
Each test warms the site under profile A, flips the override to profile B with different
config, and asserts B sees its own values (real temp homes, real config.yaml, no mocks).
"""
import json
import pytest
from hermes_constants import reset_hermes_home_override, set_hermes_home_override
@pytest.fixture
def two_profiles(tmp_path, monkeypatch):
prof_a, prof_b = tmp_path / "profA", tmp_path / "profB"
for home, limits in ((prof_a, (222, 33, 3300)), (prof_b, (888, 77, 7700))):
home.mkdir()
max_bytes, timeout, threshold = limits
(home / "config.yaml").write_text(
f"file_read_max_chars: {max_bytes}\ntool_output:\n max_bytes: {max_bytes}\n"
f"browser:\n command_timeout: {timeout}\n snapshot_threshold: {threshold}\n",
encoding="utf-8")
monkeypatch.setenv("HERMES_HOME", str(prof_a))
return prof_a, prof_b
def _under(home, fn):
token = set_hermes_home_override(str(home))
try:
return fn()
finally:
reset_hermes_home_override(token)
def test_checkpoint_and_snapshot_paths_follow_active_profile(two_profiles):
import tools.process_registry as pr
from tools.environments import modal, singularity
prof_a, prof_b = two_profiles
_under(prof_a, pr._checkpoint_path) # warm under A
assert _under(prof_b, pr._checkpoint_path) == prof_b / "processes.json"
assert _under(prof_b, modal._snapshot_store) == prof_b / "modal_snapshots.json"
assert _under(prof_b, singularity._snapshot_store) == prof_b / "singularity_snapshots.json"
assert _under(prof_a, pr._checkpoint_path) == prof_a / "processes.json"
def test_config_caches_are_keyed_by_profile(two_profiles):
import tools.browser_camofox as cam
import tools.browser_tool as bt
import tools.file_tools as ft
import tools.tool_output_limits as tol
from tools.browser_tool_lifecycle import cleanup_all_browsers
prof_a, prof_b = two_profiles
tol._reset_tool_output_limits_cache()
cleanup_all_browsers()
cam._cmd_timeout_resolved, cam._cached_cmd_timeout = False, None
def read_all():
return (tol.get_tool_output_limits()["max_bytes"], ft._get_max_read_chars(),
bt._get_command_timeout(), bt.get_browser_snapshot_threshold(), cam._get_command_timeout())
assert _under(prof_a, read_all) == (222, 222, 33, 3300, 33)
assert _under(prof_b, read_all) == (888, 888, 77, 7700, 77)
# Per-profile slots stay hot — switching back is not a single-slot ping-pong.
assert _under(prof_a, read_all) == (222, 222, 33, 3300, 33)
def test_skill_manage_schema_stable_but_creation_follows_active_profile(two_profiles):
import tools.skill_manager_tool # noqa: F401 (registers skill_manage)
from tools.registry import registry
prof_a, prof_b = two_profiles
for home, create_dir in ((prof_a, "brain-a"), (prof_b, "brain-b")):
with (home / "config.yaml").open("a", encoding="utf-8") as config:
config.write(f"skills:\n create_dir: {create_dir}\n")
def definition():
return json.dumps(registry.get_definitions({"skill_manage"}, quiet=True))
for_a = _under(prof_a, definition)
for_b = _under(prof_b, definition)
assert for_a == for_b == _under(prof_a, definition)
assert "brain-a" not in for_a and "brain-b" not in for_a
assert "skills.create_dir" in for_a
for home, create_dir, other, name in ((prof_a, "brain-a", prof_b, "first-a"),
(prof_b, "brain-b", prof_a, "only-b"),
(prof_a, "brain-a", prof_b, "second-a")):
content = (f"---\nname: {name}\n"
"description: Use when checking routing. Create in the active profile.\n"
"---\n\n# Scoped skill\n")
def create():
raw = registry.dispatch("skill_manage", {
"operations": [{"action": "create", "name": name, "content": content}]})
assert isinstance(raw, str)
return json.loads(raw)
assert not (other / "brain-a" / name / "SKILL.md").exists()
assert not (other / "brain-b" / name / "SKILL.md").exists()
result = _under(home, create)
assert result["success"], result
assert (home / create_dir / name / "SKILL.md").read_text(encoding="utf-8") == content
assert not (home / "skills" / name).exists()