Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mindset.ai/llms.txt

Use this file to discover all available pages before exploring further.

The <mindset-agent> element is the customer-facing contract. It dispatches DOM events as the agent runs, and you listen with standard addEventListener. Every event name starts with mindset:, every event bubbles, every event is composed (it crosses shadow DOM boundaries), and every event has a documented event.detail payload. You can listen on the element directly, or higher up the tree if it’s easier:
const agent = document.querySelector('mindset-agent');

// Listen on the element
agent.addEventListener('mindset:text-delta', (e) => {
  appendToOutput(e.detail.content);
});

// Or listen on document, events bubble
document.addEventListener('mindset:thread-changed', (e) => {
  updateUrl(e.detail.threadUid);
});
Events fall into three groups: lifecycle (the agent’s overall state), thread (which thread is active), and per-turn (what’s happening during a single turn). Read each group below or jump to the event sequence section for the order they fire in.

Lifecycle events

These tell you whether the agent is ready to take input, processing a turn, or in trouble. Use them to enable and disable your input controls, show busy indicators, and surface errors.

mindset:agent-registered

Fires once when the element is added to the DOM and its connectedCallback runs. The agent isn’t bound to a runtime yet at this point (that happens later, after mindset.init() completes), but the element exists and you can attach event listeners. Wait for mindset:agent-idle before calling element methods like setPageTools or sendMessage. event.detail:
FieldTypeDescription
headlessbooleantrue if the element has the headless attribute.
agent.addEventListener('mindset:agent-registered', (e) => {
  console.log('Element registered, headless mode:', e.detail.headless);
});

mindset:agent-initializing

The agent is loading models, fetching its session envelope, and discovering tools. You can’t send messages yet. event.detail: {}

mindset:agent-idle

The agent is ready and waiting for input. This fires on first becoming ready and again every time a turn ends. event.detail: {}

mindset:agent-busy

The agent is processing a turn. Sending another message during a busy state throws. Wait for mindset:agent-idle first, or check agent.isAgentBusy(). event.detail: {}

mindset:agent-error

Init failed, or the agent hit an unrecoverable error. The element stays in the error state until you re-initialize. event.detail:
FieldTypeDescription
codestringA short error code suitable for branching on.
messagestringHuman-readable description.

State transitions

The agent moves through states in a fixed pattern:
init() → mindset:agent-initializing → mindset:agent-idle
                                          ↑   ↓
                                          └───┴── mindset:agent-busy ──┐

                                                                       └─── (turn ends) ──→ mindset:agent-idle

(any state) → mindset:agent-error
Same-state transitions don’t fire the event a second time. If you want to know whenever a turn finishes, listen for mindset:complete. It’s the canonical turn-end signal and carries the full response text.
Warmup and icebreaker turns: after init, before your first sendMessage runs, you’ll see one or two automatic turns:
  1. Warmup (always): the agent does an internal context-loading pass. You’ll see agent-busy → tool-start/tool-end (with silent: true, repeated) → agent-idle, but no mindset:text-delta and no mindset:complete, and no thread events. Analytics or “first response ready” indicators should not key on this turn. They should key on the first mindset:complete after the user’s first sendMessage.
  2. Icebreaker (only if configured): if your agent has an icebreaker message, you’ll then see a full turn cycle (agent-busy → text-delta → complete → agent-idle) before your first sendMessage runs. The icebreaker fires automatically.
Both apply in both modes (built-in chat UI and headless).If you call sendMessage immediately after the first mindset:agent-idle, the call may land while warmup or the icebreaker is in flight and throw. Use the sendWhenIdle pattern. It re-checks busy state on each idle event, so it handles both cases transparently whether you know an icebreaker is configured or not.

Thread events

mindset:thread-changed

The customer-visible thread uid changed. Fires when:
  • A new thread persists server-side, shortly after the user’s first sendMessage turn completes. Listen for this event rather than timing against mindset:complete.
  • agent.switchThread(uid) succeeds
  • The active thread is deleted (the next persisted thread becomes active, or the uid drops to null)
  • A page boots with <mindset-agent thread-uid="<existing>"> and the existing thread loads
event.detail:
FieldTypeDescription
threadUidstring | nullThe new active thread’s UID. null while between threads, for example, immediately after agent.newThread() and before the user’s first message persists.
previousstring | nullThe thread UID that was active before this change.
agent.addEventListener('mindset:thread-changed', (e) => {
  // Persist the new thread in your URL or local storage
  if (e.detail.threadUid) {
    updateUrl(e.detail.threadUid);
  }
});
Important: thread uids are exposed only after the user engages. A freshly-created thread doesn’t have a customer-visible uid until a user-message turn completes and the thread persists server-side. This means:
  • agent.newThread() does NOT fire mindset:thread-changed immediately. It fires after the user sends a message and the thread persists.
  • agent.threadUid returns null between newThread() and the first persisted user turn.
This deliberately matches the chat-UI thread list semantics: the headless API never exposes a uid that wouldn’t also appear in the UI list, can’t be loaded later, or can’t be passed back to agent.switchThread(). See the methods reference § Thread-uid persistence semantics for the full transition matrix. This event suppresses no-op transitions. If the underlying thread store fires for a non-UID change (for example, an isBusy flag toggle), the DOM event does not fire.

Per-turn events

These fire during a single agent turn, in the order the runtime produces them. Most carry chunks of streaming output you’ll want to render incrementally.

mindset:text-delta

A streaming chunk of text from the agent. Append these to your output as they arrive. Each chunk may be a fragment, including punctuation or whitespace, not necessarily a complete word. event.detail:
FieldTypeDescription
contentstringThe new chunk to append.
let output = '';
agent.addEventListener('mindset:text-delta', (e) => {
  output += e.detail.content;
  document.getElementById('reply').textContent = output;
});

mindset:stream-flush

A buffer boundary in the runtime’s stream. Adapters that buffer incoming mindset:text-delta content should commit their buffer when this arrives. Treat what comes next as a new contiguous chunk. Fires multiple times per turn. Consumers that don’t render text incrementally can ignore it. Distinct from mindset:complete, which fires once at the very end of the turn. event.detail: {}

mindset:tool-start

The agent is about to execute a tool. Fires once per tool call, before the tool runs. The matching mindset:tool-end shares the same toolCallId. event.detail:
FieldTypeDescription
toolNamestringThe tool’s name.
toolCallIdstringPairs with the matching mindset:tool-end.
runIdstringLangGraph run ID.
silentbooleanSet per-invocation by the agent runtime. When true, the tool call is part of the agent’s internal reasoning (examples: search_information, recall_message) and customer UIs typically should not surface it. The same tool can be silent on one invocation and not silent on another within a single turn. Trust the flag, not the tool name.
argsunknownThe tool’s input arguments as the agent runtime produced them. The shape reflects the underlying LLM’s tool-call format and may include provider-specific wrapping. Parse defensively if you read it from the event, or use the args your setPageTools handler receives (the SDK extracts those before invoking your handler). Truncated to 5,000 chars when serialized.

mindset:tool-end

The tool finished. The detail payload includes the tool’s output and, for tools that produce visual artifacts, a widget or canvas payload. event.detail:
FieldTypeDescription
toolNamestringThe tool’s name.
toolCallIdstringThe same ID as the matching mindset:tool-start.
runIdstringLangGraph run ID.
durationMsnumberHow long the tool ran, in milliseconds.
silentbooleanSame per-invocation flag as on tool-start, mirrored on the closing event.
widgetWidgetPayload | undefinedRich-rendered payload for show_* tools.
canvasCanvasPayload | undefinedCanvas payload for tools that target the canvas surface.
llmToolCallIdstring | undefinedThe LLM-emitted tool-call ID (e.g. toolu_* for Anthropic), distinct from toolCallId.
outputstring | undefinedTool output text. Truncated to 5,000 chars.
agent.addEventListener('mindset:tool-end', (e) => {
  if (e.detail.widget) {
    renderWidget(e.detail.widget);
  }
});

mindset:citation-applied

Fires after post-processing if citations are enabled for the agent. enrichedText is the agent’s full reply with citation markers (e.g. [1], [2]) inserted. If you render citations, display this in place of the concatenated text-delta content. event.detail:
FieldTypeDescription
enrichedTextstringThe agent’s full reply with citation markers inserted.

mindset:references

The agent produced source references for the turn. Fires when retrieval-augmented generation (RAG) is enabled and the agent used source documents. event.detail:
FieldTypeDescription
recordsRecord<string, unknown>[]Source documents the response drew from. Each record carries the source’s metadata; the exact field set is enrichment-dependent and not yet pinned in the public contract.

mindset:quick-replies

The agent suggested a small set of tappable next-message options (typically 2 to 4). Render whatever arrives rather than pre-allocating slots. The array length is variable and may change as the agent runtime evolves. Show them as buttons that call agent.sendMessage(option) when clicked. event.detail:
FieldTypeDescription
questionstringA short framing prompt to display above the options.
optionsstring[]The suggested replies.

mindset:follow-up-questions

The agent suggested a small set of exploratory follow-on questions (typically 3 to 5). Render whatever arrives. The array length is variable. Distinct from mindset:quick-replies: follow-ups are open-ended prompts, quick replies are tappable canned responses. event.detail:
FieldTypeDescription
questionsstring[]The suggested follow-up questions.

mindset:interrupt

The turn paused. The agent called a UI-interrupt tool (e.g. present_choices, present_quiz) and expects the user to respond. Render UI based on interruptType and args. The turn does not produce mindset:complete. event.detail:
FieldTypeDescription
interruptTypestringThe kind of interrupt. Matches the tool that paused the turn.
argsRecord<string, unknown>The arguments the interrupt tool was called with, typically the data your UI needs to render.
toolCallIdstring | undefinedThe originating tool-call ID.
Resuming an interrupted turn: programmatic resume from the element is on the roadmap and not currently exposed. For now, treat interrupts as turn-ending. Handle the args to render your UI, and drive the next turn with agent.sendMessage() based on the user’s selection.

mindset:guard-intercept

The input was classified as a prompt-injection or jailbreak attempt and refused. The turn ends; no mindset:complete fires. event.detail:
FieldTypeDescription
refusalMessagestringThe text to display to the user.

mindset:plan-update

The agent’s plan advanced (cursor moved to the next step, plan completed, etc.). Fires when the agent uses planning steps and the plan state changes during the turn. event.detail:
FieldTypeDescription
planStateRecord<string, unknown>The full plan-state snapshot.

mindset:thread-title

The agent generated a 3-5-word title for the current thread. Fires once on the first turn of a thread, after post-processing. Persists alongside the thread document. event.detail:
FieldTypeDescription
titlestringThe generated title.

mindset:complete

The canonical turn-end signal. Fires once at the end of a normal turn with the full response text. event.detail:
FieldTypeDescription
responsestringThe complete response text the agent produced.
threadIdstringThe thread the turn ran in.
agent.addEventListener('mindset:complete', (e) => {
  console.log(`Turn done: ${e.detail.response.length} chars in thread ${e.detail.threadId}`);
});
mindset:complete is suppressed when a turn ends with mindset:guard-intercept or mindset:interrupt. Those three are mutually exclusive turn-end signals. Use mindset:complete when you need the full response in one place; use the deltas if you’re rendering progressively.

What you’ll see during a turn

A turn fires a series of events as the agent works through the user’s message. The events fall into rough phases. The agent enters a busy state, runs tools and/or generates text, optionally produces post-processing artifacts (citations, references, quick replies, follow-up questions, a new thread title), and then completes. Events that fire on most turns:
  • mindset:agent-busy at the start.
  • mindset:tool-start / mindset:tool-end (in pairs, one per tool call) if the agent calls tools.
  • mindset:text-delta (repeated) as the agent streams its reply.
  • mindset:stream-flush between text-delta bursts and around tool transitions. Commit any text buffer you’re holding when this arrives.
  • mindset:complete once, at the end of a normal turn, with the full response text.
  • mindset:agent-idle after the turn ends.
Optional events (fire only when the conditions apply):
  • mindset:references, when retrieval-augmented generation produced source documents.
  • mindset:citation-applied, when the agent enriched its reply with citation markers.
  • mindset:quick-replies, when the agent is configured to suggest tappable replies.
  • mindset:follow-up-questions, when the agent is configured to suggest open-ended follow-on questions.
  • mindset:thread-title, on the first turn of a new thread.
  • mindset:plan-update, when the agent uses planning steps.
  • mindset:thread-changed, fires after mindset:agent-idle when a new thread first persists server-side, not during the turn itself.
Don’t depend on a specific event order. Some events are reliable: mindset:agent-busy is always first, mindset:agent-idle is always last, mindset:tool-start and mindset:tool-end always pair around their tool. Post-processing events (citations, references, quick-replies, follow-up-questions, thread-title) fire after streaming completes, but the exact internal ordering between them may shift across runtime versions. Render reactively. Handle each event as it arrives rather than waiting for a specific predecessor.
Turns that don’t fire mindset:complete:
  • A turn that hits a guardrail fires mindset:guard-intercept instead, then mindset:agent-idle.
  • A turn that pauses on a UI-interrupt tool fires mindset:interrupt instead. Handle the interrupt’s args to render UI; drive the next turn with agent.sendMessage().
  • The initial warmup turn after init produces no mindset:complete either (see the warmup-and-icebreaker note in the lifecycle section above).

Listening on the document

Every event bubbles and is composed, so you can listen at any level. Listening on document is useful when you’re not sure exactly where the element will be in the DOM, or when you have multiple <mindset-agent> elements and want one handler:
document.addEventListener('mindset:thread-changed', (e) => {
  // e.target is the <mindset-agent> that fired
  console.log('Thread changed on', e.target.getAttribute('agent-uid'), 'to', e.detail.threadUid);
});
When you have multiple elements on a page, use e.target to disambiguate.

Removing listeners

Standard DOM mechanics. Keep a reference to the function so you can pass it to removeEventListener:
function onDelta(e) {
  appendToOutput(e.detail.content);
}

agent.addEventListener('mindset:text-delta', onDelta);

// Later
agent.removeEventListener('mindset:text-delta', onDelta);
In React, do this in a useEffect cleanup. See the Examples page (Headless: React tab) for the pattern.