feat(fs): append recovery remedy to guarded-mutation errors

write/edit failures with FS_STALE_VERSION or FS_NOT_OBSERVED now reach the
model with the correct recovery instruction appended (re-read / read, then
retry) while preserving the structured code and chaining the cause. The
edit-intent waterfall sits inside the same try, so the policy's
FS_NOT_OBSERVED refusal is remediated too. Re-recorded the fs-policy-reject
keyless snapshot and the bilingual README pairs.
This commit is contained in:
_Kerman
2026-08-03 19:49:30 +08:00
parent 012bc40f04
commit 1ee167aeac
13 changed files with 174 additions and 16 deletions
+4 -2
View File
@@ -12,6 +12,7 @@ import type { FsWriteOutcome } from '@deepseek-ai/dsh-fs'
import type {} from '@deepseek-ai/dsh-fs'
import type {} from '@deepseek-ai/dsh-system-prompt'
import { computeHunkDiffs, diffsFromMeta } from './diff.ts'
import { remediateFsError } from './error.ts'
import { sessionResolveOptions } from './session-cwd.ts'
import type { FsSandboxSurface } from './sandbox.ts'
@@ -113,8 +114,9 @@ export function applyWriteTool(ctx: Context, sandbox: FsSandboxSurface): void {
outcome = await ctx.fs.writeText(target, input.content, intent, exec.signal, sandboxPolicy)
} catch (error: unknown) {
// A sandbox denial becomes the shared [sandbox: …] marker (the model
// recognizes it from bash); any other error passes through.
throw sandbox.mapError(error, sandboxPolicy)
// recognizes it from bash); stale/not-observed failures gain their
// model-facing remedy; anything else passes through.
throw remediateFsError(sandbox.mapError(error, sandboxPolicy))
}
// Record the observed version (a no-op when no policy plugin listens).
ctx.emit('fs/observed', target, outcome.version, exec)