import * as react from 'react';
import { ReactNode } from 'react';
import { a as ChatInteractionField, I as InteractionAnswers, C as ChatInteractionStatus, b as ChatInteraction, c as InteractionCancelData, d as ChatSelectField, e as InteractionRequestWire } from '../contract-CEewO6DI.js';
export { f as ChatFreeTextField, g as ComposerAnswerDelivery, h as INTERACTION_CANCEL_EVENT, i as INTERACTION_EVENT, j as INTERACTION_RESOLVED_EVENT, k as InteractionAnswerValue, l as InteractionPersistedPart, N as NoticeKind, m as NoticePersistedPart, P as ParseInteractionAnswersResult, n as ParseInteractionResult, o as canTransitionInteractionStatus, p as cancelStatusFor, q as composerAnswerData, r as composerAnswerDeliveries, s as dedupeQuestionInteractionsByContent, t as fieldAcceptsFreeText, u as interactionFromWireRequest, v as interactionPartKey, w as interactionToPersistedPart, x as isRenderableInteractionKind, y as isSafeInteractionFieldKey, z as isTerminalInteractionStatus, A as noticePart, B as noticePartKey, D as parseInteractionAnswers, E as parseInteractionCancel, F as parseInteractionRequest, G as persistedPartToInteraction, H as questionInteractionContentSignature, J as stampInteractionAnswers } from '../contract-CEewO6DI.js';
import { ChatPlan } from '../plans/index.js';
import { InteractionData } from '@tangle-network/agent-interface';
export { InteractionData, InteractionOutcome, InteractionRequest } from '@tangle-network/agent-interface';
import { M as FileMention, g as ChatMentionPart, b as ChatAttachmentPart, a as ChatAttachmentKind, Q as ChatAttachmentInput } from '../parts-DIKcC1p6.js';
export { f as ChatMentionKind, P as ChatTurnFilePartInput, O as ChatTurnPartInput, N as ChatTurnRequestPayload, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, u as attachmentInputToPart, v as attachmentKindForMime, w as attachmentPartsFromMessageParts, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, z as isChatAttachmentPart, ag as mediaTypeForMentionPath, J as mentionInputToPart, ah as mentionKindForPath, K as mentionPartsFromMessageParts } from '../parts-DIKcC1p6.js';
import { E as EvidenceEntry, g as ExceptionEntry, a as WorkProductProvenance, P as ProfileBacktestSummary, Q as QualityCheck, c as WorkProductPersistedPart, h as WorkProductStatus } from '../types-DeEOhQbv.js';
import { S as StepAgentActivity } from '../agent-activity-C8ZG0F0M.js';
import { F as FlowTrace } from '../flow-types-CJxEmaRy.js';
import { a as ReviewQueueItem, c as ReviewQueueState } from '../queue-DBkulxW1.js';
export { p as parseReviewQueueItem } from '../queue-DBkulxW1.js';
export { S as SandboxTerminalConnection, a as SandboxTerminalConnectionResponse, U as UseSandboxTerminalConnectionOptions, b as UseSandboxTerminalConnectionResult, t as tabTerminalConnectionId, u as useSandboxTerminalConnection } from '../sandbox-terminal-ChNEdHF8.js';
import { CatalogModel } from '../catalog/index.js';
import { Harness } from '../harness/index.js';
export { a as ATTACHMENT_ACCEPT, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse } from '../attachment-validation-yJ6Id4pR.js';
export { a as attachmentPartKey } from '../stream-normalizer-DnuqkZvw.js';

/** Represent durable plan decisions as either approved or rejected */
type DurablePlanDecision = 'approved' | 'rejected';
/** Stable authority receipt for the follow-up turn dispatched by a plan
 * decision. Consumers must make `attachFollowUp` idempotent by `receiptId`;
 * reload and retry deliberately invoke it again. */
interface DurablePlanFollowUpReceipt {
    receiptId: string;
    planId: string;
    revision: number;
    turnId: string;
    state: string;
}
/** Describe the result of a durable plan decision including plan details and pending statuses */
interface DurablePlanDecisionResult {
    plan: ChatPlan;
    followUp?: DurablePlanFollowUpReceipt;
    idempotent: boolean;
    projectionPending?: boolean;
    effectPending?: boolean;
}
/** Define input parameters for making a durable plan decision including optional feedback */
interface DurablePlanDecisionInput {
    planId: string;
    revision: number;
    decision: DurablePlanDecision;
    feedback?: string;
}
/** Define input parameters for retrieving the current durable plan including optional revision number */
interface DurablePlanCurrentInput {
    planId: string;
    revision?: number;
}
/** Define methods to obtain and decide durable plan decisions asynchronously */
interface DurablePlanDecisionClient {
    current: (input: DurablePlanCurrentInput) => Promise<DurablePlanDecisionResult>;
    decide: (input: DurablePlanDecisionInput) => Promise<DurablePlanDecisionResult>;
}
/** Represent errors from DurablePlanClient operations including status, code, and current plan details */
declare class DurablePlanClientError extends Error {
    readonly status: number;
    readonly code?: string | undefined;
    readonly currentPlan?: ChatPlan | undefined;
    constructor(message: string, status: number, code?: string | undefined, currentPlan?: ChatPlan | undefined);
}
/** Define configuration options for creating a durable plan decision client */
interface DurablePlanDecisionClientOptions {
    url: string | ((input: DurablePlanCurrentInput | DurablePlanDecisionInput) => string);
    body?: Record<string, unknown> | ((input: DurablePlanDecisionInput) => Record<string, unknown>);
    fetchImpl?: typeof fetch;
}
/** Browser client for the shared durable-plan route. The route URL and all
 * product routing fields are injected; workspace/session identity is still
 * resolved and authorized on the server. */
declare function createDurablePlanDecisionClient(options: DurablePlanDecisionClientOptions): DurablePlanDecisionClient;
/** Define options to configure durable plan flow with plan, client, and optional callbacks */
interface UseDurablePlanFlowOptions {
    plan: ChatPlan;
    client: DurablePlanDecisionClient;
    /** Must be idempotent by receipt.receiptId. */
    attachFollowUp?: (receipt: DurablePlanFollowUpReceipt) => Promise<void> | void;
    onUpdated?: (plan: ChatPlan) => void;
}
/** Define the result and actions for managing a durable plan flow including decisions, restoration, and error handling */
interface UseDurablePlanFlowResult {
    plan: ChatPlan;
    deciding: DurablePlanDecision | null;
    restoring: boolean;
    error: string | null;
    decide: (decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>;
    restore: () => Promise<DurablePlanDecisionResult | null>;
    clearError: () => void;
}
/** Shared plan decision controller. It coalesces only concurrent attachment
 * attempts; a later retry/restore calls the consumer's idempotent transport
 * again so a lost response cannot strand an already-dispatched follow-up. */
declare function useDurablePlanFlow(options: UseDurablePlanFlowOptions): UseDurablePlanFlowResult;

/**
 * Shared answer-building + submit plumbing for the interaction cards
 * (question, plan). Client-safe, no React: cards own their state, this owns
 * the wire. Lifted from the gtm-agent fork (the most fix-absorbed of the three
 * product copies), including the 30s submit timeout that keeps a dead route
 * from wedging a card in "Submitting…".
 */

/** Status-badge labels for an interaction card. `cancelled`/`expired` read the
 *  same across cards; each card supplies its own verbs for the other states
 *  (a question is answered/declined; a plan is approved/rejected). */
declare function interactionStatusLabels(labels: {
    pending: string;
    answered: string;
    declined: string;
}): Record<ChatInteractionStatus, string>;
/** Terminal-state notes for an interaction card. The expiry/withdrawal lines
 *  share one shape around the card's noun ("question"/"plan"); any extra notes
 *  (e.g. a plan's `declined` revision line) merge on top. */
declare function interactionTerminalNotes(noun: string, extra?: Partial<Record<ChatInteractionStatus, string>>): Partial<Record<ChatInteractionStatus, string>>;
/** Define a record mapping field names to objects with optional selected, text, and custom string arrays or values */
type FieldValues = Record<string, {
    selected?: string[];
    text?: string;
    custom?: string;
}>;
/** Converts acknowledged, persisted answers back into the local field state
 * consumed by the shared cards. Persisted values are authoritative: this is
 * intentionally used only when an interaction carries `answers`, never to
 * guess an answer from the absence of an outstanding sidecar ask. */
declare function fieldValuesFromAnswers(fields: ChatInteractionField[], answers: InteractionAnswers | undefined): FieldValues;
/** The submitted value for one field, or null when it has no answer yet. */
declare function fieldAnswer(field: ChatInteractionField, values: FieldValues): InteractionData[string] | null;
/** All required fields answered → the respond payload; else null (not
 *  submittable yet). Optional unanswered fields are omitted. */
declare function buildAnswerData(fields: ChatInteractionField[], values: FieldValues): InteractionData | null;
/** Determine if a status is late answerable by checking if it is expired or cancelled */
declare function isLateAnswerableStatus(status: ChatInteractionStatus): boolean;
/** Secrets must never leave the sidecar answer channel for the visible chat
 *  transcript, so a secret-bearing ask cannot be late-answered. */
declare function hasSecretField(fields: ChatInteractionField[]): boolean;
/** Renders the late answer as a self-contained chat message: the original
 *  question, its context, and the user's answer(s). */
declare function lateAnswerMessage(interaction: ChatInteraction, data: InteractionData): string;
/** Define the timeout duration in milliseconds for submitting an interaction */
declare const INTERACTION_SUBMIT_TIMEOUT_MS = 30000;
/** Provide the timeout message displayed when the agent cannot be reached during interaction submission */
declare const INTERACTION_SUBMIT_TIMEOUT_MESSAGE = "Could not reach the agent. Try again.";
/** One card submission: which ask, resolved how, with what answers. */
interface InteractionAnswerSubmission {
    id: string;
    outcome: 'accepted' | 'declined';
    data?: InteractionData;
}
/** Resolve the result of an interaction submission indicating success or failure with details */
type InteractionSubmitResult = {
    ok: true;
} | {
    ok: false;
    expired: boolean;
    message: string;
};
/** The cards' only side-effect seam: POST one resolution, report the normalized
 *  outcome. Products bind their route URL + routing fields (workspaceId,
 *  threadId, session path param) via `createInteractionAnswerSubmitter` or a
 *  hand-rolled implementation. */
type SubmitInteractionAnswer = (submission: InteractionAnswerSubmission) => Promise<InteractionSubmitResult>;
/** Extracts the most specific error message a route returned. */
declare function responseErrorMessage(res: Response): Promise<{
    code?: string;
    message: string;
}>;
/** Define options for submitting interaction answers including URL, body, timeout, and fetch implementation */
interface InteractionAnswerSubmitterOptions {
    /** The product's answer route (the POST half of `createInteractionAnswerRoute`).
     *  A function when the URL carries the session (e.g. `/api/sessions/${id}/interactions`). */
    url: string | ((submission: InteractionAnswerSubmission) => string);
    /** Extra routing fields merged into the POST body (e.g. workspaceId, threadId). */
    body?: Record<string, unknown> | ((submission: InteractionAnswerSubmission) => Record<string, unknown>);
    timeoutMs?: number;
    fetchImpl?: typeof fetch;
}
/**
 * Runs a host-supplied submitter under the CARD's own deadline, and always
 * resolves.
 *
 * `createInteractionAnswerSubmitter` aborts its own fetch, but a product may
 * pass any `SubmitInteractionAnswer` — commonly one wrapping an untimed
 * `fetch`. The deadline cannot live only in the submitter, because what gets
 * stuck is the card: its in-flight guard is cleared by the awaited promise
 * settling, so a submitter that never settles leaves that guard set for the
 * life of the instance — "Submitting…" forever, and no answer can be sent
 * again. A submitter with its own shorter timeout simply wins the race.
 *
 * Rejection is normalized too: a submitter that throws would otherwise escape
 * the click handler as an unhandled rejection, leaving the user with a card
 * that silently did nothing. It becomes a visible, retryable message instead.
 */
declare function settleInteractionSubmit(run: () => Promise<InteractionSubmitResult>, timeoutMs?: number): Promise<InteractionSubmitResult>;
/**
 * Builds the `SubmitInteractionAnswer` the cards consume: POSTs
 * `{ ...routingFields, id, outcome, data? }` with an abortable timeout and
 * normalizes the outcome. `expired` is the 410 path — the ask is gone
 * (answered elsewhere, timed out, or the session moved on) and the card must
 * flip to the same dead state a cancel event produces.
 */
declare function createInteractionAnswerSubmitter(options: InteractionAnswerSubmitterOptions): SubmitInteractionAnswer;

type DurableChatCard = {
    kind: 'plan';
    key: string;
    plan: ChatPlan;
} | {
    kind: 'interaction';
    key: string;
    interaction: ChatInteraction;
};
/** Converts persisted/live parts to canonical cards. Legacy interaction-plan
 * cards are suppressed only when their raw part carries an explicit planId and
 * revision matching a durable plan. Identical markdown alone is never proof. */
declare function durableChatCardsFromParts(parts: Array<Record<string, unknown>>): DurableChatCard[];
interface DurableChatCardsProps {
    parts: Array<Record<string, unknown>>;
    canWrite: boolean;
    submitInteraction: SubmitInteractionAnswer;
    decidePlan: (plan: ChatPlan, decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>;
    decidingPlan?: (plan: ChatPlan) => DurablePlanDecision | null;
    planError?: (plan: ChatPlan) => string | null;
    onInteractionResolved?: (id: string, status: Exclude<ChatInteraction['status'], 'pending'>, answers?: InteractionAnswers) => void;
    onLateAnswer?: (message: string) => boolean | void | Promise<boolean | void>;
    /** Fired when the user asks the agent to re-submit an expired/withdrawn
     *  plan card as a new chat turn; receives that card's interaction. Omit to
     *  hide the affordance entirely. */
    onReRequest?: (interaction: ChatInteraction) => boolean | void | Promise<boolean | void>;
    /** Overrides the default re-request button label. */
    reRequestLabel?: string;
    renderMarkdown?: (markdown: string) => ReactNode;
    className?: string;
}
/** Ready-to-embed canonical question/plan card lane for persisted assistant
 * parts. Apps inject transport and styling callbacks instead of rebuilding the
 * lifecycle/render switch. */
declare function DurableChatCards({ parts, canWrite, submitInteraction, decidePlan, decidingPlan, planError, onInteractionResolved, onLateAnswer, onReRequest, reRequestLabel, renderMarkdown, className, }: DurableChatCardsProps): react.JSX.Element | null;

/**
 * Client-side chat-stream consumption — the NDJSON parse loop every agent
 * app's chat UI hand-rolls (and breaks). Normalizes the three line shapes the
 * agent-app chat routes emit:
 *
 *   {kind:'event', event:{type:'text'|'reasoning'|'tool_call'|'usage'|'notice'|'error', ...}}
 *   {kind:'tool_result', toolCallId, toolName, label, outcome}
 *   {type:'turn'|'metadata'|'error'|'turn_status', ...}          (route-level)
 *   {type:'interaction', data:{request}}                         (sidecar ask)
 *
 * Replayed lines carry an extra `seq` — transparently ignored. Works for
 * router-backed and sandbox-backed chats alike: anything producing these
 * lines (live pump, queued follow, resume replay) feeds the same callbacks.
 */

/** Define the structure for a chat tool call including optional ID, name, and arguments object */
interface ChatStreamToolCall {
    toolCallId?: string;
    toolName: string;
    args: Record<string, unknown>;
}
/** Describe the result of a chat stream tool including its outcome and optional metadata fields */
interface ChatStreamToolResult {
    toolCallId?: string;
    toolName?: string;
    label?: string;
    outcome: {
        ok: boolean;
        result?: unknown;
        code?: string;
        message?: string;
    };
}
/** Define callbacks to handle events and data during a chat streaming session */
interface ChatStreamCallbacks {
    onTurnId?: (turnId: string) => void;
    onText?: (delta: string) => void;
    onReasoning?: (delta: string) => void;
    onToolCall?: (call: ChatStreamToolCall) => void;
    onToolResult?: (result: ChatStreamToolResult) => void;
    onUsage?: (usage: {
        promptTokens: number;
        completionTokens: number;
    }) => void;
    onNotice?: (notice: {
        id: string;
        noticeKind: 'warning' | 'auto-declined';
        text: string;
    }) => void;
    onMetadata?: (data: Record<string, unknown>) => void;
    /** Structured detail from a loop-level error event. Fired alongside the
     *  legacy string-only `onErrorEvent` callback. */
    onErrorEventDetail?: (detail: {
        message: string;
        code?: string;
        details?: Record<string, unknown>;
    }) => void;
    /** A loop-level error event (the turn failed server-side). Optional, but the
     *  error never vanishes: when omitted, the message is synthesized into the
     *  transcript via `onText` (rendered by ChatMessages as a text segment) and
     *  logged with `console.error`. */
    onErrorEvent?: (message: string) => void;
    /** A sidecar interaction ask (kind: "question"/"plan"/…). The run is BLOCKED
     *  in the broker until the user answers; a pending ask is "waiting on the
     *  user", not "model working". Optional — a consumer that doesn't wire it
     *  parses the same stream unchanged. */
    onInteraction?: (interaction: ChatInteraction) => void;
    /** A terminal withdrawal/timeout for a previously emitted interaction. */
    onInteractionCancel?: (cancel: InteractionCancelData) => void;
    /** A durable-plan snapshot from any plan lifecycle event. */
    onPlan?: (plan: ChatPlan) => void;
}
/** Represent the result of consuming a chat stream including turn ID and content reception status */
interface ConsumeChatStreamResult {
    turnId: string | null;
    /** True when any text/reasoning/tool activity was received. */
    receivedContent: boolean;
}
/** Parse one NDJSON line into the callbacks. Exposed for tests. */
declare function dispatchChatStreamLine(line: string, cb: ChatStreamCallbacks): {
    turnId?: string;
    receivedContent: boolean;
};
/** Drain one NDJSON body into the callbacks. Throws on transport failure
 *  (caller decides whether to resume). */
declare function consumeChatStream(body: ReadableStream<Uint8Array>, cb: ChatStreamCallbacks): Promise<ConsumeChatStreamResult>;
/** Define options for managing and resuming streaming chat interactions with callbacks */
interface StreamChatOptions {
    /** Start the turn (POST the chat request); must return a streaming Response. */
    start: () => Promise<Response>;
    /** Re-attach to a turn after a transport drop (GET the resume route). */
    resume?: (turnId: string, fromSeq: number) => Promise<Response>;
    callbacks: ChatStreamCallbacks;
    /** Called before a resume replays from 0 so the UI can reset accumulated
     *  turn state (text, reasoning, tool chips). */
    onResetForResume?: () => void;
}
/**
 * Run one chat turn with automatic single-shot resume: if the transport drops
 * mid-turn and the server announced a turnId, reset and replay the buffered
 * turn. Server-side the turn keeps running either way (queued runner).
 */
declare function streamChatTurn(opts: StreamChatOptions): Promise<ConsumeChatStreamResult>;

/** Prompt-part descriptor an uploaded file carries (the upload route's
 *  `UploadedChatFile.part`), echoed back in the turn body on send. Mirrors
 *  `/chat-routes`' wire shape structurally — no server import here. */
interface ComposerFilePart {
    type: 'image' | 'file';
    filename?: string;
    mediaType?: string;
    url?: string;
    path?: string;
    content?: string;
}
interface ComposerFile {
    id: string;
    name: string;
    size?: number;
    kind: 'file' | 'folder';
    /** Number of files inside, for a folder chip. */
    fileCount?: number;
    status: 'pending' | 'uploading' | 'ready' | 'error';
    /** Uploaded part descriptor; set once the upload route returns. Only
     *  `status: 'ready'` files with a part travel on a parts-aware send. */
    part?: ComposerFilePart;
}
interface ChatComposerProps {
    /** Send the trimmed, non-empty message. Attached files travel separately via
     *  `onAttach` + `pendingFiles` (the host consumes and clears them on send).
     *  Optional when `onSendParts` is wired. */
    onSend?: (message: string) => void;
    /** Parts-aware send: receives the trimmed message plus the `part`
     *  descriptors of every `ready` pending file. Takes precedence over
     *  `onSend`; enables file-only sends (empty text, ≥1 ready part). */
    onSendParts?: (message: string, parts: ComposerFilePart[]) => void;
    /** Stop the in-flight turn; shown in place of Send while `isStreaming`. */
    onCancel?: () => void;
    isStreaming?: boolean;
    /** Block input + send (e.g. while restoring). Distinct from `isStreaming`,
     *  which keeps the textarea editable so the next turn can be composed. */
    disabled?: boolean;
    placeholder?: string;
    /** Controlled value. Omit for self-managed internal state (cleared on send). */
    value?: string;
    onValueChange?: (value: string) => void;
    /** Initial text in uncontrolled mode; ignored when `value` is provided. */
    initialValue?: string;
    /** One-shot external prefill: when this becomes a non-null string the
     *  composer adopts it as the draft (replacing any current draft), focuses the
     *  input with the caret at the end, and reports consumption via
     *  `onSeedApplied` so the host can clear its seed state. */
    seed?: string | null;
    onSeedApplied?: () => void;
    /** Inline controls (e.g. `<ModelPicker/>` + `<EffortPicker/>` or
     *  `<AgentSessionControls/>`). Rendered in a row above the input by default. */
    controls?: ReactNode;
    controlsPlacement?: 'above' | 'footer';
    /** Attachments are opt-in: pass `onAttach` to show the attach button, accept
     *  drag-and-drop onto the input, and render `pendingFiles` chips. */
    onAttach?: (files: FileList) => void;
    onAttachFolder?: (files: FileList) => void;
    pendingFiles?: ComposerFile[];
    onRemoveFile?: (id: string) => void;
    accept?: string;
    dropTitle?: string;
    dropDescription?: string;
    /** Cmd/Ctrl+L focuses the input and shows the hint. Default true. */
    focusShortcut?: boolean;
    /** Send button label. Default "Send". */
    sendLabel?: string;
    className?: string;
}
declare function ChatComposer({ onSend, onSendParts, onCancel, isStreaming, disabled, placeholder, value, onValueChange, initialValue, seed, onSeedApplied, controls, controlsPlacement, onAttach, onAttachFolder, pendingFiles, onRemoveFile, accept, dropTitle, dropDescription, focusShortcut, sendLabel, className, }: ChatComposerProps): react.JSX.Element;

type InteractionBadgeVariant = 'outline' | 'default' | 'destructive';
declare function InteractionBadge({ variant, children }: {
    variant: InteractionBadgeVariant;
    children: string;
}): react.JSX.Element;
declare function InteractionActionButton({ variant, onClick, disabled, children, }: {
    variant?: 'primary' | 'outline';
    onClick: () => void;
    disabled?: boolean;
    children: string;
}): react.JSX.Element;
interface QuestionOptionListProps {
    /** Radio/checkbox group name — unique per field so selection is isolated. */
    groupName: string;
    /** Stable prefix for per-option input ids (label htmlFor pairing). */
    idPrefix: string;
    options: ChatSelectField['options'];
    /** Checkbox (multi-select) vs radio (single). */
    multi: boolean;
    selectedValues: string[];
    disabled: boolean;
    onToggle: (value: string) => void;
}
/** The radio/checkbox option rows for a select field. Renders a fragment of
 *  option `<label>` rows so a card keeps its own wrapping layout and appends
 *  its own write-in input. */
declare function QuestionOptionList({ groupName, idPrefix, options, multi, selectedValues, disabled, onToggle, }: QuestionOptionListProps): react.JSX.Element;
interface InteractionQuestionCardProps {
    interaction: ChatInteraction;
    /** Viewer-vs-editor gate: false renders everything read-only. */
    canWrite: boolean;
    /** POST one resolution to the product's answer route (see
     *  `createInteractionAnswerSubmitter`). Never called for late answers. */
    submitAnswer: SubmitInteractionAnswer;
    /** Fired when this card resolves locally (answered, or discovered expired
     *  via a 410) so the stream/route state stays in sync. */
    onResolved?: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: ChatInteraction['answers']) => void;
    /** Delivers a late answer (the ask expired/was withdrawn) as a fresh chat
     *  turn. Return/resolve `false` when the send was rejected so the card stays
     *  retryable. Omit to hide the late-answer affordance entirely. */
    onLateAnswer?: (message: string) => boolean | void | Promise<boolean | void>;
    /** Overrides the kind badge ("Question"). */
    kindLabel?: string;
    /** Overrides the header note ("The agent asked for input"). Set it whenever an
     *  agent is not the one asking — a host that parks on a human at the graph
     *  level is not an agent's mid-run question, and a card that claims otherwise
     *  misreports why the work stopped. */
    sourceNote?: string;
    /** What happens if nobody answers, rendered beside the submit action.
     *
     *  The caller owns both the clock and the copy: this card holds no timer, so a
     *  deadline that counts down re-renders on the caller's cadence rather than
     *  driving one of its own — and the consequence of silence ("the default is
     *  taken", "the run fails") is the host's policy to state, not this card's to
     *  infer. */
    timeoutNote?: ReactNode;
    /** Renders `body` as markdown. Omitted, `body` renders as plain text — so a
     *  host that passes authored markdown without this shows its syntax raw.
     *
     *  `body` ONLY. `title` and every `field.label` stay plain strings: a label is
     *  also the input's accessible name (`aria-label`), which has to be text, and
     *  rendering one as nodes would either break that or silently disagree with
     *  what a screen reader announces. Put prose in `body`.
     *
     *  `interaction.body` is untrusted: it arrives off the wire, written by an
     *  agent or whoever authored the ask. This card never injects HTML, but a
     *  renderer that does is an XSS sink — so return React elements, and sanitize
     *  (DOMPurify or equivalent) if you must produce HTML. */
    renderMarkdown?: (markdown: string) => ReactNode;
    className?: string;
}
declare function InteractionQuestionCard({ interaction, canWrite, submitAnswer, onResolved, onLateAnswer, kindLabel, sourceNote, timeoutNote, renderMarkdown, className, }: InteractionQuestionCardProps): react.JSX.Element;

interface InteractionPlanCardProps {
    interaction: ChatInteraction;
    /** Viewer-vs-editor gate: false renders everything read-only. */
    canWrite: boolean;
    /** POST one resolution to the product's answer route (see
     *  `createInteractionAnswerSubmitter`). */
    submitAnswer: SubmitInteractionAnswer;
    /** Fired when this card resolves locally (approved/rejected, or discovered
     *  expired via a 410) so the stream/route state stays in sync. */
    onResolved?: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>) => void;
    /** Fired when the user asks the agent to re-submit an expired/withdrawn plan
     *  as a new chat turn. Receives the interaction so a callback shared across
     *  cards (e.g. via DurableChatCards) knows which plan fired. Return/resolve
     *  `false` (or throw) to report the send failed and keep the affordance
     *  retryable. Omit to hide it entirely. */
    onReRequest?: (interaction: ChatInteraction) => boolean | void | Promise<boolean | void>;
    /** Overrides the default re-request button label
     *  ("Ask agent to re-submit the plan" — gtm's exact current copy). */
    reRequestLabel?: string;
    /** Renders the plan body (markdown). Falls back to pre-wrapped plain text. */
    renderMarkdown?: (markdown: string) => ReactNode;
    className?: string;
}
declare function InteractionPlanCard({ interaction, canWrite, submitAnswer, onResolved, onReRequest, reRequestLabel, renderMarkdown, className, }: InteractionPlanCardProps): react.JSX.Element;

interface DurablePlanCardProps {
    plan: ChatPlan;
    canWrite: boolean;
    decide: (decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>;
    deciding?: DurablePlanDecision | null;
    error?: string | null;
    renderMarkdown?: (markdown: string) => ReactNode;
    className?: string;
}
declare function DurablePlanCard({ plan, canWrite, decide, deciding, error, renderMarkdown, className, }: DurablePlanCardProps): react.JSX.Element;

/** Manage storage and retrieval of interaction attempt keys by interaction and submission identifiers */
interface InteractionAttemptStore {
    get(interactionId: string, submissionSignature: string): string | null;
    set(interactionId: string, submissionSignature: string, attemptKey: string): void;
    delete(interactionId: string, submissionSignature: string): void;
}
/** Create a session-based store to manage interaction attempts using provided storage and optional namespace */
declare function createSessionInteractionAttemptStore(storage: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>, namespace?: string): InteractionAttemptStore;
/** Create an in-memory store to manage interaction attempts keyed by ID and signature */
declare function createMemoryInteractionAttemptStore(): InteractionAttemptStore;
/** Generate a stable string signature from an interaction answer submission */
declare function interactionSubmissionSignature(submission: InteractionAnswerSubmission): string;
/** Define options for submitting durable interaction answers with attempt tracking and optional key creation */
interface DurableInteractionAnswerSubmitterOptions extends InteractionAnswerSubmitterOptions {
    attempts: InteractionAttemptStore;
    createAttemptKey?: () => string;
}
/** Answer submitter for a durable interaction route. One opaque attempt key is
 * retained for an ambiguous transport/5xx result and reused after reload. A
 * changed answer has a different signature and therefore a new attempt. */
declare function createDurableInteractionAnswerSubmitter(options: DurableInteractionAnswerSubmitterOptions): SubmitInteractionAnswer;

/**
 * useChatInteractions — the interaction-state wiring every consumer of
 * `ChatStreamCallbacks.onInteraction` re-implements: an id-keyed,
 * insertion-ordered list with
 *
 *   - forward-only status transitions (a replayed/stale `pending` never
 *     resurrects a resolved card),
 *   - pending-question content dedupe (a re-emitted duplicate ask never renders
 *     a second card),
 *   - cancel-event application (`interaction.cancel` → cancelled/expired),
 *   - local resolution marking (the card's `onResolved`),
 *   - reload restore from the answer route's GET list (sidecar registry is the
 *     source of truth after a reload),
 *   - turn-end settling (client mirror of the server's finalize pass: a turn
 *     that completed without a cancel was answered; a failed turn can make no
 *     such claim).
 *
 * The reducer functions are pure and exported for non-React consumers/tests;
 * the hook is a thin `useState` shell over them.
 */

/** Insert or update one interaction. A terminal existing entry wins over any
 *  incoming state for the same id; a new pending ask that duplicates another
 *  pending ask's content is dropped. Returns the same array when unchanged. */
declare function upsertChatInteraction(list: ChatInteraction[], interaction: ChatInteraction): ChatInteraction[];
/** Applies an `interaction.cancel` event: only a pending ask moves, to
 *  `expired` (reason:"timeout") or `cancelled`. */
declare function cancelChatInteraction(list: ChatInteraction[], cancel: InteractionCancelData): ChatInteraction[];
/** Marks one ask resolved locally (the card's `onResolved`). Forward-only. */
declare function resolveChatInteraction(list: ChatInteraction[], id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: InteractionAnswers): ChatInteraction[];
/** Settles every still-pending ask when the turn ends: `answered` for a turn
 *  that completed cleanly, `expired` for one that failed. */
declare function terminalizePendingChatInteractions(list: ChatInteraction[], status: Extract<ChatInteractionStatus, 'answered' | 'expired'>): ChatInteraction[];
/** Define modes for restoring chat interactions with legacy or durable strategies */
type ChatInteractionRestoreMode = 'legacy' | 'durable';
/** Define options to control how chat interactions are restored during the restore process */
interface RestoreChatInteractionsOptions {
    /**
     * `legacy` settles pending asks absent from the sidecar list as answered,
     * preserving the pre-durable restore contract. `durable` leaves them
     * pending because absence is ambiguous until `hydrateChatInteractions`
     * applies the durable projection.
     */
    mode?: ChatInteractionRestoreMode;
}
/** Reload restore from the answer route's GET list. Legacy consumers retain
 * the historical absence→answered behavior; durable consumers opt into the
 * ambiguity-preserving mode and apply terminal parts through `hydrate`. */
declare function restoreChatInteractions(list: ChatInteraction[], outstanding: InteractionRequestWire[], options?: RestoreChatInteractionsOptions): ChatInteraction[];
/** Applies transcript/state-store projections after reload. Terminal state and
 * acknowledged answer values enrich an existing pending card without relying
 * on the sidecar's outstanding-list absence. */
declare function hydrateChatInteractions(list: ChatInteraction[], persisted: ChatInteraction[]): ChatInteraction[];
/** Resolve and manage chat interactions with methods to update, cancel, mark resolved, and restore state */
interface UseChatInteractionsResult {
    /** All known interactions, insertion-ordered. */
    interactions: ChatInteraction[];
    /** The asks currently blocking the run (waiting on the user). */
    pending: ChatInteraction[];
    /** Wire to `ChatStreamCallbacks.onInteraction` (and persisted-part replay). */
    upsert: (interaction: ChatInteraction) => void;
    /** Wire to `interaction.cancel` events. */
    applyCancel: (cancel: InteractionCancelData) => void;
    /** Wire to the cards' `onResolved`. */
    markResolved: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: InteractionAnswers) => void;
    /** Wire to the answer route's GET list after a reload/reconnect. */
    restore: (outstanding: InteractionRequestWire[], options?: RestoreChatInteractionsOptions) => void;
    /** Apply durable transcript/state projections after a reload. */
    hydrate: (persisted: ChatInteraction[]) => void;
    /** Settle still-pending asks when the turn ends. */
    terminalizePending: (status: Extract<ChatInteractionStatus, 'answered' | 'expired'>) => void;
    /** Drop everything (thread switch). */
    reset: () => void;
}
/** Resolve options for restoring chat interactions from previous sessions */
type UseChatInteractionsOptions = RestoreChatInteractionsOptions;
/** Manage chat interactions state with upsert, cancel, resolve, and restore capabilities */
declare function useChatInteractions(options?: UseChatInteractionsOptions): UseChatInteractionsResult;

/**
 * `useFileMentions` — the glue a host passes straight into `AgentComposer`'s
 * `mention` prop (`@tangle-network/sandbox-ui#184`) to wire up `@`-file
 * mentions against `createSandboxFileIndexRoute` (`/chat-routes`).
 *
 * Fetches the index once per session from `indexUrl`, refreshes it in the
 * background whenever the popover opens (a `fetchItems` call) if the cached
 * copy has aged past `refreshAfterMs`, and answers every keystroke from an
 * in-memory fuzzy filter — no per-keystroke network round trip. The returned
 * `refresh()` lets a caller force a re-fetch immediately instead of waiting
 * on `refreshAfterMs` — e.g. right after the agent creates a file mid-session.
 *
 * `MentionItem`/the `mention` prop shape mirror the FROZEN contract from
 * sandbox-ui#184 structurally (no import: `/web-react` stays dependency-free
 * beyond React, and `@tangle-network/sandbox-ui` is an optional peer).
 */

/** Mirrors sandbox-ui#184's `MentionItem` — the atomic pill's payload. For a
 *  file mention, `id` is the workspace-relative path (the pill's stable
 *  identity and the `@<id>` serialization sandbox-ui uses to round-trip
 *  `value`), `label` is the display name, and `detail` carries the full path
 *  for the popover row's secondary line. */
interface MentionItem {
    id: string;
    label: string;
    detail?: string;
    kind?: string;
}
/** Mirrors sandbox-ui#184's `AgentComposerProps['mention']` shape — plug the
 *  hook's `mention` return value straight into that prop. */
interface ComposerMentionProp {
    trigger?: string;
    fetchItems(query: string): Promise<MentionItem[]>;
    onMentionsChange?(mentions: MentionItem[]): void;
    renderItem?(item: MentionItem): ReactNode;
    emptyText?: string;
}
/**
 * Ranks `files` against `query` (case-insensitive), capped to `limit`:
 * name-prefix matches first, then name-substring, then path-substring.
 * Within a tier, shorter names sort first (the more specific match), then
 * alphabetically by path for a stable order. An empty query returns the
 * first `limit` entries unranked — the popover's default list before typing.
 * Pure and dependency-free (no fuzzy-match library) so it's cheap enough to
 * re-run on every keystroke against a 10k-entry index.
 */
declare function rankFileMentions(files: readonly FileMention[], query: string, limit: number): FileMention[];
/** Max popover results per query — enough to show a useful spread of matches
 *  without pushing the fuzzy-filtered list past what a popover can usefully
 *  render in one screen. */
declare const DEFAULT_MENTION_LIMIT = 20;
/** How long a `ready` index is served before a background refetch — long
 *  enough that a full session's worth of popover opens don't repeatedly hit
 *  the index endpoint, short enough that a stale listing doesn't linger too
 *  far past workspace file changes. Callers who need the index current right
 *  now (e.g. just after the agent creates a file) call `refresh()` instead of
 *  waiting on this window. */
declare const INDEX_REFRESH_AFTER_MS: number;
/** Popover empty-state copy for a `ready` index whose query matched nothing.
 *  Loading/warming/error states have their own copy — see `emptyTextFor`. */
declare const DEFAULT_MENTION_EMPTY_TEXT = "No matching files";
/** Define options for configuring file mention fetching, caching, and display behavior */
interface UseFileMentionsOptions {
    /** GET endpoint returning `FileIndexResponse` (a `createSandboxFileIndexRoute`). */
    indexUrl: string;
    /** Max popover results per query. Default {@link DEFAULT_MENTION_LIMIT}. */
    limit?: number;
    /** How long a `ready` index is served without a background refetch.
     *  Default {@link INDEX_REFRESH_AFTER_MS}. */
    refreshAfterMs?: number;
    /** `fetch` override for tests / non-global-fetch hosts. Default `fetch`. */
    fetchImpl?: typeof fetch;
    /** Text shown in the popover's empty state once the index is loaded and
     *  the query matched nothing. Default {@link DEFAULT_MENTION_EMPTY_TEXT}. */
    emptyText?: string;
}
/** Provide properties and methods to manage and refresh file mentions in a composer interface */
interface UseFileMentionsResult {
    /** Spread straight into `AgentComposer`'s `mention` prop. */
    mention: ComposerMentionProp;
    /** The files currently referenced by mentions in the composer's value —
     *  the send-body list (map through `fileMentionsToParts`). */
    mentions: FileMention[];
    /** Drop all currently-referenced mentions (e.g. after a successful send). */
    clearMentions: () => void;
    /** Force a re-fetch of the index right now, ignoring `refreshAfterMs` — for
     *  example right after the agent creates a file mid-session, so the next
     *  popover open sees it. Dedupes against an already-in-flight load rather
     *  than firing a second request. */
    refresh: () => Promise<void>;
}
/** Resolve and manage file mention data with configurable fetching and state handling */
declare function useFileMentions(options: UseFileMentionsOptions): UseFileMentionsResult;

/**
 * Transcript-side counterpart to the composer's `@`-mention primitive
 * (sandbox-ui#184). The composer serializes a picked file into the message
 * text as `@<path>`; this module is the exact inverse — it finds those tokens
 * again in a PERSISTED message and splits the text so a renderer can draw a
 * pill where the user typed one and leave the rest as prose.
 *
 * Pure and product-agnostic: no React, no fetch, no DOM. The only input beyond
 * the text is the message's OWN mention parts, so one message can never render
 * a pill for a path another message mentioned.
 *
 * `ChatMentionPart` and the runtime helpers `mentionInputToPart` /
 * `mentionPartsFromMessageParts` are re-exported here from `../chat-store/parts`
 * directly (not the `/chat-store` barrel), so a browser bundle gets the mention
 * vocabulary and its converters without importing `/chat-store`, whose barrel
 * pulls the drizzle peer.
 */

/** One run of a segmented message: literal prose, or a matched mention with
 *  the part that produced it. `text` for a mention segment is the token as it
 *  appears in the message (`@<path>`), so a renderer that ignores `part` still
 *  reproduces the original string exactly. */
interface MentionTextSegment {
    type: 'text' | 'mention';
    text: string;
    part?: ChatMentionPart;
}
/**
 * Split a message's text into plain-text and mention segments by matching
 * `@<path>` runs against that message's own mention parts.
 *
 * Only a part whose exact `@<path>` token appears in `content`, at a token
 * boundary on both sides, counts as a match; everything else — including
 * unrelated `@` text — passes through as plain text untouched. When two parts'
 * tokens both match at the same position (one path a prefix of another), the
 * LONGEST token wins, so nested-looking paths split at the right boundary.
 *
 * Returns the matched parts alongside the segments: a caller that also renders
 * a fallback chip row can drop the chip for anything now shown inline and keep
 * it only for mentions the text does not actually contain (a restored draft, a
 * queued message whose text was edited).
 */
declare function segmentMentionContent(content: string, parts: ReadonlyArray<ChatMentionPart>): {
    segments: MentionTextSegment[];
    matched: Set<ChatMentionPart>;
};

/**
 * Renders a message's attachment parts (images + files) as thumbnails and
 * download chips — the transcript-side counterpart to `ChatComposer`'s
 * staged-upload chips. Ported from gtm-agent's `chat-attachment-parts.tsx`
 * onto agent-app's RAW-BYTES download contract: the host supplies
 * `resolveFileUrl(part)`, a URL that serves the attachment's raw bytes
 * directly, so this module never parses a JSON `{file:{blobUrl,body}}`
 * envelope or decodes a `[base64]` marker the way gtm's vault route did.
 *
 * No icon-library or primitives dependency (`ChatComposer`'s house style):
 * the loading skeleton is an inline `animate-pulse` span and the few glyphs
 * are inline SVGs.
 */

/** Typed outcome of fetching one attachment's raw bytes. Callers must check
 *  `ok` before touching `blob` — a failed fetch never produces a blank
 *  render, it produces a visible error state. */
type AttachmentFileResult = {
    ok: true;
    blob: Blob;
} | {
    ok: false;
    message: string;
};
declare function __resetAttachmentFileCacheForTests(): void;
/** Fetches (and caches) the raw bytes behind one attachment url. Concurrent
 *  callers for the SAME url dedupe to one in-flight fetch. Only a successful
 *  settlement stays cached — evicting failures means a remount or click-retry
 *  after a transient error issues a fresh fetch. */
declare function loadAttachmentFile(url: string, fetchFile?: (url: string) => Promise<Response>): Promise<AttachmentFileResult>;
/** Drives an anchor-click download from an already-resolved blob. Returns a
 *  typed outcome rather than throwing — a chip that fails to synthesize the
 *  download must show the failure, not silently no-op. */
declare function triggerAttachmentDownload(name: string, blob: Blob): {
    ok: true;
} | {
    ok: false;
    message: string;
};
interface MessageAttachmentsProps {
    parts: ChatAttachmentPart[];
    /** URL serving the attachment's RAW bytes. */
    resolveFileUrl: (part: ChatAttachmentPart) => string;
    /** Row alignment — a user-bubble attachment row is right-aligned by
     *  default; pass `"start"` for an assistant-turn attachment, which sits
     *  inline with the rest of the transcript. */
    justify?: 'start' | 'end';
    /** Override the fetch used to load an attachment's bytes. Default:
     *  `fetch(url, { credentials: 'same-origin' })`. */
    fetchFile?: (url: string) => Promise<Response>;
}
/** Renders a message's attachment parts as a row of image thumbnails and file
 *  chips. `null` when there are none, so callers can render unconditionally
 *  without an extra length check. */
declare function MessageAttachments({ parts, resolveFileUrl, justify, fetchFile }: MessageAttachmentsProps): ReactNode;

/**
 * `useComposerAttachments` — the composer's staged-upload lifecycle: validate
 * selected/dropped/pasted files against the shared limits (the SAME
 * `sniffBinary`/`checkAttachmentType`/size-cap vocabulary the store-backed
 * upload route enforces server-side, `../chat-routes/attachment-validation`
 * + `../chat-routes/binary-sniff`), upload each accepted file with one POST
 * request per file (so a single failure never poisons the batch), and track
 * every file's status so a host composer can render chips and gate sending.
 *
 * Ported from gtm-agent's `src/components/composer-attachments.tsx`
 * (gtm#584/#592/#593 hardened the sniff gate and batch semantics this leans
 * on), de-gtm-ified:
 *   - the hardcoded `/api/vault/upload?workspaceId=` URL becomes
 *     `uploadUrl`/`buildUploadRequest` (the latter wins — it hands back both
 *     the URL and a `RequestInit` override, e.g. an auth header);
 *   - `sonner` toasts become `onReject` (client pre-validation, never hits the
 *     network) and `onError` (a request that reached the server and failed);
 *   - the sandbox-ui `validateComposerFiles` import becomes a small
 *     accept-list matcher re-implemented locally (`isAcceptedFileType`,
 *     mirroring its `accept`-string matching byte-for-byte) — this module
 *     stays free of the sandbox-ui peer;
 *   - the response is expected to be `{ files: ChatAttachmentInput[] }` (full
 *     server-authoritative descriptors — size/mediaType/kind — not gtm's
 *     `{path, name}`), so `references` is a verbatim pass-through with no
 *     client recompute;
 *   - `workspaceId`'s truthiness gate becomes `enabled` (default `true`).
 *
 * Import-free beyond React + the browser-safe `/chat-routes` validation core:
 * this module ships through `/web-react` into client bundles
 * (`tests/browser-safe-subpaths.test.ts` walks the graph), so nothing here
 * may reach a Node builtin, `sandbox-ui`, or an engine package.
 */

/** Define options for configuring file upload behavior and handling in a composer component */
interface UseComposerAttachmentsOptions {
    /** Simple upload target: every file POSTs here. Ignored when
     *  `buildUploadRequest` is provided. */
    uploadUrl?: string;
    /** Full request-building seam (auth headers, per-file routing, …) — wins
     *  over `uploadUrl` when both are set. */
    buildUploadRequest?: (args: {
        file: File;
        name: string;
        form: FormData;
    }) => {
        url: string;
        init?: Omit<RequestInit, 'body' | 'signal'>;
    };
    /** Client pre-validation rejections — a file that never reaches the
     *  network (bad type, over a size cap, over count, disallowed kind). */
    onReject?: (reason: string, file?: File) => void;
    /** A file that reached the upload endpoint and failed (HTTP error,
     *  transport error, malformed response). */
    onError?: (reason: string) => void;
    limits?: {
        maxCount?: number;
        maxBinaryBytes?: number;
        maxTextBytes?: number;
        maxTotalBytes?: number;
    };
    /** Attachment kinds accepted, checked against the sniffed content's
     *  mime. Default: both (`['image', 'file']` — i.e. no restriction). */
    allowedKinds?: ChatAttachmentKind[];
    /** `<input accept>`-style gate for the file picker/drop/paste path.
     *  Default {@link ATTACHMENT_ACCEPT}. */
    accept?: string;
    /** When `false`, `addFiles` rejects every call via `onReject` (and
     *  `blockReason` explains why) instead of staging anything — the
     *  replacement for gtm's `workspaceId`-truthiness gate (e.g. no workspace
     *  loaded yet). Default `true`. */
    enabled?: boolean;
}
/** Provide staged file chips, ready attachments, and methods to add, retry, or drop composer files */
interface UseComposerAttachmentsResult {
    /** Chip models for `ChatComposer`'s `pendingFiles` prop, one per staged
     *  file — `kind` is always `'file'` (agent-app's `ComposerFile.kind`
     *  discriminates file-vs-folder chips, not attachment media type). */
    composerFiles: ComposerFile[];
    /** Ready-to-send attachment descriptors — only files whose upload
     *  succeeded, straight from the server's response (no recompute). Feed
     *  this into `ChatTurnRequestPayload.attachments`. */
    references: ChatAttachmentInput[];
    /** Validate + stage + upload the given files, one request per file. */
    addFiles: (files: File[] | FileList) => Promise<void>;
    /** Re-upload a failed entry using its retained `File`. */
    retry: (id: string) => void;
    /** Drop one staged entry, aborting its upload and revoking its preview. */
    removeAttachment: (id: string) => void;
    /** Forget every staged entry (call after a successful send). */
    clear: () => void;
    /** True while any file is still pending or uploading. */
    hasPending: boolean;
    /** True while any file failed to upload. */
    hasError: boolean;
    /** Why a send is blocked, or `null` when the queue is clean. */
    blockReason: string | null;
}
/**
 * Owns the composer's attachment lifecycle: validate selected/dropped/pasted
 * files against the shared limits, upload each accepted file to the
 * product's store (one request per file), and track every file's status so
 * the composer can render chips and gate sending.
 *
 * Failures surface loud — a rejected file calls `onReject` and is never
 * uploaded; a failed upload calls `onError` and leaves an error chip the user
 * can retry or remove. `references` only ever contains files whose upload the
 * server actually confirmed.
 */
declare function useComposerAttachments(options: UseComposerAttachmentsOptions): UseComposerAttachmentsResult;

/**
 * Provider brand marks — real logo path data (simple-icons / SVG Logos, both
 * CC0) inlined so the picker shows actual provider identity instead of
 * colored-initial monograms. Providers without a usable mark fall back to a
 * tinted monogram chip. Aliases (z-ai/zai, moonshot/moonshotai, deepseek_ai)
 * normalize to one entry.
 */

interface ProviderLogoProps {
    provider?: string;
    size?: number;
}
/** Real brand mark when we have one; tinted monogram otherwise. */
declare function ProviderLogo({ provider, size }: ProviderLogoProps): ReactNode;

/**
 * Smooth text reveal — turns chunky network deltas into a continuous
 * typewriter paint. Streamed turns arrive in 100-500ms slabs (model burst,
 * flush windows, replay polls); revealing characters at an adaptive rate
 * makes the same bytes read as top-tier streaming. The rate scales with the
 * backlog so the reveal never falls behind the stream — it crawls when caught
 * up and sprints when a burst lands (e.g. a reasoning summary arriving all at
 * once still *types out* instead of popping in).
 */
/** Define configuration options for controlling smooth text reveal animation rates */
interface SmoothRevealOptions {
    /** Baseline reveal rate when nearly caught up. Default 90 chars/s. */
    baseCharsPerSecond?: number;
    /** Extra chars/s per backlog character — the catch-up pressure. Default 5. */
    catchUpPerChar?: number;
    /** Hard ceiling so giant bursts still animate. Default 2400 chars/s. */
    maxCharsPerSecond?: number;
}
/** Pure reveal step: how many characters should be visible after `dtMs`.
 *  Exposed for tests; the hook is a thin rAF wrapper around it. */
declare function nextRevealCount(shown: number, targetLength: number, dtMs: number, opts?: SmoothRevealOptions): number;
/**
 * Animate `target` text into view. While `enabled`, the returned string grows
 * smoothly toward `target` (which may itself keep growing); when `enabled` is
 * false the full text returns immediately (history, completed turns). A
 * target that is not an extension of the revealed prefix (new message) resets
 * the reveal.
 */
declare function useSmoothText(target: string, enabled: boolean, opts?: SmoothRevealOptions): string;

type ActivityTone = 'live' | 'ok' | 'error' | 'neutral';
/** Map a delegation status (free-form string on the wire) to a render tone. */
declare function activityTone(status: string): ActivityTone;
/** "$0.4000" under a cent shows 4 decimals; null when unknown/zero. */
declare function formatActivityCost(costUsd?: number): string | null;
/** "8s" / "2m 05s" / "1h 12m"; null when unknown. */
declare function formatActivityDuration(durationMs?: number): string | null;
/** A delegation record on the cross-context surface; `missionRef` links a
 *  promoted delegation back to the mission/step that spawned it. */
interface AgentActivityRecord extends StepAgentActivity {
    missionRef?: {
        missionId: string;
        stepId?: string;
        label?: string;
    };
}
interface AgentActivityPage {
    items: AgentActivityRecord[];
    /** Opaque continuation token; absent ⇒ no further pages. */
    nextCursor?: string;
}
/**
 * Fold a fetched page into the held rows: dedupe by `taskId` with the
 * incoming row winning (a refresh re-fetches the head page, so newer
 * snapshots of in-flight runs replace stale ones), newest `startedAt` first.
 */
declare function mergeActivityPages(existing: AgentActivityRecord[], incoming: AgentActivityRecord[]): AgentActivityRecord[];
interface WaterfallRow {
    name: string;
    kind: 'pipeline' | 'model' | 'tool';
    /** Bar geometry as percentages of the trace's total span. */
    offsetPct: number;
    widthPct: number;
    durationLabel: string;
    approx: boolean;
    /** False only when the span's meta carries an explicit failure. */
    ok: boolean;
}
/** Project a FlowTrace into proportional bar geometry for {@link FlowWaterfall}. */
declare function waterfallLayout(trace: FlowTrace): WaterfallRow[];
interface FlowWaterfallProps {
    trace: FlowTrace;
}
/** Compact proportional waterfall over a FlowTrace — span name, bar, duration
 *  per row; total + cost in the footer. */
declare function FlowWaterfall({ trace }: FlowWaterfallProps): react.JSX.Element | null;
interface MissionActivityLaneProps {
    /** The step's delegated-run snapshot (`MissionStepState.agentActivity`). */
    activity: StepAgentActivity[];
    /** Epoch ms origin for the expanded waterfall — usually the step start. */
    startedAt?: number;
    /** Wall clock for extending in-flight runs on the waterfall. */
    nowMs?: number;
}
/**
 * Collapsed sub-rows under a mission step — one row per delegated run —
 * expanding to the step's waterfall. Renders nothing for an empty lane.
 */
declare function MissionActivityLane({ activity, startedAt, nowMs }: MissionActivityLaneProps): react.JSX.Element | null;
interface AgentActivityPanelProps {
    /** Data port — page through the product's delegation records. Called with
     *  no cursor on mount/refresh, with `nextCursor` for older pages. */
    fetchActivity: (cursor?: string) => Promise<AgentActivityPage>;
    /** Render the mission link for a promoted delegation (chip, anchor, router
     *  Link — the product's routing, not ours). */
    renderMissionRef?: (ref: NonNullable<AgentActivityRecord['missionRef']>, record: AgentActivityRecord) => ReactNode;
    title?: string;
    emptyLabel?: string;
}
/**
 * The standalone cross-context delegation surface: every agent run the
 * product journaled, mission-spawned or not, with status, cost, drill-in, and
 * a mission link slot for promoted delegations.
 */
declare function AgentActivityPanel({ fetchActivity, renderMissionRef, title, emptyLabel }: AgentActivityPanelProps): react.JSX.Element;

/** Human label for a queue state. */
declare function reviewQueueStateLabel(state: ReviewQueueState): string;
/** Human label for a work-product status. */
declare function workProductStatusLabel(status: WorkProductStatus): string;
/** Every persisted work-product anchor on one message, re-validated. */
declare function workProductPartsFromMessageParts(parts: ReadonlyArray<Record<string, unknown>> | null | undefined): WorkProductPersistedPart[];
/** Properties for the transcript anchor card rendered in chat */
interface WorkProductCardProps {
    part: WorkProductPersistedPart;
    /** Open the work product (queue detail / pane) — the card's only action. */
    onOpen?: (part: WorkProductPersistedPart) => void;
    className?: string;
}
/** The chat transcript anchor card for one work-product version — a compact,
 *  system-authored pointer, not a parallel review surface. */
declare function WorkProductCard({ part, onOpen, className }: WorkProductCardProps): react.JSX.Element;
/** One fetched page of queue items with an optional continuation cursor */
interface ReviewQueuePage {
    items: ReviewQueueItem[];
    /** Opaque continuation token; absent ⇒ no further pages. */
    nextCursor?: string;
}
/** Properties for the review queue panel over a fetch data port */
interface ReviewQueuePanelProps {
    /** Data port — fetch one page of queue items (the projection's output,
     *  re-validated here at the JSON boundary). */
    fetchQueue: (cursor?: string) => Promise<ReviewQueuePage>;
    /** Open one queue item (navigate to its thread / detail pane). */
    onSelect?: (item: ReviewQueueItem) => void;
    title?: string;
    emptyLabel?: string;
    className?: string;
}
/** Merge a fetched page into held rows: dedupe by scopeKey, incoming wins,
 *  newest updatedAt first. Exported for tests. */
declare function mergeReviewQueuePages(existing: ReviewQueueItem[], incoming: ReviewQueueItem[]): ReviewQueueItem[];
/** The workspace review queue — cursor-paged, refreshable, selection via
 *  callback. States, counts, and provenance render from the projection; the
 *  panel holds no domain logic. */
declare function ReviewQueuePanel({ fetchQueue, onSelect, title, emptyLabel, className, }: ReviewQueuePanelProps): react.JSX.Element;
/** Properties for the evidence lineage table with source click-through */
interface EvidenceLineageTableProps {
    evidence: readonly EvidenceEntry[];
    /** Resolve one entry's source document to an openable URL (signed
     *  object-store/vault download) — the `resolveFileUrl` pattern. Absent →
     *  sources render as plain refs without links. */
    resolveSourceUrl?: (entry: EvidenceEntry) => string;
    className?: string;
}
/** Target → claim → source lineage rows: the "every material value traceable
 *  to source evidence" surface. Each row names the artifact target, the
 *  claim, and the source document location supporting it. */
declare function EvidenceLineageTable({ evidence, resolveSourceUrl, className }: EvidenceLineageTableProps): react.JSX.Element;
/** Properties for the severity-badged exception list */
interface ExceptionListProps {
    exceptions: readonly ExceptionEntry[];
    className?: string;
}
/** Severity-badged exception rows with resolution state. */
declare function ExceptionList({ exceptions, className }: ExceptionListProps): react.JSX.Element;
/** Properties for the pass/fail quality check list */
interface QualityCheckListProps {
    checks: readonly QualityCheck[];
    className?: string;
}
/** Pass/fail quality-check rows tagged with their source (agent self-report,
 *  platform gate, judge ensemble). */
declare function QualityCheckList({ checks, className }: QualityCheckListProps): react.JSX.Element;
/** Properties for the provenance stamp with the optional backtest slot */
interface ProvenanceStampProps {
    provenance: Pick<WorkProductProvenance, 'profileHash' | 'runId' | 'servingModels'> & Partial<Pick<WorkProductProvenance, 'costUsd' | 'producedAt'>>;
    /** The product-resolved backtest summary for this profile hash. When its
     *  trust gate failed, the composite renders as "quality: unverified" —
     *  never a naked number. */
    backtest?: ProfileBacktestSummary;
    className?: string;
}
/** The audit line a reviewer approves against: which configuration produced
 *  this document, what served it, and how that configuration measured on its
 *  backtest. */
declare function ProvenanceStamp({ provenance, backtest, className }: ProvenanceStampProps): react.JSX.Element;

/**
 * `SeatPaywall` — the shared "unlock this product" screen every agent app
 * shows when a user has no active seat and has spent past the free tier. One
 * component, adopted by all five products (gtm / creative / tax / legal /
 * insurance) in ~2 lines.
 *
 * Copy contract (design §6.8): the included monthly AI usage is framed as a
 * BENEFIT the buyer receives — never the ratio, never the word "margin", never
 * "we debit 50%". Surface the allowance, hide the economics.
 *
 * Styling contract matches the rest of `web-react`: Tailwind classes over the
 * shared design tokens (`bg-card`, `border-border`, `text-muted-foreground`,
 * `bg-primary`, …); glyphs are inline SVGs; no icon or UI library.
 */

interface SeatPaywallProps {
    /** Human product name shown in the headline, e.g. "Creative". */
    product: string;
    /** Fired when the user clicks the unlock CTA — route them to checkout. When
     *  it returns a promise the button shows a pending state and ignores repeat
     *  clicks until it settles (no double-charge on a slow checkout open). */
    onCheckout: () => void | Promise<void>;
    /** Monthly seat price in whole dollars. Default 100. */
    priceUsd?: number;
    /** Included monthly AI usage in whole dollars. Default 50. */
    includedUsageUsd?: number;
    /** Optional one-line value prop under the headline. */
    tagline?: string;
    /** CTA label. Default "Unlock {product}". */
    ctaLabel?: string;
    /** Value-prop bullets. Default = product/usage-derived only; pass your own to
     *  supply product-specific value props (the shell bakes no GTM copy). */
    benefits?: ReactNode[];
    /** Optional fine print under the CTA (e.g. "Cancel anytime."). Omitted by default. */
    footnote?: ReactNode;
}
/**
 * Centered card paywall. The price line reads
 * "$100/mo · includes $50/mo of AI usage" so the included allowance anchors the
 * value without ever exposing the ratio.
 */
declare function SeatPaywall({ product, onCheckout, priceUsd, includedUsageUsd, tagline, ctaLabel, benefits, footnote, }: SeatPaywallProps): ReactNode;

/**
 * Keyboard + pointer model for a trigger-and-popover pair, dependency-free.
 * Outside-mousedown and Escape both close; Escape also returns focus to the
 * trigger so keyboard users aren't dropped at the top of the document. The
 * returned `triggerProps` carry the ARIA contract (`aria-haspopup`/
 * `aria-expanded`); spread them onto the trigger button.
 */
declare function usePopover(open: boolean, setOpen: (open: boolean) => void): {
    containerRef: react.RefObject<HTMLDivElement | null>;
    triggerRef: react.RefObject<HTMLButtonElement | null>;
    triggerProps: {
        ref: react.RefObject<HTMLButtonElement | null>;
        'aria-haspopup': true;
        'aria-expanded': boolean;
    };
};
/**
 * Guard an async action against double-submit. `run` ignores re-entrant calls
 * while a promise is in flight and flips `pending` so the caller can disable
 * the control — the fix for double-charge / double-approve on a slow network.
 * Settles (success or throw) before clearing, and no-ops state updates after
 * unmount.
 */
declare function usePending(): {
    pending: boolean;
    run: (action: () => void | Promise<void>) => void;
};
interface ModelPickerProps {
    value: string;
    onChange: (id: string) => void;
    /** Catalogue models — from `GET`ing the app's catalogue route (see
     *  `runtime/model-catalog`), plus any product-specific entries appended. */
    models: CatalogModel[];
    loading?: boolean;
    /** Render a provider logo/badge; default is a generic sparkle. */
    renderProviderBadge?: (provider: string) => ReactNode;
    /** Section label for `featured` models. */
    recommendedLabel?: string;
    /** Pin a labeled section to the TOP of the list (above Recommended) for the
     *  models a product wants surfaced first — e.g. a tuner app's own fine-tuned
     *  models (`{ label: 'Your Fine-Tuned Models', match: (m) => m.provider === 'tuner' }`).
     *  Matching models are shown only in this section, not duplicated below. */
    priorityGroup?: {
        label: string;
        match: (model: CatalogModel) => boolean;
    };
}
/**
 * Searchable model picker pill + popover: a featured/recommended section
 * first, then per-provider groups in catalogue order (the server already
 * sorts providers by tier).
 */
declare function ModelPicker({ value, onChange, models, loading, renderProviderBadge, recommendedLabel, priorityGroup }: ModelPickerProps): react.JSX.Element;
/** One reasoning-budget level: the engine `id` is unchanged (the value the
 *  product sends to the loop); only the user-facing `label` is renamed to the
 *  plainer "how hard should it think" vocabulary from docs/product-surfaces.md.
 *  `low`→Quick, `medium`→Standard, `high`→Extended. The mapping is overridable
 *  via `EffortPickerProps.levels`, so a product can relabel without losing the
 *  ids the runtime expects. */
interface EffortLevel {
    id: string;
    label: string;
}
declare const DEFAULT_EFFORT_LEVELS: readonly EffortLevel[];
interface EffortPickerProps {
    value: string;
    onChange: (id: string) => void;
    /** Selectable levels (engine id + user-facing label). Defaults to the plain
     *  "Thinking" vocabulary; override to relabel without changing the ids the
     *  runtime receives. */
    levels?: readonly EffortLevel[];
    /** Prefix shown before the active level on the pill — the "what is this"
     *  context the bare value lacked. Default "Thinking". Pass '' to hide it. */
    label?: string;
}
/** Thinking-budget selector pill, styled to match {@link ModelPicker}. Show
 *  it only when the selected model `supportsReasoning`. "Thinking" is the
 *  plain-English name for what was internally called "effort". */
declare function EffortPicker({ value, onChange, levels, label }: EffortPickerProps): react.JSX.Element;

interface AgentSessionControlsProps {
    /** Catalog models — canonical provider-prefixed ids. */
    models: CatalogModel[];
    modelsLoading?: boolean;
    /** Selected canonical model id. */
    model: string;
    onModelChange(modelId: string): void;
    /** Current harness; harness↔model coherence is enforced on every change. */
    harness: Harness;
    onHarnessChange(harness: Harness): void;
    /** Harnesses to offer; defaults to the labeled set. */
    availableHarnesses?: ReadonlyArray<Harness>;
    /** Reasoning-effort value + setter. Shown only when the selected model
     *  `supportsReasoning`, matching `EffortPicker`'s guidance. */
    effort: string;
    onEffortChange(effort: string): void;
    /**
     * `inline` (default): model, harness, effort side by side — the prior
     * behavior. `compact`: model inline, harness + effort behind a gear popover.
     */
    layout?: 'inline' | 'compact';
    /** Hide the harness control entirely (single-harness products). */
    showHarness?: boolean;
    renderProviderBadge?: (provider: string) => ReactNode;
    className?: string;
}
declare function AgentSessionControls(props: AgentSessionControlsProps): react.JSX.Element;

/** Describe metrics related to a chat message including model, token counts, and duration */
interface ChatMessageMetrics {
    modelUsed?: string;
    promptTokens?: number;
    completionTokens?: number;
    durationMs?: number;
}
/** "$0.0042" from token counts × catalogue per-token pricing; null when unknown. */
declare function formatModelCost(msg: ChatMessageMetrics, models: CatalogModel[]): string | null;
/** "38 tok/s" from completion tokens over first-token→end duration; null when unknown. */
declare function formatTokensPerSecond(msg: ChatMessageMetrics): string | null;
/** One step of a retained tool run (e.g. a sandbox command + its output). */
interface ToolRunStep {
    at: string;
    label: string;
    detail?: string;
    status?: 'ok' | 'error';
}
/** A retained tool run keyed by the parent message's toolCallId. The product
 *  persists these server-side (fail-closed: only ids its own loop created)
 *  and serves them to the drill-in panel. */
interface ToolRunRecord {
    toolCallId: string;
    toolName: string;
    title: string;
    status: 'running' | 'complete' | 'error';
    steps: ToolRunStep[];
}
/** Define properties required to run a drill and handle its closure event */
interface RunDrillInProps {
    run: ToolRunRecord;
    onClose: () => void;
}
/**
 * Readonly side panel showing a retained tool run's transcript — the
 * "drill into what the sandbox actually did" view. Follow-ups happen in the
 * main chat, never here.
 */
declare function RunDrillIn({ run, onClose }: RunDrillInProps): react.JSX.Element;
/** Describe the structure and state of a tool call within a chat interaction */
interface ChatToolCallInfo {
    id: string;
    name: string;
    status: 'running' | 'done' | 'error';
    /** The call arguments, captured from the tool_call event — shown in the
     *  expanded card so users see exactly what the agent invoked. */
    args?: Record<string, unknown>;
    /** The tool outcome (`{ok, result}` shape). When `result.status` is
     *  'queued_for_approval' the card renders the approval state. */
    result?: unknown;
}
/** Extract `{proposalId, status}` from a tool outcome when it is a proposal
 *  awaiting human approval; null otherwise. */
declare function pendingApprovalOf(call: ChatToolCallInfo): {
    proposalId: string;
} | null;
/** One ordered piece of an assistant turn: a run of answer text, or a tool
 *  call, in the sequence the agent emitted them. A message carrying `segments`
 *  is rendered in order — interleaving text and tool chips — so the agent's
 *  pre- and post-tool reasoning reads chronologically instead of as one text
 *  blob with the tool chips collected after it. */
type ChatMessageSegment = {
    kind: 'text';
    content: string;
} | {
    kind: 'tool';
    call: ChatToolCallInfo;
};
/** Describe the structure and properties of a chat message with roles, content, and optional metadata */
interface ChatUiMessage extends ChatMessageMetrics {
    id: string;
    role: 'user' | 'assistant' | 'system';
    content: string;
    reasoning?: string;
    toolCalls?: ChatToolCallInfo[];
    /** Ordered text/tool sequence for true chronological interleaving. When
     *  present and non-empty it is rendered in place of `content` + `toolCalls`;
     *  both remain the fallback for producers that don't segment a turn. */
    segments?: ChatMessageSegment[];
    /** Persisted assistant parts. When `ChatMessages.durableCards` is supplied,
     * shared plan/question cards render directly from these projections. */
    parts?: Array<Record<string, unknown>>;
}
/** Define properties for rendering chat messages with optional models, markdown, extras, and durable cards */
interface ChatMessagesProps {
    messages: ChatUiMessage[];
    /** Catalogue models, for per-message cost from pricing. Pass [] to skip cost. */
    models?: CatalogModel[];
    /** Markdown renderer for assistant content; default renders pre-wrapped text. */
    renderMarkdown?: (content: string) => ReactNode;
    /** Extra per-message content (artifacts, custom panels) appended after the body. */
    renderExtras?: (message: ChatUiMessage) => ReactNode;
    /** Canonical durable plan/question card wiring. Apps inject only transport,
     * access, and optional visual callbacks; card selection/dedupe stays shared. */
    durableCards?: Omit<DurableChatCardsProps, 'parts' | 'renderMarkdown'>;
    userLabel?: string;
    agentLabel?: string;
    /** Render the trailing "agent is thinking" row. */
    loading?: boolean;
    /** Approve/Reject handlers for proposals awaiting approval. When omitted the
     *  chip still shows "awaiting approval" but without action buttons. */
    approval?: ProposalApprovalHandlers;
    /** Open a full-transcript view (e.g. {@link RunDrillIn}) from a tool card. */
    onToolCallClick?: (call: ChatToolCallInfo, message: ChatUiMessage) => void;
    /** Per-tool custom detail renderers for expanded tool cards. */
    toolRenderers?: ToolDetailRenderers;
    /** Stream-error affordance: when the turn failed (a thrown transport error or
     *  a loop-level `onErrorEvent`), pass the message here to render an error row.
     *  A failed turn otherwise just stops with no UI signal. */
    error?: string | null;
    /** Retry control shown on the error row; omit to render the error without a
     *  retry button (e.g. when the product retries automatically). */
    onRetry?: () => void;
    /** Zero-state renderer, shown when there are no messages and the turn is
     *  neither loading nor errored. When omitted, a branded first-run state is
     *  shown ({@link ChatEmptyState}); pass `() => null` to render nothing. */
    renderEmpty?: () => ReactNode;
    /** First-run state config used when `renderEmpty` is not supplied. Lets a
     *  product set the headline and the "doors" (e.g. start from a template, ask
     *  the agent) without replacing the whole zero-state. */
    emptyState?: ChatEmptyStateProps;
    /** Optional branded header slot rendered above the thread. Off by default to
     *  preserve the current layout; pass `{ title }` (or your own node via
     *  `header`) to show the Tangle mark + product title in the chat shell. */
    header?: ReactNode;
    /** Resolve a raw-bytes download URL for one attachment part. When set, any
     *  message carrying attachment parts (`file`/`image` parts with a `path`,
     *  see `attachmentPartsFromMessageParts`) renders them as a `MessageAttachments`
     *  row next to the bubble — thumbnails for images, download chips for files.
     *  Absent → today's rendering, byte-identical (no attachment row). */
    resolveAttachmentUrl?: (part: ChatAttachmentPart) => string;
    /** Render persisted `type:'work_product'` anchor parts as `WorkProductCard`
     *  rows under the message body — the chat card that keeps chat the driver
     *  surface for review. `onOpen` opens the product's queue/detail surface.
     *  Absent → today's rendering, byte-identical (no card row). */
    workProductCards?: {
        onOpen?: (part: WorkProductPersistedPart) => void;
    };
}
/** One starting "door" in the chat first-run state — a concrete, labeled action
 *  (start from a template, do it by hand, ask the agent), not a placeholder. */
interface ChatEmptyDoor {
    label: string;
    description?: string;
    onSelect: () => void;
}
/** Define properties for rendering the chat empty state with customizable text and starting doors */
interface ChatEmptyStateProps {
    /** Product name shown next to the Tangle mark. Default "Agent". */
    productName?: string;
    /** Headline. Default frames delegation, not messaging. */
    headline?: string;
    /** Subline under the headline. */
    subline?: string;
    /** Up to three concrete starting doors. Omit for a mark-and-prompt-only state. */
    doors?: ChatEmptyDoor[];
}
/**
 * Branded chat first-run state: the Tangle mark, a delegation-framed prompt, and
 * up to three concrete doors. Replaces the blank thread that read as "empty or
 * broken". Concrete + actionable — never a "coming soon" placeholder.
 */
declare function ChatEmptyState({ productName, headline, subline, doors, }: ChatEmptyStateProps): react.JSX.Element;
/** Handle approval and rejection actions for proposals with asynchronous support */
interface ProposalApprovalHandlers {
    onApprove: (proposalId: string, toolCallId: string) => void | Promise<void>;
    onReject: (proposalId: string, toolCallId: string) => void | Promise<void>;
}
/** Per-tool custom detail renderers for the expanded card body — keyed by
 *  tool name. Return null to fall back to the generic detail view. */
type ToolDetailRenderers = Record<string, (call: ChatToolCallInfo, message: ChatUiMessage) => ReactNode>;
/** Whole seconds elapsed while `active`, ticking once a second. Powers the live
 *  "thinking" timers (the pre-first-token row and the reasoning box) so a long
 *  thinking gap shows progress instead of a frozen label. Counts from when
 *  `active` first turns true; freezes when it clears. */
declare function useThinkingSeconds(active: boolean): number;
/**
 * The message thread: one centered column; user messages are right-aligned
 * bubbles with a User label; agent messages carry an Agent meta line with
 * model id, tokens/sec, and cost, plus a collapsible thinking section and
 * tool-call chips.
 */
declare function ChatMessages({ messages, models, renderMarkdown, renderExtras, durableCards, userLabel, agentLabel, loading, approval, onToolCallClick, toolRenderers, error, onRetry, renderEmpty, emptyState, header, resolveAttachmentUrl, workProductCards, }: ChatMessagesProps): react.JSX.Element;

export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, type AttachmentFileResult, CatalogModel, ChatAttachmentInput, ChatAttachmentKind, ChatAttachmentPart, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, type ChatInteractionRestoreMode, ChatInteractionStatus, ChatMentionPart, type ChatMessageMetrics, type ChatMessageSegment, ChatMessages, type ChatMessagesProps, ChatSelectField, type ChatStreamCallbacks, type ChatStreamToolCall, type ChatStreamToolResult, type ChatToolCallInfo, type ChatUiMessage, type ComposerFile, type ComposerFilePart, type ComposerMentionProp, type ConsumeChatStreamResult, DEFAULT_EFFORT_LEVELS, DEFAULT_MENTION_EMPTY_TEXT, DEFAULT_MENTION_LIMIT, type DurableChatCard, DurableChatCards, type DurableChatCardsProps, type DurableInteractionAnswerSubmitterOptions, DurablePlanCard, type DurablePlanCardProps, DurablePlanClientError, type DurablePlanCurrentInput, type DurablePlanDecision, type DurablePlanDecisionClient, type DurablePlanDecisionClientOptions, type DurablePlanDecisionInput, type DurablePlanDecisionResult, type DurablePlanFollowUpReceipt, type EffortLevel, EffortPicker, type EffortPickerProps, EvidenceLineageTable, type EvidenceLineageTableProps, ExceptionList, type ExceptionListProps, type FieldValues, FileMention, FlowWaterfall, type FlowWaterfallProps, INDEX_REFRESH_AFTER_MS, INTERACTION_SUBMIT_TIMEOUT_MESSAGE, INTERACTION_SUBMIT_TIMEOUT_MS, InteractionActionButton, type InteractionAnswerSubmission, type InteractionAnswerSubmitterOptions, InteractionAnswers, type InteractionAttemptStore, InteractionBadge, type InteractionBadgeVariant, InteractionCancelData, InteractionPlanCard, type InteractionPlanCardProps, InteractionQuestionCard, type InteractionQuestionCardProps, InteractionRequestWire, type InteractionSubmitResult, type MentionItem, type MentionTextSegment, MessageAttachments, type MessageAttachmentsProps, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProvenanceStamp, type ProvenanceStampProps, ProviderLogo, type ProviderLogoProps, QualityCheckList, type QualityCheckListProps, QuestionOptionList, type QuestionOptionListProps, type RestoreChatInteractionsOptions, ReviewQueueItem, type ReviewQueuePage, ReviewQueuePanel, type ReviewQueuePanelProps, ReviewQueueState, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsOptions, type UseChatInteractionsResult, type UseComposerAttachmentsOptions, type UseComposerAttachmentsResult, type UseDurablePlanFlowOptions, type UseDurablePlanFlowResult, type UseFileMentionsOptions, type UseFileMentionsResult, type WaterfallRow, WorkProductCard, type WorkProductCardProps, __resetAttachmentFileCacheForTests, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createDurableInteractionAnswerSubmitter, createDurablePlanDecisionClient, createInteractionAnswerSubmitter, createMemoryInteractionAttemptStore, createSessionInteractionAttemptStore, dispatchChatStreamLine, durableChatCardsFromParts, fieldAnswer, fieldValuesFromAnswers, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, hydrateChatInteractions, interactionStatusLabels, interactionSubmissionSignature, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, loadAttachmentFile, mergeActivityPages, mergeReviewQueuePages, nextRevealCount, pendingApprovalOf, rankFileMentions, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, reviewQueueStateLabel, segmentMentionContent, settleInteractionSubmit, streamChatTurn, terminalizePendingChatInteractions, triggerAttachmentDownload, upsertChatInteraction, useChatInteractions, useComposerAttachments, useDurablePlanFlow, useFileMentions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout, workProductPartsFromMessageParts, workProductStatusLabel };
