Skip to content

fix(telegram): expose inbound media as tool-accessible attachments (#20899) - #20906

Closed
tymrtn wants to merge 1 commit into
NousResearch:mainfrom
tymrtn:fix/20899-telegram-inbound-media-tool-access
Closed

fix(telegram): expose inbound media as tool-accessible attachments (#20899)#20906
tymrtn wants to merge 1 commit into
NousResearch:mainfrom
tymrtn:fix/20899-telegram-inbound-media-tool-access

Conversation

@tymrtn

@tymrtn tymrtn commented May 6, 2026

Copy link
Copy Markdown
Contributor

Closes #20899.

Problem

Telegram inbound photos/documents were visible to the model via the vision pipeline but had no tool-accessible local path. The agent could analyse an image but could not copy or move it on user request without asking the user to resend.

Fix

Mirror inbound photos and documents into a profile-scoped, chat-scoped attachment cache, and surface the cached path to the agent's user-message context so file/terminal tools can act on it.

Cache layout

<hermes_home>/cache/attachments/<platform>/<chat_id>/<message_id>/<filename>

Path-traversal-sanitised; per-chat directories prevent cross-chat reads; collisions are disambiguated by suffix.

Code changes

  • gateway/platforms/base.py: new cache_inbound_attachment() helper, MessageEvent.attachments: list[dict] field with {path, filename, mime_type, size, platform, message_id, chat_id}.
  • gateway/platforms/telegram.py: _handle_media_message writes through to the attachment cache (largest PhotoSize for photos, original filename for documents) and merges attachments into single-message and album/media-group events.
  • gateway/run.py: prepends a structured [Inbound attachments cached locally — use file/terminal tools to read, copy, or move them] block listing each cached file as - filename (mime, size): /abs/path before the existing document-context injection.

Tests

tests/gateway/test_telegram_attachments.py (6 tests, all passing):

  • cache layout (per-chat, per-message)
  • path-traversal sanitisation
  • photo inbound → metadata populated, file written, largest PhotoSize selected
  • document inbound → original filename preserved
  • cross-chat isolation (same message_id in different chats land in distinct directories)
  • collision disambiguation

Local regression scope: tests/gateway/test_telegram_*.py → 147/147 passing.

Notes

Builds on the existing cache_image_from_bytes / cache_document_from_bytes vision-route caches. The new attachment cache is structurally distinct so cross-chat isolation is preserved at the path layer, not just in field naming.

Retention/gc is left as future work; existing media is not deleted automatically.

…ousResearch#20899)

Closes NousResearch#20899.

Telegram inbound photos and documents are now downloaded into a
profile-scoped, chat-scoped attachment cache and surfaced to the agent
as tool-readable local file paths. The agent can then use file/terminal
tools to copy, move, or otherwise organise user-provided media without
asking the user to resend.

Changes:
- gateway/platforms/base.py: add cache_inbound_attachment helper +
  MessageEvent.attachments field. Cache layout:
  <hermes_home>/cache/attachments/<platform>/<chat_id>/<message_id>/<filename>
  Path traversal is sanitised; collisions are disambiguated.
- gateway/platforms/telegram.py: _handle_media_message mirrors photos
  (largest PhotoSize) and documents (original filename preserved) into
  the attachment cache and populates event.attachments alongside the
  existing vision-route media_urls.
- gateway/run.py: when event.attachments is present, prepend a
  structured 'Inbound attachments cached locally' block listing each
  attachment as 'filename (mime, size): /abs/path'. The agent now sees
  a tool-accessible path even when the image is also routed via native
  vision.
- tests/gateway/test_telegram_attachments.py: 6 new tests covering
  cache layout, path-traversal protection, photo/document inbound,
  cross-chat isolation, and PhotoSize selection.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the careful attachment-cache implementation. The tool-accessible native-image path behavior is already present on current main.

Automated hermes-sweeper review evidence:

  • plugins/platforms/telegram/adapter.py:7799-7801 caches inbound Telegram photos and records the local path in event.media_urls.
  • gateway/run.py:18749-18753 passes buffered native image paths to build_native_content_parts().
  • agent/image_routing.py:747-750 adds [Image attached at: <path>] to the model-visible text part for each successfully attached local image.
  • tests/agent/test_image_routing.py:373-382 verifies that the text part contains the local path while the image remains attached natively.
  • This shipped in 769ee86cd2b346f6bffedd84ca9067fde2790eeb, included in v2026.5.29.

The linked #20971 discussion identified this narrower shared resolution path. This PR's duplicate cache/event-field implementation also targets files that have since moved to plugins/platforms/telegram/adapter.py, so there is no remaining behavior here to salvage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram inbound images are visible to model but not exposed as tool-accessible attachments

3 participants