/**
 * Per-channel generative UI: what a turn may draw depends on where it is read. A web page draws the
 * catalog's components (and the sandbox); WhatsApp and Telegram draw text, reply buttons, lists and
 * images; an email draws HTML. `genui({ channels: { … } })` sets, per channel, which tools the model
 * is offered and how what it pushes is delivered — and the model is told only what that channel can
 * draw.
 *
 * Framework-free and isomorphic, like the rest of `genui`.
 */
import type { UiAction } from './actions.js';
import { type Catalog, type ComponentDefinition, type GenuiStreaming } from './catalog.js';
import { type DefineSandboxOptions } from './sandbox.js';
/** The channel a turn runs on when nothing says otherwise: a browser (AG-UI, the HTTP chat, A2UI). */
export declare const WEB_CHANNEL = "web";
/** The `channels` entry that serves every channel without one of its own. */
export declare const DEFAULT_CHANNEL = "default";
/** Channels a person reads in a messaging app or a mailbox: the server draws for them. */
export declare const MESSAGING_CHANNELS: readonly string[];
/**
 * What the model is offered on a channel: `tree` — one `ui__render` tool composing the catalog (and
 * the sandbox, when on); `per-component` — one `ui__show_<component>` tool per component that can be
 * drawn there; `text` — no UI tools, the model answers in text.
 */
export type GenuiChannelMode = 'tree' | 'per-component' | 'text';
/**
 * How what the model pushed reaches a channel the server draws for (WhatsApp, Telegram, email):
 * `native` — each component's channel conversion (`defineComponent(…, { channels })`: text, reply
 * buttons, a list, an image), its text summary otherwise; `text` — always the text summary; `html` —
 * HTML (email). A web channel draws in the browser and ignores it.
 */
export type GenuiChannelRender = 'native' | 'text' | 'html';
/** One channel's generative UI — every field falls back to the `default` entry, then to the top level. */
export interface GenuiChannelOptions {
    /** Default: the top-level `mode` on a web channel; `per-component` on a messaging channel. */
    mode?: GenuiChannelMode;
    /** Tree mode: draw the layout while the model writes it (`partial`) or once it is whole. */
    streaming?: GenuiStreaming;
    /**
     * The sandbox on this channel: `false` for none, `true` / a config for the sandbox with those
     * options (a web channel may use the app's kit while a mobile one does not). Default: the
     * top-level `sandbox` on a web channel; never on a messaging channel (it cannot run there).
     */
    sandbox?: boolean | DefineSandboxOptions;
    /** Default `native` (`html` for `email`). */
    render?: GenuiChannelRender;
    /** Charts as PNG images on a channel that takes images ({@link ChartImageRenderer}, `chartImages()`). */
    chartImages?: ChartImageRenderer;
    /** Extra words for the model on this channel, added to the UI tools' descriptions. */
    instructions?: string;
}
/** `genui({ channels })`: an entry per channel name, plus `default` for every other one. */
export interface GenuiChannels {
    web?: GenuiChannelOptions;
    mobile?: GenuiChannelOptions;
    whatsapp?: GenuiChannelOptions;
    telegram?: GenuiChannelOptions;
    email?: GenuiChannelOptions;
    default?: GenuiChannelOptions;
    [channel: string]: GenuiChannelOptions | undefined;
}
/** The top-level genui options a channel falls back to. */
export interface GenuiChannelBase {
    mode?: 'per-component' | 'tree';
    streaming?: GenuiStreaming;
    sandbox?: boolean | DefineSandboxOptions;
}
/** A channel's options with every default applied — what the tools and the channel handler follow. */
export interface ResolvedGenuiChannel {
    /** The channel's name (`web`, `whatsapp`, `mobile`, …). */
    name: string;
    /** The server draws for it (WhatsApp, Telegram, email): it never runs a sandbox or a React renderer. */
    messaging: boolean;
    /** `channels` has an entry for it (its own or `default`). `false` → it behaves as it always did. */
    configured: boolean;
    mode: GenuiChannelMode;
    streaming?: GenuiStreaming;
    /** `undefined` → the catalog's own sandbox (if it has one) is left as it is. */
    sandbox?: boolean | DefineSandboxOptions;
    render: GenuiChannelRender;
    chartImages?: ChartImageRenderer;
    instructions?: string;
}
/**
 * THE rule for which channel a turn runs on, read from its page context:
 *
 * - `pageContext.channel` is a string → that channel (an explicit override: `send({ channel })`, a
 *   mobile client posting `pageContext: { channel: 'mobile' }`);
 * - `pageContext.channel` is the address a text-channel adapter stamps (`{ name, conversation,
 *   kind? }`) → its `kind` (`whatsapp`, `telegram`), else its `name`;
 * - nothing → `web`: what AG-UI, the HTTP chat and A2UI serve (they stamp nothing, so a tool that
 *   skips skeletons when `pageContext.channel` is set keeps working).
 */
export declare function turnChannel(pageContext: {
    [key: string]: unknown;
} | undefined): string;
/** WhatsApp, Telegram and email are drawn by the server; every other channel by a client. */
export declare function isMessagingChannel(channel: string): boolean;
/** One channel's options, its own entry over `default` over the top level. */
export declare function resolveGenuiChannel(base: GenuiChannelBase, channels: GenuiChannels | undefined, channel: string | undefined): ResolvedGenuiChannel;
/** Throws on a `channels` entry that names an unknown mode or render. */
export declare function assertGenuiChannels(channels: GenuiChannels | undefined): void;
/**
 * A reply button. Pressed, it becomes the user's next turn — a {@link UiAction} whose text is the
 * label and whose context is `value`, exactly like a sandbox's `agent.send`.
 */
export interface ChannelNativeButton {
    label: string;
    /** The action's name. Default `select`. */
    action?: string;
    /** The values that go with the press (JSON). */
    value?: Record<string, unknown>;
}
export interface ChannelNativeListItem extends ChannelNativeButton {
    /** A second line under the label. */
    description?: string;
}
/** A list to pick one entry from (a WhatsApp list message, a Telegram keyboard of one row each). */
export interface ChannelNativeList {
    /** The label of the button that opens the list (WhatsApp). */
    button: string;
    /** A heading over the entries. */
    title?: string;
    items: ChannelNativeListItem[];
}
/** An image: by an `https` url the provider fetches, or its bytes. */
export interface ChannelNativeImage {
    url?: string;
    data?: Uint8Array;
    /** Default `image/png`. */
    contentType?: string;
}
/**
 * What a component is on a channel the server draws for. Each field is optional; a message with an
 * image sends `text` as its caption, one with buttons or a list sends `text` as its body.
 */
export interface ChannelNativeMessage {
    /** In the model's markdown — converted to the channel's. */
    text?: string;
    /** For `render: 'html'` (email). Trusted markup: build it from props, escaping what you insert. */
    html?: string;
    /** Reply buttons — at most 3 on WhatsApp; more become a list. */
    buttons?: ChannelNativeButton[];
    list?: ChannelNativeList;
    image?: ChannelNativeImage;
}
/**
 * What a conversion is told about the frame it converts. `version` is the schema version the props
 * were written for (the frame's — a v1 frame persisted before the component moved to v2 still says
 * 1); convert the props by it, as a renderer would.
 */
export interface ChannelConversionContext {
    /** The channel (`whatsapp`, `telegram`, `email`…). */
    channel: string;
    /** The component being converted (inside a tree: the node's). */
    component: string;
    /** The props' schema version: the frame's, or the tree's `componentVersions` entry. Default 1. */
    version: number;
    /** The frame's id, when it has one. */
    id?: string;
    /** The whole frame the component came from (a tree's, for one of its nodes), as given. */
    frame?: ChannelComponentInput;
}
/** What `describe` is told: the channel, and the version the component is at now. */
export interface ChannelDescribeContext {
    channel: string;
    component: string;
    /** The component definition's current version (default 1). */
    version: number;
}
/** `defineComponent(…, { channels: { whatsapp: (props, { version }) => ({ text, buttons }) } })`. */
export type ChannelConversion<P = Record<string, unknown>> = {
    convert(props: P, context: ChannelConversionContext): ChannelNativeMessage | ChannelNativeMessage[] | null | undefined | Promise<ChannelNativeMessage | ChannelNativeMessage[] | null | undefined>;
}['convert'];
/**
 * A conversion with words for the model: `describe` says how the component reaches the user on that
 * channel — `'a pick-list of the orders; no Refund buttons: to refund, the user picks an order and
 * replies'` — and goes into the channel's prompt (`channelPrompt`), so the model does not describe the
 * web view there. A bare conversion is described from what it can send.
 */
export interface ChannelConversionSpec<P = Record<string, unknown>> {
    convert: ChannelConversion<P>;
    /** Words for the model, or a function of the channel and the component's current version. */
    describe?: string | ((context: ChannelDescribeContext) => string | undefined);
}
/**
 * A component's conversions, by channel name — a function, or `{ convert, describe }`. `false` →
 * the component is never offered on that channel (it has nothing to say there).
 */
export type ComponentChannels<P = Record<string, unknown>> = {
    [channel: string]: ChannelConversion<P> | ChannelConversionSpec<P> | false | undefined;
};
/** Renders chart components as images, for a channel that takes them. */
export interface ChartImageRenderer {
    /** The components it draws (default for `chartImages()`: `['Chart']`). */
    readonly components: readonly string[];
    render(component: string, props: Record<string, unknown>): Promise<ChannelNativeImage | null>;
}
/**
 * Whether `definition` can be drawn on `channel`. On a web channel every component can (the client's
 * `uiCapabilities` narrow it); on a messaging channel a component can when it has a conversion for
 * it, a chart renderer draws it, or it has a text summary (`fallbackText`) — a component without one
 * would arrive as a JSON dump, so it is not offered. A sandbox never can, and `channels: { x: false }`
 * opts a component out.
 */
export declare function canRenderOnChannel(definition: ComponentDefinition<unknown>, channel: ResolvedGenuiChannel): boolean;
/**
 * The catalog as `channel` may draw it: only what it can render, and its own sandbox (or none). With
 * no `channels` configured the catalog is returned as it is.
 */
export declare function channelCatalog(catalog: Catalog, channel: ResolvedGenuiChannel): Catalog;
/** What a UI tool's description says about the channel the conversation is on. */
export declare function channelInstructions(channel: ResolvedGenuiChannel): string | undefined;
/** A pushed component, as a channel delivers it. */
export interface ChannelComponentInput {
    /** The frame's id — what a button press reports as its `componentId`. */
    id?: string;
    name: string;
    props: Record<string, unknown>;
    /** The schema version the props were written for (the frame's `version`). Default 1. */
    version?: number;
    /** A tree frame's per-component versions (`componentVersions`), for its nodes. */
    componentVersions?: Record<string, number>;
    /**
     * The frame's own text (`fallbackText`): what is sent for a component whose props were written
     * for a schema version this catalog does not have — it is never drawn from them as if they were.
     */
    fallbackText?: string;
}
/** {@link ChannelNativeMessage} plus the component it came from (so a press can name it). */
export interface ChannelRenderedMessage extends ChannelNativeMessage {
    component: string;
    componentId?: string;
    title?: string;
}
/** Plain text as minimal HTML: paragraphs, line breaks, `*bold*`. */
export declare function textToHtml(text: string): string;
/**
 * What a pushed component (a tree included) is on a channel the server draws for: one or more
 * messages — its conversions, chart images, and the text summary of everything else, in order.
 * Consecutive text is joined. Never throws: a conversion that fails sends the summary.
 */
export declare function renderChannelMessages(catalog: Catalog, component: ChannelComponentInput, channel: ResolvedGenuiChannel): Promise<ChannelRenderedMessage[]>;
/** The UI action a pressed button (or picked list entry) stands for — the user's next turn. */
export declare function channelButtonAction(button: ChannelNativeButton, from: {
    componentId?: string;
    title?: string;
}): UiAction;
/**
 * The system-prompt block for a turn on a messaging channel (WhatsApp, Telegram, email): that the
 * user is not on a web page, what the channel can show, and — component by component — how each one
 * reaches the user there, from its channel conversion (its `describe`, else what a conversion can
 * send), a chart image, or its text summary. Tool and component descriptions are written for the web
 * ("a table with a Refund button per row"); this is what keeps the model from describing that view
 * on WhatsApp. `undefined` on a web-like channel, or without `channels`.
 */
export declare function channelPrompt(catalog: Catalog, channel: ResolvedGenuiChannel): string | undefined;
/**
 * How many reply buttons and list rows a channel shows — the adapter's `capabilities.buttons` /
 * `capabilities.lists`, which the channel handler stamps on the turn (`pageContext.channel.choices`).
 */
export interface ChannelChoiceLimits {
    buttons: number;
    lists: number;
}
/**
 * What a channel actually delivered, in words for the model: each message (text, a pick-list and its
 * entries, reply buttons, numbered choices, an image, an email section), and what the user did NOT
 * get. This is what a tool's result carries as `delivered` on a messaging channel, so the model talks
 * about the list the user can pick from — not the table with Refund buttons it would have on the web.
 *
 * Choices follow the channel handler's rule, within `limits` (default: the channel's own adapter's):
 * reply buttons when there is no list (or no lists) and they fit, else a list of at most `lists`
 * rows, else numbered text the user answers by replying.
 */
export declare function describeChannelDelivery(messages: readonly ChannelRenderedMessage[], channel: ResolvedGenuiChannel, limits?: ChannelChoiceLimits): string;
/**
 * {@link describeChannelDelivery} of the components a tool pushed (last props per id, previews left
 * out), rendered the way the channel draws them. `undefined` on a channel the server does not draw
 * for, or without `channels`.
 */
export declare function describeChannelComponents(catalog: Catalog, components: readonly (ChannelComponentInput & {
    partial?: boolean;
})[], channel: ResolvedGenuiChannel, limits?: ChannelChoiceLimits): Promise<string | undefined>;
//# sourceMappingURL=channels.d.ts.map