avin-ai 0.2.5 → 0.2.6

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.
@@ -86,12 +86,10 @@ class b {
86
86
  const a = new TextDecoder().decode(r.data);
87
87
  try {
88
88
  const A = JSON.parse(a);
89
- console.log("[OvinAI] Decoded JSON from binary (inspect only):", A);
89
+ console.log("[OvinAI] Decoded JSON from binary:", A), A && typeof A == "object" && A.event && this.handleControlEvent(A);
90
90
  } catch {
91
- console.log("[OvinAI] Decoded String from binary:", a);
92
91
  }
93
92
  } catch {
94
- console.log("[OvinAI] Received binary data:", r.data.byteLength, "bytes (not decodable)");
95
93
  }
96
94
  else r.data instanceof Blob && r.data.text().then((a) => {
97
95
  try {
package/dist/avin-ai.js CHANGED
@@ -28,14 +28,14 @@ var ce=Object.defineProperty;var de=(w,p,B)=>p in w?ce(w,p,{enumerable:!0,config
28
28
  id
29
29
  }
30
30
  }
31
- `;try{const r=await(await fetch(P,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,variables:{prospectGroupId:t,name:`Widget Guest ${new Date().toLocaleString()}`}})})).json();return r.errors?(console.warn("[AvinAI] Prospect creation failed, using anonymous ID",r.errors),`anon_${Date.now()}_${Math.random().toString(36).substr(2,9)}`):((n=(i=r.data)==null?void 0:i.insert_prospects_one)==null?void 0:n.id)||`anon_${Date.now()}`}catch(s){return console.error("[AvinAI] Failed to create prospect:",s),`anon_${Date.now()}`}}static async fetchWelcomeMessage(t,e){var i,n;try{const s=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,message:"",prospect_id:e,stream:!1})});if(!s.ok)throw new Error("Failed to fetch welcome message");const r=await s.json();let a=r.response||"Hello! How can I help you today?",A;const d=r.chat_id||r.inbox_id;if(r.widget){A=r.widget;const c={text:a};A.type==="carousel"&&((i=A.data)!=null&&i.cards)?c.cards=A.data.cards:A.type==="button_list"&&((n=A.data)!=null&&n.buttons)&&(c.buttons=A.data.buttons),a=JSON.stringify(c)}return{role:"assistant",content:a,widget:A,chatId:d}}catch(s){return console.error("[AvinAI] Failed to fetch welcome message:",s),{role:"assistant",content:"Hello! How can I help you today?"}}}static async sendChatMessage(t,e,i,n,s,r,a){var A,d,c,v;try{const u={agent_id:t,message:i,prospect_id:e,stream:!0};n&&(u.inbox_id=n);const f=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)});if(!f.ok)throw new Error("Chat request failed");const E=(A=f.body)==null?void 0:A.getReader(),D=new TextDecoder;if(!E)throw new Error("No reader");let b="",x="",y=null;for(;;){const{done:ae,value:Ae}=await E.read();if(ae)break;x+=D.decode(Ae,{stream:!0});const U=x.split(`
32
- `);x=U.pop()||"";for(const le of U){const k=le.trim();if(k&&k.startsWith("data: ")){const W=k.slice(6);if(W==="[DONE]"){a(y);return}try{const C=JSON.parse(W);if(C.type==="metadata"&&C.chat_id){y=C.chat_id;continue}const X=(v=(c=(d=C.choices)==null?void 0:d[0])==null?void 0:c.delta)==null?void 0:v.content;X&&(b+=X,s(b)),C.widget&&r(C.widget),C.chat_id&&!y&&(y=C.chat_id)}catch{}}}}a(y)}catch(u){console.error("[AvinAI] Chat error:",u),s("Sorry, I encountered an error. Please try again."),a(null)}}static listenForAdminReplies(t,e,i){const n=`${I}/inbox/stream?inbox_id=${t}&sender=${encodeURIComponent(e)}`;let s=null,r=0,a=!1;const A=()=>{a||(s=new EventSource(n),console.log(`[AvinAI] Subscribed to SSE EventSource at ${s.url}`),s.onopen=()=>{console.log("[AvinAI] SSE connection opened successfully."),r=0},s.onmessage=d=>{console.log("[AvinAI] 📡 Raw SSE Event triggered:",d.data);try{const c=JSON.parse(d.data);console.log("[AvinAI] SSE stream JSON parsed:",c);const v=c.direction==="outgoing",u=!!c.content,f=c.source!=="ai";v&&u&&f&&(console.log("[AvinAI] đŸŽ¯ Displaying admin message in chat UI:",c.content),i(c.content))}catch(c){console.error("[AvinAI] SSE parse error",c)}},s.onerror=d=>{if(console.error("[AvinAI] SSE connection error or disconnect",d),s&&(s.close(),s=null),a)return;const c=Math.min(2e3*Math.pow(2,r),3e4);console.log(`[AvinAI] Reconnecting SSE in ${c}ms (Attempt ${r+1})...`),r++,setTimeout(A,c)})};return A(),{close:()=>{a=!0,s&&(s.close(),s=null,console.log("[AvinAI] SSE connection manually closed."))}}}static async createCall(t,e,i){var s,r;const n=`
31
+ `;try{const r=await(await fetch(P,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,variables:{prospectGroupId:t,name:`Widget Guest ${new Date().toLocaleString()}`}})})).json();return r.errors?(console.warn("[AvinAI] Prospect creation failed, using anonymous ID",r.errors),`anon_${Date.now()}_${Math.random().toString(36).substr(2,9)}`):((n=(i=r.data)==null?void 0:i.insert_prospects_one)==null?void 0:n.id)||`anon_${Date.now()}`}catch(s){return console.error("[AvinAI] Failed to create prospect:",s),`anon_${Date.now()}`}}static async fetchWelcomeMessage(t,e){var i,n;try{const s=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,message:"",prospect_id:e,stream:!1})});if(!s.ok)throw new Error("Failed to fetch welcome message");const r=await s.json();let a=r.response||"Hello! How can I help you today?",A;const d=r.chat_id||r.inbox_id;if(r.widget){A=r.widget;const c={text:a};A.type==="carousel"&&((i=A.data)!=null&&i.cards)?c.cards=A.data.cards:A.type==="button_list"&&((n=A.data)!=null&&n.buttons)&&(c.buttons=A.data.buttons),a=JSON.stringify(c)}return{role:"assistant",content:a,widget:A,chatId:d}}catch(s){return console.error("[AvinAI] Failed to fetch welcome message:",s),{role:"assistant",content:"Hello! How can I help you today?"}}}static async sendChatMessage(t,e,i,n,s,r,a){var A,d,c,v;try{const u={agent_id:t,message:i,prospect_id:e,stream:!0};n&&(u.inbox_id=n);const f=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)});if(!f.ok)throw new Error("Chat request failed");const E=(A=f.body)==null?void 0:A.getReader(),D=new TextDecoder;if(!E)throw new Error("No reader");let b="",x="",y=null;for(;;){const{done:ae,value:Ae}=await E.read();if(ae)break;x+=D.decode(Ae,{stream:!0});const O=x.split(`
32
+ `);x=O.pop()||"";for(const le of O){const k=le.trim();if(k&&k.startsWith("data: ")){const W=k.slice(6);if(W==="[DONE]"){a(y);return}try{const C=JSON.parse(W);if(C.type==="metadata"&&C.chat_id){y=C.chat_id;continue}const X=(v=(c=(d=C.choices)==null?void 0:d[0])==null?void 0:c.delta)==null?void 0:v.content;X&&(b+=X,s(b)),C.widget&&r(C.widget),C.chat_id&&!y&&(y=C.chat_id)}catch{}}}}a(y)}catch(u){console.error("[AvinAI] Chat error:",u),s("Sorry, I encountered an error. Please try again."),a(null)}}static listenForAdminReplies(t,e,i){const n=`${I}/inbox/stream?inbox_id=${t}&sender=${encodeURIComponent(e)}`;let s=null,r=0,a=!1;const A=()=>{a||(s=new EventSource(n),console.log(`[AvinAI] Subscribed to SSE EventSource at ${s.url}`),s.onopen=()=>{console.log("[AvinAI] SSE connection opened successfully."),r=0},s.onmessage=d=>{console.log("[AvinAI] 📡 Raw SSE Event triggered:",d.data);try{const c=JSON.parse(d.data);console.log("[AvinAI] SSE stream JSON parsed:",c);const v=c.direction==="outgoing",u=!!c.content,f=c.source!=="ai";v&&u&&f&&(console.log("[AvinAI] đŸŽ¯ Displaying admin message in chat UI:",c.content),i(c.content))}catch(c){console.error("[AvinAI] SSE parse error",c)}},s.onerror=d=>{if(console.error("[AvinAI] SSE connection error or disconnect",d),s&&(s.close(),s=null),a)return;const c=Math.min(2e3*Math.pow(2,r),3e4);console.log(`[AvinAI] Reconnecting SSE in ${c}ms (Attempt ${r+1})...`),r++,setTimeout(A,c)})};return A(),{close:()=>{a=!0,s&&(s.close(),s=null,console.log("[AvinAI] SSE connection manually closed."))}}}static async createCall(t,e,i){var s,r;const n=`
33
33
  mutation CreateCall($object: calls_insert_input!) {
34
34
  insert_calls_one(object: $object) {
35
35
  id
36
36
  }
37
37
  }
38
- `;try{const A=await(await fetch(P,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:n,variables:{object:{agent_id:t,prospect_id:e,status:"initiated",direction:"inbound",type:"web_call",is_ai_avatar_enabled:i.includes("avatar")}}})})).json();return A.errors?(console.warn("[AvinAI] Call creation failed",A.errors),`web_${Date.now()}_${Math.random().toString(36).substr(2,8)}`):((r=(s=A.data)==null?void 0:s.insert_calls_one)==null?void 0:r.id)||`web_${Date.now()}`}catch(a){return console.error("[AvinAI] Failed to create call:",a),`web_${Date.now()}_${Math.random().toString(36).substr(2,8)}`}}}class F{constructor(t){o(this,"config");o(this,"root",null);this.config=t}async initialize(t){this.config=t}}class M{constructor(t){o(this,"serverUrl");o(this,"agentId");o(this,"callId");o(this,"onConnected");o(this,"onDisconnected");o(this,"onError");o(this,"onTranscription");o(this,"onRemoteTrack");o(this,"onClientToolCall");o(this,"pc",null);o(this,"dataChannel",null);o(this,"audioElement",null);o(this,"connected",!1);o(this,"iceServers");o(this,"token");if(!t.serverUrl)throw new Error("serverUrl is required");if(!t.agentId)throw new Error("agentId is required");this.serverUrl=t.serverUrl.replace(/\/$/,""),this.agentId=t.agentId,this.callId=t.callId||this.generateCallId(),this.iceServers=t.iceServers,this.token=t.token,this.onConnected=t.onConnected||(()=>{}),this.onDisconnected=t.onDisconnected||(()=>{}),this.onError=t.onError||(e=>console.error("[WebRTC]",e)),this.onTranscription=t.onTranscription||(()=>{}),this.onRemoteTrack=t.onRemoteTrack||(()=>{}),this.onClientToolCall=t.onClientToolCall||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{if(!this.iceServers&&this.token)try{this.iceServers=await M.fetchIceServers(this.token)}catch(r){console.warn("âš ī¸ [WebRTC] Failed to fetch ICE servers:",r)}this.pc=new RTCPeerConnection({iceServers:this.iceServers||[{urls:"stun:global.relay.metered.ca:80"},{urls:["turns:global.relay.metered.ca:443?transport=tcp","turn:global.relay.metered.ca:80?transport=tcp","turn:global.relay.metered.ca:443?transport=tcp"],username:"fa97658be3343d21da3b65e6",credential:"HXHDoqeHbvZrmCuf"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("🎤 [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),t.getTracks().forEach(r=>r.stop());return}if(t.getTracks().forEach(r=>{var a;(a=this.pc)==null||a.addTrack(r,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=r=>{if(typeof r.data=="string")try{this.handleControlEvent(JSON.parse(r.data))}catch{console.warn("[WebRTC] Failed to parse message:",r.data)}else if(r.data instanceof ArrayBuffer)try{const a=new TextDecoder().decode(r.data);try{const A=JSON.parse(a);console.log("[OvinAI] Decoded JSON from binary (inspect only):",A)}catch{console.log("[OvinAI] Decoded String from binary:",a)}}catch{console.log("[OvinAI] Received binary data:",r.data.byteLength,"bytes (not decodable)")}else r.data instanceof Blob&&r.data.text().then(a=>{try{this.handleControlEvent(JSON.parse(a))}catch{console.warn("[WebRTC] Failed to parse blob data:",a)}})},this.dataChannel.onerror=r=>console.error("❌ [WebRTC] DataChannel error:",r),this.pc.ontrack=r=>{const a=r.track,A=r.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${a.kind} track`),a.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=A,this.audioElement.play().catch(d=>console.warn("Audio autoplay blocked:",d)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")}):a.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(a,A))};const e=()=>{var A,d,c,v,u,f,E,D,b,x,y;console.log("🔄 [WebRTC] State:",(A=this.pc)==null?void 0:A.connectionState,"| ICE:",(d=this.pc)==null?void 0:d.iceConnectionState);const r=((c=this.pc)==null?void 0:c.connectionState)==="connected"||((v=this.pc)==null?void 0:v.iceConnectionState)==="connected"||((u=this.pc)==null?void 0:u.iceConnectionState)==="completed",a=((f=this.pc)==null?void 0:f.connectionState)==="failed"||((E=this.pc)==null?void 0:E.iceConnectionState)==="failed"||((D=this.pc)==null?void 0:D.connectionState)==="closed"||((b=this.pc)==null?void 0:b.iceConnectionState)==="closed"||((x=this.pc)==null?void 0:x.connectionState)==="disconnected"||((y=this.pc)==null?void 0:y.iceConnectionState)==="disconnected";r&&!this.connected?(this.connected=!0,this.onConnected()):a&&this.connected&&(this.connected=!1,this.onDisconnected())};this.pc.onconnectionstatechange=e,this.pc.oniceconnectionstatechange=e;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 n=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!n.ok){const r=await n.json();throw new Error(r.error||`HTTP ${n.status}`)}const{answer:s}=await n.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(s),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(t=>{if(!this.pc)return t();if(this.pc.iceGatheringState==="complete")t();else{const e=()=>{var i,n;((i=this.pc)==null?void 0:i.iceGatheringState)==="complete"&&((n=this.pc)==null||n.removeEventListener("icegatheringstatechange",e),t())};this.pc.addEventListener("icegatheringstatechange",e),setTimeout(()=>{var i;(i=this.pc)==null||i.removeEventListener("icegatheringstatechange",e),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),t()},5e3)}})}sendEvent(t,e={}){var i;((i=this.dataChannel)==null?void 0:i.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:t,...e}))}handleControlEvent(t){switch(t.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",t.text),this.onTranscription(t.text,t.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",t.name);break;case"client_tool_call":console.log("đŸ› ī¸ [OvinAI] Client Tool Call:",t),this.onClientToolCall(t.tool_call||t.data||t);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",t.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(t=>{t.track&&t.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}sendToolResult(t,e){this.sendEvent("client_tool_result",{call_id:t,result:e})}sendSilentContext(t){this.sendEvent("silent_context",{context:t})}triggerActionInterrupt(){this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0),this.sendEvent("action_interrupt"),console.log("🛑 [OvinAI] Triggered client-side action interrupt")}static async fetchIceServers(t,e="https://coredb.travelr.club/v1/graphql"){var i;try{const s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:t},body:JSON.stringify({query:`
38
+ `;try{const A=await(await fetch(P,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:n,variables:{object:{agent_id:t,prospect_id:e,status:"initiated",direction:"inbound",type:"web_call",is_ai_avatar_enabled:i.includes("avatar")}}})})).json();return A.errors?(console.warn("[AvinAI] Call creation failed",A.errors),`web_${Date.now()}_${Math.random().toString(36).substr(2,8)}`):((r=(s=A.data)==null?void 0:s.insert_calls_one)==null?void 0:r.id)||`web_${Date.now()}`}catch(a){return console.error("[AvinAI] Failed to create call:",a),`web_${Date.now()}_${Math.random().toString(36).substr(2,8)}`}}}class F{constructor(t){o(this,"config");o(this,"root",null);this.config=t}async initialize(t){this.config=t}}class M{constructor(t){o(this,"serverUrl");o(this,"agentId");o(this,"callId");o(this,"onConnected");o(this,"onDisconnected");o(this,"onError");o(this,"onTranscription");o(this,"onRemoteTrack");o(this,"onClientToolCall");o(this,"pc",null);o(this,"dataChannel",null);o(this,"audioElement",null);o(this,"connected",!1);o(this,"iceServers");o(this,"token");if(!t.serverUrl)throw new Error("serverUrl is required");if(!t.agentId)throw new Error("agentId is required");this.serverUrl=t.serverUrl.replace(/\/$/,""),this.agentId=t.agentId,this.callId=t.callId||this.generateCallId(),this.iceServers=t.iceServers,this.token=t.token,this.onConnected=t.onConnected||(()=>{}),this.onDisconnected=t.onDisconnected||(()=>{}),this.onError=t.onError||(e=>console.error("[WebRTC]",e)),this.onTranscription=t.onTranscription||(()=>{}),this.onRemoteTrack=t.onRemoteTrack||(()=>{}),this.onClientToolCall=t.onClientToolCall||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{if(!this.iceServers&&this.token)try{this.iceServers=await M.fetchIceServers(this.token)}catch(r){console.warn("âš ī¸ [WebRTC] Failed to fetch ICE servers:",r)}this.pc=new RTCPeerConnection({iceServers:this.iceServers||[{urls:"stun:global.relay.metered.ca:80"},{urls:["turns:global.relay.metered.ca:443?transport=tcp","turn:global.relay.metered.ca:80?transport=tcp","turn:global.relay.metered.ca:443?transport=tcp"],username:"fa97658be3343d21da3b65e6",credential:"HXHDoqeHbvZrmCuf"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("🎤 [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),t.getTracks().forEach(r=>r.stop());return}if(t.getTracks().forEach(r=>{var a;(a=this.pc)==null||a.addTrack(r,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=r=>{if(typeof r.data=="string")try{this.handleControlEvent(JSON.parse(r.data))}catch{console.warn("[WebRTC] Failed to parse message:",r.data)}else if(r.data instanceof ArrayBuffer)try{const a=new TextDecoder().decode(r.data);try{const A=JSON.parse(a);console.log("[OvinAI] Decoded JSON from binary:",A),A&&typeof A=="object"&&A.event&&this.handleControlEvent(A)}catch{}}catch{}else r.data instanceof Blob&&r.data.text().then(a=>{try{this.handleControlEvent(JSON.parse(a))}catch{console.warn("[WebRTC] Failed to parse blob data:",a)}})},this.dataChannel.onerror=r=>console.error("❌ [WebRTC] DataChannel error:",r),this.pc.ontrack=r=>{const a=r.track,A=r.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${a.kind} track`),a.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=A,this.audioElement.play().catch(d=>console.warn("Audio autoplay blocked:",d)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")}):a.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(a,A))};const e=()=>{var A,d,c,v,u,f,E,D,b,x,y;console.log("🔄 [WebRTC] State:",(A=this.pc)==null?void 0:A.connectionState,"| ICE:",(d=this.pc)==null?void 0:d.iceConnectionState);const r=((c=this.pc)==null?void 0:c.connectionState)==="connected"||((v=this.pc)==null?void 0:v.iceConnectionState)==="connected"||((u=this.pc)==null?void 0:u.iceConnectionState)==="completed",a=((f=this.pc)==null?void 0:f.connectionState)==="failed"||((E=this.pc)==null?void 0:E.iceConnectionState)==="failed"||((D=this.pc)==null?void 0:D.connectionState)==="closed"||((b=this.pc)==null?void 0:b.iceConnectionState)==="closed"||((x=this.pc)==null?void 0:x.connectionState)==="disconnected"||((y=this.pc)==null?void 0:y.iceConnectionState)==="disconnected";r&&!this.connected?(this.connected=!0,this.onConnected()):a&&this.connected&&(this.connected=!1,this.onDisconnected())};this.pc.onconnectionstatechange=e,this.pc.oniceconnectionstatechange=e;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 n=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!n.ok){const r=await n.json();throw new Error(r.error||`HTTP ${n.status}`)}const{answer:s}=await n.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(s),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(t=>{if(!this.pc)return t();if(this.pc.iceGatheringState==="complete")t();else{const e=()=>{var i,n;((i=this.pc)==null?void 0:i.iceGatheringState)==="complete"&&((n=this.pc)==null||n.removeEventListener("icegatheringstatechange",e),t())};this.pc.addEventListener("icegatheringstatechange",e),setTimeout(()=>{var i;(i=this.pc)==null||i.removeEventListener("icegatheringstatechange",e),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),t()},5e3)}})}sendEvent(t,e={}){var i;((i=this.dataChannel)==null?void 0:i.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:t,...e}))}handleControlEvent(t){switch(t.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",t.text),this.onTranscription(t.text,t.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",t.name);break;case"client_tool_call":console.log("đŸ› ī¸ [OvinAI] Client Tool Call:",t),this.onClientToolCall(t.tool_call||t.data||t);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",t.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(t=>{t.track&&t.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}sendToolResult(t,e){this.sendEvent("client_tool_result",{call_id:t,result:e})}sendSilentContext(t){this.sendEvent("silent_context",{context:t})}triggerActionInterrupt(){this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0),this.sendEvent("action_interrupt"),console.log("🛑 [OvinAI] Triggered client-side action interrupt")}static async fetchIceServers(t,e="https://coredb.travelr.club/v1/graphql"){var i;try{const s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:t},body:JSON.stringify({query:`
39
39
  query GetIceServers {
40
40
  ice_servers(where: {enabled: {_eq: true}}) {
41
41
  urls
@@ -962,4 +962,4 @@ overflow: hidden;
962
962
  ${j.styles}
963
963
  ${z.styles}
964
964
  ${this.gradient?`.widget-fab { background: ${this.gradient} !important; }`:""}
965
- `,this.root.appendChild(i),this.floatingButton=new H(()=>this.togglePanel()),this.floatingButton.setIcon(this.getFabIcon()),this.floatingButton.setPosition(this.getPosStyle()),this.floatingWelcomeChips=new j(n=>this.handleFloatingChipClick(n)),this.floatingWelcomeChips.setPosition(this.getPosStyle()),this.panel=new z(()=>this.closePanel(),this.getPanelTitle()),this.root.appendChild(this.floatingWelcomeChips.getElement()),this.root.appendChild(this.floatingButton.getElement()),this.root.appendChild(this.panel.getElement()),this.setupPanelContent(),this.widgetMode.includes("chat")&&this.initializeChatSession()}getFabIcon(){return this.widgetIcon&&Q[this.widgetIcon]?Q[this.widgetIcon]:this.widgetMode.includes("chat")?h.chat:h.audioLines}getPanelTitle(){let e="Voice Assistant";return this.widgetMode.includes("chat")&&(e="Chat Assistant"),this.widgetMode.includes("avatar")&&(e="AI Avatar Chat"),e}getPosStyle(){const e={"bottom-right":"bottom: 24px; right: 24px;","bottom-left":"bottom: 24px; left: 24px;","top-right":"top: 24px; right: 24px;","top-left":"top: 24px; left: 24px;"};return e[this.position]||e["bottom-right"]}setupPanelContent(){const e=[this.primaryColor,this.secondaryColor];switch(this.widgetMode){case"chat_only":this.currentView=new ee(i=>this.handleChatSend(i));break;case"chat_voice":this.currentView=new te(i=>this.handleChatSend(i),()=>this.create_call(),()=>this.end_call(),...e);break;case"avatar_only":this.currentView=new ie(()=>this.create_call(),()=>this.end_call(),...e);break;case"chat_avatar":this.currentView=new ne(i=>this.handleChatSend(i),()=>this.create_call(),()=>this.end_call(),...e);break;case"voice_only":default:this.currentView=new $(()=>this.create_call(),()=>this.end_call(),...e);break}this.currentView&&this.panel&&this.panel.setContent(this.currentView.getElement())}async handleChatSend(e){var n,s;if(!e)return;const i=(s=(n=this.currentView)==null?void 0:n.getChatWindow)==null?void 0:s.call(n);if(i){this.chatId&&!this.eventSource&&(this.eventSource=m.listenForAdminReplies(this.chatId,this.prospectId,r=>{var a,A;(A=(a=this.currentView)==null?void 0:a.getChatWindow)==null||A.call(a).addMessage("assistant",r)})),i.addMessage("user",e),i.setTyping(!0);try{await this.chatAdapter.sendMessage(e,this.agentId,this.prospectId,this.chatId,r=>{var a;i.setTyping(!1),r&&r.type==="button_list"&&((a=r.data)!=null&&a.buttons)&&(i.addButtons(r.data.buttons),this.welcomeChipsData=r.data.buttons,this.updateWelcomeChipsVisibility())},r=>{i.setTyping(!1),i.updateLastAssistantMessage(r)},r=>{r&&(this.chatId=r)})}catch(r){i.setTyping(!1),console.error("Send message failed",r),i.addMessage("assistant","Error sending message.")}}}togglePanel(){this.isPanelOpen?this.closePanel():this.openPanel()}openPanel(){var e;this.isPanelOpen=!0,this.updateWelcomeChipsVisibility(),(e=this.panel)==null||e.open({bottom:this.position.includes("bottom")?"100px":void 0,top:this.position.includes("top")?"100px":void 0,right:this.position.includes("right")?"24px":void 0,left:this.position.includes("left")?"24px":void 0})}closePanel(){var e;this.isPanelOpen=!1,this.updateWelcomeChipsVisibility(),(e=this.panel)==null||e.close()}updateWelcomeChipsVisibility(){this.floatingWelcomeChips&&(!this.isPanelOpen&&this.welcomeChipsData.length>0?(this.floatingWelcomeChips.setChips(this.welcomeChipsData),this.floatingWelcomeChips.getElement().style.display="flex"):this.floatingWelcomeChips.getElement().style.display="none")}handleFloatingChipClick(e){this.openPanel(),this.handleChatSend(e)}}class re{static getProvider(t){return new oe(t)}}class O extends HTMLElement{constructor(){super();o(this,"shadow");o(this,"provider",null);o(this,"widgetId","");o(this,"agentId","");o(this,"serverUrl","https://inboxapi.travelr.club");o(this,"position","bottom-right");o(this,"primaryColor","#6366f1");o(this,"secondaryColor","#4f46e5");o(this,"gradient",null);this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","secondary-color","server-url","gradient"]}connectedCallback(){console.log("[AvinAI Widget] Initializing..."),this.readAttributes();const e={widgetId:this.widgetId,agentId:this.agentId,serverUrl:this.serverUrl,position:this.position,primaryColor:this.primaryColor,secondaryColor:this.secondaryColor,gradient:this.gradient};this.provider=re.getProvider(e),this.provider&&this.provider.ui_renderer(this.shadow),this.widgetId&&!this.agentId&&this.initializeExpandedConfig()}readAttributes(){this.widgetId=this.getAttribute("widget-id")||"",this.agentId=this.getAttribute("agent-id")||"",this.serverUrl=this.getAttribute("server-url")||"https://inboxapi.travelr.club",this.position=this.getAttribute("position")||"bottom-right",this.primaryColor=this.getAttribute("primary-color")||"#6366f1",this.secondaryColor=this.getAttribute("secondary-color")||this.primaryColor,this.gradient=this.getAttribute("gradient")}async initializeExpandedConfig(){try{const e=await B.fetchWidgetConfig(this.widgetId);this.provider&&await this.provider.initialize(e)}catch(e){console.error("[AvinAI Widget] Config failed:",e)}}}customElements.get("avin-convai")||customElements.define("avin-convai",O),typeof window<"u"&&(window.AvinConvAI=O,window.AvinAI=S);const g=document.currentScript;if(g){const l=g.getAttribute("data-widget-id")||g.getAttribute("widget-id"),t=g.getAttribute("data-agent-id")||g.getAttribute("agent-id");if(l||t){const e=g.getAttribute("data-position")||"bottom-right",i=g.getAttribute("data-primary-color")||g.getAttribute("primary-color")||"#6366f1",n=g.getAttribute("data-secondary-color")||g.getAttribute("secondary-color"),s=g.getAttribute("data-gradient")||g.getAttribute("gradient"),r=g.getAttribute("data-server-url")||g.getAttribute("server-url"),a=document.createElement("avin-convai");l&&a.setAttribute("widget-id",l),t&&a.setAttribute("agent-id",t),a.setAttribute("position",e),a.setAttribute("primary-color",i),n&&a.setAttribute("secondary-color",n),s&&a.setAttribute("gradient",s),r&&a.setAttribute("server-url",r),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>document.body.appendChild(a)):document.body.appendChild(a),a.addEventListener("avinai:tool_call",A=>{const d=A.detail;console.log("[AvinAI Widget] Tool call received by host page:",d.name,d.arguments),typeof window.__avinai_tool_handler=="function"&&window.__avinai_tool_handler(d,{sendResult:c=>{console.log("[AvinAI] sendResult called:",c)}})})}}})();
965
+ `,this.root.appendChild(i),this.floatingButton=new H(()=>this.togglePanel()),this.floatingButton.setIcon(this.getFabIcon()),this.floatingButton.setPosition(this.getPosStyle()),this.floatingWelcomeChips=new j(n=>this.handleFloatingChipClick(n)),this.floatingWelcomeChips.setPosition(this.getPosStyle()),this.panel=new z(()=>this.closePanel(),this.getPanelTitle()),this.root.appendChild(this.floatingWelcomeChips.getElement()),this.root.appendChild(this.floatingButton.getElement()),this.root.appendChild(this.panel.getElement()),this.setupPanelContent(),this.widgetMode.includes("chat")&&this.initializeChatSession()}getFabIcon(){return this.widgetIcon&&Q[this.widgetIcon]?Q[this.widgetIcon]:this.widgetMode.includes("chat")?h.chat:h.audioLines}getPanelTitle(){let e="Voice Assistant";return this.widgetMode.includes("chat")&&(e="Chat Assistant"),this.widgetMode.includes("avatar")&&(e="AI Avatar Chat"),e}getPosStyle(){const e={"bottom-right":"bottom: 24px; right: 24px;","bottom-left":"bottom: 24px; left: 24px;","top-right":"top: 24px; right: 24px;","top-left":"top: 24px; left: 24px;"};return e[this.position]||e["bottom-right"]}setupPanelContent(){const e=[this.primaryColor,this.secondaryColor];switch(this.widgetMode){case"chat_only":this.currentView=new ee(i=>this.handleChatSend(i));break;case"chat_voice":this.currentView=new te(i=>this.handleChatSend(i),()=>this.create_call(),()=>this.end_call(),...e);break;case"avatar_only":this.currentView=new ie(()=>this.create_call(),()=>this.end_call(),...e);break;case"chat_avatar":this.currentView=new ne(i=>this.handleChatSend(i),()=>this.create_call(),()=>this.end_call(),...e);break;case"voice_only":default:this.currentView=new $(()=>this.create_call(),()=>this.end_call(),...e);break}this.currentView&&this.panel&&this.panel.setContent(this.currentView.getElement())}async handleChatSend(e){var n,s;if(!e)return;const i=(s=(n=this.currentView)==null?void 0:n.getChatWindow)==null?void 0:s.call(n);if(i){this.chatId&&!this.eventSource&&(this.eventSource=m.listenForAdminReplies(this.chatId,this.prospectId,r=>{var a,A;(A=(a=this.currentView)==null?void 0:a.getChatWindow)==null||A.call(a).addMessage("assistant",r)})),i.addMessage("user",e),i.setTyping(!0);try{await this.chatAdapter.sendMessage(e,this.agentId,this.prospectId,this.chatId,r=>{var a;i.setTyping(!1),r&&r.type==="button_list"&&((a=r.data)!=null&&a.buttons)&&(i.addButtons(r.data.buttons),this.welcomeChipsData=r.data.buttons,this.updateWelcomeChipsVisibility())},r=>{i.setTyping(!1),i.updateLastAssistantMessage(r)},r=>{r&&(this.chatId=r)})}catch(r){i.setTyping(!1),console.error("Send message failed",r),i.addMessage("assistant","Error sending message.")}}}togglePanel(){this.isPanelOpen?this.closePanel():this.openPanel()}openPanel(){var e;this.isPanelOpen=!0,this.updateWelcomeChipsVisibility(),(e=this.panel)==null||e.open({bottom:this.position.includes("bottom")?"100px":void 0,top:this.position.includes("top")?"100px":void 0,right:this.position.includes("right")?"24px":void 0,left:this.position.includes("left")?"24px":void 0})}closePanel(){var e;this.isPanelOpen=!1,this.updateWelcomeChipsVisibility(),(e=this.panel)==null||e.close()}updateWelcomeChipsVisibility(){this.floatingWelcomeChips&&(!this.isPanelOpen&&this.welcomeChipsData.length>0?(this.floatingWelcomeChips.setChips(this.welcomeChipsData),this.floatingWelcomeChips.getElement().style.display="flex"):this.floatingWelcomeChips.getElement().style.display="none")}handleFloatingChipClick(e){this.openPanel(),this.handleChatSend(e)}}class re{static getProvider(t){return new oe(t)}}class U extends HTMLElement{constructor(){super();o(this,"shadow");o(this,"provider",null);o(this,"widgetId","");o(this,"agentId","");o(this,"serverUrl","https://inboxapi.travelr.club");o(this,"position","bottom-right");o(this,"primaryColor","#6366f1");o(this,"secondaryColor","#4f46e5");o(this,"gradient",null);this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","secondary-color","server-url","gradient"]}connectedCallback(){console.log("[AvinAI Widget] Initializing..."),this.readAttributes();const e={widgetId:this.widgetId,agentId:this.agentId,serverUrl:this.serverUrl,position:this.position,primaryColor:this.primaryColor,secondaryColor:this.secondaryColor,gradient:this.gradient};this.provider=re.getProvider(e),this.provider&&this.provider.ui_renderer(this.shadow),this.widgetId&&!this.agentId&&this.initializeExpandedConfig()}readAttributes(){this.widgetId=this.getAttribute("widget-id")||"",this.agentId=this.getAttribute("agent-id")||"",this.serverUrl=this.getAttribute("server-url")||"https://inboxapi.travelr.club",this.position=this.getAttribute("position")||"bottom-right",this.primaryColor=this.getAttribute("primary-color")||"#6366f1",this.secondaryColor=this.getAttribute("secondary-color")||this.primaryColor,this.gradient=this.getAttribute("gradient")}async initializeExpandedConfig(){try{const e=await B.fetchWidgetConfig(this.widgetId);this.provider&&await this.provider.initialize(e)}catch(e){console.error("[AvinAI Widget] Config failed:",e)}}}customElements.get("avin-convai")||customElements.define("avin-convai",U),typeof window<"u"&&(window.AvinConvAI=U,window.AvinAI=S);const g=document.currentScript;if(g){const l=g.getAttribute("data-widget-id")||g.getAttribute("widget-id"),t=g.getAttribute("data-agent-id")||g.getAttribute("agent-id");if(l||t){const e=g.getAttribute("data-position")||"bottom-right",i=g.getAttribute("data-primary-color")||g.getAttribute("primary-color")||"#6366f1",n=g.getAttribute("data-secondary-color")||g.getAttribute("secondary-color"),s=g.getAttribute("data-gradient")||g.getAttribute("gradient"),r=g.getAttribute("data-server-url")||g.getAttribute("server-url"),a=document.createElement("avin-convai");l&&a.setAttribute("widget-id",l),t&&a.setAttribute("agent-id",t),a.setAttribute("position",e),a.setAttribute("primary-color",i),n&&a.setAttribute("secondary-color",n),s&&a.setAttribute("gradient",s),r&&a.setAttribute("server-url",r),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>document.body.appendChild(a)):document.body.appendChild(a),a.addEventListener("avinai:tool_call",A=>{const d=A.detail;console.log("[AvinAI Widget] Tool call received by host page:",d.name,d.arguments),typeof window.__avinai_tool_handler=="function"&&window.__avinai_tool_handler(d,{sendResult:c=>{console.log("[AvinAI] sendResult called:",c)}})})}}})();
@@ -1,4 +1,4 @@
1
- (function(d,g){typeof exports=="object"&&typeof module<"u"?g(exports):typeof define=="function"&&define.amd?define(["exports"],g):(d=typeof globalThis<"u"?globalThis:d||self,g(d.AvinAI={}))})(this,function(d){"use strict";var ce=Object.defineProperty;var de=(d,g,w)=>g in d?ce(d,g,{enumerable:!0,configurable:!0,writable:!0,value:w}):d[g]=w;var o=(d,g,w)=>de(d,typeof g!="symbol"?g+"":g,w);var g=typeof document<"u"?document.currentScript:null;class w{constructor(t){o(this,"serverUrl");o(this,"agentId");o(this,"callId");o(this,"onConnected");o(this,"onDisconnected");o(this,"onError");o(this,"onTranscription");o(this,"onRemoteTrack");o(this,"onClientToolCall");o(this,"pc",null);o(this,"dataChannel",null);o(this,"audioElement",null);o(this,"connected",!1);o(this,"iceServers");o(this,"token");if(!t.serverUrl)throw new Error("serverUrl is required");if(!t.agentId)throw new Error("agentId is required");this.serverUrl=t.serverUrl.replace(/\/$/,""),this.agentId=t.agentId,this.callId=t.callId||this.generateCallId(),this.iceServers=t.iceServers,this.token=t.token,this.onConnected=t.onConnected||(()=>{}),this.onDisconnected=t.onDisconnected||(()=>{}),this.onError=t.onError||(e=>console.error("[WebRTC]",e)),this.onTranscription=t.onTranscription||(()=>{}),this.onRemoteTrack=t.onRemoteTrack||(()=>{}),this.onClientToolCall=t.onClientToolCall||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{if(!this.iceServers&&this.token)try{this.iceServers=await w.fetchIceServers(this.token)}catch(r){console.warn("âš ī¸ [WebRTC] Failed to fetch ICE servers:",r)}this.pc=new RTCPeerConnection({iceServers:this.iceServers||[{urls:"stun:global.relay.metered.ca:80"},{urls:["turns:global.relay.metered.ca:443?transport=tcp","turn:global.relay.metered.ca:80?transport=tcp","turn:global.relay.metered.ca:443?transport=tcp"],username:"fa97658be3343d21da3b65e6",credential:"HXHDoqeHbvZrmCuf"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("🎤 [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),t.getTracks().forEach(r=>r.stop());return}if(t.getTracks().forEach(r=>{var a;(a=this.pc)==null||a.addTrack(r,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=r=>{if(typeof r.data=="string")try{this.handleControlEvent(JSON.parse(r.data))}catch{console.warn("[WebRTC] Failed to parse message:",r.data)}else if(r.data instanceof ArrayBuffer)try{const a=new TextDecoder().decode(r.data);try{const A=JSON.parse(a);console.log("[OvinAI] Decoded JSON from binary (inspect only):",A)}catch{console.log("[OvinAI] Decoded String from binary:",a)}}catch{console.log("[OvinAI] Received binary data:",r.data.byteLength,"bytes (not decodable)")}else r.data instanceof Blob&&r.data.text().then(a=>{try{this.handleControlEvent(JSON.parse(a))}catch{console.warn("[WebRTC] Failed to parse blob data:",a)}})},this.dataChannel.onerror=r=>console.error("❌ [WebRTC] DataChannel error:",r),this.pc.ontrack=r=>{const a=r.track,A=r.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${a.kind} track`),a.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=A,this.audioElement.play().catch(p=>console.warn("Audio autoplay blocked:",p)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")}):a.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(a,A))};const e=()=>{var A,p,c,f,u,v,x,Q,E,B,m;console.log("🔄 [WebRTC] State:",(A=this.pc)==null?void 0:A.connectionState,"| ICE:",(p=this.pc)==null?void 0:p.iceConnectionState);const r=((c=this.pc)==null?void 0:c.connectionState)==="connected"||((f=this.pc)==null?void 0:f.iceConnectionState)==="connected"||((u=this.pc)==null?void 0:u.iceConnectionState)==="completed",a=((v=this.pc)==null?void 0:v.connectionState)==="failed"||((x=this.pc)==null?void 0:x.iceConnectionState)==="failed"||((Q=this.pc)==null?void 0:Q.connectionState)==="closed"||((E=this.pc)==null?void 0:E.iceConnectionState)==="closed"||((B=this.pc)==null?void 0:B.connectionState)==="disconnected"||((m=this.pc)==null?void 0:m.iceConnectionState)==="disconnected";r&&!this.connected?(this.connected=!0,this.onConnected()):a&&this.connected&&(this.connected=!1,this.onDisconnected())};this.pc.onconnectionstatechange=e,this.pc.oniceconnectionstatechange=e;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 n=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!n.ok){const r=await n.json();throw new Error(r.error||`HTTP ${n.status}`)}const{answer:s}=await n.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(s),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(t=>{if(!this.pc)return t();if(this.pc.iceGatheringState==="complete")t();else{const e=()=>{var i,n;((i=this.pc)==null?void 0:i.iceGatheringState)==="complete"&&((n=this.pc)==null||n.removeEventListener("icegatheringstatechange",e),t())};this.pc.addEventListener("icegatheringstatechange",e),setTimeout(()=>{var i;(i=this.pc)==null||i.removeEventListener("icegatheringstatechange",e),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),t()},5e3)}})}sendEvent(t,e={}){var i;((i=this.dataChannel)==null?void 0:i.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:t,...e}))}handleControlEvent(t){switch(t.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",t.text),this.onTranscription(t.text,t.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",t.name);break;case"client_tool_call":console.log("đŸ› ī¸ [OvinAI] Client Tool Call:",t),this.onClientToolCall(t.tool_call||t.data||t);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",t.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(t=>{t.track&&t.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}sendToolResult(t,e){this.sendEvent("client_tool_result",{call_id:t,result:e})}sendSilentContext(t){this.sendEvent("silent_context",{context:t})}triggerActionInterrupt(){this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0),this.sendEvent("action_interrupt"),console.log("🛑 [OvinAI] Triggered client-side action interrupt")}static async fetchIceServers(t,e="https://coredb.travelr.club/v1/graphql"){var i;try{const s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:t},body:JSON.stringify({query:`
1
+ (function(d,g){typeof exports=="object"&&typeof module<"u"?g(exports):typeof define=="function"&&define.amd?define(["exports"],g):(d=typeof globalThis<"u"?globalThis:d||self,g(d.AvinAI={}))})(this,function(d){"use strict";var ce=Object.defineProperty;var de=(d,g,w)=>g in d?ce(d,g,{enumerable:!0,configurable:!0,writable:!0,value:w}):d[g]=w;var o=(d,g,w)=>de(d,typeof g!="symbol"?g+"":g,w);var g=typeof document<"u"?document.currentScript:null;class w{constructor(t){o(this,"serverUrl");o(this,"agentId");o(this,"callId");o(this,"onConnected");o(this,"onDisconnected");o(this,"onError");o(this,"onTranscription");o(this,"onRemoteTrack");o(this,"onClientToolCall");o(this,"pc",null);o(this,"dataChannel",null);o(this,"audioElement",null);o(this,"connected",!1);o(this,"iceServers");o(this,"token");if(!t.serverUrl)throw new Error("serverUrl is required");if(!t.agentId)throw new Error("agentId is required");this.serverUrl=t.serverUrl.replace(/\/$/,""),this.agentId=t.agentId,this.callId=t.callId||this.generateCallId(),this.iceServers=t.iceServers,this.token=t.token,this.onConnected=t.onConnected||(()=>{}),this.onDisconnected=t.onDisconnected||(()=>{}),this.onError=t.onError||(e=>console.error("[WebRTC]",e)),this.onTranscription=t.onTranscription||(()=>{}),this.onRemoteTrack=t.onRemoteTrack||(()=>{}),this.onClientToolCall=t.onClientToolCall||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{if(!this.iceServers&&this.token)try{this.iceServers=await w.fetchIceServers(this.token)}catch(r){console.warn("âš ī¸ [WebRTC] Failed to fetch ICE servers:",r)}this.pc=new RTCPeerConnection({iceServers:this.iceServers||[{urls:"stun:global.relay.metered.ca:80"},{urls:["turns:global.relay.metered.ca:443?transport=tcp","turn:global.relay.metered.ca:80?transport=tcp","turn:global.relay.metered.ca:443?transport=tcp"],username:"fa97658be3343d21da3b65e6",credential:"HXHDoqeHbvZrmCuf"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("🎤 [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),t.getTracks().forEach(r=>r.stop());return}if(t.getTracks().forEach(r=>{var a;(a=this.pc)==null||a.addTrack(r,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=r=>{if(typeof r.data=="string")try{this.handleControlEvent(JSON.parse(r.data))}catch{console.warn("[WebRTC] Failed to parse message:",r.data)}else if(r.data instanceof ArrayBuffer)try{const a=new TextDecoder().decode(r.data);try{const A=JSON.parse(a);console.log("[OvinAI] Decoded JSON from binary:",A),A&&typeof A=="object"&&A.event&&this.handleControlEvent(A)}catch{}}catch{}else r.data instanceof Blob&&r.data.text().then(a=>{try{this.handleControlEvent(JSON.parse(a))}catch{console.warn("[WebRTC] Failed to parse blob data:",a)}})},this.dataChannel.onerror=r=>console.error("❌ [WebRTC] DataChannel error:",r),this.pc.ontrack=r=>{const a=r.track,A=r.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${a.kind} track`),a.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=A,this.audioElement.play().catch(p=>console.warn("Audio autoplay blocked:",p)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")}):a.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(a,A))};const e=()=>{var A,p,c,f,u,v,x,Q,E,B,m;console.log("🔄 [WebRTC] State:",(A=this.pc)==null?void 0:A.connectionState,"| ICE:",(p=this.pc)==null?void 0:p.iceConnectionState);const r=((c=this.pc)==null?void 0:c.connectionState)==="connected"||((f=this.pc)==null?void 0:f.iceConnectionState)==="connected"||((u=this.pc)==null?void 0:u.iceConnectionState)==="completed",a=((v=this.pc)==null?void 0:v.connectionState)==="failed"||((x=this.pc)==null?void 0:x.iceConnectionState)==="failed"||((Q=this.pc)==null?void 0:Q.connectionState)==="closed"||((E=this.pc)==null?void 0:E.iceConnectionState)==="closed"||((B=this.pc)==null?void 0:B.connectionState)==="disconnected"||((m=this.pc)==null?void 0:m.iceConnectionState)==="disconnected";r&&!this.connected?(this.connected=!0,this.onConnected()):a&&this.connected&&(this.connected=!1,this.onDisconnected())};this.pc.onconnectionstatechange=e,this.pc.oniceconnectionstatechange=e;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 n=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!n.ok){const r=await n.json();throw new Error(r.error||`HTTP ${n.status}`)}const{answer:s}=await n.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(s),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(t=>{if(!this.pc)return t();if(this.pc.iceGatheringState==="complete")t();else{const e=()=>{var i,n;((i=this.pc)==null?void 0:i.iceGatheringState)==="complete"&&((n=this.pc)==null||n.removeEventListener("icegatheringstatechange",e),t())};this.pc.addEventListener("icegatheringstatechange",e),setTimeout(()=>{var i;(i=this.pc)==null||i.removeEventListener("icegatheringstatechange",e),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),t()},5e3)}})}sendEvent(t,e={}){var i;((i=this.dataChannel)==null?void 0:i.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:t,...e}))}handleControlEvent(t){switch(t.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",t.text),this.onTranscription(t.text,t.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",t.name);break;case"client_tool_call":console.log("đŸ› ī¸ [OvinAI] Client Tool Call:",t),this.onClientToolCall(t.tool_call||t.data||t);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",t.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(t=>{t.track&&t.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}sendToolResult(t,e){this.sendEvent("client_tool_result",{call_id:t,result:e})}sendSilentContext(t){this.sendEvent("silent_context",{context:t})}triggerActionInterrupt(){this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0),this.sendEvent("action_interrupt"),console.log("🛑 [OvinAI] Triggered client-side action interrupt")}static async fetchIceServers(t,e="https://coredb.travelr.club/v1/graphql"){var i;try{const s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:t},body:JSON.stringify({query:`
2
2
  query GetIceServers {
3
3
  ice_servers(where: {enabled: {_eq: true}}) {
4
4
  urls
@@ -35,8 +35,8 @@
35
35
  id
36
36
  }
37
37
  }
38
- `;try{const r=await(await fetch(S,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,variables:{prospectGroupId:t,name:`Widget Guest ${new Date().toLocaleString()}`}})})).json();return r.errors?(console.warn("[AvinAI] Prospect creation failed, using anonymous ID",r.errors),`anon_${Date.now()}_${Math.random().toString(36).substr(2,9)}`):((n=(i=r.data)==null?void 0:i.insert_prospects_one)==null?void 0:n.id)||`anon_${Date.now()}`}catch(s){return console.error("[AvinAI] Failed to create prospect:",s),`anon_${Date.now()}`}}static async fetchWelcomeMessage(t,e){var i,n;try{const s=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,message:"",prospect_id:e,stream:!1})});if(!s.ok)throw new Error("Failed to fetch welcome message");const r=await s.json();let a=r.response||"Hello! How can I help you today?",A;const p=r.chat_id||r.inbox_id;if(r.widget){A=r.widget;const c={text:a};A.type==="carousel"&&((i=A.data)!=null&&i.cards)?c.cards=A.data.cards:A.type==="button_list"&&((n=A.data)!=null&&n.buttons)&&(c.buttons=A.data.buttons),a=JSON.stringify(c)}return{role:"assistant",content:a,widget:A,chatId:p}}catch(s){return console.error("[AvinAI] Failed to fetch welcome message:",s),{role:"assistant",content:"Hello! How can I help you today?"}}}static async sendChatMessage(t,e,i,n,s,r,a){var A,p,c,f;try{const u={agent_id:t,message:i,prospect_id:e,stream:!0};n&&(u.inbox_id=n);const v=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)});if(!v.ok)throw new Error("Chat request failed");const x=(A=v.body)==null?void 0:A.getReader(),Q=new TextDecoder;if(!x)throw new Error("No reader");let E="",B="",m=null;for(;;){const{done:ae,value:Ae}=await x.read();if(ae)break;B+=Q.decode(Ae,{stream:!0});const O=B.split(`
39
- `);B=O.pop()||"";for(const le of O){const D=le.trim();if(D&&D.startsWith("data: ")){const U=D.slice(6);if(U==="[DONE]"){a(m);return}try{const C=JSON.parse(U);if(C.type==="metadata"&&C.chat_id){m=C.chat_id;continue}const W=(f=(c=(p=C.choices)==null?void 0:p[0])==null?void 0:c.delta)==null?void 0:f.content;W&&(E+=W,s(E)),C.widget&&r(C.widget),C.chat_id&&!m&&(m=C.chat_id)}catch{}}}}a(m)}catch(u){console.error("[AvinAI] Chat error:",u),s("Sorry, I encountered an error. Please try again."),a(null)}}static listenForAdminReplies(t,e,i){const n=`${I}/inbox/stream?inbox_id=${t}&sender=${encodeURIComponent(e)}`;let s=null,r=0,a=!1;const A=()=>{a||(s=new EventSource(n),console.log(`[AvinAI] Subscribed to SSE EventSource at ${s.url}`),s.onopen=()=>{console.log("[AvinAI] SSE connection opened successfully."),r=0},s.onmessage=p=>{console.log("[AvinAI] 📡 Raw SSE Event triggered:",p.data);try{const c=JSON.parse(p.data);console.log("[AvinAI] SSE stream JSON parsed:",c);const f=c.direction==="outgoing",u=!!c.content,v=c.source!=="ai";f&&u&&v&&(console.log("[AvinAI] đŸŽ¯ Displaying admin message in chat UI:",c.content),i(c.content))}catch(c){console.error("[AvinAI] SSE parse error",c)}},s.onerror=p=>{if(console.error("[AvinAI] SSE connection error or disconnect",p),s&&(s.close(),s=null),a)return;const c=Math.min(2e3*Math.pow(2,r),3e4);console.log(`[AvinAI] Reconnecting SSE in ${c}ms (Attempt ${r+1})...`),r++,setTimeout(A,c)})};return A(),{close:()=>{a=!0,s&&(s.close(),s=null,console.log("[AvinAI] SSE connection manually closed."))}}}static async createCall(t,e,i){var s,r;const n=`
38
+ `;try{const r=await(await fetch(S,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,variables:{prospectGroupId:t,name:`Widget Guest ${new Date().toLocaleString()}`}})})).json();return r.errors?(console.warn("[AvinAI] Prospect creation failed, using anonymous ID",r.errors),`anon_${Date.now()}_${Math.random().toString(36).substr(2,9)}`):((n=(i=r.data)==null?void 0:i.insert_prospects_one)==null?void 0:n.id)||`anon_${Date.now()}`}catch(s){return console.error("[AvinAI] Failed to create prospect:",s),`anon_${Date.now()}`}}static async fetchWelcomeMessage(t,e){var i,n;try{const s=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,message:"",prospect_id:e,stream:!1})});if(!s.ok)throw new Error("Failed to fetch welcome message");const r=await s.json();let a=r.response||"Hello! How can I help you today?",A;const p=r.chat_id||r.inbox_id;if(r.widget){A=r.widget;const c={text:a};A.type==="carousel"&&((i=A.data)!=null&&i.cards)?c.cards=A.data.cards:A.type==="button_list"&&((n=A.data)!=null&&n.buttons)&&(c.buttons=A.data.buttons),a=JSON.stringify(c)}return{role:"assistant",content:a,widget:A,chatId:p}}catch(s){return console.error("[AvinAI] Failed to fetch welcome message:",s),{role:"assistant",content:"Hello! How can I help you today?"}}}static async sendChatMessage(t,e,i,n,s,r,a){var A,p,c,f;try{const u={agent_id:t,message:i,prospect_id:e,stream:!0};n&&(u.inbox_id=n);const v=await fetch(`${I}/widget/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)});if(!v.ok)throw new Error("Chat request failed");const x=(A=v.body)==null?void 0:A.getReader(),Q=new TextDecoder;if(!x)throw new Error("No reader");let E="",B="",m=null;for(;;){const{done:ae,value:Ae}=await x.read();if(ae)break;B+=Q.decode(Ae,{stream:!0});const U=B.split(`
39
+ `);B=U.pop()||"";for(const le of U){const D=le.trim();if(D&&D.startsWith("data: ")){const O=D.slice(6);if(O==="[DONE]"){a(m);return}try{const C=JSON.parse(O);if(C.type==="metadata"&&C.chat_id){m=C.chat_id;continue}const W=(f=(c=(p=C.choices)==null?void 0:p[0])==null?void 0:c.delta)==null?void 0:f.content;W&&(E+=W,s(E)),C.widget&&r(C.widget),C.chat_id&&!m&&(m=C.chat_id)}catch{}}}}a(m)}catch(u){console.error("[AvinAI] Chat error:",u),s("Sorry, I encountered an error. Please try again."),a(null)}}static listenForAdminReplies(t,e,i){const n=`${I}/inbox/stream?inbox_id=${t}&sender=${encodeURIComponent(e)}`;let s=null,r=0,a=!1;const A=()=>{a||(s=new EventSource(n),console.log(`[AvinAI] Subscribed to SSE EventSource at ${s.url}`),s.onopen=()=>{console.log("[AvinAI] SSE connection opened successfully."),r=0},s.onmessage=p=>{console.log("[AvinAI] 📡 Raw SSE Event triggered:",p.data);try{const c=JSON.parse(p.data);console.log("[AvinAI] SSE stream JSON parsed:",c);const f=c.direction==="outgoing",u=!!c.content,v=c.source!=="ai";f&&u&&v&&(console.log("[AvinAI] đŸŽ¯ Displaying admin message in chat UI:",c.content),i(c.content))}catch(c){console.error("[AvinAI] SSE parse error",c)}},s.onerror=p=>{if(console.error("[AvinAI] SSE connection error or disconnect",p),s&&(s.close(),s=null),a)return;const c=Math.min(2e3*Math.pow(2,r),3e4);console.log(`[AvinAI] Reconnecting SSE in ${c}ms (Attempt ${r+1})...`),r++,setTimeout(A,c)})};return A(),{close:()=>{a=!0,s&&(s.close(),s=null,console.log("[AvinAI] SSE connection manually closed."))}}}static async createCall(t,e,i){var s,r;const n=`
40
40
  mutation CreateCall($object: calls_insert_input!) {
41
41
  insert_calls_one(object: $object) {
42
42
  id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "avin-ai",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "AvinAI Voice & Chat Widget SDK — embed conversational AI into any website with one script tag",
5
5
  "author": "AvinAI <dev@avin.ai>",
6
6
  "license": "MIT",