@swoop111/dsh-tool-fs 0.0.0-stage → 0.2.0-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,142 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
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
+ /:
6
+ en: 52b93805befe8077
7
+ zh: 9786e662303f0e2f
8
+ /deepseek-ai-dsh-tool-fs:
9
+ en: fc8f28deebe5fa6b
10
+ zh: 738c6ad45034da88
11
+ /deepseek-ai-dsh-tool-fs/summary:
12
+ en: c8f608c85c8d6626
13
+ zh: 4133a867517baca5
14
+ /deepseek-ai-dsh-tool-fs/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /deepseek-ai-dsh-tool-fs/use-this-package:
18
+ en: 8fd62afcdabe55d4
19
+ zh: 9bdcae272575be8c
20
+ /deepseek-ai-dsh-tool-fs/use-this-package/minimal-composition:
21
+ en: eb21d89a3ecbd2d4
22
+ zh: f05ccbb1e7515f32
23
+ /deepseek-ai-dsh-tool-fs/use-this-package/the-tools:
24
+ en: acc54c7be5b7397c
25
+ zh: 99ea9935645aa347
26
+ /deepseek-ai-dsh-tool-fs/use-this-package/configuration:
27
+ en: 037c36f06859e293
28
+ zh: aeb3b35361de0165
29
+ /deepseek-ai-dsh-tool-fs/use-this-package/policy-and-sandbox-behavior:
30
+ en: 53d4ac2db089ef39
31
+ zh: 214ea051ed56f500
32
+ /deepseek-ai-dsh-tool-fs/use-this-package/failures-and-recovery:
33
+ en: c9eb77c3c3deceb9
34
+ zh: b474cd2a764021db
35
+ /deepseek-ai-dsh-tool-fs/understand-the-implementation:
36
+ en: e30f7038c28e93f8
37
+ zh: a3868dbd90367b13
38
+ /deepseek-ai-dsh-tool-fs/understand-the-implementation/design-concept:
39
+ en: 454b5291db9ba847
40
+ zh: 9fe7081dfb416c93
41
+ /deepseek-ai-dsh-tool-fs/understand-the-implementation/source-map:
42
+ en: 6c84b66a20a4cc5b
43
+ zh: cf840725912bc6f8
44
+ /deepseek-ai-dsh-tool-fs/understand-the-implementation/per-tool-flow:
45
+ en: dad0c07c04e0fdb5
46
+ zh: caa0c4be18ee866c
47
+ /deepseek-ai-dsh-tool-fs/understand-the-implementation/observation-and-concurrency:
48
+ en: 665d437dc35bf05f
49
+ zh: 8004334d6a3246b4
50
+ /deepseek-ai-dsh-tool-fs/further-exploration:
51
+ en: 485bc55482f576bc
52
+ zh: fc5fa55b41098bd0
53
+ /deepseek-ai-dsh-tool-fs/model-experience:
54
+ en: 215e7ba838619b7b
55
+ zh: a311da3843709f90
56
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt:
57
+ en: cf78b9dd5596495f
58
+ zh: bd44560f8c2435e8
59
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt/what-the-model-sees:
60
+ en: b09ee4be5e522c29
61
+ zh: ffd30b57d7fc96e8
62
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt/what-the-model-sees/read-guidance:
63
+ en: f8961df3f3a6a671
64
+ zh: 85c7bd418b40b089
65
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt/what-the-model-sees/write-guidance:
66
+ en: 7e34af48c583f210
67
+ zh: e39ad6823689c665
68
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt/what-the-model-sees/edit-guidance:
69
+ en: 28f07502a411303b
70
+ zh: 437897dc99fc4360
71
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt/token-effect:
72
+ en: 7a44cabe439a879a
73
+ zh: 1adf9bf3de7658b6
74
+ /deepseek-ai-dsh-tool-fs/model-experience/system-prompt/kv-cache-effect:
75
+ en: 4f1b5639f91405a8
76
+ zh: f90ffe077a2e52a3
77
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-schemas:
78
+ en: 3039bc5e7a796b21
79
+ zh: 6b7a9eaf69c9ea53
80
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-schemas/what-the-model-sees:
81
+ en: 6f2a072c474e39e7
82
+ zh: dbee4c2b206bf68f
83
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-schemas/token-effect:
84
+ en: 68517f7d65ef7e9d
85
+ zh: 544a4b81e8ea16c0
86
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-schemas/kv-cache-effect:
87
+ en: 1e6d3da20012762d
88
+ zh: 482f8bc569b8b490
89
+ /deepseek-ai-dsh-tool-fs/model-experience/read-result:
90
+ en: ebd38733c0490428
91
+ zh: b45101c325efe784
92
+ /deepseek-ai-dsh-tool-fs/model-experience/read-result/what-the-model-sees:
93
+ en: 68047e354dfb387c
94
+ zh: 1456ebecbedf7a90
95
+ /deepseek-ai-dsh-tool-fs/model-experience/read-result/token-effect:
96
+ en: 4269b4368622ec2f
97
+ zh: f4df80c1fb6972f1
98
+ /deepseek-ai-dsh-tool-fs/model-experience/read-result/kv-cache-effect:
99
+ en: f5d5f97a153f7c70
100
+ zh: 3d6bc05b9eadfbc2
101
+ /deepseek-ai-dsh-tool-fs/model-experience/image-read-result:
102
+ en: 9dac44e6d0b9d8de
103
+ zh: 7bc4dc1c5df39eaf
104
+ /deepseek-ai-dsh-tool-fs/model-experience/image-read-result/what-the-model-sees:
105
+ en: 9252b6d73776c5ff
106
+ zh: d6e433450989399e
107
+ /deepseek-ai-dsh-tool-fs/model-experience/image-read-result/token-effect:
108
+ en: 68bd5255f1c9ca48
109
+ zh: 0474213531c1aae3
110
+ /deepseek-ai-dsh-tool-fs/model-experience/image-read-result/kv-cache-effect:
111
+ en: f5d5f97a153f7c70
112
+ zh: 48e202e396bc5fcc
113
+ /deepseek-ai-dsh-tool-fs/model-experience/write-and-edit-results:
114
+ en: 1b3b6cfa3605d0b4
115
+ zh: 78e37c2aa4f76d54
116
+ /deepseek-ai-dsh-tool-fs/model-experience/write-and-edit-results/what-the-model-sees:
117
+ en: bb2a92e0e43e3273
118
+ zh: a744395576b48a93
119
+ /deepseek-ai-dsh-tool-fs/model-experience/write-and-edit-results/token-effect:
120
+ en: fb4d0b17a99f49cf
121
+ zh: 17460a2a5d72f6cc
122
+ /deepseek-ai-dsh-tool-fs/model-experience/write-and-edit-results/kv-cache-effect:
123
+ en: f5d5f97a153f7c70
124
+ zh: 3d6bc05b9eadfbc2
125
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-errors:
126
+ en: 2d4f2c8605eccab3
127
+ zh: 67f1f0218f73c6bf
128
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-errors/what-the-model-sees:
129
+ en: 4e8fac7bac8ced61
130
+ zh: 7ba921dbcddc9f97
131
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-errors/token-effect:
132
+ en: 681cadd5be7b8ac1
133
+ zh: 65e483f62fb819d9
134
+ /deepseek-ai-dsh-tool-fs/model-experience/tool-errors/kv-cache-effect:
135
+ en: f5d5f97a153f7c70
136
+ zh: 3d6bc05b9eadfbc2
137
+ /deepseek-ai-dsh-tool-fs/known-limitations-and-deferred-work:
138
+ en: ce7a91d41d2f7597
139
+ zh: d4a3c905e124fd7f
140
+ /deepseek-ai-dsh-tool-fs/known-limitations-and-deferred-work/dev-note:
141
+ en: 0c4d0c465413d319
142
+ zh: a20efeb10121b927
package/README.md CHANGED
@@ -1,3 +1,278 @@
1
- # Temporary Holding Version
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
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @deepseek-ai/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: '@deepseek-ai/dsh-fs-local'
36
+ - name: '@deepseek-ai/dsh-fs-observation-policy'
37
+ - name: '@deepseek-ai/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?`, `force?` | Line-numbered UTF-8 content with a pagination footer; `offset` is 1-based and `limit` defaults to and caps at the configured `readLimit`; `force` (present when `readRepair.duplicate` is enabled) reads the window even when the duplicate guard would refuse it |
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
+ | `readRepair.enabled` | `true` | Run the read repair rules for `read`'s addressing and path failures; disable to always error |
64
+ | `readRepair.duplicate.enabled` | `true` | Refuse a read fully covered by this session's recent window of the same unchanged file version, answering with the delivered range instead of the content |
65
+ | `readRepair.duplicate.maxSteps` | `4` | Steps committed since the recorded window beyond which the model is assumed to have forgotten the content, so the re-read is served |
66
+ | `readRepair.completion.enabled` | `true` | Complete a window's structural boundaries: prepend the line that opened the construct the window starts inside, append the lines that close the construct it ends inside (fence, bracket block, indentation block) |
67
+ | `readRepair.completion.maxLines` | `40` | Maximum lines appended to close the tail, and how far back the head search may reach for the opening line |
68
+ | `editRepair.enabled` | `true` | Run the `fs-edit-repair` engine when `edit`'s `old_string` fails to match verbatim, and re-anchor `edit` to a verified matching path when the target is absent; disable to always error |
69
+ | `editRepair.maxFileChars` | `1000000` | Content length above which the line-window repair rules are skipped |
70
+ | `editRepair.maxWindowLines` | `64` | `old_string` line count above which the line-window repair rules are skipped |
71
+ | `writeRepair.pathHint` | `true` | On a create, disclose a verified high-similarity existing path as a note — the write itself is never rewritten |
72
+
73
+ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-fs) is the exhaustive source for every accepted field and its JSDoc.
74
+
75
+ ### Policy and sandbox behavior
76
+
77
+ 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.
78
+
79
+ 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. The justification asks the model to use the language of the current user request.
80
+
81
+ ### Failures and recovery
82
+
83
+ 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`, the duplicate refusal `duplicate read blocked: lines <first>-<last> unchanged, delivered <n> steps ago; that content is still above. Use offset=<next>, or force=true.`, 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.
84
+
85
+ With `readRepair` enabled (the default), `read` repairs its measured addressing failures instead of erroring: a 0-based or otherwise invalid `offset` reads from line 1, a non-positive or over-cap `limit` lands on the configured cap, a start past the file's end reads the last window of the requested length (an empty file returns the empty window), and a missing path is re-anchored through the `fs-edit-repair` engine — a unique match from this session's prior successful path arguments, then a unique directory-tree completion, each verified present. Disclosure is minimal: the two argument normalizations are stated once per session in a `(repair: …)` line after the footer, because the delivered window and its footer already carry the effective range; a re-anchored path is stated every time, because the corrected path is a new fact; and the tail anchor states nothing.
86
+
87
+ With `readRepair.duplicate.enabled`, a read fully covered by this session's recent window of the same file version is refused: it fails with `duplicate read blocked: …`, naming the unchanged range, the steps since, and the two ways on (`force: true`, or continuing at the offset after the delivered range). The forgetting rule keeps legitimate re-reads — a mutating tool committed for the file, a compaction replaced the surface, or more than `maxSteps` steps passed since the recorded window. With `readRepair.completion.enabled`, a window that starts inside a construct gains the line that opened it, and one that ends inside a construct gains the following lines that close it; neither boundary is stated, because the added lines carry their own numbers and the footer counts the window that was delivered. Completion never modifies, reorders, or drops a delivered line, adds no opening line when the opener lies outside the searched range, and appends nothing when no closure is provable within `maxLines`. A window whose construct runs to the end of the file is served unchanged.
88
+
89
+ When an `edit` fails because its target is absent (confirmed by a stat), the engine re-anchors the edit to a verified path — a unique match from this session's prior successful path arguments, then a unique directory-tree completion — applies it through the same guarded seam, and discloses the substitution in a `(repair: …)` line; a genuine stale-version failure (target present) keeps its own remedy, and a refusal on the repaired path (unread target, sandbox denial) surfaces as-is. A `write` that creates a file at a path similar to an existing one appends a `(hint: …)` line naming that path — creation at the requested path is legitimate, so the write itself is never rewritten.
90
+
91
+ -----
92
+
93
+ <a id="understand-the-implementation"></a>
94
+ ## Understand the implementation
95
+
96
+ <details>
97
+ <summary>Implementation internals — click to expand</summary>
98
+
99
+ 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).
100
+
101
+ ### Design concept
102
+
103
+ 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.
104
+
105
+ ### Source map
106
+
107
+ | File | Role |
108
+ |---|---|
109
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `Config`, tool composition, `read_image` attachments gate |
110
+ | [`src/read.ts`](src/read.ts) | `read` executor: one stat, streaming decision, window build, observation |
111
+ | [`src/read-image.ts`](src/read-image.ts) | `read_image` executor: route and media-type gates, bounded bytes, attachment save |
112
+ | [`src/write.ts`](src/write.ts) | `write` executor: intent waterfall, atomic write, observation |
113
+ | [`src/edit.ts`](src/edit.ts) | `edit` executor: intent waterfall, literal edit, observation |
114
+ | [`src/read-render.ts`](src/read-render.ts) | Cordis-free windowing and envelope formatting |
115
+ | [`src/structure.ts`](src/structure.ts) | Fence, bracket, and indentation analysis of window boundaries |
116
+ | [`src/completion.ts`](src/completion.ts) | Structural completion: head and tail assembly over the window's neighbouring lines |
117
+ | [`src/sandbox.ts`](src/sandbox.ts) | Escalation API shared by `write`/`edit`: policy resolution and denial-marker mapping |
118
+ | [`src/error.ts`](src/error.ts) | Stable model-facing diagnostics for guarded-mutation failures |
119
+
120
+ ### Per-tool flow
121
+
122
+ 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`.
123
+
124
+ Window completion reads the file again for the lines on each side of the window: the preceding lines whenever the window starts after line 1, and the following lines only when the window ends inside an open construct. Both fetches are capped at `readRepair.completion.maxLines` lines and never alter the window's own lines.
125
+
126
+ ### Observation and concurrency
127
+
128
+ `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.
129
+
130
+ </details>
131
+
132
+ -----
133
+
134
+ <a id="further-exploration"></a>
135
+ ## Further Exploration
136
+
137
+ 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.
138
+
139
+ - [Filesystem subsystem](../../../docs/subsystems/filesystem.md) — exhaustive provider contract, policy events, and error taxonomy.
140
+ - [dsh-fs](../fs/README.md) — the `ctx.fs` contract these tools consume.
141
+ - [fs-local](../fs-local/README.md) — the host-filesystem backend these tools run against.
142
+ - [fs-sandbox](../fs-sandbox/README.md) — the sandbox-enforcing backend that adds the escalation fields.
143
+ - [fs-observation-policy](../fs-observation-policy/README.md) — the policy plugin that guards mutations through the `fs/*` events.
144
+ - [Generated tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-tool-fs) — the exhaustive schemas this package registers.
145
+
146
+ -----
147
+
148
+ <a id="model-experience"></a>
149
+ ## Model Experience
150
+
151
+ ### System prompt
152
+
153
+ #### What the model sees
154
+
155
+ 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.
156
+
157
+ ##### Read guidance
158
+
159
+ ```markdown
160
+ Use the read tool — not shell commands like cat — to inspect text files. Use offset and limit to continue reading large files.
161
+ ```
162
+
163
+ ##### Write guidance
164
+
165
+ ```markdown
166
+ Read an existing file before overwriting it with write (the default fs-observation-policy requires it) and prefer edit for targeted changes.
167
+ ```
168
+
169
+ ##### Edit guidance
170
+
171
+ ```markdown
172
+ Read a file before editing it (the default fs-observation-policy requires it), unless you just created or edited it in this session.
173
+ ```
174
+
175
+ #### Token effect
176
+
177
+ Guidance cost follows the visible tools and their applicable cross-tool recommendations.
178
+
179
+ #### KV Cache effect
180
+
181
+ 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.
182
+
183
+ ### Tool schemas
184
+
185
+ #### What the model sees
186
+
187
+ 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.
188
+
189
+ #### Token effect
190
+
191
+ Fixed schema cost on every request in that tool view.
192
+
193
+ #### KV Cache effect
194
+
195
+ 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.
196
+
197
+ ### Read result
198
+
199
+ #### What the model sees
200
+
201
+ 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)`. When a read states a repair, exactly one `(repair: <what>)` line follows the footer. 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.
202
+
203
+ #### Token effect
204
+
205
+ Read output is capped by `readLimit`, `readMaxLineLength`, and `readMaxBytes`; the retained call and result are resent until compaction.
206
+
207
+ #### KV Cache effect
208
+
209
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
210
+
211
+ ### Image read result
212
+
213
+ #### What the model sees
214
+
215
+ 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.
216
+
217
+ #### Token effect
218
+
219
+ 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.
220
+
221
+ #### KV Cache effect
222
+
223
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
224
+
225
+ ### Write and edit results
226
+
227
+ #### What the model sees
228
+
229
+ 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.
230
+
231
+ #### Token effect
232
+
233
+ Success text is small, but large mutation arguments and any result are resent until compaction.
234
+
235
+ #### KV Cache effect
236
+
237
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
238
+
239
+ ### Tool errors
240
+
241
+ #### What the model sees
242
+
243
+ 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.
244
+
245
+ #### Token effect
246
+
247
+ Only a failing call adds these retained tokens.
248
+
249
+ #### KV Cache effect
250
+
251
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
252
+
253
+ ## Known Limitations and Deferred Work
254
+
255
+ <a id="known-limitations-and-deferred-work"></a>
256
+
257
+
258
+ 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.
259
+
260
+ - **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.
261
+ - **`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`.
262
+ - **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.
263
+ - **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).
264
+ - **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.
265
+ - **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.
266
+ - **No timeout surface** — `read`/`write`/`edit` take no timeout argument and declare no timeout budget; cancellation rides `exec.signal` only ([provider rationale](../README.md)).
267
+
268
+ <a id="dev-note"></a>
269
+ ### Dev Note
270
+
271
+ <details>
272
+ <summary>Working context for maintainers — click to expand</summary>
273
+
274
+ None.
275
+
276
+ </details>
277
+
278
+ **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.