import * as react from 'react';
import { ReactNode } from 'react';
import { S as StepAgentActivity } from '../agent-activity-C8ZG0F0M.js';
import { a as FlowTrace } from '../flow-types-Cb_AblZs.js';
import { C as CatalogModel } from '../model-catalog-BEAEVDaa.js';
import { Harness } from '../harness/index.js';
import '@tangle-network/agent-interface';

/**
 * 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', ...}}
 *   {kind:'tool_result', toolCallId, toolName, label, outcome}
 *   {type:'turn'|'metadata'|'error'|'turn_status', ...}          (route-level)
 *
 * 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.
 */
interface ChatStreamToolCall {
    toolCallId?: string;
    toolName: string;
    args: Record<string, unknown>;
}
interface ChatStreamToolResult {
    toolCallId?: string;
    toolName?: string;
    label?: string;
    outcome: {
        ok: boolean;
        result?: unknown;
        code?: string;
        message?: string;
    };
}
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;
    onMetadata?: (data: Record<string, unknown>) => void;
    /** A loop-level error event (the turn failed server-side). */
    onErrorEvent?: (message: string) => void;
}
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>;
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>;

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';
}
interface ChatComposerProps {
    /** Send the trimmed, non-empty message. Attached files travel separately via
     *  `onAttach` + `pendingFiles` (the host consumes and clears them on send). */
    onSend: (message: string) => 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;
    /** 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, onCancel, isStreaming, disabled, placeholder, value, onValueChange, initialValue, controls, controlsPlacement, onAttach, onAttachFolder, pendingFiles, onRemoveFile, accept, dropTitle, dropDescription, focusShortcut, sendLabel, className, }: ChatComposerProps): react.JSX.Element;

/**
 * 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).
 */
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;

interface SandboxTerminalConnection {
    runtimeUrl: string | null;
    sidecarUrl: string | null;
    token: string | null;
    expiresAt: string | null;
    status: string;
    error: string | null;
    loading: boolean;
    sandboxId?: string;
}
interface SandboxTerminalConnectionResponse {
    runtimeUrl?: string;
    sidecarUrl?: string;
    token?: string;
    expiresAt?: string;
    status?: string;
    error?: string;
    sandboxId?: string;
}
interface UseSandboxTerminalConnectionOptions {
    workspaceId: string;
    connectionUrl?: string | ((workspaceId: string) => string);
    fetcher?: typeof fetch;
    provisionPollIntervalMs?: number;
    provisionPollTimeoutMs?: number;
    tokenRefreshSkewMs?: number;
}
interface UseSandboxTerminalConnectionResult extends SandboxTerminalConnection {
    connect: () => Promise<void>;
}
declare function useSandboxTerminalConnection(opts: UseSandboxTerminalConnectionOptions): UseSandboxTerminalConnectionResult;
/**
 * Stable-per-tab, unique-per-client terminal connection id.
 *
 * Persists in `sessionStorage` so a reload in the same tab reuses the id (the
 * sidecar restores the same PTY session via `TerminalView.connectionId`), while
 * separate tabs/windows each get a distinct id. Pass the result as
 * `TerminalView`'s `connectionId`. Without it (e.g. gtm-agent today) every tab
 * shares one connection id and their reconnects evict each other.
 *
 * Falls back to an ephemeral id when `sessionStorage` is unavailable (SSR,
 * privacy mode) — still unique per call, just not reload-stable.
 */
declare function tabTerminalConnectionId(storageKey?: string): string;

/**
 * `WorkspaceTerminalPanel` — the shared sandbox-terminal surface: a header with
 * a status badge, connect/provisioning/error states with a retry, and the lazy
 * `TerminalView` (from `@tangle-network/sandbox-ui`) mounted only once the
 * connection is live. creative-agent and gtm-agent each hand-roll a structurally
 * identical panel; only the copy and the status-tone map are app-specific, so
 * those are props and everything else lives here.
 *
 * Pair it with {@link useSandboxTerminalConnection} (the `connection` prop) and
 * {@link tabTerminalConnectionId} (the `connectionId` prop) so reloads restore
 * the same PTY and separate tabs don't evict each other.
 *
 * Styling matches the rest of `web-react`: Tailwind over the shared design
 * tokens; glyphs inline; no icon/UI library.
 */

type TerminalStatusTone = 'idle' | 'connecting' | 'connected' | 'error';
interface TerminalStatusDisplay {
    tone: TerminalStatusTone;
    label: string;
}
interface WorkspaceTerminalPanelProps {
    /** Live connection state (from {@link useSandboxTerminalConnection}). */
    connection: SandboxTerminalConnection;
    /** Stable per-tab id (from {@link tabTerminalConnectionId}) so the sidecar
     *  restores the same PTY across remounts and tabs don't collide. */
    connectionId?: string;
    /** Header title. Default "Terminal". */
    title?: string;
    /** Header subtitle / sandbox label. */
    subtitle?: string;
    /** Whether the terminal tab is visible (forwarded to `TerminalView` for fit). */
    isActive?: boolean;
    /** Reconnect handler — wire to the hook's `connect`. Shown on idle/error. */
    onRetry?: () => void;
    /** Map a `connection.status` to a badge tone + label. The default covers
     *  idle/provisioning/running/error; override for app-specific vocabulary. */
    statusDisplay?: (connection: SandboxTerminalConnection) => TerminalStatusDisplay;
    /** Extra header content, right-aligned (actions, sandbox id, …). */
    headerExtra?: ReactNode;
    className?: string;
}
declare function WorkspaceTerminalPanel({ connection, connectionId, title, subtitle, isActive, onRetry, statusDisplay, headerExtra, className, }: WorkspaceTerminalPanelProps): ReactNode;

/**
 * `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;

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[];
}
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;
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;
};
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[];
}
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;
    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;
}
/** 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;
}
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;
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, userLabel, agentLabel, loading, approval, onToolCallClick, toolRenderers, error, onRetry, renderEmpty, emptyState, header, }: ChatMessagesProps): react.JSX.Element;

export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, type ChatMessageMetrics, type ChatMessageSegment, ChatMessages, type ChatMessagesProps, type ChatStreamCallbacks, type ChatStreamToolCall, type ChatStreamToolResult, type ChatToolCallInfo, type ChatUiMessage, type ComposerFile, type ConsumeChatStreamResult, DEFAULT_EFFORT_LEVELS, type EffortLevel, EffortPicker, type EffortPickerProps, FlowWaterfall, type FlowWaterfallProps, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProviderLogo, type ProviderLogoProps, RunDrillIn, type RunDrillInProps, type SandboxTerminalConnection, type SandboxTerminalConnectionResponse, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type TerminalStatusDisplay, type TerminalStatusTone, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseSandboxTerminalConnectionOptions, type UseSandboxTerminalConnectionResult, type WaterfallRow, WorkspaceTerminalPanel, type WorkspaceTerminalPanelProps, activityTone, consumeChatStream, dispatchChatStreamLine, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, mergeActivityPages, nextRevealCount, pendingApprovalOf, streamChatTurn, tabTerminalConnectionId, usePending, usePopover, useSandboxTerminalConnection, useSmoothText, useThinkingSeconds, waterfallLayout };
