@lunora/react 1.0.0-alpha.80 → 1.0.0-alpha.81

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.
package/dist/index.d.mts CHANGED
@@ -1266,7 +1266,11 @@ interface UseVoiceAgentOptions {
1266
1266
  * Audio graph stays isolated (and mockable in a non-browser test env).
1267
1267
  */
1268
1268
  createMicrophone?: CreateMicrophone;
1269
- /** Advanced/test seam: open the transport. Defaults to `new WebSocket(url)`. */
1269
+ /**
1270
+ * Advanced/test seam: open the transport. Defaults to the WebSocket
1271
+ * implementation the client was built with (`client.getWebSocketImpl()`),
1272
+ * NOT a raw `globalThis.WebSocket`.
1273
+ */
1270
1274
  createSocket?: CreateSocket;
1271
1275
  /** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
1272
1276
  createSpeaker?: CreateSpeaker;
package/dist/index.d.ts CHANGED
@@ -1266,7 +1266,11 @@ interface UseVoiceAgentOptions {
1266
1266
  * Audio graph stays isolated (and mockable in a non-browser test env).
1267
1267
  */
1268
1268
  createMicrophone?: CreateMicrophone;
1269
- /** Advanced/test seam: open the transport. Defaults to `new WebSocket(url)`. */
1269
+ /**
1270
+ * Advanced/test seam: open the transport. Defaults to the WebSocket
1271
+ * implementation the client was built with (`client.getWebSocketImpl()`),
1272
+ * NOT a raw `globalThis.WebSocket`.
1273
+ */
1270
1274
  createSocket?: CreateSocket;
1271
1275
  /** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
1272
1276
  createSpeaker?: CreateSpeaker;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  'use client';
2
- import{AuthLoading as o,Authenticated as t,Unauthenticated as u}from"./packem_shared/AuthLoading-dC08yqew.mjs";import{useAuthState as a}from"./packem_shared/useAuthState-CoOG9XdT.mjs";import{LunoraProvider as f,useLunora as n}from"./packem_shared/LunoraProvider-DC06S-p1.mjs";import{lunoraQueryOptions as m}from"./packem_shared/lunoraQueryOptions-B5PEocLy.mjs";import{useAction as x}from"./packem_shared/useAction-CUSJu4yq.mjs";import{useAuthSessions as A,useAuthUsers as g,useImpersonate as h,useOrganizations as c}from"./packem_shared/useAuthSessions-Bq4-f3g-.mjs";import{useAgent as y}from"./packem_shared/useAgent-CNDRZNk_.mjs";import{useAgentChat as E}from"./packem_shared/useAgentChat-CXJldvLG.mjs";import{useAgentState as S}from"./packem_shared/useAgentState-BOIQjgxe.mjs";import{useAgentToolEvents as L}from"./packem_shared/useAgentToolEvents-B4gy1Q5z.mjs";import{default as F}from"./packem_shared/useAuth-x4boOoAR.mjs";import{default as I}from"./packem_shared/useClientQuery-DFs7trEf.mjs";import{default as v}from"./packem_shared/useConnectionStatus-XYsYGiT6.mjs";import{useFlag as O,useFlags as T}from"./packem_shared/useFlag-BDM923ke.mjs";import{useHttpStream as H}from"./packem_shared/useHttpStream-D9vum8Qx.mjs";import{default as j}from"./packem_shared/useInfiniteQuery-C7wp0W2d.mjs";import{useMutation as w}from"./packem_shared/useMutation-BnKec7bE.mjs";import{useMutator as D}from"./packem_shared/useMutator-BtUd-xEA.mjs";import{usePaginatedQuery as J}from"./packem_shared/usePaginatedQuery-DJNa8KKD.mjs";import{hydratePreloaded as N,default as W}from"./packem_shared/hydratePreloaded-CcBW4phD.mjs";import{usePresence as Y}from"./packem_shared/usePresence-BVjZyltT.mjs";import{default as _}from"./packem_shared/useQuery-BUzNP_Vb.mjs";import{useRateLimit as ee}from"./packem_shared/useRateLimit-Cckxi8m9.mjs";import{useStream as oe}from"./packem_shared/useStream-CmGOT0qj.mjs";import{default as ue}from"./packem_shared/useSubscription-BHVICtbD.mjs";import{useVoiceAgent as ae}from"./packem_shared/useVoiceAgent-BziH3D-1.mjs";import{createClientQuery as fe,getErrorCode as ne,getRetryAfterMs as ie,isConflictError as me,isForbiddenError as de,isRateLimitedError as xe,isUnauthorizedError as le}from"@lunora/client";import{RestrictionError as ge,UploadControl as he,UploadError as ce}from"@visulima/storage-client";import{useChunkedRestUpload as ye,useFileInput as Ce,useMultipartUpload as Ee,usePasteUpload as Qe,useTusUpload as Se,useUpload as Pe}from"@visulima/storage-client/react";export{o as AuthLoading,t as Authenticated,f as LunoraProvider,ge as RestrictionError,u as Unauthenticated,he as UploadControl,ce as UploadError,fe as createClientQuery,ne as getErrorCode,ie as getRetryAfterMs,N as hydratePreloaded,me as isConflictError,de as isForbiddenError,xe as isRateLimitedError,le as isUnauthorizedError,m as lunoraQueryOptions,x as useAction,y as useAgent,E as useAgentChat,S as useAgentState,L as useAgentToolEvents,F as useAuth,A as useAuthSessions,a as useAuthState,g as useAuthUsers,ye as useChunkedRestUpload,I as useClientQuery,v as useConnectionStatus,Ce as useFileInput,O as useFlag,T as useFlags,H as useHttpStream,h as useImpersonate,j as useInfiniteQuery,n as useLunora,Ee as useMultipartUpload,w as useMutation,D as useMutator,c as useOrganizations,J as usePaginatedQuery,Qe as usePasteUpload,W as usePreloadedQuery,Y as usePresence,_ as useQuery,ee as useRateLimit,oe as useStream,ue as useSubscription,Se as useTusUpload,Pe as useUpload,ae as useVoiceAgent};
2
+ import{AuthLoading as o,Authenticated as t,Unauthenticated as u}from"./packem_shared/AuthLoading-dC08yqew.mjs";import{useAuthState as a}from"./packem_shared/useAuthState-CoOG9XdT.mjs";import{LunoraProvider as f,useLunora as n}from"./packem_shared/LunoraProvider-DC06S-p1.mjs";import{lunoraQueryOptions as m}from"./packem_shared/lunoraQueryOptions-B5PEocLy.mjs";import{useAction as x}from"./packem_shared/useAction-CUSJu4yq.mjs";import{useAuthSessions as A,useAuthUsers as g,useImpersonate as h,useOrganizations as c}from"./packem_shared/useAuthSessions-Bq4-f3g-.mjs";import{useAgent as y}from"./packem_shared/useAgent-CNDRZNk_.mjs";import{useAgentChat as E}from"./packem_shared/useAgentChat-CXJldvLG.mjs";import{useAgentState as S}from"./packem_shared/useAgentState-BOIQjgxe.mjs";import{useAgentToolEvents as L}from"./packem_shared/useAgentToolEvents-B4gy1Q5z.mjs";import{default as F}from"./packem_shared/useAuth-x4boOoAR.mjs";import{default as I}from"./packem_shared/useClientQuery-DFs7trEf.mjs";import{default as v}from"./packem_shared/useConnectionStatus-XYsYGiT6.mjs";import{useFlag as O,useFlags as T}from"./packem_shared/useFlag-BDM923ke.mjs";import{useHttpStream as H}from"./packem_shared/useHttpStream-D9vum8Qx.mjs";import{default as j}from"./packem_shared/useInfiniteQuery-C7wp0W2d.mjs";import{useMutation as w}from"./packem_shared/useMutation-BnKec7bE.mjs";import{useMutator as D}from"./packem_shared/useMutator-BtUd-xEA.mjs";import{usePaginatedQuery as J}from"./packem_shared/usePaginatedQuery-DJNa8KKD.mjs";import{hydratePreloaded as N,default as W}from"./packem_shared/hydratePreloaded-CcBW4phD.mjs";import{usePresence as Y}from"./packem_shared/usePresence-BVjZyltT.mjs";import{default as _}from"./packem_shared/useQuery-BUzNP_Vb.mjs";import{useRateLimit as ee}from"./packem_shared/useRateLimit-Cckxi8m9.mjs";import{useStream as oe}from"./packem_shared/useStream-CmGOT0qj.mjs";import{default as ue}from"./packem_shared/useSubscription-BHVICtbD.mjs";import{useVoiceAgent as ae}from"./packem_shared/useVoiceAgent-C9NI6Vzl.mjs";import{createClientQuery as fe,getErrorCode as ne,getRetryAfterMs as ie,isConflictError as me,isForbiddenError as de,isRateLimitedError as xe,isUnauthorizedError as le}from"@lunora/client";import{RestrictionError as ge,UploadControl as he,UploadError as ce}from"@visulima/storage-client";import{useChunkedRestUpload as ye,useFileInput as Ce,useMultipartUpload as Ee,usePasteUpload as Qe,useTusUpload as Se,useUpload as Pe}from"@visulima/storage-client/react";export{o as AuthLoading,t as Authenticated,f as LunoraProvider,ge as RestrictionError,u as Unauthenticated,he as UploadControl,ce as UploadError,fe as createClientQuery,ne as getErrorCode,ie as getRetryAfterMs,N as hydratePreloaded,me as isConflictError,de as isForbiddenError,xe as isRateLimitedError,le as isUnauthorizedError,m as lunoraQueryOptions,x as useAction,y as useAgent,E as useAgentChat,S as useAgentState,L as useAgentToolEvents,F as useAuth,A as useAuthSessions,a as useAuthState,g as useAuthUsers,ye as useChunkedRestUpload,I as useClientQuery,v as useConnectionStatus,Ce as useFileInput,O as useFlag,T as useFlags,H as useHttpStream,h as useImpersonate,j as useInfiniteQuery,n as useLunora,Ee as useMultipartUpload,w as useMutation,D as useMutator,c as useOrganizations,J as usePaginatedQuery,Qe as usePasteUpload,W as usePreloadedQuery,Y as usePresence,_ as useQuery,ee as useRateLimit,oe as useStream,ue as useSubscription,Se as useTusUpload,Pe as useUpload,ae as useVoiceAgent};
@@ -0,0 +1,2 @@
1
+ 'use client';
2
+ import{useState as w,useRef as N,useCallback as E,useEffect as G}from"react";import{useLunora as X}from"./LunoraProvider-DC06S-p1.mjs";const O="/_lunora/ws",j="/_lunora/voice/",z=4001,P=e=>e.startsWith("https://")?`wss://${e.slice(8)}`:e.startsWith("http://")?`ws://${e.slice(7)}`:e,Q=e=>{const n=e.startsWith("agents:")?e.slice(7):e;return n.endsWith("Voice")?n.slice(0,-5):n},Y=(e,n)=>{if(n===void 0||n==="")return P(e);if(n.endsWith(O))return n.slice(0,-O.length);try{return new URL(n).origin}catch{return P(e)}},Z=e=>{const n=Y(e.httpUrl,e.wsUrl),s=n.endsWith("/")?n.slice(0,-1):n,i=new URLSearchParams({threadKey:e.threadKey});return`${s}${j}${encodeURIComponent(e.agent)}?${i.toString()}`},ee=(e,n)=>{if(n?.code===z)return new Error(`${e}: authentication token expired — refresh the credential and start a new call`)},te=16e3,ne=e=>{if(e.length===0)return 0;let n=0;for(const s of e)n+=s*s;return Math.sqrt(n/e.length)},re=(e,n)=>{const s=n/te,i=s>1?Math.floor(e.length/s):e.length,d=new ArrayBuffer(i*2),l=new DataView(d);for(let a=0;a<i;a+=1){const k=e[Math.floor(a*s)]??0,p=Math.max(-1,Math.min(1,k));l.setInt16(a*2,p<0?p*32768:p*32767,!0)}return new Uint8Array(d)},oe=async e=>{const n=globalThis,s=n.navigator?.mediaDevices?.getUserMedia.bind(n.navigator.mediaDevices),i=n.AudioContext??n.webkitAudioContext;if(!s||!i)throw new Error("useVoiceAgent: microphone capture requires getUserMedia + AudioContext (no browser audio available)");const d=await s({audio:{channelCount:1,echoCancellation:!0,noiseSuppression:!0}}),l=new i,a=l.createMediaStreamSource(d),k=l.createScriptProcessor(4096,1,1);let p=!1,g=!1,u=0,h=0;return k.onaudioprocess=o=>{const f=o.inputBuffer.getChannelData(0),m=p?0:ne(f);if(e.onLevel(m),p)return;if(e.onAudio(re(f,l.sampleRate)),e.isSpeaking()){h=m>=e.interruptThreshold?h+1:0,h>=e.interruptChunks&&(h=0,e.onInterrupt());return}h=0;const x=f.length/l.sampleRate*1e3;if(m>=e.silenceThreshold){g=!0,u=0;return}g&&(u+=x,u>=e.silenceDurationMs&&(g=!1,u=0,e.onSilence()))},a.connect(k),k.connect(l.destination),{setMuted:o=>{p=o},stop:()=>{k.disconnect(),a.disconnect();for(const o of d.getTracks())o.stop();l.close()}}},se=()=>{const e=globalThis,n=e.AudioContext??e.webkitAudioContext;if(!n)throw new Error("useVoiceAgent: audio playback requires AudioContext (no browser audio available)");const s=new n,i=new Set;let d=0,l=Promise.resolve(),a=0;const k=async(u,h)=>{if(h!==a)return;let o;try{o=await s.decodeAudioData(u.buffer)}catch{return}if(h!==a)return;const f=s.createBufferSource();f.buffer=o,f.connect(s.destination);const m=Math.max(s.currentTime,d);f.start(m),d=m+o.duration,i.add(f),f.onended=()=>{i.delete(f)}},p=u=>{const h=Uint8Array.from(u),o=a;l=l.then(()=>k(h,o))},g=()=>{a+=1;for(const u of i)try{u.stop()}catch{}i.clear(),d=s.currentTime};return{enqueue:p,interrupt:g,stop:()=>{g(),s.close()}}},V=1,ce=.01,ie=1200,ae=.15,ue=3,pe=e=>{const{createMicrophone:n=oe,createSpeaker:s=se,createSocket:i,interruptChunks:d=ue,interruptThreshold:l=ae,silenceDurationMs:a=ie,silenceThreshold:k=ce,threadKey:p,voice:g}=e,u=X(),[h,o]=w("idle"),[f,m]=w(!1),[x,R]=w(""),[H,v]=w(""),[$,U]=w(0),[M,L]=w(!1),[B,C]=w(void 0),c=N(void 0),_=N(!1),T=E(t=>{const r=c.current?.socket;return r?.readyState===V?(r.send(JSON.stringify(t)),!0):!1},[]),y=E(()=>{const t=c.current;if(c.current=void 0,t){t.microphone?.stop(),t.speaker?.stop(),t.socket.onmessage=null,t.socket.onerror=null,t.socket.onclose=null;try{t.socket.close()}catch{}}_.current=!1,m(!1),o("idle"),U(0)},[]),D=E(t=>{const r=c.current;switch(t.type){case"assistant_delta":{r&&(r.speaking=!0),o("speaking"),v(F=>F+t.text);break}case"assistant_done":{r&&(r.speaking=!1),v(t.text),o("listening");break}case"error":{r&&(r.speaking=!1),C(new Error(t.message)),o("listening");break}case"interrupted":{r&&(r.speaking=!1,r.suppressAudio=!1),r?.speaker?.interrupt(),o("listening");break}case"ready":{r&&(r.audioFormat=t.audioFormat,r.suppressAudio=!1),m(!0),o("listening");break}case"user_transcript":{r&&(r.suppressAudio=!1),R(t.text),v(""),o("thinking");break}}},[]),I=E(t=>{const r=c.current;!r||r.suppressAudio||(r.speaker??=s({audioFormat:r.audioFormat}),r.speaking=!0,o("speaking"),r.speaker.enqueue(t))},[s]),q=E(async()=>{if(c.current||_.current)return;_.current=!0,C(void 0),R(""),v("");let t;try{const r=Z({agent:Q(g.__lunoraRef),httpUrl:u.url,threadKey:p,wsUrl:u.wsUrl}),A=(i??(S=>{const b=u.getWebSocketImpl();if(!b)throw new Error("useVoiceAgent: no WebSocket implementation available (pass createSocket explicitly)");return new b(S)}))(r);A.binaryType="arraybuffer",t={audioFormat:"mp3",microphone:void 0,socket:A,speaker:void 0,speaking:!1,suppressAudio:!1},c.current=t,A.onmessage=S=>{if(typeof S.data=="string"){try{D(JSON.parse(S.data))}catch{}return}I(new Uint8Array(S.data))},A.onerror=()=>{C(new Error("useVoiceAgent: voice socket error"))},A.onclose=S=>{if(c.current!==t)return;const b=ee("useVoiceAgent",S);b&&C(b),y()};const W=await n({interruptChunks:d,interruptThreshold:l,isSpeaking:()=>c.current?.speaking??!1,onAudio:S=>{A.readyState===V&&A.send(S)},onInterrupt:()=>{T({type:"interrupt"}),c.current?.speaker?.interrupt(),c.current&&(c.current.speaking=!1,c.current.suppressAudio=!0),o("listening")},onLevel:U,onSilence:()=>{T({type:"commit"}),o("thinking")},silenceDurationMs:a,silenceThreshold:k});c.current===t?(t.microphone=W,L(!1),t.speaking||o("listening")):W.stop()}catch(r){c.current===t&&(C(r instanceof Error?r:new Error(String(r))),y())}finally{_.current=!1}},[u,n,i,I,D,d,l,T,a,k,y,p,g]),K=E(()=>{const t=!M;return c.current?.microphone?.setMuted(t),L(t),t},[M]),J=E(t=>{T({text:t,type:"text"})&&o("thinking")},[T]);return G(()=>y,[y]),{audioLevel:$,connected:f,endCall:y,error:B,interimTranscript:H,isMuted:M,sendText:J,startCall:q,status:h,toggleMute:K,transcript:x}};export{pe as useVoiceAgent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/react",
3
- "version": "1.0.0-alpha.80",
3
+ "version": "1.0.0-alpha.81",
4
4
  "description": "React hooks for Lunora: useQuery, useMutation, useSubscription, and useAuth",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -1,2 +0,0 @@
1
- 'use client';
2
- import{useState as w,useRef as W,useCallback as b,useEffect as J}from"react";import{useLunora as K}from"./LunoraProvider-DC06S-p1.mjs";const G=16e3,j=e=>{if(e.length===0)return 0;let o=0;for(const r of e)o+=r*r;return Math.sqrt(o/e.length)},z=(e,o)=>{const r=o/G,c=r>1?Math.floor(e.length/r):e.length,l=new ArrayBuffer(c*2),i=new DataView(l);for(let a=0;a<c;a+=1){const k=e[Math.floor(a*r)]??0,p=Math.max(-1,Math.min(1,k));i.setInt16(a*2,p<0?p*32768:p*32767,!0)}return new Uint8Array(l)},Q=async e=>{const o=globalThis,r=o.navigator?.mediaDevices?.getUserMedia.bind(o.navigator.mediaDevices),c=o.AudioContext??o.webkitAudioContext;if(!r||!c)throw new Error("useVoiceAgent: microphone capture requires getUserMedia + AudioContext (no browser audio available)");const l=await r({audio:{channelCount:1,echoCancellation:!0,noiseSuppression:!0}}),i=new c,a=i.createMediaStreamSource(l),k=i.createScriptProcessor(4096,1,1);let p=!1,g=!1,d=0,f=0;return k.onaudioprocess=s=>{const h=s.inputBuffer.getChannelData(0),m=p?0:j(h);if(e.onLevel(m),p)return;if(e.onAudio(z(h,i.sampleRate)),e.isSpeaking()){f=m>=e.interruptThreshold?f+1:0,f>=e.interruptChunks&&(f=0,e.onInterrupt());return}f=0;const x=h.length/i.sampleRate*1e3;if(m>=e.silenceThreshold){g=!0,d=0;return}g&&(d+=x,d>=e.silenceDurationMs&&(g=!1,d=0,e.onSilence()))},a.connect(k),k.connect(i.destination),{setMuted:s=>{p=s},stop:()=>{k.disconnect(),a.disconnect();for(const s of l.getTracks())s.stop();i.close()}}},X=()=>{const e=globalThis,o=e.AudioContext??e.webkitAudioContext;if(!o)throw new Error("useVoiceAgent: audio playback requires AudioContext (no browser audio available)");const r=new o,c=new Set;let l=0,i=Promise.resolve(),a=0;const k=async(d,f)=>{if(f!==a)return;let s;try{s=await r.decodeAudioData(d.buffer)}catch{return}if(f!==a)return;const h=r.createBufferSource();h.buffer=s,h.connect(r.destination);const m=Math.max(r.currentTime,l);h.start(m),l=m+s.duration,c.add(h),h.onended=()=>{c.delete(h)}},p=d=>{const f=Uint8Array.from(d),s=a;i=i.then(()=>k(f,s))},g=()=>{a+=1;for(const d of c)try{d.stop()}catch{}c.clear(),l=r.currentTime};return{enqueue:p,interrupt:g,stop:()=>{g(),r.close()}}},P=1,Y=.01,Z=1200,ee=.15,te=3,ne=e=>e.startsWith("https://")?`wss://${e.slice(8)}`:e.startsWith("http://")?`ws://${e.slice(7)}`:e,re=e=>{const o=e.__lunoraRef,r=o.startsWith("agents:")?o.slice(7):o;return r.endsWith("Voice")?r.slice(0,-5):r},se=(e,o,r)=>{const c=ne(e),l=c.endsWith("/")?c.slice(0,-1):c,i=new URLSearchParams({threadKey:r});return`${l}/_lunora/voice/${encodeURIComponent(o)}?${i.toString()}`},ie=e=>{const{createMicrophone:o=Q,createSpeaker:r=X,createSocket:c,interruptChunks:l=te,interruptThreshold:i=ee,silenceDurationMs:a=Z,silenceThreshold:k=Y,threadKey:p,voice:g}=e,d=K(),[f,s]=w("idle"),[h,m]=w(!1),[x,U]=w(""),[V,C]=w(""),[H,_]=w(0),[R,L]=w(!1),[O,v]=w(void 0),u=W(void 0),E=W(!1),T=b(t=>{const n=u.current?.socket;return n?.readyState===P?(n.send(JSON.stringify(t)),!0):!1},[]),y=b(()=>{const t=u.current;if(u.current=void 0,t){t.microphone?.stop(),t.speaker?.stop();try{t.socket.close()}catch{}}E.current=!1,m(!1),s("idle"),_(0)},[]),D=b(t=>{const n=u.current;switch(t.type){case"assistant_delta":{n&&(n.speaking=!0),s("speaking"),C(S=>S+t.text);break}case"assistant_done":{n&&(n.speaking=!1),C(t.text),s("listening");break}case"error":{n&&(n.speaking=!1),v(new Error(t.message)),s("listening");break}case"interrupted":{n&&(n.speaking=!1,n.suppressAudio=!1),n?.speaker?.interrupt(),s("listening");break}case"ready":{n&&(n.audioFormat=t.audioFormat,n.suppressAudio=!1),m(!0),s("listening");break}case"user_transcript":{n&&(n.suppressAudio=!1),U(t.text),C(""),s("thinking");break}}},[]),F=b(t=>{const n=u.current;!n||n.suppressAudio||(n.speaker??=r({audioFormat:n.audioFormat}),n.speaking=!0,s("speaking"),n.speaker.enqueue(t))},[r]),q=b(async()=>{if(!(u.current||E.current)){E.current=!0,v(void 0),U(""),C("");try{const t=se(d.url,re(g),p),S=(c??(A=>{const N=d.getWebSocketImpl();if(!N)throw new Error("useVoiceAgent: no WebSocket implementation available (pass createSocket explicitly)");return new N(A)}))(t);S.binaryType="arraybuffer";const M={audioFormat:"mp3",microphone:void 0,socket:S,speaker:void 0,speaking:!1,suppressAudio:!1};u.current=M,S.onmessage=A=>{if(typeof A.data=="string"){try{D(JSON.parse(A.data))}catch{}return}F(new Uint8Array(A.data))},S.onerror=()=>{v(new Error("useVoiceAgent: voice socket error"))},S.onclose=()=>{u.current===M&&y()};const I=await o({interruptChunks:l,interruptThreshold:i,isSpeaking:()=>u.current?.speaking??!1,onAudio:A=>{S.readyState===P&&S.send(A)},onInterrupt:()=>{T({type:"interrupt"}),u.current?.speaker?.interrupt(),u.current&&(u.current.speaking=!1,u.current.suppressAudio=!0),s("listening")},onLevel:_,onSilence:()=>{T({type:"commit"}),s("thinking")},silenceDurationMs:a,silenceThreshold:k});u.current===M?(M.microphone=I,L(!1),s("listening")):I.stop()}catch(t){v(t instanceof Error?t:new Error(String(t))),y()}finally{E.current=!1}}},[d,o,c,F,D,l,i,T,a,k,y,p,g]),B=b(()=>{const t=!R;return u.current?.microphone?.setMuted(t),L(t),t},[R]),$=b(t=>{T({text:t,type:"text"})&&s("thinking")},[T]);return J(()=>y,[y]),{audioLevel:H,connected:h,endCall:y,error:O,interimTranscript:V,isMuted:R,sendText:$,startCall:q,status:f,toggleMute:B,transcript:x}};export{ie as useVoiceAgent};