DeepSeek Harness 禁用插件后新建会话报 Unknown agent preset 怎么修
「我只是禁用了一个不常用的插件,DSH 还能启动,但所有新会话都建不出来。」 这是 DeepSeek Harness(DSH)里很典型的一类连锁:被禁用的插件提供某个服务,preset 行又把这个服务写进了 inject,而 inject 在 Cordis 里是硬依赖——服务没了,preset 行不报错,只是永久 pending。宿主启动不依赖 preset,所以看起来一切正常;新建会话要解析 preset,才把问题暴露出来。
DeepSeek Harness 症状组合:能启动、不能新建会话
一句话结论:同时看到「preset 行 pending」「插件列表无报错」「新建会话报 Unknown agent preset」这三件事,就可以按本文处理。 单独出现任意一条,都可能是别的原因。
- 插件列表里有一行或几行显示
pending (waiting for service: xxx)。 - 宿主正常启动,旧会话甚至还能打开。
- 新建会话时报
agent-preset/not-found: Unknown agent preset: standard。 - 重新启用被禁用的插件后,症状立刻消失。
据讨论里的复现,vendor/cordis/src/registry.ts:71-88 的 Inject 归一化没有 required/optional 之分,写进 inject 的键一律当硬依赖;app-boot/src/index.ts:323/331 的 requiredIds 默认是空数组,:399-405 又先判 requiredIds 再排除 pending,所以最后只剩一条 warning(#10099)。preset 的常规配置可以对照 agent preset。
为什么 pending 不报错、却能让 DSH 新会话失败
一句话结论:Cordis 的 pending 是「等依赖」,不是「失败」;宿主启动不经过 preset 解析,新建会话才经过,所以故障被推迟到使用时才出现。
把链路拆开:
- 插件 A 被禁用,它提供的服务 S 不存在。
- preset 行的
inject里有 S,于是该行不启动,停在 pending。 - 宿主启动只加载基础 bundle,不要求 preset 行就绪,启动成功。
- 新建会话要选一个 preset,发现
standard未注册,报agent-preset/not-found。
这也是为什么很多人以为是「会话数据坏了」,其实只是 preset 没起来。
DeepSeek Harness 的三种修复路径
一句话结论:马上要开会话就重新启用提供方,确实想禁用就 patch 掉 inject,长期维护就改 loader 语义。
修法一:重新启用提供服务的插件
这是最快恢复,但等于放弃禁用;适合你只是临时关掉、并不介意它继续运行的情况。
- 打开插件列表,找到处于 pending 的那一行,记下它等待的服务名;预期能看到一个具体服务名。
- 在列表里搜索提供该服务的插件,确认它当前是 disabled;预期能唯一对应到一个提供方。
- 重新启用该插件,等待状态变为 active;预期 pending 行随之启动。
- 新建一个会话;预期不再报
Unknown agent preset。 - 如果该插件本身启动失败,先修它,再谈禁用;预期提供方健康后两个症状一起消失。
修法二:把 preset 行的 inject 置空或改成可选
这是「既要禁用插件、又要能新建会话」的折中,适合 preset 并不真正依赖该服务的情况。 用 home 层 patch 覆盖,不动插件本体。
- 打开
~/.dsh/profiles/<profile>/cordis.patch.yml;预期进入 profile 层而不是插件源码。 - 找到 preset 行对应的 id,确认它当前的
inject列表;预期能看清每个硬依赖。 - 加一条同名 patch,把
inject设为null(顶层整替换,不是深度合并);预期整个键被替换。 - 重启 DSH;预期该行不再 pending。
- 新建会话验证;预期 preset 能解析,同时留意少了哪些工具。
这条修法会一并去掉该行所有注入依赖,包括它真正需要的服务,只适合 preset 行本身很薄的情况;patch 是整块替换,漏写其他字段可能把整行配置清空,改前要备份;后续插件升级改了行 id 时,patch 会失效。
修法三:改 loader,支持可选 inject 与超时降级
根治方向是让 inject 能表达 required/optional,并在依赖缺失时降级而不是永久 pending。 适合维护 fork 或提交上游。
- 在
vendor/cordis/src/registry.ts的 Inject 归一化处,支持{ required: [...], optional: [...] }形式;预期两类依赖可区分。 - 对 optional 依赖,不阻塞启动;读取时用
get()并在缺失时返回 undefined;预期该行仍能启动。 - 在
app-boot/src/index.ts:399-405的 pending 判定里,把「仅 optional 未就绪」的行视为可用;预期它变为 active。 - 给 pending 行补一条带服务名的 warning,而不是完全静默;预期日志能看出降级原因。
- 补测试:禁用提供方后,preset 行仍能启动并给出降级结果;预期测试稳定通过。
DSH 插件状态恢复后的验证与避坑
一句话结论:重启后看一次 pending 列表、建一个会话、再做一次禁用/启用往复,才能确认状态不是暂时正常。 验证步骤:
- 重启后看插件列表,确认目标行不再是 pending;预期状态为 active 或已按预期移除。
- 新建会话,发送一条会触发工具调用的消息;预期 preset 能解析、工具能执行。
- 打开旧会话;预期没有因为 patch 改动丢失原有工具。
- 再做一次「禁用 → 启用」往复;预期状态能正确切换,而不是残留 pending。
避坑要点:
- 不要把 pending 当成无害:它意味着某个能力没有加载,长期会以「功能莫名消失」的形式出现。
requiredIds默认为空是当前实现的特点,不要依赖启动失败来发现问题;需要自己监控 pending 列表。- patch 的优先级按文件顺序,后写的覆盖前写的;排查时先确认没有第二个 patch 把 inject 写回去。
- 讨论里没有官方修复版本,0.2.1-rc.2 仍可复现;升级后以 release notes 为准,别把一个 DSH插件 的旧版 pending 当成新版行为。
DeepSeek Harness agent preset 常见问题
Q:DeepSeek Harness 禁用插件后新建会话报 Unknown agent preset,怎么恢复? A:DeepSeek Harness 报 Unknown agent preset,是因为 preset 行把被禁用插件提供的服务写进了 inject,而 inject 在 Cordis 里是硬依赖,服务缺失时该行停在 pending 而不报错。启动不需要 preset,所以 DSH 能起来;新建会话必须解析 preset,于是报未知预设。重新启用该插件,或把 preset 行的 inject 置空。
Q:DeepSeek Harness 插件列表 pending waiting for service,日志没报错,为什么? A:DeepSeek Harness 的 pending 表示插件的 inject 依赖尚未就绪,常见于被依赖的服务被禁用、版本不匹配或依赖行启动失败。与 failed 不同,pending 不阻塞宿主启动,所以最容易被忽略;看它依赖的服务名,再核对提供方。
Q:DeepSeek Harness 里 inject 和可选依赖有什么区别,为什么不能混写? A:DeepSeek Harness 的 inject 声明的是硬依赖,Cordis 会等它可用才启动本行;可选依赖应当用 get() 读取或声明为可选服务。把可选工具写进 inject,会在工具被禁用时让整行 pending,表现为「插件没坏但功能消失」。
Q:改 profile 的 inject: null 后 DeepSeek Harness 重启仍 pending,怎么办? A:DeepSeek Harness 重启仍 pending,先确认 patch 是整块替换而不是深度合并,inject: null 必须落在正确的行 id 上。再检查是否有第二个 patch 写回了 inject,patch 按顺序覆盖;最后清一次 profile 构建缓存再启动。
常见问题
DeepSeek Harness 报 Unknown agent preset,是因为 preset 行把被禁用插件提供的服务写进了 inject,而 inject 在 Cordis 里是硬依赖,服务缺失时该行停在 pending 而不报错。启动不需要 preset,所以 DSH 能起来;新建会话必须解析 preset,于是报未知预设。重新启用该插件,或把 preset 行的 inject 置空。
DeepSeek Harness 的 pending 表示插件的 inject 依赖尚未就绪,常见于被依赖的服务被禁用、版本不匹配或依赖行启动失败。与 failed 不同,pending 不阻塞宿主启动,所以最容易被忽略;看它依赖的服务名,再核对提供方。
DeepSeek Harness 的 inject 声明的是硬依赖,Cordis 会等它可用才启动本行;可选依赖应当用 get() 读取或声明为可选服务。把可选工具写进 inject,会在工具被禁用时让整行 pending,表现为「插件没坏但功能消失」。
DeepSeek Harness 重启仍 pending,先确认 patch 是整块替换而不是深度合并,inject: null 必须落在正确的行 id 上。再检查是否有第二个 patch 写回了 inject,patch 按顺序覆盖;最后清一次 profile 构建缓存再启动。
相关术语
- inject
- inject 是 Cordis 插件声明依赖服务的方式,写进 inject 的服务名是硬依赖,未就绪时插件停在 pending 而不是报错。它适合必需服务,不适合可选能力。— https://github.com/deepseek-ai/deepseek-harness/discussions/10099
- agent preset
- agent preset 是 DeepSeek Harness 里一组预置的 agent 配置,包含模型、工具与 persona 等。新建会话时必须能解析出一个 preset,解析失败会报 agent-preset/not-found。— https://github.com/deepseek-ai/deepseek-harness/discussions/10099
- pending
- pending 是 Cordis 插件的等待状态,表示 inject 依赖尚未满足。与 failed 不同,pending 不阻塞宿主启动,因此经常表现为「服务能开、功能不可用」。— https://github.com/deepseek-ai/deepseek-harness/discussions/10099
来源
- #10099 — Loader inject 没有 optional/timeout 降级,禁用插件后 preset 行永久 pending· deepseek-ai(GitHub Discussions)
- DeepSeek Harness 官方仓库· deepseek-ai(GitHub)