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

@bernierllc/admin-agent-chat-ui

v0.1.0

Published

React chat UI for the admin agent: provider, trigger, panel, message list, tool-result and change-summary cards, and the useAdminAgentChat SSE hook.

Downloads

279

Readme

@bernierllc/admin-agent-chat-ui

React components for an embedded admin LLM agent: floating trigger, slide-in chat panel, streaming message bubbles, tool-result cards, and change summary.

Overview

This package provides a complete frontend UI for the admin agent chat system. Drop <AdminAgentProvider> + <AdminAgentTrigger> + <AdminAgentPanel> into any React app and get a fully functional floating chat widget that streams responses from an SSE endpoint.

Architecture:

  • All business logic lives in the useAdminAgentChat hook (headless)
  • Styled components are thin wrappers you can use as-is or replace with your own UI
  • Consumes newline-delimited JSON events from the admin-agent-nextjs SSE endpoint

Installation

npm install @bernierllc/admin-agent-chat-ui react react-dom

Quick Start

// app/(admin)/layout.tsx
import {
  AdminAgentProvider,
  AdminAgentTrigger,
  AdminAgentPanel,
} from '@bernierllc/admin-agent-chat-ui';

export default function AdminLayout({ children }: { children: React.ReactNode }) {
  return (
    <AdminAgentProvider url="/api/chat/admin">
      {children}
      <AdminAgentTrigger position="bottom-right" />
      <AdminAgentPanel />
    </AdminAgentProvider>
  );
}

This renders a Sparkles button fixed at the bottom-right. Clicking it opens a slide-in panel with a full streaming chat interface.

Headless Usage

Use only the hook and build your own UI:

import { AdminAgentProvider, useAdminAgentChat } from '@bernierllc/admin-agent-chat-ui';

function MyChat() {
  const { messages, status, sendMessage, isOpen, toggle } = useAdminAgentChat();

  if (!isOpen) return null;

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>
          <strong>{msg.role}</strong>: {msg.content}

          {msg.toolResults?.map((tr) => (
            <div
              key={tr.toolCallId}
              style={{ color: tr.success ? 'green' : 'red' }}
            >
              [{tr.toolName}] {tr.summary}
            </div>
          ))}

          {msg.changeSummary && (
            <pre>{msg.changeSummary.markdown}</pre>
          )}
        </div>
      ))}

      <p>Status: {status}</p>

      <button onClick={() => void sendMessage('Hello!')}>Say hello</button>
    </div>
  );
}

export default function App() {
  return (
    <AdminAgentProvider url="/api/chat/admin">
      <MyChat />
    </AdminAgentProvider>
  );
}

SSE Stream Protocol

The provider POSTs to url with body { message: string } and reads newline-delimited JSON events:

// Each line is one JSON object followed by \n
{"type":"text-delta","delta":"Hello "}
{"type":"text-delta","delta":"world"}
{"type":"tool-call-start","toolCallId":"tc1","toolName":"addService"}
{"type":"tool-call-result","toolCallId":"tc1","success":true,"summary":"Added Haircut at $45","data":{...}}
{"type":"change-summary","summary":{...RollupSummary}}
{"type":"done"}

// Error path:
{"type":"error","message":"Something went wrong"}

Components

<AdminAgentProvider>

Context provider. Must wrap all other components and useAdminAgentChat calls.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | url | string | Yes | — | SSE POST endpoint (e.g. /api/chat/admin) | | headers | Record<string, string> | No | {} | Extra request headers (e.g. auth token) | | children | React.ReactNode | Yes | — | Your app tree | | fetchFn | typeof fetch | No | globalThis.fetch | Override fetch (useful for testing) |


<AdminAgentTrigger>

Floating action button that toggles the panel open/closed.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | position | 'bottom-right' \| 'bottom-left' | No | 'bottom-right' | Corner to position the button | | icon | React.ReactNode | No | Sparkles SVG | Override the button icon | | className | string | No | — | Extra CSS class |

<AdminAgentTrigger position="bottom-right" />

<AdminAgentPanel>

Slide-in panel from the right edge. Renders nothing when closed.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | width | number \| string | No | 380 | Panel width (px number or CSS string) | | className | string | No | — | Extra CSS class |

<AdminAgentPanel width={420} />

<AdminAgentPanelHeader>

Header bar inside the panel with title and close button.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | title | string | No | 'Admin Agent' | Panel heading text | | className | string | No | — | Extra CSS class |


<MessageList>

Scrollable list of message bubbles. Auto-scrolls to the latest message.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | messages | UIMessage[] | Yes | — | Messages to render | | className | string | No | — | Extra CSS class |


<MessageBubble>

Single chat bubble. Delegates to <UserMessageContent> or <AssistantMessageContent> based on role, and renders <ToolResultCard> and <ChangeSummaryCard> overlays.

| Prop | Type | Required | Description | |------|------|----------|-------------| | message | UIMessage | Yes | The message to render | | className | string | No | Extra CSS class |


<AssistantMessageContent>

Text content area styled for assistant messages.

| Prop | Type | Required | Description | |------|------|----------|-------------| | content | string | Yes | Text content (rendered with pre-wrap) | | className | string | No | Extra CSS class |


<UserMessageContent>

Text content area styled for user messages (white text on indigo background).

| Prop | Type | Required | Description | |------|------|----------|-------------| | content | string | Yes | Text content | | className | string | No | Extra CSS class |


<AdminAgentInput>

Text input + send button for composing messages. Submits on Enter (Shift+Enter for newline) and disables while the agent is busy.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | placeholder | string | No | 'Message the admin agent…' | Input placeholder | | disabled | boolean | No | false | Force-disable the input | | className | string | No | — | Extra CSS class |


<AgentStatusBar>

Shows contextual status beneath the message list. Returns null when status is 'idle'.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | status | AgentStatus | Yes | — | Current agent status | | executingTool | string | No | — | Tool name shown during 'executing' state | | className | string | No | — | Extra CSS class |

Status labels:

| Status | Label | |--------|-------| | idle | (hidden) | | thinking | Thinking… | | executing | Executing <toolName>… | | responding | Responding… | | error | Error — retry? |


<ToolResultCard>

Green (success) or red (failure) card rendered per tool call. Expandable to show raw data.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | toolName | string | Yes | — | Name of the tool that ran | | success | boolean | Yes | — | Whether the tool call succeeded | | summary | string | Yes | — | One-line description of the change | | data | unknown | No | — | Raw data to show when expanded | | expanded | boolean | No | false | Start expanded |

<ToolResultCard
  toolName="addService"
  success={true}
  summary="Added 'Haircut' at $45"
  data={{ id: 42, name: 'Haircut', price: 45 }}
/>

<ChangeSummaryCard>

End-of-conversation rollup card. Collapsible. Renders RollupSummary.markdown from @bernierllc/agent-changeset.

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | summary | RollupSummary | Yes | — | Rollup from agent-changeset | | defaultExpanded | boolean | No | false | Start expanded |


Hook: useAdminAgentChat

import { useAdminAgentChat } from '@bernierllc/admin-agent-chat-ui';

const {
  messages,          // UIMessage[] — full conversation history
  status,            // AgentStatus — current state machine state
  isOpen,            // boolean — whether the panel is open
  currentToolName,   // string | null — tool executing right now
  sendMessage,       // (text: string) => Promise<void>
  abort,             // () => void — cancel in-flight request
  toggle,            // () => void — toggle open/closed
  open,              // () => void — open the panel
  close,             // () => void — close the panel
} = useAdminAgentChat();

Must be called inside <AdminAgentProvider>. Throws AdminAgentUIError with code NO_PROVIDER otherwise.

Types

type AgentStatus = 'idle' | 'thinking' | 'executing' | 'responding' | 'error';

interface UIMessage {
  id: string;
  role: 'user' | 'assistant';
  content: string;
  toolResults?: UIToolResult[];
  changeSummary?: RollupSummary; // from @bernierllc/agent-changeset
  timestamp: string;             // ISO 8601
}

interface UIToolResult {
  toolName: string;
  toolCallId: string;
  success: boolean;
  summary: string;
  data?: unknown;
}

Errors

import { AdminAgentUIError, AdminAgentStreamError } from '@bernierllc/admin-agent-chat-ui';

// Base error
class AdminAgentUIError extends Error {
  readonly code: string; // e.g. 'UI_ERROR', 'NO_PROVIDER'
}

// Stream-specific error (extends AdminAgentUIError, code = 'STREAM_ERROR')
class AdminAgentStreamError extends AdminAgentUIError {}

Both errors use Error.cause chaining for full stack traces.

State Machine

idle ──sendMessage──► thinking
thinking ────────────► executing  (on tool-call-start)
executing ───────────► thinking   (on tool-call-result, loop continues)
thinking ────────────► responding (on text-delta)
responding ──────────► idle       (on done)
any ─────────────────► error      (on error event or non-ok response)

License

Copyright (c) 2025 Bernier LLC. Licensed to the client under a limited-use license.