External Store Runtime

Runtime components, options, and adapters for using assistant-ui with externally owned chat state.

API Reference

ExternalStoreAdapter

ExternalStoreAdapter
isDisabled?boolean | undefined

Whether the entire thread is disabled. When `true`, the composer's input is also disabled (the user cannot type, attach files, or submit). For a narrower gate that keeps the input usable but blocks only sending, use `isSendDisabled`.

isSendDisabled?boolean | undefined

Whether sending new messages is currently disabled. When `true`, the thread composer's input remains usable but `send()` becomes a no-op and the thread composer's `canSend` is `false`. Use this to gate sending on external React state (e.g. while tool config is loading) without disabling the input itself the way `isDisabled` does. Edit composers (saving message edits) intentionally ignore this flag.

isRunning?boolean | undefined

Whether the thread is running. When provided, this value flows directly to `thread.isRunning`, letting the application keep the thread in a running state even after the last assistant message has completed (for example while non-message stream chunks like suggestions or metadata updates are still arriving). When omitted, `thread.isRunning` falls back to the last-message-status heuristic.

isLoading?boolean | undefined

messages?readonly T[]

messageRepository?ExportedMessageRepository

ExportedMessageRepository
headId?string | null

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

unstable_messageRepositoryInstance?MessageRepository | undefinedunstable

An externally owned message repository instance. When provided, the thread runtime adopts it as its branch store and swaps to it atomically whenever a different instance is passed, so hosts that route multiple conversations through one runtime keep each conversation's history and branches isolated in its own instance. Omit it to keep the runtime's own repository.

ExternalStoreAdapter["unstable_messageRepositoryInstance"]
messagesMap<string, RepositoryMessage>

Map
clear() => void

delete(key: string) => boolean

forEach(callbackfn: (value: { children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; }, key: string, map: Map<string, { children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; }>) => void, thisArg?: any) => void

get(key: string) => ({ children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; })

has(key: string) => boolean

set(key: string, value: { children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; }) => Map<string, { children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; }>

sizenumber

entries() => MapIterator<[string, { children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; }]>

keys() => MapIterator<string>

values() => MapIterator<{ children: string[]; next: (RepositoryParent & { prev: (RepositoryParent & any) | null; current: ThreadMessage; level: number; }) | null; } & { prev: ({ children: string[]; next: (RepositoryParent & any) | null; } & any) | null; current: ThreadMessage; level: number; }>

headRepositoryMessage | null

ExternalStoreAdapter["unstable_messageRepositoryInstance"]["head"]
childrenstring[]

nextRepositoryMessage | null

ExternalStoreAdapter["unstable_messageRepositoryInstance"]["head"]["next"]
childrenstring[]

nextRepositoryMessage | null

prevRepositoryMessage | null

currentThreadMessage

levelnumber

prevRepositoryMessage | null

ExternalStoreAdapter["unstable_messageRepositoryInstance"]["head"]["prev"]
childrenstring[]

nextRepositoryMessage | null

prevRepositoryMessage | null

currentThreadMessage

levelnumber

currentThreadMessage

ThreadMessage
status?ThreadAssistantMessage["status"]

metadata{ readonly unstable_state?: ReadonlyJSONValue; readonly unstable_annotations?: readonly ReadonlyJSONValue[]; readonly unstable_data?: readonly ReadonlyJSONValue[]; readonly steps?: readonly ThreadStep[]; readonly submittedFeedback?: { readonly type: "positive" | "negative" }; readonly timing?: MessageTiming; readonly isOptimistic?: boolean; readonly custom: Record<string, unknown>; }

attachments?ThreadUserMessage["attachments"]

idstring

createdAtDate

role"system"

contentreadonly [TextMessagePart]

levelnumber

rootRepositoryParent

RepositoryParent
childrenstring[]

nextRepositoryMessage | null

RepositoryParent["next"]
childrenstring[]

nextRepositoryMessage | null

prevRepositoryMessage | null

currentThreadMessage

levelnumber

updateLevels(message: RepositoryMessage, newLevel: number) => void

selectPathTo(message: RepositoryMessage) => void

performOp(newParent: RepositoryMessage | null, child: RepositoryMessage, operation: "cut" | "link" | "relink") => void

_messagesCachedValue<readonly ThreadMessage[]>

CachedValue
_valueT | null

func() => T

valuereadonly ThreadMessage[]

dirty() => void

headIdstring | null

canonicalHeadIdstring | null

getMessages(headId?: string) => readonly ThreadMessage[]

addOrUpdateMessage(parentId: string | null, message: ThreadMessage) => void

getMessage(messageId: string) => { parentId: string | null; message: ThreadMessage; index: number; }

deleteMessage(messageId: string, replacementId?: string | null | undefined) => void

getBranches(messageId: string) => string[]

evictOffBranchOptimisticMessages(previousHead: RepositoryMessage | null, currentHead: RepositoryMessage | null) => void

Evicts optimistic messages (`metadata.isOptimistic`) the head just moved away from. Since eviction runs on every head move, the only optimistic messages in the repository live on the branch the head previously pointed at — so we walk just that branch rather than the whole repository. Keeps a client→server id swap from leaving a phantom sibling, and drops off-branch placeholders.

switchToBranch(messageId: string) => void

resetHead(messageId: string | null) => void

clear() => void

export() => ExportedMessageRepository

import({ headId, messages }: ExportedMessageRepository) => void

suggestions?readonly ThreadSuggestion[] | undefined

state?ReadonlyJSONValue | undefined

extras?unknown

setMessages?((messages: readonly T[]) => void) | undefined

Applies a message list the runtime rewrote, and is what tells the runtime a removal it makes will survive the next snapshot. Without it, cancelling a run leaves a trailing user message in the thread and the composer untouched; an adapter that removes that message itself owns handing it back, because the runtime cannot see a removal it did not make.

unstable_onBranchChange?((event: ExternalStoreBranchChange) => void) | undefineddeprecatedunstable

Fires when the user explicitly switches branches via the runtime's `switchToBranch` action (e.g. a BranchPicker click). It does not fire on adapter resync, `append`, edit/regenerate, content-only updates, or while the thread is running. Consecutive switches that resolve to the same canonical head are de-duped. `headId` is the canonical (persisted) head of the now-visible branch — optimistic/transient ids are never surfaced. `visibleMessageIds` lists the visible path in order. This complements `setMessages` rather than replacing it: switching still requires `setMessages`, and this callback does not on its own enable branch switching.

Deprecated: This API is still under active development and might change without notice.

onImport?((messages: readonly ThreadMessage[]) => void) | undefined

onExportExternalState?(() => any) | undefined

onLoadExternalState?((state: any) => void) | undefined

onNew(message: AppendMessage) => Promise<void>

queue?ExternalThreadQueueAdapter | undefined

Opt in to message queuing. Typically produced by `createMessageQueue`.

ExternalStoreAdapter["queue"]
itemsreadonly QueueItemState[]

steerItemsreadonly QueueItemState[]

enqueue(message: AppendMessage) => void

Send a message into the queue lane, processed in order.

steer(message: AppendMessage) => void

Send a message into the steer lane, processed next.

move(queueItemId: string, placement: QueuePlacement) => void

Move a queued message between lanes or within a lane. An unanchored move into the steer lane mid-run interrupts — cancels the live run and dispatches the item — when the runtime supports cancellation; a move with `insertAfter`/`insertBefore` only places the item and never interrupts.

edit(queueItemId: string, message: AppendMessage) => void

remove(queueItemId: string) => void

onEdit?((message: AppendMessage) => Promise<void>) | undefined

onDelete?((messageId: string) => Promise<void> | void) | undefined

onReload?((parentId: string | null, config: StartRunConfig) => Promise<void>) | undefined

onResume?((config: ResumeRunConfig) => Promise<void>) | undefined

onCancel?(() => Promise<void>) | undefined

onRefetchThread?(() => Promise<void>) | undefined

Re-fetches the thread's state from the backing store, in place; a rejection reaches the `threads.reloadMainThread()` caller. Unrelated to `onReload`, which re-generates an assistant message. The caller does not stop a run in progress first, so an adapter that can stream owns whatever coordination one needs.

onAddToolResult?((options: AddToolResultOptions) => Promise<void> | void) | undefined

onResumeToolCall?((options: { toolCallId: string; payload: unknown }) => void) | undefined

onRespondToToolApproval?((options: RespondToToolApprovalOptions) => Promise<void> | void) | undefined

convertMessage?ExternalStoreMessageConverter<T> | undefined

adapters?ExternalStoreAdapter["adapters"]

ExternalStoreAdapter["adapters"]
attachments?AttachmentAdapter | undefined

ExternalStoreAdapter["adapters"]["attachments"]
acceptstring

add(state: { file: File; }) => Promise<PendingAttachment> | AsyncGenerator<PendingAttachment, void>

remove(attachment: Attachment) => Promise<void>

send(attachment: PendingAttachment) => Promise<CompleteAttachment>

speech?SpeechSynthesisAdapter | undefined

ExternalStoreAdapter["adapters"]["speech"]
speak(text: string) => SpeechSynthesisAdapter.Utterance

dictation?DictationAdapter | undefined

ExternalStoreAdapter["adapters"]["dictation"]
listen() => DictationAdapter.Session

disableInputDuringDictation?boolean

voice?RealtimeVoiceAdapter | undefined

ExternalStoreAdapter["adapters"]["voice"]
connect(options: { abortSignal?: AbortSignal; }) => RealtimeVoiceAdapter.Session

feedback?FeedbackAdapter | undefined

ExternalStoreAdapter["adapters"]["feedback"]
submit(feedback: FeedbackAdapterFeedback) => void

threadList?ExternalStoreThreadListAdapter | undefineddeprecated

Deprecated: This API is still under active development and might change without notice.

ExternalStoreAdapter["adapters"]["threadList"]
threadId?string | undefineddeprecated

Deprecated: This API is still under active development and might change without notice.

isLoading?boolean | undefined

threads?readonly ExternalStoreThreadData<"regular">[] | undefined

archivedThreads?readonly ExternalStoreThreadData<"archived">[] | undefined

onSwitchToNewThread?(() => Promise<void> | void) | undefineddeprecated

Deprecated: This API is still under active development and might change without notice.

onSwitchToThread?((threadId: string) => Promise<void> | void) | undefineddeprecated

Deprecated: This API is still under active development and might change without notice.

onRename?( threadId: string, newTitle: string, ) => (Promise<void> | void) | undefined

onUpdateCustom?(( threadId: string, custom: Record<string, unknown> | undefined, ) => Promise<void> | void) | undefined

onArchive?((threadId: string) => Promise<void> | void) | undefined

onUnarchive?((threadId: string) => Promise<void> | void) | undefined

onDelete?((threadId: string) => Promise<void> | void) | undefined

unstable_capabilities?ExternalStoreAdapter["unstable_capabilities"]unstable

ExternalStoreAdapter["unstable_capabilities"]
copy?boolean | undefined

unstable_enableToolInvocations?boolean | undefinedunstable

Opt in to the built-in client-side tool-invocations pipeline (`streamCall` / `execute` / tool-status tracking) for this thread. Defaults to `false` — the runtime does *not* drive client-side tool callbacks on its own. Set to `true` to have the runtime construct a `ToolInvocationTracker` and feed every snapshot through it, so tool callbacks fire automatically for tool-call parts in `messages`. Opt-in by default because most external-store runtimes either run tools entirely server-side, or already wire their own client-side dispatch path. Enabling the embedded tracker on top of an existing dispatch path would cause tool callbacks to run twice. When enabled, client-side tool results (from `execute()` returning, or from `streamCall` resolving) flow back through `adapter.onAddToolResult` like any other tool result, with `modelContent` populated when present.

unstable_isClientToolCall?((toolCall: ToolCallMessagePart) => boolean) | undefinedunstable

Decides whether a tool call's result is produced on the client. Only consulted when `unstable_enableToolInvocations` is `true`. A provider that runs tools itself answers its own calls, and its result arrives one or more snapshots after the call's arguments complete. In that window the call is complete and result-less, so a registered tool of the same name would otherwise execute locally and produce a result the provider never asked for. An adapter that can tell the two apart supplies this predicate; it is read once per tool call, when the call is first observed live. The predicate is also what licenses running a frontend tool while the provider's run is still open. Without it, ownership is unknown until the run ends, so a registered tool executes only once the run's outcome is known and cannot fire on a call the provider was about to answer or gate.

setToolStatuses?((statuses: Record<string, ToolExecutionStatus>) => void) | undefined

Receives the current per-tool-call execution status map whenever it changes. Only invoked when `unstable_enableToolInvocations` is `true` — the runtime maintains the map via the embedded tracker. Wire this into local React state and feed it into the converter's `metadata.toolStatuses` so the UI can render `executing` spinners and human-input prompts.

ExternalThread

ExternalThread props
0ExternalThreadProps

ExternalThreadProps
messagesreadonly ExternalThreadMessage[]

isRunning?boolean

isLoading?boolean | undefined

state?ReadonlyJSONValue | undefined

extras?unknown

isSendDisabled?boolean

Whether sending new messages is currently disabled. When `true`, the thread composer's input remains usable but `send()` is a no-op and `composer.canSend` is `false`. Edit composers (saving message edits) intentionally ignore this flag.

onNew?(message: AppendMessage) => void

Callback for new messages (non-queue runtimes).

onEdit?(message: AppendMessage) => void

onReload?(parentId: string | null) => void

onStartRun?() => void

onCancel?() => void

onResume?(() => void) | undefined

onRefetchThread?(() => Promise<void>) | undefined

Handler for re-fetching this thread's state in place, driving `threads.reloadMainThread()`. Unrelated to `onReload`, which re-generates an assistant message. Presence enables the `refetchThread` capability; rejections propagate to the caller.

onAddToolResult?((options: AddToolResultOptions) => void) | undefined

onResumeToolCall?((options: ResumeToolCallOptions) => void) | undefined

Callback for resuming a tool call that is waiting for human input.

onLoadExternalState?((state: unknown) => void) | undefined

attachmentAdapter?AttachmentAdapter | undefined

ExternalThreadProps["attachmentAdapter"]
acceptstring

add(state: { file: File; }) => Promise<PendingAttachment> | AsyncGenerator<PendingAttachment, void>

remove(attachment: Attachment) => Promise<void>

send(attachment: PendingAttachment) => Promise<CompleteAttachment>

feedbackAdapter?FeedbackAdapter | undefined

ExternalThreadProps["feedbackAdapter"]
submit(feedback: FeedbackAdapterFeedback) => void

speechAdapter?SpeechSynthesisAdapter | undefined

ExternalThreadProps["speechAdapter"]
speak(text: string) => SpeechSynthesisAdapter.Utterance

queue?ExternalThreadQueueAdapter

Queue adapter for runtimes that support message queuing and steering.

ExternalThreadQueueAdapter
itemsreadonly QueueItemState[]

steerItemsreadonly QueueItemState[]

enqueue(message: AppendMessage) => void

Send a message into the queue lane, processed in order.

steer(message: AppendMessage) => void

Send a message into the steer lane, processed next.

move(queueItemId: string, placement: QueuePlacement) => void

Move a queued message between lanes or within a lane. An unanchored move into the steer lane mid-run interrupts — cancels the live run and dispatches the item — when the runtime supports cancellation; a move with `insertAfter`/`insertBefore` only places the item and never interrupts.

edit(queueItemId: string, message: AppendMessage) => void

remove(queueItemId: string) => void

branches?ExternalThreadBranchAdapter

Branch adapter for runtimes that track sibling variants of messages.

ExternalThreadBranchAdapter
getBranches(messageId: string) => readonly string[]

Returns the sibling branch ids for a message in display order, including the message's own id. Return an empty array for messages without alternative branches.

switchToBranch(branchId: string) => void

Makes the given branch the visible one. The runtime is expected to swap the `messages` array to the selected branch. May be invoked programmatically while a run is in progress; pending queue items are not cleared, and reconciling them with the new branch is the runtime's responsibility.

onRespondToToolApproval?( options: RespondToToolApprovalOptions, ) => void | Promise<void>

Callback for tool approval decisions. Absent: responding to an approval throws a capability error.

length1

toString() => string

toLocaleString{ (): string; (locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string; }

pop() => ExternalThreadProps

push(...items: ExternalThreadProps[]) => number

concat{ (...items: ConcatArray<ExternalThreadProps>[]): ExternalThreadProps[]; (...items: (ExternalThreadProps | ConcatArray<ExternalThreadProps>)[]): ExternalThreadProps[]; }

join(separator?: string) => string

reverse() => ExternalThreadProps[]

shift() => ExternalThreadProps

slice(start?: number, end?: number) => ExternalThreadProps[]

sort(compareFn?: ((a: ExternalThreadProps, b: ExternalThreadProps) => number) | undefined) => [ExternalThreadProps]

splice{ (start: number, deleteCount?: number): ExternalThreadProps[]; (start: number, deleteCount: number, ...items: ExternalThreadProps[]): ExternalThreadProps[]; }

unshift(...items: ExternalThreadProps[]) => number

indexOf(searchElement: ExternalThreadProps, fromIndex?: number) => number

lastIndexOf(searchElement: ExternalThreadProps, fromIndex?: number) => number

every{ <S>(predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => unknown, thisArg?: any): boolean; }

some(predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => unknown, thisArg?: any) => boolean

forEach(callbackfn: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => void, thisArg?: any) => void

map<U>(callbackfn: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => U, thisArg?: any) => U[]

filter{ <S>(predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => value is S, thisArg?: any): S[]; (predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => unknown, thisArg?: any): ExternalThreadProps[]; }

reduce{ (callbackfn: (previousValue: ExternalThreadProps, currentValue: ExternalThreadProps, currentIndex: number, array: ExternalThreadProps[]) => ExternalThreadProps): ExternalThreadProps; (callbackfn: (previousValue: ExternalThreadProps, currentValue: ExternalThreadProps, currentIndex: number, array: ExternalThreadProps[]) => ExternalThreadProps, initialValue: ExternalThreadProps): ExternalThreadProps; <U>(callbackfn: (previousValue: U, currentValue: ExternalThreadProps, currentIndex: number, array: ExternalThreadProps[]) => U, initialValue: U): U; }

reduceRight{ (callbackfn: (previousValue: ExternalThreadProps, currentValue: ExternalThreadProps, currentIndex: number, array: ExternalThreadProps[]) => ExternalThreadProps): ExternalThreadProps; (callbackfn: (previousValue: ExternalThreadProps, currentValue: ExternalThreadProps, currentIndex: number, array: ExternalThreadProps[]) => ExternalThreadProps, initialValue: ExternalThreadProps): ExternalThreadProps; <U>(callbackfn: (previousValue: U, currentValue: ExternalThreadProps, currentIndex: number, array: ExternalThreadProps[]) => U, initialValue: U): U; }

find{ <S>(predicate: (value: ExternalThreadProps, index: number, obj: ExternalThreadProps[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: ExternalThreadProps, index: number, obj: ExternalThreadProps[]) => unknown, thisArg?: any): ExternalThreadProps | undefined; }

findIndex(predicate: (value: ExternalThreadProps, index: number, obj: ExternalThreadProps[]) => unknown, thisArg?: any) => number

fill(value: ExternalThreadProps, start?: number, end?: number) => [ExternalThreadProps]

copyWithin(target: number, start: number, end?: number) => [ExternalThreadProps]

entries() => ArrayIterator<[number, ExternalThreadProps]>

keys() => ArrayIterator<number>

values() => ArrayIterator<ExternalThreadProps>

includes(searchElement: ExternalThreadProps, fromIndex?: number) => boolean

flatMap<U, This>(callback: (this: This, value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => U | readonly U[], thisArg?: This | undefined) => U[]

flat<A, D>(this: A, depth?: D | undefined) => FlatArray<A, D>[]

at(index: number) => ExternalThreadProps

findLast{ <S>(predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => unknown, thisArg?: any): ExternalThreadProps | undefined; }

findLastIndex(predicate: (value: ExternalThreadProps, index: number, array: ExternalThreadProps[]) => unknown, thisArg?: any) => number

toReversed() => ExternalThreadProps[]

toSorted(compareFn?: ((a: ExternalThreadProps, b: ExternalThreadProps) => number) | undefined) => ExternalThreadProps[]

toSpliced{ (start: number, deleteCount: number, ...items: ExternalThreadProps[]): ExternalThreadProps[]; (start: number, deleteCount?: number): ExternalThreadProps[]; }

with(index: number, value: ExternalThreadProps) => ExternalThreadProps[]

ExternalThreadProps

ExternalThreadProps
messagesreadonly ExternalThreadMessage[]

isRunning?boolean

isLoading?boolean | undefined

state?ReadonlyJSONValue | undefined

extras?unknown

isSendDisabled?boolean

Whether sending new messages is currently disabled. When `true`, the thread composer's input remains usable but `send()` is a no-op and `composer.canSend` is `false`. Edit composers (saving message edits) intentionally ignore this flag.

onNew?(message: AppendMessage) => void

Callback for new messages (non-queue runtimes).

onEdit?(message: AppendMessage) => void

onReload?(parentId: string | null) => void

onStartRun?() => void

onCancel?() => void

onResume?(() => void) | undefined

onRefetchThread?(() => Promise<void>) | undefined

Handler for re-fetching this thread's state in place, driving `threads.reloadMainThread()`. Unrelated to `onReload`, which re-generates an assistant message. Presence enables the `refetchThread` capability; rejections propagate to the caller.

onAddToolResult?((options: AddToolResultOptions) => void) | undefined

onResumeToolCall?((options: ResumeToolCallOptions) => void) | undefined

Callback for resuming a tool call that is waiting for human input.

onLoadExternalState?((state: unknown) => void) | undefined

attachmentAdapter?AttachmentAdapter | undefined

ExternalThreadProps["attachmentAdapter"]
acceptstring

add(state: { file: File; }) => Promise<PendingAttachment> | AsyncGenerator<PendingAttachment, void>

remove(attachment: Attachment) => Promise<void>

send(attachment: PendingAttachment) => Promise<CompleteAttachment>

feedbackAdapter?FeedbackAdapter | undefined

ExternalThreadProps["feedbackAdapter"]
submit(feedback: FeedbackAdapterFeedback) => void

speechAdapter?SpeechSynthesisAdapter | undefined

ExternalThreadProps["speechAdapter"]
speak(text: string) => SpeechSynthesisAdapter.Utterance

queue?ExternalThreadQueueAdapter

Queue adapter for runtimes that support message queuing and steering.

ExternalThreadQueueAdapter
itemsreadonly QueueItemState[]

steerItemsreadonly QueueItemState[]

enqueue(message: AppendMessage) => void

Send a message into the queue lane, processed in order.

steer(message: AppendMessage) => void

Send a message into the steer lane, processed next.

move(queueItemId: string, placement: QueuePlacement) => void

Move a queued message between lanes or within a lane. An unanchored move into the steer lane mid-run interrupts — cancels the live run and dispatches the item — when the runtime supports cancellation; a move with `insertAfter`/`insertBefore` only places the item and never interrupts.

edit(queueItemId: string, message: AppendMessage) => void

remove(queueItemId: string) => void

branches?ExternalThreadBranchAdapter

Branch adapter for runtimes that track sibling variants of messages.

ExternalThreadBranchAdapter
getBranches(messageId: string) => readonly string[]

Returns the sibling branch ids for a message in display order, including the message's own id. Return an empty array for messages without alternative branches.

switchToBranch(branchId: string) => void

Makes the given branch the visible one. The runtime is expected to swap the `messages` array to the selected branch. May be invoked programmatically while a run is in progress; pending queue items are not cleared, and reconciling them with the new branch is the runtime's responsibility.

onRespondToToolApproval?( options: RespondToToolApprovalOptions, ) => void | Promise<void>

Callback for tool approval decisions. Absent: responding to an approval throws a capability error.

ExternalThreadQueueAdapter

The queue surface a runtime exposes so the composer can stay usable during a run and render the pending messages.

ExternalThreadQueueAdapter
itemsreadonly QueueItemState[]

steerItemsreadonly QueueItemState[]

enqueue(message: AppendMessage) => void

Send a message into the queue lane, processed in order.

steer(message: AppendMessage) => void

Send a message into the steer lane, processed next.

move(queueItemId: string, placement: QueuePlacement) => void

Move a queued message between lanes or within a lane. An unanchored move into the steer lane mid-run interrupts — cancels the live run and dispatches the item — when the runtime supports cancellation; a move with `insertAfter`/`insertBefore` only places the item and never interrupts.

edit(queueItemId: string, message: AppendMessage) => void

remove(queueItemId: string) => void

pickExternalStoreSharedOptions

const pickExternalStoreSharedOptions: (options: ExternalStoreSharedOptions) => ExternalStoreSharedOptions;

useExternalStoreRuntime

useExternalStoreRuntime
storeExternalStoreAdapter<T>

useExternalStoreSharedOptions

useExternalStoreSharedOptions
optionsExternalStoreSharedOptions

ExternalStoreSharedOptions
suggestions?readonly ThreadSuggestion[] | undefined

isDisabled?boolean | undefined

Whether the entire thread is disabled. When `true`, the composer's input is also disabled (the user cannot type, attach files, or submit). For a narrower gate that keeps the input usable but blocks only sending, use `isSendDisabled`.

isSendDisabled?boolean | undefined

Whether sending new messages is currently disabled. When `true`, the thread composer's input remains usable but `send()` becomes a no-op and the thread composer's `canSend` is `false`. Use this to gate sending on external React state (e.g. while tool config is loading) without disabling the input itself the way `isDisabled` does. Edit composers (saving message edits) intentionally ignore this flag.

unstable_capabilities?ExternalStoreSharedOptions["unstable_capabilities"]unstable

ExternalStoreSharedOptions["unstable_capabilities"]
copy?boolean | undefined