@estuary-ai/sdk 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3871,11 +3871,30 @@ var AudioPlayer = class {
3871
3871
  currentSource = null;
3872
3872
  currentMessageId = null;
3873
3873
  isPlaying = false;
3874
+ _isCleared = false;
3875
+ _interruptedMessageId = null;
3874
3876
  constructor(sampleRate, onEvent) {
3875
3877
  this.sampleRate = sampleRate;
3876
3878
  this.onEvent = onEvent;
3877
3879
  }
3880
+ /** Whether audio is currently playing */
3881
+ get playing() {
3882
+ return this.isPlaying;
3883
+ }
3884
+ /** The messageId of the currently playing audio, or null */
3885
+ get playingMessageId() {
3886
+ return this.currentMessageId;
3887
+ }
3888
+ /** Mark a messageId as interrupted so late-arriving chunks are dropped */
3889
+ setInterruptedMessageId(id) {
3890
+ this._interruptedMessageId = id;
3891
+ }
3878
3892
  enqueue(voice) {
3893
+ if (voice.messageId === this._interruptedMessageId) return;
3894
+ if (this._interruptedMessageId && voice.messageId !== this._interruptedMessageId) {
3895
+ this._interruptedMessageId = null;
3896
+ }
3897
+ this._isCleared = false;
3879
3898
  const ctx = this.getAudioContext();
3880
3899
  if (!ctx) return;
3881
3900
  const pcm16 = base64ToInt16Array(voice.audio);
@@ -3888,6 +3907,7 @@ var AudioPlayer = class {
3888
3907
  }
3889
3908
  }
3890
3909
  clear() {
3910
+ this._isCleared = true;
3891
3911
  this.queue.length = 0;
3892
3912
  if (this.currentSource) {
3893
3913
  try {
@@ -3897,6 +3917,7 @@ var AudioPlayer = class {
3897
3917
  }
3898
3918
  this.currentSource = null;
3899
3919
  }
3920
+ if (this.audioElement) this.audioElement.muted = true;
3900
3921
  this.isPlaying = false;
3901
3922
  this.currentMessageId = null;
3902
3923
  }
@@ -3940,6 +3961,7 @@ var AudioPlayer = class {
3940
3961
  return ctx;
3941
3962
  }
3942
3963
  playNext() {
3964
+ if (this._isCleared) return;
3943
3965
  const ctx = this.getAudioContext();
3944
3966
  if (!ctx || this.queue.length === 0) {
3945
3967
  if (this.isPlaying && this.currentMessageId) {
@@ -3963,9 +3985,11 @@ var AudioPlayer = class {
3963
3985
  source.connect(this.mediaStreamDest ?? ctx.destination);
3964
3986
  this.currentSource = source;
3965
3987
  source.onended = () => {
3988
+ if (this._isCleared) return;
3966
3989
  this.currentSource = null;
3967
3990
  this.playNext();
3968
3991
  };
3992
+ if (this.audioElement) this.audioElement.muted = false;
3969
3993
  ctx.resume().catch(() => {
3970
3994
  });
3971
3995
  source.start();
@@ -4071,6 +4095,7 @@ var EstuaryClient = class extends TypedEventEmitter {
4071
4095
  _memory;
4072
4096
  _sessionInfo = null;
4073
4097
  actionParsers = /* @__PURE__ */ new Map();
4098
+ _hasAutoInterrupted = false;
4074
4099
  constructor(config) {
4075
4100
  super();
4076
4101
  this.config = config;
@@ -4121,6 +4146,7 @@ var EstuaryClient = class extends TypedEventEmitter {
4121
4146
  interrupt(messageId) {
4122
4147
  this.ensureConnected();
4123
4148
  this.socketManager.emitEvent("client_interrupt", { message_id: messageId });
4149
+ this.audioPlayer?.setInterruptedMessageId(messageId ?? this.audioPlayer.playingMessageId);
4124
4150
  this.audioPlayer?.clear();
4125
4151
  if (this.config.suppressMicDuringPlayback) {
4126
4152
  this.voiceManager?.setSuppressed?.(false);
@@ -4162,6 +4188,7 @@ var EstuaryClient = class extends TypedEventEmitter {
4162
4188
  if (!this.audioPlayer && typeof AudioContext !== "undefined") {
4163
4189
  this.audioPlayer = new AudioPlayer(sampleRate, (event) => {
4164
4190
  if (event.type === "started") {
4191
+ this._hasAutoInterrupted = false;
4165
4192
  this.emit("audioPlaybackStarted", event.messageId);
4166
4193
  if (this.config.suppressMicDuringPlayback) {
4167
4194
  this.voiceManager?.setSuppressed?.(true);
@@ -4222,10 +4249,15 @@ var EstuaryClient = class extends TypedEventEmitter {
4222
4249
  this.socketManager.on("connectionStateChanged", (state) => this.emit("connectionStateChanged", state));
4223
4250
  this.socketManager.on("botResponse", (response) => this.handleBotResponse(response));
4224
4251
  this.socketManager.on("botVoice", (voice) => this.handleBotVoice(voice));
4225
- this.socketManager.on("sttResponse", (response) => this.emit("sttResponse", response));
4252
+ this.socketManager.on("sttResponse", (response) => {
4253
+ this.maybeAutoInterrupt(response);
4254
+ this.emit("sttResponse", response);
4255
+ });
4226
4256
  this.socketManager.on("interrupt", (data) => {
4257
+ this.audioPlayer?.setInterruptedMessageId(data.messageId ?? null);
4227
4258
  this.audioPlayer?.clear();
4228
4259
  this.actionParsers.clear();
4260
+ this._hasAutoInterrupted = false;
4229
4261
  if (this.config.suppressMicDuringPlayback) {
4230
4262
  this.voiceManager?.setSuppressed?.(false);
4231
4263
  }
@@ -4265,6 +4297,15 @@ var EstuaryClient = class extends TypedEventEmitter {
4265
4297
  this.emit("botVoice", voice);
4266
4298
  this.audioPlayer?.enqueue(voice);
4267
4299
  }
4300
+ maybeAutoInterrupt(stt) {
4301
+ if ((this.config.autoInterruptOnSpeech ?? true) === false) return;
4302
+ if (this.config.suppressMicDuringPlayback) return;
4303
+ if (stt.isFinal) return;
4304
+ if (!this.audioPlayer?.playing) return;
4305
+ if (this._hasAutoInterrupted) return;
4306
+ this._hasAutoInterrupted = true;
4307
+ this.interrupt();
4308
+ }
4268
4309
  };
4269
4310
 
4270
4311
  export { ConnectionState, EstuaryClient, parseActions };