@estuary-ai/sdk 0.1.7 → 0.1.8

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 {
@@ -3940,6 +3960,7 @@ var AudioPlayer = class {
3940
3960
  return ctx;
3941
3961
  }
3942
3962
  playNext() {
3963
+ if (this._isCleared) return;
3943
3964
  const ctx = this.getAudioContext();
3944
3965
  if (!ctx || this.queue.length === 0) {
3945
3966
  if (this.isPlaying && this.currentMessageId) {
@@ -3963,6 +3984,7 @@ var AudioPlayer = class {
3963
3984
  source.connect(this.mediaStreamDest ?? ctx.destination);
3964
3985
  this.currentSource = source;
3965
3986
  source.onended = () => {
3987
+ if (this._isCleared) return;
3966
3988
  this.currentSource = null;
3967
3989
  this.playNext();
3968
3990
  };
@@ -4071,6 +4093,7 @@ var EstuaryClient = class extends TypedEventEmitter {
4071
4093
  _memory;
4072
4094
  _sessionInfo = null;
4073
4095
  actionParsers = /* @__PURE__ */ new Map();
4096
+ _hasAutoInterrupted = false;
4074
4097
  constructor(config) {
4075
4098
  super();
4076
4099
  this.config = config;
@@ -4121,6 +4144,7 @@ var EstuaryClient = class extends TypedEventEmitter {
4121
4144
  interrupt(messageId) {
4122
4145
  this.ensureConnected();
4123
4146
  this.socketManager.emitEvent("client_interrupt", { message_id: messageId });
4147
+ this.audioPlayer?.setInterruptedMessageId(messageId ?? this.audioPlayer.playingMessageId);
4124
4148
  this.audioPlayer?.clear();
4125
4149
  if (this.config.suppressMicDuringPlayback) {
4126
4150
  this.voiceManager?.setSuppressed?.(false);
@@ -4162,6 +4186,7 @@ var EstuaryClient = class extends TypedEventEmitter {
4162
4186
  if (!this.audioPlayer && typeof AudioContext !== "undefined") {
4163
4187
  this.audioPlayer = new AudioPlayer(sampleRate, (event) => {
4164
4188
  if (event.type === "started") {
4189
+ this._hasAutoInterrupted = false;
4165
4190
  this.emit("audioPlaybackStarted", event.messageId);
4166
4191
  if (this.config.suppressMicDuringPlayback) {
4167
4192
  this.voiceManager?.setSuppressed?.(true);
@@ -4222,10 +4247,15 @@ var EstuaryClient = class extends TypedEventEmitter {
4222
4247
  this.socketManager.on("connectionStateChanged", (state) => this.emit("connectionStateChanged", state));
4223
4248
  this.socketManager.on("botResponse", (response) => this.handleBotResponse(response));
4224
4249
  this.socketManager.on("botVoice", (voice) => this.handleBotVoice(voice));
4225
- this.socketManager.on("sttResponse", (response) => this.emit("sttResponse", response));
4250
+ this.socketManager.on("sttResponse", (response) => {
4251
+ this.maybeAutoInterrupt(response);
4252
+ this.emit("sttResponse", response);
4253
+ });
4226
4254
  this.socketManager.on("interrupt", (data) => {
4255
+ this.audioPlayer?.setInterruptedMessageId(data.messageId ?? null);
4227
4256
  this.audioPlayer?.clear();
4228
4257
  this.actionParsers.clear();
4258
+ this._hasAutoInterrupted = false;
4229
4259
  if (this.config.suppressMicDuringPlayback) {
4230
4260
  this.voiceManager?.setSuppressed?.(false);
4231
4261
  }
@@ -4265,6 +4295,15 @@ var EstuaryClient = class extends TypedEventEmitter {
4265
4295
  this.emit("botVoice", voice);
4266
4296
  this.audioPlayer?.enqueue(voice);
4267
4297
  }
4298
+ maybeAutoInterrupt(stt) {
4299
+ if ((this.config.autoInterruptOnSpeech ?? true) === false) return;
4300
+ if (this.config.suppressMicDuringPlayback) return;
4301
+ if (stt.isFinal) return;
4302
+ if (!this.audioPlayer?.playing) return;
4303
+ if (this._hasAutoInterrupted) return;
4304
+ this._hasAutoInterrupted = true;
4305
+ this.interrupt();
4306
+ }
4268
4307
  };
4269
4308
 
4270
4309
  export { ConnectionState, EstuaryClient, parseActions };