Explanation

Platform Architecture — Agentic Avatar System

For platform developers: how the whole system works end to end — the backend services, the text-conversation flow, the live-video runtime wire protocol, how it scales, and how it handles failure. Enough detail to reimplement any layer with zero dependency on Kaltura's apps, widgets, or libraries (just a Socket.IO client + standard WebRTC).

Source of truth. Reverse-engineered and verified against the running system: the avatar management backend (management plane — organized internally into agent / avatar / catalog / intellect / application modules), the Genie brain backend (organized internally into assistant / thread / message / feedback / followup / intellect / knowledge modules), the avatar runtime client (the browser-side connection state machine + XState connect machine), the WebRTC avatar engine (the client-side session object driving the ASR/STV peer connections), the scripted-video control service (the scripted-video control API at /v1/avatar-session/*), and the avatar infrastructure module. Symbol names below are the stable contracts to navigate by; exact details live in WIRE-PROTOCOL.md.

Companion docs. New here? GETTING-STARTED.md. Building an app? API-REFERENCE.md. Driving your UI from the avatar? CLIENT-COMMANDS.md. This page is the map — the exact field-by-field mechanics (connect sequence, ASR/STV wire shapes, scaling internals, SDK module routing, failure-mode tables) live in ARCHITECTURE-REFERENCE.md; a from-scratch reimplementation recipe lives in ARCHITECTURE-RECIPE.md.

Contents


The Three Planes

The system is three planes. An app uses only the planes it needs.

Plane What it does Backend host Where documented
Management Create/configure agents, avatars, intellects, catalog, sessions api.avatar.us.kaltura.ai API-REFERENCE.md
Conversation (text) The AI brain — chat, memory, structured output genie.nvp1.ovp.kaltura.com "Text Conversation Flow" below
Runtime (video) Live photorealistic talking avatar over WebRTC conversation-manager + STV/media server + brain "Video Runtime Protocol" below

Backend Services Map

Service Public host Responsibility
avatar management backend api.avatar.us.kaltura.ai/v1 Agents, avatars, catalog (incl. ElevenLabs voice cloning), studio-intellect proxy, application/* utilities. Organized internally into agent/avatar/catalog/intellect/application modules; routes follow a <prefix>/<action> convention (e.g. catalog prefix is catalog-item).
scripted-video control service api.avatar.us.kaltura.ai/v1/avatar-session/* (nginx-proxied) The scripted-video control API: avatar-session/create (KS) → init-clientkeep-alive (10s) → end. Served by the scripted-video control service, NOT the avatar management backend — only the host/path prefix is shared via proxy.
Genie brain backend genie.nvp1.ovp.kaltura.com The brain: assistant/converse, intellect CRUD, threads, messages, feedback, followups
conversation-manager conversation.avatar.us.kaltura.ai Live-avatar control plane (Socket.IO): session orchestration, ASR signaling relay, brain output stream
STV + media server srs.avatar.us.kaltura.ai (egress host) Video origin — renders the talking face server-side and always egresses it to clients over WHEP (never RTMP; that's a server-internal detail, not part of the client contract). Send cast_mode: "rtmp" in stvNewSession (or omit it — that's the client default) to get the working URL shape: {srsBaseUrl}/rtc/v1/whep/?app=app&stream={session_id}.
TURN turn.avatar.us.kaltura.ai WebRTC relay for both media legs (default username/credential in wire.js's turnServers(), overridable via creds). Addressed with explicit ports+transports (see ARCHITECTURE-REFERENCE.md). STV uses iceTransportPolicy:'relay'; ASR's policy is client-dependent but relays via TURN either way (the ASR server only advertises a private candidate). See WIRE-PROTOCOL.md §5 for the per-client matrix.
ML services internal Machine-learning services behind application/generateAgentProfile

Text Conversation Flow

The simplest intelligent path — no video, fully headless. Client → POST https://genie.nvp1.ovp.kaltura.com/assistant/converse with a geniegpcid:<configId> KS. The response is an NDJSON (or SSE) stream of segments; the brain runs server-side. Segment type values and parsing rules are identical to the avatar's agent_raw_text stream (see ARCHITECTURE-REFERENCE.md's "Conversation Phase"). Full endpoint details: API-REFERENCE.md.


Video Runtime Protocol — The Big Picture

The live talking avatar — the full bidirectional protocol.

A full interactive agentic avatar is three concurrent channels over one Socket.IO connection plus two WebRTC peer connections:

                          ┌───────────────────────────────────────────────┐
                          │   conversation.avatar.us.kaltura.ai           │
                          │   (Socket.IO control plane + agent brain)     │
   ┌──────────┐  socket   │                                               │
   │          │◄─────────►│  • handshake / join / session                 │
   │  YOUR    │           │  • agent_raw_text  (brain output, NDJSON)     │
   │  BROWSER │           │  • stvStartedTalking / stvFinishedTalking     │
   │  CLIENT  │           │  • ASR WebRTC signaling relay                 │
   │          │           └───────────────────────────────────────────────┘
   │          │  WebRTC (ASR, mic→server)   via socket-relayed SDP/ICE
   │          │═════════════════════════════════════════►  speech-to-text + brain
   │          │
   │          │  WebRTC (STV, server→video) via WHEP (HTTP SDP)
   │  <video> │◄═════════════════════════════════════════  srs.avatar.us.kaltura.ai
   └──────────┘

The brain (Genie) runs entirely server-side. The client never calls an LLM — it publishes audio, receives video, and receives the brain's text as agent_raw_text deltas (identical format to the /assistant/converse NDJSON).

For the exact connect sequence, wire shapes, endpoints, and scaling model, see ARCHITECTURE-REFERENCE.md. For the exhaustive map — every socket event with its captured payload + repo source, the exact ICE/SDP/WHEP config, the parsed agent_raw_text delta types, and a turn-by-turn event trace — see WIRE-PROTOCOL.md (built from a live capture, verified against the running system). This section is the orientation; those docs are the reference.


Two Runtime SDK Paths (choose the right one)

There are two avatar runtimes. They are NOT interchangeable.

scripted-video control service client (/v1/avatar-session) avatar runtime client (conversation.avatar socket)
Avatar video (STV/WHEP)
Mic / ASR uplink ✅ (asr-webrtc-*)
Genie brain ❌ (you supply every line of text) ✅ (server-side, streams agent_raw_text)
You call mgmt.avatarSessions.say() (audio only — see below) nothing — the user speaks, the brain answers
Use for scripted / puppet avatars (you drive the words) interactive agentic avatars (autonomous conversation)

The protocol above describes the interactive path. The scripted path has no text-in of its own: the service's say-text route 503s on every call (a live server bug), so the SDK wraps only say-audio — you provide pre-rendered speech audio (e.g. from your own TTS call) and its duration. Full auth/lifecycle details: API-REFERENCE.md § Scripted-Video (STV-only) Sessions; runnable example: examples/scripted-video-session.mjs + .html.


Displaying the Avatar Video

The SDK assigns the WHEP stream to cfg.videoEl.srcObject and does nothing else — no CSS, no sizing. The backend's rendered aspect ratio is not a published contract (see API-REFERENCE.md § Upload a Custom Visual on catalog.createVisual preprocessing), so size the box with object-fit: cover rather than assuming a fixed aspect ratio — it fills the box and crops evenly no matter what the stream's actual aspect ratio turns out to be:

.avatar-box {
  width: 320px;
  aspect-ratio: 1 / 1;      /* pick whatever the fixed side of YOUR layout needs */
  overflow: hidden;
  border-radius: 12px;      /* optional */
}
.avatar-box video {
  width: 100%;
  height: 100%;
  object-fit: cover;        /* fills the box, crops evenly — no letterbox/pillarbox bars */
}

For a circular picture-in-picture mask, swap border-radius + overflow: hidden for clip-path: circle(50%) on .avatar-box (or directly on the <video>).

object-fit: cover never shows bars regardless of the source's actual aspect ratio — that's why it's the right default even without a published backend resolution to size against.

Omit videoEl entirely for a headless/custom-render integration (canvas, WebGL, a circular-mask renderer) — both KalturaAvatarSession and KalturaScriptedVideoSession fire a 'track' event ({track, streams}) the moment their STV peer's ontrack fires, whether or not videoEl is configured.

For a dynamic crop/object-position instead of generic object-fit: cover, both classes also fire 'videoMetadata' ({videoWidth, videoHeight}) once per connect, as soon as the decoder resolves the stream's actual dimensions. There's no fixed/published output resolution to hardcode against — this event is the source of truth.


SDK Module Map — Overview

For the public surface, entry points, and how-tos, read README.md — its Management/Experience overview has the module-to-resource map.

Both SDK entry points share one core: src/core/* is the shared leaf layer both ./management and ./experience depend on (http.js transport, errors.js, session.js, stream.js, redact.js, safety.js, ids.js, knowledge-enums.js). Core never imports from management/ or experience/. ./management (Management, src/management/client.js) enforces the two-KS guard via assertAdmin/assertConversation before any network call; ./experience (KalturaAvatarSession, src/experience/session.js) is the live socket+WHEP runtime from "Video Runtime Protocol" above, taking only a short-lived conversation token, with socket.io INJECTED, never bundled.

For the full module-by-module map (each management module's exposed surface and which backend door it writes to), the capabilities-resolution return shape, the GenUI rendering layer, and the partner-config-DTO-vs-intellect-DTO routing rule, see ARCHITECTURE-REFERENCE.md's "SDK Module Map & Data Flow".


Resilience & Failure Handling — Overview

How the system behaves under network failures, disconnects, and device problems: three reconnection tiersSocket.IO transport, the WebRTC media peers (ASR + STV), and this SDK's own avatar-session recovery — only loosely coordinated with each other. The SDK wires the WebRTC-peer tier to its own session-recovery tier (_recoverMedia_coldReconnect); a custom client that skips KalturaAvatarSession must wire that itself.

For the full three-tier table, the headline risk in detail, the failure-mode matrix, device-permission handling, WebRTC media-peer reconnection detail, and the tool-call-spiral circuit breaker mechanism, see ARCHITECTURE-REFERENCE.md's "Resilience & Failure Handling".

Click to talk with Nova — she knows this whole SDK.