From 08abc5eba8b64659c8eae0d06b5dd6ce040ee4b4 Mon Sep 17 00:00:00 2001 From: elphamale Date: Tue, 21 Jul 2026 07:36:35 +0300 Subject: [PATCH] fix(prompt): forbid MEDIA: tags in the api_server platform hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every PLATFORM_HINTS entry for a messaging platform (Telegram, WhatsApp, Discord, Slack, Signal, WebUI, desktop) teaches the model the MEDIA:/path convention because an interception mechanism actually resolves it there (native attachment delivery, or a validated/inlined data URL). The cli entry, which has no such mechanism, explicitly tells the model NOT to use it and to state the path in plain text instead. The api_server entry had neither instruction. Its /v1/runs handler never routes the final response through any MEDIA: resolver (confirmed against source: none of the four call sites of the api_server module's media-tag resolver are inside its runs-endpoint handler), so a MEDIA:/path tag there renders as inert literal text in the API response — exposing a raw host filesystem path to the caller with no delivery ever taking place. Nothing platform-specific told the model not to use a convention it's correctly taught for several sibling platforms in this same dict, so the general cross-platform habit could surface here too, unlike cli where an explicit prohibition already closes the gap. Mirrors cli's prohibition, adapted for api_server's actual constraint: no "state the path in plain text" fallback, since a typical API caller has no filesystem access to the host at all. Points at "a registered file-delivery tool" generically rather than naming any specific tool, since api_server toolsets are deployment-defined. --- agent/prompt_builder.py | 8 +++++++- tests/agent/test_prompt_builder.py | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/agent/prompt_builder.py b/agent/prompt_builder.py index 4ae71cbb3a37..0c997ff42843 100644 --- a/agent/prompt_builder.py +++ b/agent/prompt_builder.py @@ -884,7 +884,13 @@ PLATFORM_HINTS = { "You're responding through an API server. The rendering layer is unknown — " "assume plain text. No markdown formatting (no asterisks, bullets, headers, " "code fences). Treat this like a conversation, not a document. Keep responses " - "brief and natural." + "brief and natural. " + "File/media delivery: do NOT emit MEDIA:/path tags in your response — " + "that convention is only intercepted on messaging platforms (Telegram, " + "WebUI, etc.); here it renders as literal, unusable text exposing a raw " + "host filesystem path to the caller. If a registered file-delivery tool " + "is available in your toolset, use it; otherwise there is no channel to " + "deliver a file on this platform." ), "webui": ( "You are in the Hermes WebUI, a browser-based chat interface. " diff --git a/tests/agent/test_prompt_builder.py b/tests/agent/test_prompt_builder.py index 633fde7c02d9..e1ef1dd170d6 100644 --- a/tests/agent/test_prompt_builder.py +++ b/tests/agent/test_prompt_builder.py @@ -1103,6 +1103,19 @@ class TestPromptBuilderConstants: hint = PLATFORM_HINTS["whatsapp_cloud"] assert "MEDIA:" in hint + def test_api_server_hint_forbids_media_tag(self): + """api_server has no MEDIA: interception (unlike telegram/webui/etc., + which resolve it to an attachment or a validated/inlined data URL) — + _handle_runs never calls the resolver other api_server endpoints use. + Without an explicit prohibition, the model's general cross-platform + habit of using MEDIA:/path for file delivery (correct and taught for + several other platforms in this same dict) can surface here too, with + no interception to catch it: the tag renders as literal text in the + response, exposing a raw host filesystem path to the API caller.""" + hint = PLATFORM_HINTS["api_server"] + assert "MEDIA:" in hint + assert "not" in hint.lower() + def test_markdown_converting_platform_hints_do_not_forbid_markdown(self): """#12224 — WhatsApp (Baileys) and Signal adapters actively convert markdown to native formatting (gateway/platforms/whatsapp_common.py