import { type DynamicToolUIPart, type ToolUIPart, type UIMessage } from 'ai';
import { type ElicitationInput } from '../../../elicitation-input.js';
import { type UiActionMessage } from '../../../genui/actions.js';
import type { ToolConfirmation } from '../../../index.js';
import type { ApprovalTarget } from '../approvals/proposals.js';
import { type RawAnswer } from '../elicitation/answers.js';
import type { ToolCatalog } from '../presentation/phrasing.js';
import { type ToolActivityGroup, type ToolCallDescription } from '../presentation/tool-activity.js';
/** A tool UI part on a `UIMessage` — a static `tool-*` part or the `dynamic-tool` part. */
export type AnyToolUIPart = ToolUIPart | DynamicToolUIPart;
/**
 * The AI SDK's chat status, plus `reconnecting`: a turn is in flight and its stream dropped, and the
 * transport is re-attaching (`useAgentChat`). A busy status, like `streaming`.
 */
export type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'reconnecting' | 'error';
/** Server-aggregated usage for an assistant turn. */
export interface MessageUsageInfo {
    inputTokens: number;
    outputTokens: number;
    /**
     * `null` when no price is on record for the model that ran the turn. Distinct from `0`, which is
     * a turn that genuinely cost nothing — printing `$0` for an unpriced turn states a number the
     * store never had.
     */
    costUsd: number | null;
}
/** A run of contiguous prose. `isStreaming` is the PART's own state, not the message's. */
export interface TranscriptTextBlock {
    kind: 'text';
    key: string;
    text: string;
    isStreaming: boolean;
    /**
     * A user message that is a UI action (a sandbox's `agent.send`, a component's button —
     * `uiActionText`): its parts. Draw `uiAction.text` (or `uiActionSummary(uiAction)`) as a chip
     * instead of `text`, whose JSON block is for the model. Absent on every other block.
     */
    uiAction?: TranscriptUiAction;
}
/** A UI action a user message carries ({@link TranscriptTextBlock.uiAction}). */
export type TranscriptUiAction = UiActionMessage;
/** One file on a message — an uploaded attachment, or one the model produced. */
export interface TranscriptFile {
    /** Presigned or otherwise directly fetchable. Display-only: the model reads its own copy. */
    url: string;
    mediaType: string;
    filename: string | null;
    /** `image/*`, which a renderer can show inline rather than as a link. */
    isImage: boolean;
}
/** A run of contiguous files. Grouped so a renderer can lay several out as one strip. */
export interface TranscriptFilesBlock {
    kind: 'files';
    key: string;
    files: TranscriptFile[];
}
/**
 * A run of contiguous reasoning. Carries its own disclosure state because a thread can hold many
 * of them and each is toggled independently; `isOpen` defaults to `isStreaming` (thinking is worth
 * watching live, worth folding away once answered) until `toggle` is called for that run.
 */
export interface TranscriptReasoningBlock {
    kind: 'reasoning';
    key: string;
    text: string;
    isStreaming: boolean;
    /**
     * How long the model thought, in ms — the backend's measurement once the run closed (live) or as
     * persisted (reloaded). `null` while it still streams, or when nothing was recorded: pair it with
     * `useElapsed(block.isStreaming)` for a ticking label (`block.durationMs ?? elapsed`).
     */
    durationMs: number | null;
    isOpen: boolean;
    toggle: (open?: boolean) => void;
}
/**
 * A decision a run is waiting on a human for, and which one a parked call is currently sending.
 *
 * One call settles one way at a time, and WHICH one is what lets a surface report progress on the
 * affordance the person pressed instead of on all of them.
 */
export type SettleAction = 'approve' | 'reject' | 'answer' | 'skip';
/** One such decision: whether it can be made, whether it is on its way, and how to send it. */
export interface TranscriptSettleState {
    available: boolean;
    /** True from the click until the run resumes and settles the call. */
    isSubmitting: boolean;
    run: () => void;
}
/** How an approval stands. */
export type TranscriptApprovalStatus = 'pending' | 'approved' | 'rejected' | 'expired' | 'superseded';
/**
 * Who has to settle a parked call, until when, and — once it settled — how. From the stream's
 * `approval-requested` / `approval-settled` frames, or the same parts a reloaded thread carries.
 * `null` on a call whose runner never said (the call can still be awaiting approval).
 */
export interface TranscriptApproval {
    target?: ApprovalTarget;
    executionStatus?: 'queued' | 'executing' | 'succeeded' | 'failed' | null;
    confirmation?: ToolConfirmation;
    /** Open vocabulary the host defines: `'requester'`, `'admin'`, a role… */
    approver: string;
    /** ISO-8601; `null` when the request never lapses. Pair with `useApprovalCountdown`. */
    expiresAt: string | null;
    /** Why the call needs a person, when the runner said. */
    reason: string | null;
    /**
     * `pending` until a settlement arrives; then what it said. Falls back to the call's own state
     * (denied → `rejected`, an output → `approved`) for a runner that streams no settlement.
     */
    status: TranscriptApprovalStatus;
    /** The approval also covers later calls of this tool in this thread. */
    remember: boolean;
    /** Opaque ref of who decided; `null` while pending, on an expiry, or when the runner did not say. */
    decidedBy: string | null;
    /** The surface the decision came through (`'web'`, `'slack'`, `'remembered'`, …). */
    decidedVia: string | null;
    /** What the person said when declining. */
    decisionReason: string | null;
}
/** What an approval can carry beyond yes. */
export interface ApproveOptions {
    /** Approve later calls of the same tool in the same thread without asking again. */
    remember?: boolean;
}
/** One call in a tool run, with whatever human decision it is parked on. */
export interface TranscriptToolCall {
    part: AnyToolUIPart;
    toolCallId: string;
    name: string;
    /** `read` / `action` as the stream classified the call; `null` when the backend did not say. */
    toolKind: string | null;
    /** The call this one ran inside (a code-mode `execute`, a delegated agent); `null` for a top-level call. */
    parentId: string | null;
    /**
     * Calls nested under this one within the same block, in stream order. Always empty for a call
     * nothing names as its parent.
     */
    children: TranscriptToolCall[];
    /** Who has to decide, when the runner said so. See {@link TranscriptApproval}. */
    approval: TranscriptApproval | null;
    /**
     * How to talk about this call without naming it — status, phrase, label, icon, approval prompt,
     * resolved result — from the server's presentation when `toolCatalog` was given, generic otherwise.
     */
    description: ToolCallDescription;
    /**
     * Parked on a person. An `action` tool's input lands and its output never follows on its own —
     * the loop waits for an approval between the two — so a settled-looking card that never settles
     * IS the pending approval.
     */
    isAwaitingApproval: boolean;
    /** `run({ remember: true })` approves this tool for the rest of the thread. */
    approve: TranscriptSettleState & {
        run: (options?: ApproveOptions) => void;
    };
    reject: TranscriptSettleState;
    /** A failed decision — the call is still parked, so the affordance stays live. */
    error: string | null;
    /**
     * The server's machine-readable reason for {@link error}, when it gave one. `run_not_active`
     * means the turn that asked has ended: nothing is waiting for the decision, and pressing again
     * will be refused again — word it yourself and let the person send the message again.
     */
    errorCode: string | null;
}
/**
 * A run of CONSECUTIVE tool parts, grouped so a UI can collapse "5 tools ran" into one affordance.
 * Any non-tool part between two tool parts ends the run — including the AI SDK's `step-start`
 * markers, so tools from different steps never merge into one group.
 */
export interface TranscriptToolBlock {
    kind: 'tools';
    key: string;
    parts: AnyToolUIPart[];
    /** The same calls, each with its human-decision state. Same order as `parts`. */
    calls: TranscriptToolCall[];
    /**
     * The same calls as a tree: every call whose parent is NOT in this block, each carrying its
     * nested calls in `children`. Equal to `calls` when nothing is nested.
     */
    roots: TranscriptToolCall[];
    /**
     * The run folded into activity groups ("Database query ×3"), over `roots`, keyed by each tool's
     * presentation label (else its name). For another grouping, call `groupToolActivity` yourself.
     */
    activity: ToolActivityGroup[];
}
/**
 * A component the server pushed into the message (the stream's `ui` frame, or a persisted
 * `data-ui` part). Rendered by looking `component` up in the host's own registry.
 */
export interface TranscriptUiBlock {
    kind: 'ui';
    key: string;
    /** The component's identity within the message. */
    id: string;
    component: string;
    props: Record<string, unknown>;
    /** Schema version of `props`; `null` when the server did not stamp one. */
    version: number | null;
    fallbackText?: string;
    componentVersions?: Record<string, number>;
    /** The tool call that pushed it (`ctx.emitUi`); `null` for a component pushed outside a tool. */
    toolCallId: string | null;
    /**
     * A preview drawn while the model writes the call's arguments (genui `streaming: 'partial'`):
     * unvalidated, replaced in place by the final push. Absent on a final component.
     */
    partial?: true;
}
/** One choice a question offers, with its live selection state. */
export interface TranscriptQuestionOption {
    value: string;
    label: string;
    /** A single character the request suggested as a shortcut; `null` when it suggested none. */
    hotkey: string | null;
    isSelected: boolean;
    /** Pre-picked by the agent — what an untouched question submits as. */
    isDefault: boolean;
    /** Single choice: replaces the selection. Multiple: adds or removes this value. */
    select: () => void;
}
/** One question of a set, numbered against the whole. */
export interface TranscriptQuestion {
    id: string;
    prompt: string;
    /** A line of help under the prompt; `null` when the request gave none. */
    description: string | null;
    /**
     * How the answer is typed — `{ type, placeholder?, required?, min?, max?, pattern? }` — or `null`
     * for a plain pick from `options`. Render the control from `input.type`; `select` still picks
     * from `options`.
     */
    input: ElicitationInput | null;
    multiple: boolean;
    /** 1-based. The request carries every question up front, so "Question 1 of N" is honest. */
    position: number;
    options: TranscriptQuestionOption[];
    selected: string[];
    /**
     * The user has not touched this question, so a submission leaves it out and the server applies
     * the same defaults it showed. Distinct from "selected happens to equal the defaults": only the
     * first is recorded as `defaulted` rather than as a choice the user made.
     */
    isPristine: boolean;
    /** The first selected value, or `''` — what a single text/number/date field shows. */
    value: string;
    /**
     * Set a typed answer from whatever the control produced (a string, a number, a checkbox's
     * boolean, a `Date`, a list); coerced to the question's canonical strings with `coerceAnswer`.
     * `null`/`''` clears it. Does nothing once the set settled.
     */
    setValue: (raw: RawAnswer) => void;
    /**
     * Why the current selection would be refused (the server's own rules — `validateAnswer`), or
     * `null`. A pristine required question with no default reads `requires an answer`; show it once
     * the user tried to submit, or right away — the model does not decide that for you.
     */
    error: string | null;
}
/** How a question set settled, once it did. */
export interface TranscriptElicitationOutcome {
    answers: Record<string, string[]>;
    /** The user declined to answer and let the agent proceed on its own picks. */
    skipped: boolean;
    /** Questions filled from their own defaults rather than by the human. */
    defaulted: string[];
    /** The questions against the chosen labels, as the model read them back. */
    summary: string | null;
    /** Who answered (or skipped) — an approval's `decidedBy`. `null` when the run did not record it. */
    answeredBy: string | null;
    /** The surface it came through (`'web'`, `'slack'`, …) — an approval's `decidedVia`. */
    answeredVia: string | null;
}
/**
 * A question set the run put to the user, parked until someone settles it — the intake an agent
 * declares and the model's own `ask` produce the same block, because the loop streams the same
 * frame for both.
 */
export interface TranscriptElicitationBlock {
    kind: 'elicitation';
    key: string;
    /** The parked tool call — what `answer`/`skip` route by. */
    toolCallId: string;
    preamble: string | null;
    questions: TranscriptQuestion[];
    questionCount: number;
    /** Still waiting on a human. */
    isPending: boolean;
    /** Every question's current selection is acceptable (no question has an `error`). */
    isValid: boolean;
    outcome: TranscriptElicitationOutcome | null;
    /** A failed submission — the run is still parked, so the form stays live. */
    error: string | null;
    /** The server's machine-readable reason for {@link error} (`run_not_active`, …), when it gave one. */
    errorCode: string | null;
    answer: TranscriptSettleState;
    skip: TranscriptSettleState;
}
/** One retrieved passage, as it rides a retrieval tool call's output. Mirrors core's `Passage`. */
export interface RetrievedPassage {
    id: string;
    text: string;
    score: number;
    /** Citation-facing origin — a document title, URL or row id. */
    source?: string;
    metadata?: Record<string, unknown>;
}
/** The passages of one origin, folded together so a citation line reads once per source. */
export interface TranscriptSource {
    /** The `source` string the retriever stamped, falling back to the passage id when it stamped none. */
    id: string;
    label: string;
    passageCount: number;
    /** Highest relevance among this source's passages; the scale is the retriever's. */
    topScore: number;
    passages: RetrievedPassage[];
}
/**
 * The provenance behind an answer: a run of CONSECUTIVE retrieval tool parts, aggregated by origin.
 * Detection is structural — a tool output shaped `{ passages: [{ id, text, ... }] }` — because the
 * tool's NAME is not fixed: inject-mode retrieval persists as `retrieve`, and `createRetrievalTool`
 * lets the host rename `search_knowledge` to anything.
 */
export interface TranscriptSourcesBlock {
    kind: 'sources';
    key: string;
    /** What was searched, when the tool call recorded a `{ query }` input. */
    query: string | null;
    sources: TranscriptSource[];
    passageCount: number;
}
export type TranscriptBlock = TranscriptTextBlock | TranscriptFilesBlock | TranscriptReasoningBlock | TranscriptToolBlock | TranscriptSourcesBlock | TranscriptElicitationBlock | TranscriptUiBlock;
/** Where a question set's selection state is held, and where its settlement is sent. */
export interface ElicitationBlockOptions {
    /** The values a question is showing, or `undefined` while the user has not touched it. */
    picked: (toolCallId: string, questionId: string) => string[] | undefined;
    pick: (toolCallId: string, questionId: string, values: string[]) => void;
    canAnswer: boolean;
    canSkip: boolean;
    answer: (toolCallId: string) => void;
    skip: (toolCallId: string) => void;
    /** Which decision this call is sending, or `null` for none. */
    submitting: (toolCallId: string) => SettleAction | null;
    errorOf: (toolCallId: string) => string | null;
    /** The `code` of the error `errorOf` reports, when the server sent one. */
    errorCodeOf?: (toolCallId: string) => string | null;
}
/** Where an approval decision is sent, and what the last one did. */
export interface ApprovalBlockOptions {
    canApprove: boolean;
    canReject: boolean;
    approve: (toolCallId: string, options?: ApproveOptions, target?: ApprovalTarget) => void;
    reject: (toolCallId: string, target?: ApprovalTarget) => void;
    /** Which decision this call is sending, or `null` for none. */
    submitting: (toolCallId: string) => SettleAction | null;
    errorOf: (toolCallId: string) => string | null;
    /** The `code` of the error `errorOf` reports, when the server sent one. */
    errorCodeOf?: (toolCallId: string) => string | null;
}
export interface BuildBlocksOptions {
    /** Disclosure lookup for a reasoning run; `isStreaming` is the fallback when untouched. */
    isReasoningOpen: (key: string, isStreaming: boolean) => boolean;
    toggleReasoning: (key: string, open?: boolean) => void;
    /**
     * Lift retrieval tool parts out of the tool run into a `sources` block. Off by default, so a
     * renderer wired to draw tool cards keeps receiving them as the tool calls they are.
     */
    sources?: boolean;
    /**
     * Lift a question set out of the tool run into an `elicitation` block. Omitted → it stays a tool
     * card, which is all a host that has nowhere to send an answer could render anyway.
     */
    elicitation?: ElicitationBlockOptions;
    /** Wire approve/reject onto the calls parked on a human. Omitted → they are reported, not actionable. */
    approval?: ApprovalBlockOptions;
    /** Server-declared tool presentations (`useToolCatalog`), for each call's `description`. */
    toolCatalog?: ToolCatalog;
    /**
     * Leave out a failed call the model retried at once with the same tool ({@link retriedCallIds}):
     * the person sees the view the retry drew, not the refusal before it. Default `true`; `false`
     * keeps every attempt (an operator's view).
     */
    hideRetriedFailures?: boolean;
}
/**
 * Walk a message's parts into renderable blocks, buffering consecutive tool parts into one run and
 * consecutive files into one strip. A `data-ui` part becomes a `ui` block. A
 * `data-approval-requested` part is metadata about a call — it is folded into that call's
 * `approval` and takes no position of its own. Other `data-*` parts are dropped rather than guessed
 * at, but they still terminate a tool run — their position in the transcript is meaningful even
 * when their content is not modelled here.
 */
export declare function buildTranscriptBlocks(message: UIMessage, options: BuildBlocksOptions): TranscriptBlock[];
/** The message's prose, joined for the clipboard. Reasoning is excluded — it is not the answer. */
export declare function extractMessageText(parts: UIMessage['parts'] | undefined): string;
export interface UsageSummary extends MessageUsageInfo {
    totalTokens: number;
    /** `$0`, `$0.0123`, `$0.012`, `$1.23` — precision follows magnitude so sub-cent turns stay legible. */
    costLabel: string;
    tokensLabel: string;
}
export declare function describeUsage(usage: MessageUsageInfo): UsageSummary;
export interface TimestampInfo {
    iso: string;
    date: Date;
    /** "just now" / "5 min. ago" / "Mar 3" — see {@link formatRelativeTime}. */
    relative: string;
    absolute: string;
}
/** `null` for an unparseable stamp, so a bad `created_at` renders nothing instead of "Invalid Date". */
export declare function describeTimestamp(createdAt: string | null | undefined): TimestampInfo | null;
/**
 * "just now" within ±10s (server `created_at` can land a few ms ahead of the client clock), a
 * relative phrase up to a week, then a calendar date. Uses Intl.RelativeTimeFormat — no date-fns.
 */
export declare function formatRelativeTime(date: Date): string;
//# sourceMappingURL=model.d.ts.map