Official package · Realtime building blocks

A chat interface for AI agents with human handoff

An open-source support chat where an AI agent answers first and a person steps in when needed. It shows what the agent is doing while it writes, renders safe Markdown, offers follow-up questions and marks a handoff to your team.

npm versionLicense

Fastest way: one prompt with the Hibira plugin

Install the plugin in your AI coding agent and paste the prompt. The agent signs you in, creates a free app if you need one, writes the code and sends a test event. GitHub ↗

  1. Install the plugin

    claude plugin marketplace add hibira-io/agent-plugin
    claude plugin install hibira@hibira
  2. Paste this prompt

    Add Hibira Chat UI (@hibira-io/chat-ui) to this project.
    Follow https://hibira.io/guides/chat-ui, adapted to how this project is already set up.
    If I have no Hibira app yet, create a Free one that allows http://localhost so I can test right away.
    Keep the app key and secret in environment variables, add placeholders to .env.example, and never commit real values or send the secret to a browser.
    When you are done, send a test event, show me it arriving, and list the files you changed.
  • Agent steps such as Reading your question and Looking at your account, shown live from hints your backend sends.
  • Safe Markdown, never inserted as HTML; links and images only from origins you allow.
  • Up to three follow-up suggestions, a handoff badge, copy buttons and a typewriter reveal, in seven languages.
  • About 9 KB of JavaScript gzipped with no runtime dependencies; React is optional.
A support conversation in chat-ui: an automatic reply with a code block, copy buttons and suggested follow-up questions.
A support conversation in chat-ui: an automatic reply with a code block, copy buttons and suggested follow-up questions.

Sign in to see these snippets with your own cluster host. Sign in

  1. Install

    npm install @hibira-io/chat-ui pusher-js
  2. Show the chat

    import { useEffect } from 'react';
    import Pusher from 'pusher-js';
    import { Chat, createConversation } from '@hibira-io/chat-ui/react';
    import '@hibira-io/chat-ui/styles.css';
    
    const pusher = new Pusher('YOUR_APP_KEY', {
      cluster: 'hkg',
      wsHost: 'ws.hibira.io',
      channelAuthorization: { endpoint: '/pusher/auth', transport: 'ajax' },
    });
    
    const conversation = createConversation({
      pusher,
      channel: `private-conversation-${conversationId}`,
      load: (after, signal) => fetch(`/api/conversations/${conversationId}/messages?after=${after}`, { signal }).then((r) => r.json()),
      send: (text) =>
        fetch(`/api/conversations/${conversationId}/messages`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ text }),
        }).then((r) => r.json()),
    });
    
    export function SupportChat() {
      useEffect(() => conversation.start(), []);
      return <Chat conversation={conversation} />;
    }
  3. Reply from your server

    import Pusher from 'pusher';
    import { createChatServer, agentMetadata } from '@hibira-io/chat-ui/server';
    
    const pusher = new Pusher({ appId: process.env.HIBIRA_APP_ID, key: process.env.HIBIRA_KEY, secret: process.env.HIBIRA_SECRET, host: 'api.hibira.io', useTLS: true });
    const chat = createChatServer({ pusher });
    
    const typing = chat.agentTyping(channel); // "Reading your question"
    typing.step('writing');
    const answer = await yourAgent(question);
    const msg = await db.saveMessage({
      sender: 'agent',
      text: answer.text,
      metadata: agentMetadata({ handoff: answer.handoff, suggestions: answer.suggestions }),
    });
    typing.stop();
    await chat.message(channel, { seq: msg.seq, from: 'agent' });

Before you start

  • Messages stay in your own database and are read over HTTPS; the channel only carries hints, so a missed event costs a poll, never a message.
  • Private and presence channels need an auth endpoint on your backend that signs subscriptions with the app secret, as with pusher-js.

Use the app key from your dashboard; the secret stays on your server. Channels, events and authorization endpoints work exactly as they do with Pusher. Create your free account →