@scenaro/sdk separates a portable session contract from the Transport that carries it — see Transport for the full picture. For the majority of integrations — a voice button embedded on a page — the quickstart is all you need, and you’ll never see an engine-specific type. This guide is for the minority of integrations that need more: custom audio visualizers, avatars, multi-participant layouts, or anything else the current engine’s ecosystem already does well.
This is an assumed extension point, not an escape hatch you’re forced into. Scenaro doesn’t wrap or re-export the engine’s components — you import them directly from their own packages and use them next to useScenaroSession.

1. When to use this guide

Reach for Transport extensions when you need:
  • Custom audio visualizers or waveforms
  • Agent avatars driven by speaking state
  • Multi-participant layouts
  • Anything else already solved by the current engine’s component library
If you just need status, messages, config, or tool calls, stay on the portable contract — you don’t need anything in this guide.

2. Portable contract vs engine extension

useScenaroSession always returns the same portable contract (status, agentState, messages, config, tools, events) regardless of which Transport implementation is running underneath. On top of that, it also exposes room — an engine extension: the live engine handle for the current Transport implementation.
ContractStabilityWhere it’s documented
status, agentState, messages, config, tools, eventsPortable — stable across future Transport implementationsQuickstart, SDK reference
room, RoomContext, @livekit/components-react hooks/componentsEngine-specific (LiveKit today) — not guaranteed to carry over to a future Transport implementationThis guide
Code that reads status or agentState keeps working if Scenaro ever adds another Transport implementation (e.g. Pipecat) behind useScenaroSession. Code that calls room.localParticipant... or uses useVoiceAssistant() is written against the current engine directly — the same trade-off you’d make using that engine standalone. See Transport for the internal boundary that makes this possible.

3. LiveKit implementation (today)

The current Transport implementation is LiveKit. This section is the concrete, engine-specific how-to.

Install the LiveKit peer dependencies

livekit-client and @livekit/components-react are peer dependencies of @scenaro/sdk — install them once you start using this guide (the quickstart does not require them):

Using room with LiveKit components

ScenaroProvider injects RoomContext for you, so LiveKit’s components and hooks work as soon as a session is active — no manual <RoomContext.Provider> wiring required.
Scenaro never re-exports these components. When LiveKit ships something new — Agents UI, a new Session API, a new visualizer — you use it immediately, without waiting for a Scenaro equivalent.

What stays yours to maintain

Because this code is written against LiveKit’s API surface directly, keep in mind:
  • A future engine swap isn’t automatic here. If Scenaro adds a Pipecat Transport implementation behind useScenaroSession in the future, the portable contract will keep working unchanged. Code that calls room.* or LiveKit component hooks will need to be ported by hand — the same way any code written against a specific vendor’s low-level API would.
  • Support boundary. Scenaro’s contract covers session, auth, config, tools, and features. Anything under room — audio devices, track subscriptions, participant behavior — is LiveKit’s surface; consult the LiveKit docs for it.
  • Don’t mix contracts in shared components. Keep components that only need the portable contract free of LiveKit imports, so they stay reusable if the Transport implementation ever changes.

Next steps