# AgentStatus

> The run’s state in one pill, announced to screen readers.

Source: https://signoff-ui.vercel.app/docs/components/agent-status

## Installation

From npm:

```bash
npm i signoff-ui ai
```

```tsx
import { AgentStatus, deriveAgentState } from 'signoff-ui';
import 'signoff-ui/styles.css'; // once per app, or @import 'signoff-ui/tailwind.css' with Tailwind v4
```

Or as source, with the shadcn CLI:

```bash
npx shadcn@latest add https://signoff-ui.vercel.app/r/agent-status.json
```

```tsx
import { AgentStatus } from '@/components/signoff-ui/agent-status';
import { deriveAgentState } from '@/components/signoff-ui/lib/ai';
```

## Usage

```tsx
const last = messages.findLast((m) => m.role === 'assistant');
// Waiting on a person (an approval, or a client-side tool in pendingClientTools) wins over "working".
// A run that ended with text still streaming or a call unfinished, as stop() leaves it, is "stopped";
// a client-side tool the app runs itself (onToolCall), named in clientTools, reads "working" instead.
const { state, detail } = deriveAgentState({
  status,
  message: last,
  pendingClientTools: ['review_changes'],
  clientTools: ['get_location'],
});

<AgentStatus state={state} detail={detail} />
```

## How it works

Thinking, working, waiting for approval, done, stopped or error, with an elapsed timer. Changes are announced through a live region: debounced so quick flips are not read out one by one, and assertive only for approvals and errors.

`deriveAgentState` maps `useChat`'s status and the latest message to a state, with human-in-the-loop waits taking precedence. A run that ended with work unfinished, which is what `stop()` leaves behind (text or reasoning still streaming, a tool call still preparing or running), is `stopped` rather than `done`.

Client-side tools change that. Name the ones that wait on a person, such as a review, in `pendingClientTools`: they read `awaiting-approval`. Name the ones the app runs itself (`onToolCall`, then `addToolOutput`) in `clientTools`: while one runs, `useChat` is `ready`, and the run reads `working` with the tool as its detail.

## API reference

### AgentStatus

A compact, live status pill for an agent run. State changes are announced
through a polite live region (assertive for errors and approval requests),
debounced so rapid transitions do not flood screen readers.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `state` (required) | `AgentState` ('awaiting-approval' \| 'error' \| 'idle' \| 'thinking' \| 'working' \| 'done' \| 'stopped') |  |  |
| `label` | `string` |  | Overrides the state's name (`labels.agentStatus.states`). |
| `detail` | `string` |  | Secondary detail, e.g. the tool being run. |
| `startedAt` | `number` |  | Epoch ms when the run started. Shows a live elapsed timer while active. |
| `elapsedMs` | `number` |  | Fixed elapsed time to show (e.g. the final run duration). Takes precedence over `startedAt`. |
| `announce` | `boolean` | `true` | Announce state changes to assistive tech. |
| `size` | `'sm' \| 'md'` | `'md'` |  |
| `labels` | `SignoffLabelsInput` |  | Words to use instead of the English defaults: see `SignoffLabelsProvider`. |

Other props go to the root `<div>`: `className`, `id`, `aria-*`, `data-*` and event handlers.

### AgentState

```ts
type AgentState = 'awaiting-approval' | 'error' | 'idle' | 'thinking' | 'working' | 'done' | 'stopped';
```

## Accessibility

- Changes are announced through a live region, debounced by 350 ms so quick flips aren’t read one by one. Approvals and errors are assertive; everything else is polite.
- Render one `AgentStatus` per run with `announce` on; pass `announce={false}` to any copy, or the state is read twice.
- Each state has an icon and a word. The thinking shimmer and the spinner run only without `prefers-reduced-motion`.

## Theming

Slots (`data-slot`): `signoff-agent-status`.

- `data-state` on `signoff-agent-status`: 'awaiting-approval' | 'error' | 'idle' | 'thinking' | 'working' | 'done' | 'stopped'

Tokens it uses: `--signoff-accent`, `--signoff-accent-fg`, `--signoff-border`, `--signoff-fg`, `--signoff-fg-muted`, `--signoff-fg-subtle`, `--signoff-font-mono`, `--signoff-font-sans`, `--signoff-hot`, `--signoff-hot-fg`, `--signoff-surface`. See https://signoff-ui.vercel.app/docs/getting-started#theming.
