Skip to main content
All hooks exported by @aomi-labs/react. Every hook must be used inside AomiRuntimeProvider.

useAomiRuntime

The unified hook that combines all runtime APIs into a single interface. This is the primary way to interact with the Aomi runtime.
import { useAomiRuntime } from "@aomi-labs/react";

function MyComponent() {
  const aomi = useAomiRuntime();

  // User API — `user` is a nested UserState; read it with accessors
  aomi.user;                  // UserState (nested: connection, evm, svm)
  UserState.address(aomi.user);     // "0x..." | undefined
  UserState.isConnected(aomi.user); // boolean | undefined
  aomi.setUser({ evm: { address: "0x..." } });
  aomi.getUserState();
  aomi.onUserStateChange((user) => { ... });

  // Thread API
  aomi.currentThreadId;       // string
  aomi.threadViewKey;          // number (use as React key)
  aomi.threadMetadata;         // Map<string, ThreadMetadata>
  aomi.getThreadMetadata(id);
  await aomi.createThread();
  await aomi.deleteThread(id);
  await aomi.renameThread(id, "New Title");
  await aomi.archiveThread(id);
  aomi.selectThread(id);

  // Chat API
  aomi.isRunning;              // boolean
  aomi.getMessages(threadId?); // ThreadMessageLike[]
  await aomi.sendMessage("Hello!");
  aomi.cancelGeneration();

  // Notification API
  aomi.notifications;                     // Notification[]
  aomi.showNotification({ type, title });
  aomi.dismissNotification(id);
  aomi.clearAllNotifications();

  // Wallet API
  aomi.pendingWalletRequests;  // WalletRequest[]
  aomi.startWalletRequest(id);
  aomi.resolveWalletRequest(id, { txHash });
  aomi.rejectWalletRequest(id, "reason");

  // Event API
  aomi.subscribe("event_type", callback);
  await aomi.sendSystemCommand({ type, sessionId, payload });
  aomi.sseStatus;              // "connected" | "connecting" | "disconnected"
}

Return Type

type AomiRuntimeApi = {
  user: UserState;
  getUserState: () => UserState;
  setUser: (data: Partial<UserState>) => void;
  onUserStateChange: (cb: (user: UserState) => void) => () => void;

  currentThreadId: string;
  threadViewKey: number;
  threadMetadata: Map<string, ThreadMetadata>;
  getThreadMetadata: (id: string) => ThreadMetadata | undefined;
  createThread: () => Promise<string>;
  deleteThread: (id: string) => Promise<void>;
  renameThread: (id: string, title: string) => Promise<void>;
  archiveThread: (id: string) => Promise<void>;
  selectThread: (id: string) => void;

  isRunning: boolean;
  getMessages: (threadId?: string) => ThreadMessageLike[];
  sendMessage: (text: string) => Promise<void>;
  cancelGeneration: () => void;

  notifications: Notification[];
  showNotification: (params: NotificationData) => string;
  dismissNotification: (id: string) => void;
  clearAllNotifications: () => void;

  pendingWalletRequests: WalletRequest[];
  startWalletRequest: (id: string) => void;
  resolveWalletRequest: (id: string, result: WalletRequestResult) => void;
  rejectWalletRequest: (id: string, error?: string) => void;

  subscribe: (type: string, cb: EventSubscriber) => () => void;
  sendSystemCommand: (event: {
    type: string;
    sessionId: string;
    payload: unknown;
  }) => Promise<void>;
  sseStatus: SSEStatus;
};

useUser

Wallet and user state management. UserState is a nested object: EVM identity lives under evm, Solana under svm, and session-level connection facts under connection. Read fields with the UserState accessor functions rather than reaching into the object directly. useUser returns { user, setUser, addExtValue, removeExtValue, getUserState, onUserStateChange }.
import { useUser, UserState } from "@aomi-labs/react";

function WalletStatus() {
  const { user, setUser, getUserState, onUserStateChange } = useUser();

  UserState.address(user);      // "0xABC..." | undefined
  UserState.chainId(user);      // 1 | undefined
  UserState.isConnected(user);  // boolean | undefined
  UserState.ensName(user);      // "vitalik.eth" | undefined

  setUser({ connection: { is_connected: true }, evm: { address: "0x...", chain_id: 1 } });
  const current = getUserState();
  const unsubscribe = onUserStateChange((newUser) => {
    console.log("User changed:", newUser);
  });
}

UserState

UserState is canonicalized to the backend’s nested snake_case shape. The fields you read most often:
type UserState = {
  connection?: {
    is_connected?: boolean | null;
    // ...auth and provider facts
  } | null;
  evm?: {
    address?: string | null;
    chain_id?: number | string | null;
    ens_name?: string | null;
    // ...aa, sponsorship
  } | null;
  svm?: {
    address?: string | null;
    // ...cluster, capabilities
  } | null;
  ext?: Record<string, unknown> | null;
};
Accessor functions normalize the nested shape for you: UserState.address, UserState.chainId, UserState.ensName, UserState.isConnected, UserState.svmAddress, and UserState.preferredPublicKey.

useThreadContext

Low-level thread state management. Use useAomiRuntime for most cases.
import { useThreadContext } from "@aomi-labs/react";

function ThreadManager() {
  const {
    currentThreadId,
    setCurrentThreadId,
    threadViewKey,
    bumpThreadViewKey,
    allThreads,
    allThreadsMetadata,
    getThreadMessages,
    setThreadMessages,
    getThreadMetadata,
    updateThreadMetadata,
  } = useThreadContext();

  const messages = getThreadMessages(currentThreadId);
  const meta = getThreadMetadata(currentThreadId);
}

Convenience Hooks

import {
  useCurrentThreadMessages,
  useCurrentThreadMetadata,
} from "@aomi-labs/react";

const messages = useCurrentThreadMessages();
const metadata = useCurrentThreadMetadata();

useControl

Model, app, and API key state management.
import { useControl } from "@aomi-labs/react";

function ControlPanel() {
  const {
    state,
    setApiKey,
    getAvailableModels,
    getAuthorizedApps,
    onModelSelect,
    onAppSelect,
    getCurrentThreadControl,
    isProcessing,
    getControlState,
    onControlStateChange,
  } = useControl();

  state.apiKey;
  state.availableModels;
  state.authorizedApps;
  state.defaultModel;
  state.defaultApp;

  const tc = getCurrentThreadControl();
  tc.model;
  tc.app;
}

ControlState

type ControlState = {
  apiKey: string | null;
  clientId: string | null;
  availableModels: string[];
  authorizedApps: string[];
  appDescriptors: AomiAppDescriptor[];
  defaultModel: string | null;
  defaultApp: string | null;
  byokKeys: Record<string, StoredByokKey>;
};

useNotification

Toast notification management.
import { useNotification } from "@aomi-labs/react";

function NotificationExample() {
  const { notifications, showNotification, dismissNotification, clearAll } =
    useNotification();

  const id = showNotification({
    type: "success",
    title: "Transaction sent",
    message: "0x123...abc",
    duration: 5000,
  });

  dismissNotification(id);
}

Notification Type

type Notification = {
  id: string;
  type: "notice" | "success" | "error" | "wallet";
  title: string;
  message?: string;
  duration?: number;
  timestamp: number;
};

useEventContext

SSE subscription and outbound event dispatch.
import { useEventContext } from "@aomi-labs/react";

function EventListener() {
  const { subscribe, sendOutboundSystem, sseStatus } = useEventContext();

  useEffect(() => {
    const unsubscribe = subscribe("wallet_tx_request", (event) => {
      console.log("Wallet request received:", event.payload);
    });
    return unsubscribe;
  }, [subscribe]);

  await sendOutboundSystem({
    type: "wallet:tx_complete",
    sessionId: "thread-123",
    payload: { txHash: "0x...", status: "success" },
  });
}

useWalletHandler

Handles wallet transaction requests and EIP-712 signing requests from the AI backend. useWalletHandler takes a { getSession } config that returns the ClientSession for the current thread, and returns { pendingRequests, setRequests, startRequest, resolveRequest, rejectRequest }. Each request’s kind is "transaction", "eip712_sign", or "solana_sign". When you resolve a request, the result.kind must match the request’s kind.
import { useWalletHandler } from "@aomi-labs/react";

function WalletHandler() {
  const { pendingRequests, startRequest, resolveRequest, rejectRequest } =
    useWalletHandler({
      getSession: () => getCurrentSession(),
    });

  for (const req of pendingRequests) {
    if (req.kind === "transaction") {
      startRequest(req.id);
      await resolveRequest(req.id, { kind: "transaction", txHash: "0x..." });
    }

    if (req.kind === "eip712_sign") {
      startRequest(req.id);
      await resolveRequest(req.id, { kind: "eip712_sign", signature: "0x..." });
    }

    if (req.kind === "solana_sign") {
      startRequest(req.id);
      await resolveRequest(req.id, { kind: "solana_sign", signedTx: "..." });
    }
  }
}

Next Steps

Last modified on June 4, 2026