A corpus sweep under the doc/prose standards found 15 links whose #fragment named no anchor in its target — reworded headings, one relocated contract (tool-fs → the group README's no-timeout rule), and zh sides citing English slugs their Chinese headings never produce. Fixed all 15 (zh sides get the conventional explicit <a id> + English fragment), fixed the one generator-owned instance at its source (gen-doc-graphs), and extended verify-md-links to resolve fragments onto Markdown targets — same-file anchors included — against heading slugs and explicit <a id>, so the class is gated instead of manually grepped. Remaining probes (narrated history, duplication shingles, comment transcripts, budgets) came back clean; sibling-adapter README symmetry and implemented-note contrasts are deliberate keeps.
12 KiB
@deepseek-ai/dsh-tool-fs
English | 中文
面向模型的文件系统工具(read、write、edit)及其执行器。这是文件系统栈的消费方层:拥有工具名称、JSON Schema、参数校验、提示词段、读取窗口逻辑和结果格式化。它直接通过 ctx.fs 提供方 seam(@deepseek-ai/dsh-fs)读取/写入/编辑。新鲜度/观察策略由独立插件(@deepseek-ai/dsh-fs-policy)通过 fs/* 事件门禁贡献;工具不与其方法耦合。使用施加沙箱限制的提供方时,逐会话执行需要共享沙箱策略服务,工具还会为文件系统变更提供升权路径。
// Default deployment: a ctx.fs provider, the policy plugin, then the tools.
await ctx.plugin(LocalFileSystem, { cwd: process.cwd() }) // @deepseek-ai/dsh-fs-local
await ctx.plugin(FsPolicy) // @deepseek-ai/dsh-fs-policy (policy gate)
await ctx.plugin(ToolFs) // this package — registers read/write/edit
@deepseek-ai/dsh-fs-policy 是可选的:省略时,工具直接使用裸提供方(无条件写入/覆盖/编辑,无已观察状态)。加载这些工具的部署也应加载该插件,从而提供写入/编辑前读取行为。
配置
所有键均为可选;默认值是随产品交付的读取上限。
| 键 | 默认值 | 含义 |
|---|---|---|
readLimit |
2000 |
一次 read 调用返回的默认和最大行数(工具 schema 将其声明为 limit 默认值)。 |
readMaxLineLength |
2000 |
每行截断前保留的字符数(后缀会说明上限)。 |
readMaxBytes |
51200 |
一次 read 调用所选行的字节上限;溢出时以「已达上限」footer 结束窗口。 |
readStreamMinSize |
10485760 |
大于等于该大小或大小未知的文件采用流式读取,而不是整体加载到内存。 |
工具(schema 见文件系统工具 schema Agent Note)
| 工具 | 参数 | 行为 |
|---|---|---|
read |
file_path、offset?、limit? |
带行号的 UTF-8 内容和分页 footer。offset 从 1 开始;limit 默认为配置的 readLimit(2000),上限也为该值。 |
write |
file_path、content |
创建文件或完整替换文件。有策略插件时:覆盖现有文件要求先在未变版本上执行 read;创建新文件不需要。没有插件时:无条件执行。 |
edit |
file_path、非空 old_string、new_string、replace_all? |
字面量替换;除非 replace_all 为 true,否则要求唯一匹配。有策略插件时:要求先执行 read(任何窗口),且文件此后未变。没有插件时:无条件执行。 |
字段名使用 snake_case,与 Claude Code 和现有 harness 工具 schema 一致。
规范成功值分别为:read → { path, offset, lines: [{ number, text }], totalLines },write → { path, operation: 'create' | 'update', before: string | null, after },edit → { path, before, after }。原生渲染器会保留下方带行号的读取结果和变更确认。write/edit 从这些规范值派生可回放的 diff 卡片元数据,read 派生可回放的读取卡片窗口 { path, offset, lines, totalLines, lang? };规范值本身仅限于本次执行,不会添加到 tool/result,只有派生出的呈现元数据会被持久化。
工具就是执行器;策略是事件门禁
工具不注入策略服务,也不检查任何缓存。每个工具通过 ctx.fs.resolve(path, { cwd, signal }) 解析路径;它会传入调用 agent(智能体)的会话 cwd(exec.agent.session.header.cwd),使相对路径以会话工作区为基准解析并与 dsh-tool-bash 一致,同时把工具取消转发到解析过程(见每会话 cwd Agent Note)。随后执行:
- read:一次
ctx.fs.stat(用于类型、大小路由和版本),随后调用readText/streamText,构建行窗口,再发出fs/observed,使用普通ctx.emit。(1 次 stat。) - write:调用
ctx.waterfall('fs/write-intent', target, exec, () => undefined)取得可选防护,然后调用ctx.fs.writeText(target, content, intent),再发出fs/observed。(0 次 stat。) - edit:调用
ctx.waterfall('fs/edit-intent', target, exec, () => undefined)取得可选防护,然后调用ctx.fs.editText(target, edit, intent),再发出fs/observed。(0 次 stat。)
工具在每次分派中把 exec(工具执行上下文)作为不透明 actor 传入。默认 thunk 返回 undefined(不受约束的裸提供方)。加载 @deepseek-ai/dsh-fs-policy 后,它会占用单个决策槽:返回 createIfAbsent/replaceIfVersion/{ version } 或抛出 FS_NOT_OBSERVED,并在 fs/observed 时记录。后端错误(FsError)和抛出的 FS_NOT_OBSERVED 会流经 ToolRegistry.execute(),变成 isError 工具结果,并附带 { name, code }。
当 ctx.fs.sandboxMode 表明提供方施加沙箱限制时,write/edit 会公开 sandbox_permissions 与 justification,并通过 ctx.approval 解析经批准的重试。策略归属方会贡献与具体能力无关的常驻策略;工具结果仍保留操作特定的拒绝与重试引导。
fs/observed 发后即忘
fs/observed 在读取/写入/编辑已经成功之后,通过普通 ctx.emit 发出。监听器的契约是同步且只有副作用的记录器(@deepseek-ai/dsh-fs-policy 使用 WeakMap.set);工具不保护这次发出,因此监听器抛出会作为工具的 isError 结果出现。异步或可能失败的观察不属于该事件。
read 允许并发调度,因为其唯一变更是同步版本记录器。稍后的 write 或 edit 会在目标锁内重新检查版本,因此记录器竞态会以拒绝方式关闭;两个变更工具仍保持互斥。见并行工具调用 Agent Note。
包根目录只导出 Cordis 插件契约(name、inject、Config 和 apply)。读取渲染(行窗口与输出格式化)位于 src/read-render.ts(不依赖 Cordis,单独进行单元测试);src/read.ts/write.ts/edit.ts 是工具执行器,src/index.ts 负责组合。
模型体验
系统提示词
模型看到的内容
该插件注册作用域内的每个请求都会收到下方独立注册的 read、write 与 edit 指导。作用域工具限制可以隐藏 schema,而不移除这些段。
Read 指导
Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.
Write 指导
Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.
Edit 指导
Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.
Token 影响
插件启用期间,每个请求支付固定指导成本;即使限制隐藏了一个或多个工具也一样。
KV Cache 影响
只要插件作用域和指导文本不变,前缀就保持稳定。工具限制不会移除该段,但插件启用或 dispose(资源释放)可能从该段开始使复用失效。
工具 schema
模型看到的内容
模型会看到已生成的 read、write 和 edit schema,参数使用 snake_case。作用域工具限制可以为某个 agent 移除任一定义。
Token 影响
该工具视图中的每个请求都支付固定 schema 成本。
KV Cache 影响
只要可见工具定义和顺序不变,前缀就保持稳定。注册生命周期或作用域限制可能从首个变化的 schema token 开始使复用失效。
读取结果
模型看到的内容
成功读取结果精确为 <path><displayPath></path>、换行、<type>file</type>、换行、<content>、形如 <lineNumber>: <text> 的编号行、一个空行、一条 footer 和 </content>。footer 精确为 (Output capped. Showing lines <start>-<end>. Use offset=<next> to continue.)、(Showing lines <start>-<end> of <total>. Use offset=<next> to continue.) 或 (End of file - total <total> lines)。长行结尾精确为 ... (line truncated to <max> chars)。
Token 影响
读取输出受 readLimit、readMaxLineLength 和 readMaxBytes 限制;保留的调用与结果会反复发送,直到上下文压缩(compaction)。
KV Cache 影响
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
写入与编辑结果
模型看到的内容
写入精确返回五行包络:<path><displayPath></path>、<type>file</type>、<content>、Created file 或 Updated file,以及 </content>。编辑精确返回 The file <displayPath> has been updated successfully.;对于 replace_all,精确返回 The file <displayPath> has been updated. All occurrences were successfully replaced.。完整写入或替换文本仍保留在 assistant 工具调用参数中。
Token 影响
成功文本很少,但大型变更参数和所有结果会反复发送,直到上下文压缩。
KV Cache 影响
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
工具错误
模型看到的内容
失败会规范化为 Error: <message>。本包稳定的校验和读取消息是 file_path must be a non-empty string、limit must be less than or equal to <max>、old_string must be a non-empty string、old_string and new_string must differ、cannot read "<path>": not found、cannot read "<path>": not a regular file 和 offset <offset> is out of range for "<path>" (<total> lines);提供方和策略模板在各自包的 README 中逐字列出。防护变更失败还会在消息中携带恢复指令,由本包面向模型的错误包装追加:FS_STALE_VERSION(包括编辑目标缺失)追加 — re-read the file, then retry,FS_NOT_OBSERVED 追加 — read the file, then retry;结构化错误码保持不变。
Token 影响
只有失败调用会添加这些保留 token。
KV Cache 影响
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
已知限制与暂缓事项
- 未交付面向模型的目录列表工具:
ctx.fs.listDir服务于 skill(技能)发现等提供方代码,同级dsh-tool-fs-search包则提供基于 bash 的glob与grep,而不是扩展文件系统 seam。 read只处理 UTF-8 文本文件:二进制安全读取和 PDF/图像/多模态内容均延期处理;目录目标为FS_NOT_REGULAR_FILE。- 没有超时接口:
read/write/edit不接受超时参数,也不声明timeout-policy预算;取消只通过exec.signal传递(见提供方理由)。