Reference
GenUI Safety & Restrictions
Safety model (OWASP LLM05 — every widget passes through this)
src/core/safety.js:
safeText(s, max=2000)— coerces to string, strips ASCII control chars (the C0 rangeU+0000-U+001FplusU+007FDEL), length-clamps.safeUrl(url, {allow})— returns the URL only if its scheme is allow-listed (defaulthttps|http|mailto|tel). It blocksjavascript:,data:,vbscript:, and unknown schemes. A scheme-relative path (/foo,foo/bar) is allowed, but an authority-relative URL (//host,\\host) is rejected (open-redirect / embed-hijack vector). An absolute URL with embedded userinfo (https://user:pass@host/...) is also rejected (phishing / link-spoofing vector).external-videoadditionally requires an absolute http(s) URL.renderSafeLink(info, opts)— builds a real<a>viacreateElement,textContent, and a scheme-checkedhref(neverinnerHTML). Setstarget=_blankandrel=noopener noreferrer. Returnsnulloutside a browser or for an unsafe URL.sanitizeJson(v)— drops__proto__/constructor/prototype(used bysubmitStructuredDataFormandsetDynamicPrompt).clampInbound(s)— inbound text clamp.- No renderer emits HTML. The host renders the
{kind, data}descriptor withtextContent/ DOM APIs. Asummary's markdown is the host's responsibility to sanitize.
Restrictions & gotchas (read before you build)
- Live socket rarely emits widgets —
force_experience:'avatar_only'is hardcoded on the avatar join (wire.js). Use the HTTP converse path for reliable widgets. force_experienceis a hint — never assume the requested experience arrived; the renderer parses whatever shows up.followups-tool,flashcards-tool, andshow-link-toolhave been exercised against real backend output, end to end throughSegmentAssembler→ExperienceRenderer→mountWidget→ click →KavaAnalytics.buttonClicked()(see GenUI · Widget Analytics). This includes thefollowups-tool/show-link-toolboundary flush, where a different runtime arriving mid-stream closes the prior widget correctly. The other six runtimes are implemented from the schema and have not yet been observed from the backend.- Backend may add runtimes outside this set — e.g.
gen-ui-composer-tool,gen-ui-components-tool,kaltura-video-player-tool. They are NOT inRUNTIMES. The renderer routes them to{kind:'unknown'}andonUnhandledrather than faking a known kind. sourcesneeds a knowledge base to cite from — ground a new agent viaknowledge.addRecordandknowledge_ids(ungated; see API-REFERENCE.md § Ground the Agent).sourcesthen renders the brain's real retrieved citations.entryIdplayback needs the Kaltura player.video-gallerypreservesentryId. WhenembedUrlis present (it requirespartnerId;uiConfIdis optional and only pins a specific player uiConf), the SDK renders an inline player. Without it, the host renders byentryId.
Related docs
| Doc | Covers |
|---|---|
| GenUI · Per-Runtime Widget Detail | Per-runtime model keys, constraints, and descriptor shapes |
| GenUI · Authoring and Consuming Widgets | Capability gating and ExperienceRenderer/mountWidget consumption |
| GenUI Reference | Back to the index |