feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity

Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox
RFC's deferred cross-family phase.

- dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the
  deployment default mode + workspaceRoot and the per-session override event,
  renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter.
  Decouples the bash seam from dsh-session.
- dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences
  write/edit by the per-call mode (read-only denies, workspace-write contains to
  the workspace + temp roots via the shared writableRoots, danger passes
  through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent
  re-canonicalization. A policy fence in trusted code, not a kernel boundary.
- dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider
  ladder, denial/hint markers, approveEscalation) both tool families use;
  approveEscalation takes a structural approver so dsh-sandbox gains no
  approval/agent dependency, and both tools stay duplication-free.
- tool-fs: write/edit advertise sandbox_permissions/justification under a
  confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker,
  and resolve the same one-approved-wider retry.
- examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating
  that disabled the fs stack under confined modes.

RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old
sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
This commit is contained in:
kingwl
2026-07-14 20:05:57 +08:00
parent c3137b5474
commit 2dc62497ce
100 changed files with 2238 additions and 385 deletions

View File

@@ -201,7 +201,7 @@ Single-slot decision: produce the optional version guard for the next FileSystem
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md)
Source: [`packages/fs/fs/src/index.ts:123`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:124`](../../packages/fs/fs/src/index.ts)
### `fs/observed` — emit
@@ -213,7 +213,7 @@ Record that an actor observed a target at a version, after a successful read/wri
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md)
Source: [`packages/fs/fs/src/index.ts:138`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:139`](../../packages/fs/fs/src/index.ts)
### `fs/write-intent` — waterfall
@@ -225,7 +225,7 @@ Single-slot decision: produce the write intent for the next FileSystem.writeText
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md)
Source: [`packages/fs/fs/src/index.ts:109`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:110`](../../packages/fs/fs/src/index.ts)
## `llm/*`

View File

@@ -79,7 +79,7 @@ onTaskDone(listener: BashTaskListener): () => void
Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../core-data-structures/bash.md) · [BashRunResult](../core-data-structures/bash.md) · [BashTask](../core-data-structures/bash.md) · [BashTaskRead](../core-data-structures/bash.md)
Source: [`packages/bash/bash/src/index.ts:62`](../../packages/bash/bash/src/index.ts)
Source: [`packages/bash/bash/src/index.ts:61`](../../packages/bash/bash/src/index.ts)
## `ctx.codeRuntime` — `CodeRuntime` (abstract seam)
@@ -139,13 +139,13 @@ abstract stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefine
abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>
abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>
abstract listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>
abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>
abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal): Promise<FsEditOutcome>
abstract writeText( target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal, sandboxMode?: SandboxMode, ): Promise<FsWriteOutcome>
abstract editText( target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal, sandboxMode?: SandboxMode, ): Promise<FsEditOutcome>
```
Types: [FsEditOutcome](../core-data-structures/filesystem.md) · [FsEditRequest](../core-data-structures/filesystem.md) · [FsInfo](../core-data-structures/filesystem.md) · [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md) · [FsWriteOutcome](../core-data-structures/filesystem.md)
Types: [FsEditOutcome](../core-data-structures/filesystem.md) · [FsEditRequest](../core-data-structures/filesystem.md) · [FsInfo](../core-data-structures/filesystem.md) · [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md) · [FsWriteOutcome](../core-data-structures/filesystem.md) · [SandboxMode](../core-data-structures/sandbox.md)
Source: [`packages/fs/fs/src/index.ts:172`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:173`](../../packages/fs/fs/src/index.ts)
## `ctx.llm` — `LlmService`
@@ -174,7 +174,7 @@ set(session: Session, name: string): void
Types: [SessionEvent](../core-data-structures/core.md)
Source: [`packages/ui/permission/src/index.ts:115`](../../packages/ui/permission/src/index.ts)
Source: [`packages/ui/permission/src/index.ts:118`](../../packages/ui/permission/src/index.ts)
## `ctx.sandbox` — `SandboxProvider` (abstract seam)
@@ -192,7 +192,13 @@ abstract confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv
Types: [ConfinedArgv](../core-data-structures/sandbox.md) · [SandboxPolicy](../core-data-structures/sandbox.md)
Source: [`packages/sandbox/sandbox/src/index.ts:180`](../../packages/sandbox/sandbox/src/index.ts)
Source: [`packages/sandbox/sandbox/src/index.ts:191`](../../packages/sandbox/sandbox/src/index.ts)
## `ctx.sandboxPolicy` — `SandboxPolicyService`
The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment default mode and workspace root; enforcing implementations read defaultMode and workspaceRoot, and the tool layers fold each session's `sandbox/mode` override with effectiveSandboxMode on top.
Source: [`packages/sandbox/sandbox-policy/src/index.ts:60`](../../packages/sandbox/sandbox-policy/src/index.ts)
## `ctx.sessionPersistence` — `SessionPersistence` (abstract seam)