Skip to content

fix(gateway): report skipped media attachments instead of silently dropping them#32054

Open
jrockwar wants to merge 1 commit into
NousResearch:mainfrom
jrockwar:fix/notify-skipped-media-attachments
Open

fix(gateway): report skipped media attachments instead of silently dropping them#32054
jrockwar wants to merge 1 commit into
NousResearch:mainfrom
jrockwar:fix/notify-skipped-media-attachments

Conversation

@jrockwar

@jrockwar jrockwar commented May 25, 2026

Copy link
Copy Markdown

Bug Report

Discord (and all platform) media attachments are silently dropped when outside MEDIA_DELIVERY_SAFE_ROOTS.

Symptom

When the model emits MEDIA:<path> inline directives, image/file attachments never appear in Discord (or other platforms). Text-only messages arrive fine. No error is surfaced to the user.

Root Cause

BasePlatformAdapter.filter_media_delivery_paths() validates every path against allowed roots. When a file lives outside the whitelist, the gateway logs a generic WARNING with no path name, and silently drops the attachment with zero user-visible feedback.

Current Allowed Roots

  • ~/.hermes/cache/{images,audio,video,documents,screenshots}
  • Legacy dirs: ~/.hermes/{image_cache,audio_cache,video_cache,document_cache,browser_screenshots}
  • Any paths configured via the HERMES_MEDIA_ALLOW_DIRS environment variable

Impact

Any media stored outside these directories is silently stripped before reaching any platform.

Scope of this PR

This PR targets the send_message tool path specifically (the path where an LLM explicitly calls send_message with a MEDIA: tag).

The gateway auto-delivery path (gateway/platforms/base.py:3508/3522) is left unchanged — it still calls filter_* and only gets log-line visibility of skipped paths. Fixing that path properly would require injecting skipped-attachment warnings into the response text itself (so the user sees them), which is a distinct design decision from surfacing skips in a tool JSON result.

If desired, a follow-up PR can extend the same partition_* + user-visible warning pattern to the gateway auto-delivery path.

Changes

  • gateway/platforms/base.py: Add partition_media_delivery_paths() and partition_local_delivery_paths() returning (safe, skipped) tuples. Keep filter_* as backward-compatible wrappers (they already log each skipped path by name, so no changes needed at other call sites).
  • tools/send_message_tool.py: Use partition_media_delivery_paths(), surface skipped paths in JSON result under skipped_attachments and warnings, so the LLM can see why files did not arrive.
  • tests/gateway/test_platform_base.py: Regression tests for partition methods plus backward-compatibility check on filter_media_delivery_paths().

Why so few files?

All other call sites (gateway/run.py, cron/scheduler.py, tools/yuanbao_tools.py, gateway/platforms/weixin.py) already get correct per-path logging via the existing filter_* wrappers. The only place where skipped attachments were truly invisible was send_message_tool.py, where the JSON result went back to the LLM without any mention of what was dropped.

Testing

pytest tests/gateway/test_platform_base.py -v -x
    100 passed in 0.79s
pytest tests/gateway/test_send_image_file.py tests/gateway/test_signal.py        tests/gateway/test_tts_media_routing.py tests/cron/test_scheduler.py::TestDeliverResultWrapping        -v -x
    149 passed in 2.22s

New regression tests:

  • test_partition_media_returns_safe_and_skipped
  • test_partition_local_returns_safe_and_skipped
  • test_filter_backward_compatible

Fixes the silent-drop behavior on the send_message tool path.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery comp/cron Cron scheduler and job management labels May 25, 2026
…opping them

Media attachments outside allowed roots were silently stripped with only
a generic WARNING naming no specific path. Users had no way of knowing
why their images/files never arrived.

Changes:
- gateway/platforms/base.py: Add partition_media_delivery_paths() and
  partition_local_delivery_paths() returning (safe, skipped) tuples.
  Keep filter_* as backward-compatible wrappers that already log each
  skipped path by name (no change needed at other call sites).
- tools/send_message_tool.py: Use partition method, surface skipped
  paths in JSON result under 'skipped_attachments' + 'warnings'.
- tests/gateway/test_platform_base.py: Regression tests.

This is the minimal change: all other call sites (gateway/run.py,
cron/scheduler.py, yuanbao_tools.py, weixin.py) already get correct
per-path logging via the existing filter_* wrappers.
@jrockwar jrockwar force-pushed the fix/notify-skipped-media-attachments branch from 23a670c to 3124353 Compare May 25, 2026 12:42
@hclsys

hclsys commented May 25, 2026

Copy link
Copy Markdown

Nice refactor — splitting filter_* into partition_* (returns (safe, skipped)) while keeping filter_* as a back-compat wrapper that now logs the rejected path is clean, and the send_message_tool wiring is the real win: partition_media_delivery_pathsresult["skipped_attachments"] + a warnings entry pointing at HERMES_MEDIA_ALLOW_DIRS gives the agent actionable feedback instead of a silent drop. The test exercising both partition helpers covers the new contract.

Scope clarification worth noting in the PR (since the title says 'report skipped … instead of silently dropping them' broadly): this fixes the send_message tool path specifically. The other delivery call sites still call the deprecated filter_* and therefore only get the improved log (path now included), not user-visible surfacing — namely base.py:3584/3598 (the main gateway auto-delivery path), weixin.py:1682/1685, gateway/run.py:5175/11371, and cron/scheduler.py:581/666. So a model emitting MEDIA:<unsafe path> in a normal gateway turn (not via the send_message tool) will still have it dropped with only a log line. That may be intentional scoping for this PR, but if the goal is 'no silent drops anywhere', those callers would each need the same partition_* + surface treatment (or at least the gateway auto-delivery one, which is the most user-visible). Flagging so it's a deliberate choice rather than an assumed-complete fix. The send_message path itself is solid — LGTM for that scope.

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

Labels

comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants