feat(permission): user-facing permission presets — one Permissions select over the two knobs

A preset names a bundle of the two mechanism knobs — request =
workspace-write + ask, yolo = danger-full-access + never — so the editor
shows ONE 'Permissions' select where the sandbox-mode and approval-policy
tiers stay orthogonal capabilities (the Codex /approvals shape: presets over
two dials). ctx.permission (dsh-permission) owns the config-defined table,
validates the default preset's bundle against the composed knob defaults at
load (fails loud), and writes a switch THROUGH: one log-only
permission/preset event (the audit fact reverse-mapping cannot recover —
the planned 'agent' preset shares request's knob values and differs only in
composed policy) plus each knob event via its own setter, deduped — a
net-zero switch appends nothing. Every knob consumer keeps reading its own
fold, untouched.

The current preset DERIVES from the effective knob values — the fold breaks
bundle ties, a knob state outside the table is the reserved 'custom' value
(a state, not an error: shown while it holds, switchable FROM, never a
target), and defaultPreset disappears (zero-event state reverse-maps from
the composition defaults).

The ACP bridge drops the two per-knob selects for the one preset select
(advertised only when ctx.permission is composed); pending/anchor/no-op
semantics carry over unchanged, with the no-op echo acknowledged before
vocabulary validation so a client re-pushing a derived 'custom' current
never errors. The sandbox variant example composes the
service with a workspace-write default; the permission-switching,
escalation-approved and escalation-rejected scenarios are re-recorded under
it (escalations now target an outside-workspace /tmp path under
danger-full-access, self-cleaning) and config-options is re-authored on the
single-select wire.
This commit is contained in:
kingwl
2026-07-12 21:03:41 +08:00
parent 624d8d5df4
commit 95635dfa66
40 changed files with 1724 additions and 1062 deletions
+36 -1
View File
@@ -31,7 +31,7 @@ export interface AcpConfig {
Depends on: `Stream` (`@agentclientprotocol/sdk`)
Source: [`packages/ui/acp/src/index.ts:248`](../packages/ui/acp/src/index.ts)
Source: [`packages/ui/acp/src/index.ts:246`](../packages/ui/acp/src/index.ts)
## `@deepseek-ai/dsh-acp-agent`
@@ -408,6 +408,41 @@ export interface Config {
Source: [`packages/support/llm-replay/src/index.ts:429`](../packages/support/llm-replay/src/index.ts)
## `@deepseek-ai/dsh-permission`
Requires: `bash` · `approval`
```ts config-catalog
/** The {@link PermissionService} config: the deployment's preset table. */
export interface Config {
/**
* The preset table: name → knob bundle. Defaults to `request`
* (workspace-write + ask) and `yolo` (danger-full-access + never). The
* name `custom` is reserved for the derived not-a-preset state.
*/
presets?: Record<string, PresetSpec>
}
/**
* One preset's knob bundle — the sandbox mode and approval policy a session
* runs under while the preset is active — plus its presentation.
*/
export interface PresetSpec {
/** The `bash/sandbox-mode` value the preset writes through. */
sandbox: SandboxMode
/** The `approval/policy` value the preset writes through. */
approval: ApprovalPolicy
/** The display label a client shows for this preset; the raw table key when omitted. */
name?: string
/** One user-facing sentence on what the preset means; omitted when not configured. */
description?: string
}
```
Depends on: [`ApprovalPolicy`](core-data-structures/approval.md) · [`SandboxMode`](core-data-structures/sandbox.md)
Source: [`packages/ui/permission/src/index.ts:97`](../packages/ui/permission/src/index.ts)
## `@deepseek-ai/dsh-repeat-tool-guard`
```ts config-catalog