hermes - ✅(Solved) Fix [Bug]: Inbound document host paths not translated to container paths under Docker backend [2 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#18787Fetched 2026-05-03 04:54:20
View on GitHub
Comments
2
Participants
2
Timeline
8
Reactions
0
Author
Participants
Timeline (top)
labeled ×4commented ×2cross-referenced ×2

Fix Action

Fixed

PR fix notes

PR #18794: fix(gateway): translate document cache paths for docker

Description (problem / solution / changelog)

Closes #18787

Summary

  • add to_agent_visible_cache_path() to map host-side Hermes cache files to their sandbox mount paths
  • use the translated path in gateway document prompt context when terminal.backend is docker
  • cover document, nested cache, and non-cache path behavior in credential file tests

Test Plan

  • python3 -m pytest tests/tools/test_credential_files.py -q -o 'addopts='
  • python3 -m py_compile tools/credential_files.py gateway/run.py

Note: scripts/run_tests.sh tests/tools/test_credential_files.py -q could not run because this checkout has no .venv or venv; used the documented system python3 fallback.

Changed files

  • gateway/run.py (modified, +14/-2)
  • tests/tools/test_credential_files.py (modified, +39/-0)
  • tools/credential_files.py (modified, +27/-0)

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

Description (problem / solution / changelog)

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

Changed files

  • gateway/run.py (modified, +8/-2)
  • tools/credential_files.py (modified, +30/-0)

Code Example

if event.media_urls and event.message_type == MessageType.DOCUMENT:
    ...
    for i, path in enumerate(event.media_urls):
        ...
        context_note = (
            f"[The user sent a document: '{display_name}'. "
            f"The file is saved at: {path}. "          # ← host path leaks here
            f"Ask the user what they'd like you to do with it.]"
        )

---

cached_path = cache_document_from_bytes(raw_bytes, original_filename or f"document{ext}")
event.media_urls = [cached_path]   # cached_path is the host path

---

for cache_mount in get_cache_directory_mounts():
    volume_args.extend([
        "-v",
        f"{cache_mount['host_path']}:{cache_mount['container_path']}:ro",
    ])

---

# tools/credential_files.py — add inverse helper next to get_cache_directory_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's not under any auto-mounted cache dir."""
    for mount in get_cache_directory_mounts(container_base=container_base):
        if host_path.startswith(mount["host_path"]):
            return mount["container_path"] + host_path[len(mount["host_path"]):]
    return host_path
RAW_BUFFERClick to expand / collapse

Bug Description

When terminal.backend: docker is enabled, inbound documents uploaded via a messaging platform (Telegram, Slack, Discord, Feishu, Email, etc.) are cached at a host path under ~/.hermes/cache/documents/. The gateway then injects that host path verbatim into the agent's prompt. Since the agent runs inside the Docker sandbox, it cannot resolve the host path and document open attempts fail.

The cache directory is auto-mounted read-only into the container at /root/.hermes/cache/documents by tools/credential_files.py::get_cache_directory_mounts() (introduced in #4846). The file is reachable — just at a different path than the one the prompt advertises.

Steps to Reproduce

  1. Configure the gateway with terminal.backend: docker.
  2. From Telegram (or any supported messaging platform), upload a binary document — e.g. a .docx, .pdf, .xlsx. Text formats (.md, .txt ≤100KB) hit a different code path that injects content directly and aren't affected.
  3. Ask the agent to read the file.

Expected Behavior

The agent receives a path it can open from inside its sandbox — the in-container mount path (/root/.hermes/cache/documents/<file>).

Actual Behavior

The agent's prompt contains the host path (/home/<user>/.hermes/cache/documents/<file>), which doesn't exist inside the container. The agent fails to open the file. Sample agent reply:

The file path the platform handed me (/home/hermes/.hermes/cache/documents/...) isn't reachable from inside my container — that's a host path that's not bind-mounted in. So no, I can't actually open it from here.

Diagnosis

Where the host path is injected into the agent prompt (gateway/run.py:5236-5269):

if event.media_urls and event.message_type == MessageType.DOCUMENT:
    ...
    for i, path in enumerate(event.media_urls):
        ...
        context_note = (
            f"[The user sent a document: '{display_name}'. "
            f"The file is saved at: {path}. "          # ← host path leaks here
            f"Ask the user what they'd like you to do with it.]"
        )

Where the path was originally set (per-platform; example gateway/platforms/telegram.py:3171-3175):

cached_path = cache_document_from_bytes(raw_bytes, original_filename or f"document{ext}")
event.media_urls = [cached_path]   # cached_path is the host path

The same pattern exists in every platform adapter that calls cache_document_from_bytesgateway/platforms/feishu.py:3273, gateway/platforms/email.py:434, and similar in discord.py, slack.py, whatsapp.py, matrix.py, dingtalk.py, qqbot.py.

Where the cache is mounted into the sandbox (tools/environments/docker.py:439-444):

for cache_mount in get_cache_directory_mounts():
    volume_args.extend([
        "-v",
        f"{cache_mount['host_path']}:{cache_mount['container_path']}:ro",
    ])

get_cache_directory_mounts() (tools/credential_files.py:353) defaults container_base="/root/.hermes", so <HERMES_HOME>/cache/documents mounts at /root/.hermes/cache/documents.

The auto-mount mechanism added in #4846 was designed for exactly this scenario — the PR's stated intent: "make the host cache directories accessible inside remote containers so the agent can use standard terminal commands on any cached file." The mount is in place; only the prompt-side path translation is missing. Issue #6004 (open) tracks an adjacent gap for clipboard images.

Proposed Fix

A small helper at the agent prompt boundary that translates host cache paths to their in-sandbox equivalents, gated on backend == "docker" for now (other backends — Modal, Daytona, Vercel — have different mount semantics and are out of scope until verified):

# tools/credential_files.py — add inverse helper next to get_cache_directory_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's not under any auto-mounted cache dir."""
    for mount in get_cache_directory_mounts(container_base=container_base):
        if host_path.startswith(mount["host_path"]):
            return mount["container_path"] + host_path[len(mount["host_path"]):]
    return host_path

Call site: gateway/run.py:5253, only inside the document-context-injection block — not for image/audio paths in the same function, which are consumed gateway-side (vision attachment, Whisper STT) and need the host path.

The translation must be scoped narrowly to text the agent reads and uses to open files. This mirrors the inverse problem already being solved in tools/vision_tools.py in PR #14990 (_resolve_sandbox_path_to_host).

Open questions for triage

  1. Scope. Restrict to backend == "docker" for now (safe), or generalize to all backends that use get_cache_directory_mounts()? Modal does an iter_cache_files() initial-mount + resync which may or may not match the same container path layout. Happy to verify and generalize if preferred.
  2. Helper location. tools/credential_files.py (next to its inverse) vs. gateway/platforms/base.py (next to DOCUMENT_CACHE_DIR)? My preference is the former for cohesion with get_cache_directory_mounts.
  3. Translation site. Rewrite event.media_urls at platform-layer assignment (touches every adapter, but the agent always sees a usable path)? Or rewrite at the single prompt-injection site (narrower blast radius — preferred)? The platform-layer approach also risks breaking gateway-side image/audio consumers that need the host path.
  4. Cross-platform. The startswith comparison is fine on Linux/macOS but may misbehave on Windows hosts (case-insensitive paths, backslash separators). Path.resolve() + relative_to() is more robust but heavier. Preference?

Affected Component

  • Gateway (any messaging platform calling cache_document_from_bytes)
  • Backend: Docker

Environment

  • Hermes Agent: v0.12.0 (commit 20132435c)
  • OS: Debian 12
  • Python: 3.11.2
  • Terminal backend: Docker, image nikolaik/python-nodejs:python3.11-nodejs20
  • Messaging platform reproduced on: Telegram

Debug Report

Intentionally not attaching hermes debug share output — the bug reproduces from a default Docker-backend setup and the file/line trace above pinpoints the exact code path without needing session data. Happy to provide a debug share if useful.


Happy to submit a PR. Would appreciate triage direction on the open questions above before writing it.

extent analysis

TL;DR

The issue can be fixed by translating the host cache path to its in-sandbox equivalent using a helper function, such as to_agent_visible_cache_path, when the terminal.backend is set to docker.

Guidance

  • Identify the location where the host path is injected into the agent prompt and replace it with the translated path using the to_agent_visible_cache_path function.
  • Consider the scope of the fix and whether it should be restricted to backend == "docker" or generalized to other backends.
  • Determine the best location for the to_agent_visible_cache_path helper function, either in tools/credential_files.py or gateway/platforms/base.py.
  • Decide whether to rewrite event.media_urls at the platform-layer assignment or at the prompt-injection site.

Example

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's not under any auto-mounted cache dir."""
    for mount in get_cache_directory_mounts(container_base=container_base):
        if host_path.startswith(mount["host_path"]):
            return mount["container_path"] + host_path[len(mount["host_path"]):]
    return host_path

# Call site: gateway/run.py:5253
context_note = (
    f"[The user sent a document: '{display_name}'. "
    f"The file is saved at: {to_agent_visible_cache_path(path)}. "  
    f"Ask the user what they'd like you to do with it.]"
)

Notes

The fix assumes that the get_cache_directory_mounts function is correctly configured and that the cache directory is properly mounted into the sandbox. Additionally, the fix may need to be adapted for Windows hosts due to case-insensitive paths and backslash separators

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Bug]: Inbound document host paths not translated to container paths under Docker backend [2 pull requests, 2 comments, 2 participants]