<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:
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:
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:
State transitions
The agent moves through states in a fixed pattern: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:-
Warmup (always): the agent does an internal context-loading pass. You’ll see
agent-busy → tool-start/tool-end(withsilent: true, repeated)→ agent-idle, but nomindset:text-deltaand nomindset:complete, and no thread events. Analytics or “first response ready” indicators should not key on this turn. They should key on the firstmindset:completeafter the user’s firstsendMessage. -
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 firstsendMessageruns. The icebreaker fires automatically.
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
sendMessageturn completes. Listen for this event rather than timing againstmindset: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:
agent.newThread()does NOT firemindset:thread-changedimmediately. It fires after the user sends a message and the thread persists.agent.threadUidreturnsnullbetweennewThread()and the first persisted user turn.
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
mindset:complete
The canonical turn-end signal. Fires once at the end of a normal turn with the full response text.
event.detail:
mindset:complete is suppressed when a turn ends with mindset:guard-intercept, mindset:interrupt, or mindset:turn-aborted (a user stop). Those turn-end signals are mutually exclusive. Use mindset:complete when you need the full response in one place; use the deltas if you’re rendering progressively.
mindset:turn-aborted
The turn was stopped by the user — via the visible Send→Stop control or a call to agent.stop(). No mindset:complete fires. Mindset AI persists only what was already shown and keeps the thread continuable, so the next sendMessage starts a fresh turn.
event.detail:
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-busyat 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-flushbetween text-delta bursts and around tool transitions. Commit any text buffer you’re holding when this arrives.mindset:completeonce, at the end of a normal turn, with the full response text.mindset:agent-idleafter the turn ends.
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 aftermindset:agent-idlewhen 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.mindset:complete:
- A turn that hits a guardrail fires
mindset:guard-interceptinstead, thenmindset:agent-idle. - A turn that pauses on a UI-interrupt tool fires
mindset:interruptinstead. Handle the interrupt’sargsto render UI; drive the next turn withagent.sendMessage(). - The initial warmup turn after init produces no
mindset:completeeither (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 ondocument 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:
e.target to disambiguate.
Removing listeners
Standard DOM mechanics. Keep a reference to the function so you can pass it toremoveEventListener:
useEffect cleanup. See the Examples page (Headless: React tab) for the pattern.