Skip to content
signoff-uiv0.12.0

Docs

Resources

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.

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.

On the component pages: AgentMessage, AgentStatus, ApprovalCard, DiffReview, Markdown, MarkdownRendererProvider, Reasoning, RunMeter, Sources, ToolApprovalBatch, ToolApprovalCard, ToolCallTimeline, useApprovalPolicy, useDiffReview, useRunTiming, AgentMessageProps, AgentStatusProps, ApprovalAuditEvent, ApprovalCardProps, ApprovalChoice, ApprovalOutcome, ApprovalPolicy, ApprovalRuleStorage, DiffReviewDraft, DiffReviewFileState, DiffReviewProps, DiffReviewSelection, DiffViewMode, MarkdownProps, MarkdownRenderer, MarkdownRendererProps, MarkdownRendererProviderProps, ReasoningProps, RunMeterProps, RunTiming, SourcesProps, ToolApprovalBatchProps, ToolApprovalCardProps, ToolApprovalResponse, ToolCallTimelineProps, ToolMeta, UseApprovalPolicyOptions, UseDiffReviewOptions, UseDiffReviewResult.

AgentStatusDot

TypeScript
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.

SignoffLabelsProvider

TypeScript
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, Labels.

ToolCallDetails

TypeScript
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.

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.

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.

DiffWorkerFactory

type DiffWorkerFactory = () => Worker;

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

JsonViewProps

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

RunTimingMessage

TypeScript
interface RunTimingMessage {
    id: string;
    role: string;
}

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

ShownContext

TypeScript
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, ApprovalDecision, ApprovalRequest, ApprovalRule, ApprovalRuleLimits, ApprovalStatus, DiffReviewComment, DiffReviewFileResult, DiffReviewRejectedHunk, DiffReviewResult, DiffReviewResultComment, FileChange, FileDecision, HunkDecision, ModelPricing, ReviewItem, RiskLevel, RunUsage, SourceItem, SubagentData, SubagentPart, SubagentStatus, ToolTiming, 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, Getting started, Performance, RunMeter.

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, Getting started, Performance, DiffReview.

cn

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

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

computeReviewResult

TypeScript
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.

contextGaps

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

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

decisionEffect

TypeScript
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, useApprovalPolicy.

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, DiffReview.

deriveAgentState

TypeScript
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, Getting started, AG-UI, Labels.

describeRule

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

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

In the docs: Labels, useApprovalPolicy.

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, Performance.

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

TypeScript
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.

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.

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, Getting started, Chat UIs, useApprovalPolicy.

getToolParts

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

getToolPhase

TypeScript
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.

inferLanguage

function inferLanguage(path: string): string;

isSourcePart

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

isSubagentPart

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

isToolPart

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

In the docs: Introduction, Chat UIs.

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.

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, Getting started, Performance, 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, Performance, DiffReview.

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.

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.

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, Getting started, Chat UIs, 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.

ruleFromDecision

TypeScript
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.

setToolInput

TypeScript
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.

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, useApprovalPolicy.

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, DiffReview, useDiffReview.

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.

toSourceItem

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

toToolApproval

TypeScript
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, Labels, Performance, useApprovalPolicy.

toToolApprovalResponse

TypeScript
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

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

AcpPermissionOption

TypeScript
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

TypeScript
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

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

ACP RequestPermissionResponse.

AnyUIPart

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

Any part of any UIMessage.

CommentRange

TypeScript
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

TypeScript
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

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

DerivedAgentState

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

DiffHunk

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

DiffLine

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

DiffReviewResumeEntry

TypeScript
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.

DiffReviewToolOutputOptions

TypeScript
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

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

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

ParsedFileDiff

TypeScript
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

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

RangeLabel

TypeScript
interface RangeLabel {
    side: CommentSide;
    startLine: number;
    endLine: number;
}

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

RevealedMark

TypeScript
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

TypeScript
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

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

The rule that decides a call.

SafeUrlOptions

TypeScript
interface SafeUrlOptions {
    files?: boolean | undefined;
}

SignoffLabels

interface SignoffLabels { … }

In the docs: Labels.

SignoffLabelsInput

TypeScript
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

TypeScript
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.

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: 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, AgUiAgentLike, AgUiApprovalResponse, AgUiInterrupt, AgUiResumeEntry, UseAgUiAgentResult.

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.

createAgUiRun

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

fromAgUiMessages

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

AG-UI messages as AI SDK UIMessages: 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.

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.

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.

AgUiApproval

TypeScript
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

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

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

AgUiEvent

TypeScript
interface AgUiEvent {
    type: string;
}

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

AgUiMessage

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
interface AgUiToolCall {
    id: string;
    function: {
        name: string;
        arguments: string;
    };
}

signoff-ui/acp

The Agent Client Protocol, 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: AcpPermissionOption, AcpPermissionOptionKind, AcpRequestPermissionRequest, AcpRequestPermissionResponse, decisionsFromAcpOptions, fromAcpPermissionRequest, fromAcpPermissionResponse, toAcpPermissionResponse.

acpToolName

TypeScript
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.

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.

createAcpTurn

function createAcpTurn(): AcpTurn;

A turn that has just been prompted.

In the docs: ACP.

endAcpTurn

TypeScript
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.

fromAcpDiffs

TypeScript
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.

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.

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.

toAcpDiffs

TypeScript
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.

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.

AcpContentBlock

TypeScript
interface AcpContentBlock {
    type: string;
    text?: string;
}

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

AcpPermission

TypeScript
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

TypeScript
interface AcpSessionNotification {
    sessionId: string;
    update: AcpSessionUpdate;
}

The params of an ACP session/update notification.

AcpSessionUpdate

TypeScript
interface AcpSessionUpdate {
    sessionUpdate: string;
}

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

AcpToolCall

TypeScript
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

TypeScript
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

TypeScript
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.

AcpTurnItem

TypeScript
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: 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.

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, DiffReview.

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.

signoffTools

TypeScript
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, Chat UIs, Performance.

SignoffToolsOptions

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

SignoffToolsProviderProps

TypeScript
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: AgentMessageProps, MarkdownRenderer, MarkdownRendererProps, MarkdownRendererProvider, MarkdownRendererProviderProps, ReasoningProps.

On the component pages: AgentMessage, Reasoning.

Edit this page on GitHubReport an issue

Last updated