Conversation and send lifecycle
Conversation normalization and send lifecycle
Section titled “Conversation normalization and send lifecycle”Use @alneas/ai-core/ai-chat (or its /conversation and /session subpaths)
without React, an AI SDK, storage, or a provider SDK:
import { normalizeAIChatMessages, streamAIChat } from '@alneas/ai-core/ai-chat';const messages = normalizeAIChatMessages([ { id: 'user-1', role: 'user', parts: [{ type: 'text', text: 'Hello' }] },]);for await (const event of streamAIChat(adapter, { messages, signal })) { // Render or route normalized events in the consuming application.}adapter is an AIChatAdapter, for example createOpenAIChatAdapter or
createVercelAIChatAdapter, or an injected implementation. signal is an optional
AbortSignal. The Vercel adapter parses its HTTP protocol; it does not import the
ai npm SDK. Credentials and endpoint configuration belong to the application.
History normalization preserves order, nonempty IDs, roles (system/user/assistant/ tool), text/file/tool-call/tool-result/data parts, and JSON metadata. Tool-role messages contain only tool results. It does not invent messages, trim history, match tool call/result pairs globally, authorize tools or execute them. A missing required field or non-JSON value fails before the adapter is started. Unknown JSON properties are preserved. URLs/media types are checked as nonempty strings, not as safe navigation targets; enforce file/content policies in the application.
The result is detached: a new outer array containing recursively frozen snapshots. The entire history is bounded by depth 64, 10,000 visited values and 1,000,000 serialized JavaScript UTF-16 code units. Accessors, cycles, undefined properties, non-finite numbers and non-JSON objects are rejected. No silent truncation occurs. Stream events undergo the same JSON boundary plus event-shape validation.
createAIChatStream(adapter, request) snapshots the request immediately and starts
the adapter lazily on read. streamAIChat is the async-iterable convenience form;
it snapshots on first iteration. Tools and parameters are detached snapshots too.
A stream must emit an explicit finish, then EOF. Missing finish and events after
finish reject. maxResponseChars defaults to 1,000,000, configurable from 1 through
10,000,000; it counts the sum of serialized normalized events, not wire bytes.
Each event also has the JSON boundary limits. HTTP adapters separately bound the
received protocol. Configure network/request limits at the transport/server too.
Parent abort, reader cancellation, validation failures and early iteration return
abort the supplied adapter signal and request iterator cleanup. Pending reads
settle promptly even if the source ignores cancellation. An uncooperative
iterator’s return() is requested without awaiting a blocked next(); it can
finish later. Adapters must honor AbortSignal to actually release network work.
A consumer must drain or cancel its stream. Streams are not resumable and do not
retry, persist messages, subscribe to browser events or execute tools automatically.
For AI SDK 7 UIMessage/ChatTransport, use the optional
@alneas/react-ai/ai-chat binding. SDK shape mapping belongs there; generic
validation and send lifecycle remain here. Browser persistence remains in the UI.