# Conflicts: # docs/subsystems/agent-team.i18n.yaml # docs/subsystems/approval.i18n.yaml # docs/subsystems/client-modules.i18n.yaml # docs/subsystems/client-modules.md # docs/subsystems/client-modules.zh.md # docs/subsystems/code-runtime.i18n.yaml # docs/subsystems/commands.i18n.yaml # docs/subsystems/commands.md # docs/subsystems/commands.zh.md # docs/subsystems/compaction.i18n.yaml # docs/subsystems/compaction.md # docs/subsystems/compaction.zh.md # docs/subsystems/core.i18n.yaml # docs/subsystems/core.md # docs/subsystems/core.zh.md # docs/subsystems/credentials.i18n.yaml # docs/subsystems/credentials.md # docs/subsystems/credentials.zh.md # docs/subsystems/feedback.i18n.yaml # docs/subsystems/filesystem.i18n.yaml # docs/subsystems/goal.i18n.yaml # docs/subsystems/http-server.md # docs/subsystems/http-server.zh.md # docs/subsystems/invariants.i18n.yaml # docs/subsystems/invariants.md # docs/subsystems/invariants.zh.md # docs/subsystems/jobs.i18n.yaml # docs/subsystems/llm-streaming.i18n.yaml # docs/subsystems/llm-streaming.md # docs/subsystems/llm-streaming.zh.md # docs/subsystems/permission-presets.md # docs/subsystems/permission-presets.zh.md # docs/subsystems/persistence.i18n.yaml # docs/subsystems/persistence.md # docs/subsystems/persistence.zh.md # docs/subsystems/plan.i18n.yaml # docs/subsystems/plan.md # docs/subsystems/plan.zh.md # docs/subsystems/sandbox.i18n.yaml # docs/subsystems/sandbox.md # docs/subsystems/sandbox.zh.md # docs/subsystems/schedule.i18n.yaml # docs/subsystems/session-projection.i18n.yaml # docs/subsystems/session-projection.md # docs/subsystems/session-projection.zh.md # docs/subsystems/session-query.i18n.yaml # docs/subsystems/session-reference.i18n.yaml # docs/subsystems/session-reference.md # docs/subsystems/session-reference.zh.md # docs/subsystems/session-telemetry.md # docs/subsystems/session-telemetry.zh.md # docs/subsystems/session-title.i18n.yaml # docs/subsystems/session.i18n.yaml # docs/subsystems/session.md # docs/subsystems/session.zh.md # docs/subsystems/settings.i18n.yaml # docs/subsystems/settings.md # docs/subsystems/settings.zh.md # docs/subsystems/shell.i18n.yaml # docs/subsystems/shell.md # docs/subsystems/shell.zh.md # docs/subsystems/skills.i18n.yaml # docs/subsystems/skills.md # docs/subsystems/skills.zh.md # docs/subsystems/spill.i18n.yaml # docs/subsystems/storage.i18n.yaml # docs/subsystems/subagent.i18n.yaml # docs/subsystems/subagent.md # docs/subsystems/subagent.zh.md # docs/subsystems/subprocess.i18n.yaml # docs/subsystems/system-prompt.i18n.yaml # docs/subsystems/system-prompt.md # docs/subsystems/system-prompt.zh.md # docs/subsystems/tasks.md # docs/subsystems/tasks.zh.md # docs/subsystems/terminal.md # docs/subsystems/terminal.zh.md # docs/subsystems/token-meter.i18n.yaml # docs/subsystems/tools.i18n.yaml # docs/subsystems/tools.md # docs/subsystems/tools.zh.md # docs/subsystems/typert.i18n.yaml # docs/subsystems/typert.md # docs/subsystems/typert.zh.md # docs/subsystems/user-interaction.i18n.yaml # docs/subsystems/user-questions.i18n.yaml # docs/subsystems/user-questions.md # docs/subsystems/user-questions.zh.md # docs/subsystems/web.i18n.yaml # docs/subsystems/workflow.i18n.yaml # docs/subsystems/workflow.md # docs/subsystems/workflow.zh.md # docs/subsystems/workspace.i18n.yaml # docs/subsystems/workspace.md # docs/subsystems/workspace.zh.md # packages/typert/generator/tests/cordis-catalog.spec.ts
8.2 KiB
Permission Presets
English | 中文
The permission-preset layer of dsh-permission-presets (ctx.permissionPresets, PermissionPresetService) bundles the two independent enforcement knobs — sandbox mode (sandbox/mode) and approval policy (approval/policy) — into named presets a client offers as one Permissions selector. It is one optional capability, not part of the agent-loop spine, and it owns no enforcement: execution, prompt narration, and replay keep reading their knob folds, and a preset switch only records intent and writes through each knob's canonical setter. The package README owns composition status and limitations; the sandbox switching design owns the rationale.
Source: packages/interaction/permission-presets/src/index.ts
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 fresh sessions and eligible confirmed blank reuse. 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
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
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
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) — the language sides differ only in locale-specific paired document paths. 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
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
/**
* Advance one blank session after the host has confirmed it as the exact
* Web New Session reuse target. Only a still-effective
* default-origin selection advances; a started session, an explicit pick,
* legacy origin-less data, or independently changed knobs remain pinned.
* This is the permission-side half of the Web candidate selection and the
* host's blankness, membership, cwd, and archive verification.
* @param session - the live session selected for Workspace blank reuse.
*/
refreshDefaultForReuse(session: Session): void
/**
* 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): void
Types: Session · SessionEvent
Source: packages/interaction/permission-presets/src/index.ts