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

@meetreeve/chat-kit

v0.8.0

Published

React chat kit: transport adapters + primitives + drop-in presets for Reeve.Chat surfaces

Readme

@meetreeve/chat-kit

React chat kit for Reeve.Chat surfaces — transport adapters, headless primitives, and drop-in presets.

@meetreeve/chat-kit is the canonical surface for embedding Reeve-powered chat in any React app. It ships:

  • Transport adapters for WebSocket (Reeve gateway), SSE, REST, voice, and a no-network echo for demos.
  • Headless primitives with stable class names + CSS custom properties — no opinion on styling.
  • Opinionated presets that compose the above into ready-to-render surfaces (<ReeveChat>, <ReeveChat.Terminal>, etc.).

You pick the abstraction level. All three tiers compose with each other.

Install

pnpm add @meetreeve/chat-kit
# or
npm install @meetreeve/chat-kit
# or
yarn add @meetreeve/chat-kit

react and react-dom (^18 || ^19) are peer dependencies.

Quick start

import { ReeveChat } from '@meetreeve/chat-kit';
import '@meetreeve/chat-kit/styles/tokens.css';

export function ChatPage() {
  return (
    <ReeveChat
      mode="auto"
      gatewayUrl="wss://gw.meetreeve.com"
      wsToken={process.env.NEXT_PUBLIC_WS_TOKEN}
      sessionKey="chat-kit-demo:main"
      title="Chat with Reeve"
      placeholder="Ask me anything…"
    />
  );
}

mode="auto" picks the WebSocket adapter when wsToken is present, and falls back to the echo adapter (a local synthetic reply) otherwise. Set mode="live" to require WS, or mode="echo" to force the demo transport.

Three escalation tiers

Tier 1 — opinionated preset

A single component, sensible defaults. Best for new surfaces and for replacing legacy chat panels with minimal glue.

| Preset | Default adapter | Use when | |---|---|---| | <ReeveChat> | WS or Echo (auto) | General-purpose Reeve chat surface | | <ReeveChat.Terminal> | WS | Power-user / dev console with monospace lines, no bubbles | | <ReeveChat.Readiness> | SSE (optional REST fallback) | Intake/qualification flow with progress meter + capture card | | <ReeveChat.Voice> | WS wrapped by useChatVoice | Mic-first surface with tap-to-record or push-to-talk | | <ReeveChat.Library> | REST | Book / library / knowledge-base style chat with custom bubble renderer |

import { ReeveChat } from '@meetreeve/chat-kit';

<ReeveChat.Readiness
  endpoint="/api/v1/readiness-conversations"
  token={authToken}
  conversationId={conversationId}
  onComplete={({ email, phone }) => saveLead({ email, phone })}
/>

Tier 2 — compose primitives

When a preset is close but not quite, drop one level and compose. Every primitive reads from the ChatProvider context, so swapping adapters is a one-line change.

import {
  ChatProvider,
  ChatRoot,
  ChatHeader,
  ChatErrorBanner,
  ChatMessageList,
  ChatStreamIndicator,
  ChatComposer,
} from '@meetreeve/chat-kit';
import { useChatWS } from '@meetreeve/chat-kit';
import '@meetreeve/chat-kit/styles/tokens.css';

export function CustomChat() {
  const adapter = useChatWS({
    gatewayUrl: 'wss://gw.meetreeve.com',
    wsToken: getToken(),
    sessionKey: 'custom:main',
  });

  return (
    <ChatProvider adapter={adapter} theme={{ density: 'compact' }}>
      <ChatRoot variant="panel">
        <ChatHeader title="Custom chat" actions={<MyMenu />} />
        <ChatErrorBanner />
        <ChatMessageList renderBubble={(m) => <MyBubble message={m} />} />
        <ChatStreamIndicator />
        <ChatComposer
          attachmentSlot={<MyDropzone />}
          voiceSlot={<MyVoiceButton />}
          autosize
        />
      </ChatRoot>
    </ChatProvider>
  );
}

Tier 3 — bring your own adapter

Any object that satisfies the ChatAdapter contract works as a drop-in for the bundled adapters. Useful when a custom backend doesn't fit WS / SSE / REST cleanly, or when wrapping an existing state store.

import type { ChatAdapter, ChatMessage } from '@meetreeve/chat-kit';
import { ChatProvider, ChatRoot, ChatMessageList, ChatComposer } from '@meetreeve/chat-kit';

function useMyAdapter(): ChatAdapter {
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  return {
    status: 'connected',
    messages,
    streamState: { phase: 'idle' },
    error: null,
    send: (text) => {
      // Talk to your backend, push the reply into setMessages.
    },
  };
}

export function MyChat() {
  const adapter = useMyAdapter();
  return (
    <ChatProvider adapter={adapter}>
      <ChatRoot>
        <ChatMessageList />
        <ChatComposer />
      </ChatRoot>
    </ChatProvider>
  );
}

The full contract is in src/protocol/adapter.ts. Optional fields (suggestions, progress, stop, retry, clear, connect, disconnect, loadHistory) are read by primitives that need them and degrade gracefully when absent.

Adapters

| Adapter | Transport | Key options | Use when | |---|---|---|---| | useChatEcho | none (in-process) | reply?, replyDelayMs? | Storybook / demos / unauthenticated fallback | | useChatWS | WebSocket (Reeve gateway, protocol v3) | gatewayUrl, wsToken, sessionKey?, autoConnect?, onStatusChange?, onTokenExpired? | Live Reeve.Chat streaming | | useChatSSE | POST + EventStream | endpoint, token?, sessionKey?, headers? | AgentPik-style /chat2 SSE backends | | useChatREST<TConv> | REST POST + optional GET polling | endpoint, conversationId, token?, toMessages, toSuggestions?, toProgress?, pollIntervalMs? | Non-streaming or custom-DTO backends (Freya, AgentPik v1) | | useChatVoice | wraps a text adapter | textAdapter, transcribe, recordingMode, onAudioLevel?, cancelDragThresholdPx? | Mic input layered over any text adapter |

useChatWS, useChatSSE, and useChatREST also expose a __raw escape hatch (typed as UseChatWSReturn / UseChatSSEReturn / UseChatRESTReturn) for migration safety from the legacy hooks they were ported from.

Adapter notes

  • useChatWS speaks the Reeve gateway protocol v3 (connect.challenge handshake, chat.send / chat.history, chat events with state: delta | final | error | aborted). Reconnect uses exponential backoff capped at 30s / 10 attempts. Returning null from onTokenExpired surfaces "Authentication failed".
  • useChatSSE posts to ${endpoint}/${sessionKey}/messages and parses the kit's canonical frame protocol (message-start, part-start, text-delta, part-end, message-end). The legacy AgentPik v1 part shape (part: { type, ... }) is accepted as a fallback.
  • useChatREST is generic over TConversation. You provide toMessages (required) and optional toSuggestions / toProgress projectors so the kit stays agnostic to your DTO. Polling is opt-in via pollIntervalMs > 0 and requires a non-null conversationId.
  • useChatVoice is a recording-state machine; STT is injected via transcribe(blob, mimeType). Pick 'tap-to-record' or 'push-to-talk'. Wrap any ChatAdapter — voice is composable with WS, SSE, REST, or Echo.

Primitives

All primitives consume the ChatProvider context (no prop drilling) and emit rkit-* class names you can target from your own stylesheet.

| Primitive | Key props | Behavior | |---|---|---| | ChatProvider | adapter, theme?, children | Context root. theme.densitycompact \| comfortable, theme.variantpanel \| terminal \| floating \| fullscreen | | ChatRoot | variant?, className? | Outer flex container; resolves variant from prop or theme | | ChatHeader | title?, subtitle?, actions?, children? | Top bar with title/subtitle/actions; children overrides the default layout | | ChatMessageList | renderBubble?, smartAutoScroll? | Renders adapter.messages; auto-scrolls only when user is near the bottom | | ChatBubble | message, renderAttachment?, showTimestamp? | Single message bubble; shows streaming cursor when meta.isStreaming === true | | ChatComposer | placeholder?, autosize?, maxLength?, attachmentSlot?, voiceSlot? | Textarea + send/stop button; Enter sends, Shift-Enter newline | | ChatStatusPill | labels? | Renders adapter.status with a colored dot + label | | ChatStreamIndicator | onStop?, onRetry?, onDismiss?, labels? | Shows current stream phase + elapsed timer + Stop / Retry / Dismiss actions | | ChatErrorBanner | onRetry?, onDismiss? | Renders adapter.error.message with Retry / Dismiss; hides itself when no error | | ChatProgressMeter | progress?, showLabelAt? | Renders adapter.progress; shows stage label once percent ≥ threshold | | ChatSuggestionChips | chips?, showSkip?, onPick | Renders adapter.suggestions; optional synthetic Skip chip | | ChatEmptyState | greeting?, hero?, suggestions?, onPick? | Rendered only when adapter.messages.length === 0 | | ChatVoiceButton | voiceAdapter, mode?, cancelDragThresholdPx? | Mic button; drives a ChatVoiceAdapter; exposes --audio-level CSS var | | ChatCaptureCard | onCapture, consentText?, tcpaText?, placeholder?, submitLabel?, onSkip? | Email/phone capture with consent + TCPA disclosure | | ChatCompletionScreen | score?, stageTitle?, bullets?, emailGate?, children? | Post-flow summary with optional score + email gate |

Typed content blocks (DEV-1077)

ChatBubble walks message.content and dispatches each item via a BlockRenderer registry. String items render as text; structured items render via their registered renderer (with a JSON-pretty fallback for unknown types).

| Block | Source | Default renderer | | --- | --- | --- | | tool_call | studio-handler tool invocations | Collapsed header with condensed args summary + expand toggle for full JSON | | tool_result | studio-handler tool results | Status pill + summary + body dispatched by tool_name (cost chip / render-status badge / brand card / chain preview / memory list / agent output / JSON fallback) | | render_confirm_required | DEV-1074 cost-threshold gate | Total cost + per-stage breakdown + Approve / Cancel buttons | | auto_run_confirm_required | DEV-1091 auto-run gate | URL + cost range + step list + est duration + Approve / Cancel | | auto_run_progress | DEV-1091 webhook progress stream | Step / message header + <progress> bar when pct is known | | chain_preview | DEV-1075 (materialize_chain_from_dna, load_chain_session) | Per-stage BFS chips + optional CTA buttons |

Action button clicks fire adapter.action({ run_id, action_id }). The WS adapter emits a chat.action event over the open socket; SSE / REST / custom adapters implement the optional action method themselves.

ChatStreamIndicator is tool-call aware. During the streaming phase, it inspects the last block of the most recent assistant message:

  • tool_call → "Reeve is using <tool_name>…"
  • auto_run_progress (non-terminal) → "<step>: <message>"
  • otherwise the default Streaming… label

Overriding a renderer

Pass blockRenderers to <ChatProvider> to override individual renderers without losing the defaults for the rest:

import { ChatProvider, type BlockProps } from '@meetreeve/chat-kit';

function BidCard({ block }: BlockProps) {
  // ...custom UI keyed to your app's bid_card / agent shape
  return <div>...</div>;
}

<ChatProvider
  adapter={adapter}
  blockRenderers={{ tool_result: BidCard as never }}
>
  {/* every other block type still uses the default */}
</ChatProvider>

Presets

| Preset | Default transport | Layout | Use when | |---|---|---|---| | <ReeveChat> | useChatWS if wsToken, else useChatEcho | Header + messages + stream indicator + composer | General Reeve chat surface | | <ReeveChat.Terminal> | useChatWS | Status pill bar + monospace lines + monospace composer | Power-user / dev console | | <ReeveChat.Readiness> | useChatSSE (with restFallbackuseChatREST) | Progress meter + messages + suggestion chips (with Skip) + capture card at 100% | Intake / qualification flow | | <ReeveChat.Voice> | useChatWS wrapped by useChatVoice | Header with status pill + messages + composer with mic button | Voice-first chat surface | | <ReeveChat.Library> | useChatREST | Header + empty state with starter prompts + messages with book-card bubble override | Book / library / knowledge-base chat |

Every preset accepts an adapter prop that, when provided, replaces the auto-configured one. Pass any ChatAdapter to override.

Theming

The kit ships an opt-in CSS file that defines every token under :root and a few data-rkit-* selectors. Import once at your app root:

import '@meetreeve/chat-kit/styles/tokens.css';

Override tokens at any scope by re-declaring the CSS custom properties:

:root {
  --rkit-accent: #2563eb;          /* brand accent — used by user bubbles + voice button */
  --rkit-bubble-radius: 0.5rem;    /* tighten bubble corners */
  --rkit-font-base: 'Inter', system-ui, sans-serif;
}

/* Scope a single surface */
.my-chat-panel {
  --rkit-accent: #16a34a;
}

Theme hooks

Toggle bundled themes by setting a data attribute on any ancestor of <ChatRoot> (typically <html> or <body>):

  • [data-rkit-theme='dark'] — dark surface palette
  • [data-rkit-density='compact'] — tighter spacing + smaller base text
  • [data-rkit-variant='terminal'] — monospace + transparent bubbles (also applied automatically by <ReeveChat.Terminal>)

Token groups

| Group | Sample tokens | Purpose | |---|---|---| | Surface | --rkit-bg, --rkit-bg-raised, --rkit-bg-muted, --rkit-fg, --rkit-fg-muted, --rkit-border | Base background / foreground stack | | Brand | --rkit-accent, --rkit-accent-fg | Primary action color | | Bubbles | --rkit-bubble-user-bg, --rkit-bubble-assistant-bg, --rkit-bubble-system-bg, --rkit-bubble-radius, --rkit-bubble-max-width | Per-role bubble styling | | Composer | --rkit-composer-bg, --rkit-composer-border, --rkit-composer-radius | Composer surface | | Chips | --rkit-chip-bg, --rkit-chip-fg, --rkit-chip-radius | Suggestion / empty-state chips | | Capture card | --rkit-capture-card-bg, --rkit-capture-card-radius | Lead capture card | | Voice | --rkit-voice-idle-bg, --rkit-voice-recording-bg, --rkit-voice-ring-color | Voice button + audio ring | | Status | --rkit-status-connected, --rkit-status-connecting, --rkit-status-authenticating, --rkit-status-reconnecting, --rkit-status-disconnected, --rkit-status-idle, --rkit-status-error, --rkit-status-demo | Status pill colors per ChatStatus | | Spacing | --rkit-space-1--rkit-space-5 | Density scale (overridden by [data-rkit-density='compact']) | | Type | --rkit-font-base, --rkit-font-mono, --rkit-text-sm, --rkit-text-base, --rkit-text-lg | Type stack + scale | | Z indices | --rkit-z-panel, --rkit-z-banner, --rkit-z-overlay | Stacking |

See src/styles/tokens.css for the full enumerated list (~50 tokens across 12 groups, plus the dark / compact / terminal theme blocks).

Examples

License

MIT