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.
@hibira-io/chat-uinpm ↗GitHub ↗
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 ↗
Install the plugin
claude plugin marketplace add hibira-io/agent-plugin claude plugin install hibira@hibiraPaste 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.
Prefer to do it by hand? These are the steps below:
- 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.

Sign in to see these snippets with your own cluster host. Sign in
Install
npm install @hibira-io/chat-ui pusher-jsShow 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} />; }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 →