mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(gateway): resolve terminal.cwd placeholders per backend and mount mode
Split placeholder TERMINAL_CWD resolution into three cases: local falls back to MESSAGING_CWD/home; docker without workspace mount leaves cwd unset; docker with mount enabled preserves an explicit host MESSAGING_CWD path for terminal_tool's /workspace mapping. Stops leaking host Path.home() into containers without breaking the mount contract. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
ac0b4a225b
commit
1cc68fc897
2 changed files with 67 additions and 7 deletions
49
gateway/cwd_placeholder.py
Normal file
49
gateway/cwd_placeholder.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
"""Resolve gateway ``terminal.cwd`` placeholder values to ``TERMINAL_CWD``.
|
||||
|
||||
When ``terminal.cwd`` is unset or a placeholder (``.``, ``auto``, ``cwd``),
|
||||
the gateway must not blindly map host ``Path.home()`` into container backends.
|
||||
Docker with workspace mounting still needs an explicit host path signal
|
||||
(``MESSAGING_CWD`` or an absolute config path) for ``terminal_tool`` to map
|
||||
``/host/project`` → ``/workspace``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
CWD_PLACEHOLDERS = frozenset({".", "auto", "cwd"})
|
||||
|
||||
|
||||
def _truthy_env(value: str | None) -> bool:
|
||||
return (value or "").strip().lower() in {"true", "1", "yes"}
|
||||
|
||||
|
||||
def resolve_placeholder_terminal_cwd(
|
||||
*,
|
||||
configured_cwd: str,
|
||||
terminal_backend: str,
|
||||
messaging_cwd: str | None,
|
||||
docker_mount_cwd_to_workspace: bool,
|
||||
home_fallback: str,
|
||||
) -> str | None:
|
||||
"""Return the ``TERMINAL_CWD`` value to set, or ``None`` to leave it unset.
|
||||
|
||||
Cases:
|
||||
- **local** + placeholder → ``MESSAGING_CWD`` or ``home_fallback``
|
||||
- **docker** + placeholder + mount on + host ``MESSAGING_CWD`` → host path
|
||||
(for ``terminal_tool`` ``/workspace`` mapping)
|
||||
- **docker** + placeholder + mount off → ``None`` (sandbox default)
|
||||
- other non-local backends + placeholder → ``None``
|
||||
"""
|
||||
if configured_cwd and configured_cwd not in CWD_PLACEHOLDERS:
|
||||
return configured_cwd
|
||||
|
||||
backend = (terminal_backend or "local").strip().lower()
|
||||
if backend == "local":
|
||||
messaging = (messaging_cwd or "").strip()
|
||||
return messaging or home_fallback
|
||||
|
||||
if backend == "docker" and docker_mount_cwd_to_workspace:
|
||||
messaging = (messaging_cwd or "").strip()
|
||||
if messaging and messaging not in CWD_PLACEHOLDERS:
|
||||
return messaging
|
||||
|
||||
return None
|
||||
|
|
@ -1660,16 +1660,27 @@ os.environ["HERMES_EXEC_ASK"] = "1"
|
|||
|
||||
# Set terminal working directory for messaging platforms.
|
||||
# config.yaml terminal.cwd is the canonical source (bridged to TERMINAL_CWD
|
||||
# by the config bridge above). When it's unset or a placeholder, default
|
||||
# to home directory. MESSAGING_CWD is accepted as a backward-compat
|
||||
# fallback (deprecated — the warning above tells users to migrate).
|
||||
# by the config bridge above). Placeholder values are resolved per-backend —
|
||||
# see gateway/cwd_placeholder.py for the three-case contract (local vs docker
|
||||
# mount-off vs docker mount-on). MESSAGING_CWD is a backward-compat fallback.
|
||||
from gateway.cwd_placeholder import CWD_PLACEHOLDERS, resolve_placeholder_terminal_cwd
|
||||
|
||||
_configured_cwd = os.environ.get("TERMINAL_CWD", "")
|
||||
if not _configured_cwd or _configured_cwd in {".", "auto", "cwd"}:
|
||||
if os.environ.get("TERMINAL_ENV", "").strip().lower() == "ssh":
|
||||
if not _configured_cwd or _configured_cwd in CWD_PLACEHOLDERS:
|
||||
_resolved_cwd = resolve_placeholder_terminal_cwd(
|
||||
configured_cwd=_configured_cwd,
|
||||
terminal_backend=os.environ.get("TERMINAL_ENV", ""),
|
||||
messaging_cwd=os.getenv("MESSAGING_CWD"),
|
||||
docker_mount_cwd_to_workspace=os.getenv(
|
||||
"TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE", "false"
|
||||
).lower()
|
||||
in {"true", "1", "yes"},
|
||||
home_fallback=str(Path.home()),
|
||||
)
|
||||
if _resolved_cwd is None:
|
||||
os.environ.pop("TERMINAL_CWD", None)
|
||||
else:
|
||||
_fallback = os.getenv("MESSAGING_CWD") or str(Path.home())
|
||||
os.environ["TERMINAL_CWD"] = _fallback
|
||||
os.environ["TERMINAL_CWD"] = _resolved_cwd
|
||||
|
||||
from gateway.config import (
|
||||
ChannelOverride,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue