Embeddable widget

Drop a chat bubble on any website, connected to one Pepe agent.

Embeddable widget

The widget is a chat bubble you drop into any web page with one <script> tag. It renders a floating button, opens into a chat panel, and talks to a Pepe agent over a live, streaming connection, no dependency and no build step on the page that embeds it.

The widget panel mid-conversation, replying in English

Mint a widget token

A widget’s script tag sits in public page source, so it needs its own kind of token: always locked to one agent, and bound to the site’s origin.

pepe token add --agent support --widget --allowed-origin https://example.com --label "example.com widget"

--widget requires --agent: a public credential always pins to one known-safe agent, never a whole project. --allowed-origin is the site’s scheme and host; the widget’s connection is refused from anywhere else. See Authentication and tokens for the general token model this builds on.

Or do it from the dashboard

The Channels section has a + Widget button that opens a form right there (label, agent, allowed origin, and appearance), no separate trip to the tokens page. After creating one, the dashboard shows the full <script> tag already filled in with the real token, agent, and your server’s own address, ready to copy and paste. Existing widgets keep a collapsible snippet too, and their raw token stays visible any time; unlike a regular API token, a widget token’s value isn’t a secret worth hiding (see Security below), so there’s no “copy it now, you won’t see it again.” Changing which agent or origin a widget uses still means minting a new one and revoking the old (those stay rotate-only), but appearance can be edited in place at any time.

Set the look from the dashboard

Title, logo, color, theme, greeting and position don’t have to live in the <script> tag at all; set them on the widget token instead (at creation, or later via the Edit appearance button on an existing widget) and the script fetches them at load time. Precedence is per field, not all-or-nothing: the token’s value wins whenever it’s set; a field left unset on the token falls back to the tag’s own data-* attribute, then to the built-in default. So this is entirely optional (a plain data-token embed with nothing else keeps working exactly as before), and the two can mix freely: color from the dashboard, greeting hardcoded in the tag, say. The point is that a color or greeting tweak never needs a site redeploy: change it on the dashboard, reload the page, done.

Embed it

Paste the script tag on the page, pointed at your Pepe server:

<script src="https://your-pepe-host/plugin-assets/pepe-widget/widget.js"
        data-agent="support"
        data-token="pepe_your_widget_token"
        data-title="Chat"
        data-logo="https://example.com/logo.png"
        data-color="#ea580c"
        data-theme="dark"
        data-greeting="Hi! How can I help?"
        data-position="right"
        data-lang="en"></script>
Attribute What it does Default
data-agent Cosmetic only: names the visitor’s local session so more than one widget can share a page without colliding. A widget token is always locked to one agent, so this never changes who actually answers. default
data-token The widget token from token add --widget. none
data-server The host to connect to. the script’s own host
data-title The panel header’s text. “Chat”
data-logo A small square image, used for the bubble icon and next to the header title. Omit it to keep the plain chat icon. none
data-color Accent color for the bubble, header and buttons. #ea580c
data-theme light or dark; the panel’s base colors below the header. light
data-greeting The first message shown before the visitor sends anything. picked from data-lang, English otherwise
data-position left or right. right
data-lang The site’s own language (e.g. pt-BR), not the visitor’s browser locale. A site knows what language it’s written in, whereas a browser locale is only a guess about whoever’s reading it. Picks the built-in greeting when data-greeting isn’t set, and is sent once at join so the agent leans toward that language from its very first reply. none

No build step, no npm install: widget.js and its stylesheet are served directly by your Pepe server at /plugin-assets/pepe-widget/, the same generic route any future plugin’s static assets would use.

How a visitor’s session works

Each visitor gets a random id, stored in their browser’s localStorage, sent as the connection’s session so a reload continues the same conversation. Under the hood the widget speaks the same protocol described in WebSocket: prompt in, delta / done / error / watch / session_ended out. A bouncing-dots indicator shows in the panel while the agent is working on a reply, so a visitor never wonders if their message went through.

The header’s new-conversation button (a plain “+”) starts a new conversation right away: it closes the current connection, clears the panel, and reconnects under a fresh session id. That id is persisted immediately, so even a full page reload keeps talking to the new conversation, not the old one. If the agent itself ends the conversation (its end_session tool), the panel shows a small system note in its place and the next message you send starts fresh, without needing to click anything.

No slash commands. The widget speaks the simpler streaming protocol above, not a full chat session; there is no /model, /models, or any other slash command, only the reset button. A widget is always pinned to one agent's own model; to offer a visitor a different one, mint a separate widget token against an agent already set up with that model.

In the dashboard’s Chat page, widget conversations group under Widget, one subgroup per site (the token’s allowed_origin), so running more than one widget across different sites keeps their conversations easy to tell apart, distinct from the dashboard’s own built-in chat.

Security

Give it a narrow agent. A widget faces the public internet with no human approving tool calls. Bind it to an agent scoped to safe, read-only or customer-facing tools, the same guidance as any customer-facing channel in Security and sandbox.

Do it by chat

An agent with the manage_token tool can mint a widget token in conversation:

Create a widget token for the support agent, allowed from https://example.com.

The agent calls manage_token with action: "create", agent: "support", widget: true, and allowed_origin: "https://example.com". Minting a token is not read-only, so the call goes through the permission gate; the raw token comes back in the reply for you to copy into the script tag, and stays available any time with action: "list", since a widget token isn’t a secret worth hiding.

Appearance works the same way, on either action: pass any of title, logo, color, theme, greeting, position on create, or later with action: "update" and the token’s id:

Change the support widget’s greeting to “Hey! Need a hand?” and set its color to #2563eb.

The agent calls manage_token with action: "update", id: "<the token's id>", greeting: "Hey! Need a hand?", and color: "#2563eb"; a field left out of the call keeps its current value.