@deepseek-ai/dsh-fs 0.1.1-rc.1 → 0.1.2-alpha.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/README.i18n.yaml +2 -2
- package/README.md +94 -35
- package/README.zh.md +94 -35
- package/lib/index.js +9 -0
- package/lib/types/index.d.ts +9 -0
- package/package.json +11 -11
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/fs/fs/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: d90e5de8d4cbc2232dfa68a20c1d7c1c1cbdbc75
|
|
6
|
+
README.zh.md: a60f509581bfa435b9b4e00f600197e40877470e
|
package/README.md
CHANGED
|
@@ -1,55 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The ctx.fs filesystem service contract for deployments choosing or mounting a filesystem backend and developers implementing one."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-fs
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-fs` defines the `ctx.fs` filesystem service: a compact, backend-neutral contract for one execution world that resolves paths to stable identities, maps shared host files when supported, reads text and raw bytes within bounds, lists directories, and applies atomic writes and literal edits. It deliberately leaves storage mechanics to the backends that implement it — `fs-local` for the host filesystem, `fs-sandbox` for policy-enforced confinement, and `fs-e2b` for a remote execution world. Both mutations take an optional version guard, so a backend mounted without the policy plugin still gives complete, unconstrained, atomic file operations. The package also owns the `fs/*` policy-event vocabulary that the tool package dispatches and the policy plugin decides. Choose it when you need a swappable filesystem surface; the model-facing tools themselves live in `dsh-tool-fs`.
|
|
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
|
+
You rarely load `dsh-fs` directly: you mount a backend that registers as `ctx.fs`, then either call the service from your own plugin or let the `dsh-tool-fs` tools call it for you. This page serves the two audiences that do touch it — deployments choosing a backend, and developers implementing or consuming the contract.
|
|
29
|
+
|
|
30
|
+
### Choosing and mounting a backend
|
|
31
|
+
|
|
32
|
+
Pick [`fs-local`](../fs-local/README.md) for ordinary host files, [`fs-sandbox`](../fs-sandbox/README.md) when a session's mutations must be confined to its workspace and temp roots, and [`fs-e2b`](../../e2b/fs-e2b/README.md) when file state must live in a remote execution world. Mounting any backend populates `ctx.fs`; swapping backends changes nothing for the policy plugin, the tools, or the tool schemas. A composition that mounts no backend has no `ctx.fs` at all, and the tools fail at registration.
|
|
33
|
+
|
|
34
|
+
### What the service lets you do
|
|
35
|
+
|
|
36
|
+
Through `ctx.fs` you can resolve any path to a stable target identity, read a whole text file or stream it in chunks, read raw bytes up to an explicit cap, list one directory level, atomically create or replace a file, and apply a literal text edit atomically. The version guard on both mutations is optional: omit it for unconditional create-or-overwrite, or supply it to fail when the file changed since you last observed it. Every operation returns data or a typed `FsError` carrying a stable code such as `FS_NOT_FOUND`, `FS_STALE_VERSION`, or `FS_AMBIGUOUS_EDIT`, so callers branch on the code, never on message text.
|
|
37
|
+
|
|
38
|
+
-----
|
|
39
|
+
|
|
40
|
+
<a id="understand-the-implementation"></a>
|
|
41
|
+
## Understand the implementation
|
|
6
42
|
|
|
7
|
-
|
|
43
|
+
<details>
|
|
44
|
+
<summary>Implementation internals — click to expand</summary>
|
|
8
45
|
|
|
9
|
-
|
|
10
|
-
|---|---|---|
|
|
11
|
-
| tool / executor | `@deepseek-ai/dsh-tool-fs` | model-facing `read`/`write`/`edit` schemas + read windowing + text rendering; reads/writes/edits via `ctx.fs`, dispatches the `fs/*` events |
|
|
12
|
-
| policy | `@deepseek-ai/dsh-fs-observation-policy` | observed-state + read-before-edit + version-guarded write/edit, contributed through the `fs/*` event gate (no service) |
|
|
13
|
-
| provider contract | `@deepseek-ai/dsh-fs` (this) | `ctx.fs`: execution-world paths, text IO, and atomic mutation primitives (optional version guard); owns the `fs/*` event vocabulary |
|
|
14
|
-
| provider | `@deepseek-ai/dsh-fs-local` | the host-filesystem implementation |
|
|
46
|
+
This section explains the design decisions behind the contract and points at the code that realizes them; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
15
47
|
|
|
16
|
-
|
|
48
|
+
### Design philosophy
|
|
17
49
|
|
|
18
|
-
|
|
50
|
+
The contract is built on one separation and three commitments:
|
|
19
51
|
|
|
20
|
-
|
|
52
|
+
- **Contract over mechanism.** The service names what a storage layer can do — resolve, stat, read, list, write, edit — and never how it stores bytes. Backends own target identity, execution-world coordinates, decoding, binary rejection, and atomicity.
|
|
53
|
+
- **Policy stays off the base class.** Observed-state, read-before-edit, and version-guarded mutations are a plugin's job (`dsh-fs-observation-policy`), added by supplying the optional guard — so a sandboxed or remote backend inherits no model-facing observation policy.
|
|
54
|
+
- **`editText` stays on the seam.** Version check, literal match, and atomic rewrite share one critical section, so error attribution and one-wins/one-stale concurrency stay correct; a remote backend may implement it as a native compare-and-edit.
|
|
55
|
+
- **Bounds live at this seam.** `readBytes` requires `maxBytes` and fails with `FS_TOO_LARGE` rather than truncating, so no backend ever buffers an unbounded file.
|
|
21
56
|
|
|
22
|
-
|
|
57
|
+
### Source map
|
|
58
|
+
|
|
59
|
+
| File | Role |
|
|
23
60
|
|---|---|
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
| `readText(target, signal?)` | Read the whole regular text file as one decoded string. Owns regular-file checks, UTF-8 decoding, binary/NUL rejection (`FS_NOT_TEXT`). |
|
|
31
|
-
| `streamText(target, signal?)` | Stream the same text as decoded chunks for large files (cross-chunk UTF-8 decoding stays here); consumers that need a byte ceiling enforce it while consuming the stream. |
|
|
32
|
-
| `readBytes(target, signal, maxBytes)` | Read a complete regular file as raw bytes with no decoding or binary rejection. `maxBytes` is required and bounds the complete content at this seam: a known or discovered overflow fails with `FS_TOO_LARGE` instead of truncating or buffering without a bound. |
|
|
33
|
-
| `listDir(target, signal?)` | List direct directory children in stable name order. Returns entry names, entry types, resolved child targets, and cheap metadata (`version`/file `size` when available); never reads file contents. Missing targets throw `FS_NOT_FOUND`, non-directories throw `FS_NOT_DIRECTORY`, permission failures throw `FS_PERMISSION_DENIED`, and other backend I/O failures throw `FS_IO_ERROR`. Broken/disappeared children may be returned as `other` without metadata; child permission/IO failures fail the whole listing with the same structured codes. |
|
|
34
|
-
| `writeText(target, content, expected?, signal?)` | Atomic create/replace. `expected` is OPTIONAL: omit ⇒ unconditional create-or-overwrite; supply an `FsWriteIntent` (`createIfAbsent`/`replaceIfVersion`) to guard. `createIfAbsent` must perform a no-replace publication so a creator racing the initial probe is preserved. |
|
|
35
|
-
| `editText(target, edit, expected?, signal?)` | Literal edit. `expected` is OPTIONAL: omit ⇒ unconditional edit of the current content; supply `{ version }` to guard (verified BEFORE matching). A missing target reports `FS_STALE_VERSION` either way. Applies and writes atomically — one mutation critical section. |
|
|
61
|
+
| [`src/index.ts`](src/index.ts) | Service definition: the abstract `FileSystem` class, the `ctx.fs` declaration, and the `fs/*` event vocabulary |
|
|
62
|
+
| [`src/types.ts`](src/types.ts) | Vocabulary: `FsTarget`/`FsTargetKey`, `FsVersion`, `FsObservation`, `FsWriteIntent`, `FsError` and its codes |
|
|
63
|
+
|
|
64
|
+
### How a call flows
|
|
65
|
+
|
|
66
|
+
Every ordinary operation starts with `resolve(path, { cwd })`, which produces a stable `FsTarget` (an opaque `targetKey` plus a `displayPath` for model/UI output); the same file reached through different paths yields the same key. `processPathFromHostPath(hostPath)` separately maps an absolute host file into this execution world when the backend shares or explicitly maps it, and otherwise returns `undefined`. Reads then go `stat` → `readText`/`streamText`/`readBytes`, listings go `listDir`, and mutations go through one per-target critical section: the optional guard is checked, the new content is applied, and the result is published atomically.
|
|
36
67
|
|
|
37
|
-
The
|
|
68
|
+
### The `fs/*` policy events
|
|
38
69
|
|
|
39
|
-
|
|
70
|
+
The package declares three events so the emitter (`dsh-tool-fs`) and the policy listener (`dsh-fs-observation-policy`) share a vocabulary without the emitter depending on the policy plugin. `fs/write-intent` and `fs/edit-intent` are single-slot decision waterfalls: the first listener decides outright and never calls `next()`. `fs/observed` is a fire-and-forget recording event carrying an `FsObservation` — present with a version, or confirmed absent. The events carry only `dsh-fs` vocabulary plus an opaque `object` actor.
|
|
40
71
|
|
|
41
|
-
|
|
72
|
+
### Invariants
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
- `targetKey` and `version` are branded opaque ids: consumers must not parse or interpret them; only `displayPath` is for model/UI output.
|
|
75
|
+
- Failures are typed `FsError`s with stable codes, never ad-hoc message strings.
|
|
76
|
+
- The seam arms no I/O deadline; cancellation is an optional per-primitive `AbortSignal`.
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
</details>
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
-----
|
|
48
81
|
|
|
49
|
-
|
|
82
|
+
<a id="further-exploration"></a>
|
|
83
|
+
## Further Exploration
|
|
50
84
|
|
|
51
|
-
|
|
85
|
+
Read these pages when the package-level contract is not enough. They move from the exhaustive contract to the backends and consumers built on it.
|
|
52
86
|
|
|
87
|
+
- [Filesystem subsystem](../../../docs/subsystems/filesystem.md) — exhaustive provider contract, policy events, and error taxonomy.
|
|
88
|
+
- [fs-local](../fs-local/README.md) — the host-filesystem backend implementing this contract.
|
|
89
|
+
- [fs-sandbox](../fs-sandbox/README.md) — the sandbox-enforcing backend implementing this contract.
|
|
90
|
+
- [tool-fs](../tool-fs/README.md) — the model-facing tools that consume `ctx.fs`.
|
|
91
|
+
- [fs-observation-policy](../fs-observation-policy/README.md) — the policy plugin that guards mutations through the `fs/*` events.
|
|
92
|
+
- [Capability seams note](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md) — why the filesystem stack splits into contract, provider, policy, and tools.
|
|
93
|
+
|
|
94
|
+
-----
|
|
95
|
+
|
|
96
|
+
<a id="model-experience"></a>
|
|
53
97
|
## Model Experience
|
|
54
98
|
|
|
55
99
|
Indirectly, through `dsh-tool-fs`, which renders provider text and errors as bounded, retained filesystem tool results.
|
|
@@ -60,7 +104,22 @@ No direct invalidation; the named consumer owns any request-prefix changes.
|
|
|
60
104
|
|
|
61
105
|
## Known Limitations and Deferred Work
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
107
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
These limits define when the contract is a poor fit or needs special operational care. They are current package constraints, not a general filesystem comparison or a task backlog.
|
|
111
|
+
|
|
112
|
+
- **Text-only mutations by contract** — text reads and both mutations reject binary or non-UTF-8 content with `FS_NOT_TEXT`; `readBytes` is the single raw-byte primitive, and binary-safe mutations remain deferred ([tool-schemas Agent Note](../../../.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.md)).
|
|
113
|
+
- **Thirteen primitives only** — no delete, rename, copy, or watch; `listDir` lists a single level, with recursion, globbing, pagination, and search out of scope ([directory-listing note](../../../.agents/notes/archived/architecture/2026-07-03-filesystem-directory-listing-seam.md)).
|
|
114
|
+
- **No I/O deadline** — the seam arms no timeout; cancellation is a best-effort optional `AbortSignal` per primitive ([fs family stance](../README.md)).
|
|
66
115
|
- **Resolve-then-operate costs a remote backend two round-trips per tool call** — folding or caching resolution is left to such a backend.
|
|
116
|
+
|
|
117
|
+
<a id="dev-note"></a>
|
|
118
|
+
### Dev Note
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
122
|
+
|
|
123
|
+
None.
|
|
124
|
+
|
|
125
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,55 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "`ctx.fs` 文件系统服务约定:面向选择或挂载文件系统后端的部署方,以及实现后端的开发者。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-fs
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-fs` 定义 `ctx.fs` 文件系统服务:一个紧凑、与后端无关的约定,面向同一个执行世界,把路径解析为稳定身份、在受支持时映射共享宿主文件、在界内读取文本与原始字节、列出目录,并原子地执行写入与字面量编辑。它有意把存储机制留给实现它的后端——`fs-local` 面向宿主文件系统,`fs-sandbox` 面向策略强制的隔离,`fs-e2b` 面向远程执行世界。两个变更操作都带可选版本防护,因此即使不加载策略插件,挂载的后端依然提供完整、不受约束、原子的文件操作。本包还拥有由工具包分派、策略插件决策的 `fs/*` 策略事件词汇。当你需要可替换的文件系统表面时选择它;面向模型的工具本身位于 `dsh-tool-fs`。
|
|
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
|
+
你很少直接加载 `dsh-fs`:你挂载一个注册为 `ctx.fs` 的后端,然后从自己的插件调用该服务,或让 `dsh-tool-fs` 工具替你调用。本页服务于确实接触它的两类读者——选择后端的部署方,以及实现或消费该约定的开发者。
|
|
29
|
+
|
|
30
|
+
### 选择并挂载后端
|
|
31
|
+
|
|
32
|
+
普通宿主文件选择 [`fs-local`](../fs-local/README.zh.md),会话变更必须限制在工作区与临时根目录内时选择 [`fs-sandbox`](../fs-sandbox/README.zh.md),文件状态必须位于远程执行世界时选择 [`fs-e2b`](../../e2b/fs-e2b/README.zh.md)。挂载任一后端都会填充 `ctx.fs`;更换后端不会改变策略插件、工具或工具 schema。未挂载任何后端的组合就没有 `ctx.fs`,工具会在注册时失败。
|
|
33
|
+
|
|
34
|
+
### 服务能做什么
|
|
35
|
+
|
|
36
|
+
通过 `ctx.fs`,你可以把任意路径解析为稳定的目标身份、完整读取或分片流式读取文本文件、按显式上限读取原始字节、列出一层目录、原子地创建或替换文件,并原子地应用字面量文本编辑。两个变更操作上的版本防护都是可选的:省略它即无条件创建或覆盖,提供它则在文件自上次观察以来发生变化时失败。每个操作要么返回数据,要么抛出携带稳定错误码(如 `FS_NOT_FOUND`、`FS_STALE_VERSION`、`FS_AMBIGUOUS_EDIT`)的类型化 `FsError`,调用方依据错误码分支,绝不解析消息文本。
|
|
37
|
+
|
|
38
|
+
-----
|
|
39
|
+
|
|
40
|
+
<a id="understand-the-implementation"></a>
|
|
41
|
+
## 理解实现
|
|
6
42
|
|
|
7
|
-
|
|
43
|
+
<details>
|
|
44
|
+
<summary>实现细节——点击展开</summary>
|
|
8
45
|
|
|
9
|
-
|
|
10
|
-
|---|---|---|
|
|
11
|
-
| 工具/执行器 | `@deepseek-ai/dsh-tool-fs` | 面向模型的 `read`/`write`/`edit` schema、读取窗口和文本渲染;通过 `ctx.fs` 读取/写入/编辑,并分派 `fs/*` 事件 |
|
|
12
|
-
| 政策 | `@deepseek-ai/dsh-fs-observation-policy` | 已观察状态、编辑前读取和版本防护的写入/编辑,通过 `fs/*` 事件门禁贡献(无服务) |
|
|
13
|
-
| 提供方约定 | `@deepseek-ai/dsh-fs`(本包) | `ctx.fs`:执行世界路径、文本 I/O 与原子变更原语(可选版本防护);拥有 `fs/*` 事件词汇 |
|
|
14
|
-
| 提供方 | `@deepseek-ai/dsh-fs-local` | 宿主文件系统实现 |
|
|
46
|
+
本节解释约定背后的设计决策,并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
15
47
|
|
|
16
|
-
|
|
48
|
+
### 设计理念
|
|
17
49
|
|
|
18
|
-
|
|
50
|
+
该约定建立在一个分离与三项承诺之上:
|
|
19
51
|
|
|
20
|
-
|
|
52
|
+
- **约定高于机制。** 服务只命名存储层能做什么——解析、stat、读取、列出、写入、编辑——绝不规定如何存储字节。后端拥有目标身份、执行世界坐标、解码、二进制拒绝与原子性。
|
|
53
|
+
- **策略不放在基类上。** 已观察状态、编辑前读取与版本防护的变更是插件(`dsh-fs-observation-policy`)的职责,通过提供可选防护来添加——因此沙箱化或远程后端不会继承任何面向模型的观察策略。
|
|
54
|
+
- **`editText` 留在 seam 上。** 版本校验、字面量匹配与原子重写共享同一个临界区,错误归因与一方胜出/一方陈旧的并发语义因此保持正确;远程后端也可以将其实现为原生比较并编辑操作。
|
|
55
|
+
- **界限制在此 seam 上。** `readBytes` 要求 `maxBytes`,并以 `FS_TOO_LARGE` 失败而不是截断,因此任何后端都不会无界缓冲文件。
|
|
21
56
|
|
|
22
|
-
|
|
57
|
+
### 源码地图
|
|
58
|
+
|
|
59
|
+
| 文件 | 职责 |
|
|
23
60
|
|---|---|
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
| `readText(target, signal?)` | 把整个普通文本文件读取为一个解码后的字符串。负责普通文件检查、UTF-8 解码和二进制/NUL 拒绝(`FS_NOT_TEXT`)。 |
|
|
31
|
-
| `streamText(target, signal?)` | 为大文件按解码后的分片流式读取相同文本(跨分片 UTF-8 解码仍由此处负责);需要字节上限的消费方在消费流时执行该上限。 |
|
|
32
|
-
| `readBytes(target, signal, maxBytes)` | 把完整普通文件按原始字节读出,不做解码或二进制拒绝。`maxBytes` 为必填,在该 seam 上限制完整内容:已知或读取中发现的超限以 `FS_TOO_LARGE` 失败,而不是截断或无界缓冲。 |
|
|
33
|
-
| `listDir(target, signal?)` | 按稳定名称顺序列出直接子项。返回条目名称、条目类型、解析后的子目标和低成本元数据(若可用则包括 `version`/文件 `size`);绝不读取文件内容。缺失目标抛出 `FS_NOT_FOUND`,非目录抛出 `FS_NOT_DIRECTORY`,权限失败抛出 `FS_PERMISSION_DENIED`,其他后端 I/O 失败抛出 `FS_IO_ERROR`。损坏/消失的子项可以作为无元数据的 `other` 返回;子项权限/I/O 失败会使用相同结构化代码使整个列表失败。 |
|
|
34
|
-
| `writeText(target, content, expected?, signal?)` | 原子创建/替换。`expected` 是可选的:省略 ⇒ 无条件创建或覆盖;提供 `FsWriteIntent`(`createIfAbsent`/`replaceIfVersion`)⇒ 添加防护。`createIfAbsent` 必须以不替换的方式发布,使初始探测后抢先创建的文件得到保留。 |
|
|
35
|
-
| `editText(target, edit, expected?, signal?)` | 字面量编辑。`expected` 是可选的:省略 ⇒ 无条件编辑当前内容;提供 `{ version }` ⇒ 添加防护,并在匹配之前校验。无论哪种情况,目标缺失都报告 `FS_STALE_VERSION`。应用和写入以原子方式完成,使用同一个变更临界区。 |
|
|
61
|
+
| [`src/index.ts`](src/index.ts) | 服务定义:抽象 `FileSystem` 类、`ctx.fs` 声明与 `fs/*` 事件词汇 |
|
|
62
|
+
| [`src/types.ts`](src/types.ts) | 词汇:`FsTarget`/`FsTargetKey`、`FsVersion`、`FsObservation`、`FsWriteIntent`、`FsError` 及其错误码 |
|
|
63
|
+
|
|
64
|
+
### 调用流程
|
|
65
|
+
|
|
66
|
+
每个普通操作都以 `resolve(path, { cwd })` 开始,它产生稳定的 `FsTarget`(不透明 `targetKey` 加用于模型/UI 输出的 `displayPath`);经不同路径到达同一文件会产生相同 key。`processPathFromHostPath(hostPath)` 在后端共享或显式映射宿主文件时,单独把绝对宿主文件映射进此执行世界,否则返回 `undefined`。读取随后执行 `stat` → `readText`/`streamText`/`readBytes`,列出执行 `listDir`,变更则经过每个目标一个临界区:先检查可选防护,应用新内容,再原子发布结果。
|
|
36
67
|
|
|
37
|
-
|
|
68
|
+
### `fs/*` 策略事件
|
|
38
69
|
|
|
39
|
-
|
|
70
|
+
本包声明三个事件,使发出方(`dsh-tool-fs`)与策略监听器(`dsh-fs-observation-policy`)共享词汇,而无需让发出方依赖策略插件。`fs/write-intent` 与 `fs/edit-intent` 是单槽决策 waterfall(瀑布式事件):第一个监听器直接决策,绝不调用 `next()`。`fs/observed` 是发后即忘的记录事件,携带 `FsObservation`——存在并带版本,或确认缺失。事件只携带 `dsh-fs` 词汇和一个不透明 `object` 参与者。
|
|
40
71
|
|
|
41
|
-
|
|
72
|
+
### 不变式
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
- `targetKey` 与 `version` 是带品牌的不透明 id:消费方不得解析或解释它们;只有 `displayPath` 用于模型/UI 输出。
|
|
75
|
+
- 失败是携带稳定错误码的类型化 `FsError`,绝不是临时拼写的消息字符串。
|
|
76
|
+
- 该 seam 不设 I/O deadline;取消是每个原语上可选的 `AbortSignal`。
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
</details>
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
-----
|
|
48
81
|
|
|
49
|
-
|
|
82
|
+
<a id="further-exploration"></a>
|
|
83
|
+
## 进一步探索
|
|
50
84
|
|
|
51
|
-
|
|
85
|
+
当包级约定不够用时阅读以下页面。它们从穷尽式约定逐步进入构建于其上的后端与消费方。
|
|
52
86
|
|
|
87
|
+
- [文件系统子系统](../../../docs/subsystems/filesystem.zh.md)——穷尽式提供方约定、策略事件与错误分类体系。
|
|
88
|
+
- [fs-local](../fs-local/README.zh.md)——实现该约定的宿主文件系统后端。
|
|
89
|
+
- [fs-sandbox](../fs-sandbox/README.zh.md)——实现该约定的沙箱强制后端。
|
|
90
|
+
- [tool-fs](../tool-fs/README.zh.md)——消费 `ctx.fs` 的面向模型工具。
|
|
91
|
+
- [fs-observation-policy](../fs-observation-policy/README.zh.md)——通过 `fs/*` 事件防护变更的策略插件。
|
|
92
|
+
- [能力 seam 笔记](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md)——文件系统栈为何拆分为约定、提供方、策略与工具。
|
|
93
|
+
|
|
94
|
+
-----
|
|
95
|
+
|
|
96
|
+
<a id="model-experience"></a>
|
|
53
97
|
## 模型体验
|
|
54
98
|
|
|
55
99
|
通过 `dsh-tool-fs` 间接产生影响;该消费方把提供方文本和错误渲染为有界且保留的文件系统工具结果。
|
|
@@ -60,7 +104,22 @@
|
|
|
60
104
|
|
|
61
105
|
## 已知限制与延期工作
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
107
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
这些限制说明该约定何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是通用文件系统对比或任务积压。
|
|
111
|
+
|
|
112
|
+
- **变更操作约定只支持文本**:文本读取和两个变更操作都以 `FS_NOT_TEXT` 拒绝二进制/非 UTF-8 内容;`readBytes` 是唯一的原始字节原语,二进制安全的变更操作仍延期(见[工具 schema Agent Note](../../../.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.zh.md))。
|
|
113
|
+
- **只有十三个原语**:没有删除、重命名、复制或监视;`listDir` 只列出一层,递归、glob、分页与搜索不在范围内(见[目录列出笔记](../../../.agents/notes/archived/architecture/2026-07-03-filesystem-directory-listing-seam.md))。
|
|
114
|
+
- **没有 I/O deadline**:该 seam 不启动超时;取消只是每个原语上尽力而为的可选 `AbortSignal`(见[fs 能力族立场](../README.zh.md))。
|
|
66
115
|
- **先解析后操作使远程后端每次工具调用需要两次往返**:折叠或缓存解析由这种后端自行决定。
|
|
116
|
+
|
|
117
|
+
<a id="dev-note"></a>
|
|
118
|
+
### 开发备注
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
122
|
+
|
|
123
|
+
无。
|
|
124
|
+
|
|
125
|
+
</details>
|
package/lib/index.js
CHANGED
|
@@ -72,6 +72,15 @@ var FileSystem = class extends Service {
|
|
|
72
72
|
* for a backend that never confines.
|
|
73
73
|
*/
|
|
74
74
|
get sandboxMode() {}
|
|
75
|
+
/**
|
|
76
|
+
* Map an absolute path from the harness host into this filesystem's
|
|
77
|
+
* execution world when both paths identify the same file. The base provider
|
|
78
|
+
* exposes no mapping; host-backed or explicitly shared backends override it.
|
|
79
|
+
* @param hostPath - absolute path in the harness host filesystem.
|
|
80
|
+
* @returns the process path for the same file, or undefined when this
|
|
81
|
+
* execution world cannot read that host file.
|
|
82
|
+
*/
|
|
83
|
+
processPathFromHostPath(hostPath) {}
|
|
75
84
|
};
|
|
76
85
|
//#endregion
|
|
77
86
|
export { FileSystem, FileSystem as default, FsError, FsTargetKey, FsVersion };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -95,6 +95,15 @@ export declare abstract class FileSystem extends Service {
|
|
|
95
95
|
* @returns an absolute path in the backend's execution world.
|
|
96
96
|
*/
|
|
97
97
|
abstract processPath(target: FsTarget): string;
|
|
98
|
+
/**
|
|
99
|
+
* Map an absolute path from the harness host into this filesystem's
|
|
100
|
+
* execution world when both paths identify the same file. The base provider
|
|
101
|
+
* exposes no mapping; host-backed or explicitly shared backends override it.
|
|
102
|
+
* @param hostPath - absolute path in the harness host filesystem.
|
|
103
|
+
* @returns the process path for the same file, or undefined when this
|
|
104
|
+
* execution world cannot read that host file.
|
|
105
|
+
*/
|
|
106
|
+
processPathFromHostPath(hostPath: string): string | undefined;
|
|
98
107
|
/**
|
|
99
108
|
* Return the canonical `file:` URI for a target in this filesystem's
|
|
100
109
|
* execution world. Backends own URI encoding because the host platform may
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-fs",
|
|
3
3
|
"description": "Abstract filesystem capability seam (ctx.fs) for the DeepSeek Harness — vocabulary types, the FileSystem service (text IO + optional version-guarded atomic mutations), and the fs/* policy event vocabulary",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
],
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@deepseek-ai/dsh-brand": "^0.1.
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
37
|
-
"@deepseek-ai/
|
|
38
|
-
"@deepseek-ai/dsh-
|
|
39
|
-
"@deepseek-ai/
|
|
35
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
|
|
37
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
38
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
39
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@deepseek-ai/dsh-
|
|
43
|
-
"@deepseek-ai/dsh-
|
|
44
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
45
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.
|
|
46
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
42
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.2",
|
|
43
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
44
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
|
|
45
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.2",
|
|
46
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
47
47
|
}
|
|
48
48
|
}
|