/** * Source-metadata payload → `{ sources, refs }`. * * ONE decoder for two arrival paths: the live `GUIDE`/`SOURCES` NATS chunk and * the persisted row of the same name in dialog history. That is the whole point * of putting it here rather than in either consumer — a reloaded thread has to * render identically to the live turn, and two parsers is exactly how that stops * being true. * * Everything is validated, and anything that fails is DROPPED rather than * repaired: this payload is assembled from a remote MCP server's tool output, * so a malformed row means an upstream contract slip, and rendering a chip with * a blank title or a link to a non-https URL is worse than rendering nothing. * * Server-safe: no React, no browser APIs beyond `URL`. */ import type { ChatRef } from '../components/chat/chat-ref.types'; import type { ChatSource } from '../components/chat/types/message.types'; import type { SourcesEvent } from './events'; /** * A YouTube video id, from either a bare id or any of the watch/short/embed URL * shapes. Returns the ID, never a URL — the player takes an id. */ export declare function youtubeVideoId(value: unknown): string | undefined; /** * Decode one source-metadata payload into its event, or `null` when there is * nothing usable in it. * * Returning `null` for an empty result matters: the reducer stamps this onto a * message, and an event carrying two empty arrays would overwrite metadata that * an earlier chunk of the same turn had already supplied. */ export declare function sourceMetadataEvent(payload: unknown): SourcesEvent | null; /** What one assistant answer carries: its citations and its expandable refs. */ export interface SourceMetadata { sources?: ChatSource[]; refs?: ChatRef[]; } /** * Fold one decoded chunk into an answer's accumulated metadata. * * A single turn can call several remote tools, each returning its own metadata, * so this merges rather than replaces — and it is shared by the live reducer and * the history replay precisely because those two must agree on what a reloaded * answer shows. * * First writer wins per identity (`index` for a source, `type:id` for a ref), * the same rule the decoder applies within one payload. * * KNOWN LIMIT: citation numbers are assigned per tool result, so two tool calls * in one turn can both claim `[1]`. The later source is dropped rather than * renumbered — by then the numbers are already written into the answer text, * and renumbering would point `[1]` at a document the sentence is not about. */ export declare function mergeSourceMetadata(previous: SourceMetadata | null, event: SourcesEvent): SourceMetadata; //# sourceMappingURL=source-metadata.d.ts.map