@burdenoff/website-sdk 2026.1104.3 → 2026.1105.1

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.
Files changed (48) hide show
  1. package/README.md +51 -6
  2. package/dist/avatar-live-view/index.d.mts +1 -1
  3. package/dist/avatar-live-view/index.d.ts +1 -1
  4. package/dist/avatar-live-view/index.js +482 -64
  5. package/dist/avatar-live-view/index.js.map +1 -1
  6. package/dist/avatar-live-view/index.mjs +476 -58
  7. package/dist/avatar-live-view/index.mjs.map +1 -1
  8. package/dist/avatar-mic/index.d.mts +193 -0
  9. package/dist/avatar-mic/index.d.ts +193 -0
  10. package/dist/avatar-mic/index.js +738 -0
  11. package/dist/avatar-mic/index.js.map +1 -0
  12. package/dist/avatar-mic/index.mjs +736 -0
  13. package/dist/avatar-mic/index.mjs.map +1 -0
  14. package/dist/{chunk-5JQGE3XI.mjs → chunk-7HE33NBS.mjs} +4 -111
  15. package/dist/chunk-7HE33NBS.mjs.map +1 -0
  16. package/dist/chunk-JZS6FQT6.mjs +127 -0
  17. package/dist/chunk-JZS6FQT6.mjs.map +1 -0
  18. package/dist/{chunk-22OT6E26.js → chunk-MBVXDFVC.js} +18 -127
  19. package/dist/chunk-MBVXDFVC.js.map +1 -0
  20. package/dist/{chunk-7MSB3EUI.mjs → chunk-MVNN6LLO.mjs} +4 -3
  21. package/dist/chunk-MVNN6LLO.mjs.map +1 -0
  22. package/dist/chunk-N7E6Z2GF.js +132 -0
  23. package/dist/chunk-N7E6Z2GF.js.map +1 -0
  24. package/dist/{chunk-WSEQ2I6I.js → chunk-XIZE37IB.js} +4 -3
  25. package/dist/chunk-XIZE37IB.js.map +1 -0
  26. package/dist/components/explore.d.mts +1 -1
  27. package/dist/components/explore.d.ts +1 -1
  28. package/dist/components/explore.js +20 -20
  29. package/dist/components/explore.mjs +2 -2
  30. package/dist/{explore-BV4dRwoC.d.ts → explore-Dfru4j4m.d.ts} +45 -31
  31. package/dist/{explore-avatar-live-config-DRpS-mqQ.d.ts → explore-avatar-live-config-C-OwXNje.d.ts} +8 -0
  32. package/dist/{explore-avatar-live-config-B6_p0q1c.d.mts → explore-avatar-live-config-OO0hvQCK.d.mts} +8 -0
  33. package/dist/{explore-BgEeGPvY.d.mts → explore-ei8uoko9.d.mts} +45 -31
  34. package/dist/index.d.mts +11 -6
  35. package/dist/index.d.ts +11 -6
  36. package/dist/index.js +70 -68
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.mjs +9 -6
  39. package/dist/index.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/chunk-22OT6E26.js.map +0 -1
  42. package/dist/chunk-5JQGE3XI.mjs.map +0 -1
  43. package/dist/chunk-7MSB3EUI.mjs.map +0 -1
  44. package/dist/chunk-O4GS3XPZ.js +0 -10
  45. package/dist/chunk-O4GS3XPZ.js.map +0 -1
  46. package/dist/chunk-TT3MHN42.mjs +0 -8
  47. package/dist/chunk-TT3MHN42.mjs.map +0 -1
  48. package/dist/chunk-WSEQ2I6I.js.map +0 -1
@@ -0,0 +1,193 @@
1
+ /** What the worklet posts for each frame. */
2
+ interface CaptureFrameMessage {
3
+ /** s16le mono 16 kHz, `CAPTURE_FRAME_SAMPLES` samples. */
4
+ pcm: ArrayBuffer;
5
+ /** Microphone level over the frame, dBFS. */
6
+ rmsDb: number;
7
+ /** Reference-bus level over the same samples, dBFS (about -90 when silent, as it always is here). */
8
+ refDb: number;
9
+ /** Zero crossings per sample. */
10
+ zcr: number;
11
+ /** AudioContext time at the end of the frame, seconds. */
12
+ ctxTime: number;
13
+ seq: number;
14
+ }
15
+
16
+ /**
17
+ * BOFF-9797 (Stage C). The Avatar tab's own microphone capture: `getUserMedia`, through the capture worklet
18
+ * (`capture-worklet.ts`), into 16 kHz PCM frames.
19
+ *
20
+ * **Ported, not shared.** From `src/shared/assistant/voice/handsfree/micCapture.ts` in Algoshred/fe-libs at the
21
+ * commit `capture-worklet.ts` names (`CAPTURE_WORKLET_PORTED_FROM`; blob `8486c630`), with one difference: fe-libs
22
+ * routes everything its assistant plays into the worklet's second input (its playback reference, for barge-in), and the
23
+ * website plays nothing through this context (the avatar's voice is the room's own audio element, and the mic is
24
+ * offered only while no session plays), so no reference bus is made and the worklet has the microphone as its only
25
+ * input. `looksLikeHeadset`, which only fe-libs' barge-in reads, is not ported. `capture-worklet.test.ts` holds the
26
+ * parity tests.
27
+ *
28
+ * It opens `getUserMedia` with echo cancellation, noise suppression and auto gain on. Constraints are plain `true`,
29
+ * never `{ exact }`: `exact` throws OverconstrainedError on handsets that cannot honour one. The settings the browser
30
+ * actually applied are read back (`track.getSettings()`), because some Android handsets report `true` and apply nothing.
31
+ *
32
+ * `stop()` stops the track (the browser's recording indicator goes), disconnects the graph and closes the context. The
33
+ * caller does that the moment a press ends, whichever way it ends; `pagehide` does it too, whatever the caller does.
34
+ */
35
+
36
+ interface MicCaptureHandle {
37
+ context: AudioContext;
38
+ micSource: MediaStreamAudioSourceNode;
39
+ track: MediaStreamTrack;
40
+ /** What the browser reports it applied. */
41
+ settings: MediaTrackSettings;
42
+ /** Stop everything and release the microphone. Safe to call more than once. */
43
+ stop(): void;
44
+ }
45
+
46
+ /** What the mic needs of a WebSocket; {@link openBrowserSocket} adapts the browser's. (fe-libs' seam.) */
47
+ interface SocketLike {
48
+ readonly readyState: number;
49
+ send(data: string | ArrayBufferLike | ArrayBufferView): void;
50
+ close(code?: number): void;
51
+ listen(handlers: SocketHandlers): void;
52
+ }
53
+ interface SocketHandlers {
54
+ open(): void;
55
+ message(data: unknown): void;
56
+ close(code: number): void;
57
+ error(): void;
58
+ }
59
+
60
+ /**
61
+ * BOFF-9797 (Stage C). The shapes the Avatar tab's mic shares with the live avatar's view.
62
+ *
63
+ * Types only, on purpose: the view (`explore-avatar-mic.tsx`, in the view's chunk) imports this file for its types and
64
+ * nothing else, so the view never carries the mic's code. It reaches the mic's chunk (`dist/avatar-mic/`) only through
65
+ * a dynamic `import()`, the first time a visitor uses the mic.
66
+ */
67
+ /**
68
+ * What an availability answer offered the mic (`stt`, while the gateway's website speech-to-text is on): the token for
69
+ * one stream, when the gateway says it expires, and when this browser received it, on its own clock.
70
+ */
71
+ interface AvatarMicOffer {
72
+ token: string;
73
+ /** Epoch milliseconds, on the gateway's clock. */
74
+ expiresAt: number;
75
+ /** Epoch milliseconds, on this browser's clock. */
76
+ receivedAt: number;
77
+ }
78
+ /**
79
+ * Why a press did not give text:
80
+ *
81
+ * - `denied`: the microphone was refused (the visitor, the browser or the page's permissions);
82
+ * - `unsupported`: this browser cannot capture here, or has no microphone to use;
83
+ * - `unavailable`: the server will not take a stream from this page now: no offer, an offer it refuses twice, its mic
84
+ * notice is another version than this SDK's, or the website avatar is paused;
85
+ * - `limit`: a limit on voice questions is reached (the visitor's, the network's, or every visitor's together);
86
+ * - `busy`: the stream is busy, or the server's counters cannot be read: try again in a moment;
87
+ * - `failed`: anything else (the connection lost, availability or the speech service not answering, no answer in
88
+ * time).
89
+ */
90
+ type AvatarMicFailure = "denied" | "unsupported" | "unavailable" | "limit" | "busy" | "failed";
91
+ /**
92
+ * One press of the mic, as the view shows it. `epoch` names the press: a new press has a new one, and what an older one
93
+ * reports is never shown for a newer one.
94
+ *
95
+ * - `starting`: the microphone opening (the browser's permission prompt included) and the stream connecting;
96
+ * - `listening`: recording, since `startedAt` (this browser's clock), with what has been heard so far (the final text
97
+ * and, after it, the words still being recognised);
98
+ * - `finishing`: the recording has stopped (a tap, or the limit: `limit`), and the last of the text is on its way;
99
+ * - `done`: the text, final (empty when no words were heard);
100
+ * - `failed`: no text, and why (with any final text heard before it failed, which the view does not use).
101
+ */
102
+ type AvatarMicState = {
103
+ name: "idle";
104
+ } | {
105
+ name: "starting";
106
+ epoch: number;
107
+ } | {
108
+ name: "listening";
109
+ epoch: number;
110
+ startedAt: number;
111
+ text: string;
112
+ } | {
113
+ name: "finishing";
114
+ epoch: number;
115
+ text: string;
116
+ limit: boolean;
117
+ } | {
118
+ name: "done";
119
+ epoch: number;
120
+ text: string;
121
+ limit: boolean;
122
+ } | {
123
+ name: "failed";
124
+ epoch: number;
125
+ reason: AvatarMicFailure;
126
+ text: string;
127
+ };
128
+ interface AvatarMicOptions {
129
+ /** `avatarApiBase` of the SDK's gateway: the stream is `<base>/stt/stream`. */
130
+ base: string;
131
+ /** The mic notice version the visitor saw: every press names it, and the server takes only its own. */
132
+ notice: string;
133
+ /** The latest offer the view holds, or null. */
134
+ offer: () => AvatarMicOffer | null;
135
+ /**
136
+ * Ask availability again for a new offer: null when the answer carries none (the press is `unavailable`); rejects when
137
+ * there is no answer at all (the press is `failed`, and the mic is still offered).
138
+ */
139
+ refreshOffer: () => Promise<AvatarMicOffer | null>;
140
+ /** Every change of the press's state. */
141
+ onState: (state: AvatarMicState) => void;
142
+ }
143
+ /** One view's mic: one press at a time. */
144
+ interface AvatarMic {
145
+ /** A new press. One still finishing is cancelled first (superseded); one starting or listening is left alone. */
146
+ start(): void;
147
+ /** Stop recording and wait for the final text (the visitor's tap). A press still starting is cancelled instead. */
148
+ stop(): void;
149
+ /** Call off whatever is under way, release the microphone, and report nothing more of it: back to `idle`. */
150
+ cancel(): void;
151
+ /** The press as it stands. */
152
+ state(): AvatarMicState;
153
+ }
154
+ /** What the mic's chunk (`dist/avatar-mic/`) exports. */
155
+ interface AvatarMicModule {
156
+ createAvatarMic: (options: AvatarMicOptions) => AvatarMic;
157
+ }
158
+
159
+ /**
160
+ * BOFF-9797 (Stage C). The Avatar tab's mic, one press at a time: tap to start, tap to stop, thirty seconds at most.
161
+ *
162
+ * A press, in order:
163
+ *
164
+ * 1. **The microphone opens** (`startMicCapture`: the browser's permission prompt the first time). Meanwhile the offer
165
+ * is made ready: the token the last availability answer carried while it is fresh, else a new one, asked for now.
166
+ * 2. **The stream connects**, once the microphone is live (never while a permission prompt is up, so no stream is held
167
+ * open for a visitor who has not decided): `boff.web-stt.v1` and `offer.<token>`. A refused upgrade (4401) asks
168
+ * availability again and tries once more, never twice.
169
+ * 3. **The server says it is ready**: the press's `config` (its utterance id, its epoch, `en-IN`, and the mic notice the
170
+ * visitor saw), then up to {@link PRE_ROLL_MS} of what the microphone heard while the stream connected, then every
171
+ * frame as it comes. Listening, from here: what has been heard so far is reported as it is recognised.
172
+ * 4. **The recording stops**: the visitor's tap, or {@link MAX_PRESS_MS} (counted both on the clock and in audio sent),
173
+ * or the speech service's own limit. The microphone is released at once; `end`, and the last of the text.
174
+ * 5. **The text is final** (`done`): reported once, for the view to put in the box. Nothing is ever sent for the
175
+ * visitor: the text goes nowhere else.
176
+ *
177
+ * **Released on every way out.** The microphone's track is stopped the moment the recording stops, and also when the
178
+ * press fails, is cancelled (`cancel()`: the view going, the page hidden, the Chat tab) or superseded (a new press while
179
+ * one is still finishing: `cancel` for the old one, so it stops being paid for, and its text is never reported). A
180
+ * microphone that opens after its press has gone is closed as it opens. The stream is closed whenever no press needs
181
+ * it: one connection serves a press and the press that supersedes it, and no other.
182
+ */
183
+
184
+ /** What a press needs from the browser. The browser's own by default; a test hands in fakes. */
185
+ interface AvatarMicSeams {
186
+ createSocket: (url: string, protocols: string[]) => SocketLike;
187
+ startCapture: (onFrame: (frame: CaptureFrameMessage) => void) => Promise<Pick<MicCaptureHandle, "stop">>;
188
+ newUtteranceId: () => string | null;
189
+ }
190
+ /** The mic of one Avatar tab. See the file's comment for what a press does. */
191
+ declare function createAvatarMic(options: AvatarMicOptions, seams?: Partial<AvatarMicSeams>): AvatarMic;
192
+
193
+ export { type AvatarMic, type AvatarMicFailure, type AvatarMicModule, type AvatarMicOffer, type AvatarMicOptions, type AvatarMicState, createAvatarMic };
@@ -0,0 +1,193 @@
1
+ /** What the worklet posts for each frame. */
2
+ interface CaptureFrameMessage {
3
+ /** s16le mono 16 kHz, `CAPTURE_FRAME_SAMPLES` samples. */
4
+ pcm: ArrayBuffer;
5
+ /** Microphone level over the frame, dBFS. */
6
+ rmsDb: number;
7
+ /** Reference-bus level over the same samples, dBFS (about -90 when silent, as it always is here). */
8
+ refDb: number;
9
+ /** Zero crossings per sample. */
10
+ zcr: number;
11
+ /** AudioContext time at the end of the frame, seconds. */
12
+ ctxTime: number;
13
+ seq: number;
14
+ }
15
+
16
+ /**
17
+ * BOFF-9797 (Stage C). The Avatar tab's own microphone capture: `getUserMedia`, through the capture worklet
18
+ * (`capture-worklet.ts`), into 16 kHz PCM frames.
19
+ *
20
+ * **Ported, not shared.** From `src/shared/assistant/voice/handsfree/micCapture.ts` in Algoshred/fe-libs at the
21
+ * commit `capture-worklet.ts` names (`CAPTURE_WORKLET_PORTED_FROM`; blob `8486c630`), with one difference: fe-libs
22
+ * routes everything its assistant plays into the worklet's second input (its playback reference, for barge-in), and the
23
+ * website plays nothing through this context (the avatar's voice is the room's own audio element, and the mic is
24
+ * offered only while no session plays), so no reference bus is made and the worklet has the microphone as its only
25
+ * input. `looksLikeHeadset`, which only fe-libs' barge-in reads, is not ported. `capture-worklet.test.ts` holds the
26
+ * parity tests.
27
+ *
28
+ * It opens `getUserMedia` with echo cancellation, noise suppression and auto gain on. Constraints are plain `true`,
29
+ * never `{ exact }`: `exact` throws OverconstrainedError on handsets that cannot honour one. The settings the browser
30
+ * actually applied are read back (`track.getSettings()`), because some Android handsets report `true` and apply nothing.
31
+ *
32
+ * `stop()` stops the track (the browser's recording indicator goes), disconnects the graph and closes the context. The
33
+ * caller does that the moment a press ends, whichever way it ends; `pagehide` does it too, whatever the caller does.
34
+ */
35
+
36
+ interface MicCaptureHandle {
37
+ context: AudioContext;
38
+ micSource: MediaStreamAudioSourceNode;
39
+ track: MediaStreamTrack;
40
+ /** What the browser reports it applied. */
41
+ settings: MediaTrackSettings;
42
+ /** Stop everything and release the microphone. Safe to call more than once. */
43
+ stop(): void;
44
+ }
45
+
46
+ /** What the mic needs of a WebSocket; {@link openBrowserSocket} adapts the browser's. (fe-libs' seam.) */
47
+ interface SocketLike {
48
+ readonly readyState: number;
49
+ send(data: string | ArrayBufferLike | ArrayBufferView): void;
50
+ close(code?: number): void;
51
+ listen(handlers: SocketHandlers): void;
52
+ }
53
+ interface SocketHandlers {
54
+ open(): void;
55
+ message(data: unknown): void;
56
+ close(code: number): void;
57
+ error(): void;
58
+ }
59
+
60
+ /**
61
+ * BOFF-9797 (Stage C). The shapes the Avatar tab's mic shares with the live avatar's view.
62
+ *
63
+ * Types only, on purpose: the view (`explore-avatar-mic.tsx`, in the view's chunk) imports this file for its types and
64
+ * nothing else, so the view never carries the mic's code. It reaches the mic's chunk (`dist/avatar-mic/`) only through
65
+ * a dynamic `import()`, the first time a visitor uses the mic.
66
+ */
67
+ /**
68
+ * What an availability answer offered the mic (`stt`, while the gateway's website speech-to-text is on): the token for
69
+ * one stream, when the gateway says it expires, and when this browser received it, on its own clock.
70
+ */
71
+ interface AvatarMicOffer {
72
+ token: string;
73
+ /** Epoch milliseconds, on the gateway's clock. */
74
+ expiresAt: number;
75
+ /** Epoch milliseconds, on this browser's clock. */
76
+ receivedAt: number;
77
+ }
78
+ /**
79
+ * Why a press did not give text:
80
+ *
81
+ * - `denied`: the microphone was refused (the visitor, the browser or the page's permissions);
82
+ * - `unsupported`: this browser cannot capture here, or has no microphone to use;
83
+ * - `unavailable`: the server will not take a stream from this page now: no offer, an offer it refuses twice, its mic
84
+ * notice is another version than this SDK's, or the website avatar is paused;
85
+ * - `limit`: a limit on voice questions is reached (the visitor's, the network's, or every visitor's together);
86
+ * - `busy`: the stream is busy, or the server's counters cannot be read: try again in a moment;
87
+ * - `failed`: anything else (the connection lost, availability or the speech service not answering, no answer in
88
+ * time).
89
+ */
90
+ type AvatarMicFailure = "denied" | "unsupported" | "unavailable" | "limit" | "busy" | "failed";
91
+ /**
92
+ * One press of the mic, as the view shows it. `epoch` names the press: a new press has a new one, and what an older one
93
+ * reports is never shown for a newer one.
94
+ *
95
+ * - `starting`: the microphone opening (the browser's permission prompt included) and the stream connecting;
96
+ * - `listening`: recording, since `startedAt` (this browser's clock), with what has been heard so far (the final text
97
+ * and, after it, the words still being recognised);
98
+ * - `finishing`: the recording has stopped (a tap, or the limit: `limit`), and the last of the text is on its way;
99
+ * - `done`: the text, final (empty when no words were heard);
100
+ * - `failed`: no text, and why (with any final text heard before it failed, which the view does not use).
101
+ */
102
+ type AvatarMicState = {
103
+ name: "idle";
104
+ } | {
105
+ name: "starting";
106
+ epoch: number;
107
+ } | {
108
+ name: "listening";
109
+ epoch: number;
110
+ startedAt: number;
111
+ text: string;
112
+ } | {
113
+ name: "finishing";
114
+ epoch: number;
115
+ text: string;
116
+ limit: boolean;
117
+ } | {
118
+ name: "done";
119
+ epoch: number;
120
+ text: string;
121
+ limit: boolean;
122
+ } | {
123
+ name: "failed";
124
+ epoch: number;
125
+ reason: AvatarMicFailure;
126
+ text: string;
127
+ };
128
+ interface AvatarMicOptions {
129
+ /** `avatarApiBase` of the SDK's gateway: the stream is `<base>/stt/stream`. */
130
+ base: string;
131
+ /** The mic notice version the visitor saw: every press names it, and the server takes only its own. */
132
+ notice: string;
133
+ /** The latest offer the view holds, or null. */
134
+ offer: () => AvatarMicOffer | null;
135
+ /**
136
+ * Ask availability again for a new offer: null when the answer carries none (the press is `unavailable`); rejects when
137
+ * there is no answer at all (the press is `failed`, and the mic is still offered).
138
+ */
139
+ refreshOffer: () => Promise<AvatarMicOffer | null>;
140
+ /** Every change of the press's state. */
141
+ onState: (state: AvatarMicState) => void;
142
+ }
143
+ /** One view's mic: one press at a time. */
144
+ interface AvatarMic {
145
+ /** A new press. One still finishing is cancelled first (superseded); one starting or listening is left alone. */
146
+ start(): void;
147
+ /** Stop recording and wait for the final text (the visitor's tap). A press still starting is cancelled instead. */
148
+ stop(): void;
149
+ /** Call off whatever is under way, release the microphone, and report nothing more of it: back to `idle`. */
150
+ cancel(): void;
151
+ /** The press as it stands. */
152
+ state(): AvatarMicState;
153
+ }
154
+ /** What the mic's chunk (`dist/avatar-mic/`) exports. */
155
+ interface AvatarMicModule {
156
+ createAvatarMic: (options: AvatarMicOptions) => AvatarMic;
157
+ }
158
+
159
+ /**
160
+ * BOFF-9797 (Stage C). The Avatar tab's mic, one press at a time: tap to start, tap to stop, thirty seconds at most.
161
+ *
162
+ * A press, in order:
163
+ *
164
+ * 1. **The microphone opens** (`startMicCapture`: the browser's permission prompt the first time). Meanwhile the offer
165
+ * is made ready: the token the last availability answer carried while it is fresh, else a new one, asked for now.
166
+ * 2. **The stream connects**, once the microphone is live (never while a permission prompt is up, so no stream is held
167
+ * open for a visitor who has not decided): `boff.web-stt.v1` and `offer.<token>`. A refused upgrade (4401) asks
168
+ * availability again and tries once more, never twice.
169
+ * 3. **The server says it is ready**: the press's `config` (its utterance id, its epoch, `en-IN`, and the mic notice the
170
+ * visitor saw), then up to {@link PRE_ROLL_MS} of what the microphone heard while the stream connected, then every
171
+ * frame as it comes. Listening, from here: what has been heard so far is reported as it is recognised.
172
+ * 4. **The recording stops**: the visitor's tap, or {@link MAX_PRESS_MS} (counted both on the clock and in audio sent),
173
+ * or the speech service's own limit. The microphone is released at once; `end`, and the last of the text.
174
+ * 5. **The text is final** (`done`): reported once, for the view to put in the box. Nothing is ever sent for the
175
+ * visitor: the text goes nowhere else.
176
+ *
177
+ * **Released on every way out.** The microphone's track is stopped the moment the recording stops, and also when the
178
+ * press fails, is cancelled (`cancel()`: the view going, the page hidden, the Chat tab) or superseded (a new press while
179
+ * one is still finishing: `cancel` for the old one, so it stops being paid for, and its text is never reported). A
180
+ * microphone that opens after its press has gone is closed as it opens. The stream is closed whenever no press needs
181
+ * it: one connection serves a press and the press that supersedes it, and no other.
182
+ */
183
+
184
+ /** What a press needs from the browser. The browser's own by default; a test hands in fakes. */
185
+ interface AvatarMicSeams {
186
+ createSocket: (url: string, protocols: string[]) => SocketLike;
187
+ startCapture: (onFrame: (frame: CaptureFrameMessage) => void) => Promise<Pick<MicCaptureHandle, "stop">>;
188
+ newUtteranceId: () => string | null;
189
+ }
190
+ /** The mic of one Avatar tab. See the file's comment for what a press does. */
191
+ declare function createAvatarMic(options: AvatarMicOptions, seams?: Partial<AvatarMicSeams>): AvatarMic;
192
+
193
+ export { type AvatarMic, type AvatarMicFailure, type AvatarMicModule, type AvatarMicOffer, type AvatarMicOptions, type AvatarMicState, createAvatarMic };