@1interface/voice-core 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,412 @@
1
+ import { ActionCreatorWithOptionalPayload } from '@reduxjs/toolkit';
2
+ import { ActionCreatorWithoutPayload } from '@reduxjs/toolkit';
3
+ import { Context } from 'react';
4
+ import { Dispatch } from '@reduxjs/toolkit';
5
+ import { ListenerMiddlewareInstance } from '@reduxjs/toolkit';
6
+ import { Message } from '@1interface/shared-core';
7
+ import { MutableRefObject } from 'react';
8
+ import { PaymentInitPayload } from '@1interface/shared-core';
9
+ import { ReactNode } from 'react';
10
+ import { Reducer } from '@reduxjs/toolkit';
11
+ import { Storage as Storage_2 } from '@1interface/shared-core';
12
+ import { ThunkAction } from '@reduxjs/toolkit';
13
+ import { ThunkDispatch } from '@reduxjs/toolkit';
14
+ import { UnknownAction } from '@reduxjs/toolkit';
15
+
16
+ export declare interface AudioCapture {
17
+ open(opts: AudioCaptureOpenOptions): void;
18
+ setMuted(muted: boolean): void;
19
+ close(): void;
20
+ }
21
+
22
+ export declare interface AudioCaptureOpenOptions {
23
+ micDeviceId: string | null;
24
+ initialMuted: boolean;
25
+ onChunk: (buffer: ArrayBuffer) => void;
26
+ onLoadingChange?: (loading: boolean) => void;
27
+ onError?: (error: unknown) => void;
28
+ }
29
+
30
+ export declare interface AudioDeviceInfo {
31
+ deviceId: string;
32
+ label: string;
33
+ }
34
+
35
+ export declare interface AudioDevices {
36
+ enumerate(): Promise<AudioDevicesSnapshot>;
37
+ onChange(callback: () => void): () => void;
38
+ }
39
+
40
+ export declare interface AudioDevicesSnapshot {
41
+ microphones: AudioDeviceInfo[];
42
+ speakers: AudioDeviceInfo[];
43
+ }
44
+
45
+ declare interface AudioEntry {
46
+ pcmInt16: Int16Array;
47
+ sampleRate: number;
48
+ }
49
+
50
+ export declare interface AudioPlatform {
51
+ createCapture(): AudioCapture;
52
+ createPlayer(): AudioPlayer;
53
+ createThinkingSound(): ThinkingSound;
54
+ readonly devices: AudioDevices;
55
+ requestMicPermission(): Promise<MicPermissionResult>;
56
+ requiresSoftwareAecMute?: boolean;
57
+ }
58
+
59
+ export declare const AudioPlatformContext: Context<AudioPlatform>;
60
+
61
+ export declare interface AudioPlayer {
62
+ play(pcmBytes: ArrayBuffer, speakerId?: string): void;
63
+ setPrerollProvider(provider: (() => number) | null): void;
64
+ readonly remainingSeconds: number;
65
+ onDrained(callback: () => void): void;
66
+ resetForNewSegment(): void;
67
+ stop(): void;
68
+ destroy(): void;
69
+ }
70
+
71
+ export declare class BrowserAudioCapture implements AudioCapture {
72
+ private ctx;
73
+ private stream;
74
+ private source;
75
+ private gain;
76
+ private worklet;
77
+ private worker;
78
+ private visibilityHandler;
79
+ private opening;
80
+ private cancelled;
81
+ private opts;
82
+ open(opts: AudioCaptureOpenOptions): void;
83
+ setMuted(muted: boolean): void;
84
+ close(): void;
85
+ }
86
+
87
+ export declare class BrowserAudioDevices implements AudioDevices {
88
+ enumerate(): Promise<AudioDevicesSnapshot>;
89
+ onChange(callback: () => void): () => void;
90
+ }
91
+
92
+ export declare const browserAudioPlatform: AudioPlatform;
93
+
94
+ export declare class BrowserAudioPlayer implements AudioPlayer {
95
+ private ctx;
96
+ private masterGain;
97
+ private currentSpeakerId;
98
+ private nextPlayTime;
99
+ private doneTimer;
100
+ private isFirstChunk;
101
+ private prerollProvider;
102
+ private activeSources;
103
+ setPrerollProvider(provider: (() => number) | null): void;
104
+ play(pcmBytes: ArrayBuffer, speakerId?: string): void;
105
+ get remainingSeconds(): number;
106
+ onDrained(callback: () => void): void;
107
+ resetForNewSegment(): void;
108
+ stop(): void;
109
+ destroy(): void;
110
+ }
111
+
112
+ export declare class BrowserThinkingSound implements ThinkingSound {
113
+ private audio;
114
+ private timer;
115
+ private pendingPlay;
116
+ private pendingAction;
117
+ unlock(): void;
118
+ start(remainingAudioSeconds: number): void;
119
+ pause(): void;
120
+ resume(remainingAudioSeconds?: number): void;
121
+ stop(): void;
122
+ destroy(): void;
123
+ private _doPlay;
124
+ }
125
+
126
+ export declare function clearAudioBuffer(): void;
127
+
128
+ export declare function computeAdaptivePreroll(arrivals: number[]): number;
129
+
130
+ export declare function createAccumulator(sampleRate: number): string;
131
+
132
+ export declare function finalizeAudio(id: string): void;
133
+
134
+ export declare function getAudio(id: string): AudioEntry | undefined;
135
+
136
+ export declare const hydrateVoice: (storage: Storage_2) => ThunkAction<Promise<void>, unknown, unknown, UnknownAction>;
137
+
138
+ declare interface MicPermissionResult {
139
+ granted: boolean;
140
+ error?: unknown;
141
+ }
142
+
143
+ export declare const ORB_STATE_CONFIG: Record<string, OrbStateConfig>;
144
+
145
+ declare interface OrbStateConfig {
146
+ opacity: number;
147
+ playbackRate: number;
148
+ scale: number;
149
+ }
150
+
151
+ export declare function pushChunk(id: string, chunk: ArrayBuffer): void;
152
+
153
+ export declare const resetIndicatorState: ActionCreatorWithoutPayload<"voice/resetIndicatorState">;
154
+
155
+ export declare const setConnectionStatus: ActionCreatorWithOptionalPayload<VoiceConnectionStatus, "voice/setConnectionStatus">;
156
+
157
+ export declare const setError: ActionCreatorWithOptionalPayload<string, "voice/setError">;
158
+
159
+ export declare const setFirstAssistantMessageComplete: ActionCreatorWithOptionalPayload<boolean, "voice/setFirstAssistantMessageComplete">;
160
+
161
+ export declare const setIsAgentWorking: ActionCreatorWithOptionalPayload<boolean, "voice/setIsAgentWorking">;
162
+
163
+ export declare const setListeningEnabled: ActionCreatorWithOptionalPayload<boolean, "voice/setListeningEnabled">;
164
+
165
+ export declare const setMuted: ActionCreatorWithOptionalPayload<boolean, "voice/setMuted">;
166
+
167
+ export declare const setPipelineStatus: ActionCreatorWithOptionalPayload<VoicePipelineStatus, "voice/setPipelineStatus">;
168
+
169
+ export declare const setResponseStartTime: ActionCreatorWithOptionalPayload<number, "voice/setResponseStartTime">;
170
+
171
+ export declare const setSelectedMicId: ActionCreatorWithOptionalPayload<string, "voice/setSelectedMicId">;
172
+
173
+ export declare const setSelectedSpeakerId: ActionCreatorWithOptionalPayload<string, "voice/setSelectedSpeakerId">;
174
+
175
+ export declare const setStatusText: ActionCreatorWithOptionalPayload<string, "voice/setStatusText">;
176
+
177
+ export declare interface ThinkingSound {
178
+ unlock(): void;
179
+ start(remainingAudioSeconds: number): void;
180
+ pause(): void;
181
+ resume(remainingAudioSeconds?: number): void;
182
+ stop(): void;
183
+ destroy(): void;
184
+ }
185
+
186
+ export declare const useAudioPlatform: () => AudioPlatform;
187
+
188
+ export declare function useMediaDevices(): {
189
+ microphones: AudioDeviceInfo[];
190
+ speakers: AudioDeviceInfo[];
191
+ refresh: () => Promise<void>;
192
+ };
193
+
194
+ export declare const useVoice: () => VoiceContextValue;
195
+
196
+ export declare function useVoiceAudioPlayback(audioId?: string): {
197
+ playing: boolean;
198
+ toggle: () => void;
199
+ hasAudio: boolean;
200
+ };
201
+
202
+ export declare function useVoiceDisplayState(loading: boolean, errored: boolean, effectiveMuted?: boolean): VoiceDisplayState;
203
+
204
+ export declare function useVoiceMicCapture({ micOpen, muted, onChunk }: UseVoiceMicCaptureOptions): {
205
+ loading: boolean;
206
+ errored: boolean;
207
+ };
208
+
209
+ declare interface UseVoiceMicCaptureOptions {
210
+ micOpen: boolean;
211
+ muted: boolean;
212
+ onChunk: (buf: ArrayBuffer) => void;
213
+ }
214
+
215
+ export declare const useVoiceProviderValue: ({ conversationId, voiceBridge, assistantStartsConversation, }: VoiceProviderParams) => VoiceContextValue;
216
+
217
+ export declare const useVoiceUI: () => VoiceUIContextValue;
218
+
219
+ export declare function useVoiceWebSocket(conversationId: string | null, bridge: VoiceChatBridge): {
220
+ connect: () => void;
221
+ disconnect: (reason?: VoiceDisconnectReason) => void;
222
+ sendAudioChunk: (buf: ArrayBuffer) => void;
223
+ sendText: (text: string) => void;
224
+ };
225
+
226
+ export declare const VOICE_AUTH_ERROR_PATTERNS: readonly RegExp[];
227
+
228
+ export declare const VOICE_BARGE_IN_DELAY_MS = 400;
229
+
230
+ export declare const VOICE_CHUNK_SAMPLES = 2048;
231
+
232
+ export declare const VOICE_CONNECTION_STATUSES: {
233
+ readonly DISCONNECTED: "disconnected";
234
+ readonly CONNECTING: "connecting";
235
+ readonly CONNECTED: "connected";
236
+ };
237
+
238
+ declare const VOICE_DISCONNECT_REASONS: {
239
+ readonly USER: "user";
240
+ readonly CONV_CHANGE: "conv-change";
241
+ readonly LOGOUT: "logout";
242
+ readonly WATCHDOG: "watchdog";
243
+ };
244
+
245
+ export declare const VOICE_DISPLAY_STATES: {
246
+ readonly OFFLINE: "offline";
247
+ readonly CONNECTING: "connecting";
248
+ readonly READY: "ready";
249
+ readonly LOADING: "loading";
250
+ readonly IDLE: "idle";
251
+ readonly LISTENING: "listening";
252
+ readonly MUTED: "muted";
253
+ readonly PROCESSING: "processing";
254
+ readonly SPEAKING: "speaking";
255
+ };
256
+
257
+ export declare const VOICE_FATAL_ERROR_PATTERNS: readonly RegExp[];
258
+
259
+ export declare const VOICE_KEEPALIVE_INTERVAL_MS = 20000;
260
+
261
+ export declare const VOICE_PCM_INT16_MAX = 32768;
262
+
263
+ export declare const VOICE_PIPELINE_STATUSES: {
264
+ readonly IDLE: "idle";
265
+ readonly LISTENING: "listening";
266
+ readonly PROCESSING: "processing";
267
+ readonly SPEAKING: "speaking";
268
+ };
269
+
270
+ export declare const VOICE_PREROLL_DEFAULT_SECONDS = 0.2;
271
+
272
+ export declare const VOICE_PREROLL_JITTER_WINDOW = 30;
273
+
274
+ export declare const VOICE_PREROLL_MAX_SECONDS = 0.5;
275
+
276
+ export declare const VOICE_PREROLL_MIN_SAMPLES = 5;
277
+
278
+ export declare const VOICE_PREROLL_MIN_SECONDS = 0.1;
279
+
280
+ export declare const VOICE_SAMPLE_RATE = 24000;
281
+
282
+ export declare const VOICE_STORAGE_KEYS: {
283
+ readonly SELECTED_MIC_ID: "voice_selectedMicId";
284
+ readonly SELECTED_SPEAKER_ID: "voice_selectedSpeakerId";
285
+ };
286
+
287
+ export declare const VOICE_TERMINAL_ERROR_PATTERNS: readonly RegExp[];
288
+
289
+ export declare const VOICE_THINKING_SOUND_DELAY_MS = 300;
290
+
291
+ export declare const VOICE_THINKING_SOUND_PATH = "/sounds/thinking-sound.mp3";
292
+
293
+ export declare const VOICE_THINKING_SOUND_VOLUME = 0.5;
294
+
295
+ export declare const VOICE_WS_CLIENT: {
296
+ readonly START_CONVERSATION: "start_conversation";
297
+ readonly TEXT_MESSAGE: "text_message";
298
+ readonly STOP_PLAYBACK: "stop_playback";
299
+ readonly PING: "ping";
300
+ };
301
+
302
+ export declare const VOICE_WS_SERVER: {
303
+ readonly SPEECH_STARTED: "speech_started";
304
+ readonly SPEECH_STOPPED: "speech_stopped";
305
+ readonly STT_RESULT: "stt_result";
306
+ readonly TEXT_DELTA: "text_delta";
307
+ readonly STATUS_UPDATE: "status_update";
308
+ readonly STATUS_CLEAR: "clear_status";
309
+ readonly TTS_START: "tts_start";
310
+ readonly TTS_END: "tts_end";
311
+ readonly WIDGET: "widget";
312
+ readonly PAYMENT: "payment";
313
+ readonly CONVERSATION_TITLE: "conversation_title";
314
+ readonly ERROR: "error";
315
+ readonly PONG: "pong";
316
+ };
317
+
318
+ export declare type VoiceAppDispatch = Dispatch<UnknownAction>;
319
+
320
+ export declare interface VoiceChatBridge {
321
+ setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
322
+ updateStreamingMessage: (chatMsgId: string, content: string) => void;
323
+ commitStreamingMessages: () => void;
324
+ discardStreamingMessages: () => void;
325
+ shouldGreetOnConnect: () => boolean;
326
+ setVoiceSendText: (fn: ((text: string) => void) | null) => void;
327
+ onPaymentInit?: (init: PaymentInitPayload) => void;
328
+ onConversationTitle?: (title: string) => void;
329
+ }
330
+
331
+ export declare type VoiceConnectionStatus = (typeof VOICE_CONNECTION_STATUSES)[keyof typeof VOICE_CONNECTION_STATUSES];
332
+
333
+ export declare const VoiceContext: Context<VoiceContextValue>;
334
+
335
+ export declare interface VoiceContextValue {
336
+ displayState: VoiceDisplayState;
337
+ pipelineStatus: VoicePipelineStatus;
338
+ isAgentWorking: boolean;
339
+ enterVoiceMode: () => Promise<boolean>;
340
+ exitVoiceMode: () => void;
341
+ toggleMute: () => void;
342
+ sendText: (text: string) => void;
343
+ muted: boolean;
344
+ micLoading: boolean;
345
+ micErrored: boolean;
346
+ }
347
+
348
+ declare type VoiceDisconnectReason = (typeof VOICE_DISCONNECT_REASONS)[keyof typeof VOICE_DISCONNECT_REASONS];
349
+
350
+ export declare type VoiceDisplayState = (typeof VOICE_DISPLAY_STATES)[keyof typeof VOICE_DISPLAY_STATES];
351
+
352
+ export declare const voiceListenerMiddleware: ListenerMiddlewareInstance<unknown, ThunkDispatch<unknown, unknown, UnknownAction>, unknown>;
353
+
354
+ export declare type VoicePipelineStatus = (typeof VOICE_PIPELINE_STATUSES)[keyof typeof VOICE_PIPELINE_STATUSES];
355
+
356
+ export declare interface VoiceProviderParams {
357
+ conversationId: string | null;
358
+ voiceBridge: VoiceChatBridge;
359
+ assistantStartsConversation: boolean;
360
+ }
361
+
362
+ export declare const voiceReducer: Reducer<VoiceState>;
363
+
364
+ export declare interface VoiceRootState {
365
+ voice: VoiceState;
366
+ auth: {
367
+ accessToken: string | null;
368
+ };
369
+ }
370
+
371
+ export declare interface VoiceServerMessage {
372
+ type: string;
373
+ text?: string;
374
+ message?: string;
375
+ token?: string;
376
+ message_id?: number;
377
+ cancelled?: boolean;
378
+ data?: Record<string, unknown>;
379
+ [key: string]: unknown;
380
+ }
381
+
382
+ export declare interface VoiceState {
383
+ connectionStatus: VoiceConnectionStatus;
384
+ pipelineStatus: VoicePipelineStatus;
385
+ listeningEnabled: boolean;
386
+ muted: boolean;
387
+ firstAssistantMessageComplete: boolean;
388
+ statusText: string | null;
389
+ isAgentWorking: boolean;
390
+ responseStartTime: number | null;
391
+ error: string | null;
392
+ interimTranscript: string | null;
393
+ selectedMicId: string;
394
+ selectedSpeakerId: string;
395
+ }
396
+
397
+ export declare const VoiceUIContext: Context<VoiceUIContextValue>;
398
+
399
+ export declare interface VoiceUIContextValue {
400
+ strategyActive: boolean;
401
+ voiceSlot: ReactNode | null;
402
+ onEnterVoice: (() => void) | null;
403
+ onFloatingOrbClick: (() => void) | null;
404
+ voiceInputHidden: boolean;
405
+ headerCenterSlot: ReactNode | null;
406
+ floatOrbPositionRef: MutableRefObject<{
407
+ right: number;
408
+ bottom: number;
409
+ }> | null;
410
+ }
411
+
412
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react"),t=require("react-redux"),r=require("@reduxjs/toolkit"),n=require("@1interface/shared-core"),s={DISCONNECTED:"disconnected",CONNECTING:"connecting",CONNECTED:"connected"},c={IDLE:"idle",LISTENING:"listening",PROCESSING:"processing",SPEAKING:"speaking"},i={OFFLINE:"offline",CONNECTING:"connecting",READY:"ready",LOADING:"loading",IDLE:"idle",LISTENING:"listening",MUTED:"muted",PROCESSING:"processing",SPEAKING:"speaking"},o=24e3,a=32768,u="/sounds/thinking-sound.mp3",l=new Set([1006,1011,1012,1013,1014]),d=.2,p="reset",g={START_CONVERSATION:"start_conversation",TEXT_MESSAGE:"text_message",STOP_PLAYBACK:"stop_playback",PING:"ping"},S=[/rejected/i,/unauthori[sz]ed/i,/forbidden/i,/\b40[13]\b/],m=[/\b402\b/,/\b429\b/,/rate[\s-]?limit/i],h=[...S,...m],E={SPEECH_STARTED:"speech_started",SPEECH_STOPPED:"speech_stopped",STT_RESULT:"stt_result",TEXT_DELTA:"text_delta",STATUS_UPDATE:"status_update",STATUS_CLEAR:"clear_status",TTS_START:"tts_start",TTS_END:"tts_end",WIDGET:"widget",PAYMENT:"payment",CONVERSATION_TITLE:"conversation_title",ERROR:"error",PONG:"pong"},f={SELECTED_MIC_ID:"voice_selectedMicId",SELECTED_SPEAKER_ID:"voice_selectedSpeakerId"};function T(){return{connectionStatus:s.DISCONNECTED,pipelineStatus:c.IDLE,listeningEnabled:!1,muted:!1,firstAssistantMessageComplete:!1,statusText:null,isAgentWorking:!1,responseStartTime:null,error:null,interimTranscript:null,selectedMicId:"",selectedSpeakerId:""}}const I=T(),_=r.createSlice({name:"voice",initialState:I,reducers:{setConnectionStatus(e,t){e.connectionStatus=t.payload},setPipelineStatus(e,t){e.pipelineStatus=t.payload},setListeningEnabled(e,t){e.listeningEnabled=t.payload},setStatusText(e,t){e.statusText=t.payload},setIsAgentWorking(e,t){e.isAgentWorking=t.payload},setResponseStartTime(e,t){e.responseStartTime=t.payload},setError(e,t){e.error=t.payload},setInterimTranscript(e,t){e.interimTranscript=t.payload},setMuted(e,t){e.muted=t.payload},setFirstAssistantMessageComplete(e,t){e.firstAssistantMessageComplete=t.payload},setSelectedMicId(e,t){e.selectedMicId=t.payload},setSelectedSpeakerId(e,t){e.selectedSpeakerId=t.payload},resetVoiceState:()=>T(),resetIndicatorState(e){e.statusText=null,e.isAgentWorking=!1,e.responseStartTime=null}}}),{setConnectionStatus:C,setPipelineStatus:v,setListeningEnabled:A,setMuted:R,setFirstAssistantMessageComplete:y,setStatusText:k,setIsAgentWorking:N,setResponseStartTime:w,setError:x,setInterimTranscript:O,setSelectedMicId:P,setSelectedSpeakerId:b,resetVoiceState:D,resetIndicatorState:M}=_.actions,L=_.reducer,V=new Map,G=new Map;let W=0;function U(e){const t="voice_audio_"+ ++W;return G.set(t,{chunks:[],sampleRate:e}),t}function K(e,t){const r=G.get(e);r&&r.chunks.push(t)}function F(e){const t=G.get(e);if(!t||0===t.chunks.length)return void G.delete(e);const r=t.chunks.reduce((e,t)=>e+t.byteLength,0),n=new Int16Array(r/2);let s=0;for(const e of t.chunks)n.set(new Int16Array(e),s),s+=e.byteLength/2;V.set(e,{pcmInt16:n,sampleRate:t.sampleRate}),G.delete(e)}function B(e){return V.get(e)}function H(){V.clear(),G.clear()}function q(e){if(e.length<5)return d;let t=0,r=0;for(let n=1;n<e.length;n++){const s=e[n]-e[n-1];t+=s,s>r&&(r=s)}return Math.min(.5,Math.max(.1,(r-t/(e.length-1))/1e3))}class Y{ctx=null;masterGain=null;currentSpeakerId=void 0;nextPlayTime=0;doneTimer=null;isFirstChunk=!0;prerollProvider=null;activeSources=new Set;setPrerollProvider(e){this.prerollProvider=e}play(e,t){this.ctx&&t!==this.currentSpeakerId&&(this.ctx.close(),this.ctx=null,this.masterGain=null),this.ctx||(this.ctx=new AudioContext({sampleRate:o,...t?{sinkId:t}:{}}),this.masterGain=this.ctx.createGain(),this.masterGain.connect(this.ctx.destination),this.currentSpeakerId=t);const r=this.ctx,n=new Int16Array(e),s=new Float32Array(n.length);for(let e=0;e<n.length;e++)s[e]=n[e]/a;const c=r.createBuffer(1,s.length,o);c.getChannelData(0).set(s);const i=r.createBufferSource();i.buffer=c,i.connect(this.masterGain),this.activeSources.add(i),i.onended=()=>this.activeSources.delete(i);const u=r.currentTime;let l=Math.max(u,this.nextPlayTime);if(this.isFirstChunk){this.isFirstChunk=!1;const e=this.prerollProvider?this.prerollProvider():d;l=Math.max(l,u+e)}i.start(l),this.nextPlayTime=l+c.duration}get remainingSeconds(){return this.ctx?Math.max(0,this.nextPlayTime-this.ctx.currentTime):0}onDrained(e){this.doneTimer&&clearTimeout(this.doneTimer);const t=this.remainingSeconds;t<=0?e():this.doneTimer=setTimeout(()=>{this.doneTimer=null,e()},1e3*t)}resetForNewSegment(){this.isFirstChunk=!0}stop(){this.doneTimer&&(clearTimeout(this.doneTimer),this.doneTimer=null);for(const e of this.activeSources)try{e.stop()}catch(e){}this.activeSources.clear(),this.nextPlayTime=0,this.isFirstChunk=!0}destroy(){this.stop(),this.ctx&&(this.ctx.close(),this.ctx=null,this.masterGain=null)}}const $=e.createContext(null),J=()=>{const t=e.useContext($);if(!t)throw new Error("useAudioPlatform must be used inside <AudioPlatformProvider>. <VoiceProvider> mounts one by default; if you replaced VoiceProvider, you must wire your own.");return t};function j(e,t){try{e()}catch(e){n.logger.error({message:"Voice WS bridge call threw",api_name:"voice-ws",context:t,error_message:e instanceof Error?e.message:String(e)})}}function X(r,i){const a=t.useDispatch(),u=e.useRef(c.IDLE),p=e.useRef(""),h=e.useRef(null),f=t.useSelector(e=>e.voice.pipelineStatus),T=t.useSelector(e=>e.voice.selectedSpeakerId),I=t.useSelector(e=>e.voice.statusText),_=t.useSelector(e=>e.auth.accessToken);u.current=f,p.current=T,h.current=I;const R=e.useRef(null),P=e.useRef(0),b=J(),D=e.useRef(null);D.current||(D.current=b.createPlayer());const M=e.useRef(null);M.current||(M.current=b.createThinkingSound());const L=e.useRef([]),V=e.useRef(null),G=e.useRef(!1),W=e.useRef(!1),B=e.useRef(null),H=e.useRef(null),Y=e.useRef(null),$=e.useRef(null),X=e.useRef(null),z=e.useRef(!1),Z=e.useRef(null),Q=e.useRef(0),ee=e.useRef(!1),te=e.useRef(0),re=e.useRef(0),ne=e.useRef(0),se=e.useRef(0),ce=e.useRef(0),ie=e.useRef(0),oe=e.useRef(null),ae=e.useRef(null),ue=e.useRef(new Map),le=e.useRef(new Map),de=e.useRef([]),pe=e.useRef(new Set),ge=e.useRef(null),Se=e.useRef(!1),me=e.useRef(null),he=e.useRef(!1),Ee=e.useRef(!1),fe=e.useRef(""),Te=e.useCallback(()=>{const e=de.current;0!==e.length&&(de.current=[],j(()=>ve.current.setMessages(t=>[...t,...e]),"widget-flush"))},[]),Ie=e.useCallback(()=>{if(null!=ae.current)return;const e=Date.now();ae.current=e,a(w(e)),a(n.clearLastResponseTime())},[a]),_e=e.useCallback(()=>{const e=ae.current;ae.current=null,a(w(null)),null!=e&&a(n.setLastResponseTime(Date.now()-e))},[a]),Ce=e.useCallback(()=>{ae.current=null,a(w(null))},[a]),ve=e.useRef(i);ve.current=i;const Ae=e.useCallback(()=>{B.current&&(F(B.current),B.current=null)},[a]),Re=e.useCallback(()=>{M.current.stop(),Ae(),D.current.stop()},[Ae]),ye=e.useCallback((e=n.INDICATOR_TIMER_ACTIONS.KEEP)=>{a(k(null)),a(N(!1)),e===n.INDICATOR_TIMER_ACTIONS.FINALIZE?_e():e===n.INDICATOR_TIMER_ACTIONS.CANCEL&&Ce()},[a,_e,Ce]),ke=e.useRef({setStatusText:e=>a(k(e||null)),setIsAgentWorking:e=>a(N(e)),appendStreamText:(e,t)=>{if(null==t)return;const r=ue.current.get(t);if(!r)return;const n=(le.current.get(r)??"")+e;le.current.set(r,n),W.current||ve.current.updateStreamingMessage(r,n)},pushWidget:e=>{const t=`voice-widget-${n.generateUniqueId()}`,r={id:t,msg_id:t,sender:"agent",message:"",timestamp:(new Date).toISOString(),widget:[e],message_type:["widget"]};de.current.push(r)},onPayment:e=>ve.current.onPaymentInit?.(e),onConversationTitle:e=>ve.current.onConversationTitle?.(e),onError:e=>{n.logger.error({message:"Voice WS server error",api_name:"voice-ws",error_message:e}),a(x(e))},onStatusActive:e=>{Ee.current=!0,fe.current=e,Ie(),M.current?.start(D.current?.remainingSeconds??0)}}),Ne=e.useCallback(()=>{if(R.current&&R.current.readyState<=WebSocket.OPEN)return void n.logger.debug({message:"Voice WS connect() skipped — already connecting/open",api_name:"voice-ws",ready_state:R.current.readyState});if(!r)return void n.logger.debug({message:"Voice WS connect() skipped — no conversationId",api_name:"voice-ws"});if(n.isTokenAboutToExpire(n.readTokenExpiresAt())&&re.current<1){re.current+=1;const e=ne.current;return n.logger.debug({message:"Voice WS refreshing token before connect (about to expire)",api_name:"voice-ws"}),void Promise.resolve(n.getTokenRefreshHandler()?.()).then(()=>{e===ne.current&&we.current()})}n.logger.debug({message:"Voice WS connecting",api_name:"voice-ws",generation:P.current+1}),M.current.unlock();const e=++P.current,t=ne.current,i=Date.now();ce.current=i,ie.current=i,oe.current="connecting";const f=n.readAccessToken()??"",T=function(e,t){const r="undefined"!=typeof window?window.location?.origin:void 0,s=new URL(n.API_ENDPOINTS.CONVERSATION_STREAM(e),r);s.protocol="https:"===s.protocol?"wss:":"ws:",t&&s.searchParams.append("access_token",t);const c=function(){const e=n.getCustomHeaders();for(const[t,r]of Object.entries(e))if("x-client-id"===t.toLowerCase()&&r)return r;return null}();return c&&s.searchParams.append("client_id",c),s.toString()}(r,f);let I=!1;const _=new WebSocket(T);R.current=_,a(C(s.CONNECTING)),_.binaryType="arraybuffer",_.onopen=()=>{I=!0,n.logger.debug({message:"Voice WS open",api_name:"voice-ws",generation:e}),a(C(s.CONNECTED)),a(A(!0)),ve.current.shouldGreetOnConnect()&&(_.send(JSON.stringify({type:g.START_CONVERSATION})),a(N(!0)),Ie()),a(v(c.IDLE)),ue.current.clear(),pe.current.clear(),ge.current=null,Se.current=!1,me.current=null,W.current=!1,he.current=!1,Ee.current=!1,fe.current="",de.current=[],L.current=[],V.current=null,G.current=!1,a(y(!1)),D.current.setPrerollProvider(()=>V.current??d),$.current&&(clearTimeout($.current),$.current=null);const t=Date.now();ce.current=t,ie.current=t,oe.current="open",X.current&&clearInterval(X.current),X.current=setInterval(()=>{const t=Date.now(),r=t-ce.current;if(r>3e4)return n.logger.warn({message:"Voice WS PONG watchdog tripped — closing presumed-dead socket",api_name:"voice-ws",generation:e,ms_since_last_pong:r,ms_since_last_event:t-ie.current,last_event_type:oe.current,pipeline_status:u.current}),z.current=!0,X.current&&(clearInterval(X.current),X.current=null),_.readyState<=WebSocket.OPEN&&_.close(),void b(1006,"watchdog forced",!1);n.logger.debug({message:"Voice WS keepalive tick",api_name:"voice-ws",generation:e,current_generation:P.current,ready_state:_.readyState,will_send:_.readyState===WebSocket.OPEN}),_.readyState===WebSocket.OPEN&&_.send(JSON.stringify({type:g.PING}))},2e4)};let w=0;_.onmessage=t=>{if(e!==P.current)return;if(t.data instanceof ArrayBuffer){ie.current=Date.now(),oe.current="binary",w++,1===w&&(H.current&&clearInterval(H.current),H.current=setInterval(()=>{w>0&&n.logger.debug({message:"Voice WS audio chunks",api_name:"voice-ws",chunks:w})},2e3)),B.current&&K(B.current,t.data);const e=L.current;if(e.push(performance.now()),e.length>30&&e.shift(),1===w&&W.current){W.current=!1;for(const[e,t]of le.current)t&&ve.current.updateStreamingMessage(e,t)}return void D.current.play(t.data,p.current||void 0)}let r;try{r=JSON.parse(t.data)}catch{return}const s=r.type,i=r.data??r;switch(ie.current=Date.now(),oe.current=s,s){case E.SPEECH_STARTED:if((se.current>0?performance.now()-se.current:1/0)<500)break;if(_.send(JSON.stringify({type:g.STOP_PLAYBACK})),he.current=!0,Se.current=!1,a(O(null)),M.current.stop(),Y.current&&clearTimeout(Y.current),Y.current=setTimeout(()=>{D.current.stop(),Y.current=null},400),a(v(c.LISTENING)),ye(n.INDICATOR_TIMER_ACTIONS.KEEP),Ee.current);else{(u.current===c.SPEAKING||u.current===c.PROCESSING)&&null!=ge.current&&(pe.current.add(ge.current),ge.current=null);for(const e of ue.current.keys())pe.current.add(e);if(W.current){W.current=!1;for(const[e,t]of le.current)t&&ve.current.updateStreamingMessage(e,t)}j(()=>ve.current.commitStreamingMessages(),"barge-in"),Te(),le.current.clear(),Ce()}break;case E.SPEECH_STOPPED:he.current=!1,Se.current=!0,me.current=ge.current,Y.current&&(clearTimeout(Y.current),Y.current=null),a(v(c.PROCESSING)),Ee.current||(a(N(!0)),a(k(null)),Ie());break;case E.STT_RESULT:{const e=i.message_id,t=null!=e&&e===me.current;if(null!=e&&pe.current.has(e)&&!t)break;null==e||t||(ge.current=e);const r=i.text;if(!Se.current&&!t){r&&a(O(r));break}if(Se.current=!1,me.current=null,a(O(null)),r&&r.trim()){const t=e,s={id:`voice-user-${n.generateUniqueId()}`,sender:"user",message:r,timestamp:(new Date).toISOString()};ve.current.setMessages(e=>{if(null!=t){const r=ue.current.get(t);if(r){const t=e.findIndex(e=>e.id===r);if(-1!==t){const r=[...e];return r.splice(t,0,s),r}}}return[...e,s]})}break}case E.TEXT_DELTA:{const e=i.token,t=i.message_id;if(!e||null==t)break;if(pe.current.has(t))break;n.handleSharedEvent({kind:"text",text:e,messageId:t},ke.current);break}case E.STATUS_UPDATE:if(he.current)break;n.handleSharedEvent({kind:"status_update",statusText:i.text},ke.current);break;case E.WIDGET:if(!r.data)break;ye(n.INDICATOR_TIMER_ACTIONS.KEEP),n.handleSharedEvent({kind:"widget",widget:r.data},ke.current);break;case E.PAYMENT:if(!r.data)break;n.handleSharedEvent({kind:"payment",payment:r.data},ke.current);break;case E.CONVERSATION_TITLE:{const e="string"==typeof i.text?i.text.trim():"";e&&n.handleSharedEvent({kind:"conversation_title",title:e},ke.current);break}case E.STATUS_CLEAR:Ee.current=!1,fe.current="",M.current.stop(),ye(n.INDICATOR_TIMER_ACTIONS.KEEP);break;case E.TTS_START:{const e=i.message_id;if(null!=e&&pe.current.has(e)){D.current.stop(),Ae();break}if(ge.current=null,se.current=performance.now(),M.current.pause(),w=0,L.current=[],D.current.stop(),Ae(),B.current=U(o),null!=e&&!ue.current.has(e)){const t=`voice-agent-${n.generateUniqueId()}`;ue.current.set(e,t),le.current.set(t,"")}W.current=!0,ye(Ee.current?n.INDICATOR_TIMER_ACTIONS.KEEP:n.INDICATOR_TIMER_ACTIONS.FINALIZE),a(v(c.SPEAKING));break}case E.TTS_END:if(se.current=0,H.current&&(clearInterval(H.current),H.current=null),w=0,Ae(),i.cancelled&&D.current.stop(),L.current.length>0&&(V.current=q(L.current)),W.current){W.current=!1;for(const[e,t]of le.current)t&&ve.current.updateStreamingMessage(e,t)}j(()=>ve.current.commitStreamingMessages(),"tts-end"),Te();let e=!1;const t=()=>{e||(e=!0,$.current&&(clearTimeout($.current),$.current=null),a(v(c.IDLE)),G.current||(G.current=!0,a(y(!0))),null!=h.current&&M.current.resume(),j(()=>ve.current.commitStreamingMessages(),"drain"),Te(),Ee.current&&(a(N(!0)),a(k(fe.current)),M.current.start(D.current.remainingSeconds)))};D.current.onDrained(t),$.current&&clearTimeout($.current),$.current=setTimeout(t,1e3*(D.current.remainingSeconds+5));break;case E.ERROR:{const e=i.message||"Voice error";n.handleSharedEvent({kind:"error",error:e},ke.current),a(v(c.IDLE)),ye(n.INDICATOR_TIMER_ACTIONS.CANCEL);{const t=S.some(t=>t.test(e)),r=m.some(t=>t.test(e));(t||r)&&(t&&(ee.current=!0),n.logger.warn({message:"Voice WS fatal error — tearing down connection",api_name:"voice-ws",error_message:e,auth_recoverable:t}),_.close())}break}case E.PONG:ce.current=Date.now(),Q.current=0,te.current=0,re.current=0;break;default:n.logger.warn({message:"Voice WS unhandled event",api_name:"voice-ws",event_type:s})}};let x=!1;const b=(i,o,d)=>{if(x)return;if(x=!0,e!==P.current)return void n.logger.debug({message:"Voice WS closed (superseded)",api_name:"voice-ws",code:i,reason:o});const p=Date.now(),g={api_name:"voice-ws",code:i,reason:o||null,was_clean:d,generation:e,ms_since_last_event:p-ie.current,ms_since_last_pong:p-ce.current,last_event_type:oe.current,pipeline_status:u.current};!0===d||1e3===i||1005===i?n.logger.info({message:"Voice WS closed (clean)",...g}):n.logger.warn({message:"Voice WS closed (abnormal)",...g}),X.current&&(clearInterval(X.current),X.current=null),H.current&&(clearInterval(H.current),H.current=null),$.current&&(clearTimeout($.current),$.current=null),a(A(!1)),a(v(c.IDLE)),a(C(s.DISCONNECTED)),ye(n.INDICATOR_TIMER_ACTIONS.CANCEL),j(()=>ve.current.commitStreamingMessages(),"onclose"),Te(),le.current.clear(),Ee.current=!1,Se.current=!1,a(O(null)),fe.current="",me.current=null;const S=!I||ee.current;if(ee.current=!1,S)return void(t===ne.current&&te.current<1&&null!=r&&(te.current+=1,n.logger.info({message:"Voice WS auth-attributed close — refreshing session",api_name:"voice-ws",generation:e,attempt:te.current,last_close_code:i}),(async()=>{const r=await(n.getTokenRefreshHandler()?.());t===ne.current&&(r?we.current():n.logger.warn({message:"Voice WS auth refresh failed — leaving disconnected (no reconnect)",api_name:"voice-ws",generation:e}))})()));const m=z.current||l.has(i);z.current=!1;const h=Q.current>=3,E=m&&!h&&null!=r;m&&h&&n.logger.warn({message:"Voice WS auto-reconnect cap exhausted — giving up",api_name:"voice-ws",generation:e,attempts:Q.current,last_close_code:i}),E&&(Q.current+=1,n.logger.info({message:"Voice WS auto-reconnecting after abnormal close",api_name:"voice-ws",generation:e,attempt:Q.current,delay_ms:1e3,last_close_code:i}),Z.current&&clearTimeout(Z.current),Z.current=setTimeout(()=>{Z.current=null,we.current()},1e3))};_.onclose=e=>b(e.code,e.reason,e.wasClean),_.onerror=()=>{if(e!==P.current)return;const t=Date.now();n.logger.warn({message:"Voice WS error event",api_name:"voice-ws",generation:e,ready_state:_.readyState,ms_since_last_event:t-ie.current,ms_since_last_pong:t-ce.current,last_event_type:oe.current,pipeline_status:u.current}),a(C(s.DISCONNECTED))}},[r,a,Ae,Ie,_e,Ce,ye]),we=e.useRef(Ne);e.useEffect(()=>{we.current=Ne},[Ne]);const xe=e.useCallback(e=>{const t=R.current;t&&t.readyState===WebSocket.OPEN&&t.send(e)},[]),Oe=e.useCallback(e=>{const t=R.current;if(!t||t.readyState!==WebSocket.OPEN)return;t.send(JSON.stringify({type:g.STOP_PLAYBACK})),Re(),t.send(JSON.stringify({type:g.TEXT_MESSAGE,text:e}));const r=`voice-user-${n.generateUniqueId()}`;ve.current.setMessages(t=>[...t,{id:r,sender:"user",message:e,timestamp:(new Date).toISOString()}]),a(N(!0)),a(k(null)),Ie(),a(v(c.PROCESSING))},[a,Re,Ie]),Pe=e.useCallback((e="user")=>{const t=R.current;n.logger.info({message:"Voice WS disconnect() called",api_name:"voice-ws",reason:e,had_ws:null!==t,ready_state:t?.readyState,generation:P.current}),z.current=!1,Z.current&&(clearTimeout(Z.current),Z.current=null),Q.current=0,te.current=0,re.current=0,ne.current+=1,t&&t.readyState<=WebSocket.OPEN&&t.close(),R.current=null,X.current&&(clearInterval(X.current),X.current=null),H.current&&(clearInterval(H.current),H.current=null),Y.current&&(clearTimeout(Y.current),Y.current=null),$.current&&(clearTimeout($.current),$.current=null),pe.current.clear(),ge.current=null,Se.current=!1,me.current=null,he.current=!1,Ee.current=!1,fe.current="",de.current=[],W.current=!1,a(O(null)),Re(),a(A(!1)),a(v(c.IDLE)),a(C(s.DISCONNECTED)),ye(n.INDICATOR_TIMER_ACTIONS.CANCEL)},[a,Re,ye]),be=e.useRef(r),De=e.useRef(!1);e.useEffect(()=>{const e=be.current;if(be.current=r,e===r)return;const t=null!==R.current&&R.current.readyState<=WebSocket.OPEN;t&&(De.current=!0),t&&(ve.current.discardStreamingMessages(),le.current.clear(),de.current=[],Pe("conv-change")),De.current&&null!=r&&(De.current=!1,Ne())},[r,Pe,Ne]);const Me=e.useRef(_);return e.useEffect(()=>{const e=Me.current;if(Me.current=_,e&&!_){const e=R.current;e&&e.readyState<=WebSocket.OPEN&&Pe("logout")}},[_,Pe]),e.useEffect(()=>()=>{X.current&&clearInterval(X.current),H.current&&clearInterval(H.current),Y.current&&clearTimeout(Y.current),$.current&&clearTimeout($.current),Z.current&&clearTimeout(Z.current),W.current=!1,M.current.destroy(),D.current.destroy()},[]),{connect:Ne,disconnect:Pe,sendAudioChunk:xe,sendText:Oe}}function z({micOpen:r,muted:n,onChunk:s}){const c=J(),[i,o]=e.useState(!1),[a,u]=e.useState(!1),l=t.useSelector(e=>e.voice.selectedMicId),d=e.useRef(s);d.current=s;const p=e.useRef(null);return e.useEffect(()=>{if(!r)return;o(!0),u(!1);const e=c.createCapture();return p.current=e,e.open({micDeviceId:l||null,initialMuted:n,onChunk:e=>d.current(e),onLoadingChange:o,onError:()=>u(!0)}),()=>{e.close(),p.current===e&&(p.current=null)}},[r,l]),e.useEffect(()=>{p.current?.setMuted(n)},[n]),{loading:i,errored:a}}function Z(e,r,n){const o=t.useSelector(e=>e.voice.connectionStatus),a=t.useSelector(e=>e.voice.pipelineStatus),u=t.useSelector(e=>e.voice.listeningEnabled),l=t.useSelector(e=>e.voice.muted);return o===s.CONNECTING?i.CONNECTING:o===s.CONNECTED?u?e?i.LOADING:r?i.OFFLINE:n??l?i.MUTED:a===c.PROCESSING?i.PROCESSING:a===c.SPEAKING?i.SPEAKING:a===c.LISTENING?i.LISTENING:i.IDLE:i.READY:i.OFFLINE}const Q=e.createContext(null),ee=e.createContext({strategyActive:!1,voiceSlot:null,onEnterVoice:null,onFloatingOrbClick:null,voiceInputHidden:!1,headerCenterSlot:null,floatOrbPositionRef:null}),te={stop:null},re=r.createListenerMiddleware();re.startListening({actionCreator:P,effect:async e=>{const t=n.getStorageRef();t&&await t.setItem(f.SELECTED_MIC_ID,e.payload)}}),re.startListening({actionCreator:b,effect:async e=>{const t=n.getStorageRef();t&&await t.setItem(f.SELECTED_SPEAKER_ID,e.payload)}}),re.startListening({actionCreator:D,effect:()=>{H()}}),re.startListening({matcher:r.isAnyOf(n.clearSession,n.sessionExpired),effect:async(e,t)=>{const r=n.getStorageRef();r&&await Promise.all([r.removeItem(f.SELECTED_MIC_ID),r.removeItem(f.SELECTED_SPEAKER_ID)]),t.dispatch(D())}});class ne{ctx=null;stream=null;source=null;gain=null;worklet=null;worker=null;visibilityHandler=null;opening=!1;cancelled=!1;opts=null;open(e){this.opening||this.ctx||(this.cancelled=!1,this.opening=!0,this.opts=e,e.onLoadingChange?.(!0),(async()=>{let t=null,r=null,s=null,c=null,i=null,a=null;try{try{t=new AudioContext({sampleRate:o})}catch{t=new AudioContext}if(this.cancelled)return void await t.close().catch(()=>{});if(await t.audioWorklet.addModule(`${n.getBaseUrl()}/audio-recorder.worklet.js`),this.cancelled)return void await t.close().catch(()=>{});if(r=await navigator.mediaDevices.getUserMedia({audio:{...e.micDeviceId?{deviceId:{exact:e.micDeviceId}}:{},echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1,sampleRate:o,echoCancellationType:{ideal:"system"}}}),this.cancelled)return r.getTracks().forEach(e=>e.stop()),void await t.close().catch(()=>{});s=t.createMediaStreamSource(r),c=t.createGain(),c.gain.setValueAtTime(e.initialMuted?0:1,t.currentTime),i=new AudioWorkletNode(t,"audio-recorder"),a=new Worker(`${n.getBaseUrl()}/audio-processor.worker.js`);const u=a;if(i.port.onmessage=e=>{if(this.cancelled)return;const t=e.data;t&&0!==t.length&&u.postMessage({type:"frame",frame:t},[t.buffer])},a.onmessage=e=>{const t=e.data;t&&"chunk"===t.type&&t.buffer instanceof ArrayBuffer&&this.opts?.onChunk(t.buffer)},s.connect(c).connect(i).connect(t.destination),"suspended"===t.state&&await t.resume().catch(()=>{}),this.cancelled)return a.terminate(),i.disconnect(),c.disconnect(),s.disconnect(),r.getTracks().forEach(e=>e.stop()),void await t.close().catch(()=>{});this.ctx=t,this.stream=r,this.source=s,this.gain=c,this.worklet=i,this.worker=a;const l=()=>{"visible"===document.visibilityState&&"suspended"===t.state&&t.resume().catch(()=>{})};document.addEventListener("visibilitychange",l),this.visibilityHandler=l,this.opts?.onLoadingChange?.(!1)}catch(e){n.logger.error({message:"Mic capture failed",api_name:"voice-mic",error_message:e instanceof Error?e.message:String(e)}),t?.close().catch(()=>{}),r?.getTracks().forEach(e=>e.stop()),a?.terminate(),i?.disconnect(),c?.disconnect(),s?.disconnect(),this.cancelled||(this.opts?.onError?.(e),this.opts?.onLoadingChange?.(!1))}finally{this.opening=!1}})())}setMuted(e){const t=this.ctx,r=this.gain;t&&r&&(r.gain.setValueAtTime(e?0:1,t.currentTime),e&&this.worker?.postMessage({type:p}))}close(){this.cancelled=!0;const e=this.worker,t=this.worklet,r=this.gain,n=this.source,s=this.stream,c=this.ctx;e&&(e.postMessage({type:p}),e.terminate()),t?.disconnect(),r?.disconnect(),n?.disconnect(),s?.getTracks().forEach(e=>e.stop()),this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),c?.close().catch(()=>{}),this.ctx=null,this.stream=null,this.source=null,this.gain=null,this.worklet=null,this.worker=null,this.opts=null}}class se{async enumerate(){const e={microphones:[],speakers:[]};if(!navigator.mediaDevices?.enumerateDevices)return e;try{const e=await navigator.mediaDevices.enumerateDevices();let t=0,r=0;return{microphones:e.filter(e=>"audioinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label||"Microphone "+ ++t})),speakers:e.filter(e=>"audiooutput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label||"Speaker "+ ++r}))}}catch{return e}}onChange(e){const t=navigator.mediaDevices;return t?.addEventListener?(t.addEventListener("devicechange",e),()=>t.removeEventListener("devicechange",e)):()=>{}}}class ce{audio=null;timer=null;pendingPlay=null;pendingAction=null;unlock(){this.audio||(this.audio=new Audio(`${n.getBaseUrl()}${u}`),this.audio.loop=!0,this.audio.volume=.5,this.audio.preload="auto",this.audio.load());const e=this.audio.volume;this.audio.volume=0;const t=this.audio.play();t&&"function"==typeof t.then?t.then(()=>{this.pendingPlay?this.audio&&(this.audio.volume=e):(this.audio?.pause(),this.audio&&(this.audio.currentTime=0,this.audio.volume=e))}).catch(()=>{this.audio&&(this.audio.volume=e)}):(this.pendingPlay||(this.audio.pause(),this.audio.currentTime=0),this.audio.volume=e)}start(e){if(this.timer||this.audio&&!this.audio.paused)return;this.stop(),this.audio||(this.audio=new Audio(`${n.getBaseUrl()}${u}`),this.audio.loop=!0,this.audio.volume=.5);const t=Math.round(1e3*e)+300;this.timer=setTimeout(()=>{this.timer=null,this._doPlay()},t)}pause(){this.timer&&(clearTimeout(this.timer),this.timer=null),this.pendingPlay?this.pendingAction="pause":this.audio&&!this.audio.paused&&this.audio.pause()}resume(e=0){if(!this.audio)return;const t=Math.round(1e3*e)+300;this.timer=setTimeout(()=>{this.timer=null,this._doPlay()},t)}stop(){this.timer&&(clearTimeout(this.timer),this.timer=null),this.pendingPlay?this.pendingAction="stop":this.audio&&(this.audio.pause(),this.audio.currentTime=0)}destroy(){this.stop(),this.pendingAction=null,this.audio&&(this.audio.removeAttribute("src"),this.audio=null)}_doPlay(){this.pendingAction=null;const e=this.audio?.play();e instanceof Promise?(this.pendingPlay=e,e.then(()=>{this.pendingPlay=null;const e=this.pendingAction;this.pendingAction=null,"pause"===e?this.audio?.pause():"stop"===e&&this.audio&&(this.audio.pause(),this.audio.currentTime=0)}).catch(()=>{this.pendingPlay=null,this.pendingAction=null})):this.pendingPlay=null}}const ie={createCapture:()=>new ne,createPlayer:()=>new Y,createThinkingSound:()=>new ce,devices:new se,requestMicPermission:async function(){try{return(await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),{granted:!0}}catch(e){return{granted:!1,error:e}}},requiresSoftwareAecMute:!1};exports.AudioPlatformContext=$,exports.BrowserAudioCapture=ne,exports.BrowserAudioDevices=se,exports.BrowserAudioPlayer=Y,exports.BrowserThinkingSound=ce,exports.ORB_STATE_CONFIG={offline:{opacity:.15,playbackRate:.3,scale:1},connecting:{opacity:.4,playbackRate:.5,scale:1},ready:{opacity:.4,playbackRate:.4,scale:1},loading:{opacity:.6,playbackRate:.6,scale:1},idle:{opacity:.6,playbackRate:.6,scale:1},listening:{opacity:.7,playbackRate:.7,scale:1},muted:{opacity:.7,playbackRate:.5,scale:1},processing:{opacity:1,playbackRate:1,scale:1},speaking:{opacity:1,playbackRate:1,scale:1.3}},exports.VOICE_AUTH_ERROR_PATTERNS=S,exports.VOICE_BARGE_IN_DELAY_MS=400,exports.VOICE_CHUNK_SAMPLES=2048,exports.VOICE_CONNECTION_STATUSES=s,exports.VOICE_DISPLAY_STATES=i,exports.VOICE_FATAL_ERROR_PATTERNS=h,exports.VOICE_KEEPALIVE_INTERVAL_MS=2e4,exports.VOICE_PCM_INT16_MAX=a,exports.VOICE_PIPELINE_STATUSES=c,exports.VOICE_PREROLL_DEFAULT_SECONDS=d,exports.VOICE_PREROLL_JITTER_WINDOW=30,exports.VOICE_PREROLL_MAX_SECONDS=.5,exports.VOICE_PREROLL_MIN_SAMPLES=5,exports.VOICE_PREROLL_MIN_SECONDS=.1,exports.VOICE_SAMPLE_RATE=o,exports.VOICE_STORAGE_KEYS=f,exports.VOICE_TERMINAL_ERROR_PATTERNS=m,exports.VOICE_THINKING_SOUND_DELAY_MS=300,exports.VOICE_THINKING_SOUND_PATH=u,exports.VOICE_THINKING_SOUND_VOLUME=.5,exports.VOICE_WS_CLIENT=g,exports.VOICE_WS_SERVER=E,exports.VoiceContext=Q,exports.VoiceUIContext=ee,exports.browserAudioPlatform=ie,exports.clearAudioBuffer=H,exports.computeAdaptivePreroll=q,exports.createAccumulator=U,exports.finalizeAudio=F,exports.getAudio=B,exports.hydrateVoice=e=>async t=>{const[r,n]=await Promise.all([e.getItem(f.SELECTED_MIC_ID),e.getItem(f.SELECTED_SPEAKER_ID)]);r&&t(_.actions.setSelectedMicId(r)),n&&t(_.actions.setSelectedSpeakerId(n))},exports.pushChunk=K,exports.resetIndicatorState=M,exports.setConnectionStatus=C,exports.setError=x,exports.setFirstAssistantMessageComplete=y,exports.setIsAgentWorking=N,exports.setListeningEnabled=A,exports.setMuted=R,exports.setPipelineStatus=v,exports.setResponseStartTime=w,exports.setSelectedMicId=P,exports.setSelectedSpeakerId=b,exports.setStatusText=k,exports.useAudioPlatform=J,exports.useMediaDevices=function(){const r=J(),[n,s]=e.useState([]),[c,i]=e.useState([]),o=t.useSelector(e=>e.voice.listeningEnabled),a=e.useCallback(async()=>{const e=await r.devices.enumerate();s(e.microphones),i(e.speakers)},[r]);return e.useEffect(()=>(a(),r.devices.onChange(a)),[r,a]),e.useEffect(()=>{o&&a()},[o,a]),{microphones:n,speakers:c,refresh:a}},exports.useVoice=()=>{const t=e.useContext(Q);if(!t)throw new Error("useVoice must be used within VoiceProvider");return t},exports.useVoiceAudioPlayback=function(r){const[n,s]=e.useState(!1),c=e.useRef(null),i=e.useRef(null),u=e.useRef(!1),l=t.useSelector(e=>e.voice.selectedSpeakerId),d=e.useRef(l);d.current=l;const p=!!r&&!!B(r),g=e.useCallback(()=>{i.current?.stop(),i.current?.disconnect(),i.current=null,c.current?.close(),c.current=null,u.current=!1,s(!1)},[]),S=e.useCallback(()=>{if(n)return g(),void(te.stop=null);if(!r)return;const e=B(r);if(!e)return;te.stop&&te.stop();const t=d.current,l=new AudioContext({sampleRate:e.sampleRate??o,...t?{sinkId:t}:{}});c.current=l;const p=new Float32Array(e.pcmInt16.length);for(let t=0;t<e.pcmInt16.length;t++)p[t]=e.pcmInt16[t]/a;const S=l.createBuffer(1,p.length,e.sampleRate??o);S.getChannelData(0).set(p);const m=l.createBufferSource();m.buffer=S,m.connect(l.destination),i.current=m,m.onended=()=>{g(),te.stop=null},te.stop=g,u.current=!0,s(!0),m.start()},[n,r,g]);return e.useEffect(()=>()=>{u.current&&(g(),te.stop===g&&(te.stop=null))},[g]),{playing:n,toggle:S,hasAudio:p}},exports.useVoiceDisplayState=Z,exports.useVoiceMicCapture=z,exports.useVoiceProviderValue=({conversationId:r,voiceBridge:i,assistantStartsConversation:o})=>{const a=t.useDispatch(),u=J(),l=t.useSelector(e=>e.voice.muted),d=t.useSelector(e=>e.voice.connectionStatus),p=t.useSelector(e=>e.voice.pipelineStatus),g=t.useSelector(e=>e.voice.isAgentWorking),S=t.useSelector(e=>e.voice.listeningEnabled),m=t.useSelector(e=>e.voice.firstAssistantMessageComplete),[h,E]=e.useState(!1),{connect:f,disconnect:T,sendAudioChunk:I,sendText:_}=X(r,i),{orbMuted:C,effectiveMuted:v}=function(e){const t=e.userMuted||e.assistantStartsConversation&&!e.firstAssistantMessageComplete&&!e.welcomeMuteDismissed;return{orbMuted:t,effectiveMuted:t||!0===e.requiresSoftwareAecMute&&e.pipelineStatus===c.SPEAKING}}({userMuted:l,assistantStartsConversation:o,firstAssistantMessageComplete:m,welcomeMuteDismissed:h,pipelineStatus:p,requiresSoftwareAecMute:u.requiresSoftwareAecMute}),A=o&&!m;e.useEffect(()=>{A&&d===s.CONNECTED||E(!1)},[d,A]);const{loading:y,errored:k}=z({micOpen:S,muted:v,onChunk:I}),N=Z(y,k,C);e.useEffect(()=>(i.setVoiceSendText(_),()=>i.setVoiceSendText(null)),[i,_]);const w=e.useRef(!1);return{displayState:N,pipelineStatus:p,isAgentWorking:g,enterVoiceMode:e.useCallback(async()=>{if(w.current)return n.showErrorToast("Microphone permission changed — please reload the page to apply the new setting."),!1;const e=await u.requestMicPermission();return e.granted?(f(),!0):(n.logger.error({message:"Mic permission denied",api_name:"voice-mic",error_message:e.error?e.error instanceof Error?e.error.message:String(e.error):void 0}),w.current=!0,n.showErrorToast("Microphone access is required for voice mode. Please allow it in your browser's site settings and reload the page."),!1)},[u,f]),exitVoiceMode:e.useCallback(()=>{T()},[T]),toggleMute:e.useCallback(()=>{if(C)return E(!0),void a(R(!1));a(R(!0))},[a,C]),sendText:_,muted:C,micLoading:y,micErrored:k}},exports.useVoiceUI=()=>e.useContext(ee),exports.useVoiceWebSocket=X,exports.voiceListenerMiddleware=re,exports.voiceReducer=L;
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{createContext as e,useContext as t,useRef as r,useCallback as n,useEffect as s,useState as c}from"react";import{useDispatch as i,useSelector as a}from"react-redux";import{createSlice as o,createListenerMiddleware as u,isAnyOf as l}from"@reduxjs/toolkit";import{clearLastResponseTime as d,setLastResponseTime as p,INDICATOR_TIMER_ACTIONS as m,logger as h,generateUniqueId as g,isTokenAboutToExpire as S,readTokenExpiresAt as f,getTokenRefreshHandler as E,readAccessToken as v,handleSharedEvent as T,API_ENDPOINTS as y,getCustomHeaders as I,showErrorToast as _,getStorageRef as w,clearSession as k,sessionExpired as C,getBaseUrl as b}from"@1interface/shared-core";const A={DISCONNECTED:"disconnected",CONNECTING:"connecting",CONNECTED:"connected"},N={IDLE:"idle",LISTENING:"listening",PROCESSING:"processing",SPEAKING:"speaking"},P={OFFLINE:"offline",CONNECTING:"connecting",READY:"ready",LOADING:"loading",IDLE:"idle",LISTENING:"listening",MUTED:"muted",PROCESSING:"processing",SPEAKING:"speaking"},D=24e3,M=32768,O=2048,x=.5,L="/sounds/thinking-sound.mp3",R=300,G=400,W=2e4,V=new Set([1006,1011,1012,1013,1014]),F=.2,K=.1,U=.5,B=30,H=5,$="reset",q={START_CONVERSATION:"start_conversation",TEXT_MESSAGE:"text_message",STOP_PLAYBACK:"stop_playback",PING:"ping"},Y=[/rejected/i,/unauthori[sz]ed/i,/forbidden/i,/\b40[13]\b/],J=[/\b402\b/,/\b429\b/,/rate[\s-]?limit/i],j=[...Y,...J],X={SPEECH_STARTED:"speech_started",SPEECH_STOPPED:"speech_stopped",STT_RESULT:"stt_result",TEXT_DELTA:"text_delta",STATUS_UPDATE:"status_update",STATUS_CLEAR:"clear_status",TTS_START:"tts_start",TTS_END:"tts_end",WIDGET:"widget",PAYMENT:"payment",CONVERSATION_TITLE:"conversation_title",ERROR:"error",PONG:"pong"},Z={SELECTED_MIC_ID:"voice_selectedMicId",SELECTED_SPEAKER_ID:"voice_selectedSpeakerId"},z={offline:{opacity:.15,playbackRate:.3,scale:1},connecting:{opacity:.4,playbackRate:.5,scale:1},ready:{opacity:.4,playbackRate:.4,scale:1},loading:{opacity:.6,playbackRate:.6,scale:1},idle:{opacity:.6,playbackRate:.6,scale:1},listening:{opacity:.7,playbackRate:.7,scale:1},muted:{opacity:.7,playbackRate:.5,scale:1},processing:{opacity:1,playbackRate:1,scale:1},speaking:{opacity:1,playbackRate:1,scale:1.3}};function Q(){return{connectionStatus:A.DISCONNECTED,pipelineStatus:N.IDLE,listeningEnabled:!1,muted:!1,firstAssistantMessageComplete:!1,statusText:null,isAgentWorking:!1,responseStartTime:null,error:null,interimTranscript:null,selectedMicId:"",selectedSpeakerId:""}}const ee=e=>async t=>{const[r,n]=await Promise.all([e.getItem(Z.SELECTED_MIC_ID),e.getItem(Z.SELECTED_SPEAKER_ID)]);r&&t(te.actions.setSelectedMicId(r)),n&&t(te.actions.setSelectedSpeakerId(n))},te=o({name:"voice",initialState:Q(),reducers:{setConnectionStatus(e,t){e.connectionStatus=t.payload},setPipelineStatus(e,t){e.pipelineStatus=t.payload},setListeningEnabled(e,t){e.listeningEnabled=t.payload},setStatusText(e,t){e.statusText=t.payload},setIsAgentWorking(e,t){e.isAgentWorking=t.payload},setResponseStartTime(e,t){e.responseStartTime=t.payload},setError(e,t){e.error=t.payload},setInterimTranscript(e,t){e.interimTranscript=t.payload},setMuted(e,t){e.muted=t.payload},setFirstAssistantMessageComplete(e,t){e.firstAssistantMessageComplete=t.payload},setSelectedMicId(e,t){e.selectedMicId=t.payload},setSelectedSpeakerId(e,t){e.selectedSpeakerId=t.payload},resetVoiceState:()=>Q(),resetIndicatorState(e){e.statusText=null,e.isAgentWorking=!1,e.responseStartTime=null}}}),{setConnectionStatus:re,setPipelineStatus:ne,setListeningEnabled:se,setMuted:ce,setFirstAssistantMessageComplete:ie,setStatusText:ae,setIsAgentWorking:oe,setResponseStartTime:ue,setError:le,setInterimTranscript:de,setSelectedMicId:pe,setSelectedSpeakerId:me,resetVoiceState:he,resetIndicatorState:ge}=te.actions,Se=te.reducer,fe=new Map,Ee=new Map;let ve=0;function Te(e){const t="voice_audio_"+ ++ve;return Ee.set(t,{chunks:[],sampleRate:e}),t}function ye(e,t){const r=Ee.get(e);r&&r.chunks.push(t)}function Ie(e){const t=Ee.get(e);if(!t||0===t.chunks.length)return void Ee.delete(e);const r=t.chunks.reduce((e,t)=>e+t.byteLength,0),n=new Int16Array(r/2);let s=0;for(const e of t.chunks)n.set(new Int16Array(e),s),s+=e.byteLength/2;fe.set(e,{pcmInt16:n,sampleRate:t.sampleRate}),Ee.delete(e)}function _e(e){return fe.get(e)}function we(){fe.clear(),Ee.clear()}function ke(e){if(e.length<5)return F;let t=0,r=0;for(let n=1;n<e.length;n++){const s=e[n]-e[n-1];t+=s,s>r&&(r=s)}return Math.min(.5,Math.max(.1,(r-t/(e.length-1))/1e3))}class Ce{ctx=null;masterGain=null;currentSpeakerId=void 0;nextPlayTime=0;doneTimer=null;isFirstChunk=!0;prerollProvider=null;activeSources=new Set;setPrerollProvider(e){this.prerollProvider=e}play(e,t){this.ctx&&t!==this.currentSpeakerId&&(this.ctx.close(),this.ctx=null,this.masterGain=null),this.ctx||(this.ctx=new AudioContext({sampleRate:D,...t?{sinkId:t}:{}}),this.masterGain=this.ctx.createGain(),this.masterGain.connect(this.ctx.destination),this.currentSpeakerId=t);const r=this.ctx,n=new Int16Array(e),s=new Float32Array(n.length);for(let e=0;e<n.length;e++)s[e]=n[e]/M;const c=r.createBuffer(1,s.length,D);c.getChannelData(0).set(s);const i=r.createBufferSource();i.buffer=c,i.connect(this.masterGain),this.activeSources.add(i),i.onended=()=>this.activeSources.delete(i);const a=r.currentTime;let o=Math.max(a,this.nextPlayTime);if(this.isFirstChunk){this.isFirstChunk=!1;const e=this.prerollProvider?this.prerollProvider():F;o=Math.max(o,a+e)}i.start(o),this.nextPlayTime=o+c.duration}get remainingSeconds(){return this.ctx?Math.max(0,this.nextPlayTime-this.ctx.currentTime):0}onDrained(e){this.doneTimer&&clearTimeout(this.doneTimer);const t=this.remainingSeconds;t<=0?e():this.doneTimer=setTimeout(()=>{this.doneTimer=null,e()},1e3*t)}resetForNewSegment(){this.isFirstChunk=!0}stop(){this.doneTimer&&(clearTimeout(this.doneTimer),this.doneTimer=null);for(const e of this.activeSources)try{e.stop()}catch(e){}this.activeSources.clear(),this.nextPlayTime=0,this.isFirstChunk=!0}destroy(){this.stop(),this.ctx&&(this.ctx.close(),this.ctx=null,this.masterGain=null)}}const be=e(null),Ae=()=>{const e=t(be);if(!e)throw new Error("useAudioPlatform must be used inside <AudioPlatformProvider>. <VoiceProvider> mounts one by default; if you replaced VoiceProvider, you must wire your own.");return e};function Ne(e,t){try{e()}catch(e){h.error({message:"Voice WS bridge call threw",api_name:"voice-ws",context:t,error_message:e instanceof Error?e.message:String(e)})}}function Pe(e,t){const c=i(),o=r(N.IDLE),u=r(""),l=r(null),_=a(e=>e.voice.pipelineStatus),w=a(e=>e.voice.selectedSpeakerId),k=a(e=>e.voice.statusText),C=a(e=>e.auth.accessToken);o.current=_,u.current=w,l.current=k;const b=r(null),P=r(0),M=Ae(),O=r(null);O.current||(O.current=M.createPlayer());const x=r(null);x.current||(x.current=M.createThinkingSound());const L=r([]),R=r(null),G=r(!1),W=r(!1),K=r(null),U=r(null),B=r(null),H=r(null),$=r(null),j=r(!1),Z=r(null),z=r(0),Q=r(!1),ee=r(0),te=r(0),ce=r(0),pe=r(0),me=r(0),he=r(0),ge=r(null),Se=r(null),fe=r(new Map),Ee=r(new Map),ve=r([]),_e=r(new Set),we=r(null),Ce=r(!1),be=r(null),Pe=r(!1),De=r(!1),Me=r(""),Oe=n(()=>{const e=ve.current;0!==e.length&&(ve.current=[],Ne(()=>Ge.current.setMessages(t=>[...t,...e]),"widget-flush"))},[]),xe=n(()=>{if(null!=Se.current)return;const e=Date.now();Se.current=e,c(ue(e)),c(d())},[c]),Le=n(()=>{const e=Se.current;Se.current=null,c(ue(null)),null!=e&&c(p(Date.now()-e))},[c]),Re=n(()=>{Se.current=null,c(ue(null))},[c]),Ge=r(t);Ge.current=t;const We=n(()=>{K.current&&(Ie(K.current),K.current=null)},[c]),Ve=n(()=>{x.current.stop(),We(),O.current.stop()},[We]),Fe=n((e=m.KEEP)=>{c(ae(null)),c(oe(!1)),e===m.FINALIZE?Le():e===m.CANCEL&&Re()},[c,Le,Re]),Ke=r({setStatusText:e=>c(ae(e||null)),setIsAgentWorking:e=>c(oe(e)),appendStreamText:(e,t)=>{if(null==t)return;const r=fe.current.get(t);if(!r)return;const n=(Ee.current.get(r)??"")+e;Ee.current.set(r,n),W.current||Ge.current.updateStreamingMessage(r,n)},pushWidget:e=>{const t=`voice-widget-${g()}`,r={id:t,msg_id:t,sender:"agent",message:"",timestamp:(new Date).toISOString(),widget:[e],message_type:["widget"]};ve.current.push(r)},onPayment:e=>Ge.current.onPaymentInit?.(e),onConversationTitle:e=>Ge.current.onConversationTitle?.(e),onError:e=>{h.error({message:"Voice WS server error",api_name:"voice-ws",error_message:e}),c(le(e))},onStatusActive:e=>{De.current=!0,Me.current=e,xe(),x.current?.start(O.current?.remainingSeconds??0)}}),Ue=n(()=>{if(b.current&&b.current.readyState<=WebSocket.OPEN)return void h.debug({message:"Voice WS connect() skipped — already connecting/open",api_name:"voice-ws",ready_state:b.current.readyState});if(!e)return void h.debug({message:"Voice WS connect() skipped — no conversationId",api_name:"voice-ws"});if(S(f())&&te.current<1){te.current+=1;const e=ce.current;return h.debug({message:"Voice WS refreshing token before connect (about to expire)",api_name:"voice-ws"}),void Promise.resolve(E()?.()).then(()=>{e===ce.current&&Be.current()})}h.debug({message:"Voice WS connecting",api_name:"voice-ws",generation:P.current+1}),x.current.unlock();const t=++P.current,r=ce.current,n=Date.now();me.current=n,he.current=n,ge.current="connecting";const s=v()??"",i=function(e,t){const r="undefined"!=typeof window?window.location?.origin:void 0,n=new URL(y.CONVERSATION_STREAM(e),r);n.protocol="https:"===n.protocol?"wss:":"ws:",t&&n.searchParams.append("access_token",t);const s=function(){const e=I();for(const[t,r]of Object.entries(e))if("x-client-id"===t.toLowerCase()&&r)return r;return null}();return s&&n.searchParams.append("client_id",s),n.toString()}(e,s);let a=!1;const d=new WebSocket(i);b.current=d,c(re(A.CONNECTING)),d.binaryType="arraybuffer",d.onopen=()=>{a=!0,h.debug({message:"Voice WS open",api_name:"voice-ws",generation:t}),c(re(A.CONNECTED)),c(se(!0)),Ge.current.shouldGreetOnConnect()&&(d.send(JSON.stringify({type:q.START_CONVERSATION})),c(oe(!0)),xe()),c(ne(N.IDLE)),fe.current.clear(),_e.current.clear(),we.current=null,Ce.current=!1,be.current=null,W.current=!1,Pe.current=!1,De.current=!1,Me.current="",ve.current=[],L.current=[],R.current=null,G.current=!1,c(ie(!1)),O.current.setPrerollProvider(()=>R.current??F),H.current&&(clearTimeout(H.current),H.current=null);const e=Date.now();me.current=e,he.current=e,ge.current="open",$.current&&clearInterval($.current),$.current=setInterval(()=>{const e=Date.now(),r=e-me.current;if(r>3e4)return h.warn({message:"Voice WS PONG watchdog tripped — closing presumed-dead socket",api_name:"voice-ws",generation:t,ms_since_last_pong:r,ms_since_last_event:e-he.current,last_event_type:ge.current,pipeline_status:o.current}),j.current=!0,$.current&&(clearInterval($.current),$.current=null),d.readyState<=WebSocket.OPEN&&d.close(),void w(1006,"watchdog forced",!1);h.debug({message:"Voice WS keepalive tick",api_name:"voice-ws",generation:t,current_generation:P.current,ready_state:d.readyState,will_send:d.readyState===WebSocket.OPEN}),d.readyState===WebSocket.OPEN&&d.send(JSON.stringify({type:q.PING}))},2e4)};let p=0;d.onmessage=e=>{if(t!==P.current)return;if(e.data instanceof ArrayBuffer){he.current=Date.now(),ge.current="binary",p++,1===p&&(U.current&&clearInterval(U.current),U.current=setInterval(()=>{p>0&&h.debug({message:"Voice WS audio chunks",api_name:"voice-ws",chunks:p})},2e3)),K.current&&ye(K.current,e.data);const t=L.current;if(t.push(performance.now()),t.length>30&&t.shift(),1===p&&W.current){W.current=!1;for(const[e,t]of Ee.current)t&&Ge.current.updateStreamingMessage(e,t)}return void O.current.play(e.data,u.current||void 0)}let r;try{r=JSON.parse(e.data)}catch{return}const n=r.type,s=r.data??r;switch(he.current=Date.now(),ge.current=n,n){case X.SPEECH_STARTED:if((pe.current>0?performance.now()-pe.current:1/0)<500)break;if(d.send(JSON.stringify({type:q.STOP_PLAYBACK})),Pe.current=!0,Ce.current=!1,c(de(null)),x.current.stop(),B.current&&clearTimeout(B.current),B.current=setTimeout(()=>{O.current.stop(),B.current=null},400),c(ne(N.LISTENING)),Fe(m.KEEP),De.current);else{(o.current===N.SPEAKING||o.current===N.PROCESSING)&&null!=we.current&&(_e.current.add(we.current),we.current=null);for(const e of fe.current.keys())_e.current.add(e);if(W.current){W.current=!1;for(const[e,t]of Ee.current)t&&Ge.current.updateStreamingMessage(e,t)}Ne(()=>Ge.current.commitStreamingMessages(),"barge-in"),Oe(),Ee.current.clear(),Re()}break;case X.SPEECH_STOPPED:Pe.current=!1,Ce.current=!0,be.current=we.current,B.current&&(clearTimeout(B.current),B.current=null),c(ne(N.PROCESSING)),De.current||(c(oe(!0)),c(ae(null)),xe());break;case X.STT_RESULT:{const e=s.message_id,t=null!=e&&e===be.current;if(null!=e&&_e.current.has(e)&&!t)break;null==e||t||(we.current=e);const r=s.text;if(!Ce.current&&!t){r&&c(de(r));break}if(Ce.current=!1,be.current=null,c(de(null)),r&&r.trim()){const t=e,n={id:`voice-user-${g()}`,sender:"user",message:r,timestamp:(new Date).toISOString()};Ge.current.setMessages(e=>{if(null!=t){const r=fe.current.get(t);if(r){const t=e.findIndex(e=>e.id===r);if(-1!==t){const r=[...e];return r.splice(t,0,n),r}}}return[...e,n]})}break}case X.TEXT_DELTA:{const e=s.token,t=s.message_id;if(!e||null==t)break;if(_e.current.has(t))break;T({kind:"text",text:e,messageId:t},Ke.current);break}case X.STATUS_UPDATE:if(Pe.current)break;T({kind:"status_update",statusText:s.text},Ke.current);break;case X.WIDGET:if(!r.data)break;Fe(m.KEEP),T({kind:"widget",widget:r.data},Ke.current);break;case X.PAYMENT:if(!r.data)break;T({kind:"payment",payment:r.data},Ke.current);break;case X.CONVERSATION_TITLE:{const e="string"==typeof s.text?s.text.trim():"";e&&T({kind:"conversation_title",title:e},Ke.current);break}case X.STATUS_CLEAR:De.current=!1,Me.current="",x.current.stop(),Fe(m.KEEP);break;case X.TTS_START:{const e=s.message_id;if(null!=e&&_e.current.has(e)){O.current.stop(),We();break}if(we.current=null,pe.current=performance.now(),x.current.pause(),p=0,L.current=[],O.current.stop(),We(),K.current=Te(D),null!=e&&!fe.current.has(e)){const t=`voice-agent-${g()}`;fe.current.set(e,t),Ee.current.set(t,"")}W.current=!0,Fe(De.current?m.KEEP:m.FINALIZE),c(ne(N.SPEAKING));break}case X.TTS_END:if(pe.current=0,U.current&&(clearInterval(U.current),U.current=null),p=0,We(),s.cancelled&&O.current.stop(),L.current.length>0&&(R.current=ke(L.current)),W.current){W.current=!1;for(const[e,t]of Ee.current)t&&Ge.current.updateStreamingMessage(e,t)}Ne(()=>Ge.current.commitStreamingMessages(),"tts-end"),Oe();let e=!1;const t=()=>{e||(e=!0,H.current&&(clearTimeout(H.current),H.current=null),c(ne(N.IDLE)),G.current||(G.current=!0,c(ie(!0))),null!=l.current&&x.current.resume(),Ne(()=>Ge.current.commitStreamingMessages(),"drain"),Oe(),De.current&&(c(oe(!0)),c(ae(Me.current)),x.current.start(O.current.remainingSeconds)))};O.current.onDrained(t),H.current&&clearTimeout(H.current),H.current=setTimeout(t,1e3*(O.current.remainingSeconds+5));break;case X.ERROR:{const e=s.message||"Voice error";T({kind:"error",error:e},Ke.current),c(ne(N.IDLE)),Fe(m.CANCEL);{const t=Y.some(t=>t.test(e)),r=J.some(t=>t.test(e));(t||r)&&(t&&(Q.current=!0),h.warn({message:"Voice WS fatal error — tearing down connection",api_name:"voice-ws",error_message:e,auth_recoverable:t}),d.close())}break}case X.PONG:me.current=Date.now(),z.current=0,ee.current=0,te.current=0;break;default:h.warn({message:"Voice WS unhandled event",api_name:"voice-ws",event_type:n})}};let _=!1;const w=(n,s,i)=>{if(_)return;if(_=!0,t!==P.current)return void h.debug({message:"Voice WS closed (superseded)",api_name:"voice-ws",code:n,reason:s});const u=Date.now(),l={api_name:"voice-ws",code:n,reason:s||null,was_clean:i,generation:t,ms_since_last_event:u-he.current,ms_since_last_pong:u-me.current,last_event_type:ge.current,pipeline_status:o.current};!0===i||1e3===n||1005===n?h.info({message:"Voice WS closed (clean)",...l}):h.warn({message:"Voice WS closed (abnormal)",...l}),$.current&&(clearInterval($.current),$.current=null),U.current&&(clearInterval(U.current),U.current=null),H.current&&(clearTimeout(H.current),H.current=null),c(se(!1)),c(ne(N.IDLE)),c(re(A.DISCONNECTED)),Fe(m.CANCEL),Ne(()=>Ge.current.commitStreamingMessages(),"onclose"),Oe(),Ee.current.clear(),De.current=!1,Ce.current=!1,c(de(null)),Me.current="",be.current=null;const d=!a||Q.current;if(Q.current=!1,d)return void(r===ce.current&&ee.current<1&&null!=e&&(ee.current+=1,h.info({message:"Voice WS auth-attributed close — refreshing session",api_name:"voice-ws",generation:t,attempt:ee.current,last_close_code:n}),(async()=>{const e=await(E()?.());r===ce.current&&(e?Be.current():h.warn({message:"Voice WS auth refresh failed — leaving disconnected (no reconnect)",api_name:"voice-ws",generation:t}))})()));const p=j.current||V.has(n);j.current=!1;const g=z.current>=3,S=p&&!g&&null!=e;p&&g&&h.warn({message:"Voice WS auto-reconnect cap exhausted — giving up",api_name:"voice-ws",generation:t,attempts:z.current,last_close_code:n}),S&&(z.current+=1,h.info({message:"Voice WS auto-reconnecting after abnormal close",api_name:"voice-ws",generation:t,attempt:z.current,delay_ms:1e3,last_close_code:n}),Z.current&&clearTimeout(Z.current),Z.current=setTimeout(()=>{Z.current=null,Be.current()},1e3))};d.onclose=e=>w(e.code,e.reason,e.wasClean),d.onerror=()=>{if(t!==P.current)return;const e=Date.now();h.warn({message:"Voice WS error event",api_name:"voice-ws",generation:t,ready_state:d.readyState,ms_since_last_event:e-he.current,ms_since_last_pong:e-me.current,last_event_type:ge.current,pipeline_status:o.current}),c(re(A.DISCONNECTED))}},[e,c,We,xe,Le,Re,Fe]),Be=r(Ue);s(()=>{Be.current=Ue},[Ue]);const He=n(e=>{const t=b.current;t&&t.readyState===WebSocket.OPEN&&t.send(e)},[]),$e=n(e=>{const t=b.current;if(!t||t.readyState!==WebSocket.OPEN)return;t.send(JSON.stringify({type:q.STOP_PLAYBACK})),Ve(),t.send(JSON.stringify({type:q.TEXT_MESSAGE,text:e}));const r=`voice-user-${g()}`;Ge.current.setMessages(t=>[...t,{id:r,sender:"user",message:e,timestamp:(new Date).toISOString()}]),c(oe(!0)),c(ae(null)),xe(),c(ne(N.PROCESSING))},[c,Ve,xe]),qe=n((e="user")=>{const t=b.current;h.info({message:"Voice WS disconnect() called",api_name:"voice-ws",reason:e,had_ws:null!==t,ready_state:t?.readyState,generation:P.current}),j.current=!1,Z.current&&(clearTimeout(Z.current),Z.current=null),z.current=0,ee.current=0,te.current=0,ce.current+=1,t&&t.readyState<=WebSocket.OPEN&&t.close(),b.current=null,$.current&&(clearInterval($.current),$.current=null),U.current&&(clearInterval(U.current),U.current=null),B.current&&(clearTimeout(B.current),B.current=null),H.current&&(clearTimeout(H.current),H.current=null),_e.current.clear(),we.current=null,Ce.current=!1,be.current=null,Pe.current=!1,De.current=!1,Me.current="",ve.current=[],W.current=!1,c(de(null)),Ve(),c(se(!1)),c(ne(N.IDLE)),c(re(A.DISCONNECTED)),Fe(m.CANCEL)},[c,Ve,Fe]),Ye=r(e),Je=r(!1);s(()=>{const t=Ye.current;if(Ye.current=e,t===e)return;const r=null!==b.current&&b.current.readyState<=WebSocket.OPEN;r&&(Je.current=!0),r&&(Ge.current.discardStreamingMessages(),Ee.current.clear(),ve.current=[],qe("conv-change")),Je.current&&null!=e&&(Je.current=!1,Ue())},[e,qe,Ue]);const je=r(C);return s(()=>{const e=je.current;if(je.current=C,e&&!C){const e=b.current;e&&e.readyState<=WebSocket.OPEN&&qe("logout")}},[C,qe]),s(()=>()=>{$.current&&clearInterval($.current),U.current&&clearInterval(U.current),B.current&&clearTimeout(B.current),H.current&&clearTimeout(H.current),Z.current&&clearTimeout(Z.current),W.current=!1,x.current.destroy(),O.current.destroy()},[]),{connect:Ue,disconnect:qe,sendAudioChunk:He,sendText:$e}}function De({micOpen:e,muted:t,onChunk:n}){const i=Ae(),[o,u]=c(!1),[l,d]=c(!1),p=a(e=>e.voice.selectedMicId),m=r(n);m.current=n;const h=r(null);return s(()=>{if(!e)return;u(!0),d(!1);const r=i.createCapture();return h.current=r,r.open({micDeviceId:p||null,initialMuted:t,onChunk:e=>m.current(e),onLoadingChange:u,onError:()=>d(!0)}),()=>{r.close(),h.current===r&&(h.current=null)}},[e,p]),s(()=>{h.current?.setMuted(t)},[t]),{loading:o,errored:l}}function Me(e,t,r){const n=a(e=>e.voice.connectionStatus),s=a(e=>e.voice.pipelineStatus),c=a(e=>e.voice.listeningEnabled),i=a(e=>e.voice.muted);return n===A.CONNECTING?P.CONNECTING:n===A.CONNECTED?c?e?P.LOADING:t?P.OFFLINE:r??i?P.MUTED:s===N.PROCESSING?P.PROCESSING:s===N.SPEAKING?P.SPEAKING:s===N.LISTENING?P.LISTENING:P.IDLE:P.READY:P.OFFLINE}const Oe=e(null),xe=({conversationId:e,voiceBridge:t,assistantStartsConversation:o})=>{const u=i(),l=Ae(),d=a(e=>e.voice.muted),p=a(e=>e.voice.connectionStatus),m=a(e=>e.voice.pipelineStatus),g=a(e=>e.voice.isAgentWorking),S=a(e=>e.voice.listeningEnabled),f=a(e=>e.voice.firstAssistantMessageComplete),[E,v]=c(!1),{connect:T,disconnect:y,sendAudioChunk:I,sendText:w}=Pe(e,t),{orbMuted:k,effectiveMuted:C}=function(e){const t=e.userMuted||e.assistantStartsConversation&&!e.firstAssistantMessageComplete&&!e.welcomeMuteDismissed;return{orbMuted:t,effectiveMuted:t||!0===e.requiresSoftwareAecMute&&e.pipelineStatus===N.SPEAKING}}({userMuted:d,assistantStartsConversation:o,firstAssistantMessageComplete:f,welcomeMuteDismissed:E,pipelineStatus:m,requiresSoftwareAecMute:l.requiresSoftwareAecMute}),b=o&&!f;s(()=>{b&&p===A.CONNECTED||v(!1)},[p,b]);const{loading:P,errored:D}=De({micOpen:S,muted:C,onChunk:I}),M=Me(P,D,k);s(()=>(t.setVoiceSendText(w),()=>t.setVoiceSendText(null)),[t,w]);const O=r(!1);return{displayState:M,pipelineStatus:m,isAgentWorking:g,enterVoiceMode:n(async()=>{if(O.current)return _("Microphone permission changed — please reload the page to apply the new setting."),!1;const e=await l.requestMicPermission();return e.granted?(T(),!0):(h.error({message:"Mic permission denied",api_name:"voice-mic",error_message:e.error?e.error instanceof Error?e.error.message:String(e.error):void 0}),O.current=!0,_("Microphone access is required for voice mode. Please allow it in your browser's site settings and reload the page."),!1)},[l,T]),exitVoiceMode:n(()=>{y()},[y]),toggleMute:n(()=>{if(k)return v(!0),void u(ce(!1));u(ce(!0))},[u,k]),sendText:w,muted:k,micLoading:P,micErrored:D}},Le=()=>{const e=t(Oe);if(!e)throw new Error("useVoice must be used within VoiceProvider");return e},Re=e({strategyActive:!1,voiceSlot:null,onEnterVoice:null,onFloatingOrbClick:null,voiceInputHidden:!1,headerCenterSlot:null,floatOrbPositionRef:null}),Ge=()=>t(Re),We={stop:null};function Ve(e){const[t,i]=c(!1),o=r(null),u=r(null),l=r(!1),d=a(e=>e.voice.selectedSpeakerId),p=r(d);p.current=d;const m=!!e&&!!_e(e),h=n(()=>{u.current?.stop(),u.current?.disconnect(),u.current=null,o.current?.close(),o.current=null,l.current=!1,i(!1)},[]),g=n(()=>{if(t)return h(),void(We.stop=null);if(!e)return;const r=_e(e);if(!r)return;We.stop&&We.stop();const n=p.current,s=new AudioContext({sampleRate:r.sampleRate??D,...n?{sinkId:n}:{}});o.current=s;const c=new Float32Array(r.pcmInt16.length);for(let e=0;e<r.pcmInt16.length;e++)c[e]=r.pcmInt16[e]/M;const a=s.createBuffer(1,c.length,r.sampleRate??D);a.getChannelData(0).set(c);const d=s.createBufferSource();d.buffer=a,d.connect(s.destination),u.current=d,d.onended=()=>{h(),We.stop=null},We.stop=h,l.current=!0,i(!0),d.start()},[t,e,h]);return s(()=>()=>{l.current&&(h(),We.stop===h&&(We.stop=null))},[h]),{playing:t,toggle:g,hasAudio:m}}function Fe(){const e=Ae(),[t,r]=c([]),[i,o]=c([]),u=a(e=>e.voice.listeningEnabled),l=n(async()=>{const t=await e.devices.enumerate();r(t.microphones),o(t.speakers)},[e]);return s(()=>(l(),e.devices.onChange(l)),[e,l]),s(()=>{u&&l()},[u,l]),{microphones:t,speakers:i,refresh:l}}const Ke=u();Ke.startListening({actionCreator:pe,effect:async e=>{const t=w();t&&await t.setItem(Z.SELECTED_MIC_ID,e.payload)}}),Ke.startListening({actionCreator:me,effect:async e=>{const t=w();t&&await t.setItem(Z.SELECTED_SPEAKER_ID,e.payload)}}),Ke.startListening({actionCreator:he,effect:()=>{we()}}),Ke.startListening({matcher:l(k,C),effect:async(e,t)=>{const r=w();r&&await Promise.all([r.removeItem(Z.SELECTED_MIC_ID),r.removeItem(Z.SELECTED_SPEAKER_ID)]),t.dispatch(he())}});class Ue{ctx=null;stream=null;source=null;gain=null;worklet=null;worker=null;visibilityHandler=null;opening=!1;cancelled=!1;opts=null;open(e){this.opening||this.ctx||(this.cancelled=!1,this.opening=!0,this.opts=e,e.onLoadingChange?.(!0),(async()=>{let t=null,r=null,n=null,s=null,c=null,i=null;try{try{t=new AudioContext({sampleRate:D})}catch{t=new AudioContext}if(this.cancelled)return void await t.close().catch(()=>{});if(await t.audioWorklet.addModule(`${b()}/audio-recorder.worklet.js`),this.cancelled)return void await t.close().catch(()=>{});if(r=await navigator.mediaDevices.getUserMedia({audio:{...e.micDeviceId?{deviceId:{exact:e.micDeviceId}}:{},echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:1,sampleRate:D,echoCancellationType:{ideal:"system"}}}),this.cancelled)return r.getTracks().forEach(e=>e.stop()),void await t.close().catch(()=>{});n=t.createMediaStreamSource(r),s=t.createGain(),s.gain.setValueAtTime(e.initialMuted?0:1,t.currentTime),c=new AudioWorkletNode(t,"audio-recorder"),i=new Worker(`${b()}/audio-processor.worker.js`);const a=i;if(c.port.onmessage=e=>{if(this.cancelled)return;const t=e.data;t&&0!==t.length&&a.postMessage({type:"frame",frame:t},[t.buffer])},i.onmessage=e=>{const t=e.data;t&&"chunk"===t.type&&t.buffer instanceof ArrayBuffer&&this.opts?.onChunk(t.buffer)},n.connect(s).connect(c).connect(t.destination),"suspended"===t.state&&await t.resume().catch(()=>{}),this.cancelled)return i.terminate(),c.disconnect(),s.disconnect(),n.disconnect(),r.getTracks().forEach(e=>e.stop()),void await t.close().catch(()=>{});this.ctx=t,this.stream=r,this.source=n,this.gain=s,this.worklet=c,this.worker=i;const o=()=>{"visible"===document.visibilityState&&"suspended"===t.state&&t.resume().catch(()=>{})};document.addEventListener("visibilitychange",o),this.visibilityHandler=o,this.opts?.onLoadingChange?.(!1)}catch(e){h.error({message:"Mic capture failed",api_name:"voice-mic",error_message:e instanceof Error?e.message:String(e)}),t?.close().catch(()=>{}),r?.getTracks().forEach(e=>e.stop()),i?.terminate(),c?.disconnect(),s?.disconnect(),n?.disconnect(),this.cancelled||(this.opts?.onError?.(e),this.opts?.onLoadingChange?.(!1))}finally{this.opening=!1}})())}setMuted(e){const t=this.ctx,r=this.gain;t&&r&&(r.gain.setValueAtTime(e?0:1,t.currentTime),e&&this.worker?.postMessage({type:$}))}close(){this.cancelled=!0;const e=this.worker,t=this.worklet,r=this.gain,n=this.source,s=this.stream,c=this.ctx;e&&(e.postMessage({type:$}),e.terminate()),t?.disconnect(),r?.disconnect(),n?.disconnect(),s?.getTracks().forEach(e=>e.stop()),this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),c?.close().catch(()=>{}),this.ctx=null,this.stream=null,this.source=null,this.gain=null,this.worklet=null,this.worker=null,this.opts=null}}class Be{async enumerate(){const e={microphones:[],speakers:[]};if(!navigator.mediaDevices?.enumerateDevices)return e;try{const e=await navigator.mediaDevices.enumerateDevices();let t=0,r=0;return{microphones:e.filter(e=>"audioinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label||"Microphone "+ ++t})),speakers:e.filter(e=>"audiooutput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label||"Speaker "+ ++r}))}}catch{return e}}onChange(e){const t=navigator.mediaDevices;return t?.addEventListener?(t.addEventListener("devicechange",e),()=>t.removeEventListener("devicechange",e)):()=>{}}}class He{audio=null;timer=null;pendingPlay=null;pendingAction=null;unlock(){this.audio||(this.audio=new Audio(`${b()}${L}`),this.audio.loop=!0,this.audio.volume=.5,this.audio.preload="auto",this.audio.load());const e=this.audio.volume;this.audio.volume=0;const t=this.audio.play();t&&"function"==typeof t.then?t.then(()=>{this.pendingPlay?this.audio&&(this.audio.volume=e):(this.audio?.pause(),this.audio&&(this.audio.currentTime=0,this.audio.volume=e))}).catch(()=>{this.audio&&(this.audio.volume=e)}):(this.pendingPlay||(this.audio.pause(),this.audio.currentTime=0),this.audio.volume=e)}start(e){if(this.timer||this.audio&&!this.audio.paused)return;this.stop(),this.audio||(this.audio=new Audio(`${b()}${L}`),this.audio.loop=!0,this.audio.volume=.5);const t=Math.round(1e3*e)+300;this.timer=setTimeout(()=>{this.timer=null,this._doPlay()},t)}pause(){this.timer&&(clearTimeout(this.timer),this.timer=null),this.pendingPlay?this.pendingAction="pause":this.audio&&!this.audio.paused&&this.audio.pause()}resume(e=0){if(!this.audio)return;const t=Math.round(1e3*e)+300;this.timer=setTimeout(()=>{this.timer=null,this._doPlay()},t)}stop(){this.timer&&(clearTimeout(this.timer),this.timer=null),this.pendingPlay?this.pendingAction="stop":this.audio&&(this.audio.pause(),this.audio.currentTime=0)}destroy(){this.stop(),this.pendingAction=null,this.audio&&(this.audio.removeAttribute("src"),this.audio=null)}_doPlay(){this.pendingAction=null;const e=this.audio?.play();e instanceof Promise?(this.pendingPlay=e,e.then(()=>{this.pendingPlay=null;const e=this.pendingAction;this.pendingAction=null,"pause"===e?this.audio?.pause():"stop"===e&&this.audio&&(this.audio.pause(),this.audio.currentTime=0)}).catch(()=>{this.pendingPlay=null,this.pendingAction=null})):this.pendingPlay=null}}const $e={createCapture:()=>new Ue,createPlayer:()=>new Ce,createThinkingSound:()=>new He,devices:new Be,requestMicPermission:async function(){try{return(await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),{granted:!0}}catch(e){return{granted:!1,error:e}}},requiresSoftwareAecMute:!1};export{be as AudioPlatformContext,Ue as BrowserAudioCapture,Be as BrowserAudioDevices,Ce as BrowserAudioPlayer,He as BrowserThinkingSound,z as ORB_STATE_CONFIG,Y as VOICE_AUTH_ERROR_PATTERNS,G as VOICE_BARGE_IN_DELAY_MS,O as VOICE_CHUNK_SAMPLES,A as VOICE_CONNECTION_STATUSES,P as VOICE_DISPLAY_STATES,j as VOICE_FATAL_ERROR_PATTERNS,W as VOICE_KEEPALIVE_INTERVAL_MS,M as VOICE_PCM_INT16_MAX,N as VOICE_PIPELINE_STATUSES,F as VOICE_PREROLL_DEFAULT_SECONDS,B as VOICE_PREROLL_JITTER_WINDOW,U as VOICE_PREROLL_MAX_SECONDS,H as VOICE_PREROLL_MIN_SAMPLES,K as VOICE_PREROLL_MIN_SECONDS,D as VOICE_SAMPLE_RATE,Z as VOICE_STORAGE_KEYS,J as VOICE_TERMINAL_ERROR_PATTERNS,R as VOICE_THINKING_SOUND_DELAY_MS,L as VOICE_THINKING_SOUND_PATH,x as VOICE_THINKING_SOUND_VOLUME,q as VOICE_WS_CLIENT,X as VOICE_WS_SERVER,Oe as VoiceContext,Re as VoiceUIContext,$e as browserAudioPlatform,we as clearAudioBuffer,ke as computeAdaptivePreroll,Te as createAccumulator,Ie as finalizeAudio,_e as getAudio,ee as hydrateVoice,ye as pushChunk,ge as resetIndicatorState,re as setConnectionStatus,le as setError,ie as setFirstAssistantMessageComplete,oe as setIsAgentWorking,se as setListeningEnabled,ce as setMuted,ne as setPipelineStatus,ue as setResponseStartTime,pe as setSelectedMicId,me as setSelectedSpeakerId,ae as setStatusText,Ae as useAudioPlatform,Fe as useMediaDevices,Le as useVoice,Ve as useVoiceAudioPlayback,Me as useVoiceDisplayState,De as useVoiceMicCapture,xe as useVoiceProviderValue,Ge as useVoiceUI,Pe as useVoiceWebSocket,Ke as voiceListenerMiddleware,Se as voiceReducer};
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@1interface/voice-core",
3
+ "version": "0.1.0",
4
+ "license": "UNLICENSED",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/aiatcore/1interface-chat.git",
23
+ "directory": "packages/voice-core"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "dependencies": {
29
+ "@1interface/shared-core": "^0.1.0"
30
+ },
31
+ "peerDependencies": {
32
+ "@reduxjs/toolkit": "^2.0.0",
33
+ "react": "^18.0.0 || ^19.0.0",
34
+ "react-dom": "^18.0.0 || ^19.0.0",
35
+ "react-redux": "^9.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@rollup/plugin-terser": "^1.0.0",
39
+ "@testing-library/jest-dom": "^6.9.1",
40
+ "@testing-library/react": "^16.3.2",
41
+ "@types/node": "^24.12.4",
42
+ "@types/react": "^19.2.15",
43
+ "@types/react-dom": "^19.2.3",
44
+ "@vitejs/plugin-react": "5.1.1",
45
+ "@vitest/ui": "4.1.0",
46
+ "jsdom": "^27.4.0",
47
+ "terser": "5.46.1",
48
+ "typescript": "~5.9.3",
49
+ "vite": "7.3.2",
50
+ "vite-plugin-dts": "^4.5.4",
51
+ "vitest": "4.1.0"
52
+ },
53
+ "scripts": {
54
+ "build": "vite build",
55
+ "dev": "vite build --watch",
56
+ "test": "vitest",
57
+ "test:ui": "vitest --ui",
58
+ "postinstall": "rm -rf node_modules/react node_modules/react-dom 2>/dev/null || true"
59
+ }
60
+ }