diff --git a/gateway/run.py b/gateway/run.py index 15ce3ab08c..4f58aeee97 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -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}" diff --git a/tools/credential_files.py b/tools/credential_files.py index 2372950cfe..9026c67916 100644 --- a/tools/credential_files.py +++ b/tools/credential_files.py @@ -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]]: