DSH plugin 接入 SettingsNamespace:三层解析、owner scope 与热提交
DSH plugin 的设置不是另写一套表单,而是把插件自己的 Config 字段投影成 UI,键就是当前 profile 里那个条目的 id。 理解这一点能避开两个常见误解:第一,命名空间不是包名——同一个包挂载两次、条目 id 不同,就会有两个独立表单;第二,写入不是直接改文件——设置服务校验完整 Config、比对修订号,再通过配置编辑器以 Cordis patch 持久化。业务代码读取配置仍然走自己 Config 引用的 .get(),表单只是同一份数据的另一个视图。
DSH plugin 的设置表单从哪来:Config 投影与命名空间
一句话结论:设置服务把活动 profile 条目的 volatile Config 字段投影成表单,消费者读的仍是自己的 Config 引用。
- 投影来源:设置服务从当前活动 profile 的条目里取 Config 字段,生成表单描述符。
- 命名空间规则:命名空间是条目在本 profile 内的本地 id;多个实例只要条目 id 不同,表单就互相独立。
- 普通字段被排除:不是所有字段都会进表单,只有可编辑的 volatile 字段才出现。
- 业务侧读值:插件在自己的 Config 引用上调用
.get(),不要绕到设置服务去读最终值。 - 持久化路径:配置编辑器通过 Cordis patch 写回,因此改动会落在 profile 的补丁层,而不是某个私有配置文件。
DSH plugin 的三层值与修订号:这个值到底从哪来
一句话结论:描述符同时给继承值、显式覆盖值与实际值,再加一个修订号——配置来源与并发冲突都能一眼看清。
| 字段 | 含义 | 用途 |
|---|---|---|
| 继承值 | 来自组合层默认、未被显式覆盖的值 | 解释「默认是多少」 |
| 显式覆盖值 | profile 里明确写下的值 | 解释「谁改过」 |
| 实际值 | 按层解析后的最终结果 | 插件运行时真正用的值 |
| 乐观修订号 | 本次读取的版本标记 | 写入时防覆盖 |
- 排查配置不符:先看实际值是否等于显式覆盖值,再确认继承值来自哪一层。
- 写入必须带修订号:
expectedRevision不传表示无条件写,传了就必须是刚读到的那个。 - 过期即拒绝:每次写入都会验证完整 Config,并在持久化前拒绝过期修订号,不会「尽力写入」。
- 冲突处理:重新
describe拿新修订号,合并后再提交,而不是原地重试旧修订号。
DSH plugin 的三种写入:update、replace 与 mutate 怎么选
一句话结论:改字段用 update、整段重填用 replace、按路径改数组或避开秘密值用 mutate。
update(ns, patch, expectedRevision?):把提交的字段合并进条目的配置,未提交的字段保持不变。replace(ns, section, expectedRevision?):先把即时字段重置为继承配置,再应用提交的整段表单值——适合「恢复默认后整体重填」。mutate(ns, ops, expectedRevision?):按路径独立编辑;客户端响应里没有的秘密值会被保留,不会因为回传而丢失;把某个数组下标 unset 会删除该元素。- 统一校验:三者都会验证完整 Config,非法组合在写入前被拒。
- 选型判据:不确定客户端是否拿到全部字段时,优先
mutate,避免把脱敏后的空值写回去。
DSH plugin 实例级页面策略:configure 与 owner scope
一句话结论:configure 只改这个实例的页面呈现策略,不碰 Config;归属默认是调用方 fiber。
const dispose = ctx.settings.configure({ auto: true }, owner)
// auto 默认 true;返回 disposer,记得挂到插件的 effect 上
- 只影响呈现:策略控制自动页面等 UI 行为,不改变配置值。
- 一个实例只能注册一次:重复注册会抛错,说明你在两个地方各配了一遍。
- 显式指定 owner:默认归属调用方 fiber;要在别的插件实例上注册策略时传
owner。 - 用 disposer 清理:把返回的 disposer 注册进 effect,插件卸载时策略一起撤销。
- 需要原生编辑时:
prepareDocument()返回已存在的 profile patch 路径,供外部编辑器打开。
DSH plugin 的远程读写与脱敏:settingsController
一句话结论:远程命名空间背后是 settingsController,读一律脱敏,写失败被分成冲突与拒绝两类。
- 读强制脱敏:每个远程读都带
redactSecrets: true,role('secret')的字段不可能出现在响应里。 - 写复用同一套语义:远程写暴露 merge、replace 与路径寻址三种操作,与本地 API 对应。
- 错误分类:提供方拒绝会被归为
settings/conflict(修订号冲突)或settings/rejected(内容被拒),并带上服务原话。 - 两个事件别混用:
settings/document-updated表示某个条目的表单值、可用性或页面策略变了,UI 收到后应重新读取 schema、解析值与修订号;只有需要刷新注册信息时才用loader/volatile-update。 - 验证:改一个字段后观察
settings/document-updated是否带新修订号,能确认整条链路通。
DSH plugin 排查注意事项
一句话结论:设置类问题先分「值不对」「写不进」「看不见字段」三类,分别对应解析、修订号与 schema。
- 值不对 → 对比继承值、显式覆盖值、实际值三层,判断是默认还是覆盖问题。
- 写不进 → 查修订号是否过期、Config 校验是否失败。
- 看不见字段 → 检查该字段是否属于可编辑的 volatile 字段。
- 秘密值被清空 → 用了
update或replace回传脱敏空值;改用mutate按路径编辑。 - 同包两个实例互相影响 → 确认命名空间用的是条目 id,而不是包名。
- 修复状态如实:本文命名空间定义、三层值、三种写入语义与远程脱敏规则均取自官方「插件配置表单」页面;
ctx.settings与ctx.settingsController的完整签名以该页 Cordis API 区块与你所用版本为准。
常见问题
DSH plugin 的 SettingsNamespace 是当前 profile 中可唯一定位条目的本地 id,不是 npm 包名。同一个包被挂载多次、条目 id 不同时,每个实例都有独立的设置表单。
DSH plugin 的设置描述符同时给出继承值、显式 profile 覆盖值与最终实际值:继承值来自组合层提供的默认,显式覆盖是你写进 profile 的值,实际值是两者按层解析后的结果。判断「这个值从哪来」看这三个字段就够了。
DSH plugin 的设置写入有三种语义:update 合并提交字段,replace 先重置为继承配置再应用提交内容,mutate 按路径独立编辑并保留客户端未包含的秘密值。改几个字段用 update,整段重填用 replace,改数组某一项或避开脱敏字段用 mutate。
DSH plugin 的每次写入都会校验完整 Config,并在持久化前拒绝过期修订号,防止覆盖别人刚做的修改。解决办法是用 describe 重新读取 revision 再提交,而不是重试同一个修订号。
不会,DSH plugin 的每个远程读都会强制 redactSecrets,role 为 secret 的字段不可能搭在响应里返回。写操作则通过 mutate 的路径编辑避开秘密值,不需要客户端回传明文。
相关术语
- SettingsNamespace(表单命名空间)
- SettingsNamespace 是当前 profile 中可唯一定位一个条目的本地 id,用作设置表单的键。条目 id 不同即代表不同插件实例,各自拥有独立表单。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/settings
- SettingsForms(设置表单服务)
- SettingsForms 是 ctx.settings 背后的服务,把插件的 Config schema 投影成表单,并提供描述、写入与实例级页面策略。业务代码读取配置仍然走自己 Config 引用的 get()。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/settings
- 继承值(inherited value)
- 继承值是 DSH plugin 设置里来自组合层默认、尚未被 profile 显式覆盖的值。它与显式覆盖值、最终实际值一起出现在描述符中,用来解释配置来源。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/settings
- 乐观修订号(optimistic revision)
- 乐观修订号是每次读取设置时拿到的版本标记,写入时必须带回;修订号过期会被拒绝。它用乐观并发避免两个客户端互相覆盖对方的修改。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/settings
来源
- DeepSeek Harness 文档 — 插件配置表单(SettingsForms、describe/update/replace/mutate、settings 事件)· deepseek-harness.github.io
- settings 服务 README(Config 投影与配置编辑器)· deepseek-ai(GitHub)
- DeepSeek Harness 文档 — LLM 流式与适配器约定(Config 校验相关)· deepseek-harness.github.io