Commit 170b389a by Hermes Desktop

feat(skills): update media-delivery

parent 2098ba35
# macOS MEDIA Protocol — Gateway Initialization Issue
# macOS — Gateway Initialization & MEDIA Protocol
> **Source:** conversation about file delivery on macOS (2026-07-22)
> **Symptom:** MEDIA paths returned as plain text instead of files, even after correct path and cache directory setup
## Problem Description
When using the MEDIA protocol on macOS after a gateway restart, the MEDIA directive is not being parsed by the gateway. Instead of sending the file as an attachment, the gateway returns the literal text `MEDIA:/path/to/file` to the user.
## Reproduction Steps
1. File exists in cache directory: `~/.hermes/cache/documents/test.md`
2. Send `MEDIA:/Users/username/.hermes/cache/documents/test.md`
3. User receives plain text instead of downloadable file
4. Gateway logs show no MEDIA-related entries
5. Restart gateway with `hermes gateway restart`
6. MEDIA protocol starts working correctly
After a gateway restart, the MEDIA directive may not be parsed correctly. Instead of sending the file as an attachment, the gateway returns the literal text `MEDIA:/path/to/file` to the user.
## Root Cause
The MEDIA protocol parser in the gateway may not be fully initialized after a session starts or after a gateway restart. This appears to be a timing/initialization issue specific to the macOS gateway implementation.
The MEDIA protocol parser in the gateway may not be fully initialized after a session starts or after a gateway restart. This appears to be a timing/initialization issue.
## Detailed Diagnostic Steps
## Diagnostic Steps
1. **Verify file exists in cache directory**:
```bash
......@@ -42,12 +33,7 @@ The MEDIA protocol parser in the gateway may not be fully initialized after a se
tail -100 ~/.hermes/logs/gateway.error.log
```
5. **Inspect `extract_media()` implementation** (located at `gateway/platforms/base.py:3605`):
- Uses `MEDIA_TAG_CLEANUP_RE` to extract tags
- Validates paths with `validate_media_delivery_path()`
- Masks protected regions (code blocks, inline code, blockquotes)
6. **Verify configuration**:
5. **Verify configuration**:
```bash
grep -r "MEDIA\|media" ~/.hermes/config.yaml
```
......@@ -60,7 +46,7 @@ The MEDIA protocol parser in the gateway may not be fully initialized after a se
hermes gateway restart
sleep 10
```
3. **Verify gateway is ready** by checking worker status:
3. **Verify gateway is ready**:
```bash
hermes worker status
```
......@@ -68,12 +54,14 @@ The MEDIA protocol parser in the gateway may not be fully initialized after a se
## Key Code Paths
- **MEDIA tag extraction**: `gateway/platforms/base.py:extract_media()` (line 3605)
- **Path validation**: `gateway/platforms/base.py:validate_media_delivery_path()`
- **Extension allowlist**: `gateway/platforms/base.py:MEDIA_DELIVERY_EXTS` (line 1455)
- **Cleanup regex**: `gateway/platforms/base.py:MEDIA_TAG_CLEANUP_RE` (line 1491)
| Function | Location | Purpose |
|----------|----------|---------|
| `extract_media()` | `gateway/platforms/base.py` | Extracts `MEDIA:` tags from response text |
| `validate_media_delivery_path()` | `gateway/platforms/base.py` | Validates path security |
| `MEDIA_DELIVERY_EXTS` | `gateway/platforms/base.py` | Extension allowlist |
| `MEDIA_TAG_CLEANUP_RE` | `gateway/platforms/base.py` | Regex for MEDIA tag cleanup |
## Supported Extensions (from `MEDIA_DELIVERY_EXTS`)
## Supported Extensions
Images: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp`, `.tiff`, `.svg`
Video: `.mp4`, `.mov`, `.avi`, `.mkv`, `.webm`
......@@ -88,15 +76,10 @@ Web: `.html`, `.htm`
After restart, MEDIA protocol should work correctly:
```bash
# Copy file to cache
cp /path/to/file ~/.hermes/cache/documents/
# Send with MEDIA directive
MEDIA:/Users/username/.hermes/cache/documents/file.md
```
User should receive a downloadable file, not plain text.
## Prevention
- Always verify MEDIA protocol is active before sending files
......@@ -112,6 +95,8 @@ User should receive a downloadable file, not plain text.
- Issue #34517: MEDIA tag cleanup regex mismatch with extract_local_files
- Issue #34632: Windows path format (`C:/` vs `/c/`)
- Issue #35695: MEDIA tags in code blocks should not be extracted
- Windows path format issues (use `C:/...` not `/c/...`)
- Strict mode path validation failures
- File permission issues
## See Also
- [Main skill](../SKILL.md) — Full documentation
- [windows-media-path-debugging.md](windows-media-path-debugging.md) — Windows path format issues
\ No newline at end of file
# Windows MEDIA Protocol — Reproduction & Debugging
# Windows MEDIA Protocol — Path Debugging
> **Source:** conversation about file delivery issues on Windows/Linux (2026-07-15, 2026-07-22)
> **Symptoms:**
> - Windows: "Skipping unsafe MEDIA directive path"
> - Linux/Windows: Chinese filename files fail to download via Markdown links
## Issue 1: Windows Path Format (Path.resolve() Quirk)
### Reproduction Steps
### Root Cause
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** (not in cache dir)
4. Copied to `~/.hermes/cache/documents/markdown-test.md`, sent `MEDIA:/c/Users/Administrator/.hermes/cache/documents/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)
Python's `Path.resolve(strict=True)` on Windows treats `/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 skipped as "unsafe".
### Key Log Output
### Correct vs Incorrect Paths (Windows)
```
WARNING gateway.platforms.base: Skipping unsafe MEDIA directive path: /c/Users/Administrator/some-file.md
```
| 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 | ❌ |
### 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)
......@@ -35,121 +29,57 @@ except FileNotFoundError:
print(f"❌ File not found (Path sees: {path})")
```
### Correct vs Incorrect Paths (Windows)
| 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 | ❌ |
### Validated Working Approach (Windows)
### Working Approach
```bash
# 1. Copy to cache directory
cp /path/to/file ~/.hermes/cache/documents/
# 2. Send with Windows path format (NOT /c/... format)
# In your response: MEDIA:C:/Users/<user>/.hermes/cache/documents/file.md
# MEDIA:C:/Users/<user>/.hermes/cache/documents/file.md
```
---
## Issue 2: Chinese Filenames / Special Characters (Markdown Parsing)
### Symptom
File exists, MEDIA path is valid, but user cannot download the file via Markdown link.
## Issue 2: Chinese Filenames / Special Characters
### Root Cause
Markdown parsers struggle with paths containing:
- Chinese characters (中文)
- Spaces
- Special characters (`()`, `[]`, `#`, `?`, etc.)
Markdown parsers struggle with paths containing Chinese characters, spaces, or special characters (`()`, `[]`, `#`, `?`, etc.).
### Reproduction
### Quick Fix
```markdown
# ❌ Fails - Chinese filename without angle brackets
[2026 年申请表.docx](/home/lcai/.hermes/cache/documents/2026 年申请表.docx)
[2026 年申请表.docx](/home/user/.hermes/cache/documents/2026 年申请表.docx)
# ✅ Works - Same filename with angle brackets
[2026 年申请表.docx](</home/lcai/.hermes/cache/documents/2026 年申请表.docx>)
[2026 年申请表.docx](</home/user/.hermes/cache/documents/2026 年申请表.docx>)
```
### Verification Steps
1. **Check file exists**:
```bash
ls -la "/home/lcai/.hermes/cache/documents/2026 年申请表.docx"
```
2. **Test Python path resolution**:
```python
from pathlib import Path
path = Path("/home/lcai/.hermes/cache/documents/2026 年申请表.docx")
print(f"Exists: {path.exists()}")
print(f"Resolved: {path.resolve()}")
```
3. **Try different Markdown formats**:
- Without angle brackets: `[name](/path/文件.docx)`
- With angle brackets: `[name](</path/文件.docx>)`
- URL-encoded: `[name](/path/2026%20%E5%B9%B4.docx)` ⚠️ (platform-dependent)
### Solutions
#### Option A: Use Angle Brackets (Recommended for Chinese names)
```markdown
[申请表](</home/lcai/.hermes/cache/documents/申请表.docx>)
```
#### Option B: Rename to English (Best for compatibility)
```bash
mv "2026 年申请表.docx" "2026-Application-Form.docx"
```
Then send:
```markdown
[申请表](/home/lcai/.hermes/cache/documents/2026-Application-Form.docx)
```
#### Option C: URL Encode (Advanced)
```python
import urllib.parse
filename = "2026 年申请表.docx"
encoded = urllib.parse.quote(filename, safe='')
print(f"Encoded: {encoded}")
# Output: 2026%20%E5%B9%B4%E7%94%B3%E8%AF%B7%E8%A1%A8.docx
```
Then use in Markdown:
```markdown
[申请表](/home/lcai/.hermes/cache/documents/2026%20%E5%B9%B4%E7%94%B3%E8%AF%B7%E8%A1%A8.docx)
```
| Method | Example | Compatibility |
|--------|---------|--------------|
| **Angle brackets** | `[name](</path/文件.docx>)` | ✅ Good |
| **Rename to English** | `mv "文件.docx" "file.docx"` | ✅ Best |
| **MEDIA protocol** | `MEDIA:/path/文件.docx` | ✅ Best + no rename needed |
| **URL encode** | `[name](/path/%E6%96%87%E4%BB%B6.docx)` | ⚠️ Platform-dependent |
---
## Debugging Checklist
When MEDIA delivery fails, check in this order:
1. **File exists?**
```bash
ls -la /path/to/file
```
2. **Correct path format?**
- Windows: Must be `C:/...` NOT `/c/...`
- Linux: Absolute path required
2. **Correct path format?** (Windows: `C:/...` not `/c/...`)
3. **In allowed directory?**
- Must be under `~/.hermes/cache/<type>/` or other safe root
- Check: `~/.hermes/cache/documents/`, `~/.hermes/cache/images/`, etc.
3. **In allowed directory?** Under `~/.hermes/cache/<type>/` or workspace
4. **Chinese/special characters in filename?**
- Try with angle brackets: `](</path/文件.docx>)`
- Or rename to English
4. **Chinese/special characters?** Use angle brackets `<>` or MEDIA protocol
5. **Check gateway logs**:
```bash
......@@ -165,18 +95,18 @@ When MEDIA delivery fails, check in this order:
---
## Common Error Messages & Fixes
## Common Error Messages
| Error | Cause | Fix |
|-------|-------|-----|
| `Skipping unsafe MEDIA directive path` | Path not in allowed dir OR wrong format (Windows `/c/...`) | Move to cache dir OR use `C:/...` format |
| `File not found` | Path doesn't resolve correctly | Check file exists, verify path format |
| Download fails but no error | Chinese/special chars in filename | Use angle brackets `<>` or rename to English |
| `Not a regular file` | Path is a directory or symlink | Ensure path points to actual file |
| `Skipping unsafe MEDIA directive path` | Wrong path format or not in allowed dir | Use `C:/...` or move to cache dir |
| `File not found` | Path doesn't resolve | Check file exists and path format |
| Download fails but no error | Chinese/special chars in filename | Use angle brackets `<>` or rename |
| `Not a regular file` | Path is a directory or symlink | Ensure path points to a regular file |
---
## Related Documentation
## See Also
- Main skill: `media-delivery` (see "中文文件名和特殊字符处理" section)
- Hermes docs: https://hermes-agent.nousresearch.com/docs
\ No newline at end of file
- [Main skill](../SKILL.md) — Full documentation
- [workspace-security-policy.md](workspace-security-policy.md) — Path validation details
\ No newline at end of file
# Hermes Workspace Security Policy — File Delivery Restrictions
# Hermes Workspace Security Policy — File Download Restrictions
> **Source**: Conversation 2026-07-22 (session `mrvqi0lwbwg569`)
> **Symptom**: Files in `~/.hermes/cache/documents/` cannot be accessed via Markdown links, even with angle brackets
## 错误消息来源
## Problem Description
**`File is outside the session and Hermes workspaces`** 这个错误消息确实存在,来自 `hermes-webui` 项目的 `sessions.ts:791`
When attempting to send files with long Chinese filenames from `~/.hermes/cache/documents/` directory:
```typescript
// packages/server/src/controllers/hermes/sessions.ts
throw Object.assign(new Error('File is outside the session and Hermes workspaces'), {
code: 'invalid_path',
status: 400,
})
```
1. File exists and is readable: `ls -la` confirms it
2. File is in an "allowed" cache directory
3. Markdown link uses angle brackets: `](</path/to/文件.md>)`
4. **Error**: `File is outside the session and Hermes workspaces`
## 验证逻辑
## Root Cause
WebUI 中的文件下载/预览接口通过 `resolveSessionPreviewFile()` 函数进行路径校验:
**This is a Hermes security policy restriction, NOT a platform-specific issue.**
```
resolveSessionPreviewFile(ctx, pathValue)
├─ 相对路径 → resolveSessionWorkspaceFile() → 检查 session.workspace 内
└─ 绝对路径 → 检查两个根目录:
├─ session.workspace(会话的工作目录)
└─ defaultHermesWorkspace(profile) → ~/.hermes/workspace/
└─ 都不匹配 → 抛出 "File is outside the session and Hermes workspaces"
```
Hermes enforces that files must be within the **session workspace** for Markdown link delivery. Even though `~/.hermes/cache/` directories are listed as "safe roots" for MEDIA protocol, the **Markdown link delivery** path has stricter restrictions that only allow files within the **active session's workspace**.
### 允许的根目录
**Key point**: The workspace is the session-specific working directory (which may be `~/.hermes/workspace/` or another path configured for the session), NOT necessarily the default `~/.hermes/workspace/`.
| 根目录 | 说明 |
|--------|------|
| `session.workspace` | 会话特定的工作目录,由 `workdir` 参数指定 |
| `~/.hermes/workspace/` | 默认 Hermes workspace(profile 为 default 时) |
**Applies to all deployment scenarios:**
- Hermes running locally on macOS/Linux/Windows
- Hermes running on remote server accessed via Gateway
- Cross-platform access (macOS client → Linux server, etc.)
### 被拒绝的路径
## Reproduction Steps
- `~/.hermes/cache/documents/` ❌ 不在 workspace 中
- `~/.hermes/cache/images/` ❌ 不在 workspace 中
- 其他任何不在 workspace 中的绝对路径 ❌
```bash
# 1. Create a file with Chinese name in workspace
echo "# Test" > "/Users/zhaoyuehui/.hermes/workspace/测试文件.md"
### 与 MEDIA 协议的区别
# 2. Send via Markdown link — WORKS
[测试文件](</Users/zhaoyuehui/.hermes/workspace/测试文件.md>)
| 方式 | 路径验证 | 允许的目录 |
|------|---------|-----------|
| **MEDIA 协议** | `validate_media_delivery_path()` (gateway Python) | cache 目录 + workspace |
| **文件下载链接** | `resolveSessionPreviewFile()` (WebUI TypeScript) | **仅 workspace** |
# 3. Copy to cache directory
cp "/Users/zhaoyuehui/.hermes/workspace/测试文件.md" "/Users/zhaoyuehui/.hermes/cache/documents/"
## 解决方案
# 4. Try to send from cache — FAILS
[测试文件](</Users/zhaoyuehui/.hermes/cache/documents/测试文件.md>)
# Error: File is outside the session and Hermes workspaces
### 方案 A: 使用 MEDIA 协议(推荐)
```
MEDIA:/home/user/.hermes/cache/documents/file.docx
```
✅ MEDIA 协议不走 WebUI 的文件下载接口,不受 workspace 限制
## Verified Workarounds
### Workaround A: Keep Files in Workspace (Recommended)
Always copy files to the session workspace before sending:
### 方案 B: 拷贝到 workspace
```bash
cp "/path/to/source/file.md" ~/.hermes/workspace/
# Or check your session's workspace path first
cp /path/to/file ~/.hermes/workspace/
```
Then send:
然后使用 Markdown 链接:
```markdown
[文件名](</path/to/session/workspace/文件名.md>)
[file](</path/to/workspace/file.docx>)
```
### Workaround B: Base64 Encoding (Most Reliable)
When file cannot be moved to workspace (e.g., large files, read-only locations):
### 方案 C: Base64 编码
```bash
# Encode (macOS/Linux)
base64 "/path/to/文件.md"
```
Send the base64 string with decoding instructions:
```
文件已编码为 base64,请复制以下内容并解码:
IyDplb/kuK3mlofmlofku7blkI3mtYvor5XmlofmoaMKCui/meaYr+S4gOS4queUqOS6ju...
解码命令:
echo "BASE64_CONTENT" | base64 -D > "文件名.md" # macOS
echo "BASE64_CONTENT" | base64 -d > "文件名.md" # Linux
```
### Workaround C: Direct Content Display
For small files (< 10KB), read and display content directly:
```python
from hermes_tools import read_file
result = read_file("/path/to/file.md")
print(result["content"])
base64 /path/to/file
```
提供 base64 字符串和解码指令。
## Diagnostic Checklist
## 调试清单
When encountering `File is outside the session and Hermes workspaces`:
1. **Check file location**:
1. **检查文件是否在 workspace 内**
```bash
echo "$PWD"
echo "$WORKSPACE" # 通常是 ~/.hermes/workspace/
ls -la /path/to/file
```
2. **Verify if in workspace**:
```bash
# Check if file is in the session's workspace
WORKSPACE=$(hermes config get workspace.path 2>/dev/null || echo "~/.hermes/workspace")
[[ "/path/to/file" == "$WORKSPACE"* ]] && echo "In workspace" || echo "Not in workspace"
```
3. **Try copying to workspace**:
```bash
cp "/path/to/file" ~/.hermes/workspace/
```
4. **If copy fails or not desirable, use base64**:
```bash
base64 "/path/to/file"
```
## Platform Independence
| Hermes Deployment | Markdown Cache Links | Markdown Workspace Links | Base64 |
|-------------------|---------------------|-------------------------|--------|
| Local macOS | ❌ Fails | ✅ Works | ✅ Works |
| Local Linux | ❌ Fails | ✅ Works | ✅ Works |
| Local Windows | ❌ Fails | ✅ Works | ✅ Works |
| Remote (any) | ❌ Fails | ✅ Works | ✅ Works |
## Best Practices
1. **Always prefer workspace directory** for files you want to send via Markdown links
2. **For Chinese filenames, always use angle brackets**: `](</path/文件.md>)`
3. **When in doubt, use base64** — it works everywhere and bypasses all path restrictions
4. **For large files**, consider base64 or direct content display
5. **Test file delivery early** in a session to confirm behavior
## Related Issues
- Windows path format: Use `C:/...` not `/c/...` (see `windows-media-path-debugging.md`)
- Chinese filename parsing: Always use angle brackets
- MEDIA protocol initialization: May require gateway restart (see `macos-gateway-media-issue.md`)
- Cache directory restrictions: Not all "safe roots" work for Markdown links
2. **尝试 MEDIA 协议**:绕过 workspace 限制
## Key Takeaway
3. **拷贝到 workspace** 后重试
**Hermes security policy restricts Markdown file links to the session's workspace directory only. Files in `~/.hermes/cache/` directories will fail with "File is outside the session and Hermes workspaces" error, regardless of deployment platform.**
---
The workspace is the **session-specific working directory**, which may be:
- Default: `~/.hermes/workspace/`
- Custom: Configured per session via `workdir` parameter
## 相关文档
Use one of these alternatives:
1. Copy file to the session workspace (preferred for small/medium files)
2. Use base64 encoding (works for any file, any location)
3. Display content directly (for small files)
- [SKILL.md](../SKILL.md) — 主技能文档
- [windows-media-path-debugging.md](windows-media-path-debugging.md) — Windows 路径格式问题
\ No newline at end of file
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