/** * NATS chunk → normalized `ChatStreamEvent` decoder. * * THE live NATS reading path — the ONLY chunk parser in the codebase. Every * consumer (this lib's `useNatsChatAdapter`, the hub, the product app) feeds * raw chunks through `decodeNatsChunk` into `createChatStreamReducer`. The * legacy `components/chat/utils/chunk-parser.ts` (`parseChunkToAction`) that * this module superseded was DELETED — do not reintroduce a second decoder. * * Chunks map onto the transport-agnostic event union, with the JetStream * `streamSeq` lifted into the `seq` envelope (the reducer's idempotency gate * keys off it). * * Behavior is pinned by `__tests__/nats-decoder-golden.test.ts`. * * Server-safe: no React, no browser APIs. */ import type { AskOptionData } from '../components/chat/types/message.types'; import type { ChatStreamEvent } from './events'; export { GUIDE_ORIGIN } from './events'; /** Coerce the wire's ask `options[]` into the card's row shape. Rows without a * usable `label` are dropped: the label is what gets SENT when the row is * picked, so a blank one would post an empty reply. */ export declare function normalizeAskOptions(raw: unknown): AskOptionData[]; /** * Adapt ONE Product Guide event to the NATS kernel. * * The guide half of a Mingo dialog is the hub's own stream, so it is decoded by * the shared table in `./leading-frames` and then passed through here — the ONE * place that reconciles the two kernels. Both entry points use it: frames * re-streamed by the agent inside `GUIDE` chunks, and the SSE response of the * hub's confirm-tool route, which a host replays into the same dialog. * * Pass-through is the DEFAULT; only these rules alter an event, and each exists * because the two kernels genuinely disagree: * * - `text-delta` becomes `guide-delta`: the body of a guide turn belongs * inside the "OpenFrame Guide" card, and a `text` segment would strand part * of the same answer outside it. * - thinking is escaped HERE. The SSE kernel escapes `<` on the way in, the * NATS kernel does not, and the guide's thinking is full of XML-ish tokens * that would otherwise render as markup. * - `metadata` survives ONLY to carry `conversationId`, which the hub mints * and every confirm-tool call must quote back. Everything else on that * frame (the hub's model, routing) is dropped: `applyNats` rebuilds the * dialog's live model from a metadata event, so letting it through would * relabel a Mingo turn with the hub's model, and a routing frame — which * carries no model at all — would blank the badge mid-answer. * - dialog-level events stop here (`AGENT_OWNED_EVENTS`). * * Everything else crosses over as the hub typed it, gaining only `origin`. * `approvalType` in particular stays the TOOL NAME, exactly as in the hub's own * chat: the NATS kernel gates approvals on approval TIER and escalates the rest, * which is an agent-side concept a hub proposal has no tier for. Rewriting the * tool name into a fake tier to slip past that gate would make the guide half of * the stream diverge from the guide chat itself. */ export declare function guideEventForNats(event: ChatStreamEvent): ChatStreamEvent | null; /** * Decode one re-streamed Product Guide frame (a `GUIDE` chunk's `payload`) into * a NATS event. The frame grammar is decoded by the shared table, never by a * second copy here; the kernel reconciliation is `guideEventForNats`. */ export declare function guideFrameEvent(frame: Record): ChatStreamEvent | null; /** * Parse one raw NATS chunk into a normalized event. Returns `null` for * unknown/malformed chunks — an unrecognized `type`, a missing required * field, or a non-object payload are all tolerated as no-ops rather than * throwing, so a backend that adds a chunk type can't break the stream. */ export declare function decodeNatsChunk(chunk: unknown): ChatStreamEvent | null; //# sourceMappingURL=nats-decoder.d.ts.map