<!-- GENERATED by scripts/build-llms.mjs from llms/conversation.md — do not edit this file. -->

# `lr-realtime-session`

- **Import** `import '@aceshooting/lyra-ui/components/lr-realtime-session.js';` (stable tag alias; registers the tag)
- **Class** `LyraRealtimeSession`, also available unregistered from `@aceshooting/lyra-ui/components/conversation/realtime-session/realtime-session.class.js`
- **Family** `components/conversation/` — see `llms/index.md` for its siblings
- **Status** `stable` since `7.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 12 parts, 0 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-realtime-session`

Provider-neutral realtime voice shell composing connection state, `lr-audio-visualizer`,
`lr-push-to-talk`, and `lr-transcript-feed`. Transport/authentication/playback remain host-owned.

**Properties:** `state: RealtimeConnectionState = 'disconnected'` (reflected), where the closed set
is `'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error'`; `voiceState:
AudioVisualizerState = 'idle'` (attribute `voice-state`); `level: number | null = null` (finite,
clamped by the composed visualizer); `stream: MediaStream | null = null`; `sessionId: string = ''`
(attribute `session-id`) — forwarded to the transcript feed so changing sessions resets finalized
entry announcement identity; `entries: LyraTranscriptEntry[] = []` (attribute: false);
`muted: boolean = false` (reflected);
`showCapture: boolean = true` (attribute `show-capture`, reflected, string-aware true-default
converter); `label: string = ''`. Invalid attribute or direct-property values for `state` and
`voiceState` normalize to their safe defaults (`'disconnected'` and `'idle'`) through the same
closed-set converter.

When a state transition removes a focused session action, focus moves to the replacement
connect/disconnect action. Setting `showCapture` to `false` applies that handoff only when the
capture control owned focus; a surviving built-in, slotted, or external focus destination is not
moved.

**Events:** session intents are `lr-connect`, `lr-disconnect`, `lr-mute-change` (`{ muted }`), and
`lr-interrupt`. The composed capture's complete public event surface bubbles through unchanged and
is also included in `LyraRealtimeSessionEventMap`: `lr-record-start` (`{ stream }`),
`lr-record-chunk` (`{ blob }`), `lr-record-stop` (`{ blob, durationMs }`), `lr-record-cancel` (no
detail), `lr-record-error` (`{ error }`), `lr-level` (`{ level }`), and
`lr-record-state-change` (`{ state }`). These are the child's original bubbling/composed events rather than
parent re-emissions;
normal Shadow DOM retargeting means a listener outside the session observes the session as `target`.
The composed transcript's internal `lr-follow-change` event is contained and is not part of this
session shell's public event surface.

**CSS parts:** `base`, `header`, `status`, `activity`, `controls`, `connect`, `disconnect`, `mute`,
`interrupt`, `capture`, `transcript`, `error`.

Visible status and error text remain ordinary, non-live content. After the initial baseline,
non-error `state` transitions are appended to the shared polite light-DOM announcement sink and a
transition to `error` uses the shared assertive sink instead. Initial and reconnect renders stay
silent, and sinks follow the component's `ownerDocument` when it is adopted.

**Slots:** `controls` — provider-specific actions appended to the built-in session controls.
**Optional peer deps:** none. Transport, credentials, capture permission, and media playback
remain host-owned.

```ts
import "@aceshooting/lyra-ui/components/conversation/realtime-session/realtime-session.js";
```
