refactor(tool-fs): consolidate read rendering; drop the fs/observed try-catch

Two cohesion cleanups on the filesystem tool package:

- Fold window.ts + types.ts + formatReadOutput into one cordis-free
  read-render.ts. Line windowing, the FileReadOutcome shape, and output
  formatting are one concern (the read tool's rendering); splitting them across
  three files added no value. read.ts is now just the tool (schema + I/O).

- Drop observe.ts and emit fs/observed with a plain ctx.emit in read/write/edit.
  The event is contractually a synchronous, side-effect-only recorder
  (file-context's listener is a WeakMap.set), so the per-call try/catch guarded
  against a contract violation that cannot happen under the shipped listener —
  defensive code for an impossible case. The event contract (dsh-fs JSDoc,
  README, RFC) is updated to state the fire-and-forget semantics plainly.
This commit is contained in:
Dudu-0223
2026-06-29 10:34:08 +08:00
parent 4a1177093a
commit f8e99b8740
15 changed files with 100 additions and 159 deletions

View File

@@ -117,13 +117,13 @@ declare module 'cordis' {
'fs/edit-expectation'(target: FsTarget, actor: object | undefined, next: () => { version: FsVersion } | undefined | Promise<{ version: FsVersion } | undefined>): Promise<{ version: FsVersion } | undefined>
/**
* Record that an actor observed a target at a version, after a successful
* read/write/edit. Fire-and-forget. A listener MUST be a synchronous,
* side-effect-only recorder (`@deepseek-ai/dsh-file-context`'s is a
* `WeakMap.set`); the tool wraps the emit in a try/catch so a synchronous
* listener bug is logged and swallowed, never failing the already-completed
* mutation. cordis `emit` does not await listener promises, so this is not an
* async-error containment seam — async audit/telemetry does not belong here.
* No listener ⇒ nothing recorded. `actor` is the opaque tool-execution context.
* read/write/edit. Fire-and-forget (plain `emit`). A listener MUST be a
* synchronous, side-effect-only recorder (`@deepseek-ai/dsh-file-context`'s
* is a `WeakMap.set`): the tool does not guard the emit, so a listener that
* throws surfaces as the tool's `isError` result, and cordis `emit` does not
* await listener promises — async or fallible audit/telemetry does not
* belong here. No listener ⇒ nothing recorded. `actor` is the opaque
* tool-execution context.
* @mode emit
*/
'fs/observed'(target: FsTarget, version: FsVersion, actor: object | undefined): void