# Restore dialog

Restore a checkpoint, honestly. The person chooses code, conversation or both (nothing is chosen for them, and Restore stays locked until they do), and the dialog says what a restore doesn't undo: the effects of shell commands the agent ran (listing them when the app knows), changes made outside the agent, and effects that left the workspace such as sent messages and deployments. `onRestore` restores and then checks; the dialog shows that check with RestoreResult, which never says "Restored" without a check that matched.

- Registry name: `restore-dialog` (`@candor/restore-dialog`)
- 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/restore-dialog

## Install

```bash
npx candor-ui add restore-dialog
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/restore-dialog`.
It brings in: button, checkpoint-list, dialog, locale, locked, radio-group, relative-time, status-dot, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { Button } from "@/components/ui/button"
import { RestoreDialog, RestoreResult, type RestoreOutcome, type RestoreTarget } from "@/components/restore-dialog"

const checkpoint = { label: "Added tests for burst traffic", createdAt: new Date(Date.now() - 9 * 60_000), captured: "both" as const }

// The app restores, then compares each file with the checkpoint's snapshot and reports what it found.
async function restore(target: RestoreTarget): Promise<RestoreOutcome> {
  await new Promise((r) => setTimeout(r, 1200))
  return {
    target,
    checkedAt: new Date(),
    checks: [
      { id: "1", kind: "file", label: "src/server/limiter.ts", status: "matches" },
      { id: "2", kind: "file", label: "src/server/routes.ts", status: "differs", detail: "A formatter on save re-wrapped 3 lines after the restore." },
      { id: "3", kind: "file", label: "package-lock.json", status: "not-checked" },
      ...(target !== "code" ? [{ id: "4", label: "Conversation", area: "conversation" as const, status: "matches" as const }] : []),
    ],
  }
}

const earlier: RestoreOutcome = {
  target: "code",
  checks: [
    { id: "a", kind: "file", label: "src/server/limiter.ts", status: "matches" },
    { id: "b", kind: "file", label: "src/server/limiter.test.ts", status: "matches" },
  ],
}

export default function Example() {
  return (
    <div className="flex max-w-xl flex-col items-start gap-4">
      <RestoreDialog
        checkpoint={checkpoint}
        onRestore={restore}
        commandsRun={["npm install ioredis@5", "npm run db:migrate", "rm -rf .cache/limiter"]}
        outsideChanges={["Priya Shah edited src/config/limits.ts"]}
        externalEffects={["Pushed branch rate-limiter to origin", "Deployed a preview build"]}
        trigger={<Button variant="outline">Restore this checkpoint…</Button>}
      />
      <RestoreResult className="w-full" result={earlier} />
    </div>
  )
}
```

## <RestoreDialog>

Props:

- `checkpoint: Pick<Checkpoint, "label" | "createdAt" | "captured">`: The checkpoint to restore. What it captured decides which choices are available.
- `onRestore: (target: RestoreTarget) => Promise<RestoreOutcome>`: Restore what the person chose, then check the result against the checkpoint and report it. A rejection is shown as an error.
- `commandsRun?: string[] | undefined`: Shell commands the agent ran after this checkpoint, as text. Left out, the dialog says it isn't recorded; [] says none ran.
- `outsideChanges?: string[] | undefined`: Changes made outside the agent since the checkpoint, when the app recorded them ("Ana edited README.md").
- `externalEffects?: string[] | undefined`: Effects that left the workspace since the checkpoint, when the app recorded them ("Deployed to preview").
- `locked?: Partial<Record<RestoreTarget, string | undefined>> | undefined`: Choices the app doesn't allow here, with why ("Only the session owner can rewind the conversation.").
- `open?: boolean | undefined`: Whether the dialog is open, when the app controls it.
- `onOpenChange?: ((open: boolean) => void) | undefined`: Called when the dialog opens or closes.
- `trigger?: React.ReactElement | undefined`: The control that opens the dialog, when the app doesn't control `open`.

## <RestoreResult>

What a restore actually did, as the app checked it. "Restored" shows only when every check the app reports matched the checkpoint. Items that weren't checked are shown as not checked, a mismatch is flagged with what differs, and a restore that didn't finish says what happened. Used inside RestoreDialog, or alone after a restore started elsewhere.

Props:

- `result: RestoreOutcome`: The app's report: what it checked after restoring, and any error.
- `className?: string | undefined`

## Strings (`restoreDialog`)

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

- `title`: Restore to “{label}”?
- `saved`: Checkpoint from {time}.
- `choose`: What to restore
- `both`: Code and conversation
- `bothHint`: Put the files back and rewind the chat to this point.
- `code`: Code only
- `codeHint`: Put the files back. The chat stays as it is.
- `conversation`: Conversation only
- `conversationHint`: Rewind the chat to this point. The files stay as they are now.
- `noFiles`: This checkpoint didn’t capture files.
- `noConversation`: This checkpoint didn’t capture the conversation.
- `notUndone`: What a restore doesn’t undo
- `shell`: Shell commands the agent ran
- `shellDefault`: Their effects stay: installed packages, generated or deleted files, database and service changes.
- `shellListed`: { one: "{count} command ran after this checkpoint:", other: "{count} commands ran after this checkpoint:" }
- `shellNone`: No commands ran after this checkpoint.
- `shellUnknown`: Which commands ran since then isn’t recorded.
- `outside`: Changes made outside the agent
- `outsideDefault`: Edits by people or other tools since this checkpoint aren’t tracked. A code restore may overwrite them where they touch the same files.
- `external`: Effects outside this workspace
- `externalDefault`: Sent messages, deployments, pushed commits and calls to other services can’t be taken back.
- `recorded`: Recorded since this checkpoint:
- `cancel`: Cancel
- `restore`: Restore
- `chooseFirst`: Choose what to restore first.
- `restoring`: Restoring and checking…
- `close`: Close
- `tryAgain`: Try again
- `noReason`: No reason was given.
- `result`: Restore result
- `matched`: Restored
- `matchedBody`: { one: "Checked: {count} item matches the checkpoint.", other: "Checked: all {count} items match the checkpoint." }
- `differs`: Doesn’t match the checkpoint
- `differsBody`: { one: "{count} item differs from the checkpoint after the restore.", other: "{count} items differ from the checkpoint after the restore." }
- `partial`: Partly checked
- `partialBody`: { one: "{matched} matched. {count} item wasn’t checked, so whether it was put back isn’t known.", other: "{matched} matched. {count} items weren’t checked, so whether they were put back isn’t known." }
- `areaNotChecked`: Nothing checked {area}, so whether it was put back isn’t known.
- `codeArea`: the code
- `conversationArea`: the conversation
- `unchecked`: Restore ran, but wasn’t checked
- `uncheckedBody`: Nothing was checked afterwards, so whether it matches the checkpoint isn’t known.
- `failed`: The restore didn’t finish
- `failedBody`: Some items may have changed before it stopped.
- `statusMatches`: Matches
- `statusDiffers`: Differs
- `statusNotChecked`: Not checked
- `noDetail`: The app didn’t say what differs.
- `checkedAt`: Checked {time}

## Types

### RestoreTarget

What a restore puts back: the code (files), the conversation, or both.

```ts
"both" | "code" | "conversation"
```

### RestoreCheckStatus

What the app found when it checked one file or area after the restore.

```ts
"matches" | "differs" | "not-checked"
```

### RestoreCheck

One file or area the app checked (or didn't) against the checkpoint after restoring.

```ts
{ id: string /** The file path ("src/server/limiter.ts") or area ("Conversation", "Lockfile"). */ label: string /** "file" shows the label as a path. Defaults to "area". */ kind?: "file" | "area" | undefined /** * Which part of the restore this check covers. A file is code unless you say otherwise. "Restored" needs a check * for every part restored: with "both", one covering the code and one cover
```

### RestoreOutcome

What the app reports after a restore: each check it made, and an error if the restore didn't finish.

```ts
{ /** What was restored. */ target: RestoreTarget /** What the app checked afterwards. Empty means nothing was checked, and the result says so. */ checks: RestoreCheck[] /** Set when the restore didn't finish: what happened, in plain words. */ error?: string | undefined /** When the checks were made. */ checkedAt?: Date | string | undefined }
```

## 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`.
