import type { UIMessage } from 'ai';
import type React from 'react';
import type { QueuePause } from '../../../index.js';
import type { ApprovalTarget } from '../approvals/proposals.js';
import { type MessageFile } from '../attachments/files.js';
import type { ToolCatalog } from '../presentation/phrasing.js';
import type { QueuedChatMessage } from '../queue/model.js';
import { type ApproveOptions, type ChatStatus, type MessageUsageInfo, type TimestampInfo, type TranscriptBlock, type TranscriptFile, type TranscriptUiAction, type UsageSummary } from './model.js';
import { type StickToBottom } from './use-stick-to-bottom.js';
export interface TranscriptCopyState {
    /** False when the message has no prose to put on the clipboard. */
    available: boolean;
    copied: boolean;
    copy: () => void;
}
export interface TranscriptEditState {
    available: boolean;
    isEditing: boolean;
    draft: string;
    /** Whitespace-only drafts are refused, so an empty resubmit can never be sent. */
    canSave: boolean;
    start: () => void;
    cancel: () => void;
    setDraft: (value: string) => void;
    save: () => void;
    /** Controlled-textarea wiring: focus with the caret at the end, Enter saves, Escape cancels. */
    getTextareaProps: () => {
        ref: (element: HTMLTextAreaElement | null) => void;
        value: string;
        onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
        onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
    };
}
export interface TranscriptActionState {
    available: boolean;
    run: () => void;
}
export interface TranscriptItem {
    id: string;
    message: UIMessage;
    role: UIMessage['role'];
    /** Index in the FULL message array, not in the rendered window. */
    index: number;
    isUser: boolean;
    isAssistant: boolean;
    isLastAssistant: boolean;
    isStreaming: boolean;
    blocks: TranscriptBlock[];
    /** The prose alone — what `copy` puts on the clipboard and what an edit starts from. */
    text: string;
    /**
     * A user message that is a UI action: its parts (see `TranscriptTextBlock.uiAction`). `null` on
     * every other message.
     */
    uiAction: TranscriptUiAction | null;
    usage: UsageSummary | null;
    timestamp: TimestampInfo | null;
    copy: TranscriptCopyState;
    edit: TranscriptEditState;
    fork: TranscriptActionState;
    regenerate: TranscriptActionState;
}
/** What an edit-and-resubmit sends. */
export interface EditSubmitInput {
    messageId: string;
    text: string;
}
/** Names the message an action is about. */
export interface MessageActionInput {
    messageId: string;
}
/** Settling a parked question set. `answers` holds only the questions the user touched. */
export interface AnswerInput {
    toolCallId: string;
    answers?: Record<string, string[]>;
    /** The surface the answer came through; the server records `'web'` when omitted. */
    via?: string;
}
/** Declining a parked question set. */
export interface SkipInput {
    toolCallId: string;
    /** The surface the skip came through; the server records `'web'` when omitted. */
    via?: string;
}
/** Approving a tool call parked on a human. */
export interface ApproveInput extends ApproveOptions {
    target?: ApprovalTarget;
    toolCallId: string;
    /** The surface the decision came through; the server records `'web'` when omitted. */
    via?: string;
}
/** Rejecting a tool call parked on a human. */
export interface RejectInput {
    target?: ApprovalTarget;
    toolCallId: string;
    reason?: string;
    via?: string;
}
export interface TranscriptItemOptions {
    /**
     * A transcript nobody acts on (an audit view, a shared or archived conversation): no approve /
     * reject, answer / skip, edit, fork, regenerate or stop — whatever handlers or backend are in
     * scope. Parked approvals and question sets still render, with their actions unavailable.
     *
     * Without it the decision handlers DEFAULT to the in-scope backend (`<AgentProvider>`'s), so a
     * parked call is actionable with no wiring; a read-only surface would otherwise have to pass
     * `onApprove: null`, `onReject: null`, `onAnswer: null` and `onSkip: null` one by one.
     */
    readOnly?: boolean;
    /** User messages get an inline edit-and-resubmit machine. */
    editable?: boolean;
    onEditSubmit?: (input: EditSubmitInput) => void | Promise<void>;
    onFork?: (input: MessageActionInput) => void | Promise<void>;
    /** Only the LAST assistant message gets a regenerate machine. */
    regeneratable?: boolean;
    onRegenerate?: (input: MessageActionInput) => void | Promise<void>;
    /** Default: `metadata.usage` (replayed turns; see `AgentMessageMetadata`). */
    getUsage?: (message: UIMessage) => MessageUsageInfo | null;
    /** Default: `metadata.createdAt` (replayed and streamed messages). */
    getCreatedAt?: (message: UIMessage) => string | null;
    /** How long `copy.copied` stays true. Default 1500ms. */
    copyResetMs?: number;
    /** Clipboard writer; defaults to `navigator.clipboard.writeText`. */
    writeClipboard?: (text: string) => Promise<void>;
    /**
     * Lift retrieval tool parts into their own `sources` block instead of leaving them in the tool
     * run. Off by default, so a renderer already drawing tool cards keeps drawing them.
     */
    sources?: boolean;
    /**
     * Server-declared tool presentations (`useToolCatalog(...).catalog`). Given → each tool call's
     * `description` and each tool block's `activity` speak in the tool's own words; omitted → generic.
     */
    toolCatalog?: ToolCatalog;
    /**
     * Leave out a failed tool call the model retried at once with the same tool (`retriedCallIds`).
     * Default `true`; `false` keeps every attempt.
     */
    hideRetriedFailures?: boolean;
    /**
     * Settle a parked question set. Default: the in-scope backend's `answerToolCall` (the enclosing
     * `<AgentProvider>`'s), so a question set is an `elicitation` block the user can act on without
     * any wiring. `null` turns question sets back into plain tool calls.
     *
     * `answers` holds ONLY the questions the user touched. An omitted one takes its own pre-picked
     * default server-side, which is what keeps "just confirmed" and "picked exactly what was
     * pre-picked" distinguishable in the settled row.
     */
    onAnswer?: ((input: AnswerInput) => void | Promise<void>) | null;
    /** Decline the question set and let the agent proceed on its own picks. Default: the backend's. */
    onSkip?: ((input: SkipInput) => void | Promise<void>) | null;
    /**
     * Settle a tool call parked on a human. Default: the in-scope backend's `approveToolCall` /
     * `rejectToolCall`. `remember` is what `call.approve.run({ remember: true })` passed. `null`
     * leaves the call without the affordance.
     */
    onApprove?: ((input: ApproveInput) => void | Promise<void>) | null;
    onReject?: ((input: RejectInput) => void | Promise<void>) | null;
}
export interface TranscriptWindow {
    visibleCount: number;
    /** How many older messages are not mounted. */
    hiddenCount: number;
    canLoadEarlier: boolean;
    loadEarlier: () => void;
    showAll: () => void;
}
export interface TranscriptStopState {
    available: boolean;
    /** True from the click until the turn actually leaves a busy status. */
    isStopping: boolean;
    stop: () => void;
}
export interface UseChatTranscriptOptions extends TranscriptItemOptions {
    messages: UIMessage[];
    status: ChatStatus;
    initialVisibleCount?: number;
    loadEarlierStep?: number;
    /** Cancel the in-flight turn — wire to `useAgentChat`'s `cancel`. */
    onStop?: () => void | Promise<void>;
    /** Suggested follow-ups. The model only decides WHETHER they belong on screen. */
    followUps?: string[] | null;
    stickToBottomThreshold?: number;
    /**
     * Messages waiting in the thread's queue — `chat.queue` (`useAgentChat` wires it). They render
     * after the transcript as {@link ChatTranscript.queued}: pending user messages, not yet sent.
     */
    queue?: {
        /** `files` may be left out by a host that builds these itself: it is derived from `attachments`. */
        items: Array<Omit<QueuedChatMessage, 'files'> & {
            files?: MessageFile[];
        }>;
        paused: QueuePause | null;
        remove?: (id: string) => void | Promise<void>;
    } | null;
}
/** One file on a waiting message: a sent message's {@link MessageFile}, and a `TranscriptFile`. */
export type TranscriptQueuedFile = MessageFile & TranscriptFile;
/**
 * A message waiting in the thread's queue, as the transcript shows it: a user message that has not
 * been sent to the model yet. Render it after {@link ChatTranscript.items}, dimmed or tagged.
 */
export interface TranscriptQueuedItem {
    id: string;
    role: 'user';
    text: string;
    /**
     * The message's files, in the shape a sent message's have: a {@link MessageFile} (what
     * `messageFiles()` gives — `kind`, `extension`, `mediaId`) that is also a `TranscriptFile`
     * (`isImage`), so either renderer takes it as it is.
     */
    files: TranscriptQueuedFile[];
    /**
     * `'sending'` — on its way to the server. `'queued'` — waiting for the running turn.
     * `'paused'` — waiting, but the queue is paused (see {@link ChatTranscript.queuePaused}).
     */
    state: 'sending' | 'queued' | 'paused';
    /** 0-based place in the queue. */
    position: number;
    /** Runs next. */
    isNext: boolean;
    /** Queued by an interrupt — it runs as soon as the cancelled turn settles. */
    interrupt: boolean;
    remove: TranscriptActionState;
}
export interface ChatTranscript {
    /** The mounted window, oldest first. `window.hiddenCount` says what is held back. */
    items: TranscriptItem[];
    status: ChatStatus;
    isEmpty: boolean;
    /** A turn is in flight: sent but not settled. */
    isBusy: boolean;
    isStreaming: boolean;
    showEmptyState: boolean;
    /** Waiting on the first token of a turn that has no assistant message yet. */
    showTypingIndicator: boolean;
    showFollowUps: boolean;
    lastAssistantId: string | null;
    window: TranscriptWindow;
    scroll: StickToBottom;
    stop: TranscriptStopState;
    /** Messages waiting in the thread's queue, in run order — render after `items`. */
    queued: TranscriptQueuedItem[];
    /** Why the queue is not draining, or `null`. */
    queuePaused: QueuePause | null;
}
/**
 * The model behind a chat transcript: messages normalized into items with their parts grouped into
 * runs, per-message derived values (copyable text, usage labels, relative time), each action as a
 * state machine rather than a button, list windowing, and stick-to-bottom. It renders nothing and
 * names no class — `MessageList`/`MessageItem` are one renderer over it, and an app with its own
 * design system writes another without reimplementing any of this.
 */
export declare function useChatTranscript(options: UseChatTranscriptOptions): ChatTranscript;
export interface UseTranscriptItemOptions extends TranscriptItemOptions {
    message: UIMessage;
    isStreaming?: boolean;
}
/**
 * The single-message half of {@link useChatTranscript}, for a renderer that owns the list itself.
 * `regeneratable` applies directly here: one assistant message is trivially the last one.
 */
export declare function useTranscriptItem(options: UseTranscriptItemOptions): TranscriptItem;
//# sourceMappingURL=use-chat-transcript.d.ts.map