hermes profile update 以前会把你的技能和定时任务一起删掉

你们团队把一个 Hermes agent 打包成了 git 仓库,谁需要就 hermes profile install 一下,立刻得到一整套人格、技能、定时任务和配置。你在这个 profile 上又加了两样自己的东西:一个帮团队查合规的技能,和一个每天早上发日报的定时任务。几天后上游发布了新版本,你照着文档跑了一条 hermes profile update ——升级成功,技能和定时任务也没了。
这不是你的操作问题。PR #111344 修的就是这个 P1 级别的数据丢失:更新一个 distribution 时,skills/ 和 cron/ 曾被当成可以整目录替换的东西。
先分清两种“共享一个 profile”的方式
Hermes 的 profile 就是一份独立的 agent:自己的配置、凭据、记忆、技能与定时任务。把它分享给别人有两条路,用途并不一样:
- distribution(git 仓库):作者准备一份
distribution.yaml,别人用hermes profile install <repo>安装,之后用hermes profile update跟随上游更新。适合“这是一个会持续演进的产品”的场景:团队内部的合规 agent、社区发布的 agent、部署到五台机器上的同一个 agent。 - export 文件(
.tar.gz快照):用/export导出、/import导入,或者走hermes profile export/import。适合“我现在就想让你拿到我的这套配置”,没有版本概念。
会撞上这个 bug 的是第一条路,因为只有它会覆盖式更新。两者各自的边界,我们的多实例 profile 指南里讲得更细,命令层面的细节在 hermes profile 命令参考里。
为什么会删:整目录替换
distribution 的更新逻辑,过去对“归作者所有的目录”采用的是先删后拷:把目标端的 skills/ 整个删掉,再把新 payload 的 skills/ 整个拷过去。这个做法在作者的视角里完全正确——他要保证“我退休掉的文件真的消失”,而不是留下一堆陈年旧文件。
但它无法区分两件事:目录里哪些根是作者发的,哪些是用户自己加的。skills/curator-custom/ 是你加的,在作者眼里它和 skills/shipped/ 没有区别,于是在整目录替换里一起没了;cron/mine.json 同理。而 hermes profile update 的语义是“升级”,不是“重置”,用户没有任何理由预期自己的东西会消失。
现在的规则:把顶层目录当成“一堆根”,逐根合并
修复把顶层归作者所有的目录(skills/、cron/,以及 manifest 里显式声明的目录)当作一堆独立的根来处理,而不是一个整体(hermes_cli/profile_distribution.py 的 _copy_dist_payload):
- 新 payload 里有的根,整个替换掉对应的那一个(作者退休的文件确实会消失);
- 新 payload 里没有的根,原样保留——那不属于作者的发布内容;
- 对
update和install --force一致生效,默认的归作者目录集与显式配置distribution_owned: [skills]都走同一套逻辑。
对第 2 点最直观的验证来自 PR 里的实测对照:
| 场景 | 修复前 | 修复后 |
|---|---|---|
你加的 skills/curator-custom/ |
被删除 | 保留 |
你加的 cron/mine.json |
被删除 | 保留 |
| 作者发布的技能更新到 v2 | 更新 | 更新 |
| 作者退休的旧文件 | 消失 | 消失 |
这四条放在一起才是完整的行为:保护用户新增和跟上作者发布同时成立,不需要你在两者之间选一个。
三个边界情况,值得单独知道
嵌套白名单仍然精确替换。 如果 manifest 里写的是 skills/compliance/ 这样的嵌套路径,替换范围就严格限定在那一条路径上,不会波及兄弟根。
符号链接会被明确拒绝,而不是“写穿”。 如果把 skills/ 做成了指向共享目录的软链接,更新会在第一次写入之前就抛出 DistributionError。这一点比它听起来重要:如果这里被写穿,共享目录里的内容会被覆盖,而 profile 可能停留在“更新了一半”的状态——比直接报错难排查得多。
.env.template 仍然落成 .env.EXAMPLE。 落盘前会先解除链接再拷贝,避免通过软链接写进别处,同时保证它永远不会覆盖你真正的 .env。
你要做什么
升级到包含这个修复的版本后,直接重跑更新即可,不需要额外迁移:
hermes profile update
想先确认这套 profile 的归作者范围,看 manifest 里的 distribution_owned 字段;如果里面写的是嵌套路径(如 skills/compliance/),替换范围就限定在那条路径。命令的完整参数(包括 --force 等)可以用:
hermes profile update --help
如果你在这之前已经因为整目录替换丢过自己加的技能或定时任务,那部分内容不会自动回来——请从备份或另一台机器上恢复;技能目录相关的那篇里有手动重建技能位置的做法,而 cron 任务随 profile 走的那篇讲清了定时任务该放在哪里。
小结
一句话记住这个修复:hermes profile update 现在只覆盖作者发布的那部分,你在这个 profile 上加的技能和定时任务留在原地。对维护团队共享 agent 的人来说,这意味着可以放心在本地做定制,而不必担心每次上游更新都清一次场。