@moss-tools/founding-agent
v2.1.0
Published
Embeddable voice founding agent for Moss-powered company websites. Ships a Node-side token helper and a React component.
Maintainers
Readme
@moss-tools/founding-agent
Embeddable voice founding agent for Moss-powered company websites. Drop the React component onto any page and your visitors can talk to a voice agent powered by your Q&A knowledge base.
- Server helper for minting LiveKit session tokens from your backend.
- React component that renders the voice UI and connects to LiveKit on click.
- Works with Next.js, Remix, Vite, or any React setup.
Install
npm install @moss-tools/founding-agent
# required peers for the React component
npm install react react-dom tslib @livekit/components-react @livekit/components-styles livekit-client motion lucide-react zustandServer-only usage (no React) has zero runtime dependencies; Node 18+ fetch is all it needs.
Environment
You get two credentials from the Moss dashboard when you create a founding agent:
MOSS_FA_API_KEY— server-only. Keep this in a backend env var (e.g. Vercel Environment Variables). Starts withsk_.MOSS_FA_PUBLISHABLE_KEY— safe to ship to the browser. Passed to the React component. Starts withpk_.
1. Backend: mint a session token
The React component POSTs to a token endpoint you provide. That endpoint calls the Moss service with your server-only API key and returns the LiveKit token to the browser. A Next.js App Router example:
// app/api/moss-token/route.ts
import { createFoundingAgentSession } from "@moss-tools/founding-agent";
export async function POST() {
const session = await createFoundingAgentSession({
apiKey: process.env.MOSS_FA_API_KEY!,
});
return Response.json(session);
}createFoundingAgentSession returns { token, serverUrl, roomName }.
For more control (custom error handling, reusing the client) use the class form:
import { MossFoundingAgent } from "@moss-tools/founding-agent";
const agent = new MossFoundingAgent({
apiKey: process.env.MOSS_FA_API_KEY!,
timeoutMs: 10_000, // optional
});
export async function POST() {
const session = await agent.createSession();
return Response.json(session);
}2. Frontend: drop in the component
Wrap your page (or app) once with MossFoundingAgentProvider (it fetches the
public config and wires the token endpoint), then render MossFoundingAgentBubble
anywhere inside. By default the bubble pins itself to the bottom-right of the
viewport.
"use client";
import {
MossFoundingAgentBubble,
MossFoundingAgentProvider,
} from "@moss-tools/founding-agent/react";
export default function LandingPage() {
return (
<MossFoundingAgentProvider
publishableKey={process.env.NEXT_PUBLIC_MOSS_FA_PUBLISHABLE_KEY!}
>
<main>
<h1>Talk to our team</h1>
</main>
<MossFoundingAgentBubble />
</MossFoundingAgentProvider>
);
}That's the minimum. The bubble:
- Renders as a 56px circle, fixed to the bottom-right, showing the agent's portal-configured orb (or the preset
coloraura if it has none). - On click, POSTs to
/api/moss-tokento get a session token and connects to LiveKit. - Once live, the circle morphs into a horizontal pill: avatar, status line, mute, and an end-call button.
- End the call (or press
Esc) and the pill collapses back to the icon.
For inline placement (e.g. inside a hero CTA section), pass position="inline":
<MossFoundingAgentBubble position="inline" />Tailwind setup
The bubble uses Tailwind utility classes internally. If your app is on Tailwind v4, point its CSS at the package's compiled output so the utilities get picked up:
/* globals.css */
@import "tailwindcss";
@source "../node_modules/@moss-tools/founding-agent/dist/**/*.{js,mjs}";Props
<MossFoundingAgentProvider>
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| publishableKey | string | — | Required. The pk_... key for this agent. |
| tokenEndpoint | string | /api/moss-token | URL of your backend route that returns { token, serverUrl }. |
| serviceUrl | string | Moss default | Backend base URL. Changing this (or publishableKey) clears the cached config so a reused Provider cannot show the previous agent's identity. |
| onError | (error: Error) => void | — | Fires on config fetch errors. |
<MossFoundingAgentBubble>
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| position | "fixed" \| "inline" | "fixed" | "fixed" pins to the bottom-right of the viewport (with safe-area insets). "inline" renders where you place it. |
| label | string | Agent name from config | Tooltip + aria-label on the idle bubble. |
| appearance | AgentAppearance | Fetched from the portal | Explicit override of the portal-configured look: { shape, colors, soften, noise, widget_theme }. Normally omit it — the widget fetches whatever was saved in the portal and renders that orb automatically. An explicit value pins both the orb and the theme. |
| color | BubbleColorInput | "violet" | Aura color, used only when the agent has no configured appearance. A preset name ("violet" \| "cobalt" \| "teal" \| "emerald" \| "coral" \| "amber") or an arbitrary #RRGGBB hex. |
| themeMode | "dark" \| "light" | Portal widget_theme, else "light" | Themes the pill chrome and the aura. Omitted means follow the configured appearance's widget_theme. |
| className | string | — | Override applied to the bubble/pill container. |
| style | CSSProperties | — | Inline style override (merged after position styling). |
| id | string | — | DOM id on the container. |
The exported BUBBLE_COLORS constant maps preset names to their hex values, and resolveBubbleColor(value) returns the resolved hex.
Appearance
Agents configured in the portal carry an appearance: one of five canvas orb shapes (Pixel, Flow, Radio, Stripes, Conic), one or two custom hex colors, a soften blur, a grain amount, and a light/dark widget theme. The widget fetches it with the publishable key and renders it with no host-site code change, so changing the look in the portal does not require a redeploy.
Resolution order for the avatar is: explicit appearance prop, then the
fetched portal appearance, then the legacy color preset aura. Theme follows
explicit themeMode, then the appearance's widget_theme, then light.
<AgentOrb> is exported if you want the same renderer outside the bubble:
import { AgentOrb, DEFAULT_APPEARANCE } from "@moss-tools/founding-agent/react";
<AgentOrb appearance={DEFAULT_APPEARANCE} state="listening" size={64} />;It honours prefers-reduced-motion (painting one static frame) and stops
animating while offscreen or in a hidden tab.
Live pill
While a call is live the bubble expands into a pill: the agent's avatar on the
left, a status line (Tap to speak / Listening ... / Speaking ...), a mic
toggle, and a red end button. M mutes and Esc ends the call. There is no
elapsed-time counter (it was removed in 2.1.0).
Security model
- The API key is never exposed to the browser. The component only ever sees the publishable key and the short-lived LiveKit token returned by your backend.
- LiveKit tokens are minted server-side by the Moss service with minimal grants (audio publish/subscribe only, no data channel, no room create).
- Room names are chosen by the service (
fa-{slug}-{timestamp}). The browser never proposes a room name.
License
MIT
