|
|
@@ -26,9 +26,9 @@ Filesystem access is a first-class capability seam following [the capability-sea
|
|
|
2. `@deepseek-ai/dsh-fs-local` (`packages/fs/fs-local`) provides the first implementation, backed by the local filesystem.
|
|
|
3. `@deepseek-ai/dsh-tool-fs` (`packages/fs/tool-fs`) provides the model-facing `read`, `write`, and `edit` tools over `ctx.fs`, and is the executor that dispatches the `fs/*` events.
|
|
|
|
|
|
-The consumer package depends only on the interface package, never on `dsh-fs-local`. A deployment that wants a different backend loads a different provider for `ctx.fs` without changing the tool schemas or model-facing prompt guidance.
|
|
|
+The Consumer package depends only on the Service Definition package, never on `dsh-fs-local`. A deployment that wants a different backend loads a different provider for `ctx.fs` without changing the tool schemas or model-facing prompt guidance.
|
|
|
|
|
|
-The read-before-write/edit and observed-state policy is a fourth package, `@deepseek-ai/dsh-fs-policy` (`packages/fs/fs-policy`), contributed through the `fs/*` event gate rather than living on `ctx.fs`; a deployment loading `dsh-tool-fs` also loads `dsh-fs-policy` to get read-before-write/edit. This Agent Note established the three-package seam; the split of policy off the provider base class is decided by [the split-fs-seam Agent Note](../simplification/2026-06-26-fsspec-style-fs-seam.md), and its realization as an event-gate plugin (not a method service) by [the event-gate Agent Note](2026-06-26-file-context-as-event-gate.md). This document is updated to describe that landed four-package shape.
|
|
|
+The read-before-write/edit and observed-state policy is a fourth package, `@deepseek-ai/dsh-fs-policy` (`packages/fs/fs-policy`), contributed through the `fs/*` event gate rather than living on `ctx.fs`; a deployment loading `dsh-tool-fs` also loads `dsh-fs-policy` to get read-before-write/edit. This Agent Note established the three-package boundary; the split of policy off the provider base class is decided by [the split-fs-seam Agent Note](../simplification/2026-06-26-fsspec-style-fs-seam.md), and its realization as an event-gate plugin (not a method service) by [the event-gate Agent Note](2026-06-26-file-context-as-event-gate.md). This document is updated to describe that landed four-package shape.
|
|
|
|
|
|
The first backend is deliberately local-only: `dsh-fs-local` implements `ctx.fs` against the host filesystem. Future sibling backends can provide sandboxed, remote, virtual, or project-scoped filesystems behind the same interface.
|
|
|
|
|
|
@@ -53,7 +53,7 @@ The filesystem seam uses the same dependency direction as the bash trio:
|
|
|
|
|
|
`@deepseek-ai/dsh-tool-fs` depends on `@deepseek-ai/dsh-fs`, `@deepseek-ai/dsh-tools`, `@deepseek-ai/dsh-system-prompt`, and `cordis`. It registers model-facing tools and prompt sections. It must not import `node:fs`, `node:path`, or `@deepseek-ai/dsh-fs-local`; filesystem execution always goes through `ctx.fs`. If the implementation needs concrete agent or session helper types, those dependencies belong in `tool-fs`; they must not leak back into `dsh-fs`.
|
|
|
|
|
|
-The root `tool-fs` plugin registers the full filesystem tool suite (`read`, `write`, and `edit`) by composing the per-tool registration helpers. It injects `fs` and never imports an implementation package.
|
|
|
+The root `tool-fs` plugin registers the full filesystem tool suite (`read`, `write`, and `edit`) by composing the per-tool registration helpers. It injects `fs` and never imports a Service provider package.
|
|
|
|
|
|
## `ctx.fs` contract
|
|
|
|
|
|
@@ -68,7 +68,7 @@ The interface covers these semantic operations:
|
|
|
- Create or replace a UTF-8 text file.
|
|
|
- Edit an existing UTF-8 text file by literal replacement.
|
|
|
|
|
|
-The provider seam also carries the freshness hooks that policy builds on — but the observed-state store and owner derivation live in the `dsh-fs-policy` plugin, not on `ctx.fs`:
|
|
|
+The provider contract also carries the freshness hooks that policy builds on — but the observed-state store and owner derivation live in the `dsh-fs-policy` plugin, not on `ctx.fs`:
|
|
|
|
|
|
- The backend mints an opaque `version` token per target (in `stat` and in every read/mutation outcome).
|
|
|
- `writeText`/`editText` take an OPTIONAL version expectation: omit it for an unconditional bare-provider mutation, or supply it to guard the mutation inside the backend's atomic critical section.
|
|
|
@@ -127,7 +127,7 @@ The root plugin registers the full suite by composing the per-tool registration
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
-Tests follow the package boundary, not only the user-visible tools: the service seam in `dsh-fs`; real filesystem behavior through the `ctx.fs` interface in `dsh-fs-local` (resolution, symlinks, streaming, binary/UTF-8 rejection, unconditional and version-guarded writes, literal-edit semantics, line-ending preservation, structured `FsError` codes); the consumer surface in `dsh-tool-fs` against the real local provider (mock only the model/clock, never the collaborator); and integration through `ctx.tools.execute()` with and without `dsh-fs-policy`, world-verified by reading files back from disk rather than trusting either the canonical value or rendered content. The observed-state/owner-derivation policy is tested in `dsh-fs-policy`, not here.
|
|
|
+Tests follow the package boundary, not only the user-visible tools: the service contract in `dsh-fs`; real filesystem behavior through the `ctx.fs` interface in `dsh-fs-local` (resolution, symlinks, streaming, binary/UTF-8 rejection, unconditional and version-guarded writes, literal-edit semantics, line-ending preservation, structured `FsError` codes); the consumer surface in `dsh-tool-fs` against the real local provider (mock only the model/clock, never the collaborator); and integration through `ctx.tools.execute()` with and without `dsh-fs-policy`, world-verified by reading files back from disk rather than trusting either the canonical value or rendered content. The observed-state/owner-derivation policy is tested in `dsh-fs-policy`, not here.
|
|
|
|
|
|
The defensive-pattern classes this repo has been bitten by are pinned directly:
|
|
|
|
|
|
@@ -139,7 +139,7 @@ The defensive-pattern classes this repo has been bitten by are pinned directly:
|
|
|
## Alternatives considered
|
|
|
|
|
|
- **Model-facing tools directly over `node:fs`** — the tool package would own execution policy, path resolution, atomic writes, text decoding, and edit semantics at once, coupling the three independently-changing concerns the Problem names and churning schemas on any backend swap.
|
|
|
-- **One combined `dsh-fs-tools` package** — the pre-seam shape; rejected for the same interface/implementation/consumer split as bash, and the combined name never became public surface.
|
|
|
+- **One combined `dsh-fs-tools` package** — the pre-seam shape; rejected for the same Service Definition / Service provider / Consumer split as bash, and the combined name never became public surface.
|
|
|
- **Observed-state on `ctx.fs`** — the shape this Agent Note first landed; superseded by [the split-fs-seam Agent Note](../simplification/2026-06-26-fsspec-style-fs-seam.md) and [the event-gate Agent Note](2026-06-26-file-context-as-event-gate.md): a sandboxed/remote backend must not inherit model-facing observation policy, so the provider keeps only the version token and the optional version-guarded mutation.
|
|
|
|
|
|
## Consequences
|
|
|
@@ -152,7 +152,7 @@ The defensive-pattern classes this repo has been bitten by are pinned directly:
|
|
|
|
|
|
**Edit semantics are race-prone by nature.** Literal edit is a read-modify-write operation; the guard is the backend's atomic mutation critical section plus the optional version expectation, so concurrent edits settle deterministically — one wins, the other gets `FS_STALE_VERSION`.
|
|
|
|
|
|
-**Observed state does not belong on `ctx.fs`.** Recording what an execution context has seen is workflow policy, not raw filesystem I/O. This Agent Note first placed it inside the filesystem seam; the split-fs-seam Agent Note then established that a sandboxed/remote backend should not inherit model-facing observation policy, and moved it into the `dsh-fs-policy` plugin. The provider seam keeps only what write/edit safety genuinely needs at the storage layer — a backend-minted version token and an optional version-guarded mutation — while the policy plugin owns owner derivation, observed-state, and read-before-edit gating over the `fs/*` events.
|
|
|
+**Observed state does not belong on `ctx.fs`.** Recording what an execution context has seen is workflow policy, not raw filesystem I/O. This Agent Note first placed it inside the filesystem seam; the split-fs-seam Agent Note then established that a sandboxed/remote backend should not inherit model-facing observation policy, and moved it into the `dsh-fs-policy` plugin. The provider contract keeps only what write/edit safety genuinely needs at the storage layer — a backend-minted version token and an optional version-guarded mutation — while the policy plugin owns owner derivation, observed-state, and read-before-edit gating over the `fs/*` events.
|
|
|
|
|
|
**The `resolve`-then-operate shape costs an extra round-trip per call.** Each tool may resolve a path to an `FsTarget` and then issue the read/write/edit as a separate `ctx.fs` call. For the local backend this is negligible (resolution is in-memory path normalization), but a remote/sandboxed backend may turn each step into its own request, so a single `read` can become two network round-trips. Backends where the round-trip matters can cache or fold resolution internally while preserving the observable contract.
|
|
|
|