@lunora/svelte 1.0.0-alpha.109 → 1.0.0-alpha.110

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
@@ -1101,7 +1101,11 @@ interface VoiceAgentOptions {
1101
1101
  * Audio graph stays isolated (and mockable in a non-browser test env).
1102
1102
  */
1103
1103
  createMicrophone?: CreateMicrophone;
1104
- /** Advanced/test seam: open the transport. Defaults to `new WebSocket(url)`. */
1104
+ /**
1105
+ * Advanced/test seam: open the transport. Defaults to the WebSocket
1106
+ * implementation the client was built with (`client.getWebSocketImpl()`),
1107
+ * NOT a raw `globalThis.WebSocket`.
1108
+ */
1105
1109
  createSocket?: CreateSocket;
1106
1110
  /** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
1107
1111
  createSpeaker?: CreateSpeaker;
package/dist/index.d.ts CHANGED
@@ -1101,7 +1101,11 @@ interface VoiceAgentOptions {
1101
1101
  * Audio graph stays isolated (and mockable in a non-browser test env).
1102
1102
  */
1103
1103
  createMicrophone?: CreateMicrophone;
1104
- /** Advanced/test seam: open the transport. Defaults to `new WebSocket(url)`. */
1104
+ /**
1105
+ * Advanced/test seam: open the transport. Defaults to the WebSocket
1106
+ * implementation the client was built with (`client.getWebSocketImpl()`),
1107
+ * NOT a raw `globalThis.WebSocket`.
1108
+ */
1105
1109
  createSocket?: CreateSocket;
1106
1110
  /** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
1107
1111
  createSpeaker?: CreateSpeaker;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{action as t}from"./packem_shared/action-SjeZUGhX.mjs";import{agent as a}from"./packem_shared/agent-WMVWf1Ks.mjs";import{agentChat as f}from"./packem_shared/agentChat-CJ048ZZl.mjs";import{agentState as n}from"./packem_shared/agentState-CAqcW2tF.mjs";import{agentToolEvents as i}from"./packem_shared/agentToolEvents-Dyu5BFZn.mjs";import{auth as g,authGate as s}from"./packem_shared/auth-Bew4W5n1.mjs";import{connectionStatus as l}from"./packem_shared/connectionStatus-D9A79Eqw.mjs";import{getLunoraClient as h,setLunoraClient as y}from"./packem_shared/getLunoraClient-DU7BmZy1.mjs";import{flag as L,flags as v}from"./packem_shared/flag-Csh9LBiO.mjs";import{hydratePreloaded as S}from"./packem_shared/hydratePreloaded-Douu4xsv.mjs";import{mutation as q}from"./packem_shared/mutation-DLBr5Op1.mjs";import{mutator as E}from"./packem_shared/mutator-DECDuo94.mjs";import{infiniteQuery as P,paginatedQuery as T}from"./packem_shared/infiniteQuery-CwF_9I0J.mjs";import{presence as k}from"./packem_shared/presence-BcBuOI0s.mjs";import{query as z}from"./packem_shared/query-BMl8EN8S.mjs";import{rateLimit as D}from"./packem_shared/rateLimit-Cli38qWO.mjs";import{stream as H}from"./packem_shared/stream-DGLI262b.mjs";import{subscription as J}from"./packem_shared/subscription-B1x2xDOl.mjs";import{voiceAgent as M}from"./packem_shared/voiceAgent-DsY2vCkg.mjs";export{t as action,a as agent,f as agentChat,n as agentState,i as agentToolEvents,g as auth,s as authGate,l as connectionStatus,L as flag,v as flags,h as getLunoraClient,S as hydratePreloaded,P as infiniteQuery,q as mutation,E as mutator,T as paginatedQuery,k as presence,z as query,D as rateLimit,y as setLunoraClient,H as stream,J as subscription,M as voiceAgent};
1
+ import{action as t}from"./packem_shared/action-SjeZUGhX.mjs";import{agent as a}from"./packem_shared/agent-WMVWf1Ks.mjs";import{agentChat as f}from"./packem_shared/agentChat-CJ048ZZl.mjs";import{agentState as n}from"./packem_shared/agentState-CAqcW2tF.mjs";import{agentToolEvents as i}from"./packem_shared/agentToolEvents-Dyu5BFZn.mjs";import{auth as g,authGate as s}from"./packem_shared/auth-Bew4W5n1.mjs";import{connectionStatus as l}from"./packem_shared/connectionStatus-D9A79Eqw.mjs";import{getLunoraClient as h,setLunoraClient as y}from"./packem_shared/getLunoraClient-DU7BmZy1.mjs";import{flag as L,flags as v}from"./packem_shared/flag-Csh9LBiO.mjs";import{hydratePreloaded as S}from"./packem_shared/hydratePreloaded-Douu4xsv.mjs";import{mutation as q}from"./packem_shared/mutation-DLBr5Op1.mjs";import{mutator as E}from"./packem_shared/mutator-DECDuo94.mjs";import{infiniteQuery as P,paginatedQuery as T}from"./packem_shared/infiniteQuery-CwF_9I0J.mjs";import{presence as k}from"./packem_shared/presence-BcBuOI0s.mjs";import{query as z}from"./packem_shared/query-BMl8EN8S.mjs";import{rateLimit as D}from"./packem_shared/rateLimit-Cli38qWO.mjs";import{stream as H}from"./packem_shared/stream-DGLI262b.mjs";import{subscription as J}from"./packem_shared/subscription-B1x2xDOl.mjs";import{voiceAgent as M}from"./packem_shared/voiceAgent-C1ouI6ls.mjs";export{t as action,a as agent,f as agentChat,n as agentState,i as agentToolEvents,g as auth,s as authGate,l as connectionStatus,L as flag,v as flags,h as getLunoraClient,S as hydratePreloaded,P as infiniteQuery,q as mutation,E as mutator,T as paginatedQuery,k as presence,z as query,D as rateLimit,y as setLunoraClient,H as stream,J as subscription,M as voiceAgent};
@@ -0,0 +1 @@
1
+ import{writable as A,get as H}from"svelte/store";import{isClient as V}from"./agent-WMVWf1Ks.mjs";import{getLunoraClient as O}from"./getLunoraClient-DU7BmZy1.mjs";const U="/_lunora/ws",$="/_lunora/voice/",B=4001,R=e=>e.startsWith("https://")?`wss://${e.slice(8)}`:e.startsWith("http://")?`ws://${e.slice(7)}`:e,q=e=>{const t=e.startsWith("agents:")?e.slice(7):e;return t.endsWith("Voice")?t.slice(0,-5):t},K=(e,t)=>{if(t===void 0||t==="")return R(e);if(t.endsWith(U))return t.slice(0,-U.length);try{return new URL(t).origin}catch{return R(e)}},J=e=>{const t=K(e.httpUrl,e.wsUrl),r=t.endsWith("/")?t.slice(0,-1):t,i=new URLSearchParams({threadKey:e.threadKey});return`${r}${$}${encodeURIComponent(e.agent)}?${i.toString()}`},G=(e,t)=>{if(t?.code===B)return new Error(`${e}: authentication token expired — refresh the credential and start a new call`)},X=16e3,j=e=>{if(e.length===0)return 0;let t=0;for(const r of e)t+=r*r;return Math.sqrt(t/e.length)},z=(e,t)=>{const r=t/X,i=r>1?Math.floor(e.length/r):e.length,p=new ArrayBuffer(i*2),d=new DataView(p);for(let u=0;u<i;u+=1){const b=e[Math.floor(u*r)]??0,g=Math.max(-1,Math.min(1,b));d.setInt16(u*2,g<0?g*32768:g*32767,!0)}return new Uint8Array(p)},Q=async e=>{const t=globalThis,r=t.navigator?.mediaDevices?.getUserMedia.bind(t.navigator.mediaDevices),i=t.AudioContext??t.webkitAudioContext;if(!r||!i)throw new Error("useVoiceAgent: microphone capture requires getUserMedia + AudioContext (no browser audio available)");const p=await r({audio:{channelCount:1,echoCancellation:!0,noiseSuppression:!0}}),d=new i,u=d.createMediaStreamSource(p),b=d.createScriptProcessor(4096,1,1);let g=!1,m=!1,h=0,o=0;return b.onaudioprocess=a=>{const l=a.inputBuffer.getChannelData(0),f=g?0:j(l);if(e.onLevel(f),g)return;if(e.onAudio(z(l,d.sampleRate)),e.isSpeaking()){o=f>=e.interruptThreshold?o+1:0,o>=e.interruptChunks&&(o=0,e.onInterrupt());return}o=0;const E=l.length/d.sampleRate*1e3;if(f>=e.silenceThreshold){m=!0,h=0;return}m&&(h+=E,h>=e.silenceDurationMs&&(m=!1,h=0,e.onSilence()))},u.connect(b),b.connect(d.destination),{setMuted:a=>{g=a},stop:()=>{b.disconnect(),u.disconnect();for(const a of p.getTracks())a.stop();d.close()}}},Y=()=>{const e=globalThis,t=e.AudioContext??e.webkitAudioContext;if(!t)throw new Error("useVoiceAgent: audio playback requires AudioContext (no browser audio available)");const r=new t,i=new Set;let p=0,d=Promise.resolve(),u=0;const b=async(h,o)=>{if(o!==u)return;let a;try{a=await r.decodeAudioData(h.buffer)}catch{return}if(o!==u)return;const l=r.createBufferSource();l.buffer=a,l.connect(r.destination);const f=Math.max(r.currentTime,p);l.start(f),p=f+a.duration,i.add(l),l.onended=()=>{i.delete(l)}},g=h=>{const o=Uint8Array.from(h),a=u;d=d.then(()=>b(o,a))},m=()=>{u+=1;for(const h of i)try{h.stop()}catch{}i.clear(),p=r.currentTime};return{enqueue:g,interrupt:m,stop:()=>{m(),r.close()}}},L=1,Z=.01,ee=1200,te=.15,se=3,ne=(e,t)=>{const{createMicrophone:r=Q,createSpeaker:i=Y,createSocket:p,interruptChunks:d=se,interruptThreshold:u=te,silenceDurationMs:b=ee,silenceThreshold:g=Z,threadKey:m,voice:h}=t,o=A("idle"),a=A(!1),l=A(""),f=A(""),E=A(0),C=A(!1),w=A();let c,v=!1;const T=s=>{const n=c?.socket;return n?.readyState===L?(n.send(JSON.stringify(s)),!0):!1},_=()=>{const s=c;if(c=void 0,s){s.microphone?.stop(),s.speaker?.stop(),s.socket.onmessage=null,s.socket.onerror=null,s.socket.onclose=null;try{s.socket.close()}catch{}}v=!1,a.set(!1),o.set("idle"),E.set(0)},D=_,I=s=>{const n=c;switch(s.type){case"assistant_delta":{n&&(n.speaking=!0),o.set("speaking"),f.update(x=>x+s.text);break}case"assistant_done":{n&&(n.speaking=!1),f.set(s.text),o.set("listening");break}case"error":{n&&(n.speaking=!1),w.set(new Error(s.message)),o.set("listening");break}case"interrupted":{n&&(n.speaking=!1,n.suppressAudio=!1),n?.speaker?.interrupt(),o.set("listening");break}case"ready":{n&&(n.audioFormat=s.audioFormat,n.suppressAudio=!1),a.set(!0),o.set("listening");break}case"user_transcript":{n&&(n.suppressAudio=!1),l.set(s.text),f.set(""),o.set("thinking");break}}},F=s=>{const n=c;!n||n.suppressAudio||(n.speaker??=i({audioFormat:n.audioFormat}),n.speaking=!0,o.set("speaking"),n.speaker.enqueue(s))},W=async()=>{if(c||v)return;v=!0,w.set(void 0),l.set(""),f.set("");let s;try{const n=J({agent:q(h.__lunoraRef),httpUrl:e.url,threadKey:m,wsUrl:e.wsUrl}),S=(p??(k=>{const y=e.getWebSocketImpl();if(!y)throw new Error("voiceAgent: no WebSocket implementation available (pass createSocket explicitly)");return new y(k)}))(n);S.binaryType="arraybuffer",s={audioFormat:"mp3",microphone:void 0,socket:S,speaker:void 0,speaking:!1,suppressAudio:!1},c=s,S.onmessage=k=>{if(typeof k.data=="string"){try{I(JSON.parse(k.data))}catch{}return}F(new Uint8Array(k.data))},S.onerror=()=>{w.set(new Error("voiceAgent: voice socket error"))},S.onclose=k=>{if(c!==s)return;const y=G("voiceAgent",k);y&&w.set(y),_()};const M=await r({interruptChunks:d,interruptThreshold:u,isSpeaking:()=>c?.speaking??!1,onAudio:k=>{S.readyState===L&&S.send(k)},onInterrupt:()=>{T({type:"interrupt"}),c?.speaker?.interrupt(),c&&(c.speaking=!1,c.suppressAudio=!0),o.set("listening")},onLevel:k=>{E.set(k)},onSilence:()=>{T({type:"commit"}),o.set("thinking")},silenceDurationMs:b,silenceThreshold:g});c===s?(s.microphone=M,C.set(!1),s.speaking||o.set("listening")):M.stop()}catch(n){c===s&&(w.set(n instanceof Error?n:new Error(String(n))),_())}finally{v=!1}},N=()=>{const s=!H(C);return c?.microphone?.setMuted(s),C.set(s),s},P=s=>{T({text:s,type:"text"})&&o.set("thinking")};return{audioLevel:{subscribe:E.subscribe},connected:{subscribe:a.subscribe},endCall:D,error:{subscribe:w.subscribe},interimTranscript:{subscribe:f.subscribe},isMuted:{subscribe:C.subscribe},sendText:P,startCall:W,status:{subscribe:o.subscribe},toggleMute:N,transcript:{subscribe:l.subscribe}}};function ce(e,t){const r=V(e),i=r?e:O();return ne(i,r?t:e)}export{ce as voiceAgent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/svelte",
3
- "version": "1.0.0-alpha.109",
3
+ "version": "1.0.0-alpha.110",
4
4
  "description": "Svelte adapter for Lunora — live stores, optimistic mutations, and reactive loaders",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -1 +0,0 @@
1
- import{writable as A,get as I}from"svelte/store";import{isClient as P}from"./agent-WMVWf1Ks.mjs";import{getLunoraClient as W}from"./getLunoraClient-DU7BmZy1.mjs";const H=16e3,V=e=>{if(e.length===0)return 0;let o=0;for(const t of e)o+=t*t;return Math.sqrt(o/e.length)},q=(e,o)=>{const t=o/H,i=t>1?Math.floor(e.length/t):e.length,l=new ArrayBuffer(i*2),c=new DataView(l);for(let d=0;d<i;d+=1){const g=e[Math.floor(d*t)]??0,b=Math.max(-1,Math.min(1,g));c.setInt16(d*2,b<0?b*32768:b*32767,!0)}return new Uint8Array(l)},B=async e=>{const o=globalThis,t=o.navigator?.mediaDevices?.getUserMedia.bind(o.navigator.mediaDevices),i=o.AudioContext??o.webkitAudioContext;if(!t||!i)throw new Error("useVoiceAgent: microphone capture requires getUserMedia + AudioContext (no browser audio available)");const l=await t({audio:{channelCount:1,echoCancellation:!0,noiseSuppression:!0}}),c=new i,d=c.createMediaStreamSource(l),g=c.createScriptProcessor(4096,1,1);let b=!1,S=!1,h=0,r=0;return g.onaudioprocess=a=>{const p=a.inputBuffer.getChannelData(0),f=b?0:V(p);if(e.onLevel(f),b)return;if(e.onAudio(q(p,c.sampleRate)),e.isSpeaking()){r=f>=e.interruptThreshold?r+1:0,r>=e.interruptChunks&&(r=0,e.onInterrupt());return}r=0;const w=p.length/c.sampleRate*1e3;if(f>=e.silenceThreshold){S=!0,h=0;return}S&&(h+=w,h>=e.silenceDurationMs&&(S=!1,h=0,e.onSilence()))},d.connect(g),g.connect(c.destination),{setMuted:a=>{b=a},stop:()=>{g.disconnect(),d.disconnect();for(const a of l.getTracks())a.stop();c.close()}}},$=()=>{const e=globalThis,o=e.AudioContext??e.webkitAudioContext;if(!o)throw new Error("useVoiceAgent: audio playback requires AudioContext (no browser audio available)");const t=new o,i=new Set;let l=0,c=Promise.resolve(),d=0;const g=async(h,r)=>{if(r!==d)return;let a;try{a=await t.decodeAudioData(h.buffer)}catch{return}if(r!==d)return;const p=t.createBufferSource();p.buffer=a,p.connect(t.destination);const f=Math.max(t.currentTime,l);p.start(f),l=f+a.duration,i.add(p),p.onended=()=>{i.delete(p)}},b=h=>{const r=Uint8Array.from(h),a=d;c=c.then(()=>g(r,a))},S=()=>{d+=1;for(const h of i)try{h.stop()}catch{}i.clear(),l=t.currentTime};return{enqueue:b,interrupt:S,stop:()=>{S(),t.close()}}},_=1,J=.01,K=1200,O=.15,G=3,j=e=>e.startsWith("https://")?`wss://${e.slice(8)}`:e.startsWith("http://")?`ws://${e.slice(7)}`:e,z=e=>{const o=e.__lunoraRef,t=o.startsWith("agents:")?o.slice(7):o;return t.endsWith("Voice")?t.slice(0,-5):t},Q=(e,o,t)=>{const i=j(e),l=i.endsWith("/")?i.slice(0,-1):i,c=new URLSearchParams({threadKey:t});return`${l}/_lunora/voice/${encodeURIComponent(o)}?${c.toString()}`},X=(e,o)=>{const{createMicrophone:t=B,createSpeaker:i=$,createSocket:l,interruptChunks:c=G,interruptThreshold:d=O,silenceDurationMs:g=K,silenceThreshold:b=J,threadKey:S,voice:h}=o,r=A("idle"),a=A(!1),p=A(""),f=A(""),w=A(0),T=A(!1),y=A();let u,C=!1;const E=s=>{const n=u?.socket;return n?.readyState===_?(n.send(JSON.stringify(s)),!0):!1},M=()=>{const s=u;if(u=void 0,s){s.microphone?.stop(),s.speaker?.stop();try{s.socket.close()}catch{}}C=!1,a.set(!1),r.set("idle"),w.set(0)},U=M,L=s=>{const n=u;switch(s.type){case"assistant_delta":{n&&(n.speaking=!0),r.set("speaking"),f.update(k=>k+s.text);break}case"assistant_done":{n&&(n.speaking=!1),f.set(s.text),r.set("listening");break}case"error":{n&&(n.speaking=!1),y.set(new Error(s.message)),r.set("listening");break}case"interrupted":{n&&(n.speaking=!1,n.suppressAudio=!1),n?.speaker?.interrupt(),r.set("listening");break}case"ready":{n&&(n.audioFormat=s.audioFormat,n.suppressAudio=!1),a.set(!0),r.set("listening");break}case"user_transcript":{n&&(n.suppressAudio=!1),p.set(s.text),f.set(""),r.set("thinking");break}}},R=s=>{const n=u;!n||n.suppressAudio||(n.speaker??=i({audioFormat:n.audioFormat}),n.speaking=!0,r.set("speaking"),n.speaker.enqueue(s))},D=async()=>{if(!(u||C)){C=!0,y.set(void 0),p.set(""),f.set("");try{const s=Q(e.url,z(h),S),k=(l??(m=>new globalThis.WebSocket(m)))(s);k.binaryType="arraybuffer";const v={audioFormat:"mp3",microphone:void 0,socket:k,speaker:void 0,speaking:!1,suppressAudio:!1};u=v,k.onmessage=m=>{if(typeof m.data=="string"){try{L(JSON.parse(m.data))}catch{}return}R(new Uint8Array(m.data))},k.onerror=()=>{y.set(new Error("voiceAgent: voice socket error"))},k.onclose=()=>{u===v&&M()};const x=await t({interruptChunks:c,interruptThreshold:d,isSpeaking:()=>u?.speaking??!1,onAudio:m=>{k.readyState===_&&k.send(m)},onInterrupt:()=>{E({type:"interrupt"}),u?.speaker?.interrupt(),u&&(u.speaking=!1,u.suppressAudio=!0),r.set("listening")},onLevel:m=>{w.set(m)},onSilence:()=>{E({type:"commit"}),r.set("thinking")},silenceDurationMs:g,silenceThreshold:b});u===v?(v.microphone=x,T.set(!1),r.set("listening")):x.stop()}catch(s){y.set(s instanceof Error?s:new Error(String(s))),M()}finally{C=!1}}},F=()=>{const s=!I(T);return u?.microphone?.setMuted(s),T.set(s),s},N=s=>{E({text:s,type:"text"})&&r.set("thinking")};return{audioLevel:{subscribe:w.subscribe},connected:{subscribe:a.subscribe},endCall:U,error:{subscribe:y.subscribe},interimTranscript:{subscribe:f.subscribe},isMuted:{subscribe:T.subscribe},sendText:N,startCall:D,status:{subscribe:r.subscribe},toggleMute:F,transcript:{subscribe:p.subscribe}}};function te(e,o){const t=P(e),i=t?e:W();return X(i,t?o:e)}export{te as voiceAgent};