fix(gateway): translate inbound document host paths to container paths for Docker backend

When terminal.backend is docker, inbound documents uploaded via messaging
platforms (Telegram, Slack, Discord, Feishu, Email, etc.) are cached at a host
path under ~/.hermes/cache/documents, but the container sandbox only sees them
at the auto-mounted /root/.hermes/cache/documents path.

This PR adds to_agent_visible_cache_path() in tools/credential_files.py (the
natural sibling to get_cache_directory_mounts()) and calls it at the
document-context-injection site in gateway/run.py so the agent always receives
a path it can open directly, matching the mount layout already established
by get_cache_directory_mounts() (#4846).

Scope: only Docker backend for now; other backends use different mount
semantics and are left unchanged until verified.

Fixes #18787
This commit is contained in:
ambition0802 2026-05-03 07:54:31 +08:00 committed by Teknium
parent d4de7d4179
commit 7c0766e06a
2 changed files with 36 additions and 2 deletions

View file

@ -5757,6 +5757,7 @@ class GatewayRunner:
if event.media_urls and event.message_type == MessageType.DOCUMENT:
import mimetypes as _mimetypes
from tools.credential_files import to_agent_visible_cache_path
_TEXT_EXTENSIONS = {".txt", ".md", ".csv", ".log", ".json", ".xml", ".yaml", ".yml", ".toml", ".ini", ".cfg"}
for i, path in enumerate(event.media_urls):
@ -5777,16 +5778,21 @@ class GatewayRunner:
display_name = parts[2] if len(parts) >= 3 else basename
display_name = re.sub(r'[^\w.\- ]', '_', display_name)
# Translate host cache path to in-container path if running under Docker backend.
# This ensures the agent receives a path it can open inside its sandbox, as the
# cache directories are auto-mounted at /root/.hermes/cache/* by get_cache_directory_mounts().
agent_path = to_agent_visible_cache_path(path)
if mtype.startswith("text/"):
context_note = (
f"[The user sent a text document: '{display_name}'. "
f"Its content has been included below. "
f"The file is also saved at: {path}]"
f"The file is also saved at: {agent_path}]"
)
else:
context_note = (
f"[The user sent a document: '{display_name}'. "
f"The file is saved at: {path}. "
f"The file is saved at: {agent_path}. "
f"Ask the user what they'd like you to do with it.]"
)
message_text = f"{context_note}\n\n{message_text}"

View file

@ -374,6 +374,34 @@ def get_cache_directory_mounts(
return mounts
def to_agent_visible_cache_path(
host_path: str,
container_base: str = "/root/.hermes",
) -> str:
"""Translate a host cache path to its mounted path inside the sandbox.
Returns the input unchanged if it is not under any auto-mounted cache
directory, or if the active terminal backend does not require path
translation (only Docker for now).
"""
# Only Docker backend requires translation at this time. Other backends
# (Modal, Daytona, Vercel) use different mount semantics and will be
# addressed separately if needed. Backend is identified by TERMINAL_ENV
# (same env var tools/terminal_tool.py reads in _get_environment_config).
if os.environ.get("TERMINAL_ENV", "local") != "docker":
return host_path
path = Path(host_path)
for mount in get_cache_directory_mounts(container_base=container_base):
host_dir = Path(mount["host_path"])
try:
rel = path.relative_to(host_dir)
return str(Path(mount["container_path"]) / rel)
except ValueError:
continue
return host_path
def iter_cache_files(
container_base: str = "/root/.hermes",
) -> List[Dict[str, str]]: