fix: enforce config.yaml as sole CWD source + deprecate .env CWD vars + add hermes memory reset (#11029)
config.yaml terminal.cwd is now the single source of truth for working directory. MESSAGING_CWD and TERMINAL_CWD in .env are deprecated with a migration warning. Changes: 1. config.py: Remove MESSAGING_CWD from OPTIONAL_ENV_VARS (setup wizard no longer prompts for it). Add warn_deprecated_cwd_env_vars() that prints a migration hint when deprecated env vars are detected. 2. gateway/run.py: Replace all MESSAGING_CWD reads with TERMINAL_CWD (which is bridged from config.yaml terminal.cwd). MESSAGING_CWD is still accepted as a backward-compat fallback with deprecation warning. Config bridge skips cwd placeholder values so they don't clobber the resolved TERMINAL_CWD. 3. cli.py: Guard against lazy-import clobbering — when cli.py is imported lazily during gateway runtime (via delegate_tool), don't let load_cli_config() overwrite an already-resolved TERMINAL_CWD with os.getcwd() of the service's working directory. (#10817) 4. hermes_cli/main.py: Add 'hermes memory reset' command with --target all/memory/user and --yes flags. Profile-scoped via HERMES_HOME. Migration path for users with .env settings: Remove MESSAGING_CWD / TERMINAL_CWD from .env Add to config.yaml: terminal: cwd: /your/project/path Addresses: #10225, #4672, #10817, #7663
This commit is contained in:
@@ -37,6 +37,10 @@ def _simulate_config_bridge(cfg: dict, initial_env: dict | None = None):
|
||||
for cfg_key, env_var in terminal_env_map.items():
|
||||
if cfg_key in terminal_cfg:
|
||||
val = terminal_cfg[cfg_key]
|
||||
# Skip cwd placeholder values — don't overwrite already-resolved
|
||||
# TERMINAL_CWD. Mirrors the fix in gateway/run.py.
|
||||
if cfg_key == "cwd" and str(val) in (".", "auto", "cwd"):
|
||||
continue
|
||||
if isinstance(val, list):
|
||||
env[env_var] = json.dumps(val)
|
||||
else:
|
||||
@@ -146,3 +150,58 @@ class TestTopLevelCwdAlias:
|
||||
cfg = {"cwd": "/from/config"}
|
||||
result = _simulate_config_bridge(cfg, {"MESSAGING_CWD": "/from/env"})
|
||||
assert result["TERMINAL_CWD"] == "/from/config"
|
||||
|
||||
|
||||
class TestNestedTerminalCwdPlaceholderSkip:
|
||||
"""terminal.cwd placeholder values must not clobber TERMINAL_CWD.
|
||||
|
||||
When config.yaml has terminal.cwd: "." (or "auto"/"cwd"), the gateway
|
||||
config bridge should NOT write that placeholder to TERMINAL_CWD.
|
||||
This prevents .env or MESSAGING_CWD values from being overwritten.
|
||||
See issues #10225, #4672, #10817.
|
||||
"""
|
||||
|
||||
def test_terminal_dot_cwd_does_not_clobber_env(self):
|
||||
"""terminal.cwd: '.' should not overwrite a pre-set TERMINAL_CWD."""
|
||||
cfg = {"terminal": {"cwd": "."}}
|
||||
result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/my/project"})
|
||||
assert result["TERMINAL_CWD"] == "/my/project"
|
||||
|
||||
def test_terminal_auto_cwd_does_not_clobber_env(self):
|
||||
cfg = {"terminal": {"cwd": "auto"}}
|
||||
result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/my/project"})
|
||||
assert result["TERMINAL_CWD"] == "/my/project"
|
||||
|
||||
def test_terminal_cwd_keyword_does_not_clobber_env(self):
|
||||
cfg = {"terminal": {"cwd": "cwd"}}
|
||||
result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/my/project"})
|
||||
assert result["TERMINAL_CWD"] == "/my/project"
|
||||
|
||||
def test_terminal_explicit_cwd_does_override(self):
|
||||
"""terminal.cwd: '/explicit/path' SHOULD override TERMINAL_CWD."""
|
||||
cfg = {"terminal": {"cwd": "/explicit/path"}}
|
||||
result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/old/value"})
|
||||
assert result["TERMINAL_CWD"] == "/explicit/path"
|
||||
|
||||
def test_terminal_dot_cwd_falls_back_to_messaging_cwd(self):
|
||||
"""terminal.cwd: '.' with no TERMINAL_CWD should fall to MESSAGING_CWD."""
|
||||
cfg = {"terminal": {"cwd": "."}}
|
||||
result = _simulate_config_bridge(cfg, {"MESSAGING_CWD": "/from/env"})
|
||||
assert result["TERMINAL_CWD"] == "/from/env"
|
||||
|
||||
def test_terminal_dot_cwd_and_messaging_cwd_both_set(self):
|
||||
"""Pre-set TERMINAL_CWD from .env wins over terminal.cwd: '.'."""
|
||||
cfg = {"terminal": {"cwd": ".", "backend": "local"}}
|
||||
result = _simulate_config_bridge(cfg, {
|
||||
"TERMINAL_CWD": "/my/project",
|
||||
"MESSAGING_CWD": "/fallback",
|
||||
})
|
||||
assert result["TERMINAL_CWD"] == "/my/project"
|
||||
|
||||
def test_non_cwd_terminal_keys_still_bridge(self):
|
||||
"""Other terminal config keys (backend, timeout) should still bridge normally."""
|
||||
cfg = {"terminal": {"cwd": ".", "backend": "docker", "timeout": "300"}}
|
||||
result = _simulate_config_bridge(cfg, {"MESSAGING_CWD": "/from/env"})
|
||||
assert result["TERMINAL_ENV"] == "docker"
|
||||
assert result["TERMINAL_TIMEOUT"] == "300"
|
||||
assert result["TERMINAL_CWD"] == "/from/env"
|
||||
|
||||
Reference in New Issue
Block a user