{"repo":"NousResearch/hermes-agent","public_id":"53249","display_label":"Gateway support for .stl/.3mf and other attachment types","summary":"These items concern the gateway's handling of file attachments, mainly .stl and .3mf uploads (one reports Telegram rejecting them), with related changes touching .py/.markdown types and MEDIA: tag delivery through validated egress. The members appear to share a theme of expanding or fixing accepted attachment file types, though their exact scopes differ.","subsystem":"gateway","counts":{"members":4,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":65510,"merged_at":"2026-07-16T11:23:59+00:00"},"resolution":{"state":"unresolved","by":null},"freshness":{"state":"verified","last_verified_at":"2026-07-16T11:23:59+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.5},"fix_edges":[{"issue_number":35474,"pr_number":65510,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-07-16T11:23:59+00:00"},{"issue_number":37318,"pr_number":65510,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-07-16T11:23:59+00:00"},{"issue_number":42206,"pr_number":65510,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-07-16T11:23:59+00:00"},{"issue_number":53249,"pr_number":65510,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-07-16T11:23:59+00:00"}],"members":[{"kind":"issue","number":53249,"title":"Telegram gateway rejects .stl / .3mf attachments — silently breaks 3D-printing agent workflows","url":null,"state":"closed"},{"kind":"pr","number":53354,"title":"fix(gateway): accept .stl and .3mf document uploads","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"4314d451ca961cb50c3430197a3a2c7a8575fd0e","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":60069,"title":"fix(gateway): allow .py/.markdown/.stl/.3mf media & document attachments","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#65510","number":65510}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":65510,"title":"fix(gateway): deliver MEDIA: tags for every file type via validated egress","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["gateway/platforms/base.py","tests/gateway/test_platform_base.py"],"fix_hunks":[{"file":"gateway/platforms/base.py","hunks":["@@ -1504,12 +1504,16 @@ def _log_safe_path(path: str) -> str:\n     re.IGNORECASE,\n )\n \n-# Extension-less absolute paths (e.g. Caddyfile, Dockerfile, Makefile) are\n-# intentionally excluded from MEDIA_TAG_CLEANUP_RE — they are validated and\n-# delivered via MEDIA_EXTENSIONLESS_TAG_RE so prompt-injection paths that do\n-# not exist on disk are left visible instead of silently dropped. Paths with\n-# an unknown but present extension (e.g. .weirdext) stay on the #34517\n-# bare-path fallback and are not handled here.\n+# Paths NOT covered by MEDIA_TAG_CLEANUP_RE's extension alternation — both\n+# extension-less files (Caddyfile, Dockerfile, Makefile) and files with an\n+# unknown extension (.py, .log, .weirdext, ...) — are validated and delivered\n+# via MEDIA_EXTENSIONLESS_TAG_RE. Every ``MEDIA:`` path is therefore\n+# deliverable regardless of file type (#36060): known extensions extract\n+# unconditionally via the anchored pattern above, everything else extracts\n+# only after ``validate_media_delivery_path`` accepts it (exists on disk, not\n+# under the credential/system denylist, strict-mode rules honored), so\n+# prompt-injection paths that do not validate are left visible instead of\n+# silently dropped.\n MEDIA_EXTENSIONLESS_TAG_RE = re.compile(\n     r'''[`\"']?MEDIA:\\s*'''\n     r'''(?P<path>`[^`\\n]+`|\"[^\"\\n]+\"|'[^'\\n]+'|'''","@@ -1527,8 +1531,16 @@ def _normalize_media_tag_path(raw: str) -> str:\n \n \n def _path_lacks_deliverable_extension(path: str) -> bool:\n-    \"\"\"True only when the basename has no extension (Caddyfile, Makefile, …).\"\"\"\n-    return not Path(path).suffix\n+    \"\"\"True when MEDIA_TAG_CLEANUP_RE's extension alternation does not cover\n+    ``path`` — either the basename has no extension at all (Caddyfile,\n+    Makefile, …) or the extension is not in MEDIA_DELIVERY_EXTS (.py, .log,\n+    .weirdext, …). Such paths route through the validated delivery pass\n+    (``validate_media_delivery_path``) instead of the unconditional one, so\n+    every file type is deliverable (#36060) while nonexistent / denylisted\n+    paths stay visible in the text.\n+    \"\"\"\n+    suffix = Path(path).suffix.lower()\n+    return not suffix or suffix not in MEDIA_DELIVERY_EXTS\n \n \n def _strip_media_tag_directives(text: str) -> str:"]},{"file":"tests/gateway/test_platform_base.py","hunks":["@@ -631,12 +631,15 @@ def test_extract_media_and_local_files_share_one_extension_set(self):\n \n     def test_unknown_extension_not_black_holed_by_cleanup(self):\n         \"\"\"A MEDIA: tag with an unknown extension is NOT stripped from the\n-        body — it survives so extract_local_files can still see the bare path,\n-        rather than vanishing entirely (the core of issue #34517).\"\"\"\n+        body by the extension-anchored cleanup — and when the path does not\n+        validate (nonexistent file here), it is not delivered either, so the\n+        tag survives visibly instead of vanishing (the core of issue #34517).\n+        Validated unknown-extension paths DO deliver — see\n+        TestUniversalMediaEgress (#36060).\"\"\"\n         from gateway.platforms.base import MEDIA_TAG_CLEANUP_RE\n         text = \"Saved to MEDIA:/tmp/data.weirdext done\"\n         media, _ = BasePlatformAdapter.extract_media(text)\n-        assert media == []  # unknown extension is not a deliverable MEDIA tag\n+        assert media == []  # nonexistent path fails validation, not delivered\n         stripped = MEDIA_TAG_CLEANUP_RE.sub(\"\", text)\n         assert \"/tmp/data.weirdext\" in stripped  # path preserved, not dropped\n ","@@ -712,6 +715,104 @@ def test_as_document_directive_stripped_without_media_tag(self):\n         )\n \n \n+class TestUniversalMediaEgress:\n+    \"\"\"#36060: every MEDIA: path is deliverable regardless of file type.\n+\n+    Known extensions extract unconditionally (MEDIA_TAG_CLEANUP_RE); unknown\n+    extensions and extension-less files extract via the validated pass —\n+    delivered when validate_media_delivery_path accepts them, left visible\n+    when it does not (nonexistent, denylisted).\n+    \"\"\"\n+\n+    def _patch_allow_root(self, monkeypatch, root):\n+        monkeypatch.setattr(\n+            \"gateway.platforms.base.MEDIA_DELIVERY_SAFE_ROOTS\",\n+            (str(root),),\n+        )\n+        monkeypatch.delenv(\"HERMES_MEDIA_DELIVERY_STRICT\", raising=False)\n+\n+    @pytest.mark.parametrize(\"name\", [\n+        \"script.py\", \"server.log\", \"notes.weirdext\", \"app.ts\", \"run.sh\",\n+        \"config.toml\", \"styles.css\", \"contract.sol\",\n+    ])\n+    def test_unknown_extension_delivered_when_file_validates(\n+        self, tmp_path, monkeypatch, name,\n+    ):\n+        root = tmp_path / \"output\"\n+        root.mkdir()\n+        f = root / name\n+        f.write_text(\"content\", encoding=\"utf-8\")\n+        self._patch_allow_root(monkeypatch, root)\n+\n+        content = f\"Here you go:\\nMEDIA:{f}\\nDone.\"\n+        media, cleaned = BasePlatformAdapter.extract_media(content)\n+        assert len(media) == 1\n+        assert media[0][0] == str(f.resolve())\n+        assert \"MEDIA:\" not in cleaned\n+        assert \"Done.\" in cleaned\n+\n+    def test_unknown_extension_left_visible_when_not_on_disk(\n+        self, tmp_path, monkeypatch,\n+    ):\n+        root = tmp_path / \"output\"\n+        root.mkdir()\n+        self._patch_allow_root(monkeypatch, root)\n+\n+        content = \"MEDIA:/nonexistent/script.py\"\n+        media, cleaned = BasePlatformAdapter.extract_media(content)\n+        assert media == []\n+        assert \"MEDIA:/nonexistent/script.py\" in cleaned\n+\n+    def test_denylisted_paths_still_rejected_regardless_of_extension(\n+        self, tmp_path, monkeypatch,\n+    ):\n+        # A denylisted path must not deliver even though .py/.log/etc now\n+        # route through the validated pass. _media_delivery_denied_paths()\n+        # reads _MEDIA_DELIVERY_DENIED_PREFIXES at call time, so patching the\n+        # tuple exercises the real denylist logic.\n+        secret_dir = [redacted:secret-assignment] / \"secrets\"\n+        secret_dir.mkdir()\n+        f = secret_dir / \"creds.py\"\n+        f.write_text(\"TOKEN = 'x'\", encoding=\"utf-8\")\n+        monkeypatch.setattr(\n+            \"gateway.platforms.base._MEDIA_DELIVERY_DENIED_PREFIXES\",\n+            (str(secret_dir),),\n+        )\n+        monkeypatch.delenv(\"HERMES_MEDIA_DELIVERY_STRICT\", raising=False)\n+\n+        content = f\"MEDIA:{f}\"\n+        media, cleaned = BasePlatformAdapter.extract_media(content)\n+        assert media == []\n+        assert \"MEDIA:\" in cleaned  # rejected tag stays visible\n+\n+    def test_strip_for_display_strips_validated_unknown_extension(\n+        self, tmp_path, monkeypatch,\n+    ):\n+        root = tmp_path / \"output\"\n+        root.mkdir()\n+        f = root / \"server.log\"\n+        f.write_text(\"x\", encoding=\"utf-8\")\n+        self._patch_allow_root(monkeypatch, root)\n+\n+        text = f\"MEDIA:{f}\"\n+        stripped = BasePlatformAdapter.strip_media_directives_for_display(text)\n+        assert \"MEDIA:\" not in stripped\n+\n+    def test_strip_for_display_keeps_unvalidated_unknown_extension(self):\n+        text = \"MEDIA:/nonexistent/server.log\"\n+        stripped = BasePlatformAdapter.strip_media_directives_for_display(text)\n+        assert \"MEDIA:/nonexistent/server.log\" in stripped\n+\n+    def test_known_extension_still_unconditional(self):\n+        # Known extensions keep the pre-#36060 behavior: extracted (and the\n+        # tag stripped) even when the file does not exist — downstream\n+        # delivery surfaces the failure.\n+        content = \"MEDIA:/nonexistent/report.pdf\"\n+        media, cleaned = BasePlatformAdapter.extract_media(content)\n+        assert media == [(\"/nonexistent/report.pdf\", False)]\n+        assert \"MEDIA:\" not in cleaned\n+\n+\n class TestMediaDeliveryPathValidation:\n     def _patch_roots(self, monkeypatch, *roots):\n         monkeypatch.setattr(\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}