HETIBANK SDK Read the docs

Chat guide

Embed the HETIBANK chat surface in a website, an internal tool, or beside an MCP client.

Install

pnpm add @hetibank/chat @hetibank/ui @hetibank/theme @hetibank/contracts
import "@hetibank/theme/styles.css";
import "@hetibank/ui/styles.css";
import "@hetibank/chat/styles.css";

The transport is yours

The kit never owns a socket, a model or a key. You implement ChatTransport:

import type { ChatTransport, ChatMessage } from "@hetibank/contracts";

const transport: ChatTransport = {
  async send({ messages, signal }) {
    const response = await fetch("/api/chat", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ messages }),
      signal,
    });
    if (!response.ok) {
      const envelope = await response.json(); // { code, message, request_id }
      throw new Error(envelope.message);
    }
    return response.json() as Promise<ChatMessage>;
  },
};

If your backend streams, implement stream() as an AsyncIterable<ChatMessage>; useChat renders partial messages as they arrive.

Minimal surface

import { ChatProvider, ChatSurface } from "@hetibank/chat";
import { BuyerUiComponentView } from "@hetibank/websites";
import { ThemeProvider } from "@hetibank/ui";
import { createBrandTheme } from "@hetibank/theme";

const theme = createBrandTheme({ brand: { name: "Lakeshore Properties", primary: "#0b3954", accent: "#087e8b" } });

export function Assistant() {
  return (
    <ThemeProvider theme={theme} as="section">
      <ChatProvider transport={transport} voice={voiceAdapter}>
        <ChatSurface
          title="Ask about a home"
          placeholder="Find assumable homes in Henderson under $3,200/mo"
          renderComponent={(component) => <BuyerUiComponentView component={component} />}
          onAction={(action) => console.log(action)}
        />
      </ChatProvider>
    </ThemeProvider>
  );
}

renderComponent keeps the chat package independent of the websites package: pass the websites dispatcher (or your own), or omit it and the component part falls back to the descriptor's text.

Parts

A reply is an ordered list of parts, so one message can carry text, a card, evidence and actions:

PartUse
textplain sentences, the floor
markdownexplanation, short comparisons — rendered by SafeMarkdown
componenta canonical buyer-UI envelope (card, financing, cash, market snapshot)
formstructured input with validation labels
evidencedefinition ids, source and as-of for the figures above
actionssuggested replies, tool calls and links
errortyped failure with request_id

Safe Markdown

SafeMarkdown supports headings, bold/italic, inline code, lists, links and line breaks. It escapes HTML, never uses dangerouslySetInnerHTML, and opens links with rel="noopener noreferrer". Raw HTML in model output renders as text. This is tested with XSS payloads in packages/chat/test/safe-markdown.test.mjs.

Voice

Push-to-talk is an adapter:

import type { VoiceAdapter } from "@hetibank/contracts";

const voice: VoiceAdapter = {
  async start() { await recorder.start(); },
  async stop() { await recorder.stop(); },
  async *levels() { for await (const level of recorder.levels()) yield level; },
  onTranscript(handler) { return recorder.onTranscript(handler); },
};

VoiceButton and usePushToTalk handle press/release semantics, the level visualizer and reduced-motion. Speech-to-text, permissions and provider choice stay in your app.

Handoff

Use HandoffLink for "open the full page" transitions. It carries identifiers only — never a bearer token or a document URL — and the target page must reauthorize. Preserve conversation state in your own store if the dedicated page should continue the thread.

Error states

useChat maps a thrown HetibankApiError into an error part and keeps request_id. Render it with ChatErrorState; offer retry only when error.retryable is true.

What the package deliberately does not do

  • No network, model calls or API keys.
  • No conversation persistence, analytics or moderation.
  • No microphone/speech implementation.
  • No raw HTML rendering.