@x1a0f3n9/dsh-tool-fs 0.1.5-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/fs/tool-fs/README.md
5
+ README.md: 9d338a6227b7f02ef43fcbe97900b166cb722817
6
+ README.zh.md: 0c34f840ad449847a649df8abdadec01f772f37b
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ ---
2
+ description: "The model-facing read, read_image, write, and edit tools for users and maintainers composing or debugging filesystem access for agents."
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @x1a0f3n9/dsh-tool-fs
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ Use `dsh-tool-fs` to let a model read UTF-8 files with line numbers, read supported images, create or atomically replace files, and apply targeted literal edits. Results are capped, and failures provide stable error codes and recovery instructions. Add `dsh-fs-observation-policy` when writes and edits must follow a successful read; without it, mutations remain atomic but are unconditional. Image reads require durable attachment storage and an image-capable routed model. Choose the sibling discovery package for glob or grep searches.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount the tools after a `ctx.fs` backend and, for read-before-write/edit behavior, the policy plugin. The model then gets line-numbered reads, atomic writes and edits, and — with an attachment store mounted — image reads; every result is capped, and failures carry stable codes with recovery instructions.
29
+
30
+ ### Minimal composition
31
+
32
+ A backend, the policy plugin, then the tools; the attachment store is optional and enables `read_image`.
33
+
34
+ ```yaml
35
+ - name: '@x1a0f3n9/dsh-fs-local'
36
+ - name: '@x1a0f3n9/dsh-fs-observation-policy'
37
+ - name: '@x1a0f3n9/dsh-tool-fs'
38
+ ```
39
+
40
+ The policy plugin is optional: without it the tools run against the bare provider (unconditional write, overwrite, and edit with no observed-state). A deployment that loads these tools is expected to also load it, so the behavior is read-before-write/edit. `read_image` registers only while a durable `ctx.attachments` service is mounted; execution additionally refuses on a route whose exact model does not declare image input, so a text route's durable history stays free of image blocks.
41
+
42
+ ### The tools
43
+
44
+ | Tool | Arguments | Behavior |
45
+ |---|---|---|
46
+ | `read` | `file_path`, `offset?`, `limit?` | Line-numbered UTF-8 content with a pagination footer; `offset` is 1-based and `limit` defaults to and caps at the configured `readLimit` |
47
+ | `read_image` | `file_path` | Reads and persists a PNG/JPEG/WebP/GIF source; an extension-less path (normalized attachment object paths included) is identified from its file signature; normalization can downscale it before the next model request, so the model need not create a thumbnail first |
48
+ | `write` | `file_path`, `content` | Creates or fully replaces a file; with the policy plugin, overwriting requires a prior `read` at the unchanged version, creating does not |
49
+ | `edit` | `file_path`, `old_string`, `new_string`, `replace_all?` | Literal replacement requiring a unique match unless `replace_all` is true; with the policy plugin, requires a prior `read` and an unchanged file |
50
+
51
+ Field names are snake_case to match Claude Code and existing harness tool schemas. Successes return compact envelopes — a read window, an image reference, or a `Created file`/`Updated file` confirmation — and `write`/`edit` derive replayable diff-card metadata for UI presentation.
52
+
53
+ ### Configuration
54
+
55
+ All keys are optional; the defaults are the shipped read caps.
56
+
57
+ | Key | Default | Meaning |
58
+ |---|---|---|
59
+ | `readLimit` | `2000` | Default and maximum lines returned by one `read` call |
60
+ | `readMaxLineLength` | `2000` | Characters kept per line before truncation |
61
+ | `readMaxBytes` | `51200` | Byte cap on one `read` call's selected lines; overflow ends the window with a capped footer |
62
+ | `readStreamMinSize` | `10485760` | Files at or above this size (or of unknown size) stream instead of loading whole into memory |
63
+
64
+ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-fs) is the exhaustive source for every accepted field and its JSDoc.
65
+
66
+ ### Policy and sandbox behavior
67
+
68
+ Path authorization for `read` and `read_image` belongs entirely to `ctx.fs`; media-type declarations and file signatures only decide whether `read_image` accepts the bytes returned by that backend.
69
+
70
+ With the policy plugin mounted, `write` and `edit` obtain their guard from the `fs/*` intent slots, so an unread target or a stale observation fails with `FS_NOT_OBSERVED` or `FS_STALE_VERSION` and a recovery instruction. Under a confining backend (`fs-sandbox`), `write`/`edit` additionally advertise `sandbox_permissions` and `justification`; a denied mutation returns the `[sandbox: file access denied under <mode> mode]` marker with the same-turn escalation hint, and an approved retry may stamp a strictly wider mode for that one call.
71
+
72
+ ### Failures and recovery
73
+
74
+ Failures are normalized as `Error: <message>` with a structured code preserved for callers. Stable messages include `file_path must be a non-empty string`, `limit must be less than or equal to <max>`, `cannot read "<path>": not found`, `cannot read "<path>": not a regular file`, and the image-route refusal `cannot read "<path>" as an image: model "<model>" does not declare image input; switch to an image-capable model to read images`. `FS_NOT_OBSERVED` is normalized to `cannot modify "<path>": file has not been read — read the file, then retry`, independent of whether the policy or provider rejected the operation; `FS_STALE_VERSION` retains the provider's reason and appends `— re-read the file, then retry`. After the reread confirms absence, `edit` reports `FS_NOT_FOUND` instead of repeating a stale remedy, while `write` uses guarded creation.
75
+
76
+ -----
77
+
78
+ <a id="understand-the-implementation"></a>
79
+ ## Understand the implementation
80
+
81
+ <details>
82
+ <summary>Implementation internals — click to expand</summary>
83
+
84
+ This section explains the design decisions behind the tool suite and points at the code that realizes them; the observable behavior is fully covered in [Use this package](#use-this-package).
85
+
86
+ ### Design concept
87
+
88
+ The tools are the executor; policy is an event gate. The tools inject no policy service and inspect no cache — each mutation asks the single intent slot for its guard through `ctx.waterfall`, and each operation emits `fs/observed` only after it succeeded. Reads do exactly one provider `stat` (type and size routing plus the observed version); mutations do none, because the guard comes from the intent slot and the provider re-checks under its lock.
89
+
90
+ ### Source map
91
+
92
+ | File | Role |
93
+ |---|---|
94
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `Config`, tool composition, `read_image` attachments gate |
95
+ | [`src/read.ts`](src/read.ts) | `read` executor: one stat, streaming decision, window build, observation |
96
+ | [`src/read-image.ts`](src/read-image.ts) | `read_image` executor: route and media-type gates, bounded bytes, attachment save |
97
+ | [`src/write.ts`](src/write.ts) | `write` executor: intent waterfall, atomic write, observation |
98
+ | [`src/edit.ts`](src/edit.ts) | `edit` executor: intent waterfall, literal edit, observation |
99
+ | [`src/read-render.ts`](src/read-render.ts) | Cordis-free windowing and envelope formatting |
100
+ | [`src/sandbox.ts`](src/sandbox.ts) | Escalation API shared by `write`/`edit`: policy resolution and denial-marker mapping |
101
+ | [`src/error.ts`](src/error.ts) | Stable model-facing diagnostics for guarded-mutation failures |
102
+
103
+ ### Per-tool flow
104
+
105
+ All four tools share one flow shape: resolve the path with the calling session's cwd, run the applicable gate, perform exactly one provider operation, and emit `fs/observed` only after success. `read` and `read_image` pay one `stat` for type and size routing; `write` and `edit` pay none because their guard comes from the intent slot, and provider failures surface as typed `FsError` results. The per-tool executors live in `src/read.ts`, `src/read-image.ts`, `src/write.ts`, and `src/edit.ts`.
106
+
107
+ ### Observation and concurrency
108
+
109
+ `fs/observed` fires after the operation succeeded via a plain `ctx.emit`; a listener is contractually a synchronous, side-effect-only recorder, so async or fallible observation does not belong on this event. `read` opts into concurrent scheduling because its only mutation is the synchronous version recorder; recorder races fail closed when a later `write` or `edit` re-checks the version under its target lock, and both mutation tools remain exclusive.
110
+
111
+ </details>
112
+
113
+ -----
114
+
115
+ <a id="further-exploration"></a>
116
+ ## Further Exploration
117
+
118
+ Read these pages when the package-level contract is not enough. They move from the tools to the contract, backends, and policy they compose with.
119
+
120
+ - [Filesystem subsystem](../../../docs/subsystems/filesystem.md) — exhaustive provider contract, policy events, and error taxonomy.
121
+ - [dsh-fs](../fs/README.md) — the `ctx.fs` contract these tools consume.
122
+ - [fs-local](../fs-local/README.md) — the host-filesystem backend these tools run against.
123
+ - [fs-sandbox](../fs-sandbox/README.md) — the sandbox-enforcing backend that adds the escalation fields.
124
+ - [fs-observation-policy](../fs-observation-policy/README.md) — the policy plugin that guards mutations through the `fs/*` events.
125
+ - [Generated tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-tool-fs) — the exhaustive schemas this package registers.
126
+
127
+ -----
128
+
129
+ <a id="model-experience"></a>
130
+ ## Model Experience
131
+
132
+ ### System prompt
133
+
134
+ #### What the model sees
135
+
136
+ At assembly time, each guidance section checks `ctx.tools.get(name, scope)` and renders only while its tool is visible to that agent. The write paragraph recommends edit only while edit is visible. The text below is unchanged when all three tools are available; restrictions, their removal, and tool registration changes take effect on the next assembly. The same check works for direct agent restrictions and subagent `toolFilter`, including PTC capabilities behind `run_code`. The read-before-mutation sentences in write/edit describe the observation policy, not a requirement to invoke the tool named `read`. They remain when `read` is hidden: the policy still guards mutations, and another observing operation, such as `str_replace_editor` with `command: view`, can establish the same file observation. Tool visibility does not disable that precondition.
137
+
138
+ ##### Read guidance
139
+
140
+ ```markdown
141
+ 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.
142
+ ```
143
+
144
+ ##### Write guidance
145
+
146
+ ```markdown
147
+ 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-observation-policy requires it) and prefer edit for targeted changes.
148
+ ```
149
+
150
+ ##### Edit guidance
151
+
152
+ ```markdown
153
+ 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-observation-policy requires it), unless you just created or edited it in this session.
154
+ ```
155
+
156
+ #### Token effect
157
+
158
+ Guidance cost follows the visible tools and their applicable cross-tool recommendations.
159
+
160
+ #### KV Cache effect
161
+
162
+ Prefix-stable while the visible tool set, plugin scope, and guidance text are unchanged. Restrictions or plugin lifecycle changes may invalidate reuse from the first changed section.
163
+
164
+ ### Tool schemas
165
+
166
+ #### What the model sees
167
+
168
+ The model sees the generated [`read`, `read_image`, `write`, and `edit` schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-fs), with snake_case arguments. The image tool appears only while a durable attachment store is mounted; its schema is route-independent, and the strict gate refuses at execution. Scoped tool restrictions can remove any definition for one agent.
169
+
170
+ #### Token effect
171
+
172
+ Fixed schema cost on every request in that tool view.
173
+
174
+ #### KV Cache effect
175
+
176
+ Prefix-stable while the visible tool definitions and order are unchanged. Registration lifecycle or scoped restrictions may invalidate reuse from the first changed schema token.
177
+
178
+ ### Read result
179
+
180
+ #### What the model sees
181
+
182
+ A successful read is exactly `<path><displayPath></path>`, newline, `<type>file</type>`, newline, `<content>`, numbered lines as `<lineNumber>: <text>`, a blank line, one footer, and `</content>`. The footer is exactly `(Output capped. Showing lines <start>-<end>. Use offset=<next> to continue.)`, `(Showing lines <start>-<end> of <total>. Use offset=<next> to continue.)`, or `(End of file - total <total> lines)`. A long line ends exactly `... (line truncated to <max> chars)`. A missing read still returns `FS_NOT_FOUND`, but it records confirmed absence for the calling session; after an externally deleted file is re-read, a retried `write` can safely recreate it through the provider's no-replace guard.
183
+
184
+ #### Token effect
185
+
186
+ Read output is capped by `readLimit`, `readMaxLineLength`, and `readMaxBytes`; the retained call and result are resent until compaction.
187
+
188
+ #### KV Cache effect
189
+
190
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
191
+
192
+ ### Image read result
193
+
194
+ #### What the model sees
195
+
196
+ A successful `read_image` returns `<path><displayPath></path>`, `<type>image</type>`, and a `<content>` envelope naming the media type, normalized dimensions, and byte size, followed by the image itself as a native image block. The result is logged with its durable reference before the next model request.
197
+
198
+ #### Token effect
199
+
200
+ The image is billed on every later request until compaction. Each call is independently bounded by the attachment store's `maxImageBytes`/`maxImagePixels`/`maxImageDimension`; repeated successful calls accumulate history, and content addressing deduplicates only the stored bytes, not the per-request token cost.
201
+
202
+ #### KV Cache effect
203
+
204
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
205
+
206
+ ### Write and edit results
207
+
208
+ #### What the model sees
209
+
210
+ Write returns the exact five-line envelope `<path><displayPath></path>`, `<type>file</type>`, `<content>`, `Created file` or `Updated file`, then `</content>`. Edit returns exactly `The file <displayPath> has been updated successfully.` or, for `replace_all`, `The file <displayPath> has been updated. All occurrences were successfully replaced.` The full write or replacement text remains in the assistant tool-call arguments.
211
+
212
+ #### Token effect
213
+
214
+ Success text is small, but large mutation arguments and any result are resent until compaction.
215
+
216
+ #### KV Cache effect
217
+
218
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
219
+
220
+ ### Tool errors
221
+
222
+ #### What the model sees
223
+
224
+ Failures are normalized as `Error: <message>`. This package's stable validation and read messages are `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)`, `cannot read "<path>": the <ext> extension does not declare a supported image format; read_image accepts PNG/JPEG/WebP/GIF files, including extension-less files in those formats`, `cannot read "<path>": the file content is not a supported image format; read_image accepts PNG/JPEG/WebP/GIF`, `cannot read "<path>": the bytes do not decode as a supported PNG/JPEG/WebP/GIF image; the file may be truncated or corrupt`, `cannot read "<path>" as an image: model "<model>" does not declare image input; switch to an image-capable model to read images`, and the mismatch repair `cannot read "<path>": the <ext> extension declares <type>, but the bytes use a different image format; rename the file to match its actual format if it is PNG/JPEG/WebP/GIF, or convert it to one of those formats` (an extension-less mismatch reports `cannot read "<path>": the file signature claims <type>, but the bytes decode as a different image format; the file may be corrupt`). A failed 16-bit conversion reports `cannot read "<path>": the 16-bit PNG could not be converted to the normalized 8-bit sRGB form; convert it to an 8-bit PNG/JPEG/WebP and retry`. Provider and policy templates are quoted in their package READMEs. The model-facing error wrapper normalizes every `FS_NOT_OBSERVED` source to `cannot modify "<path>": file has not been read — read the file, then retry`; `FS_STALE_VERSION` keeps the provider's reason and adds `— re-read the file, then retry`. Both retain the structured error code and original cause. After that reread confirms absence, `edit` reports `FS_NOT_FOUND` instead of repeating a stale remedy, while `write` uses guarded creation.
225
+
226
+ #### Token effect
227
+
228
+ Only a failing call adds these retained tokens.
229
+
230
+ #### KV Cache effect
231
+
232
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
233
+
234
+ ## Known Limitations and Deferred Work
235
+
236
+ <a id="known-limitations-and-deferred-work"></a>
237
+
238
+
239
+ These limits define when the tool suite is a poor fit or needs special operational care. They are current package constraints, not a general filesystem comparison or a task backlog.
240
+
241
+ - **No model-facing directory listing ships** — `ctx.fs.listDir` serves provider code such as skill discovery, while the sibling `dsh-tool-fs-search` package supplies ripgrep-backed `glob` and `grep` rather than extending the filesystem seam.
242
+ - **`read` handles UTF-8 text files only** — images use the separate `read_image` tool; PDF, audio, and video remain deferred. A directory target is `FS_NOT_REGULAR_FILE`.
243
+ - **Extension-declared media type** — an extension selects the declared type and the attachment store's magic-byte validation stays authoritative; a correctly formatted image under a wrong extension is refused with the rename remedy rather than sniffed. Only a path with no extension is identified from its file signature.
244
+ - **Object paths re-enter source admission** — `read_image` on a normalized attachment object re-admits its bytes as a new source, so a deployment whose `maxImageBytes`/`maxMessageImageBytes` sit below the normalized-image byte budget can refuse an object path that `ctx.attachments.readImage` still serves; shipped defaults keep the normalized budget (4 MiB) far under the source caps (20 MiB).
245
+ - **Inline image preview rides the UI composition** — the tool-result card renders the image through the browser's `tool.call.images` slot, which the attachment presentation plugin fills; a UI without that plugin shows the result's envelope text instead.
246
+ - **No attachment-region tool** — an agent may crop an image through another available tool when it has a filesystem path; a pasted or dragged image without a path cannot be re-read at higher resolution.
247
+ - **No timeout surface** — `read`/`write`/`edit` take no timeout argument and declare no timeout budget; cancellation rides `exec.signal` only ([provider rationale](../README.md)).
248
+
249
+ <a id="dev-note"></a>
250
+ ### Dev Note
251
+
252
+ <details>
253
+ <summary>Working context for maintainers — click to expand</summary>
254
+
255
+ None.
256
+
257
+ </details>
258
+
259
+ **Runtime invariant:** No companion is published. This model-facing adapter has no independent lifecycle stream; execution relations are owned by the capability seam it calls.
package/README.zh.md ADDED
@@ -0,0 +1,259 @@
1
+ ---
2
+ description: "面向模型的 read、read_image、write 与 edit 工具:供组合或排查 agent 文件系统访问的用户与维护者使用。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @x1a0f3n9/dsh-tool-fs
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 使用 `dsh-tool-fs` 可让模型带行号读取 UTF-8 文件、读取受支持的图片、创建或原子地替换文件,以及执行有针对性的字面量编辑。结果都有上限,失败会提供稳定错误码与恢复指令。当写入和编辑必须在成功读取后执行时,请添加 `dsh-fs-observation-policy`;省略它时,变更仍是原子的,但不受此条件约束。图片读取需要持久附件存储和支持图片输入的路由模型。glob 或 grep 搜索请选择同级的发现工具包。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 在 `ctx.fs` 后端之后挂载工具,并在需要先读后写/编辑行为时挂载策略插件。模型随后获得带行号的读取、原子的写入与编辑,以及——挂载附件存储时——图像读取;每个结果都有上限,失败携带稳定错误码与恢复指令。
29
+
30
+ ### 最小组合
31
+
32
+ 一个后端、策略插件,然后是工具;附件存储为可选,用于启用 `read_image`。
33
+
34
+ ```yaml
35
+ - name: '@x1a0f3n9/dsh-fs-local'
36
+ - name: '@x1a0f3n9/dsh-fs-observation-policy'
37
+ - name: '@x1a0f3n9/dsh-tool-fs'
38
+ ```
39
+
40
+ 策略插件是可选的:省略时,工具直接使用裸提供方(无条件写入、覆盖与编辑,无已观察状态)。加载这些工具的部署也应加载该插件,从而提供写入/编辑前读取行为。`read_image` 只在持久 `ctx.attachments` 服务已挂载时注册;执行时还拒绝确切模型未声明图像输入的路由,因此文本路由的持久历史不会出现图像块。
41
+
42
+ ### 工具
43
+
44
+ | 工具 | 参数 | 行为 |
45
+ |---|---|---|
46
+ | `read` | `file_path`、`offset?`、`limit?` | 带行号的 UTF-8 内容与分页 footer;`offset` 从 1 开始,`limit` 默认为配置的 `readLimit`,上限也为该值 |
47
+ | `read_image` | `file_path` | 读取并持久保存 PNG/JPEG/WebP/GIF 源图;无扩展名路径(包括规范化附件对象路径)按文件签名识别格式;规范化可在下一次模型请求前缩小图片,因此模型无需先创建缩略图 |
48
+ | `write` | `file_path`、`content` | 创建或完整替换文件;有策略插件时,覆盖要求先在未变版本上执行 `read`,创建不需要 |
49
+ | `edit` | `file_path`、`old_string`、`new_string`、`replace_all?` | 字面量替换,除非 `replace_all` 为 true 否则要求唯一匹配;有策略插件时,要求先执行 `read` 且文件未变 |
50
+
51
+ 字段名使用 snake_case,与 Claude Code 和现有 harness 工具 schema 一致。成功返回紧凑信封——读取窗口、图像引用或 `Created file`/`Updated file` 确认——`write`/`edit` 还会派生可回放的 diff 卡片元数据供 UI 展示。
52
+
53
+ ### 配置
54
+
55
+ 所有键均为可选;默认值是随产品交付的读取上限。
56
+
57
+ | 键 | 默认值 | 含义 |
58
+ |---|---|---|
59
+ | `readLimit` | `2000` | 一次 `read` 调用返回的默认和最大行数 |
60
+ | `readMaxLineLength` | `2000` | 每行截断前保留的字符数 |
61
+ | `readMaxBytes` | `51200` | 一次 `read` 调用所选行的字节上限;溢出时以「已达上限」footer 结束窗口 |
62
+ | `readStreamMinSize` | `10485760` | 大于等于该大小或大小未知的文件采用流式读取,而不是整体加载到内存 |
63
+
64
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tool-fs)是每个受支持字段及其 JSDoc 的穷尽式真源。
65
+
66
+ ### 策略与沙箱行为
67
+
68
+ `read` 与 `read_image` 的路径授权完全由 `ctx.fs` 负责;媒体类型声明和文件签名只决定 `read_image` 是否接受该后端返回的字节。
69
+
70
+ 挂载策略插件后,`write` 与 `edit` 从 `fs/*` 意图槽位取得防护,因此未读目标或陈旧观察会以 `FS_NOT_OBSERVED` 或 `FS_STALE_VERSION` 及恢复指令失败。使用施加沙箱限制的后端(`fs-sandbox`)时,`write`/`edit` 还会公开 `sandbox_permissions` 与 `justification`;被拒绝的变更返回 `[sandbox: file access denied under <mode> mode]` 标记与同轮次升级提示,获批的重试可以在该次调用中加盖严格更宽的模式。
71
+
72
+ ### 失败与恢复
73
+
74
+ 失败被规范化为 `Error: <message>`,并为调用方保留结构化错误码。稳定消息包括 `file_path must be a non-empty string`、`limit must be less than or equal to <max>`、`cannot read "<path>": not found`、`cannot read "<path>": not a regular file`,以及图像路由拒绝 `cannot read "<path>" as an image: model "<model>" does not declare image input; switch to an image-capable model to read images`。无论拒绝来自策略还是提供方,`FS_NOT_OBSERVED` 都规范化为 `cannot modify "<path>": file has not been read — read the file, then retry`;`FS_STALE_VERSION` 保留提供方原因并追加 `— re-read the file, then retry`。该次重新读取确认缺失后,`edit` 报告 `FS_NOT_FOUND` 而不会重复陈旧恢复指令,`write` 则使用防护创建。
75
+
76
+ -----
77
+
78
+ <a id="understand-the-implementation"></a>
79
+ ## 理解实现
80
+
81
+ <details>
82
+ <summary>实现细节——点击展开</summary>
83
+
84
+ 本节解释工具套件背后的设计决策,并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
85
+
86
+ ### 设计理念
87
+
88
+ 工具就是执行器;策略是事件门禁。工具不注入策略服务,也不检查任何缓存——每次变更都通过 `ctx.waterfall` 向单一意图槽位请求防护,每个操作只在成功后发出 `fs/observed`。读取恰好执行一次提供方 `stat`(类型与大小路由加观察到的版本);变更一次也不执行,因为防护来自意图槽位,提供方在锁内重新检查。
89
+
90
+ ### 源码地图
91
+
92
+ | 文件 | 职责 |
93
+ |---|---|
94
+ | [`src/index.ts`](src/index.ts) | 插件入口:`Config`、工具组合、`read_image` 附件门禁 |
95
+ | [`src/read.ts`](src/read.ts) | `read` 执行器:一次 stat、流式决策、窗口构建、观察 |
96
+ | [`src/read-image.ts`](src/read-image.ts) | `read_image` 执行器:路由与媒体类型门禁、有界字节、附件保存 |
97
+ | [`src/write.ts`](src/write.ts) | `write` 执行器:意图 waterfall、原子写入、观察 |
98
+ | [`src/edit.ts`](src/edit.ts) | `edit` 执行器:意图 waterfall、字面量编辑、观察 |
99
+ | [`src/read-render.ts`](src/read-render.ts) | 不依赖 Cordis 的窗口构建与信封格式化 |
100
+ | [`src/sandbox.ts`](src/sandbox.ts) | `write`/`edit` 共享的升权 API:策略解析与拒绝标记映射 |
101
+ | [`src/error.ts`](src/error.ts) | 防护变更失败的稳定模型侧诊断 |
102
+
103
+ ### 各工具流程
104
+
105
+ 四个工具共享同一种流程形态:用调用会话的 cwd 解析路径、运行适用的门禁、恰好执行一次提供方操作,并且只在成功后发出 `fs/observed`。`read` 与 `read_image` 为类型与大小路由付出一次 `stat`;`write` 与 `edit` 不执行 stat,因为防护来自意图槽位,提供方失败以类型化 `FsError` 结果呈现。各工具执行器位于 `src/read.ts`、`src/read-image.ts`、`src/write.ts` 与 `src/edit.ts`。
106
+
107
+ ### 观察与并发
108
+
109
+ `fs/observed` 在操作成功之后通过普通 `ctx.emit` 发出;监听器的约定是同步且只有副作用的记录器,因此异步或可能失败的观察不属于该事件。`read` 允许并发调度,因为它唯一改变状态的操作是同步记录版本;稍后的 `write` 或 `edit` 会在目标锁内重新检查版本,因此记录器竞态会安全地失败,两个变更工具仍保持互斥。
110
+
111
+ </details>
112
+
113
+ -----
114
+
115
+ <a id="further-exploration"></a>
116
+ ## 进一步探索
117
+
118
+ 当包级约定不够用时阅读以下页面。它们从工具逐步进入它们所组合的约定、后端与策略。
119
+
120
+ - [文件系统子系统](../../../docs/subsystems/filesystem.zh.md)——穷尽式提供方约定、策略事件与错误分类体系。
121
+ - [dsh-fs](../fs/README.zh.md)——这些工具消费的 `ctx.fs` 约定。
122
+ - [fs-local](../fs-local/README.zh.md)——这些工具运行于其上的宿主文件系统后端。
123
+ - [fs-sandbox](../fs-sandbox/README.zh.md)——添加升权字段的沙箱强制后端。
124
+ - [fs-observation-policy](../fs-observation-policy/README.zh.md)——通过 `fs/*` 事件防护变更的策略插件。
125
+ - [生成工具目录](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-fs)——本包注册的穷尽式 schema。
126
+
127
+ -----
128
+
129
+ <a id="model-experience"></a>
130
+ ## 模型体验
131
+
132
+ ### 系统提示词
133
+
134
+ #### 模型看到的内容
135
+
136
+ 组装时,每个指导段落通过 `ctx.tools.get(name, scope)` 检查对应工具,仅在该 agent 可见时输出。write 段落仅在 edit 可见时推荐 edit。三个工具都可用时,下方原文保持不变;限制的施加、解除和工具注册变化在下次组装时生效。同一检查适用于直接限制 agent 和 subagent 的 `toolFilter`,也适用于通过 `run_code` 暴露的 PTC 能力。 write/edit 中的先读后改句子描述观察策略,并非要求调用名为 `read` 的工具。隐藏 `read` 时仍保留这些句子:策略继续保护修改操作,其他产生观察记录的操作(例如 `str_replace_editor` 的 `command: view`)也能建立同一文件观察记录。工具可见性不会禁用该前置条件。
137
+
138
+ ##### Read 指导
139
+
140
+ ```markdown
141
+ 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.
142
+ ```
143
+
144
+ ##### Write 指导
145
+
146
+ ```markdown
147
+ 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-observation-policy requires it) and prefer edit for targeted changes.
148
+ ```
149
+
150
+ ##### Edit 指导
151
+
152
+ ```markdown
153
+ 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-observation-policy requires it), unless you just created or edited it in this session.
154
+ ```
155
+
156
+ #### Token 影响
157
+
158
+ 指导成本取决于可见工具及其适用的跨工具推荐。
159
+
160
+ #### KV Cache 影响
161
+
162
+ 可见工具集合、插件作用域和指导文本不变时,前缀保持稳定。限制或插件生命周期变化可能从首个变化的段落开始使复用失效。
163
+
164
+ ### 工具 schema
165
+
166
+ #### 模型看到的内容
167
+
168
+ 模型会看到已生成的 [`read`、`read_image`、`write` 和 `edit` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-fs),参数使用 snake_case。图片工具只在持久附件存储已挂载时出现;schema 本身与路由无关,严格门禁在执行时拒绝。作用域工具限制可以为某个 agent 移除任一定义。
169
+
170
+ #### Token 影响
171
+
172
+ 该工具视图中的每个请求都支付固定 schema 成本。
173
+
174
+ #### KV Cache 影响
175
+
176
+ 只要可见工具定义和顺序不变,前缀就保持稳定。注册生命周期或作用域限制可能从首个变化的 schema token 开始使复用失效。
177
+
178
+ ### 读取结果
179
+
180
+ #### 模型看到的内容
181
+
182
+ 成功读取结果精确为 `<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)`。读取缺失目标仍返回 `FS_NOT_FOUND`,但会为调用会话记录确认缺失;外部删除的文件被重新读取后,重试的 `write` 可以通过提供方的不替换防护安全地重新创建该文件。
183
+
184
+ #### Token 影响
185
+
186
+ 读取输出受 `readLimit`、`readMaxLineLength` 与 `readMaxBytes` 限制;保留的调用与结果会反复发送,直到上下文压缩(compaction)。
187
+
188
+ #### KV Cache 影响
189
+
190
+ 仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
191
+
192
+ ### 图像读取结果
193
+
194
+ #### 模型看到的内容
195
+
196
+ 成功的 `read_image` 返回 `<path><displayPath></path>`、`<type>image</type>` 和写明媒体类型、规范化尺寸与字节数的 `<content>` 信封,随后是作为原生图像块的图像本身。结果会随持久引用写入会话日志,然后才进入下一次模型请求。
197
+
198
+ #### Token 影响
199
+
200
+ 图像在之后每次请求中都会计费,直到压缩。每次调用都独立受附件存储的 `maxImageBytes`/`maxImagePixels`/`maxImageDimension` 约束;重复成功调用会在历史中累积,内容寻址只去重存储的字节,不去重每次请求的 token 成本。
201
+
202
+ #### KV Cache 影响
203
+
204
+ 仅追加;新可见内容跟在可复用请求前缀之后,不会使既有 KV 缓存条目失效。
205
+
206
+ ### 写入与编辑结果
207
+
208
+ #### 模型看到的内容
209
+
210
+ 写入精确返回五行包络:`<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 工具调用参数中。
211
+
212
+ #### Token 影响
213
+
214
+ 成功文本很少,但大型变更参数和所有结果会反复发送,直到上下文压缩。
215
+
216
+ #### KV Cache 影响
217
+
218
+ 仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
219
+
220
+ ### 工具错误
221
+
222
+ #### 模型看到的内容
223
+
224
+ 失败会规范化为 `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)`、`cannot read "<path>": the <ext> extension does not declare a supported image format; read_image accepts PNG/JPEG/WebP/GIF files, including extension-less files in those formats`、`cannot read "<path>": the file content is not a supported image format; read_image accepts PNG/JPEG/WebP/GIF`、`cannot read "<path>": the bytes do not decode as a supported PNG/JPEG/WebP/GIF image; the file may be truncated or corrupt`、`cannot read "<path>" as an image: model "<model>" does not declare image input; switch to an image-capable model to read images`,以及类型不匹配的修复消息 `cannot read "<path>": the <ext> extension declares <type>, but the bytes use a different image format; rename the file to match its actual format if it is PNG/JPEG/WebP/GIF, or convert it to one of those formats`(无扩展名路径的不匹配报告 `cannot read "<path>": the file signature claims <type>, but the bytes decode as a different image format; the file may be corrupt`)。16-bit 转换失败会报告 `cannot read "<path>": the 16-bit PNG could not be converted to the normalized 8-bit sRGB form; convert it to an 8-bit PNG/JPEG/WebP and retry`。提供方和策略模板在各自包的 README 中逐字列出。模型侧错误包装把所有 `FS_NOT_OBSERVED` 来源规范化为 `cannot modify "<path>": file has not been read — read the file, then retry`;`FS_STALE_VERSION` 保留提供方原因并追加 `— re-read the file, then retry`。两者都保留结构化错误码和原始原因。该次重新读取确认缺失后,`edit` 会报告 `FS_NOT_FOUND`,而不会重复陈旧恢复指令;`write` 则使用带防护的创建。
225
+
226
+ #### Token 影响
227
+
228
+ 只有失败调用会添加这些保留 token。
229
+
230
+ #### KV Cache 影响
231
+
232
+ 仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
233
+
234
+ ## 已知限制与延期工作
235
+
236
+ <a id="known-limitations-and-deferred-work"></a>
237
+
238
+
239
+ 这些限制说明工具套件何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是通用文件系统对比或任务积压。
240
+
241
+ - **未交付面向模型的目录列表工具**:`ctx.fs.listDir` 服务于 skill(技能)发现等提供方代码,同级 `dsh-tool-fs-search` 包则提供基于 ripgrep 的 `glob` 与 `grep`,而不是扩展文件系统 seam。
242
+ - **`read` 只处理 UTF-8 文本文件**:图像使用独立的 `read_image` 工具;PDF、音频和视频仍延期处理。目录目标为 `FS_NOT_REGULAR_FILE`。
243
+ - **媒体类型按扩展名声明**:扩展名选择声明类型,附件存储的魔数校验保持权威;扩展名错误但格式正确的图像会得到改名修复提示,而不是被嗅探接受。只有没有扩展名的路径按文件签名识别格式。
244
+ - **对象路径重新走源准入**:对规范化附件对象调用 `read_image` 会把其字节作为新来源重新准入,因此把 `maxImageBytes`/`maxMessageImageBytes` 配置得低于规范化图片字节预算的部署可能拒绝 `ctx.attachments.readImage` 仍可读取的对象路径;默认配置下规范化预算(4 MiB)远低于源上限(20 MiB)。
245
+ - **内嵌图像预览依赖 UI 组合**:工具结果卡片经由浏览器的 `tool.call.images` 槽位渲染图像,由附件呈现插件填充;未组合该插件的 UI 改为显示结果的信封文本。
246
+ - **没有附件区域工具**:agent 在拥有文件系统路径时可以通过其他可用工具裁剪图片;没有路径的粘贴或拖入图片无法按更高分辨率重新读取。
247
+ - **没有超时接口**:`read`/`write`/`edit` 不接受超时参数,也不声明超时预算;取消只通过 `exec.signal` 传递(见[提供方理由](../README.zh.md))。
248
+
249
+ <a id="dev-note"></a>
250
+ ### 开发备注
251
+
252
+ <details>
253
+ <summary>维护者的工作上下文——点击展开</summary>
254
+
255
+ 无。
256
+
257
+ </details>
258
+
259
+ **运行时不变式:** 不发布伴生入口。这个模型侧 adapter 没有独立 lifecycle stream;执行关系由它调用的 capability seam 负责。