API Reference

All exports from @assistant-ui/store.

Hooks

useAui()

function useAui(): AssistantClient;

Returns the store from the nearest AuiProvider. Does not re-render when scopes change — returns a stable reference.

useAui(scopes)

function useAui(scopes: useAui.Props): AssistantClient;

Takes the store from the nearest AuiProvider and extends it by filling the provided scopes. Returns a new AssistantClient that includes both the parent's scopes and the newly provided ones.

type useAui.Props = {
  [K in ClientNames]?: ClientElement<K> | DerivedElement<K>;
};

useAuiState

function useAuiState<T>(selector: (state: AssistantState) => T): T;

Subscribes to a slice of state. Re-renders only when the selected value changes (compared by Object.is). The selector must return a specific value — not the entire state object.

useAuiEvent

function useAuiEvent<TEvent extends AssistantEventName>(
  selector: AssistantEventSelector<TEvent>,
  callback: AssistantEventCallback<TEvent>,
): void;

Subscribes to events. The selector can be a string ("scope.event") or an object ({ scope, event }). Unsubscribes on unmount.


Components

AuiProvider

<AuiProvider value={aui}>{children}</AuiProvider>

Provides an AssistantClient to the React tree. Child components can access it via useAui().

AuiIf

<AuiIf condition={(s) => s.counter.count > 0}>
  <ResetButton />
</AuiIf>

Renders children only when the condition returns true. Uses useAuiState internally.

RenderChildrenWithAccessor

<RenderChildrenWithAccessor
  getItemState={(aui) => aui.todoList().todo({ index }).getState()}
>
  {(getItem) =>
    children({
      get todo() {
        return getItem();
      },
    })
  }
</RenderChildrenWithAccessor>

Sets up a lazy item accessor for list rendering. The getItem function defers reading state until the consumer accesses it — if the children render function never reads the item, no subscription is created. When children returns a propless component (e.g. {() => <Todo />}), the output is automatically memoized.

PropType
getItemState(aui: AssistantClient) => T
children(getItem: () => T) => ReactNode

See Rendering Lists for the full pattern.


Resource utilities

Derived

function Derived<K extends ClientNames>(config: Derived.Props<K>): DerivedElement<K>;

Creates a derived scope that points to data in a parent scope.

// static meta
Derived({
  source: "thread",
  query: { index: 0 },
  get: (aui) => aui.thread().message({ index: 0 }),
});

// dynamic meta
Derived({
  getMeta: (aui) => ({ source: "thread", query: { index } }),
  get: (aui) => aui.thread().message({ index }),
});

The get function receives the current AssistantClient and must return the result of calling a parent scope method. It uses tapEffectEvent internally — always calls the latest closure.

attachTransformScopes

function attachTransformScopes<T extends (...args: any[]) => ResourceElement<any>>(
  resource: T,
  transform: (scopes: ScopesConfig, parent: AssistantClient) => ScopesConfig,
): void;

Attaches a transform function to a resource. When the resource is mounted via useAui, the transform runs and can add or modify sibling scopes. Transforms are applied iteratively — new root scopes trigger their own transforms.

One transform per resource. Throws on duplicate.


Tap utilities

These are used inside Tap resources to integrate with Store.

tapClientResource

function tapClientResource<TMethods extends ClientMethods>(
  element: ResourceElement<TMethods>,
): {
  state: InferClientState<TMethods>;
  methods: TMethods;
  key: string | number | undefined;
};

Wraps a single resource element into a client. Adds the client to the internal client stack for event scoping.

state is inferred from the element's getState() return type. If getState is not defined, state is undefined.

tapClientLookup

function tapClientLookup<TMethods extends ClientMethods>(
  getElements: () => readonly ResourceElement<TMethods>[],
  getElementsDeps: readonly unknown[],
): {
  state: InferClientState<TMethods>[];
  get: (lookup: { index: number } | { key: string }) => TMethods;
};

Wraps a list of resource elements into clients. Each element must have a key (via withKey). Uses tapClientResource internally for each element.

get resolves a client by index or key. Throws if the lookup doesn't match.

tapClientList

function tapClientList<TData, TMethods extends ClientMethods>(
  props: tapClientList.Props<TData, TMethods>,
): {
  state: InferClientState<TMethods>[];
  get: (lookup: { index: number } | { key: string }) => TMethods;
  add: (data: TData) => void;
};

Manages a dynamic list of clients with add/remove. Built on tapClientLookup.

type tapClientList.Props<TData, TMethods> = {
  initialValues: TData[];
  getKey: (data: TData) => string;
  resource: ContravariantResource<TMethods, tapClientList.ResourceProps<TData>>;
};

type tapClientList.ResourceProps<TData> = {
  key: string;
  getInitialData: () => TData;
  remove: () => void;
};

getInitialData() is called once on mount. remove() removes the item from the list. Throws on duplicate key.

tapAssistantClientRef

function tapAssistantClientRef(): {
  parent: AssistantClient;
  current: AssistantClient | null;
};

Returns a ref to the store being built. current is null during resource creation and populated after all sibling scopes are mounted. Use in tapEffect to access sibling scopes at runtime.

tapAssistantEmit

function tapAssistantEmit(): <TEvent extends Exclude<AssistantEventName, "*">>(
  event: TEvent,
  payload: AssistantEventPayload[TEvent],
) => void;

Returns a stable emit function. Events are delivered via microtask — listeners fire after the current state update settles.


Types

ScopeRegistry

interface ScopeRegistry {}

Module augmentation point. Augment this interface to register scopes:

declare module "@assistant-ui/store" {
  interface ScopeRegistry {
    counter: {
      methods: {
        getState: () => { count: number };
        increment: () => void;
      };
      meta?: { source: ClientNames; query: Record<string, unknown> };
      events?: { "counter.incremented": { newCount: number } };
    };
  }
}

methods is required. meta and events are optional.

ClientOutput

type ClientOutput<K extends ClientNames> = ClientSchemas[K]["methods"] & ClientMethods;

The return type for a resource implementing scope K. Use as the return type annotation on your resource function.

ClientNames

type ClientNames = keyof ClientSchemas;

Union of all registered scope names.

AssistantClient

type AssistantClient = {
  [K in ClientNames]: AssistantClientAccessor<K>;
} & {
  subscribe(listener: () => void): Unsubscribe;
  on<TEvent extends AssistantEventName>(
    selector: AssistantEventSelector<TEvent>,
    callback: AssistantEventCallback<TEvent>,
  ): Unsubscribe;
};

The store object returned by useAui(). Each scope is an accessor. subscribe fires on any state change. on subscribes to typed events.

AssistantClientAccessor

type AssistantClientAccessor<K extends ClientNames> =
  (() => ClientSchemas[K]["methods"]) &
  (
    | ClientMeta<K>
    | { source: "root"; query: Record<string, never> }
    | { source: null; query: null }
  ) &
  { name: K };

A scope accessor. Call it (aui.counter()) to resolve the scope's methods. Read .source, .query, and .name for metadata.

AssistantState

type AssistantState = {
  [K in ClientNames]: ClientSchemas[K]["methods"] extends {
    getState: () => infer S;
  }
    ? S
    : never;
};

The state object passed to useAuiState selectors. Each key is the return type of that scope's getState().

ClientMeta

type ClientMeta<K extends ClientNames> =
  "meta" extends keyof ClientSchemas[K]
    ? Pick<ClientSchemas[K]["meta"], "source" | "query">
    : never;

The source and query shape for scope K, if meta is declared in ScopeRegistry.

ClientElement

type ClientElement<K extends ClientNames> = ResourceElement<ClientOutput<K>>;

A resource element that implements scope K.

Unsubscribe

type Unsubscribe = () => void;

Event types

AssistantEventName

type AssistantEventName = keyof AssistantEventPayload;

Union of all registered event names, plus "*".

AssistantEventPayload

type AssistantEventPayload = ClientEventMap & {
  "*": { [K in keyof ClientEventMap]: { event: K; payload: ClientEventMap[K] } }[keyof ClientEventMap];
};

Maps event names to their payload types. The "*" key receives a wrapped { event, payload } object.

AssistantEventSelector

type AssistantEventSelector<TEvent extends AssistantEventName> =
  | TEvent
  | { scope: AssistantEventScope<TEvent>; event: TEvent };

A string ("scope.event") or object ({ scope, event }). Strings default to scope matching the event's source.

AssistantEventScope

type AssistantEventScope<TEvent extends AssistantEventName> =
  | "*"
  | EventSource<TEvent>
  | AncestorsOf<EventSource<TEvent>>;

Valid scopes to listen at: the event's source scope, any ancestor of that scope, or "*" for all.

AssistantEventCallback

type AssistantEventCallback<TEvent extends AssistantEventName> = (
  payload: AssistantEventPayload[TEvent],
) => void;

normalizeEventSelector

function normalizeEventSelector<TEvent extends AssistantEventName>(
  selector: AssistantEventSelector<TEvent>,
): { scope: AssistantEventScope<TEvent>; event: TEvent };

Converts a string selector to { scope, event } form. Strings like "counter.incremented" become { scope: "counter", event: "counter.incremented" }.