# Turn request

The agent asks the person to take the screen: for a sign-in, a one-time code, a check it must not pass itself. It says why, what to do, what the app says the agent won't do meanwhile, and whether the agent can still see the screen while the person has it, or that the app hasn't said. Take the screen, an optional quieter alternative, and Not now are offered side by side: none is focused, pre-selected or drawn as the default. After the person hands the screen back, the app passes the agent's `report` of what it saw, shown as the agent's words, not checked.

The app must stop the agent's input while the person has the screen: Candor sends no input and can't enforce `wontDo`, which is the app's commitment, drawn as the app gives it. It composes with AgentScreen through the app: on `onTake`, set AgentScreen's `control="person"`, and call its `onHandBack` as usual. `variant="strip"` draws it as a band to sit in AgentScreen's place or above it.

When the request changes (another one, or the report after hand-back) and focus was in it, focus goes to its heading; when focus is elsewhere, the report's arrival is announced.

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

## Install

```bash
npx candor-ui add turn-request
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/turn-request`.
It brings in: agent-avatar, bidi, button, chat, heading-level, heading-scope, locale, locked, tokens, use-announcer, use-focus-within, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import type { AgentIdentity } from "@/lib/candor/chat"
import { AgentScreen } from "@/components/candor/agent-screen"
import { TurnRequest } from "@/components/candor/turn-request"

const agent: AgentIdentity = { id: "a-web", name: "Browser agent", role: "dev" }

// A drawn stand-in for the screenshot your server sends: a sign-in page asking for a one-time code.
const frame =
  "data:image/svg+xml," +
  encodeURIComponent(
    `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 400"><rect width="640" height="400" fill="#f6f6f4"/><text x="200" y="130" font-family="sans-serif" font-size="22" fill="#333">Enter your one-time code</text><rect x="200" y="160" width="240" height="40" rx="4" fill="#fff" stroke="#ccc"/><rect x="200" y="220" width="120" height="36" rx="4" fill="#3b6fd8"/><text x="236" y="244" font-family="sans-serif" font-size="15" fill="#fff">Verify</text></svg>`
  )

export default function Example() {
  // "asking", then "person" while they have the screen, then "back" once they hand it back; or "link" when they asked
  // for a sign-in link instead, or "declined".
  const [phase, setPhase] = React.useState<"asking" | "declined" | "link" | "person" | "back">("asking")
  // While the person has the screen, or a link is on its way, the request stays where it is with its buttons locked
  // and saying why, so focus stays on the button pressed instead of falling to the page.
  const busy = phase === "person" ? "You have the screen. Hand it back when you're done." : phase === "link" ? "A sign-in link is on its way to your phone." : null

  return (
    <div className="flex max-w-3xl flex-col gap-4">
      {phase !== "declined" && (
        <TurnRequest
          agent={agent}
          why="The supplier's site asks for a one-time code sent to your phone."
          todo="Enter the code and press Verify, then hand the screen back."
          // Your app's commitment: it must stop the agent's input while you have the screen. Candor sends none.
          wontDo={["Type or click anything", "Keep a copy of what you type"]}
          seesScreen={false}
          onTake={() => setPhase("person")}
          takeLocked={busy}
          onNotNow={() => setPhase("declined")}
          notNowLocked={busy}
          // The quieter way keeps the code on the person's own device: the agent never handles it.
          alternative={{ label: "Send me a sign-in link instead", why: "You open the link and sign in on your own device; the agent waits.", onChoose: () => setPhase("link") }}
          alternativeLocked={busy}
          report={phase === "back" ? "The code was accepted and the supplier's order page is open." : undefined}
        />
      )}
      <AgentScreen
        agent={agent}
        connection="live"
        source={{ kind: "image", src: frame }}
        action={phase === "person" ? undefined : "Waiting at the one-time code page"}
        control={phase === "person" ? "person" : "agent"}
        controlStyle="strip"
        waiting={phase === "asking" ? "enter the one-time code" : undefined}
        onHandBack={() => setPhase("back")}
      />
    </div>
  )
}
```

## <TurnRequest>

Props:

- `agent: AgentIdentity`: The agent asking, named by the app.
- `why: string`: Why the agent asks, in the app's or the agent's words: "The supplier's site wants a one-time code." Shown as text.
- `todo: string`: What the person is asked to do, in the app's words: "Sign in, then hand the screen back." Shown as text.
- `wontDo?: readonly string[] | undefined`: What the agent won't do while the person has the screen, in the app's words: "Read what you type", "Click anything". The app's commitment: the app must hold to it, as Candor can't. Left out or empty, it says the app hasn't said.
- `seesScreen?: boolean | undefined`: Whether the agent can still see the screen while the person has it (and so sees what they type). Left out, it reads "The app hasn’t said whether {agent} can see the screen while you have it."
- `onTake?: (() => void) | undefined`: Shows Take the screen; called when it's pressed. Set AgentScreen's `control="person"` from here.
- `takeLocked?: string | null | undefined`: Why the person can't take the screen, if they can't. The button stays and says so. `null` or unset means they can.
- `onNotNow?: (() => void) | undefined`: Shows Not now; called when it's pressed.
- `notNowLocked?: string | null | undefined`: Why the person can't decline, if they can't. `null` or unset means they can.
- `alternative?: TurnRequestAlternative | undefined`: A quieter way, offered beside taking the screen, with why someone might choose it.
- `alternativeLocked?: string | null | undefined`: Why the alternative can't be chosen, if it can't. `null` or unset means it can.
- `report?: string | undefined`: After the person hands the screen back: the agent's account of what it saw, as text. With it, the request is over: the actions go and the report shows, named as the agent's and not checked.
- `variant?: "card" | "strip" | undefined` (default `"card"`): "card" (the default), or "strip": a denser band to sit in AgentScreen's place or above it.
- `className?: string | undefined`
- `headingLevel?: HeadingLevel | undefined`: The level of its title. Defaults to one below the nearest `HeadingScope`, else 2.

## Strings (`turnRequest`)

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

- `title`: {agent} asks you to take the screen
- `why`: Why
- `todo`: What to do
- `wontDo`: What the app says {agent} won’t do meanwhile
- `wontDoUnknown`: The app hasn’t said what {agent} won’t do while you have the screen.
- `seesScreen`: {agent} can still see the screen while you have it.
- `seesScreenNot`: {agent} can’t see the screen while you have it.
- `seesScreenUnknown`: The app hasn’t said whether {agent} can see the screen while you have it.
- `take`: Take the screen
- `notNow`: Not now
- `back`: The screen is back with {agent}
- `report`: What {agent} says it saw
- `reportNote`: {agent}’s own account, not checked.

## Types

### TurnRequestAlternative

A quieter way to do what the agent asks, offered beside taking the screen: its label, why, and what it does.

```ts
{
  /** The button's words: "Send me a sign-in link instead". */
  label: string;
  /** Why someone might choose it, in the app's words: "You sign in on your own device; the agent waits." */
  why: string;
  /** Called when the person chooses it. */
  onChoose: () => void;
}
```

## Types from other items

The props above take these. Each is defined by another item; import it from the path shown.

### AgentIdentity

Import: `import type { AgentIdentity } from "@/lib/candor/chat"` (item `chat`, https://candor.themethodic.com/docs/chat.md)

```ts
{
  id: string;
  /** The displayed name, e.g. "Architect agent". Never derived from model output. */
  name: string;
  role: AgentRole;
  specialty?: string | undefined;
}
```

### HeadingLevel

Import: `import type { HeadingLevel } from "@/lib/candor/heading-level"` (item `heading-level`, https://candor.themethodic.com/docs/heading-level.md)

A heading's level in the page's outline: `1` is `<h1>`, `6` is `<h6>`. Every Candor item that draws a heading takes its `headingLevel` as this type; the level is where the heading sits in the outline, never how large it looks.

```ts
1 | 2 | 3 | 4 | 5 | 6
```

### AgentRole

Import: `import type { AgentRole } from "@/lib/candor/chat"` (item `chat`, https://candor.themethodic.com/docs/chat.md)

What an agent does: "assistant", "coder", "reviewer"… The app registers each role's glyph and hue (AgentRolesProvider).

```ts
string
```

## 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`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
