Persistence adapters for saving assistant-ui message history, remote thread lists, and long-running chat sessions across browser reloads.
API Reference
ExportedMessageRepository
- headId?string | null
- messagesArray<{ message: ThreadMessage; parentId: string | null; runConfig?: RunConfig; }>
GenericThreadHistoryAdapter
- load() => Promise<MessageFormatRepository<TMessage>>
- pin?() => void
Snapshot the current thread so later writes survive a switch.
- append(item: MessageFormatItem<TMessage>) => Promise<void>
- update?(item: MessageFormatItem<TMessage>, localMessageId: string) => Promise<void>
- delete?(items: MessageFormatItem<TMessage>[]) => Promise<void>
- reportTelemetry?(items: MessageFormatItem<TMessage>[], options?: { durationMs?: number; stepTimestamps?: { start_ms: number; end_ms: number; }[]; }) => void
InMemoryThreadListAdapter
- list?() => Promise<RemoteThreadListResponse>
- rename?() => Promise<void>
- updateCustom?() => Promise<void>
- archive?() => Promise<void>
- unarchive?() => Promise<void>
- delete?() => Promise<void>
- initialize?(threadId: string) => Promise<RemoteThreadInitializeResponse>
- generateTitle?() => Promise<AssistantStream>
- fetch?(threadId: string) => Promise<RemoteThreadMetadata>
MessageFormatAdapter
- formatstring
- encode(item: MessageFormatItem<TMessage>) => TStorageFormat
- decode(stored: MessageStorageEntry<TStorageFormat>) => MessageFormatItem<TMessage>
- getId(message: TMessage) => string
RemoteThreadListAdapter
const runtime = useRemoteThreadListRuntime({
adapter: myRemoteThreadListAdapter,
runtimeHook: () => useLocalRuntime(chatModelAdapter),
});- list(params?: RemoteThreadListPageOptions) => Promise<RemoteThreadListResponse>
- rename(remoteId: string, newTitle: string) => Promise<void>
- updateCustom?(remoteId: string, custom: Record<string, unknown> | undefined) => Promise<void>
- archive(remoteId: string) => Promise<void>
- unarchive(remoteId: string) => Promise<void>
- delete(remoteId: string) => Promise<void>
- initialize(threadId: string) => Promise<RemoteThreadInitializeResponse>
- generateTitle(remoteId: string, unstable_messages: readonly ThreadMessage[]) => Promise<AssistantStream>
- fetch(threadId: string) => Promise<RemoteThreadMetadata>
- unstable_Provider?RemoteThreadListProviderComponent | undefinedunstable
Optional React component wrapped around each active thread. Use it to inject per-thread context such as a history or attachments adapter (see `useCloudThreadListAdapter` for the canonical shape). `useRemoteThreadListRuntime` renders this component when present. If it is omitted, that host synthesizes a `RuntimeAdapterProvider` from `unstable_useAdapters`. The `RemoteThreadList` store entry ignores it; expose `unstable_useAdapters` for that host. The Provider must render `children` on its first commit; deferring them behind a loading state, a Suspense boundary, or a `useEffect`-gated render is unsupported and leaves thread context unavailable to downstream consumers. Load data inside an always-mounted child instead.
- unstable_useAdapters?(() => RuntimeAdapters | null | undefined) | undefinedunstable
Hook the `RemoteThreadList` store entry calls once per mounted thread body (the main-thread slot by default; every started thread with `backgroundThreads`), then provides to the `thread` factory. This is not mounted per listed thread. `useRemoteThreadListRuntime` also calls it when `unstable_Provider` is omitted. Resolve `threadListItem` lazily on each adapter call; do not capture it at hook mount. The hook must keep a stable hook count across adapter swaps; a different count throws. Memoize the returned object. Per-thread history also requires the `thread` factory to be keyed by thread id (`withKey(id, thread(...))`). History loaders such as `useExternalHistory` run once per mount; an unkeyed factory keeps one instance across switches and the next thread's messages never load.
ThreadHistoryAdapter
const runtime = useLocalRuntime(chatModelAdapter, {
adapters: {
history: myHistoryAdapter,
},
});- load() => Promise<ExportedMessageRepository & { state?: ReadonlyJSONValue; unstable_resume?: boolean; }>
- resume?(options: ChatModelRunOptions) => AsyncGenerator<ChatModelRunResult, void, unknown>
- append(item: ExportedMessageRepositoryItem) => Promise<void>
- update?(item: ExportedMessageRepositoryItem) => Promise<void>
Rewrites a previously appended message in place, keyed by its message id. Adapters that implement this let a runtime persist a run paused for tool approval and finalize the same message once the run resumes. An update may arrive for an id whose earlier write failed; treat it as an upsert keyed on the message id rather than assuming the entry exists.
- delete?(items: ExportedMessageRepositoryItem[]) => Promise<void>
- withFormat?<TMessage, TStorageFormat extends Record<string, unknown>>(formatAdapter: MessageFormatAdapter<TMessage, TStorageFormat>) => GenericThreadHistoryAdapter<TMessage>
Required when used with `useAISDKRuntime` / `useChatRuntime`.