@vanira/sdk 0.0.42 → 0.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/VaniraAI-D2KUxJJO.js +682 -0
  2. package/dist/VaniraAI-EFYOmJ2E.cjs +1 -0
  3. package/dist/__tests__/adapters.test.d.ts +14 -0
  4. package/dist/adapters/PeerConnectionAdapter.d.ts +90 -0
  5. package/dist/adapters/browser/BrowserAudioAdapter.d.ts +9 -0
  6. package/dist/adapters/browser/BrowserDataChannelAdapter.d.ts +16 -0
  7. package/dist/adapters/browser/BrowserMediaAdapter.d.ts +10 -0
  8. package/dist/adapters/browser/BrowserPeerAdapter.d.ts +9 -0
  9. package/dist/adapters/browser/index.d.ts +4 -0
  10. package/dist/adapters/interfaces.d.ts +70 -0
  11. package/dist/adapters/react-native/RNAudioAdapter.d.ts +9 -0
  12. package/dist/adapters/react-native/RNDataChannelAdapter.d.ts +16 -0
  13. package/dist/adapters/react-native/RNMediaAdapter.d.ts +17 -0
  14. package/dist/adapters/react-native/RNPeerAdapter.d.ts +19 -0
  15. package/dist/adapters/react-native/index.d.ts +4 -0
  16. package/dist/browser.d.ts +1 -0
  17. package/dist/core/VaniraAI.d.ts +2 -0
  18. package/dist/index.d.ts +21 -3
  19. package/dist/platforms/browser.cjs +8 -0
  20. package/dist/platforms/browser.d.ts +28 -0
  21. package/dist/platforms/browser.js +479 -0
  22. package/dist/platforms/react-native.cjs +1 -0
  23. package/dist/platforms/react-native.d.ts +46 -0
  24. package/dist/platforms/react-native.js +116 -0
  25. package/dist/react/PresetRenderer.d.ts +1 -2
  26. package/dist/react/index.d.ts +0 -4
  27. package/dist/react-native.d.ts +1 -0
  28. package/dist/runtime/browserRuntime.d.ts +17 -0
  29. package/dist/runtime/reactNativeRuntime.d.ts +18 -0
  30. package/dist/runtime/types.d.ts +96 -0
  31. package/dist/types.d.ts +36 -4
  32. package/dist/ui/presets/WidgetPresetRenderer.d.ts +2 -0
  33. package/dist/vanira-sdk.es.js +1788 -231
  34. package/dist/vanira-sdk.js +36 -36
  35. package/dist/vanira-sdk.js.map +1 -1
  36. package/dist/vanira-sdk.umd.js +1538 -0
  37. package/package.json +18 -3
  38. package/dist/react/presets/CalendarPreset.d.ts +0 -4
  39. package/dist/react/presets/CameraPreset.d.ts +0 -16
  40. package/dist/react/presets/FormPreset.d.ts +0 -4
  41. package/dist/react/presets/NavigatePreset.d.ts +0 -16
  42. package/dist/react/presets/UploadPreset.d.ts +0 -17
  43. package/dist/react/registry.d.ts +0 -3
  44. package/dist/react/types.d.ts +0 -21
@@ -0,0 +1 @@
1
+ "use strict";var b=Object.defineProperty;var y=(u,e,t)=>e in u?b(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var a=(u,e,t)=>y(u,typeof e!="symbol"?e+"":e,t);var f=typeof document<"u"?document.currentScript:null;class T{constructor(e){a(this,"serverUrl");a(this,"agentId");a(this,"callId");a(this,"prospectId");a(this,"apiKey");a(this,"backendUrl");a(this,"token");a(this,"onConnected");a(this,"onDisconnected");a(this,"onError");a(this,"onTranscription");a(this,"onLocalStream");a(this,"onRemoteTrack");a(this,"onClientToolCall");a(this,"onSessionStarted");a(this,"sessionStartedEmitted",!1);a(this,"pc",null);a(this,"dataChannel",null);a(this,"audioElement",null);a(this,"localStream",null);a(this,"connected",!1);if(!e.agentId)throw new Error("agentId is required");if(!e.serverUrl&&!e.apiKey)throw new Error("Provide either serverUrl or apiKey (to use createCall())");this.agentId=e.agentId,this.serverUrl=e.serverUrl?e.serverUrl.replace(/\/$/,""):"",this.apiKey=e.apiKey,this.backendUrl=(e.backendUrl||"https://api.vanira.io").replace(/\/$/,"");const t=typeof window<"u";if(this.prospectId=e.prospectId,!this.prospectId&&t)try{this.prospectId=window.sessionStorage&&window.sessionStorage.getItem("vanira_prospect_id")||window.localStorage&&window.localStorage.getItem("vanira_prospect_id")||void 0}catch{}const n=e.sessionBehavior==="continue";if(e.callId)this.callId=e.callId;else if(n&&t)try{this.callId=window.sessionStorage&&window.sessionStorage.getItem("vanira_latest_call_id")||window.localStorage&&window.localStorage.getItem("vanira_latest_call_id")||void 0}catch{}else this.callId=void 0;this.onConnected=e.onConnected||(()=>{}),this.onDisconnected=e.onDisconnected||(()=>{}),this.onError=e.onError||(i=>console.error("[WebRTC]",i)),this.onTranscription=e.onTranscription||(()=>{}),this.onLocalStream=e.onLocalStream||(()=>{}),this.onRemoteTrack=e.onRemoteTrack||(()=>{}),this.onClientToolCall=e.onClientToolCall||(()=>{}),this.onSessionStarted=e.onSessionStarted}async createCall(){var i;if(!this.apiKey)throw new Error("[VaniraAI] apiKey is required to use createCall()");const e={"Content-Type":"application/json","X-API-Key":this.apiKey},t=await fetch(`${this.backendUrl}/calls/create`,{method:"POST",headers:e,body:JSON.stringify({agent_id:this.agentId,type:"web",prospect_id:this.prospectId,call_id:this.callId})});if(!t.ok){const r=await t.json().catch(()=>({}));throw new Error(`[VaniraAI] createCall failed (${t.status}): ${((i=r==null?void 0:r.detail)==null?void 0:i.message)||(r==null?void 0:r.message)||t.statusText}`)}const n=await t.json();if(!n.call_id||!n.worker_url)throw new Error("[VaniraAI] createCall response missing call_id or worker_url");if(this.callId=n.call_id,this.prospectId=n.prospect_id||this.prospectId,this.serverUrl=n.worker_url,typeof window<"u")try{window.sessionStorage&&(window.sessionStorage.setItem("vanira_prospect_id",this.prospectId||""),window.sessionStorage.setItem("vanira_latest_call_id",this.callId||"")),window.localStorage&&(window.localStorage.setItem("vanira_prospect_id",this.prospectId||""),window.localStorage.setItem("vanira_latest_call_id",this.callId||""))}catch(r){console.warn("[WebRTC] Failed to save session variables to storage:",r)}if(this.onSessionStarted&&this.prospectId&&this.callId)try{this.onSessionStarted({prospectId:this.prospectId,callId:this.callId,serverUrl:this.serverUrl}),this.sessionStartedEmitted=!0}catch(r){console.error("[WebRTC] Error in onSessionStarted callback:",r)}await this.connect()}async connect(){var e;if(this.apiKey&&!this.serverUrl){await this.createCall();return}if(!this.serverUrl)throw new Error("[VaniraAI] serverUrl is missing. Provide apiKey or serverUrl.");if(this.callId||(this.callId=this.generateCallId()),this.onSessionStarted&&this.prospectId&&this.callId&&!this.sessionStartedEmitted)try{this.onSessionStarted({prospectId:this.prospectId,callId:this.callId,serverUrl:this.serverUrl}),this.sessionStartedEmitted=!0}catch(t){console.error("[WebRTC] Error in onSessionStarted callback:",t)}console.log("đŸ”ĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:global.relay.metered.ca:80"},{urls:["turns:global.relay.metered.ca:443?transport=tcp","turn:global.relay.metered.ca:443?transport=tcp","turn:global.relay.metered.ca:80?transport=tcp"],username:"fa97658be3343d21da3b65e6",credential:"HXHDoqeHbvZrmCuf"}],iceTransportPolicy:"all"}),console.log("using ice servers:",[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}]);let t;try{t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}})}catch(o){if(console.error("🎤 [WebRTC] Microphone access failed:",o),o.name==="NotAllowedError"||o.name==="PermissionDeniedError"||(e=o.message)!=null&&e.includes("Permission denied")){const s=navigator.userAgent,c=/iPad|iPhone|iPod/.test(s)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1;let p="Microphone access denied. Please allow microphone access in your browser settings.";c&&(s.includes("CriOS")?p="Microphone access blocked. Please enable it in iOS Settings > Chrome > Microphone, then reload the page.":s.includes("FxiOS")?p="Microphone access blocked. Please enable it in iOS Settings > Firefox > Microphone, then reload the page.":p="Microphone access blocked. Please enable it in iOS Settings > Safari > Microphone (or tap 'aA' > Website Settings > Allow Microphone).");const h=new Error(p);throw h.name=o.name,h}throw o}if(console.log("🎤 [WebRTC] Microphone access granted"),this.localStream=t,!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),t.getTracks().forEach(o=>o.stop());return}if(this.onLocalStream(t),t.getTracks().forEach(o=>{var s;(s=this.pc)==null||s.addTrack(o,t)}),!this.pc)throw new Error("RTCPeerConnection was closed unexpectedly");this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("📡 [WebRTC] DataChannel opened"),this.dataChannel.onmessage=o=>{if(typeof o.data=="string")try{this.handleControlEvent(JSON.parse(o.data))}catch{console.warn("[WebRTC] Failed to parse message:",o.data)}else if(o.data instanceof ArrayBuffer)try{const s=new TextDecoder().decode(o.data);try{const c=JSON.parse(s);c&&typeof c=="object"&&c.event==="client_tool_call"?(console.log("[VaniraAI] Safely decoding binary tool_call to JSON:",c),this.handleControlEvent(c)):console.log("[VaniraAI] Decoded JSON from binary (inspect only):",c)}catch{console.log("[VaniraAI] Decoded String from binary:",s)}}catch{console.log("[VaniraAI] Received binary data:",o.data.byteLength,"bytes (not decodable)")}else o.data instanceof Blob&&o.data.text().then(s=>{try{this.handleControlEvent(JSON.parse(s))}catch{console.warn("[WebRTC] Failed to parse blob data:",s)}})},this.dataChannel.onerror=o=>console.error("❌ [WebRTC] DataChannel error:",o),this.pc.ontrack=o=>{const s=o.track,c=o.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${s.kind} track`),s.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=c,this.audioElement.play().catch(p=>console.warn("Audio autoplay blocked:",p)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")},this.onRemoteTrack(s,c)):s.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(s,c))};const n=()=>{var c,p,h,g,_,C,m,I,w,S,v;console.log("🔄 [WebRTC] State:",(c=this.pc)==null?void 0:c.connectionState,"| ICE:",(p=this.pc)==null?void 0:p.iceConnectionState);const o=((h=this.pc)==null?void 0:h.connectionState)==="connected"||((g=this.pc)==null?void 0:g.iceConnectionState)==="connected"||((_=this.pc)==null?void 0:_.iceConnectionState)==="completed",s=((C=this.pc)==null?void 0:C.connectionState)==="failed"||((m=this.pc)==null?void 0:m.iceConnectionState)==="failed"||((I=this.pc)==null?void 0:I.connectionState)==="closed"||((w=this.pc)==null?void 0:w.iceConnectionState)==="closed"||((S=this.pc)==null?void 0:S.connectionState)==="disconnected"||((v=this.pc)==null?void 0:v.iceConnectionState)==="disconnected";o&&!this.connected?(this.connected=!0,this.onConnected()):s&&this.connected&&(this.connected=!1,this.onDisconnected())};this.pc.onconnectionstatechange=n,this.pc.oniceconnectionstatechange=n;const i=await this.pc.createOffer();await this.pc.setLocalDescription(i),console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."),await this.waitForIceGathering(),console.log("🧊 [WebRTC] ICE gathering complete"),console.log("📤 [WebRTC] Sending offer via HTTP...");const r=this.serverUrl.includes("?")?this.serverUrl:`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,l=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!l.ok){const o=await l.json();throw new Error(o.error||`HTTP ${l.status}`)}const{answer:d}=await l.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(d),console.log("✅ [WebRTC] Connection established!")}catch(t){throw console.error("❌ [WebRTC] Connection failed:",t),this.disconnect(),this.onError(t.message||t),t}}waitForIceGathering(){return new Promise(e=>{if(!this.pc)return e();if(this.pc.iceGatheringState==="complete")e();else{const t=()=>{var n,i;((n=this.pc)==null?void 0:n.iceGatheringState)==="complete"&&((i=this.pc)==null||i.removeEventListener("icegatheringstatechange",t),e())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var n;(n=this.pc)==null||n.removeEventListener("icegatheringstatechange",t),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),e()},5e3)}})}sendEvent(e,t={}){var n;if(((n=this.dataChannel)==null?void 0:n.readyState)==="open"){const i={event:e,...t};console.log(`📤 [WebRTC] Sending event: ${e}`,i),this.dataChannel.send(JSON.stringify(i))}else console.warn(`âš ī¸ [WebRTC] Cannot send event ${e} - DataChannel not open`)}handleControlEvent(e){var t;switch(e.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: clearAudio received (leaving stream unpaused)");break;case"transcription":console.log("📝 [WebRTC] Transcription:",e.text),this.onTranscription(e.text,e.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",e.name);break;case"client_tool_call":{const n=e.tool_call||e.data||e,i=(n==null?void 0:n.tool_call_id)||(n==null?void 0:n.call_id)||e.tool_call_id||"";i&&((t=this.dataChannel)==null?void 0:t.readyState)==="open"?(this.dataChannel.send(JSON.stringify({event:"client_tool_ack",data:{tool_call_id:i}})),console.log("✅ [VaniraAI] Sent client_tool_ack for:",i)):i||console.warn("âš ī¸ [VaniraAI] client_tool_call received without tool_call_id — ack skipped");const r=(n==null?void 0:n.arguments)||(n==null?void 0:n.args)||{},l=(n==null?void 0:n.client_fields)||{},d=(l==null?void 0:l.preset_id)||(r==null?void 0:r.preset_id);if(d){console.log(`🎨 [VaniraAI] Preset detected: ${d}`),console.log("🎨 [VaniraAI] Tool args:",r),console.log("🎨 [VaniraAI] Client fields:",l),console.log(`🎨 [VaniraAI] Preset "${d}" — dispatching vanira:preset event`),window.dispatchEvent(new CustomEvent("vanira:preset",{detail:{toolCall:n,client:this}})),this.onClientToolCall(n);break}console.log("đŸ› ī¸ [VaniraAI] Client Tool Call:",e),this.onClientToolCall(n);break}default:console.log("â„šī¸ [WebRTC] Unknown event:",e.event)}}disconnect(){console.log("🔴 [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),this.pc&&(this.pc.getSenders().forEach(e=>{e.track&&e.track.stop()}),this.pc.close(),this.pc=null),this.localStream&&(this.localStream.getTracks().forEach(e=>{try{e.stop()}catch{}}),this.localStream=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}sendToolResult(e,t){this.sendEvent("client_tool_result",{call_id:e,result:t})}sendToolAck(e){var t;((t=this.dataChannel)==null?void 0:t.readyState)==="open"?(this.dataChannel.send(JSON.stringify({event:"client_tool_ack",data:{tool_call_id:e}})),console.log("✅ [VaniraAI] Sent client_tool_ack for:",e)):console.warn("âš ī¸ [VaniraAI] Cannot send tool_ack: DataChannel not open")}sendContextUpdate(e){this.sendEvent("client_context_update",{data:{context:e}})}sendActionTrigger(e,t={}){this.sendEvent("client_action_trigger",{data:{action_name:e,data:t}})}triggerActionInterrupt(){this.sendEvent("action_interrupt"),console.log("🛑 [VaniraAI] Triggered client-side action interrupt")}async uploadMedia(e,t="general",n=""){if(!this.serverUrl)throw new Error("Upload failed: serverUrl is not set. Connect the WebRTCClient first.");if(!this.callId)throw new Error("Upload failed: callId is missing.");let i="";try{i=new URL(this.serverUrl).origin}catch{i=this.serverUrl.replace(/\/webrtc.*$/,"").replace(/\/$/,"")}const r=`${i}/media/upload`,l=new FormData;l.append("file",e),l.append("call_id",this.callId),l.append("reason",t),console.log(`[VaniraAI] Uploading media to ${r} (Call: ${this.callId}, Reason: ${t})...`);const d=await fetch(r,{method:"POST",body:l});if(!d.ok){let h=`HTTP ${d.status}`;try{h=(await d.json()).error||h}catch{}throw new Error(`Upload failed: ${h}`)}const o=await d.json(),s=o.media_id,c=o.url,p=o.content_type||e.type;if(!s||!c)throw new Error("Upload failed: server response missing media_id or url");if(console.log(`[VaniraAI] Media uploaded successfully. ID: ${s}, URL: ${c}`),this.dataChannel&&this.dataChannel.readyState==="open"){const h={event:"client_media_update",data:{media_id:s,media_url:c,content_type:p,reason:t,message:n}};this.dataChannel.send(JSON.stringify(h)),console.log("[VaniraAI] Sent client_media_update notification via DataChannel:",h)}else console.warn("[VaniraAI] WebRTC DataChannel is not open. Media uploaded but AI notification skipped.");return{media_id:s,url:c}}static async fetchIceServers(e,t="https://api.vanira.io"){try{const n=await fetch(`${t}/discovery/ice-servers`,{headers:{"X-API-Key":e}});if(!n.ok)throw new Error("Failed to fetch ICE servers");return(await n.json()).ice_servers||[]}catch(n){throw console.error("[WebRTC] Failed to fetch ICE servers:",n),n}}}const E={};class R{constructor(e){a(this,"config");a(this,"_status","idle");a(this,"client",null);a(this,"listeners",new Map);if(!e.agentId)throw new Error("[VaniraAI] agentId is required");this.config=e}get serverUrl(){var e;return(e=this.client)==null?void 0:e.serverUrl}get callId(){var e;return(e=this.client)==null?void 0:e.callId}get prospectId(){var e;return(e=this.client)==null?void 0:e.prospectId}get status(){return this._status}get isConnected(){return this._status==="connected"}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),this}off(e,t){var n;return(n=this.listeners.get(e))==null||n.delete(t),this}emit(e,t){var n;(n=this.listeners.get(e))==null||n.forEach(i=>i(t))}async start(){if(this._status==="connecting"||this._status==="connected")return console.warn("[VaniraAI] Already connecting or connected. Call stop() first."),{callId:this.callId||"",prospectId:this.prospectId||"",serverUrl:this.serverUrl||""};this._setStatus("connecting");const e=this.config.serverUrl||(this.config.apiKey?"":this._inferServerUrl());try{return this.client=new T({serverUrl:e,agentId:this.config.agentId,callId:this.config.callId,prospectId:this.config.prospectId,sessionBehavior:this.config.sessionBehavior,token:this.config.token,apiKey:this.config.apiKey,backendUrl:this.config.backendUrl,iceServers:this.config.iceServers,runtime:this.config.runtime,onSessionStarted:t=>{this.emit("session_started",t)},onConnected:()=>{this._setStatus("connected"),this.emit("connected")},onDisconnected:()=>{this._setStatus("disconnected"),this.emit("disconnected")},onError:t=>{this._setStatus("error"),this.emit("error",typeof t=="string"?t:(t==null?void 0:t.message)||"Connection failed")},onTranscription:(t,n)=>{this.emit("transcription",{text:t,isFinal:n})},onClientToolCall:t=>{const n=(t==null?void 0:t.data)||t,i={name:n.name||n.tool_name||"",arguments:n.arguments||n.args||{},tool_call_id:n.tool_call_id||n.call_id||"",execution_mode:n.execution_mode||"fire_and_forget",client_fields:n.client_fields||{}};this.emit("tool_call",i)},onRemoteTrack:(t,n)=>{this.emit("track",{track:t,stream:n})}}),await this.client.connect(),{callId:this.callId||"",prospectId:this.prospectId||"",serverUrl:this.serverUrl||""}}catch(t){throw this._setStatus("error"),this.emit("error",(t==null?void 0:t.message)||"Failed to start call"),t}}stop(){this.client&&(this.client.disconnect(),this.client=null),this._setStatus("disconnected")}sendToolResult(e,t){this._assertConnected("sendToolResult"),this.client.sendToolResult(e,t)}sendToolError(e,t){this._assertConnected("sendToolError"),this.client.sendEvent("client_tool_result",{call_id:e,result:{status:"error",error:t}})}updateContext(e){this._assertConnected("updateContext"),this.client.sendContextUpdate(e)}triggerInterrupt(e,t={}){this._assertConnected("triggerInterrupt"),this.client.triggerActionInterrupt(),this.client.sendActionTrigger(e,t)}interruptAudioOnly(){this._assertConnected("interruptAudioOnly"),this.client.triggerActionInterrupt()}async uploadMedia(e,t="general",n="",i=!1){this._assertConnected("uploadMedia"),i&&this.client.triggerActionInterrupt();const r=50*1024*1024;if(e.size>r)throw new Error(`Upload failed: file is too large (${(e.size/1024/1024).toFixed(1)} MB). Maximum is 50 MB.`);const l=["image/jpeg","image/png","image/gif","image/webp","image/bmp","application/pdf"],d=e.type;if(d&&!l.includes(d))throw new Error(`Upload failed: unsupported file type '${d}'. Supported: JPEG, PNG, GIF, WebP, BMP, PDF.`);return this.client.uploadMedia(e,t,n)}_setStatus(e){this._status=e}_inferServerUrl(){return(typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("VaniraAI-EFYOmJ2E.cjs",document.baseURI).href}<"u"&&E||{}).VITE_WEBRTC_SERVER_URL||"https://in-godspeed.travelr.club"}_assertConnected(e){if(!this.client||!this.isConnected)throw new Error(`[VaniraAI] Cannot call ${e}() when not connected. Call start() first.`)}}exports.VaniraAI=R;exports.WebRTCClient=T;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Vanira SDK — Cross-platform adapter unit tests
3
+ *
4
+ * Tests cover:
5
+ * 1. BrowserPeerAdapter — wraps RTCPeerConnection creation
6
+ * 2. BrowserDataChannelAdapter — normalises browser message formats
7
+ * 3. RNDataChannelAdapter — handles RN string-only messages
8
+ * 4. BrowserMediaAdapter — wraps getUserMedia
9
+ * 5. BrowserAudioAdapter — wraps HTMLAudioElement
10
+ * 6. Runtime capabilities — correct flags per runtime
11
+ * 7. WebRTCClient — ICE server propagation, runtime injection
12
+ * 8. DataChannel protocol — JSON parsing, client_tool_call ack
13
+ */
14
+ export {};
@@ -0,0 +1,90 @@
1
+ /**
2
+ * PeerConnectionAdapter + DataChannelAdapter
3
+ *
4
+ * These interfaces abstract the two remaining WebRTC browser globals that the
5
+ * core session directly instantiated:
6
+ *
7
+ * new RTCPeerConnection(config) → PeerConnectionAdapter.create(config)
8
+ * RTCDataChannel event binding → DataChannelAdapter.bind(channel, handlers)
9
+ *
10
+ * The core (WebRTCClient) no longer calls these constructors directly.
11
+ * Each platform provides its own implementation.
12
+ *
13
+ * Platform notes:
14
+ * Browser — wraps native RTCPeerConnection and RTCDataChannel globals
15
+ * React Native — wraps react-native-webrtc polyfilled RTCPeerConnection (same API)
16
+ * Flutter — Dart flutter_webrtc package (createPeerConnection(), RTCDataChannel)
17
+ */
18
+ export interface PeerConnectionIceServer {
19
+ urls: string | string[];
20
+ username?: string;
21
+ credential?: string;
22
+ }
23
+ export interface PeerConnectionConfig {
24
+ iceServers: PeerConnectionIceServer[];
25
+ iceTransportPolicy?: 'all' | 'relay';
26
+ }
27
+ /**
28
+ * Abstracts RTCPeerConnection construction.
29
+ * Core session code calls adapter.create() instead of `new RTCPeerConnection()`.
30
+ *
31
+ * This is the minimal factory interface — once created, the returned
32
+ * RTCPeerConnection is used directly (its method surface is already standardised
33
+ * by W3C and polyfilled identically by react-native-webrtc).
34
+ */
35
+ export interface PeerConnectionAdapter {
36
+ /**
37
+ * Create and return a new peer connection.
38
+ * The returned object conforms to the standard RTCPeerConnection interface —
39
+ * callers may use it directly for addTrack, createDataChannel, createOffer, etc.
40
+ */
41
+ create(config: PeerConnectionConfig): RTCPeerConnection;
42
+ }
43
+ /**
44
+ * Normalised message payload delivered to onMessage.
45
+ * The adapter handles the browser's string / ArrayBuffer / Blob variants
46
+ * and always delivers decoded UTF-8 text.
47
+ */
48
+ export interface DataChannelMessage {
49
+ /** Decoded text content — always a string regardless of wire format */
50
+ text: string;
51
+ }
52
+ export interface DataChannelHandlers {
53
+ onOpen(): void;
54
+ /** Always receives decoded text; binary frames are decoded by the adapter */
55
+ onMessage(msg: DataChannelMessage): void;
56
+ onError(error: unknown): void;
57
+ }
58
+ /**
59
+ * Returned by DataChannelAdapter.bind().
60
+ * Core code uses this controller to send messages and check state.
61
+ */
62
+ export interface DataChannelController {
63
+ /** Send a JSON-serialised text message */
64
+ send(text: string): void;
65
+ /** Whether the channel is in the 'open' state */
66
+ isOpen(): boolean;
67
+ /** Close the data channel */
68
+ close(): void;
69
+ }
70
+ /**
71
+ * Abstracts DataChannel event binding and message normalisation.
72
+ *
73
+ * Browser has three message wire formats: string, ArrayBuffer, and Blob.
74
+ * React Native (react-native-webrtc) delivers messages as strings only.
75
+ * The adapter normalises all formats to decoded UTF-8 text before calling
76
+ * onMessage, so the core never needs to handle binary framing.
77
+ *
78
+ * Binary frame handling policy (preserving existing behaviour):
79
+ * - Frames that decode to JSON with event === 'client_tool_call' are forwarded.
80
+ * - All other binary frames are logged but NOT forwarded to prevent false
81
+ * clearAudio events from being triggered by audio packet payloads.
82
+ */
83
+ export interface DataChannelAdapter {
84
+ /**
85
+ * Bind event handlers to a DataChannel and return a controller.
86
+ * @param channel The RTCDataChannel returned by pc.createDataChannel()
87
+ * @param handlers Event handlers — onMessage always receives decoded text
88
+ */
89
+ bind(channel: RTCDataChannel, handlers: DataChannelHandlers): DataChannelController;
90
+ }
@@ -0,0 +1,9 @@
1
+ import { AudioAdapter, AudioPlayerHandle } from '../interfaces';
2
+
3
+ /**
4
+ * Browser AudioAdapter.
5
+ * Creates an HTMLAudioElement-backed player for the incoming remote audio track.
6
+ */
7
+ export declare class BrowserAudioAdapter implements AudioAdapter {
8
+ createRemotePlayer(stream: MediaStream): AudioPlayerHandle;
9
+ }
@@ -0,0 +1,16 @@
1
+ import { DataChannelAdapter, DataChannelController, DataChannelHandlers } from '../PeerConnectionAdapter';
2
+
3
+ /**
4
+ * Browser DataChannelAdapter.
5
+ *
6
+ * Normalises browser's three wire formats (string / ArrayBuffer / Blob) to
7
+ * decoded UTF-8 text before invoking onMessage.
8
+ *
9
+ * Binary frame policy — preserved from original WebRTCClient behaviour:
10
+ * Only binary frames that decode to JSON with event === 'client_tool_call'
11
+ * are forwarded. All other binary frames are logged but dropped to prevent
12
+ * false clearAudio events from audio packet payloads.
13
+ */
14
+ export declare class BrowserDataChannelAdapter implements DataChannelAdapter {
15
+ bind(channel: RTCDataChannel, handlers: DataChannelHandlers): DataChannelController;
16
+ }
@@ -0,0 +1,10 @@
1
+ import { MediaAdapter, MediaAudioConstraints } from '../interfaces';
2
+
3
+ /**
4
+ * Browser MediaAdapter.
5
+ * Delegates directly to navigator.mediaDevices — available in all modern browsers.
6
+ */
7
+ export declare class BrowserMediaAdapter implements MediaAdapter {
8
+ getUserAudio(constraints: MediaAudioConstraints): Promise<MediaStream>;
9
+ stopStream(stream: MediaStream): void;
10
+ }
@@ -0,0 +1,9 @@
1
+ import { PeerConnectionAdapter, PeerConnectionConfig } from '../PeerConnectionAdapter';
2
+
3
+ /**
4
+ * Browser PeerConnectionAdapter.
5
+ * Wraps `new RTCPeerConnection(config)` so the core never instantiates it directly.
6
+ */
7
+ export declare class BrowserPeerAdapter implements PeerConnectionAdapter {
8
+ create(config: PeerConnectionConfig): RTCPeerConnection;
9
+ }
@@ -0,0 +1,4 @@
1
+ export { BrowserAudioAdapter } from './BrowserAudioAdapter';
2
+ export { BrowserMediaAdapter } from './BrowserMediaAdapter';
3
+ export { BrowserPeerAdapter } from './BrowserPeerAdapter';
4
+ export { BrowserDataChannelAdapter } from './BrowserDataChannelAdapter';
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Cross-platform adapter interfaces for the Vanira SDK.
3
+ *
4
+ * The core session (WebRTCClient / VaniraAI) depends only on these interfaces.
5
+ * Platform-specific implementations live in:
6
+ * adapters/browser/ — Web / browser
7
+ * adapters/react-native/ — React Native (react-native-webrtc)
8
+ *
9
+ * Future:
10
+ * flutter-sdk/lib/src/adapters/ — Dart implementations for Flutter
11
+ */
12
+ export interface MediaAudioConstraints {
13
+ echoCancellation?: boolean;
14
+ noiseSuppression?: boolean;
15
+ autoGainControl?: boolean;
16
+ /** Accepts a number or an ideal-hint object */
17
+ sampleRate?: number | {
18
+ ideal: number;
19
+ };
20
+ channelCount?: number | {
21
+ ideal: number;
22
+ };
23
+ }
24
+ export interface AudioPlayerHandle {
25
+ /**
26
+ * Stop current playback and reset position.
27
+ * Called when the server sends a `clearAudio` event or on action interrupt.
28
+ */
29
+ pause(): void;
30
+ /**
31
+ * Release all resources tied to this player.
32
+ * Called during disconnect() cleanup.
33
+ */
34
+ cleanup(): void;
35
+ /**
36
+ * Register a callback to fire when natural playback ends.
37
+ * The core session uses this to send the `playedStream` event back to the server.
38
+ *
39
+ * NOTE — React Native: this callback is never invoked because react-native-webrtc
40
+ * routes audio automatically through the native stack. The RNAudioAdapter stubs
41
+ * this out and logs a warning. The server must not rely on receiving `playedStream`
42
+ * from RN clients; use a server-side estimated-duration fallback instead.
43
+ */
44
+ onEnded(callback: () => void): void;
45
+ }
46
+ export interface AudioAdapter {
47
+ /**
48
+ * Attach the incoming remote MediaStream to an audio output and start playing.
49
+ * Returns a handle the session holds for later pause / cleanup calls.
50
+ *
51
+ * Browser: creates an HTMLAudioElement, sets srcObject, calls play().
52
+ * React Native: no-op — react-native-webrtc routes audio to the native speaker
53
+ * automatically via the peer connection. Returns a stub handle.
54
+ */
55
+ createRemotePlayer(stream: MediaStream): AudioPlayerHandle;
56
+ }
57
+ export interface MediaAdapter {
58
+ /**
59
+ * Request the user's microphone.
60
+ *
61
+ * Browser: navigator.mediaDevices.getUserMedia({ audio: constraints })
62
+ * React Native: react-native-webrtc mediaDevices.getUserMedia (same API, polyfilled)
63
+ * Flutter: flutter_webrtc navigator.mediaDevices.getUserMedia (Dart, same shape)
64
+ */
65
+ getUserAudio(constraints: MediaAudioConstraints): Promise<MediaStream>;
66
+ /**
67
+ * Stop all tracks on a stream, releasing the underlying hardware device.
68
+ */
69
+ stopStream(stream: MediaStream): void;
70
+ }
@@ -0,0 +1,9 @@
1
+ import { AudioAdapter, AudioPlayerHandle } from '../interfaces';
2
+
3
+ /**
4
+ * React Native AudioAdapter.
5
+ * Returns a stub handle since react-native-webrtc manages audio output natively.
6
+ */
7
+ export declare class RNAudioAdapter implements AudioAdapter {
8
+ createRemotePlayer(_stream: MediaStream): AudioPlayerHandle;
9
+ }
@@ -0,0 +1,16 @@
1
+ import { DataChannelAdapter, DataChannelController, DataChannelHandlers } from '../PeerConnectionAdapter';
2
+
3
+ /**
4
+ * React Native DataChannelAdapter.
5
+ *
6
+ * react-native-webrtc delivers DataChannel messages as strings (no ArrayBuffer
7
+ * or Blob variants). This adapter wraps the channel without the binary
8
+ * normalisation needed in the browser version, keeping it lightweight.
9
+ *
10
+ * Difference from BrowserDataChannelAdapter:
11
+ * - No ArrayBuffer / Blob handling needed
12
+ * - readyState check still included for safety
13
+ */
14
+ export declare class RNDataChannelAdapter implements DataChannelAdapter {
15
+ bind(channel: RTCDataChannel, handlers: DataChannelHandlers): DataChannelController;
16
+ }
@@ -0,0 +1,17 @@
1
+ import { MediaAdapter, MediaAudioConstraints } from '../interfaces';
2
+
3
+ /**
4
+ * React Native MediaAdapter.
5
+ *
6
+ * react-native-webrtc polyfills `navigator.mediaDevices.getUserMedia` with
7
+ * the same API surface as the browser. This adapter calls it identically.
8
+ *
9
+ * getDisplayMedia (screen capture) is intentionally absent — it is not
10
+ * supported by react-native-webrtc and will throw if called on RN.
11
+ * Any UI that offers screen sharing must gate it behind `Platform.OS === 'web'`
12
+ * before calling the MediaAdapter.
13
+ */
14
+ export declare class RNMediaAdapter implements MediaAdapter {
15
+ getUserAudio(constraints: MediaAudioConstraints): Promise<MediaStream>;
16
+ stopStream(stream: MediaStream): void;
17
+ }
@@ -0,0 +1,19 @@
1
+ import { PeerConnectionAdapter, PeerConnectionConfig } from '../PeerConnectionAdapter';
2
+
3
+ /**
4
+ * React Native PeerConnectionAdapter.
5
+ *
6
+ * react-native-webrtc polyfills RTCPeerConnection into the global scope with
7
+ * the same API surface as the browser. This adapter wraps the construction
8
+ * identically to BrowserPeerAdapter — the value is isolation, not difference.
9
+ *
10
+ * Usage note: call `registerGlobals()` from react-native-webrtc once at
11
+ * app entry before constructing this adapter. Without that call,
12
+ * RTCPeerConnection will be undefined at runtime.
13
+ *
14
+ * import { registerGlobals } from 'react-native-webrtc';
15
+ * registerGlobals(); // call once in app entry (e.g. index.js)
16
+ */
17
+ export declare class RNPeerAdapter implements PeerConnectionAdapter {
18
+ create(config: PeerConnectionConfig): RTCPeerConnection;
19
+ }
@@ -0,0 +1,4 @@
1
+ export { RNAudioAdapter } from './RNAudioAdapter';
2
+ export { RNMediaAdapter } from './RNMediaAdapter';
3
+ export { RNPeerAdapter } from './RNPeerAdapter';
4
+ export { RNDataChannelAdapter } from './RNDataChannelAdapter';
@@ -0,0 +1 @@
1
+ export * from './platforms/browser'
@@ -42,6 +42,8 @@ export interface VaniraAIConfig {
42
42
  apiKey?: string;
43
43
  /** Override the default backend URL (e.g. http://localhost:8000). */
44
44
  backendUrl?: string;
45
+ /** Platform runtime configuration */
46
+ runtime?: any;
45
47
  }
46
48
  export interface ClientToolCall {
47
49
  /** The ref_code of the triggered action */
package/dist/index.d.ts CHANGED
@@ -1,10 +1,28 @@
1
+ /**
2
+ * @vanira/sdk — Main entry point
3
+ *
4
+ * For platform-specific imports with pre-wired adapters, prefer:
5
+ * import { ... } from '@vanira/sdk/browser' // Web
6
+ * import { ... } from '@vanira/sdk/react-native' // React Native
7
+ *
8
+ * This index exports core classes and types for callers that manage their
9
+ * own adapter wiring, or that import the browser bundle directly.
10
+ */
1
11
  export { VaniraAI } from './core/VaniraAI';
2
12
  export type { VaniraAIConfig, VaniraAIStatus, ClientToolCall, TranscriptionEvent } from './core/VaniraAI';
3
- export { VaniraWidget } from './ui/VaniraWidget';
4
13
  export { WebRTCClient } from './core/WebRTCClient';
5
- export { ConfigService } from './api/services/ConfigService';
6
- export { ChatService } from './api/services/ChatService';
7
14
  export { SessionManager } from './core/SessionManager';
8
15
  export type { SessionData, StoredMessage, SessionEvent } from './core/SessionManager';
16
+ export { ConfigService } from './api/services/ConfigService';
17
+ export { ChatService } from './api/services/ChatService';
18
+ export { VaniraWidget } from './ui/VaniraWidget';
19
+ export type { AudioAdapter, AudioPlayerHandle, MediaAdapter, MediaAudioConstraints, } from './adapters/interfaces';
20
+ export type { PeerConnectionAdapter, DataChannelAdapter, DataChannelController, DataChannelHandlers, DataChannelMessage, PeerConnectionConfig, PeerConnectionIceServer, } from './adapters/PeerConnectionAdapter';
21
+ export { BrowserAudioAdapter } from './adapters/browser/BrowserAudioAdapter';
22
+ export { BrowserMediaAdapter } from './adapters/browser/BrowserMediaAdapter';
23
+ export { BrowserPeerAdapter } from './adapters/browser/BrowserPeerAdapter';
24
+ export { BrowserDataChannelAdapter } from './adapters/browser/BrowserDataChannelAdapter';
25
+ export type { VaniraRuntime, RuntimeCapabilities } from './runtime/types';
26
+ export { browserRuntime, browserCapabilities, createBrowserClient, createBrowserAI, } from './runtime/browserRuntime';
9
27
  export * from './types';
10
28
  export * from './react';
@@ -0,0 +1,8 @@
1
+ "use strict";var J=Object.defineProperty;var W=(i,e,t)=>e in i?J(i,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[e]=t;var p=(i,e,t)=>W(i,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("../VaniraAI-EFYOmJ2E.cjs"),f=class f{constructor(e){p(this,"tabId");p(this,"sessionKey");p(this,"channelName");p(this,"channel",null);p(this,"heartbeatTimer",null);p(this,"listeners",new Map);const t=e.replace(/[^a-z0-9_-]/gi,"_");this.sessionKey=`vaniraai_session_${t}`,this.channelName=`vaniraai_channel_${t}`,this.tabId=this._getOrCreateTabId(),this._setupChannel()}getTabId(){return this.tabId}claimSession(){const e=this._loadSession();if(e){const o=Date.now()-e.lastActive,s=e.tabId===this.tabId,n=o>f.HEARTBEAT_TIMEOUT_MS;if(s)return this._startHeartbeat(),this._emit("session_restored"),!0;if(!n)return this._emit("tab_conflict"),!1}const t=e?{...e,tabId:this.tabId,lastActive:Date.now()}:{tabId:this.tabId,prospectId:"",chatId:null,conversationId:null,messages:[],lastActive:Date.now()};this._saveSession(t),this._startHeartbeat(),this._broadcast({type:"took_over",tabId:this.tabId});const a=!!(e!=null&&e.prospectId);return this._emit(a?"session_restored":"session_claimed"),!0}forceClaimSession(){const e=this._loadSession(),t=e?{...e,tabId:this.tabId,lastActive:Date.now()}:{tabId:this.tabId,prospectId:"",chatId:null,conversationId:null,messages:[],lastActive:Date.now()};this._saveSession(t),this._startHeartbeat(),this._broadcast({type:"took_over",tabId:this.tabId}),this._emit("session_claimed")}saveIds(e,t,a){const o=this._loadSession()??this._blankSession();this._saveSession({...o,prospectId:e,chatId:t,conversationId:a??o.conversationId??null,tabId:this.tabId,lastActive:Date.now()})}pushMessage(e,t){const a=this._loadSession()??this._blankSession();a.messages.push({role:e,content:t,timestamp:Date.now()}),a.messages.length>100&&(a.messages=a.messages.slice(-100)),this._saveSession({...a,tabId:this.tabId,lastActive:Date.now()})}updateLastAssistantMessage(e){const t=this._loadSession()??this._blankSession(),a=t.messages;let o=!1;for(let s=a.length-1;s>=0;s--)if(a[s].role==="assistant"&&a[s].content===""){a[s].content=e,o=!0;break}if(!o){for(let s=a.length-1;s>=0;s--)if(a[s].role==="assistant"){a[s].content=e,o=!0;break}}o||a.push({role:"assistant",content:e,timestamp:Date.now()}),this._saveSession({...t,tabId:this.tabId,lastActive:Date.now()})}getSession(){return this._loadSession()}clearSession(){localStorage.removeItem(this.sessionKey),this._broadcast({type:"session_cleared",tabId:this.tabId}),this._emit("session_cleared")}clearChatKeepProspect(){const e=this._loadSession();e&&this._saveSession({...e,chatId:null,conversationId:null,messages:[],lastActive:Date.now()})}destroy(){this._stopHeartbeat(),this.channel&&(this.channel.close(),this.channel=null)}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),this}off(e,t){var a;return(a=this.listeners.get(e))==null||a.delete(t),this}_getOrCreateTabId(){let e=sessionStorage.getItem("vaniraai_tab_id");return e||(e=`tab_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,sessionStorage.setItem("vaniraai_tab_id",e)),e}_setupChannel(){typeof BroadcastChannel>"u"||(this.channel=new BroadcastChannel(this.channelName),this.channel.onmessage=e=>{const t=e.data;t.tabId!==this.tabId&&(t.type==="took_over"&&(this._stopHeartbeat(),this._emit("tab_took_over")),t.type,t.type==="session_cleared"&&this._emit("session_cleared"))})}_broadcast(e){var t;(t=this.channel)==null||t.postMessage(e)}_startHeartbeat(){this._stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{const e=this._loadSession();e&&e.tabId===this.tabId&&(this._saveSession({...e,lastActive:Date.now()}),this._broadcast({type:"heartbeat",tabId:this.tabId}))},f.HEARTBEAT_INTERVAL_MS)}_stopHeartbeat(){this.heartbeatTimer!==null&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}_loadSession(){try{const e=localStorage.getItem(this.sessionKey);return e?JSON.parse(e):null}catch{return null}}_saveSession(e){try{localStorage.setItem(this.sessionKey,JSON.stringify(e))}catch(t){console.warn("[VaniraAI] SessionManager: failed to save session",t)}}_blankSession(){return{tabId:this.tabId,prospectId:"",chatId:null,conversationId:null,messages:[],lastActive:Date.now()}}_emit(e){var t;(t=this.listeners.get(e))==null||t.forEach(a=>a())}};p(f,"HEARTBEAT_INTERVAL_MS",5e3),p(f,"HEARTBEAT_TIMEOUT_MS",15e3);let C=f;const K="https://api.vanira.io";class F{static async fetchWidgetConfig(e,t){var o;const a={"Content-Type":"application/json"};t&&(a["X-API-Key"]=t);try{const s=await fetch(`${K}/assistant/widget/${e}/config`,{method:"GET",headers:a});if(!s.ok)throw new Error(`Widget config fetch failed: HTTP ${s.status}`);const n=await s.json();return(o=n.agent)!=null&&o.client&&(n.client=n.agent.client),n}catch(s){throw console.error("[VaniraAI] Failed to fetch widget config:",s),s}}}const q="https://coredb.travelr.club/v1/graphql";let g="https://inboxapi.travelr.club";class G{static setChatUrl(e){g=e}static async createChatProspect(e){var a,o;const t=`
2
+ mutation CreateChatProspect($prospectGroupId: uuid!, $name: String!) {
3
+ insert_prospects_one(object: {prospect_group_id: $prospectGroupId, name: $name, source: WEBSITE_WIDGET}) {
4
+ id
5
+ }
6
+ }
7
+ `;try{const n=await(await fetch(q,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:t,variables:{prospectGroupId:e,name:`Widget Guest ${new Date().toLocaleString()}`}})})).json();return n.errors?(console.warn("[VaniraAI] Prospect creation failed, using anonymous ID",n.errors),`anon_${Date.now()}_${Math.random().toString(36).substr(2,9)}`):((o=(a=n.data)==null?void 0:a.insert_prospects_one)==null?void 0:o.id)||`anon_${Date.now()}`}catch(s){return console.error("[VaniraAI] Failed to create prospect:",s),`anon_${Date.now()}`}}static async fetchWelcomeMessage(e,t,a,o){try{const s={"Content-Type":"application/json"},n=await fetch(`${g}/widget/chat`,{method:"POST",headers:s,body:JSON.stringify({agent_id:e,...a?{widget_id:a}:{},message:"",prospect_id:t,stream:!1})});if(!n.ok)throw new Error("Failed to fetch welcome message");const r=await n.json();let h=r.response||"Hey! how can I help you ?",d;const c=r.chat_id||r.inbox_id,u=r.conversation_id||null;return r.widget&&(d=r.widget),{role:"assistant",content:h,widget:d,chatId:c,conversationId:u}}catch(s){return console.error("[VaniraAI] Failed to fetch welcome message:",s),{role:"assistant",content:"Hey! how can I help you ?"}}}static async sendChatMessage(e,t,a,o,s,n,r,h,d){var c,u,w,m;try{const y={"Content-Type":"application/json"},S={agent_id:e,message:a,prospect_id:t,stream:!0};o&&(S.inbox_id=o),h&&(S.widget_id=h);const E=await fetch(`${g}/widget/chat`,{method:"POST",headers:y,body:JSON.stringify(S)});if(!E.ok)throw new Error("Chat request failed");const D=(c=E.body)==null?void 0:c.getReader(),j=new TextDecoder;if(!D)throw new Error("No reader");let B="",v="",_=null,b=null;for(;;){const{done:x,value:U}=await D.read();if(x)break;v+=j.decode(U,{stream:!0});const O=v.split(`
8
+ `);v=O.pop()||"";for(const L of O){const A=L.trim();if(A&&A.startsWith("data: ")){const $=A.slice(6);if($==="[DONE]"){r(_,b);return}try{const l=JSON.parse($);if(l.type==="metadata"){(l.chat_id||l.inbox_id)&&(_=l.chat_id||l.inbox_id),l.conversation_id&&(b=l.conversation_id);continue}const V=(m=(w=(u=l.choices)==null?void 0:u[0])==null?void 0:w.delta)==null?void 0:m.content;V&&(B+=V,s(B)),l.widget&&n(l.widget),l.chat_id&&!_&&(_=l.chat_id),l.conversation_id&&!b&&(b=l.conversation_id)}catch{}}}}r(_,b)}catch(y){console.error("[VaniraAI] Chat error:",y),s("Sorry, I encountered an error. Please try again."),r(null,null)}}static listenForAdminReplies(e,t,a){const o=`${g}/inbox/stream?inbox_id=${e}&sender=${encodeURIComponent(t)}`;let s=null,n=0,r=!1;const h=()=>{r||(s=new EventSource(o),console.log(`[VaniraAI] Subscribed to SSE EventSource at ${s.url}`),s.onopen=()=>{console.log("[VaniraAI] SSE connection opened successfully."),n=0},s.onmessage=d=>{console.log("[VaniraAI] 📡 Raw SSE Event triggered:",d.data);try{const c=JSON.parse(d.data);console.log("[VaniraAI] SSE stream JSON parsed:",c);const u=c.direction==="outgoing",w=!!c.content,m=c.source!=="ai";u&&w&&m&&(console.log("[VaniraAI] đŸŽ¯ Displaying admin message in chat UI:",c.content),a(c.content))}catch(c){console.error("[VaniraAI] SSE parse error",c)}},s.onerror=d=>{if(console.error("[VaniraAI] SSE connection error or disconnect",d),s&&(s.close(),s=null),r)return;const c=Math.min(2e3*Math.pow(2,n),3e4);console.log(`[VaniraAI] Reconnecting SSE in ${c}ms (Attempt ${n+1})...`),n++,setTimeout(h,c)})};return h(),{close:()=>{r=!0,s&&(s.close(),s=null,console.log("[VaniraAI] SSE connection manually closed."))}}}static async createCall(e,t,a,o,s){try{const n="https://api.vanira.io",r={"Content-Type":"application/json"};s&&(r["X-API-Key"]=s);const h=await fetch(`${n}/calls/create`,{method:"POST",headers:r,body:JSON.stringify({agent_id:e,prospect_id:a||void 0,type:"web"})}),d=await h.json();if(!h.ok)throw new Error(`[VaniraAI] Call creation failed HTTP ${h.status}`);if(!d.worker_url)throw new Error("[VaniraAI] Worker URL missing from response. Call cannot proceed.");return{callId:d.call_id||d.id||`web_${Date.now()}`,workerUrl:d.worker_url}}catch(n){throw console.error("[VaniraAI] Failed to create call:",n),n}}static async resolveConversation(e){try{if(!(await fetch(`${g}/inbox/conversations/resolve`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({conversation_id:e})})).ok)throw new Error("Failed to resolve conversation")}catch(t){throw console.error("[VaniraAI] Failed to resolve conversation:",t),t}}}class X{constructor(e){p(this,"el");this.el=new Audio,this.el.srcObject=e,this.el.play().catch(t=>console.warn("[BrowserAudio] Autoplay blocked — user gesture may be required:",t))}pause(){}cleanup(){this.el.pause(),this.el.srcObject=null}onEnded(e){this.el.onended=e}}class P{createRemotePlayer(e){return new X(e)}}class R{async getUserAudio(e){return navigator.mediaDevices.getUserMedia({audio:e})}stopStream(e){e.getTracks().forEach(t=>t.stop())}}class N{create(e){return new RTCPeerConnection({iceServers:e.iceServers,iceTransportPolicy:e.iceTransportPolicy??"all"})}}class k{bind(e,t){return e.onopen=()=>t.onOpen(),e.onerror=a=>t.onError(a),e.onmessage=a=>{if(typeof a.data=="string"){t.onMessage({text:a.data});return}if(a.data instanceof ArrayBuffer){try{const o=new TextDecoder().decode(a.data),s=JSON.parse(o);(s==null?void 0:s.event)==="client_tool_call"?(console.log("[DataChannel] Binary client_tool_call decoded and forwarded"),t.onMessage({text:o})):console.log("[DataChannel] Binary frame dropped (not client_tool_call):",s==null?void 0:s.event)}catch{console.log("[DataChannel] Non-decodable binary frame:",a.data.byteLength,"bytes — dropped")}return}a.data instanceof Blob&&a.data.text().then(o=>{t.onMessage({text:o})}).catch(o=>{console.warn("[DataChannel] Failed to read Blob message:",o)})},{send(a){e.readyState==="open"&&e.send(a)},isOpen(){return e.readyState==="open"},close(){e.close()}}}}var M;const H={supportsAudioEndedEvent:!0,supportsScreenShare:typeof navigator<"u"&&typeof((M=navigator.mediaDevices)==null?void 0:M.getDisplayMedia)=="function",supportsDom:typeof document<"u",supportsHtmlAudio:typeof Audio<"u",supportsCustomElements:typeof customElements<"u",supportsLocalStorage:typeof localStorage<"u",supportsBroadcastChannel:typeof BroadcastChannel<"u"},T={name:"browser",callType:"web",runtimeName:"browser",callIdPrefix:"web_",capabilities:H,audio:new P,media:new R,peer:new N,dataChannel:new k};function z(i){return new I.WebRTCClient({...i,runtime:T})}function Q(i){return new I.VaniraAI({...i,runtime:T})}exports.VaniraAI=I.VaniraAI;exports.WebRTCClient=I.WebRTCClient;exports.BrowserAudioAdapter=P;exports.BrowserDataChannelAdapter=k;exports.BrowserMediaAdapter=R;exports.BrowserPeerAdapter=N;exports.ChatService=G;exports.ConfigService=F;exports.SessionManager=C;exports.browserCapabilities=H;exports.browserRuntime=T;exports.createBrowserAI=Q;exports.createBrowserClient=z;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @vanira/sdk — Browser entry point
3
+ *
4
+ * Usage:
5
+ * import { createBrowserClient, VaniraAI, WebRTCClient } from '@vanira/sdk/browser'
6
+ *
7
+ * This entry point:
8
+ * - Exports factory functions (createBrowserClient, createBrowserAI) that
9
+ * pre-wire all browser adapters via browserRuntime.
10
+ * - Re-exports core classes and adapter classes for manual wiring.
11
+ * - Does NOT include widget / custom-element / DOM bootstrapping code
12
+ * (that lives in cdn.ts and ui/).
13
+ */
14
+ export { createBrowserClient, createBrowserAI, browserRuntime, browserCapabilities, } from '../runtime/browserRuntime';
15
+ export { WebRTCClient } from '../core/WebRTCClient';
16
+ export { VaniraAI } from '../core/VaniraAI';
17
+ export { SessionManager } from '../core/SessionManager';
18
+ export { BrowserAudioAdapter } from '../adapters/browser/BrowserAudioAdapter';
19
+ export { BrowserMediaAdapter } from '../adapters/browser/BrowserMediaAdapter';
20
+ export { BrowserPeerAdapter } from '../adapters/browser/BrowserPeerAdapter';
21
+ export { BrowserDataChannelAdapter } from '../adapters/browser/BrowserDataChannelAdapter';
22
+ export { ConfigService } from '../api/services/ConfigService';
23
+ export { ChatService } from '../api/services/ChatService';
24
+ export type { WebRTCClientConfig, ControlEvent, WidgetConfig, WidgetMode, Agent, } from '../types';
25
+ export type { VaniraAIConfig, VaniraAIStatus, ClientToolCall, TranscriptionEvent, } from '../core/VaniraAI';
26
+ export type { AudioAdapter, MediaAdapter, AudioPlayerHandle, MediaAudioConstraints, } from '../adapters/interfaces';
27
+ export type { PeerConnectionAdapter, DataChannelAdapter, PeerConnectionConfig, DataChannelController, } from '../adapters/PeerConnectionAdapter';
28
+ export type { VaniraRuntime, RuntimeCapabilities, } from '../runtime/types';