@pipecat-ai/moq-transport 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"mappings":"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","sources":["transports/moq-transport/src/index.ts","transports/moq-transport/src/moqTransport.ts"],"sourcesContent":["export * from \"./moqTransport\";\n","/*\n * Copyright (c) 2024-2026, Daily\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport * as Json from \"@moq/json\";\nimport * as Moq from \"@moq/net\";\nimport * as Publish from \"@moq/publish\";\nimport { Effect, Signal } from \"@moq/signals\";\nimport * as Watch from \"@moq/watch\";\nimport {\n type PipecatClientOptions,\n type RTVIEventCallbacks,\n RTVIMessage,\n RTVIError,\n Transport,\n type Tracks,\n type TransportState,\n} from \"@pipecat-ai/client-js\";\n\nconst DEFAULT_NAMESPACE = \"pipecat\";\nconst DEFAULT_CLIENT_ID = \"client0\";\nconst DEFAULT_BOT_ID = \"bot0\";\nconst DEFAULT_TRANSCRIPT_TRACK = \"transcript.json.z\";\n// Bounded jitter buffer on the audio decoder. Lower = more interactive\n// but more drops on bad networks. Matches the bot's audio_in_max_latency_ms\n// in spirit (each side enforces its own deadline).\nconst DEFAULT_AUDIO_LATENCY_MS = 80;\n// Latency ceiling for buffered TTS playback (ms). A finite cap (vs\n// uncapped) because the player's group/jitter buffer needs a concrete\n// maximum span to retain before dropping. The bot paces its writes a\n// little under this (~25s, see the Python transport's\n// `audio_out_max_buffer_ms`) so the producer self-limits below this drop\n// ceiling and the player never actually has to drop.\nconst DEFAULT_AUDIO_BUFFER_MAX_MS = 30 * 1000;\n// Sample rate we pin on the publish-side Opus encoder. Opus only supports\n// {8, 12, 16, 24, 48} kHz; 48 kHz is the upstream default and what most\n// browsers report as the mic's native rate. The bot reads this from our\n// catalog and resamples to its `audio_in_sample_rate` after decode, so we\n// don't need to match it exactly — but pinning it keeps the catalog\n// unambiguous.\nconst DEFAULT_AUDIO_SAMPLE_RATE = 48000;\n\n/**\n * Constructor options for the MoQ transport.\n *\n * The browser dials a MOQ peer (relay or bot in serve mode) at\n * ``relayUrl`` and publishes its mic under ``<namespace>/<clientId>``\n * while consuming the bot under ``<namespace>/<botId>``. Audio tracks\n * inside each broadcast are catalog-driven (codec, sample rate, channel\n * count are discovered at connect time), so they aren't pinned here.\n */\nexport interface MoqTransportOptions {\n /**\n * Full URL of the MOQ peer (e.g. ``https://relay.example.com:4080/moq``).\n * WebTransport (HTTPS/HTTP3); @moq/net races WebSocket as a fallback\n * if WebTransport isn't reachable.\n */\n relayUrl: string;\n\n /**\n * Pinned cert hashes for self-signed dev setups. Same shape as the\n * native WebTransport ``serverCertificateHashes`` option.\n */\n serverCertificateHashes?: WebTransportHash[];\n\n /**\n * This client's participant id. Combined with ``namespace`` it forms\n * the broadcast path the client publishes under: ``<namespace>/<clientId>``.\n */\n clientId?: string;\n\n /**\n * The peer (bot) participant id to consume. Subscriptions target\n * ``<namespace>/<botId>``; track names come from the bot's catalog.\n */\n botId?: string;\n\n /**\n * Top-level namespace (analogous to a room name). Defaults to ``\"pipecat\"``.\n */\n namespace?: string;\n\n /**\n * Track name for RTVI server→client messages (transcripts, bot-ready,\n * speech events, etc.). The client subscribes at\n * ``<namespace>/<botId>/<transcriptTrack>``. Defaults to\n * ``\"transcript.json.z\"``.\n *\n * Stays pinned because the transcript is a ``@moq/json`` stream track\n * (an ordered, lossless append-log, wire-compatible with the bot's Rust\n * ``moq_json::stream``), not a catalog rendition — the catalog only\n * describes media tracks (audio/video). The ``.z`` suffix marks that the\n * stream is compressed.\n */\n transcriptTrack?: string;\n\n /**\n * Latency floor (ms) — the jitter buffer the player keeps before\n * playback. Lower = more interactive, more drops; higher = smoother,\n * more glass-to-glass delay. Maps to the `min` bound of the\n * ``@moq/watch`` ``Sync.latency`` range.\n */\n audioLatencyMs?: number;\n\n /**\n * Latency ceiling for buffered playback (the `max` bound of the\n * ``@moq/watch`` ``Sync.latency`` range).\n *\n * The bot writes TTS audio faster than real-time with future-dated\n * timestamps; the player buffers it and plays at the encoded pace\n * instead of skipping ahead. This sets how much it's allowed to\n * build up before re-anchoring:\n *\n * - a number (default: 30 s) — cap the buffer at that many ms. The\n * player retains up to this much faster-than-real-time audio before\n * dropping; an interruption (``user-started-speaking``) flushes early\n * via ``reset()``. The bot paces a little under this so it never\n * actually overruns the cap.\n * - ``\"real-time\"`` — collapse to the floor (minimize latency, the old\n * skip-ahead behavior; only useful with a live, real-time publisher).\n */\n audioBufferMaxMs?: number | \"real-time\";\n\n /**\n * Sample rate (Hz) the client publishes its mic audio at. Must be one\n * of Opus's supported rates: 8000, 12000, 16000, 24000, or 48000.\n * Defaults to ``48000`` to match the typical browser mic rate.\n *\n * The bot reads this from the client's catalog and resamples to its\n * own ``audio_in_sample_rate`` after Opus decode, so exact agreement\n * isn't required — pinning a known value just keeps the catalog\n * unambiguous.\n *\n * Bot-side playback sample rate isn't an option here: ``MoqTransport``\n * reads whatever the bot advertises in its catalog and configures its\n * playback ``AudioContext`` accordingly.\n */\n audioSampleRate?: number;\n}\n\ninterface ResolvedOptions {\n relayUrl: string;\n serverCertificateHashes?: WebTransportHash[];\n clientId: string;\n botId: string;\n namespace: string;\n transcriptTrack: string;\n audioLatencyMs: number;\n audioBufferMaxMs: number | \"real-time\";\n audioSampleRate: number;\n}\n\nfunction applyDefaults(opts: MoqTransportOptions): ResolvedOptions {\n return {\n relayUrl: opts.relayUrl,\n serverCertificateHashes: opts.serverCertificateHashes,\n clientId: opts.clientId ?? DEFAULT_CLIENT_ID,\n botId: opts.botId ?? DEFAULT_BOT_ID,\n namespace: opts.namespace ?? DEFAULT_NAMESPACE,\n transcriptTrack: opts.transcriptTrack ?? DEFAULT_TRANSCRIPT_TRACK,\n audioLatencyMs: opts.audioLatencyMs ?? DEFAULT_AUDIO_LATENCY_MS,\n audioBufferMaxMs: opts.audioBufferMaxMs ?? DEFAULT_AUDIO_BUFFER_MAX_MS,\n audioSampleRate: opts.audioSampleRate ?? DEFAULT_AUDIO_SAMPLE_RATE,\n };\n}\n\n/**\n * Shape of the bot's `/start` response when it's running the MoQ transport\n * (``pipecat.transports.moq.transport``). The bot nests its config under\n * ``moq`` so a single `/start` endpoint can serve multiple transport types\n * — apps pass this straight to ``PipecatClient.connect()`` with no\n * transport-specific glue on the app side.\n */\ninterface MoqStartResponse {\n moq?: {\n relayUrl: string;\n certHash: string | null;\n namespace: string;\n clientId: string;\n botId: string;\n transcriptTrack: string;\n };\n}\n\nfunction isMoqStartResponse(value: object): value is MoqStartResponse {\n return \"moq\" in value;\n}\n\n/** Decode the bot's base64 SHA-256 cert hash into the ArrayBuffer shape\n * ``WebTransport.serverCertificateHashes`` expects. */\nfunction decodeCertHash(b64: string): ArrayBuffer {\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes.buffer;\n}\n\n/**\n * ``MoqTransport`` — Pipecat Client SDK transport plugin for Media-over-QUIC.\n *\n * Built on the official ``moq`` library family:\n *\n * - ``@moq/net`` for connection management (``Connection.Reload``\n * auto-reconnects on drops; races WebTransport + WebSocket).\n * - ``@moq/publish`` for mic capture and Opus encoding via\n * ``Publish.Source.Microphone`` + ``Publish.Broadcast``.\n * - ``@moq/watch`` for bot audio playback: ``Watch.Broadcast`` discovers\n * the catalog, ``Watch.Audio.Source`` picks a rendition, and\n * ``Watch.Audio.Decoder`` + ``Watch.Audio.Emitter`` drive an\n * ``AudioWorklet`` with a ring buffer (loss-tolerant, low-latency).\n * - ``@moq/json`` for the transcript tracks — the ``Stream`` mode: an\n * ordered, lossless append-log (wire-compatible with the bot's Rust\n * ``moq_json::stream``), so no RTVI event is dropped. Not the snapshot\n * mode, which collapses to the latest value.\n * - ``@moq/signals`` for reactive plumbing.\n *\n * Two transcript tracks flow in opposite directions, both named\n * ``transcript.json.z`` (or whatever the shared ``transcriptTrack`` option\n * is set to) on their respective broadcasts:\n *\n * - Bot → client: bot publishes RTVI events (``bot-output``,\n * ``bot-transcription``, ``bot-started-speaking``, ``metrics``, …);\n * client subscribes and delivers to ``PipecatClient`` via ``_onMessage``.\n * - Client → bot: ``sendMessage`` writes each RTVI message on the client's\n * own transcript track; the bot's Python transport\n * (``pipecat.transports.moq.transport``) subscribes and dispatches them\n * through ``RTVIProcessor`` the same way any other transport does. This\n * is what carries ``client-ready`` for protocol-version negotiation,\n * typed text input, function-call results, and any other client→server\n * RTVI traffic.\n */\nexport class MoqTransport extends Transport {\n public static SERVICE_NAME = \"moq-transport\";\n\n // Resolved options (defaults applied), captured at construction time\n // and overridden by `_validateConnectionParams` at connect time.\n private _moqOptions: ResolvedOptions;\n\n // Connection + reactive root.\n private _reload: Moq.Connection.Reload | null = null;\n private _signals: Effect | null = null;\n\n // Publish side (mic → bot).\n private _publishBroadcast: Publish.Broadcast | null = null;\n private _microphone: Publish.Source.Microphone | null = null;\n // RTVI JSON side-channel from client → bot, symmetric with the bot's\n // outbound `transcript` track. Published as a lossless `@moq/json`\n // append-stream (compression on); `sendMessage` appends each RTVI\n // message and the bot's `_forward_peer_transcript` on the Python side\n // drains it. The stream Producer binds to a single track with no\n // built-in fan-out, and `publishTrack` serves each subscriber its own\n // track, so we hold one Producer per subscription (`_transcriptOut`)\n // and replay the message log (`_transcriptLog`) into each — so a bot\n // that subscribes late still gets every message, in order.\n private _transcriptOut: Set<Json.Stream.Producer<RTVIMessage>> | null = null;\n private _transcriptLog: RTVIMessage[] | null = null;\n private _micEnabled = new Signal(true);\n private _micConstraints = new Signal<MediaTrackConstraints | undefined>(\n undefined,\n );\n private _preferredMicId = new Signal<string | undefined>(undefined);\n // Encoder sample rate is wired into the catalog the bot reads. We\n // expose this as a Signal so `_connect` can update it from the resolved\n // options at connect time.\n private _audioSampleRate = new Signal<number | undefined>(undefined);\n\n // Watch side (bot → playback). Buffered playback via @moq/watch:\n // Broadcast -> Audio.Source -> Decoder -> Emitter, with `Sync.latencyMax`\n // letting faster-than-real-time TTS build up instead of the player\n // skipping ahead, and `reset()` flushing the buffer on interruption.\n private _watchBroadcast: Watch.Broadcast | null = null;\n private _sync: Watch.Sync | null = null;\n private _audioSource: Watch.Audio.Source | null = null;\n private _audioDecoder: Watch.Audio.Decoder | null = null;\n private _audioEmitter: Watch.Audio.Emitter | null = null;\n\n // Synthesized `MediaStreamTrack` for the bot, so `tracks().bot.audio`\n // returns something a `<audio>` element / visualizer can consume.\n // Tapped off `Watch.Audio.Decoder.root` (an `AudioNode`) into a\n // `MediaStreamAudioDestinationNode`.\n private _botAudioTrack: MediaStreamTrack | undefined;\n\n // Transport lifecycle state. Mirrored to `_callbacks.onTransportStateChanged`.\n declare protected _state: TransportState;\n\n constructor(options: MoqTransportOptions) {\n super();\n this._moqOptions = applyDefaults(options);\n this._state = \"disconnected\";\n }\n\n // --------------------------------------------------------------------\n // Lifecycle\n // --------------------------------------------------------------------\n\n initialize(\n options: PipecatClientOptions,\n messageHandler: (ev: RTVIMessage) => void,\n ): void {\n this._options = options;\n this._callbacks = options.callbacks ?? ({} as RTVIEventCallbacks);\n this._onMessage = messageHandler;\n this.state = \"initialized\";\n }\n\n async initDevices(): Promise<void> {\n // Spin up the Microphone with permission requested so labels are\n // available for the picker before the user clicks Connect. Real\n // capture constraints (incl. sample rate) get applied in `_connect`\n // once we have the resolved options.\n if (!this._microphone) {\n this._microphone = new Publish.Source.Microphone({\n enabled: this._micEnabled,\n constraints: this._micConstraints,\n device: { preferred: this._preferredMicId },\n });\n }\n this._microphone.device.requestPermission();\n }\n\n _validateConnectionParams(connectParams?: unknown): MoqTransportOptions {\n if (connectParams === undefined || connectParams === null) {\n return this._optionsAsInput();\n }\n if (typeof connectParams !== \"object\") {\n throw new RTVIError(\"MoqTransport connect params must be an object\");\n }\n // The bot's `/start` response is `{ moq: {...} }`, not already-shaped\n // `MoqTransportOptions` — unwrap and decode it here so apps can pass\n // the raw response straight through with no transport-specific glue.\n if (isMoqStartResponse(connectParams)) {\n const { moq } = connectParams;\n if (!moq) {\n throw new RTVIError(\n \"MoqTransport connect params did not include `moq` config (server may not be running with `-t moq`)\",\n );\n }\n return {\n ...this._optionsAsInput(),\n relayUrl: moq.relayUrl,\n clientId: moq.clientId,\n botId: moq.botId,\n namespace: moq.namespace,\n transcriptTrack: moq.transcriptTrack,\n serverCertificateHashes: moq.certHash\n ? [{ algorithm: \"sha-256\", value: decodeCertHash(moq.certHash) }]\n : undefined,\n };\n }\n return { ...this._optionsAsInput(), ...(connectParams as Partial<MoqTransportOptions>) };\n }\n\n /** Return the resolved options re-shaped to the public input type\n * (used when merging with connect-time params). */\n private _optionsAsInput(): MoqTransportOptions {\n return { ...this._moqOptions };\n }\n\n async _connect(connectParams?: MoqTransportOptions): Promise<void> {\n const merged = connectParams\n ? { ...this._moqOptions, ...applyDefaults({ ...this._moqOptions, ...connectParams }) }\n : this._moqOptions;\n if (!merged.relayUrl) {\n throw new RTVIError(\"MoqTransport requires `relayUrl`\");\n }\n this._moqOptions = merged;\n\n this.state = \"connecting\";\n\n let url: URL;\n try {\n url = new URL(merged.relayUrl);\n } catch (err) {\n this.state = \"error\";\n throw new RTVIError(\n `MoqTransport invalid relayUrl ${JSON.stringify(merged.relayUrl)}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n\n // @moq/net narrows `algorithm` to the literal `\"sha-256\"`, while\n // the browser's `WebTransportHash` types it as `string`. Cast at\n // the boundary; SHA-256 is the only algorithm WebTransport accepts.\n const webtransport = merged.serverCertificateHashes\n ? {\n serverCertificateHashes: merged.serverCertificateHashes.flatMap(\n (h) =>\n h.value ? [{ algorithm: \"sha-256\" as const, value: h.value }] : [],\n ),\n }\n : undefined;\n\n // Reload auto-reconnects on disconnect; Publish.Broadcast and\n // Watch.Broadcast both react to its `established` signal.\n this._reload = new Moq.Connection.Reload({\n enabled: new Signal(true),\n url: new Signal(url),\n webtransport,\n });\n\n // One reactive root for status mirroring. Connect/Watch are\n // self-driving via their own internal effects.\n this._signals = new Effect();\n this._signals.run((eff) => {\n const status = eff.get(this._reload!.status);\n if (status === \"connected\") {\n if (this._state === \"connecting\") this.state = \"connected\";\n } else if (status === \"connecting\") {\n this.state = \"connecting\";\n } else if (status === \"disconnected\") {\n if (this._state !== \"disconnecting\" && this._state !== \"disconnected\") {\n this.state = \"disconnected\";\n }\n }\n });\n\n const ourPath = Moq.Path.from(merged.namespace, merged.clientId);\n const botPath = Moq.Path.from(merged.namespace, merged.botId);\n\n // ----------------------------------------------------------------\n // Publish — Microphone owns getUserMedia, device selection, and\n // produces a reactive `source` signal that Publish.Broadcast\n // consumes. Constraints (channelCount:1, sampleRate) and preferred\n // deviceId flow through the signals we hold a reference to, so\n // updateMic() re-routes audio without re-creating the broadcast.\n // The encoder's sampleRate Signal pins what the catalog advertises.\n // ----------------------------------------------------------------\n this._micConstraints.set({\n channelCount: { exact: 1 },\n sampleRate: { ideal: merged.audioSampleRate },\n echoCancellation: true,\n noiseSuppression: true,\n });\n this._audioSampleRate.set(merged.audioSampleRate);\n\n if (!this._microphone) {\n this._microphone = new Publish.Source.Microphone({\n enabled: this._micEnabled,\n constraints: this._micConstraints,\n device: { preferred: this._preferredMicId },\n });\n }\n\n this._publishBroadcast = new Publish.Broadcast({\n connection: this._reload.established,\n enabled: new Signal(true),\n name: new Signal(ourPath),\n audio: {\n source: this._microphone.source,\n enabled: this._micEnabled,\n sampleRate: this._audioSampleRate,\n },\n });\n\n // Client-side transcript: `sendMessage` appends each RTVI message to\n // a lossless JSON append-stream. `publishTrack` hands us a fresh track\n // per subscription (only one, in the normal single-bot flow), so we\n // spin up a `Json.Stream.Producer` per subscriber and replay the\n // message log into it — a bot that subscribes after we've already sent\n // messages still gets the full log, in order. The bot subscribes to\n // this track by its name — same convention as the bot's own transcript\n // track — so no catalog entry is needed.\n this._transcriptLog = [];\n this._transcriptOut = new Set<Json.Stream.Producer<RTVIMessage>>();\n this._publishBroadcast.publishTrack(\n merged.transcriptTrack,\n (track, effect) => {\n const producer = new Json.Stream.Producer<RTVIMessage>(track, {\n compression: true,\n });\n for (const msg of this._transcriptLog ?? []) producer.append(msg);\n this._transcriptOut?.add(producer);\n effect.cleanup(() => {\n this._transcriptOut?.delete(producer);\n producer.finish();\n });\n },\n );\n\n // Log the mic settings the browser actually granted, so we can see\n // when a UA ignores the constraint (e.g. macOS often pins 48k\n // regardless of `sampleRate.ideal`).\n this._signals.run((eff) => {\n const src = eff.get(this._microphone!.source);\n if (!src) return;\n const track = \"track\" in src ? src.track : src;\n const s = track.getSettings();\n console.log(\n `[MoqTransport] publish: requested=${merged.audioSampleRate}Hz, ` +\n `mic granted=${s.sampleRate}Hz, channels=${s.channelCount}, ` +\n `deviceId=${s.deviceId}`,\n );\n });\n\n // ----------------------------------------------------------------\n // Watch — Broadcast handles catalog discovery and rendition\n // tracking; Audio.Source picks the active audio rendition;\n // Audio.Decoder runs the WebCodecs decode loop and feeds an\n // AudioWorklet ring buffer; Audio.Emitter routes that to the\n // speakers. `Sync.latencyMax` lets faster-than-real-time TTS build\n // up a buffer instead of the player skipping ahead; `reset()`\n // (invoked on `user-started-speaking`) flushes it on interruption.\n // We also tap Decoder.root → MediaStreamAudioDestinationNode so\n // tracks().bot.audio returns a MediaStreamTrack.\n //\n // `catalogFormat: \"hang\"` is pinned because the pipecat bot publishes\n // a hang-format catalog (camelCase `sampleRate`). The auto-detector\n // would also land on \"hang\" here (no suffix on the broadcast name,\n // and hang is the DEFAULT_FORMAT) but pinning removes ambiguity if\n // a future publisher adds a suffix or a different default ships.\n // ----------------------------------------------------------------\n this._watchBroadcast = new Watch.Broadcast({\n connection: this._reload.established,\n enabled: new Signal(true),\n name: new Signal(botPath),\n catalogFormat: new Signal<Watch.CatalogFormat>(\"hang\"),\n });\n\n // Latency range: floor = interactive jitter buffer (audioLatencyMs),\n // ceiling = how much faster-than-real-time TTS the player will hold\n // before dropping (audioBufferMaxMs). A number-typed max opens the\n // buffer; \"real-time\" collapses to the floor (skip-ahead behavior).\n const sync = new Watch.Sync({\n connection: this._reload.established,\n latency: new Signal<Watch.Latency>({\n min: merged.audioLatencyMs as Moq.Time.Milli,\n max:\n merged.audioBufferMaxMs === \"real-time\"\n ? \"real-time\"\n : (merged.audioBufferMaxMs as Moq.Time.Milli),\n }),\n });\n this._sync = sync;\n this._audioSource = new Watch.Audio.Source(sync, {\n broadcast: this._watchBroadcast,\n });\n this._audioDecoder = new Watch.Audio.Decoder(this._audioSource, {\n enabled: new Signal(true),\n });\n this._audioEmitter = new Watch.Audio.Emitter(this._audioDecoder);\n\n // Bridge Decoder.root (AudioNode) → MediaStreamTrack for tracks().bot.audio.\n this._signals.run((eff) => {\n const ctx = eff.get(this._audioDecoder!.context);\n const root = eff.get(this._audioDecoder!.root);\n if (!ctx || !root) return;\n const dest = ctx.createMediaStreamDestination();\n root.connect(dest);\n this._botAudioTrack = dest.stream.getAudioTracks()[0];\n eff.cleanup(() => {\n try {\n root.disconnect(dest);\n } catch {\n // best-effort.\n }\n this._botAudioTrack?.stop();\n this._botAudioTrack = undefined;\n });\n });\n\n // Log the bot's announced audio config + the AudioContext rate we\n // ended up at. If these disagree the voice will play at the wrong\n // pitch — the catalog drives the AudioContext, so a mismatch means\n // either a parser bug or the bot is advertising a rate that doesn't\n // match its actual Opus stream.\n this._signals.run((eff) => {\n const config = eff.get(this._audioSource!.config);\n const ctx = eff.get(this._audioDecoder!.context);\n if (!config && !ctx) return;\n console.log(\n `[MoqTransport] consume: catalog codec=${config?.codec}, ` +\n `catalog sampleRate=${config?.sampleRate}Hz, ` +\n `catalog channels=${config?.numberOfChannels}, ` +\n `AudioContext rate=${ctx?.sampleRate}Hz`,\n );\n });\n\n // Transcript — a lossless JSON append-stream over a single track. We\n // re-subscribe on each (re)connect; @moq/json's stream Consumer yields\n // every appended record in order, losslessly.\n this._signals.run((eff) => {\n const conn = eff.get(this._reload!.established);\n if (!conn) return;\n const botBroadcast = conn.consume(botPath);\n const track = botBroadcast.subscribe(merged.transcriptTrack, 0);\n const consumer = new Json.Stream.Consumer<RTVIMessage>(track, {\n compression: true,\n });\n const ac = new AbortController();\n this._drainTranscript(consumer, ac.signal).catch((e) => {\n if (!ac.signal.aborted) {\n console.warn(\"MoqTransport bot-transcript loop:\", e);\n }\n });\n eff.cleanup(() => {\n ac.abort();\n try {\n track.close();\n } catch {\n // best-effort.\n }\n });\n });\n }\n\n async _disconnect(): Promise<void> {\n if (this._state === \"disconnected\") return;\n this.state = \"disconnecting\";\n try {\n this._audioEmitter?.close();\n this._audioDecoder?.close();\n this._audioSource?.close();\n this._sync?.close();\n this._watchBroadcast?.close();\n // Each producer's own effect.cleanup (registered in the `publishTrack`\n // callback above) already calls producer.finish() when this cascades\n // through the broadcast's internal effect scope — no need to also\n // finish them explicitly here.\n this._publishBroadcast?.close();\n this._microphone?.close();\n this._signals?.close();\n this._reload?.close();\n } finally {\n this._audioEmitter = null;\n this._audioDecoder = null;\n this._audioSource = null;\n this._sync = null;\n this._watchBroadcast = null;\n this._transcriptOut = null;\n this._transcriptLog = null;\n this._publishBroadcast = null;\n this._microphone = null;\n this._signals = null;\n this._reload = null;\n this.state = \"disconnected\";\n // Fire the SDK's disconnected callback. `state = \"disconnected\"`\n // above already triggers `onTransportStateChanged`, but the SDK\n // wires UI teardown (voice-ui-kit's connection-state hook, the\n // `Disconnected` event, message-dispatcher cleanup) through\n // `onDisconnected` — without this call the transport goes idle\n // but the UI stays stuck in \"connected\" and never restores its\n // \"Connect\" button. Matches the pattern used by\n // small-webrtc-transport, openai-realtime-webrtc-transport, and\n // gemini-live-websocket-transport.\n this._callbacks?.onDisconnected?.();\n }\n }\n\n sendReadyMessage(): void {\n // Flip transport state to `ready` and dispatch the RTVI `client-ready`\n // message on the client→bot transcript track so the bot's RTVIProcessor\n // can negotiate protocol version. Mirrors the pattern used by\n // small-webrtc-transport and daily-transport.\n this.state = \"ready\";\n this.sendMessage(RTVIMessage.clientReady());\n }\n\n // --------------------------------------------------------------------\n // State\n // --------------------------------------------------------------------\n\n get state(): TransportState {\n return this._state;\n }\n\n set state(next: TransportState) {\n if (this._state === next) return;\n this._state = next;\n this._callbacks?.onTransportStateChanged?.(next);\n }\n\n // --------------------------------------------------------------------\n // Devices\n // --------------------------------------------------------------------\n\n async getAllMics(): Promise<MediaDeviceInfo[]> {\n return this._microphone?.device.available.peek() ?? [];\n }\n\n async getAllCams(): Promise<MediaDeviceInfo[]> {\n return [];\n }\n\n async getAllSpeakers(): Promise<MediaDeviceInfo[]> {\n return [];\n }\n\n updateMic(micId: string): void {\n this._preferredMicId.set(micId);\n }\n\n updateCam(_camId: string): void {}\n\n updateSpeaker(_speakerId: string): void {}\n\n get selectedMic(): MediaDeviceInfo | Record<string, never> {\n const id = this._microphone?.device.active.peek();\n if (!id) return {};\n return (\n this._microphone?.device.available.peek()?.find((d) => d.deviceId === id) ??\n {}\n );\n }\n\n get selectedCam(): MediaDeviceInfo | Record<string, never> {\n return {};\n }\n\n get selectedSpeaker(): MediaDeviceInfo | Record<string, never> {\n return {};\n }\n\n enableMic(enable: boolean): void {\n this._micEnabled.set(enable);\n }\n\n enableCam(_enable: boolean): void {}\n\n enableScreenShare(_enable: boolean): void {}\n\n get isCamEnabled(): boolean {\n return false;\n }\n\n get isMicEnabled(): boolean {\n return this._micEnabled.get();\n }\n\n get isSharingScreen(): boolean {\n return false;\n }\n\n // --------------------------------------------------------------------\n // Messaging + tracks\n // --------------------------------------------------------------------\n\n sendMessage(message: RTVIMessage): void {\n if (!this._transcriptOut || !this._transcriptLog) {\n console.warn(\n \"[MoqTransport] sendMessage called before connect; dropping\",\n message,\n );\n return;\n }\n // Append to the lossless stream. Keep a log too, so a bot that\n // subscribes after this point is replayed every message in order\n // (see the `publishTrack` serve callback above).\n this._transcriptLog.push(message);\n for (const producer of this._transcriptOut) producer.append(message);\n }\n\n tracks(): Tracks {\n const localSource = this._microphone?.source.peek();\n const localAudio =\n localSource && \"track\" in localSource ? localSource.track : localSource;\n return {\n local: localAudio ? { audio: localAudio } : {},\n bot: this._botAudioTrack ? { audio: this._botAudioTrack } : {},\n };\n }\n\n // --------------------------------------------------------------------\n // Internals — transcript\n // --------------------------------------------------------------------\n\n /** Pull RTVI messages off the @moq/json stream consumer and hand each\n * one to `PipecatClient` via `_onMessage`. The handler resolves the\n * SDK's connect promise when bot-ready arrives. */\n private async _drainTranscript(\n consumer: Json.Stream.Consumer<RTVIMessage>,\n signal: AbortSignal,\n ): Promise<void> {\n for (;;) {\n const message = await consumer.next();\n if (!message || signal.aborted) break;\n if (typeof message !== \"object\") continue;\n // Intra-transport shutdown signal from the bot: bot is about to\n // close its MoQ session. Disable auto-reconnect, tear down the\n // audio decoder + subscribers on our side while WebTransport is\n // still healthy — without this the connection just vanishes\n // underneath Chrome's WebCodecs decoder and can crash the\n // renderer. Not an RTVI event; don't forward it to `_onMessage`.\n const label = (message as { label?: string }).label;\n const type = (message as { type?: string }).type;\n if (label === \"moq-transport\" && type === \"session-ending\") {\n console.log(\"[MoqTransport] bot signaled session-ending — closing cleanly\");\n // Fire-and-forget; we're returning from the drain loop right\n // after, and _disconnect() aborts the consumer that owns us.\n void this._disconnect();\n return;\n }\n // Interruption: the user started talking, so flush the\n // already-buffered TTS for the bot's previous utterance instead\n // of letting it drain. With the bot writing faster than\n // real-time, that buffer can be seconds long.\n if (type === \"user-started-speaking\") {\n this._resetBotAudio();\n }\n this._onMessage?.(message);\n }\n }\n\n /** Flush buffered bot audio and re-anchor playback at an utterance\n * boundary. Called on interruption (`user-started-speaking`) so the\n * already-buffered TTS for the previous utterance stops immediately\n * instead of draining: re-anchor the sync reference and flush the\n * decoder's ring buffer. */\n private _resetBotAudio(): void {\n this._sync?.reset();\n this._audioDecoder?.reset();\n }\n}\n"],"names":[],"version":3,"file":"index.js.map"}
@@ -0,0 +1,557 @@
1
+ import {Stream as $3Y32T$Stream} from "@moq/json";
2
+ import {Connection as $3Y32T$Connection, Path as $3Y32T$Path} from "@moq/net";
3
+ import {Source as $3Y32T$Source, Broadcast as $3Y32T$Broadcast} from "@moq/publish";
4
+ import {Signal as $3Y32T$Signal, Effect as $3Y32T$Effect} from "@moq/signals";
5
+ import {Broadcast as $3Y32T$Broadcast1, Sync as $3Y32T$Sync, Audio as $3Y32T$Audio} from "@moq/watch";
6
+ import {RTVIError as $3Y32T$RTVIError, RTVIMessage as $3Y32T$RTVIMessage, Transport as $3Y32T$Transport} from "@pipecat-ai/client-js";
7
+
8
+
9
+ function $parcel$export(e, n, v, s) {
10
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
11
+ }
12
+ var $549dd95e7ad3e81f$exports = {};
13
+
14
+ $parcel$export($549dd95e7ad3e81f$exports, "MoqTransport", () => $549dd95e7ad3e81f$export$739cde4b077543d1);
15
+ /*
16
+ * Copyright (c) 2024-2026, Daily
17
+ *
18
+ * SPDX-License-Identifier: BSD-2-Clause
19
+ */
20
+
21
+
22
+
23
+
24
+
25
+ const $549dd95e7ad3e81f$var$DEFAULT_NAMESPACE = "pipecat";
26
+ const $549dd95e7ad3e81f$var$DEFAULT_CLIENT_ID = "client0";
27
+ const $549dd95e7ad3e81f$var$DEFAULT_BOT_ID = "bot0";
28
+ const $549dd95e7ad3e81f$var$DEFAULT_TRANSCRIPT_TRACK = "transcript.json.z";
29
+ // Bounded jitter buffer on the audio decoder. Lower = more interactive
30
+ // but more drops on bad networks. Matches the bot's audio_in_max_latency_ms
31
+ // in spirit (each side enforces its own deadline).
32
+ const $549dd95e7ad3e81f$var$DEFAULT_AUDIO_LATENCY_MS = 80;
33
+ // Latency ceiling for buffered TTS playback (ms). A finite cap (vs
34
+ // uncapped) because the player's group/jitter buffer needs a concrete
35
+ // maximum span to retain before dropping. The bot paces its writes a
36
+ // little under this (~25s, see the Python transport's
37
+ // `audio_out_max_buffer_ms`) so the producer self-limits below this drop
38
+ // ceiling and the player never actually has to drop.
39
+ const $549dd95e7ad3e81f$var$DEFAULT_AUDIO_BUFFER_MAX_MS = 30000;
40
+ // Sample rate we pin on the publish-side Opus encoder. Opus only supports
41
+ // {8, 12, 16, 24, 48} kHz; 48 kHz is the upstream default and what most
42
+ // browsers report as the mic's native rate. The bot reads this from our
43
+ // catalog and resamples to its `audio_in_sample_rate` after decode, so we
44
+ // don't need to match it exactly — but pinning it keeps the catalog
45
+ // unambiguous.
46
+ const $549dd95e7ad3e81f$var$DEFAULT_AUDIO_SAMPLE_RATE = 48000;
47
+ function $549dd95e7ad3e81f$var$applyDefaults(opts) {
48
+ return {
49
+ relayUrl: opts.relayUrl,
50
+ serverCertificateHashes: opts.serverCertificateHashes,
51
+ clientId: opts.clientId ?? $549dd95e7ad3e81f$var$DEFAULT_CLIENT_ID,
52
+ botId: opts.botId ?? $549dd95e7ad3e81f$var$DEFAULT_BOT_ID,
53
+ namespace: opts.namespace ?? $549dd95e7ad3e81f$var$DEFAULT_NAMESPACE,
54
+ transcriptTrack: opts.transcriptTrack ?? $549dd95e7ad3e81f$var$DEFAULT_TRANSCRIPT_TRACK,
55
+ audioLatencyMs: opts.audioLatencyMs ?? $549dd95e7ad3e81f$var$DEFAULT_AUDIO_LATENCY_MS,
56
+ audioBufferMaxMs: opts.audioBufferMaxMs ?? $549dd95e7ad3e81f$var$DEFAULT_AUDIO_BUFFER_MAX_MS,
57
+ audioSampleRate: opts.audioSampleRate ?? $549dd95e7ad3e81f$var$DEFAULT_AUDIO_SAMPLE_RATE
58
+ };
59
+ }
60
+ function $549dd95e7ad3e81f$var$isMoqStartResponse(value) {
61
+ return "moq" in value;
62
+ }
63
+ /** Decode the bot's base64 SHA-256 cert hash into the ArrayBuffer shape
64
+ * ``WebTransport.serverCertificateHashes`` expects. */ function $549dd95e7ad3e81f$var$decodeCertHash(b64) {
65
+ const bin = atob(b64);
66
+ const bytes = new Uint8Array(bin.length);
67
+ for(let i = 0; i < bin.length; i++)bytes[i] = bin.charCodeAt(i);
68
+ return bytes.buffer;
69
+ }
70
+ class $549dd95e7ad3e81f$export$739cde4b077543d1 extends (0, $3Y32T$Transport) {
71
+ constructor(options){
72
+ super();
73
+ // Connection + reactive root.
74
+ this._reload = null;
75
+ this._signals = null;
76
+ // Publish side (mic → bot).
77
+ this._publishBroadcast = null;
78
+ this._microphone = null;
79
+ // RTVI JSON side-channel from client → bot, symmetric with the bot's
80
+ // outbound `transcript` track. Published as a lossless `@moq/json`
81
+ // append-stream (compression on); `sendMessage` appends each RTVI
82
+ // message and the bot's `_forward_peer_transcript` on the Python side
83
+ // drains it. The stream Producer binds to a single track with no
84
+ // built-in fan-out, and `publishTrack` serves each subscriber its own
85
+ // track, so we hold one Producer per subscription (`_transcriptOut`)
86
+ // and replay the message log (`_transcriptLog`) into each — so a bot
87
+ // that subscribes late still gets every message, in order.
88
+ this._transcriptOut = null;
89
+ this._transcriptLog = null;
90
+ this._micEnabled = new (0, $3Y32T$Signal)(true);
91
+ this._micConstraints = new (0, $3Y32T$Signal)(undefined);
92
+ this._preferredMicId = new (0, $3Y32T$Signal)(undefined);
93
+ // Encoder sample rate is wired into the catalog the bot reads. We
94
+ // expose this as a Signal so `_connect` can update it from the resolved
95
+ // options at connect time.
96
+ this._audioSampleRate = new (0, $3Y32T$Signal)(undefined);
97
+ // Watch side (bot → playback). Buffered playback via @moq/watch:
98
+ // Broadcast -> Audio.Source -> Decoder -> Emitter, with `Sync.latencyMax`
99
+ // letting faster-than-real-time TTS build up instead of the player
100
+ // skipping ahead, and `reset()` flushing the buffer on interruption.
101
+ this._watchBroadcast = null;
102
+ this._sync = null;
103
+ this._audioSource = null;
104
+ this._audioDecoder = null;
105
+ this._audioEmitter = null;
106
+ this._moqOptions = $549dd95e7ad3e81f$var$applyDefaults(options);
107
+ this._state = "disconnected";
108
+ }
109
+ // --------------------------------------------------------------------
110
+ // Lifecycle
111
+ // --------------------------------------------------------------------
112
+ initialize(options, messageHandler) {
113
+ this._options = options;
114
+ this._callbacks = options.callbacks ?? {};
115
+ this._onMessage = messageHandler;
116
+ this.state = "initialized";
117
+ }
118
+ async initDevices() {
119
+ // Spin up the Microphone with permission requested so labels are
120
+ // available for the picker before the user clicks Connect. Real
121
+ // capture constraints (incl. sample rate) get applied in `_connect`
122
+ // once we have the resolved options.
123
+ if (!this._microphone) this._microphone = new $3Y32T$Source.Microphone({
124
+ enabled: this._micEnabled,
125
+ constraints: this._micConstraints,
126
+ device: {
127
+ preferred: this._preferredMicId
128
+ }
129
+ });
130
+ this._microphone.device.requestPermission();
131
+ }
132
+ _validateConnectionParams(connectParams) {
133
+ if (connectParams === undefined || connectParams === null) return this._optionsAsInput();
134
+ if (typeof connectParams !== "object") throw new (0, $3Y32T$RTVIError)("MoqTransport connect params must be an object");
135
+ // The bot's `/start` response is `{ moq: {...} }`, not already-shaped
136
+ // `MoqTransportOptions` — unwrap and decode it here so apps can pass
137
+ // the raw response straight through with no transport-specific glue.
138
+ if ($549dd95e7ad3e81f$var$isMoqStartResponse(connectParams)) {
139
+ const { moq: moq } = connectParams;
140
+ if (!moq) throw new (0, $3Y32T$RTVIError)("MoqTransport connect params did not include `moq` config (server may not be running with `-t moq`)");
141
+ return {
142
+ ...this._optionsAsInput(),
143
+ relayUrl: moq.relayUrl,
144
+ clientId: moq.clientId,
145
+ botId: moq.botId,
146
+ namespace: moq.namespace,
147
+ transcriptTrack: moq.transcriptTrack,
148
+ serverCertificateHashes: moq.certHash ? [
149
+ {
150
+ algorithm: "sha-256",
151
+ value: $549dd95e7ad3e81f$var$decodeCertHash(moq.certHash)
152
+ }
153
+ ] : undefined
154
+ };
155
+ }
156
+ return {
157
+ ...this._optionsAsInput(),
158
+ ...connectParams
159
+ };
160
+ }
161
+ /** Return the resolved options re-shaped to the public input type
162
+ * (used when merging with connect-time params). */ _optionsAsInput() {
163
+ return {
164
+ ...this._moqOptions
165
+ };
166
+ }
167
+ async _connect(connectParams) {
168
+ const merged = connectParams ? {
169
+ ...this._moqOptions,
170
+ ...$549dd95e7ad3e81f$var$applyDefaults({
171
+ ...this._moqOptions,
172
+ ...connectParams
173
+ })
174
+ } : this._moqOptions;
175
+ if (!merged.relayUrl) throw new (0, $3Y32T$RTVIError)("MoqTransport requires `relayUrl`");
176
+ this._moqOptions = merged;
177
+ this.state = "connecting";
178
+ let url;
179
+ try {
180
+ url = new URL(merged.relayUrl);
181
+ } catch (err) {
182
+ this.state = "error";
183
+ throw new (0, $3Y32T$RTVIError)(`MoqTransport invalid relayUrl ${JSON.stringify(merged.relayUrl)}: ${err instanceof Error ? err.message : String(err)}`);
184
+ }
185
+ // @moq/net narrows `algorithm` to the literal `"sha-256"`, while
186
+ // the browser's `WebTransportHash` types it as `string`. Cast at
187
+ // the boundary; SHA-256 is the only algorithm WebTransport accepts.
188
+ const webtransport = merged.serverCertificateHashes ? {
189
+ serverCertificateHashes: merged.serverCertificateHashes.flatMap((h)=>h.value ? [
190
+ {
191
+ algorithm: "sha-256",
192
+ value: h.value
193
+ }
194
+ ] : [])
195
+ } : undefined;
196
+ // Reload auto-reconnects on disconnect; Publish.Broadcast and
197
+ // Watch.Broadcast both react to its `established` signal.
198
+ this._reload = new $3Y32T$Connection.Reload({
199
+ enabled: new (0, $3Y32T$Signal)(true),
200
+ url: new (0, $3Y32T$Signal)(url),
201
+ webtransport: webtransport
202
+ });
203
+ // One reactive root for status mirroring. Connect/Watch are
204
+ // self-driving via their own internal effects.
205
+ this._signals = new (0, $3Y32T$Effect)();
206
+ this._signals.run((eff)=>{
207
+ const status = eff.get(this._reload.status);
208
+ if (status === "connected") {
209
+ if (this._state === "connecting") this.state = "connected";
210
+ } else if (status === "connecting") this.state = "connecting";
211
+ else if (status === "disconnected") {
212
+ if (this._state !== "disconnecting" && this._state !== "disconnected") this.state = "disconnected";
213
+ }
214
+ });
215
+ const ourPath = $3Y32T$Path.from(merged.namespace, merged.clientId);
216
+ const botPath = $3Y32T$Path.from(merged.namespace, merged.botId);
217
+ // ----------------------------------------------------------------
218
+ // Publish — Microphone owns getUserMedia, device selection, and
219
+ // produces a reactive `source` signal that Publish.Broadcast
220
+ // consumes. Constraints (channelCount:1, sampleRate) and preferred
221
+ // deviceId flow through the signals we hold a reference to, so
222
+ // updateMic() re-routes audio without re-creating the broadcast.
223
+ // The encoder's sampleRate Signal pins what the catalog advertises.
224
+ // ----------------------------------------------------------------
225
+ this._micConstraints.set({
226
+ channelCount: {
227
+ exact: 1
228
+ },
229
+ sampleRate: {
230
+ ideal: merged.audioSampleRate
231
+ },
232
+ echoCancellation: true,
233
+ noiseSuppression: true
234
+ });
235
+ this._audioSampleRate.set(merged.audioSampleRate);
236
+ if (!this._microphone) this._microphone = new $3Y32T$Source.Microphone({
237
+ enabled: this._micEnabled,
238
+ constraints: this._micConstraints,
239
+ device: {
240
+ preferred: this._preferredMicId
241
+ }
242
+ });
243
+ this._publishBroadcast = new $3Y32T$Broadcast({
244
+ connection: this._reload.established,
245
+ enabled: new (0, $3Y32T$Signal)(true),
246
+ name: new (0, $3Y32T$Signal)(ourPath),
247
+ audio: {
248
+ source: this._microphone.source,
249
+ enabled: this._micEnabled,
250
+ sampleRate: this._audioSampleRate
251
+ }
252
+ });
253
+ // Client-side transcript: `sendMessage` appends each RTVI message to
254
+ // a lossless JSON append-stream. `publishTrack` hands us a fresh track
255
+ // per subscription (only one, in the normal single-bot flow), so we
256
+ // spin up a `Json.Stream.Producer` per subscriber and replay the
257
+ // message log into it — a bot that subscribes after we've already sent
258
+ // messages still gets the full log, in order. The bot subscribes to
259
+ // this track by its name — same convention as the bot's own transcript
260
+ // track — so no catalog entry is needed.
261
+ this._transcriptLog = [];
262
+ this._transcriptOut = new Set();
263
+ this._publishBroadcast.publishTrack(merged.transcriptTrack, (track, effect)=>{
264
+ const producer = new $3Y32T$Stream.Producer(track, {
265
+ compression: true
266
+ });
267
+ for (const msg of this._transcriptLog ?? [])producer.append(msg);
268
+ this._transcriptOut?.add(producer);
269
+ effect.cleanup(()=>{
270
+ this._transcriptOut?.delete(producer);
271
+ producer.finish();
272
+ });
273
+ });
274
+ // Log the mic settings the browser actually granted, so we can see
275
+ // when a UA ignores the constraint (e.g. macOS often pins 48k
276
+ // regardless of `sampleRate.ideal`).
277
+ this._signals.run((eff)=>{
278
+ const src = eff.get(this._microphone.source);
279
+ if (!src) return;
280
+ const track = "track" in src ? src.track : src;
281
+ const s = track.getSettings();
282
+ console.log(`[MoqTransport] publish: requested=${merged.audioSampleRate}Hz, ` + `mic granted=${s.sampleRate}Hz, channels=${s.channelCount}, ` + `deviceId=${s.deviceId}`);
283
+ });
284
+ // ----------------------------------------------------------------
285
+ // Watch — Broadcast handles catalog discovery and rendition
286
+ // tracking; Audio.Source picks the active audio rendition;
287
+ // Audio.Decoder runs the WebCodecs decode loop and feeds an
288
+ // AudioWorklet ring buffer; Audio.Emitter routes that to the
289
+ // speakers. `Sync.latencyMax` lets faster-than-real-time TTS build
290
+ // up a buffer instead of the player skipping ahead; `reset()`
291
+ // (invoked on `user-started-speaking`) flushes it on interruption.
292
+ // We also tap Decoder.root → MediaStreamAudioDestinationNode so
293
+ // tracks().bot.audio returns a MediaStreamTrack.
294
+ //
295
+ // `catalogFormat: "hang"` is pinned because the pipecat bot publishes
296
+ // a hang-format catalog (camelCase `sampleRate`). The auto-detector
297
+ // would also land on "hang" here (no suffix on the broadcast name,
298
+ // and hang is the DEFAULT_FORMAT) but pinning removes ambiguity if
299
+ // a future publisher adds a suffix or a different default ships.
300
+ // ----------------------------------------------------------------
301
+ this._watchBroadcast = new $3Y32T$Broadcast1({
302
+ connection: this._reload.established,
303
+ enabled: new (0, $3Y32T$Signal)(true),
304
+ name: new (0, $3Y32T$Signal)(botPath),
305
+ catalogFormat: new (0, $3Y32T$Signal)("hang")
306
+ });
307
+ // Latency range: floor = interactive jitter buffer (audioLatencyMs),
308
+ // ceiling = how much faster-than-real-time TTS the player will hold
309
+ // before dropping (audioBufferMaxMs). A number-typed max opens the
310
+ // buffer; "real-time" collapses to the floor (skip-ahead behavior).
311
+ const sync = new $3Y32T$Sync({
312
+ connection: this._reload.established,
313
+ latency: new (0, $3Y32T$Signal)({
314
+ min: merged.audioLatencyMs,
315
+ max: merged.audioBufferMaxMs === "real-time" ? "real-time" : merged.audioBufferMaxMs
316
+ })
317
+ });
318
+ this._sync = sync;
319
+ this._audioSource = new $3Y32T$Audio.Source(sync, {
320
+ broadcast: this._watchBroadcast
321
+ });
322
+ this._audioDecoder = new $3Y32T$Audio.Decoder(this._audioSource, {
323
+ enabled: new (0, $3Y32T$Signal)(true)
324
+ });
325
+ this._audioEmitter = new $3Y32T$Audio.Emitter(this._audioDecoder);
326
+ // Bridge Decoder.root (AudioNode) → MediaStreamTrack for tracks().bot.audio.
327
+ this._signals.run((eff)=>{
328
+ const ctx = eff.get(this._audioDecoder.context);
329
+ const root = eff.get(this._audioDecoder.root);
330
+ if (!ctx || !root) return;
331
+ const dest = ctx.createMediaStreamDestination();
332
+ root.connect(dest);
333
+ this._botAudioTrack = dest.stream.getAudioTracks()[0];
334
+ eff.cleanup(()=>{
335
+ try {
336
+ root.disconnect(dest);
337
+ } catch {
338
+ // best-effort.
339
+ }
340
+ this._botAudioTrack?.stop();
341
+ this._botAudioTrack = undefined;
342
+ });
343
+ });
344
+ // Log the bot's announced audio config + the AudioContext rate we
345
+ // ended up at. If these disagree the voice will play at the wrong
346
+ // pitch — the catalog drives the AudioContext, so a mismatch means
347
+ // either a parser bug or the bot is advertising a rate that doesn't
348
+ // match its actual Opus stream.
349
+ this._signals.run((eff)=>{
350
+ const config = eff.get(this._audioSource.config);
351
+ const ctx = eff.get(this._audioDecoder.context);
352
+ if (!config && !ctx) return;
353
+ console.log(`[MoqTransport] consume: catalog codec=${config?.codec}, ` + `catalog sampleRate=${config?.sampleRate}Hz, ` + `catalog channels=${config?.numberOfChannels}, ` + `AudioContext rate=${ctx?.sampleRate}Hz`);
354
+ });
355
+ // Transcript — a lossless JSON append-stream over a single track. We
356
+ // re-subscribe on each (re)connect; @moq/json's stream Consumer yields
357
+ // every appended record in order, losslessly.
358
+ this._signals.run((eff)=>{
359
+ const conn = eff.get(this._reload.established);
360
+ if (!conn) return;
361
+ const botBroadcast = conn.consume(botPath);
362
+ const track = botBroadcast.subscribe(merged.transcriptTrack, 0);
363
+ const consumer = new $3Y32T$Stream.Consumer(track, {
364
+ compression: true
365
+ });
366
+ const ac = new AbortController();
367
+ this._drainTranscript(consumer, ac.signal).catch((e)=>{
368
+ if (!ac.signal.aborted) console.warn("MoqTransport bot-transcript loop:", e);
369
+ });
370
+ eff.cleanup(()=>{
371
+ ac.abort();
372
+ try {
373
+ track.close();
374
+ } catch {
375
+ // best-effort.
376
+ }
377
+ });
378
+ });
379
+ }
380
+ async _disconnect() {
381
+ if (this._state === "disconnected") return;
382
+ this.state = "disconnecting";
383
+ try {
384
+ this._audioEmitter?.close();
385
+ this._audioDecoder?.close();
386
+ this._audioSource?.close();
387
+ this._sync?.close();
388
+ this._watchBroadcast?.close();
389
+ // Each producer's own effect.cleanup (registered in the `publishTrack`
390
+ // callback above) already calls producer.finish() when this cascades
391
+ // through the broadcast's internal effect scope — no need to also
392
+ // finish them explicitly here.
393
+ this._publishBroadcast?.close();
394
+ this._microphone?.close();
395
+ this._signals?.close();
396
+ this._reload?.close();
397
+ } finally{
398
+ this._audioEmitter = null;
399
+ this._audioDecoder = null;
400
+ this._audioSource = null;
401
+ this._sync = null;
402
+ this._watchBroadcast = null;
403
+ this._transcriptOut = null;
404
+ this._transcriptLog = null;
405
+ this._publishBroadcast = null;
406
+ this._microphone = null;
407
+ this._signals = null;
408
+ this._reload = null;
409
+ this.state = "disconnected";
410
+ // Fire the SDK's disconnected callback. `state = "disconnected"`
411
+ // above already triggers `onTransportStateChanged`, but the SDK
412
+ // wires UI teardown (voice-ui-kit's connection-state hook, the
413
+ // `Disconnected` event, message-dispatcher cleanup) through
414
+ // `onDisconnected` — without this call the transport goes idle
415
+ // but the UI stays stuck in "connected" and never restores its
416
+ // "Connect" button. Matches the pattern used by
417
+ // small-webrtc-transport, openai-realtime-webrtc-transport, and
418
+ // gemini-live-websocket-transport.
419
+ this._callbacks?.onDisconnected?.();
420
+ }
421
+ }
422
+ sendReadyMessage() {
423
+ // Flip transport state to `ready` and dispatch the RTVI `client-ready`
424
+ // message on the client→bot transcript track so the bot's RTVIProcessor
425
+ // can negotiate protocol version. Mirrors the pattern used by
426
+ // small-webrtc-transport and daily-transport.
427
+ this.state = "ready";
428
+ this.sendMessage((0, $3Y32T$RTVIMessage).clientReady());
429
+ }
430
+ // --------------------------------------------------------------------
431
+ // State
432
+ // --------------------------------------------------------------------
433
+ get state() {
434
+ return this._state;
435
+ }
436
+ set state(next) {
437
+ if (this._state === next) return;
438
+ this._state = next;
439
+ this._callbacks?.onTransportStateChanged?.(next);
440
+ }
441
+ // --------------------------------------------------------------------
442
+ // Devices
443
+ // --------------------------------------------------------------------
444
+ async getAllMics() {
445
+ return this._microphone?.device.available.peek() ?? [];
446
+ }
447
+ async getAllCams() {
448
+ return [];
449
+ }
450
+ async getAllSpeakers() {
451
+ return [];
452
+ }
453
+ updateMic(micId) {
454
+ this._preferredMicId.set(micId);
455
+ }
456
+ updateCam(_camId) {}
457
+ updateSpeaker(_speakerId) {}
458
+ get selectedMic() {
459
+ const id = this._microphone?.device.active.peek();
460
+ if (!id) return {};
461
+ return this._microphone?.device.available.peek()?.find((d)=>d.deviceId === id) ?? {};
462
+ }
463
+ get selectedCam() {
464
+ return {};
465
+ }
466
+ get selectedSpeaker() {
467
+ return {};
468
+ }
469
+ enableMic(enable) {
470
+ this._micEnabled.set(enable);
471
+ }
472
+ enableCam(_enable) {}
473
+ enableScreenShare(_enable) {}
474
+ get isCamEnabled() {
475
+ return false;
476
+ }
477
+ get isMicEnabled() {
478
+ return this._micEnabled.get();
479
+ }
480
+ get isSharingScreen() {
481
+ return false;
482
+ }
483
+ // --------------------------------------------------------------------
484
+ // Messaging + tracks
485
+ // --------------------------------------------------------------------
486
+ sendMessage(message) {
487
+ if (!this._transcriptOut || !this._transcriptLog) {
488
+ console.warn("[MoqTransport] sendMessage called before connect; dropping", message);
489
+ return;
490
+ }
491
+ // Append to the lossless stream. Keep a log too, so a bot that
492
+ // subscribes after this point is replayed every message in order
493
+ // (see the `publishTrack` serve callback above).
494
+ this._transcriptLog.push(message);
495
+ for (const producer of this._transcriptOut)producer.append(message);
496
+ }
497
+ tracks() {
498
+ const localSource = this._microphone?.source.peek();
499
+ const localAudio = localSource && "track" in localSource ? localSource.track : localSource;
500
+ return {
501
+ local: localAudio ? {
502
+ audio: localAudio
503
+ } : {},
504
+ bot: this._botAudioTrack ? {
505
+ audio: this._botAudioTrack
506
+ } : {}
507
+ };
508
+ }
509
+ // --------------------------------------------------------------------
510
+ // Internals — transcript
511
+ // --------------------------------------------------------------------
512
+ /** Pull RTVI messages off the @moq/json stream consumer and hand each
513
+ * one to `PipecatClient` via `_onMessage`. The handler resolves the
514
+ * SDK's connect promise when bot-ready arrives. */ async _drainTranscript(consumer, signal) {
515
+ for(;;){
516
+ const message = await consumer.next();
517
+ if (!message || signal.aborted) break;
518
+ if (typeof message !== "object") continue;
519
+ // Intra-transport shutdown signal from the bot: bot is about to
520
+ // close its MoQ session. Disable auto-reconnect, tear down the
521
+ // audio decoder + subscribers on our side while WebTransport is
522
+ // still healthy — without this the connection just vanishes
523
+ // underneath Chrome's WebCodecs decoder and can crash the
524
+ // renderer. Not an RTVI event; don't forward it to `_onMessage`.
525
+ const label = message.label;
526
+ const type = message.type;
527
+ if (label === "moq-transport" && type === "session-ending") {
528
+ console.log("[MoqTransport] bot signaled session-ending \u2014 closing cleanly");
529
+ // Fire-and-forget; we're returning from the drain loop right
530
+ // after, and _disconnect() aborts the consumer that owns us.
531
+ this._disconnect();
532
+ return;
533
+ }
534
+ // Interruption: the user started talking, so flush the
535
+ // already-buffered TTS for the bot's previous utterance instead
536
+ // of letting it drain. With the bot writing faster than
537
+ // real-time, that buffer can be seconds long.
538
+ if (type === "user-started-speaking") this._resetBotAudio();
539
+ this._onMessage?.(message);
540
+ }
541
+ }
542
+ /** Flush buffered bot audio and re-anchor playback at an utterance
543
+ * boundary. Called on interruption (`user-started-speaking`) so the
544
+ * already-buffered TTS for the previous utterance stops immediately
545
+ * instead of draining: re-anchor the sync reference and flush the
546
+ * decoder's ring buffer. */ _resetBotAudio() {
547
+ this._sync?.reset();
548
+ this._audioDecoder?.reset();
549
+ }
550
+ }
551
+ $549dd95e7ad3e81f$export$739cde4b077543d1.SERVICE_NAME = "moq-transport";
552
+
553
+
554
+
555
+
556
+ export {$549dd95e7ad3e81f$export$739cde4b077543d1 as MoqTransport};
557
+ //# sourceMappingURL=index.module.js.map