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
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.