Runtime components, options, and adapters for using assistant-ui with externally owned chat state.
API Reference
ExternalStoreAdapter
- unstable_persistsHistory?boolean | undefinedunstable
The runtime writes its messages to the thread list's history adapter itself, so `useExternalStoreRuntime` does not copy them.
- 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 | undefined; readonly unstable_annotations?: readonly ReadonlyJSONValue[] | undefined; readonly unstable_data?: readonly ReadonlyJSONValue[] | undefined; readonly steps?: readonly ThreadStep[] | undefined; readonly submittedFeedback?: | { readonly type: "positive" | "negative"; readonly comment?: string; } | undefined; readonly timing?: MessageTiming | undefined; readonly isOptimistic?: boolean; readonly modality?: MessageModality | undefined; 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`) on the branch the head just moved away from. Only that branch is walked, so an optimistic message added off the head branch (such as the server-id copy that replaces a client-id placeholder before the head moves to it) is kept until a `switchToBranch` or `resetHead` moves the head off the branch it is on. Keeps a client→server id swap from leaving a phantom sibling.
- 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.
- onVoiceTranscript?((message: ThreadMessage) => void) | undefined
Called with each message a voice session adds to the thread: every finalized transcript, and every text message typed into a session that takes typed text (it carries no `metadata.modality`). The host appends it to its own messages under the same id, which is how the runtime knows the host carries it. A message that finalizes while `isLoading` is true is delivered once loading ends, to the callback on the adapter current at that moment, and is not delivered at all when the conversation changed while it waited. A host that routes conversations through one runtime is recognized by its `unstable_messageRepositoryInstance`; one that swaps only its `messages` cannot be told apart from a load finishing. A host that does not implement this callback keeps these messages for the session only.
- 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
Removes a message from the host's store. The runtime drops the message from its branches when `messages` stops carrying the id, and reads a `messages` update that still carries it after every call for it has settled as a declined delete. A host that accepts the delete therefore publishes the removal before the returned promise settles.
- 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
- unstable_onRecordToolInteraction?((options: Unstable_RecordToolInteractionOptions) => Promise<void> | void) | undefinedunstable
Stores a user interaction on a tool call part of a message this store owns and exposes it on that part's `unstable_interactions`. Without it, recording an interaction rejects and nothing is kept.
- 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, options?: { signal?: AbortSignal; }) => 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
- 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, options?: { signal?: AbortSignal; }) => 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.
- unstable_onRecordToolInteraction?((options: Unstable_RecordToolInteractionOptions) => void | Promise<void>) | undefinedunstable
- 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
- 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, options?: { signal?: AbortSignal; }) => 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.
- unstable_onRecordToolInteraction?((options: Unstable_RecordToolInteractionOptions) => void | Promise<void>) | undefinedunstable
ExternalThreadQueueAdapter
The queue surface a runtime exposes so the composer can stay usable during a run and render the pending messages.
- 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
- storeExternalStoreAdapter<T>
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