Persistence Adapters

Persistence adapters for saving assistant-ui message history, remote thread lists, and long-running chat sessions across browser reloads.

API Reference

ExportedMessageRepository

ExportedMessageRepository
headId?string | null

messagesArray<{ message: ThreadMessage; parentId: string | null; runConfig?: RunConfig; }>

GenericThreadHistoryAdapter

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

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

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),
});
RemoteThreadListAdapter
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,
  },
});
ThreadHistoryAdapter
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`.