Skip to content

Docs

Components

AgentStatus

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

Example

Thinking
Workingread_file3.42s
Waiting for approvalrun_command
Done21.8s
Stopped6.24s
Rate limited by provider

Interactive, and the same on the components page. Try the keyboard below on it.

Installation

npm i signoff-ui ai
import { AgentStatus, deriveAgentState } from 'signoff-ui';
// Once per app (with Tailwind v4: @import 'signoff-ui/tailwind.css'; in your CSS)
import 'signoff-ui/styles.css';

The styles are once per app; Getting started has the Tailwind v4 and plain CSS options.

Usage

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

Generated from the types the package ships, so it matches the version you install.

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.

Props

staterequired
AgentState
'idle' | 'thinking' | 'working' | 'awaiting-approval' | 'done' | 'stopped' | 'error'
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
booleanDefault true

Announce state changes to assistive tech.

size
'sm' | 'md'Default 'md'
labels
SignoffLabelsInput

Words to use instead of the English defaults: see SignoffLabelsProvider.

Other props go to the root <div>: className (merged with tailwind-merge), id, aria-*, data-* and event handlers.

AgentState

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

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

Every color, radius and font is a CSS variable, so the overrides in Theming apply, on :root or scoped to any element. These hooks are read from the component's markup.

Slots (data-slot)
signoff-agent-status
State attributes
data-stateon signoff-agent-status'idle' | 'thinking' | 'working' | 'awaiting-approval' | 'done' | 'stopped' | 'error'
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
app/globals.css
/* Only this component, and only inside .settings-panel */
.settings-panel [data-slot='signoff-agent-status'] {
  --signoff-radius: 4px;
}

[data-slot='signoff-agent-status'][data-state='idle'] {
  outline: 1px solid var(--signoff-accent);
}