fix(fs): address review — rename to dsh-fs-policy, fs/*-intent events, RFC currency, ENOTDIR

Rename per review naming decisions:
- package dsh-file-context → dsh-fs-policy (dir, package name, plugin name,
  tsconfig refs, importers, type-equiv manifest, generated catalog + module-graph)
- events fs/write-expectation → fs/write-intent, fs/edit-expectation → fs/edit-intent
  (fs/observed unchanged); type FsWriteExpectation → FsWriteIntent, "expectation"
  wording → "intent" throughout
- exported FileContextExec → FsPolicyExec

Make the implemented RFCs describe what shipped, not the superseded designs:
the 2026-06-17 capability-seam + tool-schemas RFCs no longer place policy on
ctx.fs or use full/partial-view authorization, and the fsspec RFC's ctx.fileContext
service prose is rewritten to the fs/* event-gate reality (freshness-based auth).
Sharpen docs/rfc/implemented/AGENTS.md: a rename is a fact to fix IN PLACE — the
"new RFC" escape hatch is for macro decision reversals only, not renames.

Code fixes from review:
- fsio.ts resolveLocalTarget/probe translate ENOTDIR (a parent path segment is a
  file) into the structured FsError taxonomy instead of leaking a raw Node error;
  resolve reports FS_NOT_FOUND, probe reports absent. Regression tests proven to
  fail on the unfixed code.
- tool-fs HMR test now asserts prompt sections (not just tool schemas) are
  withdrawn on disposal.
- fs/observed is a plain (unguarded) ctx.emit: correct the fs-policy comment,
  filesystem.md, and tool-fs module doc that wrongly claimed the tool "contains"
  a throwing listener; a throw surfaces as the tool's isError result.
- drop the false "loaded by the default product config" claim (no config wires
  the fs tools yet), the duplicate ctx.bash service-map row, the stale
  FileReadRequest catalog link-map entry, and the fs/fs README EOF blank line;
  correct the dsh-fs package.json description.
This commit is contained in:
Tianyi Cui
2026-07-02 03:12:38 +08:00
parent e4b4eaaf38
commit 30c1863755
42 changed files with 360 additions and 323 deletions

View File

@@ -1,10 +1,10 @@
/**
* The model-facing `write` tool: create or fully replace a UTF-8 text file. The
* tool is the executor: it dispatches the `fs/write-expectation` waterfall to
* tool is the executor: it dispatches the `fs/write-intent` waterfall to
* obtain the optional version guard, calls `ctx.fs.writeText` directly, and
* emits `fs/observed`. The default thunk returns `undefined` (unconditional
* create-or-overwrite — the bare provider); a policy plugin
* (`@deepseek-ai/dsh-file-context`) occupies the single decision slot and
* (`@deepseek-ai/dsh-fs-policy`) occupies the single decision slot and
* returns `createIfAbsent`/`replaceIfVersion` instead. The tool stats ZERO
* times either way.
*
@@ -39,7 +39,7 @@ export function applyWriteTool(ctx: Context): void {
ctx.systemPrompt.section({
name: 'tool:write',
order: 101,
text: 'Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default file-context policy requires it) and prefer edit for targeted changes.',
text: '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-policy requires it) and prefer edit for targeted changes.',
})
ctx.tools.register(defineTool({
@@ -54,8 +54,8 @@ export function applyWriteTool(ctx: Context): void {
const target = await ctx.fs.resolve(input.filePath)
// Single-slot decision: the policy plugin produces createIfAbsent/
// replaceIfVersion; the bare default is undefined (unconditional). No stat.
const expectation = await ctx.waterfall('fs/write-expectation', target, exec, () => undefined)
const outcome = await ctx.fs.writeText(target, input.content, expectation, exec.signal)
const intent = await ctx.waterfall('fs/write-intent', target, exec, () => undefined)
const outcome = await ctx.fs.writeText(target, input.content, intent, exec.signal)
// Record the observed version (a no-op when no policy plugin listens).
ctx.emit('fs/observed', target, outcome.version, exec)
return [{ type: 'text', text: formatWriteOutput(target.displayPath, outcome) }]