@voice-ai-labs/web-sdk 0.7.0 → 0.9.0

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.esm.js CHANGED
@@ -10702,7 +10702,7 @@ function getMatch(exp, ua) {
10702
10702
  }
10703
10703
  function getOSVersion(ua) {
10704
10704
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
10705
- }var version$1 = "2.17.2";const version = version$1;
10705
+ }var version$1 = "2.17.3";const version = version$1;
10706
10706
  const protocolVersion = 16;/** Base error that all LiveKit specific custom errors inherit from. */
10707
10707
  class LivekitError extends Error {
10708
10708
  constructor(code, message, options) {
@@ -10918,7 +10918,7 @@ var RoomEvent;
10918
10918
  RoomEvent["Reconnected"] = "reconnected";
10919
10919
  /**
10920
10920
  * When disconnected from room. This fires when room.disconnect() is called or
10921
- * when an unrecoverable connection issue had occured.
10921
+ * when an unrecoverable connection issue had occurred.
10922
10922
  *
10923
10923
  * DisconnectReason can be used to determine why the participant was disconnected. Notable reasons are
10924
10924
  * - DUPLICATE_IDENTITY: another client with the same identity has joined the room
@@ -12628,6 +12628,10 @@ function getEmptyAudioStreamTrack() {
12628
12628
  }
12629
12629
  return emptyAudioStreamTrack.clone();
12630
12630
  }
12631
+ /** An object that represents a serialized version of a `new Promise((resolve, reject) => {})`
12632
+ * constructor. Wait for a promise resolution with `await future.promise` and explicitly resolve or
12633
+ * reject the inner promise with `future.resolve(...)` or `future.reject(...)`.
12634
+ */
12631
12635
  class Future {
12632
12636
  get isResolved() {
12633
12637
  return this._isResolved;
@@ -12920,6 +12924,7 @@ class BaseKeyProvider extends eventsExports.EventEmitter {
12920
12924
  constructor() {
12921
12925
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12922
12926
  super();
12927
+ this.latestManuallySetKeyIndex = 0;
12923
12928
  /**
12924
12929
  * Callback being invoked after a key has been ratcheted.
12925
12930
  * Can happen when:
@@ -12956,11 +12961,17 @@ class BaseKeyProvider extends eventsExports.EventEmitter {
12956
12961
  throw new Error('participant identity needs to be passed for encryption key if sharedKey option is false');
12957
12962
  }
12958
12963
  this.keyInfoMap.set("".concat(participantIdentity !== null && participantIdentity !== void 0 ? participantIdentity : 'shared', "-").concat(keyIndex !== null && keyIndex !== void 0 ? keyIndex : 0), keyInfo);
12959
- this.emit(KeyProviderEvent.SetKey, keyInfo);
12964
+ if (keyIndex !== undefined) {
12965
+ this.latestManuallySetKeyIndex = keyIndex;
12966
+ }
12967
+ this.emit(KeyProviderEvent.SetKey, keyInfo, keyIndex !== undefined);
12960
12968
  }
12961
12969
  getKeys() {
12962
12970
  return Array.from(this.keyInfoMap.values());
12963
12971
  }
12972
+ getLatestManuallySetKeyIndex() {
12973
+ return this.latestManuallySetKeyIndex;
12974
+ }
12964
12975
  getOptions() {
12965
12976
  return this.options;
12966
12977
  }
@@ -13008,14 +13019,14 @@ class E2EEManager extends eventsExports.EventEmitter {
13008
13019
  case 'initAck':
13009
13020
  if (data.enabled) {
13010
13021
  this.keyProvider.getKeys().forEach(keyInfo => {
13011
- this.postKey(keyInfo);
13022
+ this.postKey(keyInfo, false);
13012
13023
  });
13013
13024
  }
13014
13025
  break;
13015
13026
  case 'enable':
13016
13027
  if (data.enabled) {
13017
13028
  this.keyProvider.getKeys().forEach(keyInfo => {
13018
- this.postKey(keyInfo);
13029
+ this.postKey(keyInfo, false);
13019
13030
  });
13020
13031
  }
13021
13032
  if (this.encryptionEnabled !== data.enabled && data.participantIdentity === ((_a = this.room) === null || _a === void 0 ? void 0 : _a.localParticipant.identity)) {
@@ -13140,8 +13151,10 @@ class E2EEManager extends eventsExports.EventEmitter {
13140
13151
  if (!this.room) {
13141
13152
  throw new TypeError("expected room to be present on signal connect");
13142
13153
  }
13154
+ const latestKeyIndex = keyProvider.getLatestManuallySetKeyIndex();
13143
13155
  keyProvider.getKeys().forEach(keyInfo => {
13144
- this.postKey(keyInfo);
13156
+ var _a;
13157
+ this.postKey(keyInfo, latestKeyIndex === ((_a = keyInfo.keyIndex) !== null && _a !== void 0 ? _a : 0));
13145
13158
  });
13146
13159
  this.setParticipantCryptorEnabled(this.room.localParticipant.isE2EEEnabled, this.room.localParticipant.identity);
13147
13160
  });
@@ -13163,7 +13176,7 @@ class E2EEManager extends eventsExports.EventEmitter {
13163
13176
  };
13164
13177
  this.worker.postMessage(msg);
13165
13178
  });
13166
- keyProvider.on(KeyProviderEvent.SetKey, keyInfo => this.postKey(keyInfo)).on(KeyProviderEvent.RatchetRequest, (participantId, keyIndex) => this.postRatchetRequest(participantId, keyIndex));
13179
+ keyProvider.on(KeyProviderEvent.SetKey, (keyInfo, updateCurrentKeyIndex) => this.postKey(keyInfo, updateCurrentKeyIndex !== null && updateCurrentKeyIndex !== void 0 ? updateCurrentKeyIndex : true)).on(KeyProviderEvent.RatchetRequest, (participantId, keyIndex) => this.postRatchetRequest(participantId, keyIndex));
13167
13180
  }
13168
13181
  encryptData(data) {
13169
13182
  return __awaiter(this, void 0, void 0, function* () {
@@ -13224,7 +13237,7 @@ class E2EEManager extends eventsExports.EventEmitter {
13224
13237
  };
13225
13238
  this.worker.postMessage(msg);
13226
13239
  }
13227
- postKey(_ref) {
13240
+ postKey(_ref, updateCurrentKeyIndex) {
13228
13241
  let {
13229
13242
  key,
13230
13243
  participantIdentity,
@@ -13240,7 +13253,8 @@ class E2EEManager extends eventsExports.EventEmitter {
13240
13253
  participantIdentity: participantIdentity,
13241
13254
  isPublisher: participantIdentity === ((_a = this.room) === null || _a === void 0 ? void 0 : _a.localParticipant.identity),
13242
13255
  key,
13243
- keyIndex
13256
+ keyIndex,
13257
+ updateCurrentKeyIndex
13244
13258
  }
13245
13259
  };
13246
13260
  this.worker.postMessage(msg);
@@ -15356,48 +15370,106 @@ function requireLib() {
15356
15370
  lib.parseImageAttributes = parser.parseImageAttributes;
15357
15371
  lib.parseSimulcastStreamList = parser.parseSimulcastStreamList;
15358
15372
  return lib;
15359
- }var libExports = requireLib();function r(r, e, n) {
15360
- var i, t, o;
15361
- void 0 === e && (e = 50), void 0 === n && (n = {});
15362
- var a = null != (i = n.isImmediate) && i,
15363
- u = null != (t = n.callback) && t,
15364
- c = n.maxWait,
15365
- v = Date.now(),
15366
- l = [];
15367
- function f() {
15368
- if (void 0 !== c) {
15369
- var r = Date.now() - v;
15370
- if (r + e >= c) return c - r;
15371
- }
15372
- return e;
15373
- }
15374
- var d = function () {
15375
- var e = [].slice.call(arguments),
15376
- n = this;
15377
- return new Promise(function (i, t) {
15378
- var c = a && void 0 === o;
15379
- if (void 0 !== o && clearTimeout(o), o = setTimeout(function () {
15380
- if (o = void 0, v = Date.now(), !a) {
15381
- var i = r.apply(n, e);
15382
- u && u(i), l.forEach(function (r) {
15383
- return (0, r.resolve)(i);
15384
- }), l = [];
15385
- }
15386
- }, f()), c) {
15387
- var d = r.apply(n, e);
15388
- return u && u(d), i(d);
15389
- }
15390
- l.push({
15391
- resolve: i,
15392
- reject: t
15373
+ }var libExports = requireLib();/**
15374
+ * Originally from ts-debounce (https://github.com/chodorowicz/ts-debounce)
15375
+ * with the following license:
15376
+ *
15377
+ * MIT License
15378
+ *
15379
+ * Copyright (c) 2017 Jakub Chodorowicz
15380
+ *
15381
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
15382
+ * of this software and associated documentation files (the "Software"), to deal
15383
+ * in the Software without restriction, including without limitation the rights
15384
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15385
+ * copies of the Software, and to permit persons to whom the Software is
15386
+ * furnished to do so, subject to the following conditions:
15387
+ *
15388
+ * The above copyright notice and this permission notice shall be included in all
15389
+ * copies or substantial portions of the Software.
15390
+ *
15391
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15392
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15393
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15394
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15395
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15396
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15397
+ * SOFTWARE.
15398
+ *
15399
+ * Modified to use CriticalTimers for reliable timer execution.
15400
+ */
15401
+ /* eslint-disable @typescript-eslint/no-this-alias, @typescript-eslint/no-unused-expressions, @typescript-eslint/no-shadow */
15402
+ function debounce(func) {
15403
+ let waitMilliseconds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50;
15404
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
15405
+ var _a, _b;
15406
+ let timeoutId;
15407
+ const isImmediate = (_a = options.isImmediate) !== null && _a !== void 0 ? _a : false;
15408
+ const callback = (_b = options.callback) !== null && _b !== void 0 ? _b : false;
15409
+ const maxWait = options.maxWait;
15410
+ let lastInvokeTime = Date.now();
15411
+ let promises = [];
15412
+ function nextInvokeTimeout() {
15413
+ if (maxWait !== undefined) {
15414
+ const timeSinceLastInvocation = Date.now() - lastInvokeTime;
15415
+ if (timeSinceLastInvocation + waitMilliseconds >= maxWait) {
15416
+ return maxWait - timeSinceLastInvocation;
15417
+ }
15418
+ }
15419
+ return waitMilliseconds;
15420
+ }
15421
+ const debouncedFunction = function () {
15422
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
15423
+ args[_key] = arguments[_key];
15424
+ }
15425
+ const context = this;
15426
+ return new Promise((resolve, reject) => {
15427
+ const invokeFunction = function () {
15428
+ timeoutId = undefined;
15429
+ lastInvokeTime = Date.now();
15430
+ if (!isImmediate) {
15431
+ const result = func.apply(context, args);
15432
+ callback && callback(result);
15433
+ // biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code
15434
+ promises.forEach(_ref => {
15435
+ let {
15436
+ resolve
15437
+ } = _ref;
15438
+ return resolve(result);
15439
+ });
15440
+ promises = [];
15441
+ }
15442
+ };
15443
+ const shouldCallNow = isImmediate && timeoutId === undefined;
15444
+ if (timeoutId !== undefined) {
15445
+ CriticalTimers.clearTimeout(timeoutId);
15446
+ }
15447
+ timeoutId = CriticalTimers.setTimeout(invokeFunction, nextInvokeTimeout());
15448
+ if (shouldCallNow) {
15449
+ const result = func.apply(context, args);
15450
+ callback && callback(result);
15451
+ return resolve(result);
15452
+ }
15453
+ promises.push({
15454
+ resolve,
15455
+ reject
15393
15456
  });
15394
15457
  });
15395
15458
  };
15396
- return d.cancel = function (r) {
15397
- void 0 !== o && clearTimeout(o), l.forEach(function (e) {
15398
- return (0, e.reject)(r);
15399
- }), l = [];
15400
- }, d;
15459
+ debouncedFunction.cancel = function (reason) {
15460
+ if (timeoutId !== undefined) {
15461
+ CriticalTimers.clearTimeout(timeoutId);
15462
+ }
15463
+ // biome-ignore lint/suspicious/useIterableCallbackReturn: vendored code
15464
+ promises.forEach(_ref2 => {
15465
+ let {
15466
+ reject
15467
+ } = _ref2;
15468
+ return reject(reason);
15469
+ });
15470
+ promises = [];
15471
+ };
15472
+ return debouncedFunction;
15401
15473
  }/* The svc codec (av1/vp9) would use a very low bitrate at the begining and
15402
15474
  increase slowly by the bandwidth estimator until it reach the target bitrate. The
15403
15475
  process commonly cost more than 10 seconds cause subscriber will get blur video at
@@ -15433,7 +15505,7 @@ class PCTransport extends eventsExports.EventEmitter {
15433
15505
  this.remoteStereoMids = [];
15434
15506
  this.remoteNackMids = [];
15435
15507
  // debounced negotiate interface
15436
- this.negotiate = r(onError => __awaiter(this, void 0, void 0, function* () {
15508
+ this.negotiate = debounce(onError => __awaiter(this, void 0, void 0, function* () {
15437
15509
  this.emit(PCEvents.NegotiationStarted);
15438
15510
  try {
15439
15511
  yield this.createAndSendOffer();
@@ -16760,7 +16832,7 @@ class LocalTrack extends Track {
16760
16832
  this.pendingDeviceChange = false;
16761
16833
  this._isUpstreamPaused = false;
16762
16834
  this.handleTrackMuteEvent = () => this.debouncedTrackMuteHandler().catch(() => this.log.debug('track mute bounce got cancelled by an unmute event', this.logContext));
16763
- this.debouncedTrackMuteHandler = r(() => __awaiter(this, void 0, void 0, function* () {
16835
+ this.debouncedTrackMuteHandler = debounce(() => __awaiter(this, void 0, void 0, function* () {
16764
16836
  yield this.pauseUpstream();
16765
16837
  }), 5000);
16766
16838
  this.handleTrackUnmuteEvent = () => __awaiter(this, void 0, void 0, function* () {
@@ -16832,7 +16904,7 @@ class LocalTrack extends Track {
16832
16904
  getSourceTrackSettings() {
16833
16905
  return this._mediaStreamTrack.getSettings();
16834
16906
  }
16835
- setMediaStreamTrack(newTrack, force) {
16907
+ setMediaStreamTrack(newTrack, force, isUnmuting) {
16836
16908
  return __awaiter(this, void 0, void 0, function* () {
16837
16909
  var _a;
16838
16910
  if (newTrack === this._mediaStreamTrack && !force) {
@@ -16889,7 +16961,8 @@ class LocalTrack extends Track {
16889
16961
  this._mediaStreamTrack = newTrack;
16890
16962
  if (newTrack) {
16891
16963
  // sync muted state with the enabled state of the newly provided track
16892
- this._mediaStreamTrack.enabled = !this.isMuted;
16964
+ // if restarting as part of an unmute, set enabled to true directly to avoid mute cycling
16965
+ this._mediaStreamTrack.enabled = isUnmuting ? true : !this.isMuted;
16893
16966
  // when a valid track is replace, we'd want to start producing
16894
16967
  yield this.resumeUpstream();
16895
16968
  this.attachedElements.forEach(el => {
@@ -17002,7 +17075,7 @@ class LocalTrack extends Track {
17002
17075
  }
17003
17076
  });
17004
17077
  }
17005
- restart(constraints) {
17078
+ restart(constraints, isUnmuting) {
17006
17079
  return __awaiter(this, void 0, void 0, function* () {
17007
17080
  this.manuallyStopped = false;
17008
17081
  const unlock = yield this.trackChangeLock.lock();
@@ -17051,7 +17124,7 @@ class LocalTrack extends Track {
17051
17124
  }
17052
17125
  newTrack.addEventListener('ended', this.handleEnded);
17053
17126
  this.log.debug('re-acquired MediaStreamTrack', this.logContext);
17054
- yield this.setMediaStreamTrack(newTrack);
17127
+ yield this.setMediaStreamTrack(newTrack, false, isUnmuting);
17055
17128
  this._constraints = constraints;
17056
17129
  this.pendingDeviceChange = false;
17057
17130
  this.emit(TrackEvent.Restarted, this);
@@ -17434,7 +17507,7 @@ class LocalTrack extends Track {
17434
17507
  }
17435
17508
  if (this.source === Track.Source.Microphone && (this.stopOnMute || this._mediaStreamTrack.readyState === 'ended' || this.pendingDeviceChange) && !this.isUserProvided) {
17436
17509
  this.log.debug('reacquiring mic track', this.logContext);
17437
- yield this.restartTrack();
17510
+ yield this.restart(undefined, true);
17438
17511
  }
17439
17512
  yield _super.unmute.call(this);
17440
17513
  return this;
@@ -17457,14 +17530,14 @@ class LocalTrack extends Track {
17457
17530
  yield this.restart(constraints);
17458
17531
  });
17459
17532
  }
17460
- restart(constraints) {
17533
+ restart(constraints, isUnmuting) {
17461
17534
  const _super = Object.create(null, {
17462
17535
  restart: {
17463
17536
  get: () => super.restart
17464
17537
  }
17465
17538
  });
17466
17539
  return __awaiter(this, void 0, void 0, function* () {
17467
- const track = yield _super.restart.call(this, constraints);
17540
+ const track = yield _super.restart.call(this, constraints, isUnmuting);
17468
17541
  this.checkForSilence();
17469
17542
  return track;
17470
17543
  });
@@ -18097,7 +18170,7 @@ class LocalVideoTrack extends LocalTrack {
18097
18170
  }
18098
18171
  if (this.source === Track.Source.Camera && !this.isUserProvided) {
18099
18172
  this.log.debug('reacquiring camera track', this.logContext);
18100
- yield this.restartTrack();
18173
+ yield this.restart(undefined, true);
18101
18174
  }
18102
18175
  yield _super.unmute.call(this);
18103
18176
  return this;
@@ -19989,12 +20062,11 @@ function applyUserDataCompat(newObj, oldObj) {
19989
20062
  throw new DataStreamError("Extra chunk(s) received - expected ".concat(this.totalByteSize, " bytes of data total, received ").concat(this.bytesReceived, " bytes"), DataStreamErrorReason.LengthExceeded);
19990
20063
  }
19991
20064
  }
19992
- constructor(info, stream, totalByteSize, outOfBandFailureRejectingFuture) {
20065
+ constructor(info, stream, totalByteSize) {
19993
20066
  this.reader = stream;
19994
20067
  this.totalByteSize = totalByteSize;
19995
20068
  this._info = info;
19996
20069
  this.bytesReceived = 0;
19997
- this.outOfBandFailureRejectingFuture = outOfBandFailureRejectingFuture;
19998
20070
  }
19999
20071
  }
20000
20072
  class ByteStreamReader extends BaseStreamReader {
@@ -20007,50 +20079,44 @@ class ByteStreamReader extends BaseStreamReader {
20007
20079
  }
20008
20080
  [Symbol.asyncIterator]() {
20009
20081
  const reader = this.reader.getReader();
20010
- let rejectingSignalFuture = new Future();
20011
- let activeSignal = null;
20012
- let onAbort = null;
20013
- if (this.signal) {
20014
- const signal = this.signal;
20015
- onAbort = () => {
20016
- var _a;
20017
- (_a = rejectingSignalFuture.reject) === null || _a === void 0 ? void 0 : _a.call(rejectingSignalFuture, signal.reason);
20018
- };
20019
- signal.addEventListener('abort', onAbort);
20020
- activeSignal = signal;
20021
- }
20082
+ // Suppress unhandled rejection on reader.closed — errors are
20083
+ // already propagated through reader.read() to the consumer.
20084
+ reader.closed.catch(() => {});
20022
20085
  const cleanup = () => {
20023
20086
  reader.releaseLock();
20024
- if (activeSignal && onAbort) {
20025
- activeSignal.removeEventListener('abort', onAbort);
20026
- }
20027
20087
  this.signal = undefined;
20028
20088
  };
20029
20089
  return {
20030
20090
  next: () => __awaiter(this, void 0, void 0, function* () {
20031
- var _a, _b;
20032
20091
  try {
20033
- const {
20034
- done,
20035
- value
20036
- } = yield Promise.race([reader.read(),
20037
- // Rejects if this.signal is aborted
20038
- rejectingSignalFuture.promise,
20039
- // Rejects if something external says it should, like a participant disconnecting, etc
20040
- (_b = (_a = this.outOfBandFailureRejectingFuture) === null || _a === void 0 ? void 0 : _a.promise) !== null && _b !== void 0 ? _b : new Promise(() => {
20041
- /* never resolves */
20042
- })]);
20043
- if (done) {
20092
+ const signal = this.signal;
20093
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
20094
+ throw signal.reason;
20095
+ }
20096
+ const result = yield new Promise((resolve, reject) => {
20097
+ if (signal) {
20098
+ const onAbort = () => reject(signal.reason);
20099
+ signal.addEventListener('abort', onAbort, {
20100
+ once: true
20101
+ });
20102
+ reader.read().then(resolve, reject).finally(() => {
20103
+ signal.removeEventListener('abort', onAbort);
20104
+ });
20105
+ } else {
20106
+ reader.read().then(resolve, reject);
20107
+ }
20108
+ });
20109
+ if (result.done) {
20044
20110
  this.validateBytesReceived(true);
20045
20111
  return {
20046
20112
  done: true,
20047
20113
  value: undefined
20048
20114
  };
20049
20115
  } else {
20050
- this.handleChunkReceived(value);
20116
+ this.handleChunkReceived(result.value);
20051
20117
  return {
20052
20118
  done: false,
20053
- value: value.content
20119
+ value: result.value.content
20054
20120
  };
20055
20121
  }
20056
20122
  } catch (err) {
@@ -20121,8 +20187,8 @@ class TextStreamReader extends BaseStreamReader {
20121
20187
  * A TextStreamReader instance can be used as an AsyncIterator that returns the entire string
20122
20188
  * that has been received up to the current point in time.
20123
20189
  */
20124
- constructor(info, stream, totalChunkCount, outOfBandFailureRejectingFuture) {
20125
- super(info, stream, totalChunkCount, outOfBandFailureRejectingFuture);
20190
+ constructor(info, stream, totalChunkCount) {
20191
+ super(info, stream, totalChunkCount);
20126
20192
  this.receivedChunks = new Map();
20127
20193
  }
20128
20194
  handleChunkReceived(chunk) {
@@ -20146,55 +20212,49 @@ class TextStreamReader extends BaseStreamReader {
20146
20212
  */
20147
20213
  [Symbol.asyncIterator]() {
20148
20214
  const reader = this.reader.getReader();
20215
+ // Suppress unhandled rejection on reader.closed — errors are
20216
+ // already propagated through reader.read() to the consumer.
20217
+ reader.closed.catch(() => {});
20149
20218
  const decoder = new TextDecoder('utf-8', {
20150
20219
  fatal: true
20151
20220
  });
20152
- let rejectingSignalFuture = new Future();
20153
- let activeSignal = null;
20154
- let onAbort = null;
20155
- if (this.signal) {
20156
- const signal = this.signal;
20157
- onAbort = () => {
20158
- var _a;
20159
- (_a = rejectingSignalFuture.reject) === null || _a === void 0 ? void 0 : _a.call(rejectingSignalFuture, signal.reason);
20160
- };
20161
- signal.addEventListener('abort', onAbort);
20162
- activeSignal = signal;
20163
- }
20221
+ const signal = this.signal;
20164
20222
  const cleanup = () => {
20165
20223
  reader.releaseLock();
20166
- if (activeSignal && onAbort) {
20167
- activeSignal.removeEventListener('abort', onAbort);
20168
- }
20169
20224
  this.signal = undefined;
20170
20225
  };
20171
20226
  return {
20172
20227
  next: () => __awaiter(this, void 0, void 0, function* () {
20173
- var _a, _b;
20174
20228
  try {
20175
- const {
20176
- done,
20177
- value
20178
- } = yield Promise.race([reader.read(),
20179
- // Rejects if this.signal is aborted
20180
- rejectingSignalFuture.promise,
20181
- // Rejects if something external says it should, like a participant disconnecting, etc
20182
- (_b = (_a = this.outOfBandFailureRejectingFuture) === null || _a === void 0 ? void 0 : _a.promise) !== null && _b !== void 0 ? _b : new Promise(() => {
20183
- /* never resolves */
20184
- })]);
20185
- if (done) {
20229
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
20230
+ throw signal.reason;
20231
+ }
20232
+ const result = yield new Promise((resolve, reject) => {
20233
+ if (signal) {
20234
+ const onAbort = () => reject(signal.reason);
20235
+ signal.addEventListener('abort', onAbort, {
20236
+ once: true
20237
+ });
20238
+ reader.read().then(resolve, reject).finally(() => {
20239
+ signal.removeEventListener('abort', onAbort);
20240
+ });
20241
+ } else {
20242
+ reader.read().then(resolve, reject);
20243
+ }
20244
+ });
20245
+ if (result.done) {
20186
20246
  this.validateBytesReceived(true);
20187
20247
  return {
20188
20248
  done: true,
20189
20249
  value: undefined
20190
20250
  };
20191
20251
  } else {
20192
- this.handleChunkReceived(value);
20252
+ this.handleChunkReceived(result.value);
20193
20253
  let decodedResult;
20194
20254
  try {
20195
- decodedResult = decoder.decode(value.content);
20255
+ decodedResult = decoder.decode(result.value.content);
20196
20256
  } catch (err) {
20197
- throw new DataStreamError("Cannot decode datastream chunk ".concat(value.chunkIndex, " as text: ").concat(err), DataStreamErrorReason.DecodeFailed);
20257
+ throw new DataStreamError("Cannot decode datastream chunk ".concat(result.value.chunkIndex, " as text: ").concat(err), DataStreamErrorReason.DecodeFailed);
20198
20258
  }
20199
20259
  return {
20200
20260
  done: false,
@@ -20291,18 +20351,17 @@ class TextStreamReader extends BaseStreamReader {
20291
20351
  this.textStreamControllers.clear();
20292
20352
  }
20293
20353
  validateParticipantHasNoActiveDataStreams(participantIdentity) {
20294
- var _a, _b, _c, _d;
20295
20354
  // Terminate any in flight data stream receives from the given participant
20296
20355
  const textStreamsBeingSentByDisconnectingParticipant = Array.from(this.textStreamControllers.entries()).filter(entry => entry[1].sendingParticipantIdentity === participantIdentity);
20297
20356
  const byteStreamsBeingSentByDisconnectingParticipant = Array.from(this.byteStreamControllers.entries()).filter(entry => entry[1].sendingParticipantIdentity === participantIdentity);
20298
20357
  if (textStreamsBeingSentByDisconnectingParticipant.length > 0 || byteStreamsBeingSentByDisconnectingParticipant.length > 0) {
20299
20358
  const abnormalEndError = new DataStreamError("Participant ".concat(participantIdentity, " unexpectedly disconnected in the middle of sending data"), DataStreamErrorReason.AbnormalEnd);
20300
20359
  for (const [id, controller] of byteStreamsBeingSentByDisconnectingParticipant) {
20301
- (_b = (_a = controller.outOfBandFailureRejectingFuture).reject) === null || _b === void 0 ? void 0 : _b.call(_a, abnormalEndError);
20360
+ controller.controller.error(abnormalEndError);
20302
20361
  this.byteStreamControllers.delete(id);
20303
20362
  }
20304
20363
  for (const [id, controller] of textStreamsBeingSentByDisconnectingParticipant) {
20305
- (_d = (_c = controller.outOfBandFailureRejectingFuture).reject) === null || _d === void 0 ? void 0 : _d.call(_c, abnormalEndError);
20364
+ controller.controller.error(abnormalEndError);
20306
20365
  this.textStreamControllers.delete(id);
20307
20366
  }
20308
20367
  }
@@ -20331,10 +20390,6 @@ class TextStreamReader extends BaseStreamReader {
20331
20390
  return;
20332
20391
  }
20333
20392
  let streamController;
20334
- const outOfBandFailureRejectingFuture = new Future();
20335
- outOfBandFailureRejectingFuture.promise.catch(err => {
20336
- this.log.error(err);
20337
- });
20338
20393
  const info = {
20339
20394
  id: streamHeader.streamId,
20340
20395
  name: (_a = streamHeader.contentHeader.value.name) !== null && _a !== void 0 ? _a : 'unknown',
@@ -20355,12 +20410,11 @@ class TextStreamReader extends BaseStreamReader {
20355
20410
  info,
20356
20411
  controller: streamController,
20357
20412
  startTime: Date.now(),
20358
- sendingParticipantIdentity: participantIdentity,
20359
- outOfBandFailureRejectingFuture
20413
+ sendingParticipantIdentity: participantIdentity
20360
20414
  });
20361
20415
  }
20362
20416
  });
20363
- streamHandlerCallback(new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength), outOfBandFailureRejectingFuture), {
20417
+ streamHandlerCallback(new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)), {
20364
20418
  identity: participantIdentity
20365
20419
  });
20366
20420
  } else if (streamHeader.contentHeader.case === 'textHeader') {
@@ -20370,10 +20424,6 @@ class TextStreamReader extends BaseStreamReader {
20370
20424
  return;
20371
20425
  }
20372
20426
  let streamController;
20373
- const outOfBandFailureRejectingFuture = new Future();
20374
- outOfBandFailureRejectingFuture.promise.catch(err => {
20375
- this.log.error(err);
20376
- });
20377
20427
  const info = {
20378
20428
  id: streamHeader.streamId,
20379
20429
  mimeType: streamHeader.mimeType,
@@ -20394,12 +20444,11 @@ class TextStreamReader extends BaseStreamReader {
20394
20444
  info,
20395
20445
  controller: streamController,
20396
20446
  startTime: Date.now(),
20397
- sendingParticipantIdentity: participantIdentity,
20398
- outOfBandFailureRejectingFuture
20447
+ sendingParticipantIdentity: participantIdentity
20399
20448
  });
20400
20449
  }
20401
20450
  });
20402
- streamHandlerCallback(new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength), outOfBandFailureRejectingFuture), {
20451
+ streamHandlerCallback(new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)), {
20403
20452
  identity: participantIdentity
20404
20453
  });
20405
20454
  }
@@ -21079,7 +21128,7 @@ class RemoteVideoTrack extends RemoteTrack {
21079
21128
  }
21080
21129
  this.prevStats = stats;
21081
21130
  });
21082
- this.debouncedHandleResize = r(() => {
21131
+ this.debouncedHandleResize = debounce(() => {
21083
21132
  this.updateDimensions();
21084
21133
  }, REACTION_DELAY);
21085
21134
  this.adaptiveStreamSettings = adaptiveStreamSettings;
@@ -22066,6 +22115,8 @@ class Participant extends eventsExports.EventEmitter {
22066
22115
  this.handleClosing = () => {
22067
22116
  var _a, _b, _c, _d, _e, _f;
22068
22117
  if (this.reconnectFuture) {
22118
+ // @throws-transformer ignore - introduced due to adding Throws into Future, investigate this
22119
+ // further
22069
22120
  this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
22070
22121
  (_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.reject) === null || _b === void 0 ? void 0 : _b.call(_a, new Error('Got disconnected during reconnection attempt'));
22071
22122
  this.reconnectFuture = undefined;
@@ -24423,7 +24474,7 @@ class Room extends eventsExports.EventEmitter {
24423
24474
  }
24424
24475
  if (nextUrl && !((_b = this.abortController) === null || _b === void 0 ? void 0 : _b.signal.aborted)) {
24425
24476
  this.log.info("Initial connection failed with ConnectionError: ".concat(error.message, ". Retrying with another region: ").concat(nextUrl), this.logContext);
24426
- this.recreateEngine();
24477
+ this.recreateEngine(true);
24427
24478
  yield connectFn(resolve, reject, nextUrl);
24428
24479
  } else {
24429
24480
  this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, getDisconnectReasonFromConnectionError(error));
@@ -24508,7 +24559,7 @@ class Room extends eventsExports.EventEmitter {
24508
24559
  if (this.state === ConnectionState.Reconnecting || this.isResuming || ((_a = this.engine) === null || _a === void 0 ? void 0 : _a.pendingReconnect)) {
24509
24560
  this.log.info('Reconnection attempt replaced by new connection attempt', this.logContext);
24510
24561
  // make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
24511
- this.recreateEngine();
24562
+ this.recreateEngine(true);
24512
24563
  } else {
24513
24564
  // create engine if previously disconnected
24514
24565
  this.maybeCreateEngine();
@@ -25621,9 +25672,13 @@ class Room extends eventsExports.EventEmitter {
25621
25672
  setupLocalParticipantEvents() {
25622
25673
  this.localParticipant.on(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).on(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).on(ParticipantEvent.AttributesChanged, this.onLocalAttributesChanged).on(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).on(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).on(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).on(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).on(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).on(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).on(ParticipantEvent.AudioStreamAcquired, this.startAudio).on(ParticipantEvent.ChatMessage, this.onLocalChatMessageSent).on(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
25623
25674
  }
25624
- recreateEngine() {
25625
- var _a;
25626
- (_a = this.engine) === null || _a === void 0 ? void 0 : _a.close();
25675
+ recreateEngine(sendLeave) {
25676
+ const oldEngine = this.engine;
25677
+ if (sendLeave && oldEngine && !oldEngine.client.isDisconnected) {
25678
+ oldEngine.client.sendLeave().finally(() => oldEngine.close());
25679
+ } else {
25680
+ oldEngine === null || oldEngine === void 0 ? void 0 : oldEngine.close();
25681
+ }
25627
25682
  /* @ts-ignore */
25628
25683
  this.engine = undefined;
25629
25684
  this.isResuming = false;
@@ -26289,7 +26344,39 @@ var DataTrackDepacketizerDropReason;
26289
26344
  DataTrackDepacketizerDropReason[DataTrackDepacketizerDropReason["UnknownFrame"] = 1] = "UnknownFrame";
26290
26345
  DataTrackDepacketizerDropReason[DataTrackDepacketizerDropReason["BufferFull"] = 2] = "BufferFull";
26291
26346
  DataTrackDepacketizerDropReason[DataTrackDepacketizerDropReason["Incomplete"] = 3] = "Incomplete";
26292
- })(DataTrackDepacketizerDropReason || (DataTrackDepacketizerDropReason = {}));var CheckStatus;
26347
+ })(DataTrackDepacketizerDropReason || (DataTrackDepacketizerDropReason = {}));var DataTrackPublishErrorReason;
26348
+ (function (DataTrackPublishErrorReason) {
26349
+ /**
26350
+ * Local participant does not have permission to publish data tracks.
26351
+ *
26352
+ * Ensure the participant's token contains the `canPublishData` grant.
26353
+ */
26354
+ DataTrackPublishErrorReason[DataTrackPublishErrorReason["NotAllowed"] = 0] = "NotAllowed";
26355
+ /** A track with the same name is already published by the local participant. */
26356
+ DataTrackPublishErrorReason[DataTrackPublishErrorReason["DuplicateName"] = 1] = "DuplicateName";
26357
+ /** Request to publish the track took long to complete. */
26358
+ DataTrackPublishErrorReason[DataTrackPublishErrorReason["Timeout"] = 2] = "Timeout";
26359
+ /** No additional data tracks can be published by the local participant. */
26360
+ DataTrackPublishErrorReason[DataTrackPublishErrorReason["LimitReached"] = 3] = "LimitReached";
26361
+ /** Cannot publish data track when the room is disconnected. */
26362
+ DataTrackPublishErrorReason[DataTrackPublishErrorReason["Disconnected"] = 4] = "Disconnected";
26363
+ // NOTE: this was introduced by web / there isn't a corresponding case in the rust version.
26364
+ DataTrackPublishErrorReason[DataTrackPublishErrorReason["Cancelled"] = 5] = "Cancelled";
26365
+ })(DataTrackPublishErrorReason || (DataTrackPublishErrorReason = {}));
26366
+ var DataTrackPushFrameErrorReason;
26367
+ (function (DataTrackPushFrameErrorReason) {
26368
+ /** Track is no longer published. */
26369
+ DataTrackPushFrameErrorReason[DataTrackPushFrameErrorReason["TrackUnpublished"] = 0] = "TrackUnpublished";
26370
+ /** Frame was dropped. */
26371
+ // NOTE: this should become a web specific error, the rust version of this "dropped" error means
26372
+ // something different and will be renamed to "QueueFull".
26373
+ DataTrackPushFrameErrorReason[DataTrackPushFrameErrorReason["Dropped"] = 1] = "Dropped";
26374
+ })(DataTrackPushFrameErrorReason || (DataTrackPushFrameErrorReason = {}));
26375
+ var DataTrackOutgoingPipelineErrorReason;
26376
+ (function (DataTrackOutgoingPipelineErrorReason) {
26377
+ DataTrackOutgoingPipelineErrorReason[DataTrackOutgoingPipelineErrorReason["Packetizer"] = 0] = "Packetizer";
26378
+ DataTrackOutgoingPipelineErrorReason[DataTrackOutgoingPipelineErrorReason["Encryption"] = 1] = "Encryption";
26379
+ })(DataTrackOutgoingPipelineErrorReason || (DataTrackOutgoingPipelineErrorReason = {}));getLogger(LoggerNames.DataTracks);var CheckStatus;
26293
26380
  (function (CheckStatus) {
26294
26381
  CheckStatus[CheckStatus["IDLE"] = 0] = "IDLE";
26295
26382
  CheckStatus[CheckStatus["RUNNING"] = 1] = "RUNNING";
@@ -27316,6 +27403,39 @@ class BaseClient {
27316
27403
  });
27317
27404
  return this.handleResponse(response);
27318
27405
  }
27406
+ /**
27407
+ * Perform GET request that returns binary data (Blob)
27408
+ */
27409
+ async getForBlob(path, params) {
27410
+ const url = new URL(`${this.apiUrl}${path}`);
27411
+ if (params) {
27412
+ Object.entries(params).forEach(([key, value]) => {
27413
+ if (value !== undefined && value !== null) {
27414
+ if (Array.isArray(value)) {
27415
+ value.forEach(v => url.searchParams.append(key, String(v)));
27416
+ }
27417
+ else {
27418
+ url.searchParams.append(key, String(value));
27419
+ }
27420
+ }
27421
+ });
27422
+ }
27423
+ const response = await fetch(url.toString(), {
27424
+ method: 'GET',
27425
+ headers: this.getHeaders(),
27426
+ });
27427
+ if (!response.ok) {
27428
+ let errorData = null;
27429
+ try {
27430
+ errorData = await response.json();
27431
+ }
27432
+ catch {
27433
+ // Not JSON
27434
+ }
27435
+ throw new VoiceAIError(errorData?.error || errorData?.detail || `Request failed with status ${response.status}`, response.status, errorData?.code);
27436
+ }
27437
+ return response.blob();
27438
+ }
27319
27439
  /**
27320
27440
  * Perform POST request
27321
27441
  */
@@ -27621,6 +27741,14 @@ class AgentClient extends BaseClient {
27621
27741
  async getStatus(agentId) {
27622
27742
  return super.get(`/connection/agent-status/${encodeURIComponent(agentId)}`);
27623
27743
  }
27744
+ /**
27745
+ * Create an outbound call for an agent.
27746
+ *
27747
+ * Requires backend-side credentials (service token or dev API key).
27748
+ */
27749
+ async createOutboundCall(options) {
27750
+ return this.post('/calls/outbound', options);
27751
+ }
27624
27752
  /**
27625
27753
  * Assign a knowledge base to an agent for RAG
27626
27754
  *
@@ -27714,19 +27842,19 @@ class AnalyticsClient extends BaseClient {
27714
27842
  /**
27715
27843
  * Get transcript download URL for a call
27716
27844
  *
27717
- * @param summaryId - The call summary ID
27845
+ * @param callId - The call identifier
27718
27846
  * @returns Object with transcript URL
27719
27847
  *
27720
27848
  * @example
27721
27849
  * ```typescript
27722
- * const { url } = await client.analytics.getTranscriptUrl(12345);
27850
+ * const { url } = await client.analytics.getTranscriptUrl("call_12345");
27723
27851
  *
27724
27852
  * // Download transcript
27725
27853
  * window.open(url, '_blank');
27726
27854
  * ```
27727
27855
  */
27728
- async getTranscriptUrl(summaryId) {
27729
- return this.get(`/agent/call-history/${summaryId}/transcript`);
27856
+ async getTranscriptUrl(callId) {
27857
+ return this.get(`/agent/call-history/${callId}/transcript`);
27730
27858
  }
27731
27859
  /**
27732
27860
  * Get agent stats summary
@@ -28270,6 +28398,45 @@ class TTSClient extends BaseClient {
28270
28398
  async deleteVoice(voiceId) {
28271
28399
  return this.httpDelete(`/tts/voice/${encodeURIComponent(voiceId)}`);
28272
28400
  }
28401
+ // ==========================================================================
28402
+ // PRONUNCIATION DICTIONARY MANAGEMENT
28403
+ // ==========================================================================
28404
+ async listPronunciationDictionaries() {
28405
+ return this.get('/tts/pronunciation-dictionaries');
28406
+ }
28407
+ async getPronunciationDictionary(dictionaryId) {
28408
+ return this.get(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}`);
28409
+ }
28410
+ async createPronunciationDictionaryFromFile(options) {
28411
+ const formData = new FormData();
28412
+ formData.append('file', options.file);
28413
+ formData.append('name', options.name);
28414
+ if (options.language !== undefined) {
28415
+ formData.append('language', options.language);
28416
+ }
28417
+ return this.postFormData('/tts/pronunciation-dictionaries/add-from-file', formData);
28418
+ }
28419
+ async createPronunciationDictionaryFromRules(options) {
28420
+ return this.post('/tts/pronunciation-dictionaries/add-from-rules', options);
28421
+ }
28422
+ async updatePronunciationDictionary(dictionaryId, options) {
28423
+ return this.patch(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}`, options);
28424
+ }
28425
+ async deletePronunciationDictionary(dictionaryId) {
28426
+ return this.httpDelete(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}`);
28427
+ }
28428
+ async downloadPronunciationDictionaryVersion(dictionaryId, version) {
28429
+ return this.getForBlob(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}/${version}/download`);
28430
+ }
28431
+ async setPronunciationDictionaryRules(dictionaryId, rules) {
28432
+ return this.post(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}/set-rules`, { rules });
28433
+ }
28434
+ async addPronunciationDictionaryRules(dictionaryId, rules) {
28435
+ return this.post(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}/add-rules`, { rules });
28436
+ }
28437
+ async removePronunciationDictionaryRules(dictionaryId, ruleIds) {
28438
+ return this.post(`/tts/pronunciation-dictionaries/${encodeURIComponent(dictionaryId)}/remove-rules`, { rule_ids: ruleIds });
28439
+ }
28273
28440
  }
28274
28441
 
28275
28442
  /**
@@ -29171,19 +29338,14 @@ class VoiceAI {
29171
29338
  ? '/connection/test-connection-details'
29172
29339
  : '/connection/connection-details';
29173
29340
  const endpoint = `${url}${endpointPath}`;
29341
+ if (!options.agentId) {
29342
+ throw new Error('agentId is required.');
29343
+ }
29174
29344
  const requestData = {};
29175
29345
  if (options.agentId)
29176
29346
  requestData.agent_id = options.agentId;
29177
- if (options.agentConfig) {
29178
- requestData.metadata = JSON.stringify(options.agentConfig);
29179
- }
29180
- else if (options.metadata) {
29181
- requestData.metadata = options.metadata;
29182
- }
29183
- if (options.environment) {
29184
- requestData.environment = typeof options.environment === 'string'
29185
- ? options.environment
29186
- : JSON.stringify(options.environment);
29347
+ if (options.dynamicVariables) {
29348
+ requestData.dynamic_variables = options.dynamicVariables;
29187
29349
  }
29188
29350
  const apiKey = options.apiKey || this.apiKey;
29189
29351
  this.effectiveApiKey = apiKey;