fix(gateway): surface dropped MEDIA paths in send_message result - #34178
fix(gateway): surface dropped MEDIA paths in send_message result#34178konsisumer wants to merge 1 commit into
Conversation
|
Verified: the refactoring correctly surfaces a real UX problem where The approach is clean:
One consideration: the warning message is appended every time dropped_media is non-empty and the send succeeds. If a model repeatedly sends messages with the same dropped path, the user will see the same warning each time. This is probably the right behavior (each send is a distinct event), but worth noting if noise becomes a concern. |
|
Competing with #32880, #32054, and #31864 — all fix #32644 (silent MEDIA attachment drop). This PR adds |
|
Rebased onto current |
2db2660 to
40d8f64
Compare
teknium1
left a comment
There was a problem hiding this comment.
Thanks for preserving the filter API while making rejected paths observable in the structured send result.
Problems
- The added result warning is not visible to the default
hermes senduser: successful results print onlynoteorsentinhermes_cli/send_cmd.py:120-128. A stripped attachment would therefore still printsentunless the caller selects JSON output. - The new remediation text tells users to set
HERMES_MEDIA_ALLOW_DIRS/HERMES_MEDIA_TRUST_RECENT_FILES. The supported configuration isgateway.media_delivery_allow_dirs,gateway.strict, andgateway.trust_recent_files; the latter is only consulted in strict mode (hermes_cli/config.py:2927-2950).
Suggested changes
- Render this warning in the normal CLI formatter and add a regression test for that output.
- Point the warning at the supported
gateway.*config keys, including the strict-mode prerequisite for recency trust. - Scope the rationale to current CLI/MCP callers:
send_messageis intentionally not agent-callable on current main (tools/send_message_tool.py:1955-1965).
Automated hermes-sweeper review.
| # Surface MEDIA paths that the delivery allowlist dropped so the | ||
| # caller can react instead of treating a text-only send as a fully | ||
| # successful attachment send (issue #32644). | ||
| if dropped_media and isinstance(result, dict) and result.get("success"): |
There was a problem hiding this comment.
Please also surface this warning through the default hermes send formatter. hermes_cli/send_cmd.py:120-128 ignores warnings and prints only sent for a successful result, so the normal CLI user would still receive the false-success presentation this change is intended to prevent.
|
Thanks for the review. Addressed in this push:
|
40d8f64 to
361a938
Compare
|
Rebased onto current |
361a938 to
2015083
Compare
What does this PR do?
The
send_messagetool extractedMEDIA:directives, ran them throughfilter_media_delivery_paths(which silently drops paths outside the delivery allowlist), and then sent the remaining text. A successful text send was returned to the model as{"success": true}with no signal that the attachment had been stripped, so the agent reported "media sent" while the user only saw plain text — the exact failure reported in #32644.This PR adds
partition_media_delivery_pathsonBasePlatformAdapterreturning(safe, dropped).filter_media_delivery_pathskeeps its old return shape by delegating to the partition (no behavior change for existing callers).send_message_tool._handle_sendswitches to the partition and, on a successful send, attaches a"MEDIA attachment(s) were dropped"warning plus amedia_dropped: [...]field listing the rejected paths, so the agent can correct the path or expandHERMES_MEDIA_ALLOW_DIRSinstead of treating text-only delivery as a fully successful attachment send.Scope is intentionally narrow: this addresses acceptance criteria 1, 2, and the
send_messagehalf of 4 from the issue. The gateway post-stream delivery path ingateway/run.py::_deliver_post_stream_mediaalso callsfilter_media_delivery_pathsand silently drops paths after text has already streamed; turning that into a user-visible follow-up message is a separate UX decision and is deferred to a follow-up PR. The operator-facingsendVoice/sendAudiosmoke-test command (AC 3) is likewise deferred.Related Issue
Refs #32644
Type of Change
Changes Made
gateway/platforms/base.py: addBasePlatformAdapter.partition_media_delivery_paths(media_files) -> (safe, dropped). Refactorfilter_media_delivery_pathsto delegate to it so existing callers keep their old return shape and per-path warning log.tools/send_message_tool.py: in_handle_send, use the partition helper and, on a successful send, attach a user-facingwarningsentry plus amedia_dropped: [<paths>]field so the model/operator sees that the attachment was filtered out. The warning is only added when the underlying send succeeded — error results are not mutated.tests/gateway/test_platform_base.py: addtest_partition_returns_dropped_paths_so_caller_can_warncovering the new(safe, dropped)return shape under the strict allowlist.tests/tools/test_send_message_tool.py: the existingtest_media_tag_outside_allowed_roots_is_not_sentnow asserts thatresult["media_dropped"]and the warning are populated (it previously codified the silent-success bug). Addtest_media_dropped_warning_not_added_when_send_errorsso an upstream send failure isn't masked by the new warning.How to Test
pytest tests/gateway/test_platform_base.py::TestMediaDeliveryPathValidation tests/tools/test_send_message_tool.py -q— 134 tests, all green locally.HERMES_MEDIA_DELIVERY_STRICT=1andHERMES_MEDIA_TRUST_RECENT_FILES=0, callsend_message_tool({"action": "send", "target": "telegram:<chat>", "message": "[[audio_as_voice]]\nMEDIA:/tmp/outside-allowlist.ogg"}). The returned JSON now contains"media_dropped": ["/tmp/outside-allowlist.ogg"]plus awarningsentry naming the dropped attachment, instead of a bare{"success": true}._send_to_platformreturns{"error": "..."}, the test confirmsmedia_droppedis not attached — so a real send failure isn't masked.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/tools/test_send_message_tool.py tests/gateway/test_platform_base.py -qand all tests passscripts/check-windows-footguns.pyclean on the four touched files.Documentation & Housekeeping
docs/, docstrings) — N/A, new method's docstring covers the behavior change and existing callers see no API change.cli-config.yaml.exampleif I added/changed config keys — N/A, no new config keys.CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/A.send_messageschema is unchanged; the result dict gains an optionalmedia_droppedfield on success which is additive.Addressing maintainer feedback
@alt-glitch noted (PR triage comment) that this competes with #32880, #32054, and #31864 — all targeting #32644 (silent MEDIA attachment drop):
run.pyandscheduler.py.filter_*return type across 13 call sites.This PR sits between them: it adds
partition_media_delivery_pathsonBasePlatformAdapterand surfaces dropped paths in thesend_messageresult, while keepingfilter_media_delivery_paths' existing return shape (no change at its other call sites). Scope is intentionally limited to the four files above; the gateway post-stream path and the smoke-test command remain deferred (Refs #32644, notCloses). Happy to defer to whichever of the competing PRs the maintainers prefer — flagging the overlap here so the duplication is visible.