description: "The model-facing read, read_image, write, and edit tools for users and maintainers composing or debugging filesystem access for agents."
English | 中文
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.
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.
A backend, the policy plugin, then the tools; the attachment store is optional and enables read_image.
- name: '@deepseek-ai/dsh-fs-local'
- name: '@deepseek-ai/dsh-fs-observation-policy'
- name: '@deepseek-ai/dsh-tool-fs'
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.
| Tool | Arguments | Behavior |
|---|---|---|
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 |
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 |
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 |
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 |
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.
All keys are optional; the defaults are the shipped read caps.
| Key | Default | Meaning |
|---|---|---|
readLimit |
2000 |
Default and maximum lines returned by one read call |
readMaxLineLength |
2000 |
Characters kept per line before truncation |
readMaxBytes |
51200 |
Byte cap on one read call's selected lines; overflow ends the window with a capped footer |
readStreamMinSize |
10485760 |
Files at or above this size (or of unknown size) stream instead of loading whole into memory |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
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.
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.
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.
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.
ctx.fs contract these tools consume.fs/* events.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.
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.
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.
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.
Guidance cost follows the visible tools and their applicable cross-tool recommendations.
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.
The model sees the generated read, read_image, write, and edit schemas, 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.
Fixed schema cost on every request in that tool view.
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.
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.
Read output is capped by readLimit, readMaxLineLength, and readMaxBytes; the retained call and result are resent until compaction.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
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.
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.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
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.
Success text is small, but large mutation arguments and any result are resent until compaction.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
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.
Only a failing call adds these retained tokens.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
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.
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.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.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).tool.call.images slot, which the attachment presentation plugin fills; a UI without that plugin shows the result's envelope text instead.read/write/edit take no timeout argument and declare no timeout budget; cancellation rides exec.signal only (provider rationale).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.