Issue #86190
问题:Plugin skills 在 sandbox 模式下无法被 agent 读取。
错误信息:
Path escapes sandbox root: ~/.openclaw/plugin-skills/xxx/SKILL.md
原因分析
Plugin Skills 的存储方式
Plugin skills 存储在 ~/.openclaw/plugin-skills/ 目录下,以 symlink 形式存在:
~/.openclaw/plugin-skills/browser-automation → /path/to/plugin/skills/browser-automation
这是 publishPluginSkills 函数创建的,用于暴露 plugin 提供的 skills。
Sandbox 的文件访问机制
Sandbox 模式下,agent 运行在 Docker 容器中。容器的 read tool 只能访问 /workspace 目录。
为了让 agent 能读取 skills,syncSkillsToWorkspace 函数负责将 skills 复制到容器的 /workspace/skills/ 目录。
同步失败的原因
const sourceDir = entry.syncSourceDir ?? entry.skill.baseDir;对于 plugin skills:
entry.skill.baseDir= symlink 路径entry.syncSourceDir= undefined(未设置)
结果:尝试从 symlink 路径复制,但 symlink 路径是 host 路径,容器无法访问。
为什么其他 skills 不受影响
Core skills 直接存储在 ~/.openclaw/skills/ 目录下,是真实的目录路径,syncSourceDir 会被正确设置。
只有 plugin skills 是 symlink,才会触发这个问题。
修复方案
代码改动
在 loadGeneratedPluginSkillRecords 函数中,为 plugin skill records 设置 syncSourceDir:
function setSyncSourceForPluginSkill(
record: LoadedSkillRecord,
syncSourceDir: string,
): LoadedSkillRecord {
return {
...record,
syncSourceDir,
syncDirName: path.basename(record.skill.baseDir),
};
}为什么不修改 baseDir
baseDir 会出现在 <available_skills> prompt 中,显示给 agent。如果改成真实路径,会暴露 host 文件系统结构。
因此:
baseDir保持 symlink 路径(用于 display)syncSourceDir设置为真实路径(用于同步)
Real Behavior Proof
对于 sandbox 相关修复,证明需要覆盖完整的验证链条:
| 证明层面 | 内容 | 方式 |
|---|---|---|
| 环境正确 | Sandbox 容器运行 | docker ps |
| 中间步骤正确 | Skills 同步到容器 | ls /workspace/skills/ |
| 最终结果正确 | 文件可读 | head SKILL.md |
证明步骤
# 1. 启动 sandbox 环境
node openclaw.mjs gateway
# 2. 触发 skill 加载
node openclaw.mjs tui --local
# 3. 验证容器运行
docker ps
# 4. 验证 skills 同步
docker exec <container_id> ls -la /workspace/skills/
# 5. 验证文件可读
docker exec <container_id> head -30 /workspace/skills/browser-automation/SKILL.mdClawSweeper 审查
第一次提交的证明不完整(只有启动日志)。补充三层证明后:
rating: 🦞 diamond lobster ✨ media proof bonus
proof: sufficient
测试
新增测试文件,覆盖:
- Symlinked plugin skill 同步到 sandbox workspace
- 多个 plugin skill roots
- Escaped symlink targets 不被同步
- Sandbox prompt paths 不泄露 host 路径
Test Files: 2 passed (2)
Tests: 6 passed (6)
合并
Maintainer: steipete
Commit: d8f6d65
关键收获
Real Behavior Proof 的完整性
对于 sandbox 相关修复,只证明启动成功或测试通过是不够的。需要证明完整的验证链条。
Symlink 在 Sandbox 场景下的处理
Symlink 的特殊之处:
fs.realpath()可以获取真实路径fs.cp()默认不解引用 symlink- 需要显式指定复制源为真实路径
相关链接
最后更新: 2026-05-27