# AgentMessage

> A whole assistant message: reasoning, streaming markdown, tool calls, approvals and sources.

Source: https://agent-ui-kit-demo.vercel.app/docs/components/agent-message

## Installation

From npm:

```bash
npm i @dgesteves/agent-ui-kit ai
```

```tsx
import { AgentMessage } from '@dgesteves/agent-ui-kit';
import '@dgesteves/agent-ui-kit/styles.css'; // once per app, or @import '@dgesteves/agent-ui-kit/tailwind.css' with Tailwind v4
```

Or as source, with the shadcn CLI:

```bash
npx shadcn@latest add @agent-ui-kit/agent-message
```

```tsx
import { AgentMessage } from '@/components/agent-ui/agent-message';
```

## Usage

```tsx
const { messages, status, addToolApprovalResponse } = useChat();
const last = messages.findLast((m) => m.role === 'assistant');

{last && (
  <AgentMessage
    message={last}
    streaming={status === 'streaming'}
    tools={{ run_command: { label: 'Run command', risk: 'high' } }}
    onToolApproval={addToolApprovalResponse}
  />
)}
```

## API reference

### AgentMessage

Renders an assistant `UIMessage` part by part: streaming markdown, collapsible
reasoning, consecutive tool calls grouped into a timeline (with inline approval
cards), files, and the message's sources with linked inline citations.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `message` (required) | `Pick<UIMessage, 'id' \| 'role' \| 'parts'>` |  |  |
| `streaming` | `boolean` | `false` | The message is still being generated. Text parts with an explicit `state` take precedence. |
| `active` | `boolean` | `true` | Whether the run can still make progress, including while it waits on the user. Pass `false` once it has ended (stopped, failed, or an older message): tool calls that never settled read "Stopped" instead of running forever. See `ToolCallTimeline`'s `active`. |
| `tools` | `Record<string, ToolMeta>` |  |  |
| `renderTool` | `(part: ToolPart) => ReactNode \| undefined` |  | Render a tool part yourself. Return `undefined` to use the default timeline, or `null` to render nothing. Custom-rendered parts split the timeline. |
| `onToolApproval` | `(response: ToolApprovalResponse) => void \| PromiseLike<void>` |  | Enables inline approval cards. Pass `useChat().addToolApprovalResponse`. |
| `approvalProps` | `Partial<Omit<ApprovalCardProps, 'toolName' \| 'status' \| 'onApprove' \| 'onDeny'>>` |  | Props forwarded to every approval card, e.g. `{ autoFocus: true }`. |
| `renderData` | `(part: DataPart) => ReactNode` |  | Render `data-*` parts. They are skipped when omitted. |
| `showSources` | `boolean` | `true` |  |
| `sourcesVariant` | `'chips' \| 'cards'` | `'chips'` |  |
| `allowedImageHosts` | `readonly string[]` |  | Where images in text, reasoning and image file parts may load from: host names, `'self'` for relative URLs, or `'*'` for every image. Default: none, so other images render as links. File parts with `data:` and `blob:` URLs always preview. See `MarkdownProps.allowedImageHosts`. |
| `timings` | `ToolTimings` |  | Externally measured tool timings. Measured client-side when omitted. |

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

### ToolMeta

Per-tool presentation. Every field is optional.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` |  | Display label. Defaults to the humanized tool name. |
| `icon` | `ReactNode` |  |  |
| `summary` | `(input: unknown, part: ToolPart) => ReactNode` |  | One-line summary shown next to the label. Defaults to the most descriptive input field. Not called before any input has arrived; while the input streams it can be partial. |
| `renderOutput` | `(output: unknown, part: ToolPart) => ReactNode` |  | Replace the default JSON output view. |
| `risk` | `RiskLevel \| ((input: unknown) => RiskLevel)` |  | Risk shown on approval requests for this tool. |

### ToolApprovalResponse

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` (required) | `string` |  |  |
| `approved` (required) | `boolean` |  |  |
| `reason` | `string` |  |  |

## Accessibility

| Keys | Action |
| --- | --- |
| ↑ ↓ | Move between tool calls in a timeline |
| Y N | Approve or deny, with focus in an approval card |

- The message is an `<article>` with `aria-busy` while it streams, so screen readers wait for the text to settle.
- Each part keeps its own behavior: the tool timeline, approval cards, reasoning and sources below all apply.
- Images in model output don't load unless `allowedImageHosts` allows them; a blocked image is a link with its alt text, read as "Image:".
- Citation markers such as `[2]` become links named "Source 2" to the matching source.

## Theming

Slots (`data-slot`): `agent-message`.

- `data-role` on `agent-message`: the message’s role: assistant, user or system

Tokens it uses: `--aui-border`, `--aui-fg`, `--aui-fg-subtle`, `--aui-font-sans`, `--aui-ring`, `--aui-surface`, `--aui-surface-2`. See https://agent-ui-kit-demo.vercel.app/docs/getting-started#theming.
