Reference

Authoring & Consuming Widgets

← Back to GenUI Reference

Authoring: which capability turns each widget on

Set capabilities at intellect creation (the agent's configuration). Do not change them after creation, see Client-Side Commands. Source of truth: src/management/capabilities.js (CAPABILITY_INFO, CAPABILITY_DEFAULTS, OFF_BY_DEFAULT).

kind is tool, segment, mode, or prompt. This only names the mechanism that gates the capability on or off. It says nothing about whether the capability's content is persona-steerable.

avatar_filler (kind: 'prompt') is the exception to watch for. Its filler phrasing is NOT reliably steerable via base_directive, even though it streams as a "spoken" segment alongside avatar/text (see Wire Protocol · Events Catalog). Disable the capability if the default phrasing doesn't fit your persona.

Capability Default Kind Gates runtime Notes
kaltura_genie_experiences ON mode (master) Master switch for structured GenUI. Leaving it on can make the agent prefer built-in widgets over a custom tool. Turn it OFF for command-only agents. See External API Integrations § Don't skip kaltura_genie_experiences: 'off'
generate_followup_questions ON segment followups None
include_sources ON segment sources Pairs with use_knowledge_base (RAG)
video_gallery OFF segment video-gallery (and content-gallery) None
external_video OFF segment external-video None
show_link OFF segment show-link None
avatar_show_content OFF prompt (avatar visual push) None
use_knowledge_base ON tool (feeds sources) async_search_knowledge_base RAG

The structured data-collection form (user-properties-form) is configured via the intellect's user_properties_forms field, not a boolean capability. On the wire this field is a LIST of {call_stage, properties:[{key,type}]} forms. setUserPropertiesForms accepts a single form object and wraps it into a one-element list.

Every capability and its default is listed in Configure an Intellect.

Consuming widgets in your app

The 2-line happy path: pass a DOM Element as mount and the SDK renders for you, live:

import { ExperienceRenderer } from '@kaltura/intelligent-agents/experience/genui';

// session: an active KalturaAvatarSession from your app
new ExperienceRenderer({
  session,
  mount: document.getElementById('widgets'),
  onAction: (action, payload) => console.log(action, payload),
}).start();

mountWidget (genui/renderers/mount.js, exported from ./experience/genui) is the SDK's last-mile descriptor→DOM renderer:

Call it directly with mountWidget(descriptor, targetEl, { replace?, onAction? }), or let ExperienceRenderer call it when mount is an Element (or target).

ExperienceRenderer options

new ExperienceRenderer({ session?, mount, target?, onAction?, renderers?, replace?, onUnhandled?, urlPolicy?, partnerId?, uiConfId?, clearOnTurnStart?, maxRendered? }) (genui/renderer.js). partnerId alone enables video-gallery to build a player-embed iframe URL from each entryId. uiConfId (string or number) is optional and pins a specific player uiConf in that URL.

mount is a (descriptor)=>void function (full control) or a DOM Element (auto-rendered via mountWidget). maxRendered (default 100) caps the rendered history: the oldest descriptor is dropped when the cap is exceeded, so a long-lived session can't grow memory unboundedly.

Live vs. headless dispatch

In the live socket runtime, .start() subscribes to session.on('brainSegment') and flushes on turnEnd/avatarStopTalking, resetting on interrupted. clearOnTurnStart (default true) also resets the assembler and clear()s rendered/last on the session's turnStart event (re-emitted from the raw agent_start_speech socket event). So a widget from turn N never lingers into turn N+1. Set false for intentional cross-turn persistence.

Headless, call .render(runtimeName, widget) (or .render(segment)) per segment from a conversations.stream() feed. This is the reliable path.

Registration, fallback, and provenance

onAction, WIDGET_KINDS, and the hand-rolled escape hatch

onAction(action, payload) surfaces interactions mountWidget can't fulfil itself:

WIDGET_KINDS (exported) is the frozen list of every kind (the nine first-class GenUI runtimes, plus unknown and error). Use it for an exhaustive host switch or a parity test.

You can still read seg.metadata.runtimeName and build DOM yourself (use renderSafeLink/safeText/safeUrl so nothing un-sanitized hits the DOM). Prefer mountWidget. The hand-rolled path is for a fully custom design system only.

Markdown rendering (opt-in)

mountWidget(descriptor, el, {markdown:true}) parses a summary widget's text (genui/renderers/markdown.js, renderMarkdown) as markdown instead of flat text. It supports:

A markdown table renders through the tableEl builder (genui/renderers/dom-helpers.js), one shared, safe <table> construction path with no duplicate table logic.

This is markdown-in-plain-text rendering, not a new wire segment type. The underlying descriptor is still {kind:'summary', data:{summary, ...}}. Only the render path changes.

It is never innerHTML. Every text run is built via textContent/createTextNode, so a raw <script> tag in the LLM output is inert text, not markup. Every extracted URL goes through safeUrl too. A javascript: or other unsafe-scheme link degrades to plain safe text, never a dead or unsafe href.

Without opts.markdown, the summary renders as flat escaped text.

Theming + a11y contract (kgenui classes)

mountWidget emits semantic, accessible DOM and a stable kgenui/kgenui__* class contract. Theme it in your CSS. The SDK ships none.

Scope Classes
Root kgenui kgenui--{kind} (role="group", aria-label); kgenui--unknown/kgenui--error for the two fallback kinds
Shared across widgets kgenui__title, kgenui__text, kgenui__list, kgenui__card, kgenui__link, kgenui__img, kgenui__gallery, kgenui__meta, kgenui__muted, kgenui__sr-only
followups kgenui__chip, kgenui__chips
flashcards kgenui__flip, kgenui__back
sources kgenui__source, kgenui__snippet, kgenui__score
summary kgenui__bullet
video-gallery kgenui__player, kgenui__play, kgenui__play-glyph
external-video kgenui__embed (iframe wrap)
user-properties-form kgenui__form, kgenui__field, kgenui__label, kgenui__help
user-properties-form and graded-question (free-text input/submit) kgenui__input, kgenui__submit
graded-question kgenui__prompt, kgenui__feedback, kgenui__explanation, kgenui__options-group, kgenui__options, kgenui__option-label, kgenui__radio, kgenui__answer-echo
Opt-in markdown ({markdown:true}) kgenui__markdown, kgenui__md-h, kgenui__md-p, kgenui__md-list, kgenui__md-code, kgenui__md-pre, kgenui__md-link, and the shared kgenui__table (also used by other structured-table output)

Built-in accessibility:

The SDK ships zero CSS for these class names. Style the .kgenui__* block in your own stylesheet.

screen_share_analysis / sendScreenShot(data)

An avatar agent can look at the user's screen. Turn it on with the agent's screenShare setting (agent fields). Setting the screen_share_analysis capability on the intellect is not enough.

On a live session, call session.startScreenShare(). It opens the browser's screen picker and sends a still image every 2 seconds. session.stopScreenShare() ends it, and so does the user's own "stop sharing" control. See Screen share for options, events and error codes.

session.sendScreenShot(data) sends one still you captured yourself (data is an ArrayBuffer or Uint8Array). Use it only when you need your own capture path. Both calls throw capability_disabled unless the agent's screenShare setting is on.

Doc Covers
GenUI · Model and Runtimes Runtime catalog, data flow, delivery paths, force_experience
GenUI · Per-Runtime Widget Detail Per-runtime model keys, constraints, and descriptor shapes
GenUI · Widget Analytics Reporting widget interactions to KAVA without double-counting
GenUI Reference Back to the index
Click to talk with Nova — she knows this whole SDK.
Nova AI assistant — knows this whole site

Nova is an AI assistant and can make mistakes. Check important details in the docs. Reloading starts a fresh chat.