# API index

> Every export of each entry point, read from the published types: a link to the component page that documents it, or its signature and description.

Source: https://signoff-ui.vercel.app/docs/api

Every export of signoff-ui, by entry point, read from the package's published types when the site builds, so the page lists exactly what the package exports.

The components, the hooks and the types they take each have a component page, with props in full, defaults, examples, keyboard and accessibility, and each entry point links them there. Every other export is here, with its declaration and its description as your editor shows them. Import an export from the entry point it is listed under. The main entry, `signoff-ui`, also exports everything in `signoff-ui/core`.

## signoff-ui

The components and hooks. They are client modules, each with its own `'use client'` directive. For the pure helpers in server code, import from `signoff-ui/core`.

It also exports everything in [`signoff-ui/core`](https://signoff-ui.vercel.app/docs/api#signoff-uicore).

On the component pages: [`AgentMessage`](https://signoff-ui.vercel.app/docs/components/agent-message#agentmessage), [`AgentStatus`](https://signoff-ui.vercel.app/docs/components/agent-status#agentstatus), [`ApprovalCard`](https://signoff-ui.vercel.app/docs/components/approval-card#approvalcard), [`DiffReview`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreview), [`Markdown`](https://signoff-ui.vercel.app/docs/components/markdown#markdown), [`MarkdownRendererProvider`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrendererprovider), [`Reasoning`](https://signoff-ui.vercel.app/docs/components/reasoning#reasoning), [`RunMeter`](https://signoff-ui.vercel.app/docs/components/run-meter#runmeter), [`Sources`](https://signoff-ui.vercel.app/docs/components/sources#sources), [`ToolApprovalBatch`](https://signoff-ui.vercel.app/docs/components/approval-card#toolapprovalbatch), [`ToolApprovalCard`](https://signoff-ui.vercel.app/docs/components/approval-card#toolapprovalcard), [`ToolCallTimeline`](https://signoff-ui.vercel.app/docs/components/tool-call-timeline#toolcalltimeline), [`useApprovalPolicy`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#useapprovalpolicy), [`useDiffReview`](https://signoff-ui.vercel.app/docs/components/use-diff-review#usediffreview), [`useRunTiming`](https://signoff-ui.vercel.app/docs/components/run-meter#useruntiming), [`AgentMessageProps`](https://signoff-ui.vercel.app/docs/components/agent-message#agentmessage), [`AgentStatusProps`](https://signoff-ui.vercel.app/docs/components/agent-status#agentstatus), [`ApprovalAuditEvent`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#approvalauditevent), [`ApprovalCardProps`](https://signoff-ui.vercel.app/docs/components/approval-card#approvalcard), [`ApprovalChoice`](https://signoff-ui.vercel.app/docs/components/approval-card#approvalchoice), [`ApprovalOutcome`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#approvaloutcome), [`ApprovalPolicy`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#useapprovalpolicy), [`ApprovalRuleStorage`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#approvalrulestorage), [`DiffReviewDraft`](https://signoff-ui.vercel.app/docs/components/use-diff-review#diffreviewdraft), [`DiffReviewFileState`](https://signoff-ui.vercel.app/docs/components/use-diff-review#diffreviewfilestate), [`DiffReviewProps`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreview), [`DiffReviewSelection`](https://signoff-ui.vercel.app/docs/components/use-diff-review#diffreviewselection), [`DiffViewMode`](https://signoff-ui.vercel.app/docs/components/diff-review#diffviewmode), [`MarkdownProps`](https://signoff-ui.vercel.app/docs/components/markdown#markdown), [`MarkdownRenderer`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrenderer), [`MarkdownRendererProps`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrendererprops), [`MarkdownRendererProviderProps`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrendererprovider), [`ReasoningProps`](https://signoff-ui.vercel.app/docs/components/reasoning#reasoning), [`RunMeterProps`](https://signoff-ui.vercel.app/docs/components/run-meter#runmeter), [`RunTiming`](https://signoff-ui.vercel.app/docs/components/run-meter#useruntiming), [`SourcesProps`](https://signoff-ui.vercel.app/docs/components/sources#sources), [`ToolApprovalBatchProps`](https://signoff-ui.vercel.app/docs/components/approval-card#toolapprovalbatch), [`ToolApprovalCardProps`](https://signoff-ui.vercel.app/docs/components/approval-card#toolapprovalcard), [`ToolApprovalResponse`](https://signoff-ui.vercel.app/docs/components/approval-card#toolapprovalresponse), [`ToolCallTimelineProps`](https://signoff-ui.vercel.app/docs/components/tool-call-timeline#toolcalltimeline), [`ToolMeta`](https://signoff-ui.vercel.app/docs/components/agent-message#toolmeta), [`UseApprovalPolicyOptions`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#useapprovalpolicy), [`UseDiffReviewOptions`](https://signoff-ui.vercel.app/docs/components/use-diff-review#usediffreview), [`UseDiffReviewResult`](https://signoff-ui.vercel.app/docs/components/use-diff-review#usediffreview).

### AgentStatusDot

```ts
function AgentStatusDot(props: {
    state: AgentState;
    className?: string;
}): ReactNode;
```

### JsonView

`function JsonView(props: JsonViewProps): JSX.Element;`

Read-only JSON with light syntax tinting, a copy button and long-output folding.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs).

### SignoffLabelsProvider

```ts
function SignoffLabelsProvider(props: {
    labels: SignoffLabelsInput;
    children: ReactNode;
}): JSX.Element;
```

Labels for every component inside: a translation of `defaultLabels`, whole or in part. Providers
nest, the inner one winning key by key. Labels hold functions, so render the provider from a
Client Component (a module with 'use client') and keep the object stable: at module scope, or memoized.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Labels](https://signoff-ui.vercel.app/docs/labels).

### ToolCallDetails

```ts
function ToolCallDetails(props: {
    part: ToolPart;
    meta?: ToolMeta | undefined;
    labels?: SignoffLabelsInput | undefined;
}): JSX.Element;
```

The expanded body of a tool call: input, output or error, and approval outcome.

### useActivityWindow

`function useActivityWindow(active: boolean): ActivityWindow;`

Records when `active` last turned on and off. Restarts when it turns on again.

### useHydrated

`function useHydrated(): boolean;`

False during SSR and hydration, true afterwards. Clock-derived values (live
durations, waterfall widths) render only once hydrated so server and client markup match.

### usePrefersReducedMotion

`function usePrefersReducedMotion(): boolean;`

Tracks the user's `prefers-reduced-motion` setting.

### useSignoffLabels

`function useSignoffLabels(overrides?: SignoffLabelsInput): SignoffLabels;`

Every label in effect here: the English, the providers' over it, and `overrides` over both.

In the docs: [Labels](https://signoff-ui.vercel.app/docs/labels).

### useToolTimings

`function useToolTimings(parts: readonly AnyUIPart[]): ToolTimings;`

Measures tool-call timings client-side by watching AI SDK state transitions, a sub-agent's calls
included. Calls that are already settled when first observed (e.g. restored history)
get no timing rather than a misleading zero.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs).

### approvalRequestOf

`function approvalRequestOf(part: ToolPart): ApprovalRequest | undefined;`

The request a tool part makes, for a policy.

### memoryRuleStorage

`function memoryRuleStorage(initial?: readonly ApprovalRule[]): ApprovalRuleStorage;`

Rules kept in memory, for this page only: the default.

### webStorageRules

`function webStorageRules(key?: string, storage?: () => Storage | undefined): ApprovalRuleStorage;`

Rules kept in `localStorage` (or another `Storage`) as JSON under `key`, and the ids of default
rules the person removed under `<key>:removed-defaults`. A rule with a `when` is not kept: JSON
can't hold the code. Nothing is read or written on the server.

In the docs: [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### DiffWorkerFactory

`type DiffWorkerFactory = () => Worker;`

Starts a worker that answers `DiffReview`'s diff requests: see lib/diff-worker.ts.

### JsonViewProps

```ts
interface JsonViewProps {
    value: unknown;
    label: string;
    collapseAfter?: number;
    className?: string;
    labels?: SignoffLabelsInput | undefined;
}
```

### RunTimingMessage

```ts
interface RunTimingMessage {
    id: string;
    role: string;
}
```

What `useRunTiming` reads of a message. AI SDK `UIMessage`s and `useAgUiAgent`'s messages fit.

### ShownContext

```ts
interface ShownContext {
    start: number;
    end: number;
}
```

Unchanged lines shown in a gap: from its start (after the hunk above) and from its end.

## signoff-ui/core

The pure helpers, with no React and no `'use client'`, so Server Components, Route Handlers and plain Node can call them: approval rules, the review's result and what goes back to the agent, tool and source parts, usage and cost, untrusted text and URLs, and the labels.

On the component pages: [`AgentState`](https://signoff-ui.vercel.app/docs/components/agent-status#agentstate), [`ApprovalDecision`](https://signoff-ui.vercel.app/docs/components/approval-card#approvaldecision), [`ApprovalRequest`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#approvalrequest), [`ApprovalRule`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#approvalrule), [`ApprovalRuleLimits`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#approvalrulelimits), [`ApprovalStatus`](https://signoff-ui.vercel.app/docs/components/approval-card#approvalstatus), [`DiffReviewComment`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreviewcomment), [`DiffReviewFileResult`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreviewfileresult), [`DiffReviewRejectedHunk`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreviewrejectedhunk), [`DiffReviewResult`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreviewresult), [`DiffReviewResultComment`](https://signoff-ui.vercel.app/docs/components/diff-review#diffreviewresultcomment), [`FileChange`](https://signoff-ui.vercel.app/docs/components/diff-review#filechange), [`FileDecision`](https://signoff-ui.vercel.app/docs/components/diff-review#filedecision), [`HunkDecision`](https://signoff-ui.vercel.app/docs/components/diff-review#hunkdecision), [`ModelPricing`](https://signoff-ui.vercel.app/docs/components/run-meter#modelpricing), [`ReviewItem`](https://signoff-ui.vercel.app/docs/components/use-diff-review#reviewitem), [`RiskLevel`](https://signoff-ui.vercel.app/docs/components/approval-card#risklevel), [`RunUsage`](https://signoff-ui.vercel.app/docs/components/run-meter#runusage), [`SourceItem`](https://signoff-ui.vercel.app/docs/components/sources#sourceitem), [`SubagentData`](https://signoff-ui.vercel.app/docs/components/tool-call-timeline#subagentdata), [`SubagentPart`](https://signoff-ui.vercel.app/docs/components/tool-call-timeline#subagentpart), [`SubagentStatus`](https://signoff-ui.vercel.app/docs/components/tool-call-timeline#subagentstatus), [`ToolTiming`](https://signoff-ui.vercel.app/docs/components/tool-call-timeline#tooltiming), [`ToToolApprovalOptions`](https://signoff-ui.vercel.app/docs/components/use-approval-policy#totoolapprovaloptions).

### addUsage

`function addUsage(a: RunUsage | undefined, b: RunUsage | undefined): LanguageModelUsage;`

Add two usages field by field. A run that continues after an approval or a
client-side tool is a new request with its own `totalUsage`, and `useChat`
replaces `metadata.usage` rather than adding to it, so sum on the server:
`addUsage(lastMessage.metadata?.usage, part.totalUsage)`.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Performance](https://signoff-ui.vercel.app/docs/limits), [RunMeter](https://signoff-ui.vercel.app/docs/components/run-meter).

### applyHunks

`function applyHunks(file: Pick<ParsedFileDiff, 'hunks' | 'oldContent' | 'newContent'>, accepted: ReadonlySet<string> | readonly string[]): string;`

Apply only the accepted hunks to the original contents.
Accepting every hunk yields `newContent`; rejecting every hunk yields `oldContent`.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Performance](https://signoff-ui.vercel.app/docs/limits), [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review).

### cn

`function cn(...inputs: ClassValue[]): string;`

Merge class names, resolving Tailwind utility conflicts (last one wins).

### computeReviewResult

```ts
function computeReviewResult(files: readonly ParsedFileDiff[], decisions: Readonly<Record<string, HunkDecision>>, options?: {
    comments?: readonly DiffReviewComment[];
    viewed?: Readonly<Record<string, boolean>>;
}): DiffReviewResult;
```

Compute the review result for a set of parsed files, decisions, comments and viewed files.

In the docs: [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review).

### contextGaps

`function contextGaps(file: ParsedFileDiff): ContextGap[];`

The unchanged lines around a file's hunks. Only with both contents: a patch alone has none.

### decisionEffect

```ts
function decisionEffect(decision: ApprovalDecision): {
    approved: boolean;
    scope: 'once' | 'session' | 'always';
};
```

What a decision does now, and the rule it leaves, if any.

### decisionsFromAcpOptions

`function decisionsFromAcpOptions(options: readonly AcpPermissionOption[]): ApprovalDecision[];`

The decisions to offer for an ACP request: one per kind of option the agent sent, plus
`allow-session`, which the client keeps as a rule of its own (ACP has no session option). A kind
the agent sent several options of is left out, since a button can't say which one it picks:
Claude Code's ExitPlanMode sends up to three `allow_always` options, one of them clearing the
context and switching to auto mode, so it gets Approve and Deny but no Always. Deny stays
whenever the agent sent a `reject_once`, and answers its first one.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp), [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### decisionsFromToolOutput

`function decisionsFromToolOutput(output: unknown): Record<string, HunkDecision>;`

The decisions a review's tool output records, keyed as `DiffReview`'s `decisions`: to show a
review again from its saved result, read-only, with the same `files`. Anything that is not such
an output, or one made before it recorded decisions, gives none.

In the docs: [CopilotKit](https://signoff-ui.vercel.app/docs/copilotkit), [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review).

### deriveAgentState

```ts
function deriveAgentState(options: {
    status: ChatStatus;
    message?: Pick<UIMessage, 'role' | 'parts'> | undefined;
    pendingClientTools?: readonly string[];
    clientTools?: readonly string[];
    writingResponse?: string;
}): DerivedAgentState;
```

Map `useChat` status plus the latest assistant message to a single agent state.
Human-in-the-loop waits (approvals, client-side tools awaiting output) take
precedence over transport status, because the run cannot progress without the user.

A run that ended (`ready`) with work still unfinished in the message is `stopped`, not `done`:
text or reasoning still streaming, a tool call still preparing, running or partial, or a
sub-agent still running. That is what `stop()` leaves behind, and what an AG-UI run cancelled or
aborted mid-way does. A client-side tool the app runs itself is the exception: name it in
`clientTools`.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [AG-UI](https://signoff-ui.vercel.app/docs/ag-ui), [Labels](https://signoff-ui.vercel.app/docs/labels).

### describeRule

`function describeRule(rule: Pick<ApprovalRule, 'tool' | 'args'>): string;`

A rule in words: "run_command with command npm test*".

In the docs: [Labels](https://signoff-ui.vercel.app/docs/labels), [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### estimateCost

`function estimateCost(usage: RunUsage | undefined, pricing: ModelPricing): CostBreakdown;`

Estimate the cost of a run from AI SDK usage and per-million-token pricing.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Performance](https://signoff-ui.vercel.app/docs/limits).

### evaluateRules

`function evaluateRules(rules: readonly ApprovalRule[], toolName: string, input: unknown): RuleMatch | undefined;`

The rule that decides a call, if any. A deny rule wins over an allow rule, whatever their
order; among rules of the same effect the most specific is the one reported. Each rule's matching
takes a bounded amount of work, in proportion to the arguments it reads, an array's item counting
as one character at least: far more than common patterns need, however long the arguments. The
bound is each rule's, so a call's work grows with the number of rules times the arguments' length,
and a deny rule reads a string up to twice (whole, then by command) and an array up to four times
(all its items in one pass, each once however often it comes, then the items joined, each both
ways): cap the arguments where a client sends rules. A rule that runs out before it can tell
counts against the call: an allow rule allows nothing, and a deny rule denies, reported as
`unchecked` when no other deny rule covers the call.

### formatCost

`function formatCost(usd: number | undefined | null): string;`

### formatDuration

`function formatDuration(ms: number | undefined | null): string;`

Formatting helpers shared by the components. All output is locale-stable.

### formatTokens

`function formatTokens(n: number | undefined | null): string;`

### fromAcpPermissionRequest

```ts
function fromAcpPermissionRequest(request: AcpRequestPermissionRequest): ApprovalRequest & {
    title?: string;
};
```

An ACP permission request as the card's request: the tool call's id, name, and raw input.

In the docs: [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### fromAcpPermissionResponse

`function fromAcpPermissionResponse(response: AcpRequestPermissionResponse, options: readonly AcpPermissionOption[]): ApprovalDecision | 'cancelled';`

An ACP response as the decision it records, or `cancelled`.

### getAllToolParts

`function getAllToolParts(parts: readonly AnyUIPart[]): ToolPart[];`

Every tool call in `parts`, with those of its sub-agents, at any depth.

In the docs: [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### getApprovalStatus

`function getApprovalStatus(part: ToolPart): ApprovalStatus | undefined;`

The approval state of a tool part, or `undefined` for parts outside the approval flow.

### getSourceParts

`function getSourceParts(parts: readonly AnyUIPart[]): SourcePart[];`

Collect sources, de-duplicated by URL (or source id for documents).

### getToolPartName

`function getToolPartName(part: ToolPart): string;`

Same semantics as the SDK's `getToolName`, without the runtime import.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui), [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### getToolParts

`function getToolParts(parts: readonly AnyUIPart[]): ToolPart[];`

### getToolPhase

```ts
function getToolPhase(part: Pick<ToolPart, 'state'> & {
    approval?: {
        approved?: boolean;
        isAutomatic?: boolean;
    } | undefined;
    preliminary?: boolean | undefined;
}): ToolPhase;
```

### globToRegExp

`function globToRegExp(pattern: string): RegExp;`

A glob as a regular expression over the whole value:
- `*` matches any run of characters but shell control characters (`;`, `&`, `|`, `` ` ``, `<`,
  `>`, line breaks) and `$(`;
- `?` matches one such character;
- `**` matches any run of characters at all;
- `\` makes the next character literal; everything else is literal.

The expression backtracks: with a few `*` and a long value it doesn't match, a test can take
seconds. `matchesGlob` gives the same answers without backtracking.

### humanizeToolName

`function humanizeToolName(name: string): string;`

`read_file` / `readFile` / `read-file` → "Read file".

In the docs: [Getting started](https://signoff-ui.vercel.app/docs/getting-started).

### inferLanguage

`function inferLanguage(path: string): string;`

### isSourcePart

```ts
function isSourcePart(part: {
    type: string;
}): part is SourcePart;
```

### isSubagentPart

```ts
function isSubagentPart(part: {
    type: string;
}): part is SubagentPart;
```

### isToolPart

```ts
function isToolPart(part: {
    type: string;
}): part is ToolPart;
```

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui).

### matchesGlob

`function matchesGlob(value: string, pattern: string): boolean;`

Whether a whole value matches a glob (see `globToRegExp`). It reads the value once, keeping the
places in the pattern that match so far, so its time grows with the value's length times the
pattern's, and no more: a long argument written by a model can't hold up the server or the page.

### mergeLabels

`function mergeLabels<T extends SignoffLabelsInput>(base: T, ...overrides: (SignoffLabelsInput | undefined)[]): T;`

Labels with some replaced: plain objects merge key by key, strings and functions replace.

In the docs: [Labels](https://signoff-ui.vercel.app/docs/labels).

### observeToolTimings

`function observeToolTimings(prev: ToolTimings, tools: readonly ToolPart[], now: number): ToolTimings;`

Pure reducer behind `useToolTimings`, exported for tests and custom stores.

### parseApprovalRules

`function parseApprovalRules(value: unknown, options?: ApprovalRuleLimits): ApprovalRule[];`

The rules a client sent, such as a request body's `rules`, checked on the server: each field of
the right type and within `limits`, other fields dropped. A rule that fails a check is left out,
and so is everything when `value` is not an array. A deny rule left out takes with it the allow
rules it could overrule: those on a tool it names, or all of them when its tool is missing, not
a string or too long, when it comes after the first `maxRules`, or when the deny rules left out
name more than 10 tools. A rule left out still holds on the page, which answers the call when the
server asks.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Performance](https://signoff-ui.vercel.app/docs/limits), [Comparison](https://signoff-ui.vercel.app/docs/comparison).

### parseFileChange

`function parseFileChange(change: FileChange, options?: ParseFileChangeOptions): ParsedFileDiff;`

Turn a `FileChange` into hunks with line numbers and word-level segments. Contents are diffed;
past `maxEditLength` the changed region is one replacing hunk (`fallback: 'replace'`).

In the docs: [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Performance](https://signoff-ui.vercel.app/docs/limits), [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review).

### revealText

`function revealText(text: string, options?: RevealTextOptions): RevealedPart[];`

The text in parts, with marks in place of what would make it read as something it is not:
characters that do not show, such as a bidi override (`U+202E`), which shows the text after it
reversed, or a zero-width space, and with `blanks`, long runs of spaces or of blank lines. Text
with none of them is one part, the text itself. The joiners and variation selectors in an emoji
are left as they are.

In the docs: [Performance](https://signoff-ui.vercel.app/docs/limits).

### reviewItems

`function reviewItems(file: ParsedFileDiff): ReviewItem[];`

What a file asks to be decided: its hunks, or the file itself when it has none but changed.

### reviewResumeEntry

`function reviewResumeEntry(interruptId: string, result: DiffReviewResult, options?: DiffReviewToolOutputOptions): DiffReviewResumeEntry;`

The review as the answer to an AG-UI interrupt: pass it to `useAgUiAgent`'s `resolve`, or put it
in `RunAgentInput.resume` yourself. The payload is `reviewToolOutput(result, options)`.

In the docs: [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review).

### reviewToolOutput

`function reviewToolOutput(result: DiffReviewResult, options?: DiffReviewToolOutputOptions): DiffReviewToolOutput;`

A review result shaped for the agent: a one-line summary, each file's decision (with its contents
by default, and `viewed: true` when the reviewer marked it viewed), the rejected hunks with their
lines, every comment with its lines, and each decided hunk by id, so the review can be shown
again from it (`decisionsFromToolOutput`). JSON, so it is an AI SDK tool output as it is:
`addToolOutput({ tool, toolCallId, output: reviewToolOutput(result) })`.
The summary is written in English for the model, whatever language the UI is in.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui), [CopilotKit](https://signoff-ui.vercel.app/docs/copilotkit).

### riskFromMcpAnnotations

`function riskFromMcpAnnotations(annotations: unknown): RiskLevel | undefined;`

A risk level from an MCP tool's annotations, for `ToolMeta.risk` or the card's `risk`. AI SDK 7's
MCP client puts them on each tool's `metadata.annotations`, and on its calls' parts as
`toolMetadata.annotations`. The first that applies: `'high'` for a tool its server marks
destructive (`destructiveHint: true`), `'low'` for one it marks read-only (`readOnlyHint: true`),
and `'medium'` for one it marks as changing things (`readOnlyHint: false`) or as reaching outside
systems, such as the web (`openWorldHint: true`).

Only hints the server set count. The MCP spec reads a missing `destructiveHint` or
`openWorldHint` as `true`, which would rate every tool with no annotations as risky: a tool with
none of these hints gets `undefined`, and its card shows no risk. A value other than `true` or
`false` counts as missing.

A hint, not a guarantee: a server describes its own tools, and one you don't trust can mark a
tool that deletes as read-only. Show it to a person; don't let it decide an approval.

In the docs: [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### ruleFromDecision

```ts
function ruleFromDecision(decision: ApprovalDecision, request: Pick<ApprovalRequest, 'toolName'>, options?: {
    args?: Record<string, string> | undefined;
    reason?: string;
    id?: string;
    now?: number;
}): ApprovalRule | undefined;
```

The rule a decision leaves: none for `allow-once` and `deny-once`. `args` narrows it to some
arguments; without it, it covers every call of the tool.

### ruleMatches

`function ruleMatches(rule: ApprovalRule, toolName: string, input: unknown): boolean;`

Whether a rule applies to a call: its tool, every argument pattern, and its own test. A deny rule
reads more than an allow rule, to hold when a command is written another way: each command of a
shell line, from its first word past `NAME=value` settings, a path and a redirect, or after `sudo`,
`env`, `sh -c`, `find -exec`… (see `commands`); an array by its items; and an object, or a `when`
that throws, as covered. Matching takes a bounded amount of work (see `evaluateRules`), and what
it can't finish counts against the call: an allow rule doesn't apply, a deny rule does.

### safeUrl

`function safeUrl(url: string | null | undefined, options?: SafeUrlOptions): string | undefined;`

The URL when a link may use it: http, https, mailto, a relative URL, and with `files`, `data:`
and `blob:`. Anything else, `javascript:` first, is `undefined`, to render as text instead: React
18 renders a `javascript:` link as it is, and React 19 only replaces it with one that throws.

In the docs: [Performance](https://signoff-ui.vercel.app/docs/limits).

### setToolInput

```ts
function setToolInput<M extends {
    parts: readonly unknown[];
}>(messages: readonly M[], toolCallId: string, input: unknown): M[];
```

Messages with one tool call's input replaced, for approving edited arguments with AI SDK 7: set
them with `setMessages` (from `useChat`), then answer the approval. The server runs the call with the
input in the messages, after checking it against the tool's schema and `toolApproval` again.
When the tool's schema changed what the model sent (a default filled in, a value coerced, a key
dropped), the approval keeps what the model sent as `inputSchemaInput`, and the server checks the
call against it, so it gets the edit too. The server runs only an edit that comes out of the
schema unchanged, and out of `experimental_refineToolInput` too when that is set: keep the fields
the schema fills in (the card starts an edit from the input it shows, defaults included), and use
transforms that give the same result when run twice.
With `experimental_toolApprovalSecret` set, the server signs the input it asked about and refuses
any other, so edits fail there by design.

In the docs: [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### suggestArgs

`function suggestArgs(input: unknown): Record<string, string> | undefined;`

The argument patterns a rule made from this call would start with: for a shell command, its
program and subcommand (`npm test*`), or the whole command when it has no subcommand or runs what
its arguments say (`rm -rf build`, `node -e "…"`), since `rm*` would allow `rm -rf ~`; otherwise
each string, number and boolean argument as it is. Globs are escaped. The person can widen them
before saving.

### toAcpPermissionResponse

`function toAcpPermissionResponse(decision: ApprovalDecision, options: readonly AcpPermissionOption[]): AcpRequestPermissionResponse;`

A decision as ACP's response: the agent's one option of the same kind. `allow-session` selects
`allow_once`: the session rule stays on the client, so it never grants more than the agent's
"once". With no option of that kind, or several, the request is cancelled, as ACP asks of a
client that can't answer: it never picks one of several grants for the person. `deny-once`
answers the first of several `reject_once` options, since a denial grants nothing.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp), [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### toPatch

`function toPatch(files: readonly ParsedFileDiff[], decisions: Readonly<Record<string, HunkDecision>>): string;`

The accepted changes as a git-style unified diff, from the original files: `git apply` and
`patch -p1` read it. Rejected and undecided changes are left out, and the hunks after them are
renumbered to match. A binary file accepted is listed as changed, without its contents.

In the docs: [Performance](https://signoff-ui.vercel.app/docs/limits), [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review), [useDiffReview](https://signoff-ui.vercel.app/docs/components/use-diff-review).

### toPlainRules

`function toPlainRules(rules: readonly ApprovalRule[]): ApprovalRule[];`

Rules as plain data, to send with a request: copies, as JSON carries them. A rule with a `when`
is left out, since `when` is code and without it the rule would cover more calls than it does on
the page. For a deny rule with a `when`, so are the allow rules on the tools it covers, which it
would overrule on the page: the server asks about those calls, and the page answers them with
every rule.

In the docs: [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### toSourceItem

`function toSourceItem(source: SourcePart | SourceItem): SourceItem;`

### toToolApproval

```ts
function toToolApproval(rules: readonly ApprovalRule[] | (() => readonly ApprovalRule[]), options?: ToToolApprovalOptions): (options: {
    toolCall: {
        toolName: string;
        input: unknown;
    };
}) => ToolApprovalStatusLike;
```

Rules as AI SDK 7's generic `toolApproval` function: a matching rule approves or denies the call
on the server, with the rule in the reason (`reason` words it), and any other call gets
`otherwise` (by default `'user-approval'`, a person decides). The SDK checks it again on the
approved call before it runs, edited arguments included.

Rules a client sends are the choices of the person using it: take them for that person's own
runs only, and keep rules that protect others on the server.

In the docs: [Getting started](https://signoff-ui.vercel.app/docs/getting-started), [Labels](https://signoff-ui.vercel.app/docs/labels), [Performance](https://signoff-ui.vercel.app/docs/limits), [useApprovalPolicy](https://signoff-ui.vercel.app/docs/components/use-approval-policy).

### toToolApprovalResponse

```ts
function toToolApprovalResponse(id: string, decision: ApprovalDecision, reason?: string): {
    id: string;
    approved: boolean;
    reason?: string;
};
```

A decision as the response `addToolApprovalResponse` (from `useChat`) takes.

### AGENT_STATE_LABEL

`const AGENT_STATE_LABEL: Record<AgentState, string>;`

The English state names. `AgentStatus` reads them from `labels.agentStatus.states`.

### APPROVAL_DECISIONS

`const APPROVAL_DECISIONS: readonly ApprovalDecision[];`

### DEFAULT_MAX_EDIT_LENGTH

`const DEFAULT_MAX_EDIT_LENGTH = 2000;`

The most lines added plus lines removed a file's diff may take before it is shown as one hunk
that replaces the changed region. Diffing costs about the square of this number: 2,000 is about
0.2 s on a laptop, in a worker for large files, while 10,000 (a 5,000-line rewrite) was 2.7 s.

### defaultLabels

`const defaultLabels: SignoffLabels;`

The English labels, as the components read without any.

### TOOL_PHASE_LABEL

`const TOOL_PHASE_LABEL: Record<ToolPhase, string>;`

The English phase names. The timeline reads them from `labels.toolCallTimeline.phases`.

### TOOL_STATES

```ts
const TOOL_STATES: readonly [
    "input-streaming",
    "input-available",
    "approval-requested",
    "approval-responded",
    "output-available",
    "output-error",
    "output-denied"
];
```

### AcpPermissionOption

```ts
interface AcpPermissionOption {
    optionId: string;
    name: string;
    kind: AcpPermissionOptionKind;
}
```

ACP `PermissionOption`: a choice the agent offers.

### AcpPermissionOptionKind

`type AcpPermissionOptionKind = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';`

ACP `PermissionOptionKind`.

### AcpRequestPermissionRequest

```ts
interface AcpRequestPermissionRequest {
    sessionId: string;
    toolCall: {
        toolCallId: string;
        title?: string | null | undefined;
        name?: string | null | undefined;
        kind?: string | null | undefined;
        rawInput?: unknown;
    };
    options: readonly AcpPermissionOption[];
}
```

What `toAcpPermissionResponse` reads of ACP's `RequestPermissionRequest`.

### AcpRequestPermissionResponse

```ts
interface AcpRequestPermissionResponse {
    outcome: {
        outcome: 'cancelled';
    } | {
        outcome: 'selected';
        optionId: string;
    };
}
```

ACP `RequestPermissionResponse`.

### AnyUIPart

`type AnyUIPart = UIMessage['parts'][number];`

Any part of any `UIMessage`.

### CommentRange

```ts
interface CommentRange extends LineRange {
    oldStartLine?: number | undefined;
    oldEndLine?: number | undefined;
}
```

The lines a comment records: their range, and with removed lines among lines of the proposed
file, their range in the original too.

### CommentSide

`type CommentSide = 'new' | 'old';`

### ContextGap

```ts
interface ContextGap {
    before: number;
    oldStart: number;
    oldEnd: number;
    delta: number;
}
```

A gap of unchanged lines between hunks (or before the first, or after the last) that can be shown.

### CostBreakdown

```ts
interface CostBreakdown {
    input: number;
    cachedInput: number;
    cacheWrite: number;
    output: number;
    total: number;
}
```

### DerivedAgentState

```ts
interface DerivedAgentState {
    state: AgentState;
    detail?: string | undefined;
}
```

### DiffHunk

```ts
interface DiffHunk {
    id: string;
    index: number;
    oldStart: number;
    oldLines: number;
    newStart: number;
    newLines: number;
    header: string;
    lines: DiffLine[];
    additions: number;
    deletions: number;
}
```

### DiffLine

```ts
interface DiffLine {
    type: DiffLineType;
    content: string;
    oldNumber?: number | undefined;
    newNumber?: number | undefined;
    segments?: DiffSegment[] | undefined;
    noNewline?: true | undefined;
}
```

### DiffReviewResumeEntry

```ts
interface DiffReviewResumeEntry {
    interruptId: string;
    status: 'resolved';
    payload: DiffReviewToolOutput;
}
```

An AG-UI `ResumeEntry` answering an interrupt with a review (`AgUiResumeEntry`, typed here).

### DiffReviewToolOutput

`interface DiffReviewToolOutput { … }`

The review as the agent reads it, for `addToolOutput` (AI SDK) or an AG-UI resume payload.

In the docs: [Getting started](https://signoff-ui.vercel.app/docs/getting-started).

### DiffReviewToolOutputOptions

```ts
interface DiffReviewToolOutputOptions {
    contents?: boolean;
    patch?: string;
}
```

### FileStatus

`type FileStatus = 'added' | 'deleted' | 'modified' | 'renamed';`

### HeadingLevel

`type HeadingLevel = 2 | 3 | 4 | 5 | 6;`

Heading levels a component title can render as.

### OfferedChoices

```ts
interface OfferedChoices {
    approve: boolean;
    session: boolean;
    always: boolean;
    deny: boolean;
    alwaysDeny: boolean;
}
```

Which choices an approval card offers, for its keyboard hint.

### ParsedFileDiff

```ts
interface ParsedFileDiff {
    id: string;
    path: string;
    oldPath?: string | undefined;
    status: FileStatus;
    language: string;
    hunks: DiffHunk[];
    additions: number;
    deletions: number;
    oldContent?: string | undefined;
    newContent?: string | undefined;
    binary?: true | undefined;
    fallback?: 'replace' | undefined;
}
```

### ParseFileChangeOptions

```ts
interface ParseFileChangeOptions {
    context?: number | undefined;
    id?: string | undefined;
    maxEditLength?: number | undefined;
}
```

### RangeLabel

```ts
interface RangeLabel {
    side: CommentSide;
    startLine: number;
    endLine: number;
}
```

Lines of a hunk, as a range in one of the files.

### RevealedMark

```ts
interface RevealedMark {
    kind: 'invisible' | 'blank';
    text: string;
    count: number;
    lines: number;
    codes: string[];
    shown: string;
}
```

A run of characters to show as a mark, in place of the characters.

### RevealedPart

`type RevealedPart = string | RevealedMark;`

A part of the text: as it is, or a mark.

### RevealTextOptions

```ts
interface RevealTextOptions {
    blanks?: boolean | undefined;
}
```

Text and URLs from the agent, shown to the person who decides on them. A prompt injection can
write them too, so the components show what would make them read as something other than what
runs or opens: characters that do not show, a long run of spaces or of blank lines, and a link
whose text names another host. No React here, so servers can call these too.

### RuleMatch

```ts
interface RuleMatch {
    effect: 'allow' | 'deny';
    rule: ApprovalRule;
    unchecked?: boolean | undefined;
}
```

The rule that decides a call.

### SafeUrlOptions

```ts
interface SafeUrlOptions {
    files?: boolean | undefined;
}
```

### SignoffLabels

`interface SignoffLabels { … }`

In the docs: [Labels](https://signoff-ui.vercel.app/docs/labels).

### SignoffLabelsInput

```ts
type SignoffLabelsInput = {
    [K in keyof SignoffLabels]?: DeepPartial<SignoffLabels[K]>;
};
```

A part of the labels: any section, any key, at any depth.

### SourcePart

`type SourcePart = SourceUrlUIPart | SourceDocumentUIPart;`

### ToolApprovalStatusLike

```ts
type ToolApprovalStatusLike = 'not-applicable' | 'approved' | 'denied' | 'user-approval' | {
    type: 'approved' | 'denied' | 'user-approval';
    reason?: string;
};
```

AI SDK 7's `ToolApprovalStatus`, as `toToolApproval` returns it.

### ToolPart

`type ToolPart = ToolUIPart<UITools> | DynamicToolUIPart;`

A static (`tool-${name}`) or dynamic tool part.

In the docs: [CopilotKit](https://signoff-ui.vercel.app/docs/copilotkit).

### ToolPhase

`type ToolPhase = 'streaming' | 'running' | 'awaiting-approval' | 'success' | 'error' | 'denied';`

A UI-level phase derived from the SDK tool state. Several SDK states collapse
into one visual phase (e.g. an approved call that has not returned yet is "running").

### ToolState

`type ToolState = ToolPart['state'];`

The AI SDK v7 tool part states.

### ToolTimings

`type ToolTimings = Readonly<Record<string, ToolTiming>>;`

## signoff-ui/ag-ui

[AG-UI agents](https://signoff-ui.vercel.app/docs/ag-ui): `useAgUiAgent`, and the converters and the run reducer it wires to an `@ag-ui/client` agent. Those are pure, for a store of your own.

On the component pages: [`useAgUiAgent`](https://signoff-ui.vercel.app/docs/components/use-ag-ui-agent#useaguiagent), [`AgUiAgentLike`](https://signoff-ui.vercel.app/docs/components/use-ag-ui-agent#useaguiagent), [`AgUiApprovalResponse`](https://signoff-ui.vercel.app/docs/components/use-ag-ui-agent#aguiapprovalresponse), [`AgUiInterrupt`](https://signoff-ui.vercel.app/docs/components/use-ag-ui-agent#aguiinterrupt), [`AgUiResumeEntry`](https://signoff-ui.vercel.app/docs/components/use-ag-ui-agent#aguiresumeentry), [`UseAgUiAgentResult`](https://signoff-ui.vercel.app/docs/components/use-ag-ui-agent#useaguiagent).

### answerAgUiInterrupt

`function answerAgUiInterrupt(run: AgUiRunState, answer: AgUiApprovalResponse | AgUiResumeEntry): AgUiRunState;`

Record an answer to an open interrupt. A tool approval becomes the conventional
`{ approved, reason? }` payload, with `editedArgs` when the person edited the arguments: AG-UI's
approve-with-edits field, a full replacement of the call's arguments. The same arguments go as
`input` too, the name earlier versions sent, which is deprecated. Pass a resume entry for other
interrupts.

In the docs: [AG-UI](https://signoff-ui.vercel.app/docs/ag-ui).

### createAgUiRun

`function createAgUiRun(interrupts?: readonly AgUiInterrupt[]): AgUiRunState;`

### fromAgUiMessages

`function fromAgUiMessages(messages: readonly AgUiMessage[], run?: AgUiRunState): UIMessage[];`

AG-UI messages as AI SDK `UIMessage`s: each user message, then the assistant, reasoning, tool and
activity messages after it grouped into one assistant message, the way the AI SDK groups a
multi-step run. Tool calls become dynamic tool parts with their results; with the run state,
streaming flags and tool-call interrupts (as approvals) are applied too. Activity messages become
`data-${activityType}` parts. System and developer messages are left out.

A sub-agent's messages become one `data-signoff-subagent` part, after the tool call that started
it (`SUBAGENT_STARTED`'s `parentToolCallId`), which may be another sub-agent's, or else where its
first message was. `ToolCallTimeline` nests its calls under that call.

In the docs: [AG-UI](https://signoff-ui.vercel.app/docs/ag-ui).

### fromAgUiUsage

`function fromAgUiUsage(usage: AgUiTokenUsage): RunUsage;`

An AG-UI `TokenUsage` entry as AI SDK usage.

### getAgUiResume

`function getAgUiResume(run: AgUiRunState): AgUiResumeEntry[] | undefined;`

The resume entries for the next run, once every open interrupt has an answer.

In the docs: [AG-UI](https://signoff-ui.vercel.app/docs/ag-ui).

### parsePartialJson

`function parsePartialJson(text: string): unknown;`

Parse JSON that may be cut off mid-stream, such as tool arguments as they arrive: open strings,
arrays and objects are closed, and a trailing key or partial literal is dropped.
`{"query": "stre` gives `{ query: 'stre' }`. Returns `undefined` when nothing parses.

### reduceAgUiRun

`function reduceAgUiRun(run: AgUiRunState, event: AgUiEvent): AgUiRunState;`

Fold one AG-UI event into the run state. Pure, so it fits `useReducer` or any store;
`useAgUiAgent` uses it with an `@ag-ui/client` agent.

In the docs: [AG-UI](https://signoff-ui.vercel.app/docs/ag-ui).

### AgUiApproval

```ts
interface AgUiApproval {
    id: string;
    approved?: boolean | undefined;
    reason?: string | undefined;
    requestReason?: string | undefined;
}
```

A decision on a tool-call interrupt, as the tool part shows it.

### AgUiContentPart

```ts
interface AgUiContentPart {
    type: string;
    text?: string;
    source?: {
        type: string;
        value: string;
        mimeType?: string;
    };
}
```

Text, image, audio, video or document content (AG-UI `ContentPart`).

### AgUiEvent

```ts
interface AgUiEvent {
    type: string;
}
```

An AG-UI event. Only `type` is required here; the fields each event type carries are read as needed.

### AgUiMessage

```ts
interface AgUiMessage {
    id: string;
    role: string;
    content?: unknown;
    toolCalls?: readonly AgUiToolCall[];
    toolCallId?: string;
    error?: string;
    activityType?: string;
    subagentRunId?: string;
}
```

Any AG-UI message: user, assistant, reasoning, tool, activity, system or developer.

### AgUiRunState

```ts
interface AgUiRunState {
    status: ChatStatus;
    error?: {
        message: string;
        code?: string | undefined;
    } | undefined;
    usage?: RunUsage | undefined;
    step?: string | undefined;
    interrupts: AgUiInterrupt[];
    answers: Record<string, AgUiResumeEntry>;
    approvals: Record<string, AgUiApproval>;
    streaming: {
        messages: string[];
        toolCalls: string[];
    };
    subagents?: AgUiSubagent[] | undefined;
}
```

What the event stream says about runs, beyond the messages themselves.

### AgUiSubagent

```ts
interface AgUiSubagent {
    subagentRunId: string;
    name?: string | undefined;
    parentToolCallId?: string | undefined;
    parentSubagentRunId?: string | undefined;
    status: SubagentStatus;
    error?: {
        message: string;
        code?: string | undefined;
    } | undefined;
}
```

A sub-agent the runs started (`SUBAGENT_STARTED`), and how it ended.

### AgUiSubscriber

```ts
interface AgUiSubscriber {
    onRunInitialized?(params: {
        messages: readonly AgUiMessage[];
    }): void;
    onEvent?(params: {
        event: AgUiEvent;
    }): void;
    onMessagesChanged?(params: {
        messages: readonly AgUiMessage[];
    }): void;
    onRunFailed?(params: {
        error: Error;
    }): void;
    onRunFinalized?(params: {
        messages: readonly AgUiMessage[];
    }): void;
}
```

The subscriber callbacks `useAgUiAgent` registers (a subset of `@ag-ui/client`'s `AgentSubscriber`).

### AgUiTokenUsage

```ts
interface AgUiTokenUsage {
    inputTokens?: number;
    outputTokens?: number;
    totalTokens?: number;
    reasoningTokens?: number;
    cachedInputTokens?: number;
    cacheWriteInputTokens?: number;
}
```

AG-UI `TokenUsage`. Totals include cached and reasoning tokens, as in the AI SDK.

### AgUiToolCall

```ts
interface AgUiToolCall {
    id: string;
    function: {
        name: string;
        arguments: string;
    };
}
```

## signoff-ui/acp

The [Agent Client Protocol](https://signoff-ui.vercel.app/docs/acp), with no React: a prompt turn's `session/update` notifications and permission requests folded into a `UIMessage`, a tool call's diff content as `DiffReview` files, and the answer to `session/request_permission` from the approval card's decision.

It also exports these, listed under [`signoff-ui/core`](https://signoff-ui.vercel.app/docs/api#signoff-uicore): [`AcpPermissionOption`](https://signoff-ui.vercel.app/docs/api#acppermissionoption), [`AcpPermissionOptionKind`](https://signoff-ui.vercel.app/docs/api#acppermissionoptionkind), [`AcpRequestPermissionRequest`](https://signoff-ui.vercel.app/docs/api#acprequestpermissionrequest), [`AcpRequestPermissionResponse`](https://signoff-ui.vercel.app/docs/api#acprequestpermissionresponse), [`decisionsFromAcpOptions`](https://signoff-ui.vercel.app/docs/api#decisionsfromacpoptions), [`fromAcpPermissionRequest`](https://signoff-ui.vercel.app/docs/api#fromacppermissionrequest), [`fromAcpPermissionResponse`](https://signoff-ui.vercel.app/docs/api#fromacppermissionresponse), [`toAcpPermissionResponse`](https://signoff-ui.vercel.app/docs/api#toacppermissionresponse).

### acpToolName

```ts
function acpToolName(toolCall: {
    name?: string | null | undefined;
    kind?: string | null | undefined;
    title?: string | null | undefined;
}): string;
```

The tool name rules and cards use for an ACP tool call: its programmatic `name` when the agent
sends one, else its `kind` (`edit`, `execute`…), else its `title`.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### answerAcpPermission

`function answerAcpPermission(turn: AcpTurn, toolCallId: string, decision: ApprovalDecision | 'cancelled'): AcpTurn;`

Record the answer to a tool call's permission request, as the card shows it. Send the agent
`toAcpPermissionResponse(decision, options)` as the request's response.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### createAcpTurn

`function createAcpTurn(): AcpTurn;`

A turn that has just been prompted.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### endAcpTurn

```ts
function endAcpTurn(turn: AcpTurn, result: {
    stopReason: string;
} | {
    error: {
        message: string;
    };
}): AcpTurn;
```

The turn's end: `session/prompt`'s response, or the error it returned.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### fromAcpDiffs

```ts
function fromAcpDiffs(content: readonly AcpToolCallContent[] | null | undefined, options?: {
    root?: string | undefined;
}): FileChange[];
```

A tool call's diff content as `DiffReview` files. ACP paths are absolute: pass the session's
`cwd` as `root` to show them relative to it. A diff with no `oldText` is a new file.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### reduceAcpTurn

`function reduceAcpTurn(turn: AcpTurn, update: AcpSessionNotification | AcpSessionUpdate): AcpTurn;`

Fold one `session/update` (the notification's params, or its `update`) into the turn. Message and
thought chunks append to the reply, tool calls and their updates merge by id; plans, commands, mode
and usage updates, and the user's own message chunks, leave it as it was. Pure, for `useReducer` or
any store.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### requestAcpPermission

`function requestAcpPermission(turn: AcpTurn, request: AcpRequestPermissionRequest): AcpTurn;`

A `session/request_permission` request: its tool call waits on a person, with the options offered.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### toAcpDiffs

```ts
function toAcpDiffs(files: readonly Pick<FileChange, 'path' | 'oldContent' | 'newContent'>[], options?: {
    root?: string | undefined;
}): Extract<AcpToolCallContent, {
    type: 'diff';
}>[];
```

Files as ACP diff content, `{ type: 'diff', path, oldText, newText }`: a new file has no
`oldText`. With `root`, relative paths are made absolute under it.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### toAcpMessage

`function toAcpMessage(turn: AcpTurn, id?: string): UIMessage;`

The turn as an assistant `UIMessage`: text and reasoning parts, and a dynamic tool part per call.
A call waiting on its permission request is `approval-requested`, with the tool call id as the
approval id; one denied (or whose request was cancelled) is `output-denied`. The tool's name is
its `name`, else its `kind`, else its `title`; the title is the part's `title`.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### AcpContentBlock

```ts
interface AcpContentBlock {
    type: string;
    text?: string;
}
```

An ACP `ContentBlock`. Text is read; images, audio and resources are left out of the message.

### AcpPermission

```ts
interface AcpPermission {
    options: readonly AcpPermissionOption[];
    decision?: ApprovalDecision | 'cancelled' | undefined;
}
```

A permission the agent asked for: the options it offered, and once answered, the decision.

### AcpSessionNotification

```ts
interface AcpSessionNotification {
    sessionId: string;
    update: AcpSessionUpdate;
}
```

The params of an ACP `session/update` notification.

### AcpSessionUpdate

```ts
interface AcpSessionUpdate {
    sessionUpdate: string;
}
```

An ACP `SessionUpdate`. Only `sessionUpdate` is required here; each kind's fields are read as needed.

### AcpToolCall

```ts
interface AcpToolCall {
    toolCallId: string;
    title?: string | null | undefined;
    name?: string | null | undefined;
    kind?: string | null | undefined;
    status?: AcpToolCallStatus | null | undefined;
    content?: readonly AcpToolCallContent[] | null | undefined;
    locations?: readonly {
        path: string;
        line?: number | null | undefined;
    }[] | null | undefined;
    rawInput?: unknown;
    rawOutput?: unknown;
}
```

An ACP `ToolCall`, or a `ToolCallUpdate` (every field but the id optional, `null` for unchanged).

### AcpToolCallContent

```ts
type AcpToolCallContent = {
    type: 'content';
    content: AcpContentBlock;
} | {
    type: 'diff';
    path: string;
    oldText?: string | null | undefined;
    newText: string;
} | {
    type: 'terminal';
    terminalId: string;
};
```

ACP `ToolCallContent`: content, a diff, or a terminal.

### AcpToolCallStatus

`type AcpToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';`

ACP `ToolCallStatus`.

### AcpTurn

```ts
interface AcpTurn {
    status: ChatStatus;
    stopReason?: string | undefined;
    error?: {
        message: string;
    } | undefined;
    items: readonly AcpTurnItem[];
    toolCalls: Readonly<Record<string, AcpToolCall>>;
    permissions: Readonly<Record<string, AcpPermission>>;
}
```

One prompt turn of an ACP session, from `session/prompt` until its response.

In the docs: [ACP](https://signoff-ui.vercel.app/docs/acp).

### AcpTurnItem

```ts
type AcpTurnItem = {
    type: 'text' | 'reasoning';
    text: string;
} | {
    type: 'tool';
    toolCallId: string;
};
```

A piece of the agent's reply, in the order it arrived.

## signoff-ui/assistant-ui

Tool UIs for [assistant-ui](https://signoff-ui.vercel.app/docs/chat-ui#assistant-ui): `signoffTools()` goes in `MessagePrimitive.Parts`' `components.tools`. `@assistant-ui/react` is an optional peer, and only its types are used.

### ApprovalToolUI

`function ApprovalToolUI(props: ToolCallMessagePartProps): JSX.Element | null;`

The approval card for a call at an approval gate (`approval` on the part), answered through
`respondToApproval`. Renders nothing for other calls, for a request that is not a plain decision
(a question, a choice to select), and for one cancelled or expired before anyone decided.

In the docs: [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui).

### ReviewToolUI

`function ReviewToolUI(props: ToolCallMessagePartProps): JSX.Element | null;`

A call whose arguments carry `files` (`FileChange[]`), reviewed in a `DiffReview` once its
arguments are in. The review goes back as the call's result, `reviewToolOutput(review)`, and the
review stays on the page, read-only, with the decisions that result records: a thread loaded
again shows each hunk as it was decided. Its viewed marks and comments are not shown again.

In the docs: [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui), [DiffReview](https://signoff-ui.vercel.app/docs/components/diff-review).

### SignoffToolsProvider

`function SignoffToolsProvider(props: SignoffToolsProviderProps): JSX.Element;`

Options for the tool UIs below it: approval rules, tool labels and risk, review props. Optional.

In the docs: [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui).

### signoffTools

```ts
function signoffTools(options?: SignoffToolsOptions): {
    by_name: Record<string, ToolCallMessagePartComponent>;
    Fallback: ToolCallMessagePartComponent;
};
```

`components.tools` for `MessagePrimitive.Parts`: a `DiffReview` for the `review` tools, and for
every other call the approval card when it has an approval gate, else `Fallback`. The components
are the same on every call, so it can run on every render.

In the docs: [Introduction](https://signoff-ui.vercel.app/docs), [Chat UIs](https://signoff-ui.vercel.app/docs/chat-ui), [Performance](https://signoff-ui.vercel.app/docs/limits).

### SignoffToolsOptions

```ts
interface SignoffToolsOptions {
    review?: readonly string[] | undefined;
    Fallback?: ToolCallMessagePartComponent | undefined;
}
```

### SignoffToolsProviderProps

```ts
interface SignoffToolsProviderProps {
    policy?: ApprovalPolicy | undefined;
    tools?: Record<string, ToolMeta> | undefined;
    review?: Omit<DiffReviewProps, 'files' | 'onSubmit' | 'readOnly'> | undefined;
    approval?: Omit<ToolApprovalCardProps, 'part' | 'onRespond' | 'policy' | 'meta'> | undefined;
    output?: DiffReviewToolOutputOptions | undefined;
    children?: ReactNode;
}
```

## signoff-ui/custom-markdown

`AgentMessage` and `Reasoning` for a Markdown renderer of your own, such as Streamdown, without react-markdown: pass `renderMarkdown`, or set one with `MarkdownRendererProvider`. Without one, text shows as it is.

It also exports these, listed under [`signoff-ui`](https://signoff-ui.vercel.app/docs/api#signoff-ui): [`AgentMessageProps`](https://signoff-ui.vercel.app/docs/components/agent-message#agentmessage), [`MarkdownRenderer`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrenderer), [`MarkdownRendererProps`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrendererprops), [`MarkdownRendererProvider`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrendererprovider), [`MarkdownRendererProviderProps`](https://signoff-ui.vercel.app/docs/components/agent-message#markdownrendererprovider), [`ReasoningProps`](https://signoff-ui.vercel.app/docs/components/reasoning#reasoning).

On the component pages: [`AgentMessage`](https://signoff-ui.vercel.app/docs/components/agent-message#agentmessage), [`Reasoning`](https://signoff-ui.vercel.app/docs/components/reasoning#reasoning).
