Commit da3f0f2b by Hermes Desktop

feat(skills): update media-delivery

parent d588d234
---
name: media-delivery
description: Send files through messaging platforms via Hermes MEDIA protocol — path rules, Windows quirks, allowed directories, and per-platform limitations.
version: 1.0.0
author: Hermes Agent
platforms: [windows, linux, macos]
metadata:
hermes:
tags: [media, delivery, weixin, wechat, telegram, files, attachments, gateway]
---
# Media Delivery (MEDIA Protocol)
Send files to users through messaging platforms by including a **MEDIA directive** in your response:
```
MEDIA:/absolute/path/to/file
```
Hermes detects the `MEDIA:` prefix, validates the path against security rules, and uploads the file as a native attachment on the current platform.
---
## 1. Path Validation
The gateway's `validate_media_delivery_path()` function enforces:
### Allowed Directories (safe roots)
Files **must** live under one of these Hermes-managed cache directories:
| Directory | Content type |
|-----------|-------------|
| `~/.hermes/cache/images/` | Images (jpg, png, webp, gif) |
| `~/.hermes/cache/audio/` | Audio (ogg, opus, mp3, wav, m4a, flac) |
| `~/.hermes/cache/videos/` | Video (mp4, mov, avi, mkv, webm, 3gp) |
| `~/.hermes/cache/documents/` | Documents & other files |
| `~/.hermes/browser_screenshots/` | Screenshots |
| `~/.hermes/image_cache/` | Legacy image cache |
| `~/.hermes/audio_cache/` | Legacy audio cache |
| `~/.hermes/video_cache/` | Legacy video cache |
| `~/.hermes/document_cache/` | Legacy document cache |
To place a file for delivery, copy it into one of these directories first:
```bash
# Example: deliver a document
cp /some/output/report.pdf ~/.hermes/cache/documents/
MEDIA:~/.hermes/cache/documents/report.pdf
```
### Denied Locations (always rejected)
These are **never** accepted, even in non-strict mode:
- `/etc`, `/proc`, `/sys`, `/dev`, `/root`, `/boot`, `/var/log`, `/var/lib`, `/var/run`
- `~/.ssh/`, `~/.aws/`, `~/.gnupg/`, `~/.kube/`, `~/.docker/`
- `~/.hermes/.env`, `~/.hermes/auth.json`, and other credential stores under HERMES_HOME
### Strict Mode
Opt-in via `config.yaml` -> `gateway.strict: true` or `HERMES_MEDIA_DELIVERY_STRICT=1`. In strict mode, files are only accepted if they:
1. Live under an allowlisted root (safe roots above), **or**
2. Were **recently produced** (within the recency window, default 1 second)
Use `HERMES_MEDIA_ALLOW_DIRS` env var to add custom allowlisted roots in strict mode.
---
## 2. Windows Path Format (CRITICAL GOTCHA)
**On Windows, always use `C:/Users/...` format, NOT `/c/Users/...` (git-bash/MSYS format).**
The MEDIA validation runs through Python's `Path.resolve(strict=True)`, which on Windows interprets `/c/Users/...` as a **relative** path (`\c\Users\...` off the current C: drive), not an absolute one. This causes the file to be reported as nonexistent and the path to be skipped as "unsafe".
| Correct ✅ | Wrong ❌ |
|-----------|---------|
| `MEDIA:C:/Users/...` | `MEDIA:/c/Users/...` |
| `MEDIA:C:\Users\...` | `MEDIA:/C:/Users/...` |
Forward slashes (`C:/Users/...`) work fine — both `Path.resolve()` and Windows APIs accept them.
### Quick fix when coming from terminal
When you create or find a file in terminal (where `/c/Users/...` paths work), copy it to a cache dir and reference it with the Windows-form path:
```bash
# Terminal (git-bash) — works fine here
cp /c/Users/Administrator/report.pdf ~/.hermes/cache/documents/
# In response — MUST use Windows format
MEDIA:C:/Users/Administrator/.hermes/cache/documents/report.pdf
```
---
## 3. File-Type Detection
The gateway auto-detects the content type from the file extension:
| Extension | Sent as |
|-----------|---------|
| `.jpg`, `.jpeg`, `.png`, `.webp`, `.gif` | Image (native preview on most platforms) |
| `.mp4`, `.mov`, `.avi`, `.mkv`, `.webm`, `.3gp` | Video (inline playback on supported platforms) |
| `.ogg`, `.opus`, `.mp3`, `.wav`, `.m4a`, `.flac` | Audio (or voice if `is_voice=True`) |
| Everything else (`.pdf`, `.md`, `.txt`, `.zip`, etc.) | Document (downloadable attachment) |
---
## 4. Per-Platform Notes
### Weixin / WeChat (微信)
- MEDIA protocol IS supported
- Image delivery works reliably (images extracted from markdown `![]()` are also sent)
- File/document delivery works, but may have size limits
- Voice messages supported via `MEDIA_VOICE` type
- Path validation is strict — files MUST be in a cache directory and path MUST resolve through Python
### Telegram
- MEDIA protocol fully supported
- Images, video, audio, documents all work
- Larger file size limit
### Discord / Slack
- MEDIA protocol supported
- File-type restrictions may apply per platform
---
## 5. Diagnostic Checklist
If MEDIA delivery fails:
1. **Check gateway logs**: `grep "MEDIA\|media" ~/.hermes/logs/gateway.log` — look for "Skipping unsafe" or "media delivery failed"
2. **Verify file exists**: use Python (not bash) to check: `python -c "import pathlib; print(pathlib.Path('path').resolve(strict=True))"`
3. **Check path format** (Windows): must be `C:/...` not `/c/...`
4. **Verify location**: file must be under `~/.hermes/cache/<type>/` or another safe root
5. **Check file permissions**: must be a regular file (not a symlink to a denied location)
6. **Try restarting gateway**: `hermes gateway restart` — helps if cache dirs were created mid-session
# Windows MEDIA Protocol — Reproduction & Debugging
> **Source:** conversation about Weixin file delivery on Windows (2026-07-15)
> **Symptom:** MEDIA paths always rejected with "Skipping unsafe MEDIA directive path"
## Reproduction Steps
1. File created at `C:\Users\Administrator\markdown-test.md`
2. Sent `MEDIA:/c/Users/Administrator/markdown-test.md` — rejected
3. Copied to `/tmp/markdown-test.md`, sent `MEDIA:/tmp/markdown-test.md` — rejected
4. Copied to `~/.hermes/document_cache/markdown-test.md`, sent `MEDIA:/c/Users/Administrator/.hermes/document_cache/markdown-test.md` — rejected
5. Finally determined via log inspection that Python's `Path.resolve()` on Windows treats `/c/Users/` as a relative path (`\c\Users\...` off C: drive)
## Key Log Output
```
WARNING gateway.platforms.base: Skipping unsafe MEDIA directive path: /c/Users/Administrator/some-file.md
```
## Verification Script
```python
# Run this to check if a path resolves on your Windows machine
from pathlib import Path
path = Path("C:/Users/Administrator/.hermes/cache/documents/test.md")
try:
resolved = path.resolve(strict=True)
print(f"✅ Resolves to: {resolved}")
except FileNotFoundError:
print(f"❌ File not found (Path sees: {path})")
```
## Validated Working Approach
```bash
# 1. Copy to cache directory
cp /path/to/file ~/.hermes/cache/documents/
# 2. Send with Windows path format
# In your response: MEDIA:C:/Users/<user>/.hermes/cache/documents/file.md
```
## Correct vs Incorrect Paths
| Format | Terminal (bash) | Python/Validation | Works? |
|--------|-----------------|-------------------|--------|
| `/c/Users/...` | ✅ Yes | ❌ Relative path | ❌ |
| `C:/Users/...` | ✅ Yes | ✅ Absolute path | ✅ |
| `C:\Users\...` | ⚠️ Needs escaping | ✅ Absolute path | ✅ |
| `~/Users/...` | ⚠️ Depends on shell | ❌ Not absolute | ❌ |
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment