# Permission mode

The agent's permission mode, with what each mode lets it do written out, and a way to change it. Modes that hand decisions away from the person (allow everything, a classifier) ask "Switch to …?" first. A mode your policy fixes is locked and says why. The mode shown is the one the agent last reported, never the one just picked: while the change is on its way the switcher says so, and a mode that wasn't reported reads "Not reported".

- Registry name: `permission-mode` (`@candor/permission-mode`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/permission-mode

## Install

```bash
npx candor-ui add permission-mode
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/permission-mode`.
It brings in: button, locale, locked, popover, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { PermissionMode } from "@/components/permission-mode"

export default function Example() {
  // The mode shown is the one the agent reports; here the "agent" reports the change after a moment.
  const [mode, setMode] = React.useState("default")
  const [pending, setPending] = React.useState(false)
  const change = (next: string) => {
    setPending(true)
    setTimeout(() => (setMode(next), setPending(false)), 600)
  }
  return (
    <div className="flex max-w-md flex-col gap-6">
      <PermissionMode mode={mode} pending={pending} onChange={change} modes={[{ id: "default" }, { id: "acceptEdits" }, { id: "plan" }, { id: "auto" }, { id: "bypassPermissions", locked: "Your workspace’s policy turns this off." }]} />
      <PermissionMode variant="compact" mode={mode} pending={pending} onChange={change} />
    </div>
  )
}
```

## <PermissionMode>

Props:

- `mode?: string | undefined`: The mode the agent last reported. Undefined while it hasn't said.
- `modes?: PermissionModeOption[] | undefined` (default `builtIn.map((id) => ({ id }))`): The modes to offer, in order. Defaults to the six SDK modes.
- `onChange?: ((mode: string) => void) | undefined`: Asks for a change. Show the new mode once the agent reports it (`mode`).
- `pending?: boolean | undefined` (default `false`): A change is on its way.
- `error?: string | undefined`: Why the last change failed, if it did. Pass "" for no reason.
- `locked?: string | undefined`: Why the mode can't be changed at all here: a policy, a role.
- `variant?: "list" | "compact" | undefined` (default `"list"`): "list" shows every mode with its consequences; "compact" is a button that opens the list, for a toolbar.
- `className?: string | undefined`

## Strings (`permissionMode`)

The fixed words, English by default. Override any of them under `permissionMode` in `<LocaleProvider strings={{ permissionMode: { … } }}>`; the table is exported as `permissionModeStrings`.

- `label`: Permission mode
- `current`: Permission mode: {mode}
- `default`: Ask before acting
- `defaultDetail`: The agent asks before anything a rule doesn’t already allow.
- `acceptEdits`: Accept file edits
- `acceptEditsDetail`: File edits in the project and simple file commands run without asking. Anything else still asks.
- `plan`: Plan only
- `planDetail`: The agent reads and plans. It changes nothing until you approve the plan.
- `auto`: Automatic
- `autoDetail`: A classifier allows or blocks each action instead of you. It can be wrong, and you’re not asked.
- `dontAsk`: Don’t ask
- `dontAskDetail`: Nothing asks you: what a rule doesn’t already allow is denied.
- `bypassPermissions`: Allow everything
- `bypassPermissionsDetail`: Nothing is checked. The agent can run any command and change or delete any file it can reach.
- `confirmTitle`: Switch to “{mode}”?
- `confirm`: Switch
- `cancel`: Keep “{mode}”
- `changing`: Changing…
- `failed`: The mode didn’t change: {reason}
- `failedNoReason`: The mode didn’t change.
- `unknown`: Not reported

## Types

### PermissionModeOption

A mode the switcher offers. The six SDK modes have built-in words; your own need `label` and `detail`.

```ts
{ /** The mode's name as the agent knows it: "default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions". */ id: string label?: string | undefined /** What the agent may do in this mode, in a sentence. */ detail?: string | undefined /** Why this mode can't be chosen here, e.g. "Your organisation's policy turns this off." */ locked?: string | undefined /** Ask "Switch to …?" before cha
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
