Files
deepseek-harness/packages/fs/fs-policy
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.

Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.

The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.

Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:13 +08:00
..

@deepseek-ai/dsh-fs-policy

English | 中文

The fs-policy plugin: it records observed presence or absence and adds read-before-edit plus guarded write/edit on top of the ctx.fs provider contract (@deepseek-ai/dsh-fs) — through the fs/* event gate, NOT through a method service. This plugin registers no ctx.fsPolicy service and has no public read/write/edit/resolve methods. It is the policy third of the filesystem stack: not a swappable seam, but the policy that does not belong on the FileSystem provider base class.

import type { Context } from '@deepseek-ai/cordis'
import * as FsPolicy from '@deepseek-ai/dsh-fs-policy'

declare const ctx: Context

// No service to inject — this plugin only registers the three fs/* listeners.
// Load it alongside a ctx.fs provider (e.g. @deepseek-ai/dsh-fs-local) and the
// @deepseek-ai/dsh-tool-fs tools; the tools dispatch the fs/* events this plugin
// decides. Order does not matter for resolution (no inject), but the policy
// listener should be the first decider registered for the fs/*-intent slots.
await ctx.plugin(FsPolicy)

The four-layer split

Layer Package Role
tool / executor @deepseek-ai/dsh-tool-fs model-facing schemas + read windowing + text rendering; reads/writes/edits via ctx.fs, dispatches the fs/* events
policy @deepseek-ai/dsh-fs-policy (this) observed-state + read-before-edit + version-guarded write/edit, contributed through the fs/* event gate (no service)
provider contract @deepseek-ai/dsh-fs ctx.fs: text IO + atomic mutation primitives (optional version guard); owns the fs/* event vocabulary
provider @deepseek-ai/dsh-fs-local local implementation of ctx.fs

How the gate participates

Three fs/* events (declared by @deepseek-ai/dsh-fs, dispatched by @deepseek-ai/dsh-tool-fs):

Event This plugin's listener
fs/write-intent Unseen or observed absent → { kind: 'createIfAbsent' }; observed present → { kind: 'replaceIfVersion', version: vObserved }. Single-slot decision; does NOT call next().
fs/edit-intent Unseen → FS_NOT_OBSERVED; observed absent → FS_NOT_FOUND; observed present → { version: vObserved } as the CAS basis. Single-slot decision; does NOT call next().
fs/observed Records { kind: 'present', version } or { kind: 'absent' } for this owner+target. Synchronous, side-effect-only WeakMap.set.

Observed state is the prior-observation record; freshness is provider CAS

Observed state is a weak owner-to-target map with three logical states: unseen, confirmed absent, or present at a version. A successful file read or mutation records presence; a metadata miss from read or the str_replace_editor view, str_replace, or insert command records absence before returning FS_NOT_FOUND. The plugin performs no filesystem I/O: it converts that state into a provider guard. Presence supplies the observed version, while absence lets only a createIfAbsent write proceed; edit has no version basis and returns FS_NOT_FOUND. A windowed read observes the whole file version, so a later targeted edit is allowed only while that file remains unchanged. State is discarded on plugin disposal and is not persisted across sessions.

Single-slot, first-wins

The fs/write-intent/fs/edit-intent slots hold exactly one decider — this plugin fully decides and does not call next(). The slot is first-wins by registration order; this plugin owning it is the default-deployment convention, not an event-enforced invariant (a decider registered before / prepended would win instead). This is not a composable authorization chain — layered permission/audit/sandbox interception belongs on tools/execute.

No method coupling

Because the plugin influences the world only through events, removing it does not break @deepseek-ai/dsh-tool-fs at a service-injection boundary: the tool falls through to the bare ctx.fs provider (unconditional write/edit, no observed-state). Loading it back layers the policy on. That graceful add/remove is the whole point of the event gate over a mandatory method service.

Model Experience

Filesystem tool outcome

What the model sees

This plugin adds no prompt or schema. It rejects an edit without a prior observation with code FS_NOT_OBSERVED and exact message edit requires reading "<path>" first; editing a target just observed absent returns FS_NOT_FOUND. Guarded mutations whose positive observation is stale propagate the provider-owned FS_STALE_VERSION error. dsh-tool-fs owns the model-facing error wrapper, which appends the recovery instruction to FS_STALE_VERSION (— re-read the file, then retry) and FS_NOT_OBSERVED (— read the file, then retry) messages while preserving the code. Following the stale remedy on an externally deleted target now records absence: the next guarded write may recreate it with createIfAbsent, while the provider atomically preserves any concurrent creator.

Token effect

Zero tokens on allowed operations beyond the ordinary tool result. A denial adds the small retained error result and avoids any success payload.

KV Cache effect

Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.

Known Limitations and Deferred Work

  • Observed state does not survive a session resume — persistence of the WeakMap record is deferred, so a resumed session must re-read files before guarded writes/edits.
  • Actors without an agent session can never satisfy the policy — their edits throw FS_NOT_OBSERVED and their writes always resolve createIfAbsent, so a non-agent caller cannot overwrite an existing file through the gate.
  • Direct ctx.fs reads emit no fs/observed — a file read outside the read tool stays unobserved, and a later guarded edit rejects with FS_NOT_OBSERVED until the tool reads it.
  • Authorization is version freshness, not view completeness — any windowed read authorizes a full-file overwrite of an unchanged file, deliberately weaker than a full-view rule (seam-split Agent Note).