Permission Presets
The preset table
Section titled “The preset table”A preset is a table key mapping to one sandbox/approval bundle plus optional client presentation; the default table ships workspace-write (workspace-write + ask) and danger-full-access (danger-full-access + never).
/** One preset's sandbox/approval bundle and optional client presentation. */interface PresetSpec { /** The `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}/** The {@link PermissionPresetService} config: preset table and composition default. */interface Config { /** * The preset table: name → knob bundle. Defaults to `workspace-write` * (workspace-write + ask) and `danger-full-access` (danger-full-access + * never). The name `custom` is reserved for the derived not-a-preset state. */ presets?: Record<string, PresetSpec> /** * Default for new sessions. When omitted, the preset matching the composed * sandbox and approval defaults is used. */ defaultPreset?: string}The service requires a confining ctx.shell executor and ctx.approval, and misconfiguration fails at plugin load: a table entry named custom throws (the name is reserved for the derived not-a-preset state), and composing over a bash executor that does not confine (no sandboxMode capability fact) throws, because presets bundle a sandbox mode.
Current preset and the derived custom
Section titled “Current preset and the derived custom”current(events) derives the effective preset from the knobs, not from its own event alone: it folds the session’s effective sandbox mode (falling back to the executor’s configured mode) and effective approval policy (falling back to the approval service config, then ask), prefers a still-matching recorded selection, then the first matching table entry in declaration order, and otherwise returns CUSTOM_PRESET ('custom'). custom is derived-only: clients may display it as the current value, but it is never a switch target or an event payload.
names lists the switchable presets in table declaration order; optionOf(name) builds the option a client renders for a table key (label falls back to the key) or for custom, and throws for any other name.
/** The select-option shape a presentation layer advertises for one preset (or for the derived `custom` state). */interface PresetOption { /** Stable option value: the table key, or `custom`. */ value: string /** The display label. */ name: string /** One user-facing sentence on what the value means; omitted when not configured. */ description?: string}Switching and the permission/preset event
Section titled “Switching and the permission/preset event”set(session, name) resolves the preset (unknown names throw), appends a log-only permission/preset event unless name is already the effective preset, then writes each knob through its own setter — setSandboxMode from dsh-sandbox-policy and setApprovalPolicy from dsh-user-approval — only when that knob’s effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing at all.
permission/preset is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so current() can preserve WHICH preset the user chose when two presets share a bundle; effectivePermissionPreset(events) folds the last one, and replay needs no catch-up state. The complete event declaration is in the persistence log event catalog; the method signatures are in the generated service catalog.
Cordis API
Section titled “Cordis API”Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — this section is byte-identical in both language sides of the page. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.
ctx.permissionPresets — PermissionPresetService
Section titled “ctx.permissionPresets — PermissionPresetService”Owns the deployment’s permission presets and their write path. Requires a confining ctx.shell executor and ctx.approval; unmatched knob values are reported as CUSTOM_PRESET, not an error.
/** * Resolve the preset matching the effective knob values. A still-matching * last selection wins shared-bundle ties; otherwise the first table match * wins, or {@link CUSTOM_PRESET} when no entry matches. * @param events - the session's events in log order. * @returns the effective preset name, or `custom` when nothing matches. */current(events: readonly SessionEvent[]): string
/** * Build the whole select value for one folded knob state: every table * option in declaration order, `custom` appended exactly while derived. * @param state - the folded knob overrides. * @returns the `permissions` projection payload. */selectFor(state: KnobState): PermissionSelect
/** * Resolve a preset's knob bundle. * @param name - the preset name to resolve. * @returns the configured bundle. * @throws when `name` is not in the table. */resolve(name: string): PresetSpec
/** * Build the client option for a table entry or {@link CUSTOM_PRESET}. A * missing label falls back to the table key. * @param name - a table key, or `custom`. * @returns the option a client renders. * @throws when `name` is neither a table key nor `custom`. */optionOf(name: string): PresetOption
/** * Record a changed preset, then update each changed knob through its own * setter. Selecting the effective preset again appends nothing. * @param session - the session the switch belongs to. * @param name - the preset to switch to; unknown names throw. */set(session: Session, name: string): voidTypes: Session · SessionEvent
Source: packages/interaction/permission-presets/src/index.ts:159