diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index 51a2fa8..4cd9a2c 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -230,6 +230,18 @@ except Exception: # identity to its sub-agents. Drop only per-session runtime markers - user # configuration such as CLAUDE_PROJECT_DIR or CLAUDE_CONFIG_DIR still # inherits. The family ids mirror what orc_onboard.sh auto-detects. + # + # ISSUE-45: the server restores its saved layout on start and would + # relaunch every saved agent with herdr's own resume command (no permission + # flags, deleted agents included). It gets HERDR_CONFIG_PATH = the user's + # herdr config with [session] resume_agents_on_restore = false, rebuilt on + # every start (lib_py/herdr_config.py). Without that file the server still + # starts - with restore on, as before. + MAM_SERVER_CFG=$(python3 -m lib_py.herdr_config server-config --fallback-dir "$_SHIM_DIR/.." || true) + export MAM_SERVER_CFG + if [ -z "$MAM_SERVER_CFG" ]; then + echo "WARNING: restore-on-restart stays enabled for herdr session $_MAM_SESSION (no server config)" >&2 + fi _mam_server_pid=$(python3 -c ' import os, subprocess, sys deny = ("CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION", "CLAUDE_CODE_SESSION_ID", @@ -237,8 +249,10 @@ deny = ("CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION", "CLAUDE_CODE_SESSION_ID", "CLAUDE_CODE_MESSAGING_TOKEN", "CLAUDE_CODE_MESSAGING_SOCKET", "CLAUDE_CODE_BRIDGE_SESSION_ID", "ANTIGRAVITY_CONVERSATION_ID", "HERMES_SESSION_ID", "GROK_SESSION_ID", - "OPENCODE_SESSION_ID") + "OPENCODE_SESSION_ID", "MAM_SERVER_CFG") env = {k: v for k, v in os.environ.items() if k not in deny} +if os.environ.get("MAM_SERVER_CFG"): + env["HERDR_CONFIG_PATH"] = os.environ["MAM_SERVER_CFG"] p = subprocess.Popen(sys.argv[1:], start_new_session=True, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) diff --git a/tests/conftest.py b/tests/conftest.py index ab7560b..c4f47e3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -236,6 +236,11 @@ def mam_sandbox(tmp_path, monkeypatch, request): monkeypatch.delenv("HERDR_SESSION_NAME", raising=False) monkeypatch.delenv("HERDR_SERVER_NAME", raising=False) monkeypatch.delenv("HERDR_WORKSPACE_ID", raising=False) + # Issue #45: the shim bootstrap writes a herdr server config next to the + # user's; with these unset that is $HOME/.config/herdr, inside the sandbox. + # Every pane of a MAM server inherits HERDR_CONFIG_PATH. + monkeypatch.delenv("HERDR_CONFIG_PATH", raising=False) + monkeypatch.delenv("XDG_CONFIG_HOME", raising=False) import sys monkeypatch.setenv("AGENT_PYTHON_BIN", sys.executable) diff --git a/tests/test_issue9_server_env.py b/tests/test_issue9_server_env.py index 500dfb2..9edca48 100644 --- a/tests/test_issue9_server_env.py +++ b/tests/test_issue9_server_env.py @@ -150,6 +150,27 @@ def test_bootstrap_strips_agent_runtime_markers(mam_sandbox): assert key in server_env +def test_bootstrap_hands_the_server_a_no_restore_config(mam_sandbox): + """Issue #45: the server starts with HERDR_CONFIG_PATH = the user's herdr + config plus [session] resume_agents_on_restore = false (that herdr obeys it + is test_issue45_restore.py, on a real server).""" + import tomllib + env = _install_fake_herdr(mam_sandbox) + user_cfg = mam_sandbox / ".config" / "herdr" / "config.toml" + user_cfg.parent.mkdir(parents=True) + user_cfg.write_text("[ui]\nconfirm_close = false\n") + + res = _run_through_shim(mam_sandbox, env, "issue45-probe") + assert res.returncode == 0, res.stderr + assert "restore-on-restart stays enabled" not in res.stderr, res.stderr + + server_env = json.loads(Path(env["FAKE_HERDR_ENV_DUMP"]).read_text()) + assert server_env.get("HERDR_CONFIG_PATH") == str(user_cfg.parent / "mam-server-config.toml") + assert "MAM_SERVER_CFG" not in server_env + cfg = tomllib.loads(Path(server_env["HERDR_CONFIG_PATH"]).read_text()) + assert cfg == {"ui": {"confirm_close": False}, "session": {"resume_agents_on_restore": False}} + + def test_no_autostart_skips_bootstrap_but_still_routes_the_call(mam_sandbox): env = _install_fake_herdr(mam_sandbox) env["MAM_NO_AUTOSTART"] = "1"