@assistant-ui/runtime

Agent skills for building AI chat interfaces with [assistant-ui](https://assistant-ui.com).

View in AI SkillSafe app
48 downloads
0 stars
0 demos
SKILL.md
nameruntime
descriptionGuide to the assistant-ui runtime system, single-thread state, and the imperative aui client API in @assistant-ui/react. Use when creating a runtime (useLocalRuntime with a ChatModelAdapter, useExternalStoreRuntime for Redux/Zustand, useRemoteThreadListRuntime, useAssistantTransportRuntime), wiring AssistantRuntimeProvider, or reading/mutating thread, message, composer, and attachment state. Covers the unified hooks useAui, useAuiState, useAuiEvent and the v0.15 property accessors (aui.thread, aui.threads, aui.message, aui.composer, aui.part; availability via aui.thread.source != null), the s.optional.<scope> state view, scope methods (append, cancelRun, startRun, resumeRun, message({index}), part({index}), composer().send), capabilities, adapters (attachment, speech, dictation, suggestion, feedback, history), realtime voice, and types (ThreadMessage, MessageState, MessagePart, MessageStatus, ChatModelRunResult). The v0.12-era legacy context hooks (useAssistantRuntime, useThreadRuntime, useThread, useMessage, useComposer, useMessagePart, useAttachment) were removed in 0.15; route those to update. Use for provider \"Cannot read property of undefined\" errors or state not updating. For multi-thread list UI and switching between conversations use thread-list instead.
licenseMIT

assistant-ui Runtime

Always consult assistant-ui.com/llms.txt for the latest API.

References

Runtime Hierarchy

AssistantRuntime
├── ThreadListRuntime (thread management)
│   ├── ThreadListItemRuntime (per-thread item)
│   └── ...
└── ThreadRuntime (current thread)
    ├── ComposerRuntime (input state)
    └── MessageRuntime[] (per-message)
        └── MessagePartRuntime[] (per-content-part)

State Access (Modern API)

import { useAui, useAuiState, useAuiEvent } from "@assistant-ui/react";

function ChatControls() {
  const api = useAui();
  const messages = useAuiState(s => s.thread.messages);
  const isRunning = useAuiState(s => s.thread.isRunning);

  useAuiEvent("composer.send", (e) => {
    console.log("Sent in thread:", e.threadId);
  });

  return (
    <div>
      <button onClick={() => api.thread.append({
        role: "user",
        content: [{ type: "text", text: "Hello!" }],
      })}>
        Send
      </button>
      {isRunning && (
        <button onClick={() => api.thread.cancelRun()}>Cancel</button>
      )}
    </div>
  );
}

Scope Accessors Are Properties

As of 0.15, aui.<scope> is a property, not a call. Calling it still works but is deprecated. Methods on a scope keep their parentheses.

const aui = useAui();

aui.thread.getState();            // property accessor
aui.threads.switchToNewThread();
aui.thread.composer().send();     // composer() is a method of the thread scope
aui.thread.message({ index: 0 }); // selector object, not a bare index

Selecting an unavailable scope no longer throws; aui.message is always truthy. Check availability before use:

if (aui.message.source != null) {
  aui.message.reload();
}

source, query, and name are reserved accessor properties and never resolve to scope methods.

Thread Operations

const aui = useAui();
const thread = aui.thread;

thread.append({ role: "user", content: [{ type: "text", text: "Hello" }] });
thread.startRun({ parentId: null });
thread.cancelRun();

const state = thread.getState();  // { messages, isRunning, capabilities, composer, ... }

Message Operations

message() takes a selector object of { index } or { id }.

const message = aui.thread.message({ index: 0 });

message.reload();
message.switchToBranch({ position: "next" });
message.submitFeedback({ type: "positive" });

// Editing goes through the message's edit composer
const editComposer = message.composer();
editComposer.beginEdit();
editComposer.setText("Updated");
editComposer.send();

Events

Almost every event is now deprecated as state-derivable: derive from useAuiState instead of listening.

// Preferred: derive from state
const isRunning = useAuiState((s) => s.thread.isRunning);

// Still non-deprecated
useAuiEvent("thread.modelContextUpdate", ({ threadId }) => {});
useAuiEvent("composer.attachmentAddError", (e) => {});
Event Status
thread.modelContextUpdate Current (model context lives in a provider, not in state)
composer.attachmentAddError Current
composer.send, composer.attachmentAdd Deprecated: observe composer text / attachments
thread.runStart, thread.runEnd Deprecated: observe s.thread.isRunning
thread.initialize Deprecated: observe s.thread.messages becoming non-empty
threadListItem.switchedTo, threadListItem.switchedAway Deprecated: compare s.threads.mainThreadId

Optional Scopes

s.optional.<scope> resolves to undefined instead of throwing when a scope is not mounted, which is the safe way to read a scope from a component that renders both inside and outside it:

const partType = useAuiState((s) => s.optional.part?.type);

Capabilities

const caps = useAuiState(s => s.thread.capabilities);

RuntimeCapabilities: switchToBranch, switchBranchDuringRun, edit, reload, delete, cancel, unstable_copy, speech, dictation, voice, attachments, feedback, queue. Note unstable_copy and speech, not copy and speak.

Runtimes derive most of these from what you supply (a callback, an adapter) rather than from an explicit option.

Common Gotchas

"Cannot read property of undefined"

  • Ensure hooks are called inside AssistantRuntimeProvider
  • For a scope that may not be mounted, read s.optional.<scope> or guard on aui.<scope>.source != null

A legacy hook import fails to resolve

  • useAssistantRuntime, useThreadRuntime, useThread, useMessage, useComposer, useMessagePart, useAttachment, useThreadListItem, useThreadList and friends were removed in 0.15. See the /update skill for the mapping table.

State not updating

  • Use selectors with useAuiState to prevent unnecessary re-renders

Messages array empty

  • Check runtime is configured
  • Verify API response format

Embed badges

Add these to your README to show the skill's verification status.

SkillSafe verified badge
Verified badge
[![SkillSafe verified badge](https://api.skillsafe.ai/v1/badge/@assistant-ui/runtime/verified)](https://skillsafe.ai/skill/@assistant-ui/runtime/)
Installs badge
Installs badge
[![Installs badge](https://api.skillsafe.ai/v1/badge/@assistant-ui/runtime/installs)](https://skillsafe.ai/skill/@assistant-ui/runtime/)
Scan badge
Scan badge
[![Scan badge](https://api.skillsafe.ai/v1/badge/@assistant-ui/runtime/scan)](https://skillsafe.ai/skill/@assistant-ui/runtime/)
Eval pass rate badge
Eval pass rate
[![Eval pass rate badge](https://api.skillsafe.ai/v1/badge/@assistant-ui/runtime/eval)](https://skillsafe.ai/skill/@assistant-ui/runtime/)