npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@economic/agents-react

v1.4.1

Published

React hooks for connecting to agents built with [`@economic/agents`](../agents/README.md). They wrap the Cloudflare `agents` and `@cloudflare/ai-chat` hooks and add typed connection state, message feedback, and the per-user multi-chat model used by `Assis

Readme

@economic/agents-react

React hooks for connecting to agents built with @economic/agents. They wrap the Cloudflare agents and @cloudflare/ai-chat hooks and add typed connection state, message feedback, and the per-user multi-chat model used by Assistant.

Install

npm install @economic/agents-react

React 19 is a peer dependency.

Which hook do I use?

The package exports three hooks, layered on top of each other:

  • useAgent — the thin connection primitive both build on. Reach for it only when you need raw access to the connection.
  • useChat — connects directly to a single chat agent by name. Use it when you address one conversation yourself (e.g. one chat per route) rather than going through an Assistant.
  • useAssistant — the high-level hook for the v2 model: one assistant per user, many chats. It manages the chat list (create / open / delete), connects to the right chat facet, and exposes the active chat. Use this when your server uses an Assistant.

useAgent

The connection primitive useChat and useAssistant are built on. Connects to an agent by name and exposes its connection and call for invoking @callable methods.

import { useAgent } from "@economic/agents-react";

const agent = useAgent({
  host: "localhost:8787",
  agentName: "SupportAgent",
  name: "support",
  actorId: userId, // when set, the DO name becomes `${actorId}:${name}`
  authToken,
});

await agent.call("checkOrder", ["1234"]);
console.log(agent.state?.status);

useChat

Connects to a single chat agent by name — no Assistant needed. Returns the connection, the message interface, and message-feedback helpers.

import { useChat } from "@economic/agents-react";

const { status, chat, submitMessageFeedback, getMessageFeedback } = useChat({
  host: "localhost:8787",
  agentName: "MyChatAgent",
  name: `${userId}:${chatId}`,
  authToken,
  toolContext: { locale: "en-DK" },
});

// send a message
chat.sendMessage({ role: "user", parts: [{ type: "text", text: "Hello" }] });

// rate a message (1 = up, -1 = down) and read all feedback
await submitMessageFeedback(messageId, 1, "Helpful!");
const feedback = await getMessageFeedback();

Returns

| Field | Type | Description | | ----------------------- | ------------------------------------------------ | ------------------------------------------------------------- | | status | AgentConnectionStatus | Connection status. | | agent | connection object | The underlying useAgent connection. | | chat | useAgentChat result | messages, sendMessage, setMessages, status, stop, … | | submitMessageFeedback | (id, rating, comment?) => Promise<void> | Submit thumbs up/down feedback for a message. | | getMessageFeedback | () => Promise<Record<string, MessageFeedback>> | All feedback for the chat, keyed by message id. |


useAssistant

For the per-user, many-chats model: connects to the user's Assistant DO, keeps the chat list in sync, and manages which chat is active.

import { useAssistant } from "@economic/agents-react";

function Chat({ userId, authToken }: { userId: string; authToken: string }) {
  const { status, chats, currentChatName, assistant, chat } = useAssistant({
    host: "localhost:8787",
    agentName: "MyAssistant", // matches the Assistant DO binding/class name
    name: userId, // the Assistant is keyed by user
    authToken,
    toolContext: { locale: "en-DK" }, // forwarded to server tools as the request body
    welcomeMessage: "Hi! How can I help?",
  });

  return (
    <div>
      <button onClick={() => assistant.createChat()}>New chat</button>

      <ul>
        {chats.map((c) => (
          <li key={c.name}>
            <button onClick={() => assistant.openChat(c.name)}>{c.title ?? "Untitled"}</button>
            <button onClick={() => assistant.deleteChat(c.name)}>Delete</button>
          </li>
        ))}
      </ul>

      {chat.chat.messages.map((m) => (
        <Message key={m.id} message={m} />
      ))}

      <Composer
        onSend={(text) => chat.chat.sendMessage({ role: "user", parts: [{ type: "text", text }] })}
      />
    </div>
  );
}

Returns

| Field | Type | Description | | ----------------- | ----------------------- | --------------------------------------------------------------------------- | | status | AgentConnectionStatus | Connection status of the assistant, or of the active chat once one is open. | | chats | ChatSummary[] | The user's chats, most recently updated first. | | currentChatName | string \| undefined | Id of the active chat, if any. | | assistant | AssistantActions | getChats, createChat, openChat, deleteChat. | | chat | useChat result | The active chat — chat.chat is the message interface (see below). |

assistant actions:

| Action | Signature | Description | | -------------------- | ----------------------------------- | ------------------------------------------------ | | createChat() | () => Promise<string> | Creates a chat, makes it active, returns its id. | | openChat(chatId) | (chatId: string) => void | Switches the active chat. | | getChats() | () => Promise<ChatSummary[]> | Refreshes and returns the chat list. | | deleteChat(chatId) | (chatId: string) => Promise<void> | Deletes a chat and clears the active selection. |

Titles and summaries are generated server-side a few seconds after the first message, so refetch with getChats() shortly after sending the opening message if you want the new title to appear.


Options

All hooks share these connection options (useAssistant omits actorId):

| Option | Type | Description | | -------------------------------- | -------------------------- | ----------------------------------------------------------------------------- | | host | string | Worker host, e.g. localhost:8787 or my-agent.workers.dev. | | agentName | string | The agent's DO binding/class name. | | name | string | DO name. For useAssistant this is the user id; for useChat, the chat key. | | actorId | string? | If set, the DO name becomes ${actorId}:${name} (useAgent / useChat). | | authToken | string? | Bearer token; sent as an Authorization header and WebSocket subprotocol. | | enabled | boolean? | Gate the connection (defaults to enabled when a name is present). | | sub | { agent; name }[]? | Sub-agent routing (used internally by useAssistant). | | toolContext | Record<string, unknown>? | Forwarded to server tools as the request body (useChat / useAssistant). | | welcomeMessage | string? | Seeds an initial assistant message when the chat is empty. | | onOpen / onClose / onError | event handlers | WebSocket lifecycle callbacks. |


Types

type AgentConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized" | "error";
type AgentConnectionType = "agent" | "chat" | "assistant";

type AgentConnectionState = {
  status: AgentConnectionStatus;
  type: AgentConnectionType;
  subAgentName?: string;
};

type ChatSummary = {
  name: string;
  title?: string;
  summary?: string;
  created_at: number;
  updated_at: number;
};

type MessageFeedback = {
  message_id: string;
  rating: number; // 1 = up, -1 = down
  comment?: string;
  created_at: number;
  updated_at: number;
};

The package also exports the handler types AssistantActions, GetChatsHandler, CreateChatHandler, OpenChatHandler, DeleteChatHandler, MessageFeedbackHandler, and GetMessageFeedbackHandler.


Authentication

Pass a JWT as authToken. It is sent both as an Authorization: Bearer … header and as a WebSocket subprotocol (["bearer", token]), matching the server-side getJwtAuthConfig. When verification fails, the connection status becomes "unauthorized".

.