/** * TORUK Dynamic UI wire protocol — block splitting, normalization and resolution. * * VENDORED VERBATIM from TORUK Core: `packages/components/src/dynamic-ui/split-blocks.ts`. * This is the authoritative parser for the `ui` / `artifact` fence contract that Core's * server injects into the system prompt (`packages/server/src/dynamic-ui/contract.ts`), * including the prop normalization and clamping that makes model-authored props safe to * render. It is framework-agnostic and dependency-free, so the SDK runs the exact same * code rather than re-deriving the contract and drifting from it. * * DO NOT hand-edit to add behaviour. When Core's copy changes, re-vendor this file and * `split-blocks.test.ts` alongside it (only the formatting is adapted to SDK Prettier). */ export type UiComponentType = 'table' | 'chart' | 'form' | 'card' | 'mermaid' | 'artifact' | 'artifact-ref' | 'approval' | 'rating' | 'feedback' | 'choice' | 'infocard'; export interface UiComponent { type: UiComponentType; props: Record; } export type Block = { kind: 'md'; content: string; } | { kind: 'ui'; component: UiComponent; }; export type BlockWithId = Block & { id?: string; }; export interface BlockSplitter { feed(chunk: string): void; end(): void; abort(): void; } export interface LifecycleBlockSplitterCallbacks { /** Prose markdown fragments, emitted token-by-token for progressive (live) wire streaming. */ emit: (block: Block) => void; onPending?: (id: string, componentType: string) => void; onPendingUpdate?: (id: string, componentType: string) => void; onFinal?: (block: BlockWithId) => void; } export interface LifecycleBlockSplitter extends BlockSplitter { /** Complete in-progress table/fence state without terminating the splitter. */ flush(): void; } export declare function normalizeUiComponent(type: unknown, props: Record): UiComponent | null; export declare function createBlockSplitter(emit: (block: Block) => void, onFallback?: (fenceKind: 'ui' | 'artifact') => void): BlockSplitter; /** * Live splitter with lifecycle callbacks. Prose markdown is emitted via `emit()` **token-by-token** * (progressive wire streaming): the safe portion of the in-progress line is streamed immediately, * the only held-back case being a line that could still be a `ui`/`artifact` fence opener — those * are buffered until the closing fence and emitted as one atomic UI block via `onFinal`. GFM pipe * tables are NOT promoted to ui:table here (that would conflict with token streaming); they stream * as markdown text and the replay path (resolveBlocks) normalizes them to ui:table on reload. */ export declare function createLifecycleBlockSplitter(callbacks: LifecycleBlockSplitterCallbacks, onFallback?: (fenceKind: 'ui' | 'artifact') => void): LifecycleBlockSplitter; /** * Split a markdown string into blocks, converting GFM pipe tables (header + `|---|` separator + rows) * into `ui:table` components. Anything that isn't a well-formed table stays markdown. */ export declare function extractTables(content: string): Block[]; export declare function splitBlocks(fullText: string): Block[]; /** * Two-layer block resolver for live/replay consistency. * Layer 1: explicit `kind:'ui'` blocks pass through unchanged. * Layer 2: each `kind:'md'` block is inspected via `splitBlocks(content)` so fences, * GFM pipe tables, and other component shapes are promoted to typed ui blocks. */ export declare function resolveBlocks(blocks: Block[]): Block[];