@whereby.com/assistant-sdk 0.0.0-canary-20250917154617 → 0.0.0-canary-20250923134558

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.cjs CHANGED
@@ -35,6 +35,10 @@ const TRIGGER_EVENT_SUCCESS = "trigger_event_success";
35
35
  const AUDIO_STREAM_READY = "AUDIO_STREAM_READY";
36
36
  const ASSISTANT_JOINED_ROOM = "ASSISTANT_JOINED_ROOM";
37
37
  const ASSISTANT_LEFT_ROOM = "ASSISTANT_LEFT_ROOM";
38
+ const PARTICIPANT_VIDEO_TRACK_ADDED = "PARTICIPANT_VIDEO_TRACK_ADDED";
39
+ const PARTICIPANT_VIDEO_TRACK_REMOVED = "PARTICIPANT_VIDEO_TRACK_REMOVED";
40
+ const PARTICIPANT_AUDIO_TRACK_ADDED = "PARTICIPANT_AUDIO_TRACK_ADDED";
41
+ const PARTICIPANT_AUDIO_TRACK_REMOVED = "PARTICIPANT_AUDIO_TRACK_REMOVED";
38
42
 
39
43
  /******************************************************************************
40
44
  Copyright (c) Microsoft Corporation.
@@ -580,15 +584,53 @@ class Assistant extends EventEmitter {
580
584
  this.mediaStream = null;
581
585
  this.audioSource = null;
582
586
  this.combinedStream = null;
587
+ this.remoteMediaTracks = {};
583
588
  this.roomUrl = null;
589
+ this.stateSubscriptions = [];
584
590
  this.handleConnectionStatusChange = (status) => {
585
591
  if (status === "connected") {
586
592
  this.emit(ASSISTANT_JOINED_ROOM, { roomUrl: this.roomUrl || "" });
587
593
  }
588
594
  if (["left", "kicked"].includes(status)) {
595
+ this.stateSubscriptions.forEach((unsubscribe) => unsubscribe());
589
596
  this.emit(ASSISTANT_LEFT_ROOM, { roomUrl: this.roomUrl || "" });
590
597
  }
591
598
  };
599
+ this.handleRemoteParticipantsTracksChange = (remoteParticipants) => {
600
+ const currentRemoteMediaTracks = remoteParticipants.flatMap(({ id: participantId, stream }) => {
601
+ if (!stream) {
602
+ return [];
603
+ }
604
+ const tracks = stream.getTracks();
605
+ tracks.forEach((track) => {
606
+ if (!this.remoteMediaTracks[track.id]) {
607
+ const eventName = track.kind === "video" ? PARTICIPANT_VIDEO_TRACK_ADDED : PARTICIPANT_AUDIO_TRACK_ADDED;
608
+ this.emit(eventName, {
609
+ participantId,
610
+ stream,
611
+ track,
612
+ });
613
+ this.remoteMediaTracks[track.id] = {
614
+ participantId,
615
+ stream,
616
+ track,
617
+ };
618
+ }
619
+ });
620
+ return tracks;
621
+ });
622
+ Object.values(this.remoteMediaTracks).forEach(({ participantId, stream, track }) => {
623
+ if (!currentRemoteMediaTracks.includes(track)) {
624
+ const eventName = track.kind === "video" ? PARTICIPANT_VIDEO_TRACK_REMOVED : PARTICIPANT_AUDIO_TRACK_REMOVED;
625
+ this.emit(eventName, {
626
+ participantId,
627
+ stream,
628
+ track,
629
+ });
630
+ delete this.remoteMediaTracks[track.id];
631
+ }
632
+ });
633
+ };
592
634
  this.assistantKey = assistantKey;
593
635
  this.client = new core.WherebyClient();
594
636
  this.roomConnection = this.client.getRoomConnection();
@@ -612,9 +654,10 @@ class Assistant extends EventEmitter {
612
654
  };
613
655
  const audioMixer = new AudioMixer(handleStreamReady);
614
656
  this.combinedStream = audioMixer.getCombinedAudioStream();
615
- this.roomConnection.subscribeToRemoteParticipants(audioMixer.handleRemoteParticipants.bind(audioMixer));
616
- this.roomConnection.subscribeToConnectionStatus(this.handleConnectionStatusChange);
657
+ this.stateSubscriptions.push(this.roomConnection.subscribeToRemoteParticipants(audioMixer.handleRemoteParticipants.bind(audioMixer)));
617
658
  }
659
+ this.stateSubscriptions.push(this.roomConnection.subscribeToConnectionStatus(this.handleConnectionStatusChange));
660
+ this.stateSubscriptions.push(this.roomConnection.subscribeToRemoteParticipants(this.handleRemoteParticipantsTracksChange));
618
661
  }
619
662
  joinRoom(roomUrl) {
620
663
  return __awaiter(this, void 0, void 0, function* () {
@@ -731,7 +774,7 @@ const webhookRouter = (webhookTriggers, emitter) => {
731
774
  res.status(200);
732
775
  res.end();
733
776
  });
734
- router.post("/", jsonParser, (req, res) => {
777
+ router.post("/", jsonParser, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
735
778
  var _a, _b, _c, _d, _e, _f, _g, _h;
736
779
  assert(req.body, "message body is required");
737
780
  assert("type" in req.body, "webhook type is required");
@@ -739,19 +782,19 @@ const webhookRouter = (webhookTriggers, emitter) => {
739
782
  switch (req.body.type) {
740
783
  case "room.client.joined":
741
784
  shouldTriggerOnReceivedWebhook =
742
- (_b = (_a = webhookTriggers["room.client.joined"]) === null || _a === void 0 ? void 0 : _a.call(webhookTriggers, req.body)) !== null && _b !== void 0 ? _b : false;
785
+ (_b = (yield ((_a = webhookTriggers["room.client.joined"]) === null || _a === void 0 ? void 0 : _a.call(webhookTriggers, req.body)))) !== null && _b !== void 0 ? _b : false;
743
786
  break;
744
787
  case "room.client.left":
745
788
  shouldTriggerOnReceivedWebhook =
746
- (_d = (_c = webhookTriggers["room.client.left"]) === null || _c === void 0 ? void 0 : _c.call(webhookTriggers, req.body)) !== null && _d !== void 0 ? _d : false;
789
+ (_d = (yield ((_c = webhookTriggers["room.client.left"]) === null || _c === void 0 ? void 0 : _c.call(webhookTriggers, req.body)))) !== null && _d !== void 0 ? _d : false;
747
790
  break;
748
791
  case "room.session.started":
749
792
  shouldTriggerOnReceivedWebhook =
750
- (_f = (_e = webhookTriggers["room.session.started"]) === null || _e === void 0 ? void 0 : _e.call(webhookTriggers, req.body)) !== null && _f !== void 0 ? _f : false;
793
+ (_f = (yield ((_e = webhookTriggers["room.session.started"]) === null || _e === void 0 ? void 0 : _e.call(webhookTriggers, req.body)))) !== null && _f !== void 0 ? _f : false;
751
794
  break;
752
795
  case "room.session.ended":
753
796
  shouldTriggerOnReceivedWebhook =
754
- (_h = (_g = webhookTriggers["room.session.ended"]) === null || _g === void 0 ? void 0 : _g.call(webhookTriggers, req.body)) !== null && _h !== void 0 ? _h : false;
797
+ (_h = (yield ((_g = webhookTriggers["room.session.ended"]) === null || _g === void 0 ? void 0 : _g.call(webhookTriggers, req.body)))) !== null && _h !== void 0 ? _h : false;
755
798
  break;
756
799
  }
757
800
  if (shouldTriggerOnReceivedWebhook) {
@@ -760,7 +803,7 @@ const webhookRouter = (webhookTriggers, emitter) => {
760
803
  }
761
804
  res.status(200);
762
805
  res.end();
763
- });
806
+ }));
764
807
  return router;
765
808
  };
766
809
  class Trigger extends EventEmitter.EventEmitter {
@@ -773,11 +816,8 @@ class Trigger extends EventEmitter.EventEmitter {
773
816
  const app = express();
774
817
  const router = webhookRouter(this.webhookTriggers, this);
775
818
  app.use(router);
776
- const server = app.listen(this.port, () => {
777
- // console.log(`Bot trigger server now running on port[${this.port}]`);
778
- });
779
- process.on("SIGTERM", () => {
780
- server.close();
819
+ app.listen(this.port).on("error", (error) => {
820
+ console.error("Could not start Trigger web server", error);
781
821
  });
782
822
  }
783
823
  }
@@ -788,5 +828,9 @@ exports.AUDIO_STREAM_READY = AUDIO_STREAM_READY;
788
828
  exports.Assistant = Assistant;
789
829
  exports.AudioSink = AudioSink;
790
830
  exports.AudioSource = AudioSource;
831
+ exports.PARTICIPANT_AUDIO_TRACK_ADDED = PARTICIPANT_AUDIO_TRACK_ADDED;
832
+ exports.PARTICIPANT_AUDIO_TRACK_REMOVED = PARTICIPANT_AUDIO_TRACK_REMOVED;
833
+ exports.PARTICIPANT_VIDEO_TRACK_ADDED = PARTICIPANT_VIDEO_TRACK_ADDED;
834
+ exports.PARTICIPANT_VIDEO_TRACK_REMOVED = PARTICIPANT_VIDEO_TRACK_REMOVED;
791
835
  exports.TRIGGER_EVENT_SUCCESS = TRIGGER_EVENT_SUCCESS;
792
836
  exports.Trigger = Trigger;
package/dist/index.d.cts CHANGED
@@ -88,12 +88,16 @@ type WherebyWebhookTriggerTypes = {
88
88
  "room.session.ended": WherebyWebhookRoomSessionEnded;
89
89
  };
90
90
  type WherebyWebhookTriggers = Partial<{
91
- [Type in keyof WherebyWebhookTriggerTypes]: (data: WherebyWebhookTriggerTypes[Type]) => boolean;
91
+ [Type in keyof WherebyWebhookTriggerTypes]: (data: WherebyWebhookTriggerTypes[Type]) => Promise<boolean> | boolean;
92
92
  }>;
93
93
 
94
94
  declare const AUDIO_STREAM_READY = "AUDIO_STREAM_READY";
95
95
  declare const ASSISTANT_JOINED_ROOM = "ASSISTANT_JOINED_ROOM";
96
96
  declare const ASSISTANT_LEFT_ROOM = "ASSISTANT_LEFT_ROOM";
97
+ declare const PARTICIPANT_VIDEO_TRACK_ADDED = "PARTICIPANT_VIDEO_TRACK_ADDED";
98
+ declare const PARTICIPANT_VIDEO_TRACK_REMOVED = "PARTICIPANT_VIDEO_TRACK_REMOVED";
99
+ declare const PARTICIPANT_AUDIO_TRACK_ADDED = "PARTICIPANT_AUDIO_TRACK_ADDED";
100
+ declare const PARTICIPANT_AUDIO_TRACK_REMOVED = "PARTICIPANT_AUDIO_TRACK_REMOVED";
97
101
  type AssistantEvents = {
98
102
  [ASSISTANT_JOINED_ROOM]: [{
99
103
  roomUrl: string;
@@ -105,6 +109,26 @@ type AssistantEvents = {
105
109
  stream: MediaStream;
106
110
  track: MediaStreamTrack;
107
111
  }];
112
+ [PARTICIPANT_VIDEO_TRACK_ADDED]: [{
113
+ participantId: string;
114
+ stream: MediaStream;
115
+ track: MediaStreamTrack;
116
+ }];
117
+ [PARTICIPANT_VIDEO_TRACK_REMOVED]: [{
118
+ participantId: string;
119
+ stream: MediaStream;
120
+ track: MediaStreamTrack;
121
+ }];
122
+ [PARTICIPANT_AUDIO_TRACK_ADDED]: [{
123
+ participantId: string;
124
+ stream: MediaStream;
125
+ track: MediaStreamTrack;
126
+ }];
127
+ [PARTICIPANT_AUDIO_TRACK_REMOVED]: [{
128
+ participantId: string;
129
+ stream: MediaStream;
130
+ track: MediaStreamTrack;
131
+ }];
108
132
  };
109
133
 
110
134
  type AssistantOptions = {
@@ -120,9 +144,12 @@ declare class Assistant extends EventEmitter<AssistantEvents> {
120
144
  private mediaStream;
121
145
  private audioSource;
122
146
  private combinedStream;
147
+ private remoteMediaTracks;
123
148
  private roomUrl;
149
+ private stateSubscriptions;
124
150
  constructor({ assistantKey, startCombinedAudioStream, startLocalMedia }: AssistantOptions);
125
151
  private handleConnectionStatusChange;
152
+ private handleRemoteParticipantsTracksChange;
126
153
  joinRoom(roomUrl: string): Promise<void>;
127
154
  startLocalMedia(): void;
128
155
  getLocalMediaStream(): MediaStream | null;
@@ -169,5 +196,5 @@ declare class AudioSink extends wrtc__default.nonstandard.RTCAudioSink {
169
196
  }) => void): () => void;
170
197
  }
171
198
 
172
- export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, TRIGGER_EVENT_SUCCESS, Trigger };
199
+ export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, PARTICIPANT_AUDIO_TRACK_ADDED, PARTICIPANT_AUDIO_TRACK_REMOVED, PARTICIPANT_VIDEO_TRACK_ADDED, PARTICIPANT_VIDEO_TRACK_REMOVED, TRIGGER_EVENT_SUCCESS, Trigger };
173
200
  export type { AssistantEvents };
package/dist/index.d.mts CHANGED
@@ -88,12 +88,16 @@ type WherebyWebhookTriggerTypes = {
88
88
  "room.session.ended": WherebyWebhookRoomSessionEnded;
89
89
  };
90
90
  type WherebyWebhookTriggers = Partial<{
91
- [Type in keyof WherebyWebhookTriggerTypes]: (data: WherebyWebhookTriggerTypes[Type]) => boolean;
91
+ [Type in keyof WherebyWebhookTriggerTypes]: (data: WherebyWebhookTriggerTypes[Type]) => Promise<boolean> | boolean;
92
92
  }>;
93
93
 
94
94
  declare const AUDIO_STREAM_READY = "AUDIO_STREAM_READY";
95
95
  declare const ASSISTANT_JOINED_ROOM = "ASSISTANT_JOINED_ROOM";
96
96
  declare const ASSISTANT_LEFT_ROOM = "ASSISTANT_LEFT_ROOM";
97
+ declare const PARTICIPANT_VIDEO_TRACK_ADDED = "PARTICIPANT_VIDEO_TRACK_ADDED";
98
+ declare const PARTICIPANT_VIDEO_TRACK_REMOVED = "PARTICIPANT_VIDEO_TRACK_REMOVED";
99
+ declare const PARTICIPANT_AUDIO_TRACK_ADDED = "PARTICIPANT_AUDIO_TRACK_ADDED";
100
+ declare const PARTICIPANT_AUDIO_TRACK_REMOVED = "PARTICIPANT_AUDIO_TRACK_REMOVED";
97
101
  type AssistantEvents = {
98
102
  [ASSISTANT_JOINED_ROOM]: [{
99
103
  roomUrl: string;
@@ -105,6 +109,26 @@ type AssistantEvents = {
105
109
  stream: MediaStream;
106
110
  track: MediaStreamTrack;
107
111
  }];
112
+ [PARTICIPANT_VIDEO_TRACK_ADDED]: [{
113
+ participantId: string;
114
+ stream: MediaStream;
115
+ track: MediaStreamTrack;
116
+ }];
117
+ [PARTICIPANT_VIDEO_TRACK_REMOVED]: [{
118
+ participantId: string;
119
+ stream: MediaStream;
120
+ track: MediaStreamTrack;
121
+ }];
122
+ [PARTICIPANT_AUDIO_TRACK_ADDED]: [{
123
+ participantId: string;
124
+ stream: MediaStream;
125
+ track: MediaStreamTrack;
126
+ }];
127
+ [PARTICIPANT_AUDIO_TRACK_REMOVED]: [{
128
+ participantId: string;
129
+ stream: MediaStream;
130
+ track: MediaStreamTrack;
131
+ }];
108
132
  };
109
133
 
110
134
  type AssistantOptions = {
@@ -120,9 +144,12 @@ declare class Assistant extends EventEmitter<AssistantEvents> {
120
144
  private mediaStream;
121
145
  private audioSource;
122
146
  private combinedStream;
147
+ private remoteMediaTracks;
123
148
  private roomUrl;
149
+ private stateSubscriptions;
124
150
  constructor({ assistantKey, startCombinedAudioStream, startLocalMedia }: AssistantOptions);
125
151
  private handleConnectionStatusChange;
152
+ private handleRemoteParticipantsTracksChange;
126
153
  joinRoom(roomUrl: string): Promise<void>;
127
154
  startLocalMedia(): void;
128
155
  getLocalMediaStream(): MediaStream | null;
@@ -169,5 +196,5 @@ declare class AudioSink extends wrtc__default.nonstandard.RTCAudioSink {
169
196
  }) => void): () => void;
170
197
  }
171
198
 
172
- export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, TRIGGER_EVENT_SUCCESS, Trigger };
199
+ export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, PARTICIPANT_AUDIO_TRACK_ADDED, PARTICIPANT_AUDIO_TRACK_REMOVED, PARTICIPANT_VIDEO_TRACK_ADDED, PARTICIPANT_VIDEO_TRACK_REMOVED, TRIGGER_EVENT_SUCCESS, Trigger };
173
200
  export type { AssistantEvents };
package/dist/index.d.ts CHANGED
@@ -88,12 +88,16 @@ type WherebyWebhookTriggerTypes = {
88
88
  "room.session.ended": WherebyWebhookRoomSessionEnded;
89
89
  };
90
90
  type WherebyWebhookTriggers = Partial<{
91
- [Type in keyof WherebyWebhookTriggerTypes]: (data: WherebyWebhookTriggerTypes[Type]) => boolean;
91
+ [Type in keyof WherebyWebhookTriggerTypes]: (data: WherebyWebhookTriggerTypes[Type]) => Promise<boolean> | boolean;
92
92
  }>;
93
93
 
94
94
  declare const AUDIO_STREAM_READY = "AUDIO_STREAM_READY";
95
95
  declare const ASSISTANT_JOINED_ROOM = "ASSISTANT_JOINED_ROOM";
96
96
  declare const ASSISTANT_LEFT_ROOM = "ASSISTANT_LEFT_ROOM";
97
+ declare const PARTICIPANT_VIDEO_TRACK_ADDED = "PARTICIPANT_VIDEO_TRACK_ADDED";
98
+ declare const PARTICIPANT_VIDEO_TRACK_REMOVED = "PARTICIPANT_VIDEO_TRACK_REMOVED";
99
+ declare const PARTICIPANT_AUDIO_TRACK_ADDED = "PARTICIPANT_AUDIO_TRACK_ADDED";
100
+ declare const PARTICIPANT_AUDIO_TRACK_REMOVED = "PARTICIPANT_AUDIO_TRACK_REMOVED";
97
101
  type AssistantEvents = {
98
102
  [ASSISTANT_JOINED_ROOM]: [{
99
103
  roomUrl: string;
@@ -105,6 +109,26 @@ type AssistantEvents = {
105
109
  stream: MediaStream;
106
110
  track: MediaStreamTrack;
107
111
  }];
112
+ [PARTICIPANT_VIDEO_TRACK_ADDED]: [{
113
+ participantId: string;
114
+ stream: MediaStream;
115
+ track: MediaStreamTrack;
116
+ }];
117
+ [PARTICIPANT_VIDEO_TRACK_REMOVED]: [{
118
+ participantId: string;
119
+ stream: MediaStream;
120
+ track: MediaStreamTrack;
121
+ }];
122
+ [PARTICIPANT_AUDIO_TRACK_ADDED]: [{
123
+ participantId: string;
124
+ stream: MediaStream;
125
+ track: MediaStreamTrack;
126
+ }];
127
+ [PARTICIPANT_AUDIO_TRACK_REMOVED]: [{
128
+ participantId: string;
129
+ stream: MediaStream;
130
+ track: MediaStreamTrack;
131
+ }];
108
132
  };
109
133
 
110
134
  type AssistantOptions = {
@@ -120,9 +144,12 @@ declare class Assistant extends EventEmitter<AssistantEvents> {
120
144
  private mediaStream;
121
145
  private audioSource;
122
146
  private combinedStream;
147
+ private remoteMediaTracks;
123
148
  private roomUrl;
149
+ private stateSubscriptions;
124
150
  constructor({ assistantKey, startCombinedAudioStream, startLocalMedia }: AssistantOptions);
125
151
  private handleConnectionStatusChange;
152
+ private handleRemoteParticipantsTracksChange;
126
153
  joinRoom(roomUrl: string): Promise<void>;
127
154
  startLocalMedia(): void;
128
155
  getLocalMediaStream(): MediaStream | null;
@@ -169,5 +196,5 @@ declare class AudioSink extends wrtc__default.nonstandard.RTCAudioSink {
169
196
  }) => void): () => void;
170
197
  }
171
198
 
172
- export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, TRIGGER_EVENT_SUCCESS, Trigger };
199
+ export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, PARTICIPANT_AUDIO_TRACK_ADDED, PARTICIPANT_AUDIO_TRACK_REMOVED, PARTICIPANT_VIDEO_TRACK_ADDED, PARTICIPANT_VIDEO_TRACK_REMOVED, TRIGGER_EVENT_SUCCESS, Trigger };
173
200
  export type { AssistantEvents };
package/dist/index.mjs CHANGED
@@ -14,6 +14,10 @@ const TRIGGER_EVENT_SUCCESS = "trigger_event_success";
14
14
  const AUDIO_STREAM_READY = "AUDIO_STREAM_READY";
15
15
  const ASSISTANT_JOINED_ROOM = "ASSISTANT_JOINED_ROOM";
16
16
  const ASSISTANT_LEFT_ROOM = "ASSISTANT_LEFT_ROOM";
17
+ const PARTICIPANT_VIDEO_TRACK_ADDED = "PARTICIPANT_VIDEO_TRACK_ADDED";
18
+ const PARTICIPANT_VIDEO_TRACK_REMOVED = "PARTICIPANT_VIDEO_TRACK_REMOVED";
19
+ const PARTICIPANT_AUDIO_TRACK_ADDED = "PARTICIPANT_AUDIO_TRACK_ADDED";
20
+ const PARTICIPANT_AUDIO_TRACK_REMOVED = "PARTICIPANT_AUDIO_TRACK_REMOVED";
17
21
 
18
22
  /******************************************************************************
19
23
  Copyright (c) Microsoft Corporation.
@@ -559,15 +563,53 @@ class Assistant extends EventEmitter$1 {
559
563
  this.mediaStream = null;
560
564
  this.audioSource = null;
561
565
  this.combinedStream = null;
566
+ this.remoteMediaTracks = {};
562
567
  this.roomUrl = null;
568
+ this.stateSubscriptions = [];
563
569
  this.handleConnectionStatusChange = (status) => {
564
570
  if (status === "connected") {
565
571
  this.emit(ASSISTANT_JOINED_ROOM, { roomUrl: this.roomUrl || "" });
566
572
  }
567
573
  if (["left", "kicked"].includes(status)) {
574
+ this.stateSubscriptions.forEach((unsubscribe) => unsubscribe());
568
575
  this.emit(ASSISTANT_LEFT_ROOM, { roomUrl: this.roomUrl || "" });
569
576
  }
570
577
  };
578
+ this.handleRemoteParticipantsTracksChange = (remoteParticipants) => {
579
+ const currentRemoteMediaTracks = remoteParticipants.flatMap(({ id: participantId, stream }) => {
580
+ if (!stream) {
581
+ return [];
582
+ }
583
+ const tracks = stream.getTracks();
584
+ tracks.forEach((track) => {
585
+ if (!this.remoteMediaTracks[track.id]) {
586
+ const eventName = track.kind === "video" ? PARTICIPANT_VIDEO_TRACK_ADDED : PARTICIPANT_AUDIO_TRACK_ADDED;
587
+ this.emit(eventName, {
588
+ participantId,
589
+ stream,
590
+ track,
591
+ });
592
+ this.remoteMediaTracks[track.id] = {
593
+ participantId,
594
+ stream,
595
+ track,
596
+ };
597
+ }
598
+ });
599
+ return tracks;
600
+ });
601
+ Object.values(this.remoteMediaTracks).forEach(({ participantId, stream, track }) => {
602
+ if (!currentRemoteMediaTracks.includes(track)) {
603
+ const eventName = track.kind === "video" ? PARTICIPANT_VIDEO_TRACK_REMOVED : PARTICIPANT_AUDIO_TRACK_REMOVED;
604
+ this.emit(eventName, {
605
+ participantId,
606
+ stream,
607
+ track,
608
+ });
609
+ delete this.remoteMediaTracks[track.id];
610
+ }
611
+ });
612
+ };
571
613
  this.assistantKey = assistantKey;
572
614
  this.client = new WherebyClient();
573
615
  this.roomConnection = this.client.getRoomConnection();
@@ -591,9 +633,10 @@ class Assistant extends EventEmitter$1 {
591
633
  };
592
634
  const audioMixer = new AudioMixer(handleStreamReady);
593
635
  this.combinedStream = audioMixer.getCombinedAudioStream();
594
- this.roomConnection.subscribeToRemoteParticipants(audioMixer.handleRemoteParticipants.bind(audioMixer));
595
- this.roomConnection.subscribeToConnectionStatus(this.handleConnectionStatusChange);
636
+ this.stateSubscriptions.push(this.roomConnection.subscribeToRemoteParticipants(audioMixer.handleRemoteParticipants.bind(audioMixer)));
596
637
  }
638
+ this.stateSubscriptions.push(this.roomConnection.subscribeToConnectionStatus(this.handleConnectionStatusChange));
639
+ this.stateSubscriptions.push(this.roomConnection.subscribeToRemoteParticipants(this.handleRemoteParticipantsTracksChange));
597
640
  }
598
641
  joinRoom(roomUrl) {
599
642
  return __awaiter(this, void 0, void 0, function* () {
@@ -710,7 +753,7 @@ const webhookRouter = (webhookTriggers, emitter) => {
710
753
  res.status(200);
711
754
  res.end();
712
755
  });
713
- router.post("/", jsonParser, (req, res) => {
756
+ router.post("/", jsonParser, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
714
757
  var _a, _b, _c, _d, _e, _f, _g, _h;
715
758
  assert(req.body, "message body is required");
716
759
  assert("type" in req.body, "webhook type is required");
@@ -718,19 +761,19 @@ const webhookRouter = (webhookTriggers, emitter) => {
718
761
  switch (req.body.type) {
719
762
  case "room.client.joined":
720
763
  shouldTriggerOnReceivedWebhook =
721
- (_b = (_a = webhookTriggers["room.client.joined"]) === null || _a === void 0 ? void 0 : _a.call(webhookTriggers, req.body)) !== null && _b !== void 0 ? _b : false;
764
+ (_b = (yield ((_a = webhookTriggers["room.client.joined"]) === null || _a === void 0 ? void 0 : _a.call(webhookTriggers, req.body)))) !== null && _b !== void 0 ? _b : false;
722
765
  break;
723
766
  case "room.client.left":
724
767
  shouldTriggerOnReceivedWebhook =
725
- (_d = (_c = webhookTriggers["room.client.left"]) === null || _c === void 0 ? void 0 : _c.call(webhookTriggers, req.body)) !== null && _d !== void 0 ? _d : false;
768
+ (_d = (yield ((_c = webhookTriggers["room.client.left"]) === null || _c === void 0 ? void 0 : _c.call(webhookTriggers, req.body)))) !== null && _d !== void 0 ? _d : false;
726
769
  break;
727
770
  case "room.session.started":
728
771
  shouldTriggerOnReceivedWebhook =
729
- (_f = (_e = webhookTriggers["room.session.started"]) === null || _e === void 0 ? void 0 : _e.call(webhookTriggers, req.body)) !== null && _f !== void 0 ? _f : false;
772
+ (_f = (yield ((_e = webhookTriggers["room.session.started"]) === null || _e === void 0 ? void 0 : _e.call(webhookTriggers, req.body)))) !== null && _f !== void 0 ? _f : false;
730
773
  break;
731
774
  case "room.session.ended":
732
775
  shouldTriggerOnReceivedWebhook =
733
- (_h = (_g = webhookTriggers["room.session.ended"]) === null || _g === void 0 ? void 0 : _g.call(webhookTriggers, req.body)) !== null && _h !== void 0 ? _h : false;
776
+ (_h = (yield ((_g = webhookTriggers["room.session.ended"]) === null || _g === void 0 ? void 0 : _g.call(webhookTriggers, req.body)))) !== null && _h !== void 0 ? _h : false;
734
777
  break;
735
778
  }
736
779
  if (shouldTriggerOnReceivedWebhook) {
@@ -739,7 +782,7 @@ const webhookRouter = (webhookTriggers, emitter) => {
739
782
  }
740
783
  res.status(200);
741
784
  res.end();
742
- });
785
+ }));
743
786
  return router;
744
787
  };
745
788
  class Trigger extends EventEmitter {
@@ -752,13 +795,10 @@ class Trigger extends EventEmitter {
752
795
  const app = express();
753
796
  const router = webhookRouter(this.webhookTriggers, this);
754
797
  app.use(router);
755
- const server = app.listen(this.port, () => {
756
- // console.log(`Bot trigger server now running on port[${this.port}]`);
757
- });
758
- process.on("SIGTERM", () => {
759
- server.close();
798
+ app.listen(this.port).on("error", (error) => {
799
+ console.error("Could not start Trigger web server", error);
760
800
  });
761
801
  }
762
802
  }
763
803
 
764
- export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, TRIGGER_EVENT_SUCCESS, Trigger };
804
+ export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, PARTICIPANT_AUDIO_TRACK_ADDED, PARTICIPANT_AUDIO_TRACK_REMOVED, PARTICIPANT_VIDEO_TRACK_ADDED, PARTICIPANT_VIDEO_TRACK_REMOVED, TRIGGER_EVENT_SUCCESS, Trigger };
@@ -14,6 +14,10 @@ const TRIGGER_EVENT_SUCCESS = "trigger_event_success";
14
14
  const AUDIO_STREAM_READY = "AUDIO_STREAM_READY";
15
15
  const ASSISTANT_JOINED_ROOM = "ASSISTANT_JOINED_ROOM";
16
16
  const ASSISTANT_LEFT_ROOM = "ASSISTANT_LEFT_ROOM";
17
+ const PARTICIPANT_VIDEO_TRACK_ADDED = "PARTICIPANT_VIDEO_TRACK_ADDED";
18
+ const PARTICIPANT_VIDEO_TRACK_REMOVED = "PARTICIPANT_VIDEO_TRACK_REMOVED";
19
+ const PARTICIPANT_AUDIO_TRACK_ADDED = "PARTICIPANT_AUDIO_TRACK_ADDED";
20
+ const PARTICIPANT_AUDIO_TRACK_REMOVED = "PARTICIPANT_AUDIO_TRACK_REMOVED";
17
21
 
18
22
  /******************************************************************************
19
23
  Copyright (c) Microsoft Corporation.
@@ -559,15 +563,53 @@ class Assistant extends EventEmitter$1 {
559
563
  this.mediaStream = null;
560
564
  this.audioSource = null;
561
565
  this.combinedStream = null;
566
+ this.remoteMediaTracks = {};
562
567
  this.roomUrl = null;
568
+ this.stateSubscriptions = [];
563
569
  this.handleConnectionStatusChange = (status) => {
564
570
  if (status === "connected") {
565
571
  this.emit(ASSISTANT_JOINED_ROOM, { roomUrl: this.roomUrl || "" });
566
572
  }
567
573
  if (["left", "kicked"].includes(status)) {
574
+ this.stateSubscriptions.forEach((unsubscribe) => unsubscribe());
568
575
  this.emit(ASSISTANT_LEFT_ROOM, { roomUrl: this.roomUrl || "" });
569
576
  }
570
577
  };
578
+ this.handleRemoteParticipantsTracksChange = (remoteParticipants) => {
579
+ const currentRemoteMediaTracks = remoteParticipants.flatMap(({ id: participantId, stream }) => {
580
+ if (!stream) {
581
+ return [];
582
+ }
583
+ const tracks = stream.getTracks();
584
+ tracks.forEach((track) => {
585
+ if (!this.remoteMediaTracks[track.id]) {
586
+ const eventName = track.kind === "video" ? PARTICIPANT_VIDEO_TRACK_ADDED : PARTICIPANT_AUDIO_TRACK_ADDED;
587
+ this.emit(eventName, {
588
+ participantId,
589
+ stream,
590
+ track,
591
+ });
592
+ this.remoteMediaTracks[track.id] = {
593
+ participantId,
594
+ stream,
595
+ track,
596
+ };
597
+ }
598
+ });
599
+ return tracks;
600
+ });
601
+ Object.values(this.remoteMediaTracks).forEach(({ participantId, stream, track }) => {
602
+ if (!currentRemoteMediaTracks.includes(track)) {
603
+ const eventName = track.kind === "video" ? PARTICIPANT_VIDEO_TRACK_REMOVED : PARTICIPANT_AUDIO_TRACK_REMOVED;
604
+ this.emit(eventName, {
605
+ participantId,
606
+ stream,
607
+ track,
608
+ });
609
+ delete this.remoteMediaTracks[track.id];
610
+ }
611
+ });
612
+ };
571
613
  this.assistantKey = assistantKey;
572
614
  this.client = new WherebyClient();
573
615
  this.roomConnection = this.client.getRoomConnection();
@@ -591,9 +633,10 @@ class Assistant extends EventEmitter$1 {
591
633
  };
592
634
  const audioMixer = new AudioMixer(handleStreamReady);
593
635
  this.combinedStream = audioMixer.getCombinedAudioStream();
594
- this.roomConnection.subscribeToRemoteParticipants(audioMixer.handleRemoteParticipants.bind(audioMixer));
595
- this.roomConnection.subscribeToConnectionStatus(this.handleConnectionStatusChange);
636
+ this.stateSubscriptions.push(this.roomConnection.subscribeToRemoteParticipants(audioMixer.handleRemoteParticipants.bind(audioMixer)));
596
637
  }
638
+ this.stateSubscriptions.push(this.roomConnection.subscribeToConnectionStatus(this.handleConnectionStatusChange));
639
+ this.stateSubscriptions.push(this.roomConnection.subscribeToRemoteParticipants(this.handleRemoteParticipantsTracksChange));
597
640
  }
598
641
  joinRoom(roomUrl) {
599
642
  return __awaiter(this, void 0, void 0, function* () {
@@ -710,7 +753,7 @@ const webhookRouter = (webhookTriggers, emitter) => {
710
753
  res.status(200);
711
754
  res.end();
712
755
  });
713
- router.post("/", jsonParser, (req, res) => {
756
+ router.post("/", jsonParser, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
714
757
  var _a, _b, _c, _d, _e, _f, _g, _h;
715
758
  assert(req.body, "message body is required");
716
759
  assert("type" in req.body, "webhook type is required");
@@ -718,19 +761,19 @@ const webhookRouter = (webhookTriggers, emitter) => {
718
761
  switch (req.body.type) {
719
762
  case "room.client.joined":
720
763
  shouldTriggerOnReceivedWebhook =
721
- (_b = (_a = webhookTriggers["room.client.joined"]) === null || _a === void 0 ? void 0 : _a.call(webhookTriggers, req.body)) !== null && _b !== void 0 ? _b : false;
764
+ (_b = (yield ((_a = webhookTriggers["room.client.joined"]) === null || _a === void 0 ? void 0 : _a.call(webhookTriggers, req.body)))) !== null && _b !== void 0 ? _b : false;
722
765
  break;
723
766
  case "room.client.left":
724
767
  shouldTriggerOnReceivedWebhook =
725
- (_d = (_c = webhookTriggers["room.client.left"]) === null || _c === void 0 ? void 0 : _c.call(webhookTriggers, req.body)) !== null && _d !== void 0 ? _d : false;
768
+ (_d = (yield ((_c = webhookTriggers["room.client.left"]) === null || _c === void 0 ? void 0 : _c.call(webhookTriggers, req.body)))) !== null && _d !== void 0 ? _d : false;
726
769
  break;
727
770
  case "room.session.started":
728
771
  shouldTriggerOnReceivedWebhook =
729
- (_f = (_e = webhookTriggers["room.session.started"]) === null || _e === void 0 ? void 0 : _e.call(webhookTriggers, req.body)) !== null && _f !== void 0 ? _f : false;
772
+ (_f = (yield ((_e = webhookTriggers["room.session.started"]) === null || _e === void 0 ? void 0 : _e.call(webhookTriggers, req.body)))) !== null && _f !== void 0 ? _f : false;
730
773
  break;
731
774
  case "room.session.ended":
732
775
  shouldTriggerOnReceivedWebhook =
733
- (_h = (_g = webhookTriggers["room.session.ended"]) === null || _g === void 0 ? void 0 : _g.call(webhookTriggers, req.body)) !== null && _h !== void 0 ? _h : false;
776
+ (_h = (yield ((_g = webhookTriggers["room.session.ended"]) === null || _g === void 0 ? void 0 : _g.call(webhookTriggers, req.body)))) !== null && _h !== void 0 ? _h : false;
734
777
  break;
735
778
  }
736
779
  if (shouldTriggerOnReceivedWebhook) {
@@ -739,7 +782,7 @@ const webhookRouter = (webhookTriggers, emitter) => {
739
782
  }
740
783
  res.status(200);
741
784
  res.end();
742
- });
785
+ }));
743
786
  return router;
744
787
  };
745
788
  class Trigger extends EventEmitter {
@@ -752,13 +795,10 @@ class Trigger extends EventEmitter {
752
795
  const app = express();
753
796
  const router = webhookRouter(this.webhookTriggers, this);
754
797
  app.use(router);
755
- const server = app.listen(this.port, () => {
756
- // console.log(`Bot trigger server now running on port[${this.port}]`);
757
- });
758
- process.on("SIGTERM", () => {
759
- server.close();
798
+ app.listen(this.port).on("error", (error) => {
799
+ console.error("Could not start Trigger web server", error);
760
800
  });
761
801
  }
762
802
  }
763
803
 
764
- export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, TRIGGER_EVENT_SUCCESS, Trigger };
804
+ export { ASSISTANT_JOINED_ROOM, ASSISTANT_LEFT_ROOM, AUDIO_STREAM_READY, Assistant, AudioSink, AudioSource, PARTICIPANT_AUDIO_TRACK_ADDED, PARTICIPANT_AUDIO_TRACK_REMOVED, PARTICIPANT_VIDEO_TRACK_ADDED, PARTICIPANT_VIDEO_TRACK_REMOVED, TRIGGER_EVENT_SUCCESS, Trigger };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@whereby.com/assistant-sdk",
3
3
  "description": "Assistant SDK for whereby.com",
4
4
  "author": "Whereby AS",
5
- "version": "0.0.0-canary-20250917154617",
5
+ "version": "0.0.0-canary-20250923134558",
6
6
  "license": "MIT",
7
7
  "files": [
8
8
  "dist",
@@ -51,10 +51,10 @@
51
51
  "prettier": "^3.5.3",
52
52
  "typescript": "^5.8.3",
53
53
  "@whereby.com/eslint-config": "0.1.0",
54
+ "@whereby.com/jest-config": "0.1.0",
54
55
  "@whereby.com/prettier-config": "0.1.0",
55
56
  "@whereby.com/rollup-config": "0.1.0",
56
- "@whereby.com/tsconfig": "0.1.0",
57
- "@whereby.com/jest-config": "0.1.0"
57
+ "@whereby.com/tsconfig": "0.1.0"
58
58
  },
59
59
  "dependencies": {
60
60
  "@roamhq/wrtc": "github:whereby/node-webrtc#patch/rtc_audio_source",
@@ -64,7 +64,7 @@
64
64
  "express": "5.1.0",
65
65
  "uuid": "^11.0.3",
66
66
  "ws": "^8.18.0",
67
- "@whereby.com/core": "0.0.0-canary-20250917154617"
67
+ "@whereby.com/core": "0.0.0-canary-20250923134558"
68
68
  },
69
69
  "prettier": "@whereby.com/prettier-config",
70
70
  "scripts": {