DSH / Atlas
2026-06-17implementedfeature

Filesystem tool schemas — model-facing read/write/edit shapes

文件系统工具 schema——面向模型的读/写/编辑接口形状

[The filesystem capability-seam Agent Note](../architecture/2026-06-17-filesystem-capability-seam.md) defines the filesystem capability seam (`ctx.fs`), the package split (`dsh-fs`, `dsh-fs-local`, `dsh-tool-fs`, plus the `dsh-fs-observation-policy` policy plugin), and the observed-file/stale-version policy for read-before-write/edit checks — which the [split-fs-seam](../simplification/2026-06-26-fsspec-style-fs-seam

English

Problem

The filesystem capability-seam Agent Note defines the filesystem capability seam (ctx.fs), the package split (dsh-fs, dsh-fs-local, dsh-tool-fs, plus the dsh-fs-observation-policy policy plugin), and the observed-file/stale-version policy for read-before-write/edit checks — which the split-fs-seam and event-gate Agent Notes moved off ctx.fs into the dsh-fs-observation-policy plugin on the fs/* event gate. The remaining decision for the first filesystem tool delivery is the model-facing schema: what arguments the model sees for read, write, and edit.

The schema must be small, yet stable enough that local/remote/sandboxed filesystem backends do not require model-facing churn, and must avoid importing every option from reference systems. Claude Code and OpenCode expose similar core file tools but differ in naming style and extra flags; this decision picks the minimal shared surface.

Decision

@deepseek-ai/dsh-tool-fs exposes these three model-facing tools in the first filesystem suite:

ToolOur schemaClaude CodeOpenCodeNotes
readread(file_path, offset?, limit?)Read(file_path, offset?, limit?, pages?)read(filePath, offset?, limit?)Files only; 1-indexed offset; no image/PDF/multimodal support in the first pass.
writewrite(file_path, content)Write(file_path, content)write(content, filePath)Creates or overwrites UTF-8 text. Under the default fs-observation-policy, updates to existing files require a prior observation; new-file creates do not.
editedit(file_path, old_string, new_string, replace_all?)Edit(file_path, old_string, new_string, replace_all?)edit(filePath, oldString, newString, replaceAll?)Literal string replacement; unique match required by default; under the default fs-observation-policy requires a prior observation (any windowed read counts).

The schema uses snake_case field names (file_path, old_string, new_string, replace_all) to align with Claude Code and with existing DeepSeek Harness tool-schema examples. The Consumer package translates these model-facing names into ctx.fs calls and fs/* event dispatches.

Tool schemas

read

read inspects a UTF-8 text file and returns line-numbered content.

Arguments:

  • file_path: string — required. Path to read, resolved by ctx.fs.
  • offset?: number — optional. 1-based first line to return. Defaults to the first line.
  • limit?: number — optional. Maximum number of lines to return. Defaults and caps are implementation details of dsh-tool-fs / ctx.fs.

Non-goals for the first pass:

  • No PDF pages argument.
  • No image or multimodal file reads.
  • No directory listing through read; if needed, listing becomes a separate future tool.

write

write creates or fully replaces a UTF-8 text file.

Arguments:

  • file_path: string — required. Path to write, resolved by ctx.fs.
  • content: string — required. Full UTF-8 text content to write.

Under the default fs-observation-policy, updating an existing file with write requires a prior observation (a read/write/edit) of that file by the same execution context; the dsh-fs-observation-policy plugin supplies the observed version as the stale guard on fs/write-intent. Creating a new file does not require a prior observation. With the policy plugin absent, write is an unconditional bare-provider create-or-overwrite.

The schema does not expose expected_hash, expected_version, or create_only as model-facing parameters. Stale-version checks are driven by backend-produced versions and the policy plugin's observed state, not by asking the model to copy version tokens through the schema.

edit

edit updates an existing UTF-8 text file by replacing literal text.

Arguments:

  • file_path: string — required. Path to edit, resolved by ctx.fs.
  • old_string: string — required. Literal text to replace. Empty strings are invalid in the first pass.
  • new_string: string — required. Literal replacement text; an empty string deletes the match.
  • replace_all?: boolean — optional. Defaults to false. When false, old_string must identify exactly one match.

edit requires a prior observation of the file in the same execution context (any windowed read counts — authorization is version freshness, not a full-view requirement), or a prior write/edit by that context. The dsh-fs-observation-policy policy plugin derives the owner and supplies the recorded version as the stale guard; the provider's mutation lock enforces it.

The first pass rejects Codex-style patch grammars and multi-mode edit APIs. It uses one strict literal replacement mode so the model-facing contract stays simple and the backend can own exact-match, duplicate-match, line-ending, and stale-version semantics.

Result shape

The first implementation formatted ContentBlock[] in execute. The canonical tool-output contract now keeps ctx.fs result facts as the tool's validated value and derives the same model text through output.render; file-state recording/refreshing remains on ctx.fs.

Default native projections:

ToolStructured ctx.fs outcome consumed by tool-fsDefault model projection
readreturned lines, returned line count, total line count, target display path, file version, partial-view flagline-numbered text plus pagination footer
writecreate/update operation, target display path, new file versionconcise create/update success text
editreplacement count, replace-all flag, target display path, new file versionconcise edit success text

The structured outcome does not restate model arguments such as file_path, old_string, or content unless the backend has resolved them into new information such as displayPath, targetKey, or a new version. Token-conscious truncation is part of the model projection, not the backend's canonical result.

Deferred

The following are deliberately out of scope for the first filesystem schema pass:

  • Model-facing expected_hash, expected_version, or create_only parameters.
  • Directory listing, glob, grep, and search tools.
  • Binary-safe read/write operations.
  • PDF/image/multimodal read.
  • Code Mode projection values for filesystem tools.
  • A canonical edit diff format.

Testing

Schema tests pin the required/optional argument set per tool, empty-old_string rejection, the replace_all default, the snake_case field names, description prose that states the observation policy, and root-plugin suite registration; integration tests execute all three tools through ctx.tools.execute() against the real dsh-fs-local provider and verify the model arguments translate into the expected ctx.fs calls and fs/* dispatches.

Alternatives considered

  • A Codex-style patch grammar or multi-mode edit API — rejected: one strict literal replacement mode keeps the model-facing contract simple and lets the backend own exact-match, duplicate-match, line-ending, and stale-version semantics.
  • camelCase argument names (OpenCode's style) — snake_case aligns with Claude Code and the existing harness tool-schema examples, and naming is public API once shipped.
  • Model-facing expected_hash / expected_version / create_only parameters — rejected: stale checks are driven by backend-minted versions and the policy plugin's observed state, never by fragile model-copied tokens.

Consequences

The first schema is intentionally smaller than Claude Code's. Dropping PDF pages, multimodal read, rich grep/list flags, and expected hash fields keeps the implementation focused, but users may ask for those quickly. They arrive as separate Agent Notes or focused follow-ups rather than overloads of the initial schema.

No explicit model-facing stale guard in v1. The schema does not ask the model to provide an expected hash/version. That is intentional: stale checks come from backend-produced versions and the dsh-fs-observation-policy plugin's observed state, not from fragile model-copied tokens. Filesystem safety failures surface through structured FsError codes owned by dsh-fs, not through model-supplied version fields.

Naming becomes public API. Once shipped, changing file_path to filePath or old_string to oldString would churn prompts, examples, and downstream clients. This Agent Note chooses snake_case up front and treats it as the stable model-facing contract.

中文

问题

文件系统能力 seam Agent Note 定义了文件系统能力 seam(ctx.fs)、包拆分(dsh-fsdsh-fs-localdsh-tool-fs,加上 dsh-fs-observation-policy 策略插件),以及针对 read-before-write/edit 检查的已观测文件/陈旧版本策略——拆分文件系统 seam事件门 Agent Note 后来将其从 ctx.fs 移至 dsh-fs-observation-policy 插件的 fs/* 事件门上。首次文件系统工具交付剩余的决策是面向模型的 schema:模型在 readwriteedit 中看到哪些参数。

该 schema 必须足够小,但又要足够稳定,使本地、远程、沙箱文件系统后端不需要改动面向模型的接口,并且必须避免从参考系统中照搬所有选项。Claude Code 和 OpenCode 暴露了类似的核心文件工具,但在命名风格和额外 flag 上有所不同;本决策选择最小的共有接口。

决策

@deepseek-ai/dsh-tool-fs 在首个文件系统工具套件中暴露以下三个面向模型的工具:

工具我们的 schemaClaude CodeOpenCode说明
readread(file_path, offset?, limit?)Read(file_path, offset?, limit?, pages?)read(filePath, offset?, limit?)仅支持文件;offset 从 1 开始;首版不支持图片、PDF 或多模态内容。
writewrite(file_path, content)Write(file_path, content)write(content, filePath)创建或覆盖 UTF-8 文本。在默认 fs-observation-policy 下,更新现有文件前必须先观测;创建新文件则不需要。
editedit(file_path, old_string, new_string, replace_all?)Edit(file_path, old_string, new_string, replace_all?)edit(filePath, oldString, newString, replaceAll?)字面字符串替换;默认要求唯一匹配;在默认 fs-observation-policy 下必须先观测(任意窗口读取均算作观测)。

schema 使用 snake_case 字段名(file_pathold_stringnew_stringreplace_all),与 Claude Code 及现有 DeepSeek Harness 工具 schema 示例保持一致。消费方包将这些面向模型的名称转换为 ctx.fs 调用和 fs/* 事件分发。

工具 schema

read

read 检视一个 UTF-8 文本文件并返回带行号的内容。

参数:

  • file_path: string——必填。要读取的路径,由 ctx.fs 解析。
  • offset?: number——可选。返回的第一行,从 1 开始。默认为第一行。
  • limit?: number——可选。返回的最大行数。默认值与上限是 dsh-tool-fs / ctx.fs 的实现细节。

首次实现不涉及的内容:

  • 无 PDF pages 参数。
  • 无图片或多模态文件读取。
  • 不通过 read 列出目录;如有需要,目录列表将作为单独的后续工具。

write

write 创建或完整替换一个 UTF-8 文本文件。

参数:

  • file_path: string——必填。要写入的路径,由 ctx.fs 解析。
  • content: string——必填。要写入的完整 UTF-8 文本内容。

在默认 fs-observation-policy 下,使用 write 更新已有文件需要同一执行上下文先前对该文件有过一次观测(read/write/edit);dsh-fs-observation-policy 插件将观测到的版本作为 fs/write-intent 上的陈旧版本防护提供。创建新文件不需要先前观测。如果策略插件不存在,write 是由裸提供方无条件执行的创建或覆盖操作。

schema 不将 expected_hashexpected_versioncreate_only 作为面向模型的参数暴露。陈旧版本检查由后端产生的版本和策略插件的观测状态驱动,而非要求模型通过 schema 复制版本令牌。

edit

edit 通过替换字面文本来更新已有的 UTF-8 文本文件。

参数:

  • file_path: string——必填。要编辑的路径,由 ctx.fs 解析。
  • old_string: string——必填。要替换的字面文本。首次实现中空字符串无效。
  • new_string: string——必填。字面替换文本;空字符串表示删除匹配内容。
  • replace_all?: boolean——可选。默认为 false。为 false 时,old_string 必须恰好匹配一处。

edit 要求同一执行上下文先前观测过该文件(任何窗口化的 read 都算——授权取决于观测到的版本是否仍为最新,而不要求查看全文),或该上下文先前对该文件执行过 write/edit。dsh-fs-observation-policy 策略插件推导所有者,并将记录的版本作为陈旧版本防护提供;提供方的变更锁会强制执行该防护。

首次实现拒绝 Codex 风格的 patch 语法和多模式 edit API。它使用一种严格的字面替换模式,使面向模型的约定保持简单,并让后端掌控精确匹配、重复匹配、行尾和陈旧版本的语义。

结果形状

首次实现曾将 ContentBlock[] 格式化逻辑放在 execute 中。规范工具输出约定如今将 ctx.fs 的结果事实保留为工具经校验的值,并通过 output.render 派生相同的模型文本;文件状态的记录/刷新仍归 ctx.fs 所有。

默认原生投影:

工具tool-fs 使用的结构化 ctx.fs 结果默认模型投影
read返回的行、返回行数、总行数、目标显示路径、文件版本、部分视图标记带行号的文本及分页页脚
write创建/更新操作、目标显示路径、新文件版本简洁的创建/更新成功文本
edit替换次数、全量替换标记、目标显示路径、新文件版本简洁的编辑成功文本

结构化结果不会重复模型参数(如 file_pathold_stringcontent),除非后端已将其解析为新信息(如 displayPathtargetKey 或新版本)。以节省 token 为目的的截断属于模型投影的职责,而非后端规范结果的一部分。

延后事项

以下内容被明确排除在首次文件系统 schema 实现之外:

  • 面向模型的 expected_hashexpected_versioncreate_only 参数。
  • 目录列表、glob、grep 和搜索工具。
  • 二进制安全的读/写操作。
  • PDF/图片/多模态 read
  • 文件系统工具的 Code Mode 投影值。
  • 规范的 edit diff 格式。

测试

schema 测试固定每个工具的必填/可选参数集、空 old_string 拒绝、replace_all 默认值、snake_case 字段名、描述文字中对观测策略的说明,以及根插件套件注册;集成测试通过 ctx.tools.execute() 对真实的 dsh-fs-local 提供方执行全部三个工具,并验证模型参数被正确转换为预期的 ctx.fs 调用和 fs/* 分发。

曾考虑的替代方案

  • Codex 风格的 patch 语法或多模式 edit API:否决。一种严格的字面替换模式使面向模型的约定保持简单,并让后端掌控精确匹配、重复匹配、行尾和陈旧版本的语义。
  • camelCase 参数名(OpenCode 风格):snake_case 与 Claude Code 及现有 harness 工具 schema 示例一致,且命名一旦发布即成为公开 API。
  • 面向模型的 expected_hash / expected_version / create_only 参数:否决。陈旧检查由后端产生的版本和策略插件的观测状态驱动,从不依赖模型复制的脆弱令牌。

后果

首版 schema 有意小于 Claude Code 的。 去掉 PDF pages、多模态 read、丰富的 grep/list flag 和 expected hash 字段使实现保持聚焦,但用户可能很快就会提出这些需求。这些功能将通过独立 Agent Note 或聚焦的后续工作引入,而不是让初始 schema 承载过多内容。

v1 中没有显式的面向模型的陈旧版本防护。 schema 不要求模型提供 expected hash/version。这是有意为之:陈旧检查来自后端产生的版本和 dsh-fs-observation-policy 插件的观测状态,而非模型复制的脆弱令牌。文件系统安全失败通过 dsh-fs 拥有的结构化 FsError 代码暴露,而非模型提供的版本字段。

命名成为公开 API。 一旦发布,将 file_path 改为 filePathold_string 改为 oldString 会导致提示词、示例和下游客户端随之改动。本 Agent Note 预先选择 snake_case,并将其视为稳定的面向模型的约定。