Commit 170b389a by Hermes Desktop

feat(skills): update media-delivery

parent 2098ba35
---
name: media-delivery
description: Send files to users via MEDIA protocol (native attachments) or Markdown links (workspace files) — path rules, Windows quirks, Chinese filename handling, and per-platform limitations.
version: 1.2.0
version: 1.3.0
author: Hermes Agent
platforms: [windows, linux, macos]
metadata:
......@@ -29,8 +29,9 @@ Hermes detects the `MEDIA:` prefix, validates the path against security rules, a
| 文件存在但无法下载 | 中文/空格/特殊字符 | 用尖括号 `](</path/文件.docx>)` 或重命名 |
| 文件找不到 | 不在允许目录 | 移到 `~/.hermes/cache/<type>/``~/.hermes/workspace/` |
| 下载失败无报错 | 文件名含中文/空格 | 重命名为英文 或 用尖括号 |
| `File is outside the session and Hermes workspaces` | 文件不在 workspace 内 | 用 MEDIA 协议 或 拷贝到 `workspace/` |
| `.md` 文件无法下载 | 在 cache 目录被限制 | 移到 `workspace/` 目录 |
| `File is outside the session` | Markdown 链接路径不在 workspace | 用 MEDIA 协议 或 移到 workspace |
| MEDIA 路径返回纯文本 | Gateway 未完成初始化 | 等待 5-10 秒或 `hermes gateway restart` |
### 两种交付方式对比
......@@ -46,7 +47,6 @@ Hermes detects the `MEDIA:` prefix, validates the path against security rules, a
├─ 文件在 ~/.hermes/cache/* ?
│ └─ 是 → 用 MEDIA 协议 (推荐)
│ 或 重命名后 Markdown 链接 (不推荐)
├─ 文件在 ~/.hermes/workspace/ ?
│ └─ 是 → 用 Markdown 链接 (推荐)
......@@ -56,24 +56,11 @@ Hermes detects the `MEDIA:` prefix, validates the path against security rules, a
└─ 复制文件到 workspace 或 cache,然后发送
```
### 中文文件名处理规则
| 文件名特征 | MEDIA 协议 | Markdown 链接 |
|-----------|-----------|--------------|
| 纯英文无空格 | ✅ `MEDIA:~/file.docx` | ✅ `[file](~/file.docx)` |
| 英文有空格 | ✅ `MEDIA:~/my file.docx` | ⚠️ `[file](</path/my file.docx>)` |
| 含中文 | ✅ `MEDIA:~/文件.docx` | ⚠️ `[文件](</path/文件.docx>)` |
| 含特殊字符 | ✅ `MEDIA:~/文件 (1).docx` | ⚠️ `[文件](</path/文件 (1).docx>)` |
**最佳实践**
- **MEDIA 协议**:优先使用,支持所有文件名
- **Markdown 链接**:仅用于 workspace 目录,中文文件名必须用尖括号
---
## 1. Path Validation
The gateway's `validate_media_delivery_path()` function enforces:
The gateway's `validate_media_delivery_path()` function enforces a unified security policy for both MEDIA protocol and Markdown link paths.
### Allowed Directories (safe roots)
......@@ -125,7 +112,9 @@ Use `HERMES_MEDIA_ALLOW_DIRS` env var to add custom allowlisted roots in strict
---
## 2. Windows Path Format (CRITICAL GOTCHA)
## 2. Path Format
### 2.1 Windows Path Format (CRITICAL GOTCHA)
**On Windows, always use `C:/Users/...` format, NOT `/c/Users/...` (git-bash/MSYS format).**
......@@ -138,7 +127,7 @@ The MEDIA validation runs through Python's `Path.resolve(strict=True)`, which on
Forward slashes (`C:/Users/...`) work fine — both `Path.resolve()` and Windows APIs accept them.
### Quick fix when coming from terminal
#### 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:
......@@ -150,13 +139,11 @@ cp /c/Users/Administrator/report.pdf ~/.hermes/cache/documents/
MEDIA:C:/Users/Administrator/.hermes/cache/documents/report.pdf
```
---
## 2.5 空格与中文文件名处理 (CRITICAL)
### 2.2 空格与中文文件名处理 (CRITICAL)
**文件名包含空格、中文或特殊字符时,Markdown 链接需要特殊处理。**
### 问题示例
#### 问题示例
```markdown
# ❌ 失败 - 中文文件名
......@@ -170,7 +157,7 @@ MEDIA:C:/Users/Administrator/.hermes/cache/documents/report.pdf
[my report.pdf](</home/lcai/.hermes/workspace/my report.pdf>)
```
### 规则总结
#### 规则总结
| 文件名特征 | MEDIA 协议 | Markdown 链接 (无尖括号) | Markdown 链接 (有尖括号) | 推荐做法 |
|-----------|-----------|---------|---------|---------|
......@@ -179,52 +166,33 @@ MEDIA:C:/Users/Administrator/.hermes/cache/documents/report.pdf
| 含中文 | ✅ | ❌ | ✅ | **MEDIA 优先 或 Markdown 用尖括号** |
| 含特殊字符 `()[]#?` | ✅ | ❌ | ✅ | **MEDIA 优先 或 Markdown 用尖括号** |
### 三种解决方案
#### 三种解决方案(按推荐优先级排序)
#### 方案 A: 使用 MEDIA 协议(推荐用于中文文件名)
**方案 A: 使用 MEDIA 协议(推荐)**
```
MEDIA:/home/lcai/.hermes/workspace/2026 年申请表.docx
```
**最佳**:MEDIA 协议原生支持中文文件名,无需特殊处理
✅ 原生支持中文文件名,无需特殊处理
#### 方案 B: 使用尖括号(Markdown 链接的中文文件名)
**方案 B: 使用尖括号(Markdown 链接)**
```markdown
[2026 年申请表](</home/lcai/.hermes/workspace/2026 年申请表.docx>)
```
⚠️ **可行**:但必须用尖括号包裹路径
⚠️ 必须用尖括号包裹路径
#### 方案 C: 重命名为英文(最佳兼容性)
**方案 C: 重命名为英文(最佳兼容性)**
```bash
# 重命名文件
mv "2026 年申请表.docx" "2026-Application-Form.docx"
```
然后发送:
```markdown
[2026 年申请表](/home/lcai/.hermes/workspace/2026-Application-Form.docx)
```
**最兼容**:避免所有格式问题
### 最佳实践
✅ 避免所有格式问题
1. **优先使用 MEDIA 协议处理中文文件名**
```
MEDIA:/home/lcai/.hermes/workspace/2026 年湖南省未来产业创新发展优秀典型案例申请表.docx
```
✅ 原生支持,无需特殊处理
#### 文件位置选择
2. **如果必须用 Markdown 链接,始终用尖括号**:
```markdown
[申请表](</path/to/文件.docx>) # ✅ 正确
[申请表](/path/to/文件.docx) # ❌ 错误
```
3. **避免特殊字符**:
- ✅ `2026-申请表_v1.docx`
- ❌ `2026 年 (最终版)[确认].docx`
4. **文件位置选择**:
- **workspace 目录**:✅ 支持 Markdown 链接和 MEDIA 协议
- **cache 目录**:⚠️ 仅推荐 MEDIA 协议,部分格式(如 `.md`)可能被限制
- **workspace 目录**:✅ 支持 Markdown 链接和 MEDIA 协议
- **cache 目录**:⚠️ 仅推荐 MEDIA 协议,部分格式(如 `.md`)可能被限制
---
......@@ -246,54 +214,10 @@ The gateway auto-detects the content type from the file extension:
### 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
- File/document delivery works, but may have size limits (~100MB)
- Voice messages supported via `MEDIA_VOICE` type
- Path validation is strict — files MUST be in a cache directory and path MUST resolve through Python
### 中文文件名和特殊字符处理 (CRITICAL)
**当文件名包含中文、空格或特殊字符时,Markdown 链接需要尖括号包裹。**
#### 问题示例
```markdown
[2026 年湖南省未来产业创新发展优秀典型案例申请表.docx](/home/lcai/.hermes/workspace/2026 年湖南省未来产业创新发展优秀典型案例申请表.docx)
````
❌ **失败**:中文字符导致 Markdown 解析器无法正确识别路径
#### 解决方案:使用尖括号包裹路径
```markdown
[2026 年湖南省未来产业创新发展优秀典型案例申请表.docx](</home/lcai/.hermes/workspace/2026 年湖南省未来产业创新发展优秀典型案例申请表.docx>)
```
✅ **成功**:尖括号 `<>` 告诉 Markdown 解析器这是一个完整的路径字符串
#### 规则总结
| 文件名类型 | 推荐格式 | 示例 |
|-----------|---------|------|
| 纯英文、无空格 | 普通格式 | `[file.pdf](/path/to/file.pdf)` |
| 包含中文 | **MEDIA 协议优先** 或 尖括号 | `MEDIA:/path/to/文件.docx` 或 `[文件](</path/to/文件.docx>)` |
| 包含空格 | **MEDIA 协议优先** 或 尖括号 | `MEDIA:/path/to/my file.pdf` 或 `[file](</path/to/my file.pdf>)` |
| 包含特殊字符 | **MEDIA 协议优先** 或 尖括号 | `MEDIA:/path/to/文件 (1).pdf` 或 `[文件](</path/to/文件 (1).pdf>)` |
#### 最佳实践
1. **中文文件名优先使用 MEDIA 协议**:
```
MEDIA:/home/lcai/.hermes/workspace/2026 年申请表.docx
```
✅ 原生支持,无需特殊处理
2. **如果必须使用 Markdown 链接,始终用尖括号**:
```markdown
[申请表](</home/lcai/.hermes/workspace/申请表.docx>)
```
3. **验证文件可访问性**:在发送前检查文件是否存在
```bash
ls -la "/home/lcai/.hermes/workspace/2026 年湖南省未来产业创新发展优秀典型案例申请表.docx"
```
4. **文件位置选择**:
- **workspace 目录**:✅ 支持 Markdown 链接和 MEDIA 协议
- **cache 目录**:⚠️ 仅推荐 MEDIA 协议,部分格式可能被限制
- 中文文件名处理详见 [2.2 节](#22-空格与中文文件名处理-critical)
### Telegram
- MEDIA protocol fully supported
......@@ -369,45 +293,29 @@ MEDIA:C:/Users/YourUsername/.hermes/cache/documents/your-file.txt
#### Scenario 1: Sending Terminal Output
```bash
# Run command and capture output
your-command > /tmp/output.log
# Move to cache
cp /tmp/output.log ~/.hermes/cache/documents/
# Send to user
MEDIA:C:/Users/YourUsername/.hermes/cache/documents/output.log
```
#### Scenario 2: Sending a Screenshot
```bash
# Capture screenshot (macOS example)
screencapture -i ~/.hermes/cache/images/screenshot-$(date +%s).png
# Send to user
MEDIA:~/.hermes/cache/images/screenshot-1234567890.png
```
#### Scenario 3: Sending Generated Code
```bash
# Write code to file
cat > ~/.hermes/cache/documents/solution.py << 'EOF'
# Your generated code here
EOF
# Send to user
MEDIA:~/.hermes/cache/documents/solution.py
```
#### Scenario 4: Sending Data Export
```bash
# Export data to CSV
python export_data.py --output /tmp/data.csv
# Move to cache
cp /tmp/data.csv ~/.hermes/cache/documents/
# Send to user
MEDIA:C:/Users/YourUsername/.hermes/cache/documents/data.csv
```
......@@ -417,51 +325,17 @@ MEDIA:C:/Users/YourUsername/.hermes/cache/documents/data.csv
-`error-log-2026-07-22-143052.txt`
-`temp.txt`
2. **Avoid Chinese/special characters in filenames when possible**:
- ✅ `2026-Hunan-Innovation-Application.docx`
- ❌ `2026 年湖南省未来产业创新发展优秀典型案例申请表.docx`
If you must use Chinese names, **always wrap the path in angle brackets**:
```markdown
[申请表](</path/to/申请表.docx>)
```
3. **Clean up old cache files**: Periodically remove files older than 24 hours
2. **Clean up old cache files**: Periodically remove files older than 24 hours
```bash
find ~/.hermes/cache -type f -mtime +1 -delete
```
4. **Verify file exists before sending**:
3. **Verify file exists before sending**:
```python
python -c "from pathlib import Path; print('✅' if Path('path').exists() else '❌')"
```
5. **For large files**: Check platform limits
- WeChat: ~100MB
- Telegram: ~2GB
- Discord: ~25MB (free), ~500MB (Nitro)
6. **Don't send sensitive data**: Never place credentials, tokens, or private keys in cache directories
7. **Test with Chinese filenames**: If a file with Chinese characters fails to download:
- **优先用 MEDIA 协议**:`MEDIA:/path/to/文件.docx`
- 或用尖括号:`](</path/to/文件.docx>)`
- 或重命名为英文
8. **File location matters for delivery method**:
- **Workspace** (`~/.hermes/workspace/`): ✅ 支持 MEDIA 协议和 Markdown 链接
- **Cache** (`~/.hermes/cache/documents/`): ⚠️ 推荐 MEDIA 协议,Markdown 链接部分格式受限
如果需要用 Markdown 链接发送文件,**优先放在 workspace 目录**:
```bash
# 推荐:放在 workspace 用于 Markdown 链接
cp file.docx ~/.hermes/workspace/
# 然后用:[file](</home/lcai/.hermes/workspace/文件.docx>)
# 或者:放在 cache 用于 MEDIA 协议
cp file.docx ~/.hermes/cache/documents/
# 然后用:MEDIA:~/.hermes/cache/documents/文件.docx
```
4. **Don't send sensitive data**: Never place credentials, tokens, or private keys in cache directories
---
......@@ -484,15 +358,7 @@ If MEDIA delivery fails, check in this order:
4. **Verify location**: file must be under `~/.hermes/cache/<type>/` or another safe root
5. **Check filename for Chinese/special characters**:
- If filename contains 中文、空格 or `()[]#?`, **must use angle brackets**:
```markdown
[文件](</path/to/文件.docx>)
```
- Or rename to English:
```bash
mv "文件.docx" "file.docx"
```
5. **Check filename for Chinese/special characters**: 详见 [2.2 节](#22-空格与中文文件名处理-critical)
6. **Check file permissions**: must be a regular file (not a symlink to a denied location)
......@@ -500,11 +366,12 @@ If MEDIA delivery fails, check in this order:
```bash
hermes gateway restart
```
Helps if cache dirs were created mid-session
Helps if cache dirs were created mid-session, or gateway not fully initialized
8. **Check workspace security policy**:
- Markdown links only work for files in the **session's workspace** (not necessarily `~/.hermes/workspace/`)
- Files in `~/.hermes/cache/` will fail with "File is outside the session"
- WebUI 文件下载链接只允许 **session workspace**`~/.hermes/workspace/` 内的文件
- `~/.hermes/cache/` 不在 workspace 内,会报错 `File is outside the session and Hermes workspaces`
- 解决方案:用 MEDIA 协议 或 拷贝到 workspace
- See: `references/workspace-security-policy.md` for details
---
......@@ -513,4 +380,4 @@ If MEDIA delivery fails, check in this order:
- [`hermes-agent`](hermes-agent): Configure Hermes itself
- [`markdown-viewer`](markdown-viewer): Create visual diagrams and charts
- [`computer-use`](computer-use): Take screenshots for delivery
- [`computer-use`](computer-use): Take screenshots for delivery
\ No newline at end of file
# 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