DSH plugin 文件系统 seam:FsTarget、FsErrorCode 与自定义后端
DSH plugin 的文件系统 seam 有一个反直觉的设计:目标标识是不透明的,消费方不该把它当路径。 resolve() 把用户路径解析成 FsTarget,里面 displayPath 可以展示,但 targetKey 是品牌化 id——本地后端用类 realpath 字符串,远程后端可能用 URI 或文件 id。写与编辑则靠另一个不透明令牌 FsVersion 做新鲜度守卫,策略插件记录它、比较它,但谁都不解释它的内容。
这套设计让「换后端」不牵动工具与策略:dsh-fs-local 换成远程实现,工具 schema 与审批逻辑都不用改。
DSH plugin 文件系统 seam 的四个部件
一句话结论:服务定义、后端、观测策略、工具消费方各管一层,替换后端不改变策略或工具 schema。
| 部件 | 职责 |
|---|---|
dsh-fs | 拥有 ctx.fs 与带可选守卫的原子文本操作 |
dsh-fs-local | 本地磁盘后端 |
dsh-fs-observation-policy | 记录观测到的存在/缺失,通过事件加新鲜度规则 |
dsh-tool-fs | 执行面向模型的 read/write/edit 并渲染窗口 |
- 没有策略插件时:
write无条件创建或覆盖,edit无条件替换字面文本。 - 策略插件通过事件介入:它裁决
fs/*waterfall,而不是提供策略方法。 - 移除策略不会坏工具:工具调用
ctx.fs并分发事件,不依赖策略服务。 - 默认应当加载:加载了
dsh-tool-fs的部署也应加载观测策略,让默认行为是「先读后写」。 - 在主干之外:文件系统属于可选能力,agent loop 不依赖它。
DSH plugin 的 FsTarget 与 FsVersion:两个不透明标识
一句话结论:路径只在 resolve 时出现一次,之后都用 FsTarget;版本令牌只用于守卫,不用于解读。
interface FsTarget {
targetKey: FsTargetKey // 不透明,禁止解析
displayPath: string // 可展示
}
- 跨能力坐标:需要与子进程共享执行世界时,用
processPath(target)、processPathFromHostPath(hostPath)、fileUrl(target)、contains(parent, child),而不是自己拼路径。 - 版本令牌:
FsVersion由后端产生,策略插件存储并比较;消费方只看相关元数据。 - 为什么这样设计:远程后端的「路径」可能是 URI 或文件 id,把标识当路径就锁死了后端。
- 验证:换一个后端跑同一套工具调用,
displayPath变了但调用约定不变——说明你没依赖路径形态。
DSH plugin 的读取路径:stat、lstat、listDir 与有上限的 readBytes
一句话结论:stat 只给元数据;需要信任边界时先 lstat 再 resolve;字节读取必须有上限。
stat返回元数据:version、type(file/directory/other)、可选size;目标不存在返回undefined。- 用 type 提前拒绝:读取前先拒绝目录与特殊文件,别等读取失败。
- 用 size 选读法:文本消费方据此选择
readText或streamText,不需要靠失败探测。 readBytes必填上限:readBytes(target, signal, maxBytes)的超限会以FS_TOO_LARGE失败,而不是截断或无界缓冲。lstat不跟随链接:resolve会跟随 symlink 以产生稳定标识;有信任规则的消费方可以先lstat,在解析前拒绝symlink。listDir只给元数据:按稳定名称顺序返回直接子项,不读内容;权限或 IO 失败以FS_PERMISSION_DENIED或FS_IO_ERROR让整个列表失败。
DSH plugin 的写入与编辑守卫:FsWriteIntent 与原子替换
一句话结论:守卫可选,省略即无条件;带守卫时先验版本再改内容,整个过程在一个临界区里。
type FsWriteIntent =
| { kind: 'createIfAbsent' }
| { kind: 'replaceIfVersion'; version: FsVersion }
createIfAbsent:目标已存在就以FS_NOT_OBSERVED拒绝——即使它在初始探测之后才出现,也拒绝。replaceIfVersion:仅在目标存在且版本匹配时替换,否则FS_STALE_VERSION。- 省略守卫:无条件创建或覆盖,不是一个隐藏的第三种意图。
- 写入结果:
FsWriteOutcome带operation(create/update)、新版本、before(不存在时为 null)与after,两者都做 LF 归一化,作为差异计算的基础。 editText是提供方级操作:不是「读 + 写」的组合;带守卫时先校验版本再字面匹配,oldString必须非空,replaceAll控制替换全部还是要求唯一匹配。- 原子性:匹配、行尾处理、陈旧检查与替换在同一个变更临界区内完成。
DSH plugin 的策略事件:两个 waterfall 与一个记录事件
一句话结论:write/edit 意图是单槽决策 waterfall,观测结果是即发即弃通知——监听方必须同步且只做副作用。
fs/write-intent与fs/edit-intent:单槽 waterfall;工具分发时附带默认 thunk(返回 undefined 即裸提供方),监听方完全决策而不调用next()。- 先到先得:slot 按注册顺序占用,由策略插件占据是部署约定而非强制不变式。
fs/observed:携带FsObservation(present 带版本 / absent),通过普通ctx.emit分发。- 监听方纪律:必须同步、只产生副作用——工具不会捕获该 emit 抛出的异常,抛错可能取代原本待返回的错误,甚至让已成功的变更返回
isError。 - 观测的所有者:策略插件通过收窄事件里的不透明 actor 推导,不需要导入工具、agent 或会话包。
DSH plugin 的 FsErrorCode 与排查注意事项
一句话结论:文件系统问题先看错误码,再看是「守卫拒绝」还是「后端失败」。
FS_NOT_OBSERVED→ 带createIfAbsent时目标已存在,改用 replace 或有条件创建。FS_STALE_VERSION→ 版本过期或目标缺失,重新 resolve 再试。FS_TOO_LARGE→ 超过readBytes的 maxBytes,改流式或提高上限。FS_PERMISSION_DENIED/FS_IO_ERROR→ 后端层问题,不是策略拒绝。- 编辑报版本错而不是匹配失败 → 正常,守卫先验版本。
- 修复状态如实:本文的四部件分工、两个不透明标识、守卫语义与事件纪律均取自官方「文件系统」页面;错误码全集与接口签名以该页 Cordis API 与你所用版本为准。
常见问题
DSH plugin 的文件系统由四部分组成:dsh-fs 拥有 ctx.fs 与带可选守卫的原子文本操作,dsh-fs-local 实现本地后端,观测策略插件记录存在或缺失状态,dsh-tool-fs 执行面向模型的读写编辑。
DSH plugin 的 targetKey 是品牌化的不透明标识,本地后端用类 realpath 字符串,远程后端可能用 URI 或文件 id。消费方只能展示 displayPath,解析 targetKey 会在换后端时立刻失效。
需要,DSH plugin 的部署在加载 dsh-tool-fs 时也应加载观测策略插件,这样默认行为才是先读后写或先读后编辑。没有策略插件时,write 会无条件创建或覆盖,edit 会无条件替换字面文本。
能,DSH plugin 的 createIfAbsent 在目标已存在时以 FS_NOT_OBSERVED 拒绝,即使目标是在提供方初始探测之后才出现的也拒绝。因为发布操作本身不得替换已有内容。
DSH plugin 的 editText 在带守卫时先校验预期版本,再做字面匹配,因此陈旧内容报 FS_STALE_VERSION 而不是匹配失败。匹配、行尾处理、陈旧检查与原子替换在同一个变更临界区内完成。
相关术语
- FsTarget(文件目标)
- FsTarget 是后端把用户路径解析成稳定标识后的结果,含不透明 targetKey 与用于展示的 displayPath。所有后续操作都接收它,而不是重新接收路径。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/filesystem
- FsVersion(文件版本令牌)
- FsVersion 是后端持有的不透明新鲜度令牌,写入与编辑用它做陈旧守卫。策略插件记录它用于检查,消费方不得解释其内容。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/filesystem
- FsWriteIntent(写入意图)
- FsWriteIntent 是带守卫的写入意图,只有 createIfAbsent 与 replaceIfVersion 两种;省略守卫字段表示无条件创建或覆盖,而不是第三种意图。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/filesystem
- FsObservation(观测结果)
- FsObservation 表示对某个目标的一次权威观测:存在则携带用于守卫替换的版本,缺失则只授权带守卫的创建、绝不授权编辑。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/filesystem
来源
- DeepSeek Harness 文档 — 文件系统(FsTarget、写入守卫、fs 策略事件、错误与 IO 超时)· deepseek-harness.github.io
- dsh-fs 提供方源码(types.ts 与 index.ts)· deepseek-ai(GitHub)
- dsh-fs-observation-policy(观测状态与新鲜度规则)· deepseek-ai(GitHub)