@unith-ai/core-client 2.0.5 → 2.0.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.
package/dist/lib.web.js CHANGED
@@ -503,6 +503,7 @@ var EventType = /* @__PURE__ */ ((EventType2) => {
503
503
  EventType2["PING"] = "ping";
504
504
  EventType2["PONG"] = "pong";
505
505
  EventType2["BINARY"] = "binary";
506
+ EventType2["STOP"] = "stop";
506
507
  return EventType2;
507
508
  })(EventType || {});
508
509
  var StreamingEventType = /* @__PURE__ */ ((StreamingEventType2) => {
@@ -511,6 +512,7 @@ var StreamingEventType = /* @__PURE__ */ ((StreamingEventType2) => {
511
512
  StreamingEventType2["METADATA"] = "metadata";
512
513
  StreamingEventType2["ERROR"] = "error";
513
514
  StreamingEventType2["CACHE"] = "cache";
515
+ StreamingEventType2["STOP"] = "stop";
514
516
  return StreamingEventType2;
515
517
  })(StreamingEventType || {});
516
518
  function isJoinEvent(event) {
@@ -596,6 +598,17 @@ var Connection = class _Connection {
596
598
  });
597
599
  });
598
600
  this.socket.addEventListener("close", (event) => {
601
+ if (this.onMessageCallback) {
602
+ const parsedEvent = {
603
+ event: "timeout" /* TIME_OUT */,
604
+ user_id: "",
605
+ username: ""
606
+ };
607
+ if (isValidSocketEvent(parsedEvent)) {
608
+ this.onMessageCallback(parsedEvent);
609
+ return;
610
+ }
611
+ }
599
612
  this.disconnect({
600
613
  reason: "error",
601
614
  message: event.reason || "The connection was closed by the server.",
@@ -700,6 +713,10 @@ var Connection = class _Connection {
700
713
  this.socket.close();
701
714
  }
702
715
  sendMessage(message) {
716
+ if (this.socket.readyState !== WebSocket.OPEN) {
717
+ console.warn("Attempted to send a message on a closed socket.");
718
+ return;
719
+ }
703
720
  this.socket.send(JSON.stringify(message));
704
721
  }
705
722
  sendPingEvent(message) {
@@ -707,6 +724,7 @@ var Connection = class _Connection {
707
724
  console.warn("Ping event sent without a callback set.");
708
725
  return;
709
726
  }
727
+ if (this.socket.readyState !== WebSocket.OPEN) return;
710
728
  this.socket.send(JSON.stringify(message));
711
729
  }
712
730
  onPingPong(callback) {
@@ -753,11 +771,11 @@ var IdleVideo = class _IdleVideo {
753
771
  );
754
772
  }
755
773
  } else {
756
- const errorMessage = await response.text();
774
+ const errorMessage = await response.json();
757
775
  const error = new Error(
758
- `An error occurred retrieving idle video: ${response.status} ${response.statusText}. Response: ${errorMessage}`
776
+ errorMessage.detail || "An error occurred retrieving your digital human."
759
777
  );
760
- error.name = "IdleVideoError";
778
+ error.name = "headError";
761
779
  throw error;
762
780
  }
763
781
  }
@@ -774,11 +792,11 @@ var IdleVideo = class _IdleVideo {
774
792
  );
775
793
  }
776
794
  } else {
777
- const errorMessage = await response.text();
795
+ const errorMessage = await response.json();
778
796
  const error = new Error(
779
- `An error occurred retrieving avatar: ${response.status} ${response.statusText}. Response: ${errorMessage}`
797
+ errorMessage.detail || "An error occurred retrieving your digital human."
780
798
  );
781
- error.name = "AvatarError";
799
+ error.name = "headError";
782
800
  throw error;
783
801
  }
784
802
  }
@@ -795,11 +813,11 @@ var IdleVideo = class _IdleVideo {
795
813
  );
796
814
  }
797
815
  } else {
798
- const errorMessage = await response.text();
816
+ const errorMessage = await response.json();
799
817
  const error = new Error(
800
- `An error occurred retrieving idle video: ${response.status} ${response.statusText}. Response: ${errorMessage}`
818
+ errorMessage.detail || "An error occurred retrieving your digital human."
801
819
  );
802
- error.name = "IdleVideoError";
820
+ error.name = "headError";
803
821
  throw error;
804
822
  }
805
823
  }
@@ -25406,15 +25424,54 @@ var N = class _N {
25406
25424
  };
25407
25425
  N.DEFAULT_BASE_URI = "wss://api.elevenlabs.io";
25408
25426
 
25427
+ // src/utils/storage.ts
25428
+ var storage = window.localStorage;
25429
+ var location = window.location.origin + window.location.pathname;
25430
+ var getFromStorage = (key, orgId, headId) => {
25431
+ if (typeof storage !== "undefined") {
25432
+ const prefix = `chat:${location}:${orgId}:${headId}:`;
25433
+ return storage.getItem(`${prefix}${key}`);
25434
+ }
25435
+ };
25436
+ var setToStorage = (key, val, orgId, headId) => {
25437
+ if (typeof storage !== "undefined") {
25438
+ const prefix = `chat:${location}:${orgId}:${headId}:`;
25439
+ storage.setItem(`${prefix}${key}`, val);
25440
+ return;
25441
+ }
25442
+ };
25443
+
25444
+ // src/utils/microphone.ts
25445
+ var EXPIRATION_OFFSET = 5 * 60 * 1e3;
25446
+ var defaultElevenLabsOptions = {
25447
+ vadSilenceThresholdSecs: 1.5,
25448
+ noiseSuppression: true,
25449
+ vadThreshold: 0.4,
25450
+ minSpeechDurationMs: 100,
25451
+ minSilenceDurationMs: 100,
25452
+ disableDynamicSpeechRecognition: false
25453
+ };
25454
+ function getLanguageCode(languageTag) {
25455
+ if (!languageTag || typeof languageTag !== "string") {
25456
+ throw new Error("Invalid language tag");
25457
+ }
25458
+ const parts = languageTag.trim().split("-");
25459
+ const languageCode = parts[0].toLowerCase();
25460
+ if (!/^[a-z]{2,3}$/.test(languageCode)) {
25461
+ throw new Error("Invalid ISO 639 language code format");
25462
+ }
25463
+ return languageCode;
25464
+ }
25465
+
25409
25466
  // src/modules/microphone.ts
25410
25467
  var Microphone = class _Microphone {
25411
- constructor(provider, options, elevenLabsOptions, user, headInfo, microphoneAccess, token, sendMessage) {
25468
+ constructor(provider, options, elevenLabsOptions, user, headInfo, microphoneAccess, token, voiceInterruptions) {
25412
25469
  this.provider = provider;
25413
25470
  this.options = options;
25414
25471
  this.elevenLabsOptions = elevenLabsOptions;
25415
25472
  this.user = user;
25416
25473
  this.headInfo = headInfo;
25417
- this.sendMessage = sendMessage;
25474
+ this.voiceInterruptions = voiceInterruptions;
25418
25475
  this.microphoneStatus = "OFF";
25419
25476
  this.microphoneAccess = false;
25420
25477
  this.recognizer = null;
@@ -25447,7 +25504,7 @@ var Microphone = class _Microphone {
25447
25504
  * @param sendMessage
25448
25505
  * @returns
25449
25506
  */
25450
- static async initializeMicrophone(options, provider, elevenLabsOptions, user, headInfo, microphoneAccess, sendMessage) {
25507
+ static async initializeMicrophone(options, provider, elevenLabsOptions, user, headInfo, microphoneAccess, voiceInterruptions) {
25451
25508
  let token = {
25452
25509
  token: "",
25453
25510
  generatedAt: null,
@@ -25469,7 +25526,7 @@ var Microphone = class _Microphone {
25469
25526
  headInfo,
25470
25527
  microphoneAccess,
25471
25528
  token,
25472
- sendMessage
25529
+ voiceInterruptions
25473
25530
  );
25474
25531
  }
25475
25532
  updateMicrophoneStatus(status) {
@@ -25499,13 +25556,33 @@ var Microphone = class _Microphone {
25499
25556
  const sdk = speechSdk;
25500
25557
  let SpeechConfig, AudioConfig, SpeechRecognizer, ResultReason, PhraseListGrammar;
25501
25558
  if (sdk.SpeechConfig) {
25502
- ({ SpeechConfig, AudioConfig, SpeechRecognizer, ResultReason, PhraseListGrammar } = sdk);
25559
+ ({
25560
+ SpeechConfig,
25561
+ AudioConfig,
25562
+ SpeechRecognizer,
25563
+ ResultReason,
25564
+ PhraseListGrammar
25565
+ } = sdk);
25503
25566
  } else if ((_a = sdk.default) == null ? void 0 : _a.SpeechConfig) {
25504
- ({ SpeechConfig, AudioConfig, SpeechRecognizer, ResultReason, PhraseListGrammar } = sdk.default);
25567
+ ({
25568
+ SpeechConfig,
25569
+ AudioConfig,
25570
+ SpeechRecognizer,
25571
+ ResultReason,
25572
+ PhraseListGrammar
25573
+ } = sdk.default);
25505
25574
  } else if ((_b = sdk.SpeechSDK) == null ? void 0 : _b.SpeechConfig) {
25506
- ({ SpeechConfig, AudioConfig, SpeechRecognizer, ResultReason, PhraseListGrammar } = sdk.SpeechSDK);
25575
+ ({
25576
+ SpeechConfig,
25577
+ AudioConfig,
25578
+ SpeechRecognizer,
25579
+ ResultReason,
25580
+ PhraseListGrammar
25581
+ } = sdk.SpeechSDK);
25507
25582
  } else {
25508
- throw new Error("Azure Speech SDK does not expose expected symbols. Ensure microsoft-cognitiveservices-speech-sdk is installed.");
25583
+ throw new Error(
25584
+ "Azure Speech SDK does not expose expected symbols. Ensure microsoft-cognitiveservices-speech-sdk is installed."
25585
+ );
25509
25586
  }
25510
25587
  const speechConfig = SpeechConfig.fromAuthorizationToken(
25511
25588
  this.tokenObj.token,
@@ -25528,6 +25605,12 @@ var Microphone = class _Microphone {
25528
25605
  });
25529
25606
  }
25530
25607
  };
25608
+ this.recognizer.recognizing = (_3, event) => {
25609
+ if (!this.voiceInterruptions || event.result.text.length < 2) return;
25610
+ this.options.onMicrophonePartialSpeechRecognitionResult({
25611
+ transcript: event.result.text
25612
+ });
25613
+ };
25531
25614
  this.recognizer.startContinuousRecognitionAsync(
25532
25615
  () => {
25533
25616
  this.micBeep.play();
@@ -25565,11 +25648,10 @@ var Microphone = class _Microphone {
25565
25648
  generatedAt: Date.now()
25566
25649
  };
25567
25650
  }
25568
- this.connection = N.connect({
25651
+ const params = {
25569
25652
  token: this.tokenObj.token,
25570
25653
  modelId: "scribe_v2_realtime",
25571
25654
  includeTimestamps: false,
25572
- // languageCode: getLanguageCode(this.headInfo.lang_speech_recognition),
25573
25655
  microphone: {
25574
25656
  echoCancellation: true,
25575
25657
  noiseSuppression: this.elevenLabsOptions.noiseSuppression
@@ -25579,7 +25661,13 @@ var Microphone = class _Microphone {
25579
25661
  vadThreshold: this.elevenLabsOptions.vadThreshold,
25580
25662
  minSpeechDurationMs: this.elevenLabsOptions.minSpeechDurationMs,
25581
25663
  minSilenceDurationMs: this.elevenLabsOptions.minSilenceDurationMs
25582
- });
25664
+ };
25665
+ if (this.elevenLabsOptions.disableDynamicSpeechRecognition) {
25666
+ params.languageCode = getLanguageCode(
25667
+ this.headInfo.lang_speech_recognition || "en-US"
25668
+ );
25669
+ }
25670
+ this.connection = N.connect(params);
25583
25671
  this.connection.on(P.SESSION_STARTED, () => {
25584
25672
  this.micBeep.play();
25585
25673
  this.updateMicrophoneStatus("ON");
@@ -25588,6 +25676,13 @@ var Microphone = class _Microphone {
25588
25676
  if (data.text.length < 2 || data.text[0] === "(") return;
25589
25677
  this.handleRecognitionResult(data.text);
25590
25678
  });
25679
+ this.connection.on(P.PARTIAL_TRANSCRIPT, (data) => {
25680
+ if (!this.voiceInterruptions || data.text.length < 2 || data.text[0] === "(")
25681
+ return;
25682
+ this.options.onMicrophonePartialSpeechRecognitionResult({
25683
+ transcript: data.text
25684
+ });
25685
+ });
25591
25686
  this.connection.on(P.ERROR, (error) => {
25592
25687
  console.error("Error:", error);
25593
25688
  this.options.onMicrophoneError({
@@ -25769,23 +25864,6 @@ var SyncController = class {
25769
25864
  }
25770
25865
  };
25771
25866
 
25772
- // src/utils/storage.ts
25773
- var storage = window.localStorage;
25774
- var location = window.location.origin + window.location.pathname;
25775
- var getFromStorage = (key, orgId, headId) => {
25776
- if (typeof storage !== "undefined") {
25777
- const prefix = `chat:${location}:${orgId}:${headId}:`;
25778
- return storage.getItem(`${prefix}${key}`);
25779
- }
25780
- };
25781
- var setToStorage = (key, val, orgId, headId) => {
25782
- if (typeof storage !== "undefined") {
25783
- const prefix = `chat:${location}:${orgId}:${headId}:`;
25784
- storage.setItem(`${prefix}${key}`, val);
25785
- return;
25786
- }
25787
- };
25788
-
25789
25867
  // src/modules/user.ts
25790
25868
  var User = class _User {
25791
25869
  constructor(accessToken, tokenType, id, username, password, orgId, headId, apiBase) {
@@ -26173,7 +26251,7 @@ var Vp8VideoOutput = class _Vp8VideoOutput {
26173
26251
  cancelAnimationFrame(this.animationFrameId);
26174
26252
  this.animationFrameId = null;
26175
26253
  }
26176
- this.frameBuffer = [];
26254
+ this.frameBuffer.splice(0);
26177
26255
  this.isProcessingFrame = false;
26178
26256
  this.currentSequenceId = 0;
26179
26257
  this.startTime = 0;
@@ -26181,35 +26259,8 @@ var Vp8VideoOutput = class _Vp8VideoOutput {
26181
26259
  count: 0,
26182
26260
  lastTime: 0
26183
26261
  };
26184
- if (this.decoder && this.decoder.state === "configured") {
26185
- try {
26186
- this.decoder.flush();
26187
- } catch (error) {
26188
- console.warn("Error flushing decoder:", error);
26189
- }
26190
- }
26191
- this.clearCanvas();
26192
26262
  this.state = "ready" /* READY */;
26193
- }
26194
- interrupt(fadeOut = false) {
26195
- if (this.state === "destroyed" /* DESTROYED */) return;
26196
- this.state = "interrupted" /* INTERRUPTED */;
26197
- this.frameBuffer = [];
26198
- this.isProcessingFrame = false;
26199
- if (this.decoder && this.decoder.state === "configured") {
26200
- try {
26201
- this.decoder.flush();
26202
- } catch (error) {
26203
- console.warn("Error flushing decoder:", error);
26204
- }
26205
- }
26206
- if (fadeOut) {
26207
- this.fadeOutCanvas().then(() => {
26208
- this.clearCanvas();
26209
- });
26210
- } else {
26211
- this.clearCanvas();
26212
- }
26263
+ return;
26213
26264
  }
26214
26265
  destroy() {
26215
26266
  if (this.state === "destroyed" /* DESTROYED */) return;
@@ -26315,22 +26366,6 @@ var Vp8VideoOutput = class _Vp8VideoOutput {
26315
26366
  clearCanvas() {
26316
26367
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
26317
26368
  }
26318
- async fadeOutCanvas() {
26319
- return new Promise((resolve) => {
26320
- let opacity = 1;
26321
- const fadeStep = () => {
26322
- opacity -= 0.05;
26323
- this.canvas.style.opacity = opacity.toString();
26324
- if (opacity <= 0) {
26325
- this.canvas.style.opacity = "1";
26326
- resolve();
26327
- } else {
26328
- requestAnimationFrame(fadeStep);
26329
- }
26330
- };
26331
- fadeStep();
26332
- });
26333
- }
26334
26369
  setupContextLossHandling() {
26335
26370
  this.handleContextLoss = (event) => {
26336
26371
  event.preventDefault();
@@ -26365,6 +26400,7 @@ var VideoOutput = class _VideoOutput {
26365
26400
  this.bufferCheckAnimationId = null;
26366
26401
  this.lastBufferCheckTime = 0;
26367
26402
  this.sessionStarted = false;
26403
+ this.isRestoringIdleVideo = false;
26368
26404
  //cached video
26369
26405
  this.isShowingCachedVideo = false;
26370
26406
  this.onSpeakingStartCallback = null;
@@ -26468,7 +26504,7 @@ var VideoOutput = class _VideoOutput {
26468
26504
  this.lastBufferCheckTime = 0;
26469
26505
  const checkBuffer = (timestamp) => {
26470
26506
  if (timestamp - this.lastBufferCheckTime >= 100) {
26471
- if (this.sessionStarted && this.videoOutput.getBufferLength() > 0) {
26507
+ if (this.sessionStarted && this.videoOutput.getBufferLength() > 0 && !this.isRestoringIdleVideo) {
26472
26508
  this.hideIdleVideoBeforeStream();
26473
26509
  } else if (!this.videoOutput.getStreamingStatus() && this.videoOutput.getBufferLength() === 0) {
26474
26510
  this.showIdleVideoAfterStream();
@@ -26512,7 +26548,20 @@ var VideoOutput = class _VideoOutput {
26512
26548
  { once: true }
26513
26549
  );
26514
26550
  } catch (error) {
26515
- console.error("Failed to play cached video:", error);
26551
+ this.cachedVideo.style.opacity = "0";
26552
+ this.isShowingCachedVideo = false;
26553
+ await this.showIdleVideo();
26554
+ }
26555
+ }
26556
+ async stopCachedVideo() {
26557
+ if (!this.isShowingCachedVideo || !this.cachedVideo || this.isTransitioning)
26558
+ return;
26559
+ this.isShowingCachedVideo = false;
26560
+ try {
26561
+ await this.crossfadeFromCachedToIdle();
26562
+ await this.cleanupCachedVideo();
26563
+ await this.showIdleVideo();
26564
+ } catch (error) {
26516
26565
  this.cachedVideo.style.opacity = "0";
26517
26566
  this.isShowingCachedVideo = false;
26518
26567
  await this.showIdleVideo();
@@ -26587,6 +26636,9 @@ var VideoOutput = class _VideoOutput {
26587
26636
  getStreamingStatus() {
26588
26637
  return this.videoOutput.getStreamingStatus();
26589
26638
  }
26639
+ isPlayingCachedVideo() {
26640
+ return this.isShowingCachedVideo;
26641
+ }
26590
26642
  async showIdleVideo() {
26591
26643
  var _a, _b;
26592
26644
  if (!this.idleVideo || this.isShowingIdleVideo) return;
@@ -26623,15 +26675,16 @@ var VideoOutput = class _VideoOutput {
26623
26675
  return this.videoOutput.addFrame(uint8Array, timeStamp, isKeyframe);
26624
26676
  }
26625
26677
  clearFrame() {
26626
- this.showIdleVideo();
26627
- return this.videoOutput.clearFrame();
26678
+ this.isRestoringIdleVideo = true;
26679
+ this.showIdleVideoAfterStream();
26680
+ this.videoOutput.clearFrame();
26628
26681
  }
26629
26682
  async toggleStream(status) {
26683
+ if (status) {
26684
+ this.isRestoringIdleVideo = false;
26685
+ }
26630
26686
  return this.videoOutput.toggleStream(status);
26631
26687
  }
26632
- interrupt(fadeOut) {
26633
- this.videoOutput.interrupt(fadeOut);
26634
- }
26635
26688
  destroy() {
26636
26689
  if (this.bufferCheckAnimationId) {
26637
26690
  cancelAnimationFrame(this.bufferCheckAnimationId);
@@ -26655,16 +26708,6 @@ var VideoOutput = class _VideoOutput {
26655
26708
  }
26656
26709
  };
26657
26710
 
26658
- // src/utils/microphone.ts
26659
- var EXPIRATION_OFFSET = 5 * 60 * 1e3;
26660
- var defaultElevenLabsOptions = {
26661
- vadSilenceThresholdSecs: 1.5,
26662
- noiseSuppression: true,
26663
- vadThreshold: 0.4,
26664
- minSpeechDurationMs: 100,
26665
- minSilenceDurationMs: 100
26666
- };
26667
-
26668
26711
  // src/utils/sync.ts
26669
26712
  var DEFAULT_SYNC_CONFIG = {
26670
26713
  tolerance: 40,
@@ -26705,6 +26748,7 @@ var Conversation = class _Conversation {
26705
26748
  this.status = "connecting";
26706
26749
  this.volume = 1;
26707
26750
  this.sessionStarted = false;
26751
+ this.isStoppingLastResponse = false;
26708
26752
  this.messageCounter = 0;
26709
26753
  this.monitor = null;
26710
26754
  this.microphone = null;
@@ -26723,7 +26767,7 @@ var Conversation = class _Conversation {
26723
26767
  });
26724
26768
  if ("suggestions" in event) {
26725
26769
  const suggestions = event.suggestions || [];
26726
- if (suggestions.length > 1) {
26770
+ if (suggestions.length > 0) {
26727
26771
  this.suggestionsQueue = suggestions;
26728
26772
  }
26729
26773
  }
@@ -26733,7 +26777,8 @@ var Conversation = class _Conversation {
26733
26777
  if (!errorType || ![
26734
26778
  "resource_exhausted",
26735
26779
  "deadline_exceeded",
26736
- "inactivity_timeout"
26780
+ "inactivity_timeout",
26781
+ "canceled"
26737
26782
  ].includes(errorType)) {
26738
26783
  this.options.onError({
26739
26784
  message: "A connection error occurred. Please try again.",
@@ -26742,21 +26787,12 @@ var Conversation = class _Conversation {
26742
26787
  });
26743
26788
  return;
26744
26789
  }
26790
+ if (errorType === "canceled") {
26791
+ return;
26792
+ }
26745
26793
  if (errorType === "resource_exhausted") {
26746
- if (this.avController.isPlaying) {
26747
- this.options.onError({
26748
- message: "We are experiencing heavy traffic and we can't deliver the response, try again",
26749
- endConversation: false,
26750
- type: "toast"
26751
- });
26752
- } else {
26753
- this.options.onError({
26754
- message: "The system is experiencing heavy traffic. Please try again later.",
26755
- endConversation: true,
26756
- type: "toast"
26757
- });
26758
- return;
26759
- }
26794
+ this.options.onHighDemand();
26795
+ return;
26760
26796
  } else if (errorType === "deadline_exceeded") {
26761
26797
  this.options.onError({
26762
26798
  message: "We are experiencing heavy traffic and we can't deliver the response, try again",
@@ -26817,6 +26853,15 @@ var Conversation = class _Conversation {
26817
26853
  if (this.avController.isStoppingAV) return;
26818
26854
  this.handleVideoFrame(event);
26819
26855
  }
26856
+ if (event.type === "stop") {
26857
+ this.videoOutput.clearFrame();
26858
+ this.audioOutput.worklet.port.postMessage({
26859
+ type: "stopPlayback"
26860
+ });
26861
+ this.options.onStoppingEnd();
26862
+ this.isStoppingLastResponse = false;
26863
+ return;
26864
+ }
26820
26865
  this.avController.playAudioVideo();
26821
26866
  };
26822
26867
  this.handleVideoFrame = async (event) => {
@@ -26942,7 +26987,7 @@ var Conversation = class _Conversation {
26942
26987
  this.options.onConnect({
26943
26988
  userId: connection.userId,
26944
26989
  headInfo: {
26945
- name: this.headInfo.name,
26990
+ name: this.headInfo.alias,
26946
26991
  phrases: this.headInfo.phrases,
26947
26992
  language: this.headInfo.language,
26948
26993
  avatar: this.headInfo.avatarSrc
@@ -26971,6 +27016,8 @@ var Conversation = class _Conversation {
26971
27016
  onMicrophoneStatusChange: () => {
26972
27017
  },
26973
27018
  onMicrophoneSpeechRecognitionResult: () => {
27019
+ },
27020
+ onMicrophonePartialSpeechRecognitionResult: () => {
26974
27021
  }
26975
27022
  },
26976
27023
  elevenLabsOptions: defaultElevenLabsOptions,
@@ -26992,6 +27039,8 @@ var Conversation = class _Conversation {
26992
27039
  },
26993
27040
  onStoppingEnd: () => {
26994
27041
  },
27042
+ onStoppingStart: () => {
27043
+ },
26995
27044
  onTimeout: () => {
26996
27045
  },
26997
27046
  onTimeoutWarning: () => {
@@ -27000,6 +27049,8 @@ var Conversation = class _Conversation {
27000
27049
  },
27001
27050
  onError: () => {
27002
27051
  },
27052
+ onHighDemand: () => {
27053
+ },
27003
27054
  ...partialOptions
27004
27055
  };
27005
27056
  }
@@ -27163,23 +27214,47 @@ var Conversation = class _Conversation {
27163
27214
  }
27164
27215
  }
27165
27216
  /**
27166
- * To stop current response, we'll do the following:
27167
- * 1. set a flag that'll prevent adding new audio & video events to their respective queues
27168
- * 2. clear video queue & switch state to idle
27169
- * 3. clear audio queue
27170
- * 4. send an event if all expected response from BE has been received. If not, FE will keep status in 'stopping' mode
27217
+ * To stop streaming response, we'll send a stop message to the BE. The BE will then stop sending audio and video frames, which will naturally end the response. This is more efficient and leads to a better user experience as it allows for a smoother transition when stopping the response.
27218
+ *
27219
+ * To stop cached response, we just stop the video and fade to idle.
27171
27220
  */
27172
- // public stopCurrentResponse() {
27173
- // this.avController.toggleStoppingVideo(true);
27174
- // this.videoOutput.clearFrame();
27175
- // this.audioOutput.worklet.port.postMessage({
27176
- // type: "stopPlayback",
27177
- // });
27178
- // if (!this.videoOutput.getStreamingStatus()) {
27179
- // this.options.onStoppingEnd();
27180
- // this.avController.toggleStoppingVideo(false);
27181
- // }
27182
- // }
27221
+ async stopResponse() {
27222
+ if (this.isStoppingLastResponse) {
27223
+ return;
27224
+ }
27225
+ this.isStoppingLastResponse = true;
27226
+ this.options.onStoppingStart();
27227
+ if (!this.connection) {
27228
+ this.isStoppingLastResponse = false;
27229
+ this.options.onStoppingEnd();
27230
+ throw new Error("Connection not established");
27231
+ }
27232
+ if (this.videoOutput.isPlayingCachedVideo()) {
27233
+ await this.videoOutput.stopCachedVideo();
27234
+ this.isStoppingLastResponse = false;
27235
+ this.options.onStoppingEnd();
27236
+ return;
27237
+ }
27238
+ if (!this.avController.isPlaying) {
27239
+ this.isStoppingLastResponse = false;
27240
+ this.options.onStoppingEnd();
27241
+ return;
27242
+ }
27243
+ const sessionId = `${this.user.id}::${this.user.orgId}::${this.user.headId}::${this.user.sessionId.toString().padStart(5, "0")}`;
27244
+ const message = {
27245
+ id: this.messageCounter,
27246
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
27247
+ speaker: "user",
27248
+ text: "",
27249
+ isSent: false,
27250
+ user_id: this.user.id,
27251
+ username: this.user.username,
27252
+ event: "stop" /* STOP */,
27253
+ visible: true,
27254
+ session_id: sessionId
27255
+ };
27256
+ this.connection.sendMessage(message);
27257
+ }
27183
27258
  async toggleMute() {
27184
27259
  this.volume = this.volume === 0 ? 1 : 0;
27185
27260
  this.audioOutput.toggleMute();
@@ -27188,7 +27263,7 @@ var Conversation = class _Conversation {
27188
27263
  return this.volume;
27189
27264
  }
27190
27265
  async startSession() {
27191
- var _a;
27266
+ var _a, _b;
27192
27267
  this.sessionStarted = true;
27193
27268
  if (this.audioOutput.context.state === "suspended") {
27194
27269
  await this.audioOutput.context.resume();
@@ -27208,13 +27283,13 @@ var Conversation = class _Conversation {
27208
27283
  this.user,
27209
27284
  this.headInfo,
27210
27285
  this.microphoneAccess,
27211
- this.sendMessage
27286
+ (_b = this.options.voiceInterruptions) != null ? _b : false
27212
27287
  );
27213
27288
  }
27214
27289
  return this.connection;
27215
27290
  }
27216
27291
  async toggleMicrophone() {
27217
- var _a;
27292
+ var _a, _b;
27218
27293
  if (this.options.microphoneProvider === "custom") {
27219
27294
  throw new Error("Cannot toggle microphone for custom provider.");
27220
27295
  }
@@ -27226,7 +27301,7 @@ var Conversation = class _Conversation {
27226
27301
  this.user,
27227
27302
  this.headInfo,
27228
27303
  this.microphoneAccess,
27229
- this.sendMessage
27304
+ (_b = this.options.voiceInterruptions) != null ? _b : false
27230
27305
  );
27231
27306
  }
27232
27307
  if (!this.microphoneAccess && this.microphone.status() === "OFF") {
@@ -27284,6 +27359,7 @@ export {
27284
27359
  EventType,
27285
27360
  StreamingEventType,
27286
27361
  VideoTransitionType,
27362
+ defaultElevenLabsOptions,
27287
27363
  isBinaryEvent,
27288
27364
  isConversationEndEvent,
27289
27365
  isJoinEvent,