/** * Text-to-speech playback for the widget. * * ## What this replaced * * The previous controller drove a `MediaSource` + `SourceBuffer` pipeline fed by * `tts_start` / `tts_data` / `tts_end` SSE frames. TORUK-CORE has never emitted * those events. On-demand speech therefore read a stream that produced nothing: * no audio, no error, no way for a user to tell it had failed. Around 250 lines * of chunk-queue and buffer-teardown machinery existed to serve a protocol with * no server on the other end. * * CORE's actual contract is buffered, and there are two ways in: * * - **On demand** — `POST :deploymentId/tts` answers with one JSON envelope * carrying `{ data, contentType }`, where `data` is base64 for the whole clip. * - **Automatic** — when the flow has auto-TTS on, CORE pushes the same object * as a single `ttsAudio` SSE frame during the turn, and persists it on the * message so it comes back with history. * * Both are handled here, and both end up on one `