BuildersPut it in your productUse your own UI
Use your own UI
Drives a Mindset agent from your own chat interface with the UI-less client, and draws its quick replies, cards and widgets.
Skills for your coding agent
Build our own chat panel for the billing-help agent with the UI-less client, in our app's style. Draw quick replies and capability cards as buttons, and show the summary for any other widget.The UI-less client runs the same agent as the element, in the page, with no Mindset UI: you draw every message. It uses the same session endpoint as Put an agent on your page. The UI-less client is the full API.
<script type="module">
import { createAgentConversation } from "https://eu.mindset.ai/sdk/mindset-agent-uiless.js";
const chat = createAgentConversation({
agent: "billing-help",
getSession: async () => {
const r = await fetch("/api/session");
if (!r.ok) throw new Error("Could not start a session");
return r.json();
},
});
let reply = "";
chat.on((event) => {
if (event.type === "text_delta") reply += event.content;
if (event.type === "complete") console.log("Agent:", reply);
if (event.type === "run_error") console.error("Turn failed:", event.message);
});
try {
await chat.send("What does my last invoice cover?");
} catch {
// The run_error event above already reported what happened.
}
</script>In a bundled app, import it at runtime and tell your bundler not to resolve the address. Create the conversation once (in React, guard it in a ref), because each createAgentConversation asks your backend for a session.
const UILESS_URL = "https://eu.mindset.ai/sdk/mindset-agent-uiless.js";
// Vite: /* @vite-ignore */ webpack: /* webpackIgnore: true */
const { createAgentConversation } = await import(/* @vite-ignore */ UILESS_URL);Widgets#
A widget arrives as a widget-payload event during a turn, or as widget on a message in a restored transcript. kind, summary and toolCallId are public contract, and a message that showed a widget has its summary as its content.
kind | What doc holds | What to draw |
|---|---|---|
quick_replies | { replies: [{ label, value? }] } | One button per reply. A tap sends value, or label when there is no value |
capability_cards | { cards: [{ title, description, prompt }] } | One card per entry. A tap sends prompt |
chart, callout, table, card, badge, stat, widget | A widget tree. Not a public contract | summary, as plain text |
This module draws text, quick replies, capability cards, and a summary for everything else:
<div id="log"></div>
<form id="composer">
<input id="message" autocomplete="off" />
<button type="submit">Send</button>
</form>
<script type="module">
import { createAgentConversation } from "https://eu.mindset.ai/sdk/mindset-agent-uiless.js";
const log = document.querySelector("#log");
const form = document.querySelector("#composer");
const input = document.querySelector("#message");
const chat = createAgentConversation({
agent: "billing-help",
getSession: async () => {
const r = await fetch("/api/session");
if (!r.ok) throw new Error("Could not start a session");
return r.json();
},
});
function addLine(text, className) {
const p = document.createElement("p");
p.className = className;
p.textContent = text;
log.appendChild(p);
return p;
}
function send(text) {
addLine(text, "user");
chat.send(text).catch(() => {});
}
function addButtons(items) {
const row = document.createElement("div");
for (const { label, text, detail } of items) {
const button = document.createElement("button");
button.type = "button";
button.textContent = detail ? `${label}: ${detail}` : label;
button.addEventListener("click", () => {
row.querySelectorAll("button").forEach((b) => (b.disabled = true));
send(text);
});
row.appendChild(button);
}
log.appendChild(row);
}
function drawWidget(kind, doc, summary) {
if (kind === "quick_replies" && Array.isArray(doc?.replies)) {
addButtons(doc.replies.map((r) => ({ label: r.label, text: (r.value ?? r.label).trim() })));
} else if (kind === "capability_cards" && Array.isArray(doc?.cards)) {
addButtons(doc.cards.map((c) => ({ label: c.title, detail: c.description, text: c.prompt })));
} else if (summary) {
addLine(summary, "widget-summary");
}
}
let reply = null;
chat.on((event) => {
switch (event.type) {
case "text_delta":
reply ??= addLine("", "agent");
reply.textContent += event.content;
break;
case "widget-payload":
drawWidget(event.kind, event.doc, event.summary);
break;
case "complete":
case "run_error":
reply = null;
break;
default:
break;
}
});
form.addEventListener("submit", (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;
input.value = "";
send(text);
});
</script>For a returning user's history, call drawWidget(m.widget.kind, m.widget.doc, m.widget.summary) for each message in history_settled that has a widget. Events reference has every field.
What you can't do yet#
- Draw charts, tables, cards and authored widgets reliably. Nothing promises the shape of
doc. - Run a scripted agent. A script's greeting, cards and phases appear only in the element.
If you need these, use the element, or have the agent answer in text for your integration.