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:
@@ -121,8 +121,8 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
'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>',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -151,6 +151,11 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
'abstract confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv',
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sandboxPolicy',
|
||||
summary: 'The sandbox-policy service (`ctx.sandboxPolicy`).',
|
||||
methods: [],
|
||||
},
|
||||
{
|
||||
key: 'sessionPersistence',
|
||||
summary: 'Abstract durable session-persistence service.',
|
||||
|
||||
Reference in New Issue
Block a user