@voice-ai-labs/web-sdk 0.9.1 → 1.0.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.3";const version = version$1;
10705
+ }var version$1 = "2.18.0";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) {
@@ -18655,6 +18655,9 @@ class RTCEngine extends eventsExports.EventEmitter {
18655
18655
  get isClosed() {
18656
18656
  return this._isClosed;
18657
18657
  }
18658
+ get isNewlyCreated() {
18659
+ return this._isNewlyCreated;
18660
+ }
18658
18661
  get pendingReconnect() {
18659
18662
  return !!this.reconnectTimeout;
18660
18663
  }
@@ -18672,6 +18675,7 @@ class RTCEngine extends eventsExports.EventEmitter {
18672
18675
  this.subscriberPrimary = false;
18673
18676
  this.pcState = PCState.New;
18674
18677
  this._isClosed = true;
18678
+ this._isNewlyCreated = true;
18675
18679
  this.pendingTrackResolvers = {};
18676
18680
  this.reconnectAttempts = 0;
18677
18681
  this.reconnectStart = 0;
@@ -18929,7 +18933,7 @@ class RTCEngine extends eventsExports.EventEmitter {
18929
18933
  room: (_b = (_a = this.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.name,
18930
18934
  roomID: (_d = (_c = this.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.room) === null || _d === void 0 ? void 0 : _d.sid,
18931
18935
  participant: (_f = (_e = this.latestJoinResponse) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.identity,
18932
- pID: this.participantSid
18936
+ participantID: this.participantSid
18933
18937
  };
18934
18938
  }
18935
18939
  join(url_1, token_1, opts_1, abortSignal_1) {
@@ -18937,6 +18941,7 @@ class RTCEngine extends eventsExports.EventEmitter {
18937
18941
  var _this2 = this;
18938
18942
  let useV0Path = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
18939
18943
  return function* () {
18944
+ _this2._isNewlyCreated = false;
18940
18945
  _this2.url = url;
18941
18946
  _this2.token = token;
18942
18947
  _this2.signalOpts = opts;
@@ -20592,7 +20597,7 @@ class OutgoingDataStreamManager {
20592
20597
  mimeType: info.mimeType,
20593
20598
  topic: info.topic,
20594
20599
  timestamp: numberToBigInt(info.timestamp),
20595
- totalLength: numberToBigInt(options === null || options === void 0 ? void 0 : options.totalSize),
20600
+ totalLength: numberToBigInt(info.size),
20596
20601
  attributes: info.attributes,
20597
20602
  contentHeader: {
20598
20603
  case: 'textHeader',
@@ -20702,7 +20707,7 @@ class OutgoingDataStreamManager {
20702
20707
  }
20703
20708
  streamBytes(options) {
20704
20709
  return __awaiter(this, void 0, void 0, function* () {
20705
- var _a, _b, _c, _d, _e, _f;
20710
+ var _a, _b, _c, _d, _e;
20706
20711
  const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
20707
20712
  const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
20708
20713
  const info = {
@@ -20716,7 +20721,7 @@ class OutgoingDataStreamManager {
20716
20721
  encryptionType: ((_e = this.engine.e2eeManager) === null || _e === void 0 ? void 0 : _e.isDataChannelEncryptionEnabled) ? Encryption_Type.GCM : Encryption_Type.NONE
20717
20722
  };
20718
20723
  const header = new DataStream_Header({
20719
- totalLength: numberToBigInt((_f = info.size) !== null && _f !== void 0 ? _f : 0),
20724
+ totalLength: numberToBigInt(info.size),
20720
20725
  mimeType: info.mimeType,
20721
20726
  streamId,
20722
20727
  topic: info.topic,
@@ -24089,7 +24094,7 @@ class Participant extends eventsExports.EventEmitter {
24089
24094
  }
24090
24095
  get logContext() {
24091
24096
  return Object.assign(Object.assign({}, super.logContext), {
24092
- rpID: this.sid,
24097
+ remoteParticipantID: this.sid,
24093
24098
  remoteParticipant: this.identity
24094
24099
  });
24095
24100
  }
@@ -25245,7 +25250,7 @@ class Room extends eventsExports.EventEmitter {
25245
25250
  room: this.name,
25246
25251
  roomID: (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.sid,
25247
25252
  participant: this.localParticipant.identity,
25248
- pID: this.localParticipant.sid
25253
+ participantID: this.localParticipant.sid
25249
25254
  };
25250
25255
  }
25251
25256
  /**
@@ -25299,7 +25304,7 @@ class Room extends eventsExports.EventEmitter {
25299
25304
  return (_b = (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.numPublishers) !== null && _b !== void 0 ? _b : 0;
25300
25305
  }
25301
25306
  maybeCreateEngine() {
25302
- if (this.engine && !this.engine.isClosed) {
25307
+ if (this.engine && (this.engine.isNewlyCreated || !this.engine.isClosed)) {
25303
25308
  return;
25304
25309
  }
25305
25310
  this.engine = new RTCEngine(this.options);
@@ -25752,7 +25757,7 @@ class Room extends eventsExports.EventEmitter {
25752
25757
  }
25753
25758
  if (!trackId.startsWith('TR')) {
25754
25759
  this.log.warn("Tried to add a track whose 'sid' could not be determined for a participant, that's not present. Sid: ".concat(participantSid, ", streamId: ").concat(streamId, ", trackId: ").concat(trackId), Object.assign(Object.assign({}, this.logContext), {
25755
- rpID: participantSid,
25760
+ remoteParticipantID: participantSid,
25756
25761
  streamId,
25757
25762
  trackId
25758
25763
  }));
@@ -26295,6 +26300,11 @@ function mapArgs(args) {
26295
26300
  return arg;
26296
26301
  });
26297
26302
  }// This file was generated from JSON Schema using quicktype, do not modify it directly.
26303
+ var DataTrackHandleErrorReason;
26304
+ (function (DataTrackHandleErrorReason) {
26305
+ DataTrackHandleErrorReason[DataTrackHandleErrorReason["Reserved"] = 0] = "Reserved";
26306
+ DataTrackHandleErrorReason[DataTrackHandleErrorReason["TooLarge"] = 1] = "TooLarge";
26307
+ })(DataTrackHandleErrorReason || (DataTrackHandleErrorReason = {}));
26298
26308
  var DataTrackDeserializeErrorReason;
26299
26309
  (function (DataTrackDeserializeErrorReason) {
26300
26310
  DataTrackDeserializeErrorReason[DataTrackDeserializeErrorReason["TooShort"] = 0] = "TooShort";
@@ -26316,11 +26326,6 @@ var DataTrackExtensionTag;
26316
26326
  })(DataTrackExtensionTag || (DataTrackExtensionTag = {}));
26317
26327
  DataTrackExtensionTag.UserTimestamp;
26318
26328
  DataTrackExtensionTag.E2ee;
26319
- var DataTrackHandleErrorReason;
26320
- (function (DataTrackHandleErrorReason) {
26321
- DataTrackHandleErrorReason[DataTrackHandleErrorReason["Reserved"] = 0] = "Reserved";
26322
- DataTrackHandleErrorReason[DataTrackHandleErrorReason["TooLarge"] = 1] = "TooLarge";
26323
- })(DataTrackHandleErrorReason || (DataTrackHandleErrorReason = {}));
26324
26329
  /** Marker indicating a packet's position in relation to a frame. */
26325
26330
  var FrameMarker;
26326
26331
  (function (FrameMarker) {
@@ -26344,7 +26349,17 @@ var DataTrackDepacketizerDropReason;
26344
26349
  DataTrackDepacketizerDropReason[DataTrackDepacketizerDropReason["UnknownFrame"] = 1] = "UnknownFrame";
26345
26350
  DataTrackDepacketizerDropReason[DataTrackDepacketizerDropReason["BufferFull"] = 2] = "BufferFull";
26346
26351
  DataTrackDepacketizerDropReason[DataTrackDepacketizerDropReason["Incomplete"] = 3] = "Incomplete";
26347
- })(DataTrackDepacketizerDropReason || (DataTrackDepacketizerDropReason = {}));var DataTrackPublishErrorReason;
26352
+ })(DataTrackDepacketizerDropReason || (DataTrackDepacketizerDropReason = {}));var DataTrackSubscribeErrorReason;
26353
+ (function (DataTrackSubscribeErrorReason) {
26354
+ /** The track has been unpublished and is no longer available */
26355
+ DataTrackSubscribeErrorReason[DataTrackSubscribeErrorReason["Unpublished"] = 0] = "Unpublished";
26356
+ /** Request to subscribe to data track timed-out */
26357
+ DataTrackSubscribeErrorReason[DataTrackSubscribeErrorReason["Timeout"] = 1] = "Timeout";
26358
+ /** Cannot subscribe to data track when disconnected */
26359
+ DataTrackSubscribeErrorReason[DataTrackSubscribeErrorReason["Disconnected"] = 2] = "Disconnected";
26360
+ /** Subscription to data track cancelled by caller */
26361
+ DataTrackSubscribeErrorReason[DataTrackSubscribeErrorReason["Cancelled"] = 4] = "Cancelled";
26362
+ })(DataTrackSubscribeErrorReason || (DataTrackSubscribeErrorReason = {}));getLogger(LoggerNames.DataTracks);getLogger(LoggerNames.DataTracks);var DataTrackPublishErrorReason;
26348
26363
  (function (DataTrackPublishErrorReason) {
26349
26364
  /**
26350
26365
  * Local participant does not have permission to publish data tracks.
@@ -27320,15 +27335,33 @@ class VoiceAIError extends Error {
27320
27335
  */
27321
27336
  class BaseClient {
27322
27337
  constructor(config) {
27323
- this.apiKey = config.apiKey;
27338
+ this.apiKey = config.apiKey || '';
27339
+ this.authToken = config.authToken;
27340
+ this.getAuthToken = config.getAuthToken;
27324
27341
  this.apiUrl = config.apiUrl;
27325
27342
  }
27343
+ async resolveBearerToken() {
27344
+ if (this.apiKey) {
27345
+ return this.apiKey;
27346
+ }
27347
+ if (this.authToken) {
27348
+ return this.authToken;
27349
+ }
27350
+ if (this.getAuthToken) {
27351
+ const resolvedToken = await this.getAuthToken();
27352
+ if (typeof resolvedToken === 'string' && resolvedToken.trim()) {
27353
+ return resolvedToken;
27354
+ }
27355
+ }
27356
+ throw new VoiceAIError('Authentication required. Configure apiKey, authToken, or getAuthToken.', 401, 'UNAUTHORIZED');
27357
+ }
27326
27358
  /**
27327
27359
  * Build headers with authentication
27328
27360
  */
27329
- getHeaders(contentType = 'application/json') {
27361
+ async getHeaders(contentType = 'application/json') {
27362
+ const bearerToken = await this.resolveBearerToken();
27330
27363
  const headers = {
27331
- 'Authorization': `Bearer ${this.apiKey}`,
27364
+ 'Authorization': `Bearer ${bearerToken}`,
27332
27365
  };
27333
27366
  if (contentType) {
27334
27367
  headers['Content-Type'] = contentType;
@@ -27358,7 +27391,7 @@ class BaseClient {
27358
27391
  }
27359
27392
  // Handle specific error codes
27360
27393
  if (response.status === 401) {
27361
- throw new VoiceAIError('Authentication failed. Please check your API key.', 401, 'UNAUTHORIZED');
27394
+ throw new VoiceAIError('Authentication failed. Please check your API key or auth token.', 401, 'UNAUTHORIZED');
27362
27395
  }
27363
27396
  if (response.status === 403) {
27364
27397
  const detail = errorData?.detail || errorData?.error;
@@ -27399,7 +27432,7 @@ class BaseClient {
27399
27432
  }
27400
27433
  const response = await fetch(url.toString(), {
27401
27434
  method: 'GET',
27402
- headers: this.getHeaders(),
27435
+ headers: await this.getHeaders(),
27403
27436
  });
27404
27437
  return this.handleResponse(response);
27405
27438
  }
@@ -27422,7 +27455,7 @@ class BaseClient {
27422
27455
  }
27423
27456
  const response = await fetch(url.toString(), {
27424
27457
  method: 'GET',
27425
- headers: this.getHeaders(),
27458
+ headers: await this.getHeaders(),
27426
27459
  });
27427
27460
  if (!response.ok) {
27428
27461
  let errorData = null;
@@ -27442,7 +27475,7 @@ class BaseClient {
27442
27475
  async post(path, body) {
27443
27476
  const response = await fetch(`${this.apiUrl}${path}`, {
27444
27477
  method: 'POST',
27445
- headers: this.getHeaders(),
27478
+ headers: await this.getHeaders(),
27446
27479
  body: body !== undefined ? JSON.stringify(body) : undefined,
27447
27480
  });
27448
27481
  return this.handleResponse(response);
@@ -27453,7 +27486,7 @@ class BaseClient {
27453
27486
  async postFormData(path, formData) {
27454
27487
  // Don't set Content-Type header - browser will set it with boundary
27455
27488
  const headers = {
27456
- 'Authorization': `Bearer ${this.apiKey}`,
27489
+ 'Authorization': `Bearer ${await this.resolveBearerToken()}`,
27457
27490
  };
27458
27491
  const response = await fetch(`${this.apiUrl}${path}`, {
27459
27492
  method: 'POST',
@@ -27468,7 +27501,7 @@ class BaseClient {
27468
27501
  async put(path, body) {
27469
27502
  const response = await fetch(`${this.apiUrl}${path}`, {
27470
27503
  method: 'PUT',
27471
- headers: this.getHeaders(),
27504
+ headers: await this.getHeaders(),
27472
27505
  body: body !== undefined ? JSON.stringify(body) : undefined,
27473
27506
  });
27474
27507
  return this.handleResponse(response);
@@ -27479,7 +27512,7 @@ class BaseClient {
27479
27512
  async patch(path, body) {
27480
27513
  const response = await fetch(`${this.apiUrl}${path}`, {
27481
27514
  method: 'PATCH',
27482
- headers: this.getHeaders(),
27515
+ headers: await this.getHeaders(),
27483
27516
  body: body !== undefined ? JSON.stringify(body) : undefined,
27484
27517
  });
27485
27518
  return this.handleResponse(response);
@@ -27490,7 +27523,7 @@ class BaseClient {
27490
27523
  async httpDelete(path) {
27491
27524
  const response = await fetch(`${this.apiUrl}${path}`, {
27492
27525
  method: 'DELETE',
27493
- headers: this.getHeaders(),
27526
+ headers: await this.getHeaders(),
27494
27527
  });
27495
27528
  return this.handleResponse(response);
27496
27529
  }
@@ -27500,7 +27533,7 @@ class BaseClient {
27500
27533
  async postForBlob(path, body) {
27501
27534
  const response = await fetch(`${this.apiUrl}${path}`, {
27502
27535
  method: 'POST',
27503
- headers: this.getHeaders(),
27536
+ headers: await this.getHeaders(),
27504
27537
  body: body !== undefined ? JSON.stringify(body) : undefined,
27505
27538
  });
27506
27539
  if (!response.ok) {
@@ -27522,7 +27555,7 @@ class BaseClient {
27522
27555
  async postForStream(path, body) {
27523
27556
  const response = await fetch(`${this.apiUrl}${path}`, {
27524
27557
  method: 'POST',
27525
- headers: this.getHeaders(),
27558
+ headers: await this.getHeaders(),
27526
27559
  body: body !== undefined ? JSON.stringify(body) : undefined,
27527
27560
  });
27528
27561
  if (!response.ok) {
@@ -27787,6 +27820,7 @@ class AgentClient extends BaseClient {
27787
27820
  * Provides methods for:
27788
27821
  * - Getting call history with filters
27789
27822
  * - Getting transcript URLs
27823
+ * - Getting recording status and URLs
27790
27824
  * - Getting agent stats summary
27791
27825
  */
27792
27826
  /**
@@ -27837,6 +27871,12 @@ class AnalyticsClient extends BaseClient {
27837
27871
  params.end_date = options.end_date;
27838
27872
  if (options?.agent_ids)
27839
27873
  params.agent_ids = options.agent_ids;
27874
+ if (options?.agent_name)
27875
+ params.agent_name = options.agent_name;
27876
+ if (options?.sort_by)
27877
+ params.sort_by = options.sort_by;
27878
+ if (options?.sort_dir)
27879
+ params.sort_dir = options.sort_dir;
27840
27880
  return this.get('/agent/call-history', params);
27841
27881
  }
27842
27882
  /**
@@ -27856,6 +27896,24 @@ class AnalyticsClient extends BaseClient {
27856
27896
  async getTranscriptUrl(callId) {
27857
27897
  return this.get(`/agent/call-history/${callId}/transcript`);
27858
27898
  }
27899
+ /**
27900
+ * Get recording status or download URL for a call
27901
+ *
27902
+ * @param callId - The call identifier
27903
+ * @returns Object with recording status and optional URL
27904
+ *
27905
+ * @example
27906
+ * ```typescript
27907
+ * const recording = await client.analytics.getRecordingUrl("call_12345");
27908
+ *
27909
+ * if (recording.status === 'ready' && recording.url) {
27910
+ * window.open(recording.url, '_blank');
27911
+ * }
27912
+ * ```
27913
+ */
27914
+ async getRecordingUrl(callId) {
27915
+ return this.get(`/agent/call-history/${callId}/recording`);
27916
+ }
27859
27917
  /**
27860
27918
  * Get agent stats summary
27861
27919
  *
@@ -28000,6 +28058,29 @@ class KnowledgeBaseClient extends BaseClient {
28000
28058
  }
28001
28059
  }
28002
28060
 
28061
+ class GoogleManagedToolsClient extends BaseClient {
28062
+ constructor(config) {
28063
+ super(config);
28064
+ }
28065
+ async startOAuth(agentId, options = {}) {
28066
+ return this.post(`/google/${encodeURIComponent(agentId)}/oauth/start`, {
28067
+ return_path: options.returnUrl,
28068
+ managed_tools: options.managedTools,
28069
+ });
28070
+ }
28071
+ async getStatus(agentId) {
28072
+ return this.get(`/google/${encodeURIComponent(agentId)}/status`);
28073
+ }
28074
+ async disconnect(agentId) {
28075
+ return this.httpDelete(`/google/${encodeURIComponent(agentId)}/disconnect`);
28076
+ }
28077
+ }
28078
+ class ManagedToolsClient {
28079
+ constructor(config) {
28080
+ this.google = new GoogleManagedToolsClient(config);
28081
+ }
28082
+ }
28083
+
28003
28084
  /**
28004
28085
  * Phone Number Management Client
28005
28086
  *
@@ -28306,7 +28387,7 @@ class TTSClient extends BaseClient {
28306
28387
  /**
28307
28388
  * Clone a voice from a reference audio file
28308
28389
  *
28309
- * Accepts an audio file (MP3, WAV, or OGG, max 7.5MB) and creates
28390
+ * Accepts an audio file (MP3, WAV, or M4A, max 7.5MB) and creates
28310
28391
  * a cloned voice. The voice starts in PENDING status and moves to
28311
28392
  * PROCESSING, then AVAILABLE once ready.
28312
28393
  *
@@ -28439,6 +28520,648 @@ class TTSClient extends BaseClient {
28439
28520
  }
28440
28521
  }
28441
28522
 
28523
+ const IANA_TIMEZONE_IDS = Object.freeze([
28524
+ "Africa/Abidjan",
28525
+ "Africa/Accra",
28526
+ "Africa/Addis_Ababa",
28527
+ "Africa/Algiers",
28528
+ "Africa/Asmera",
28529
+ "Africa/Bamako",
28530
+ "Africa/Bangui",
28531
+ "Africa/Banjul",
28532
+ "Africa/Bissau",
28533
+ "Africa/Blantyre",
28534
+ "Africa/Brazzaville",
28535
+ "Africa/Bujumbura",
28536
+ "Africa/Cairo",
28537
+ "Africa/Casablanca",
28538
+ "Africa/Ceuta",
28539
+ "Africa/Conakry",
28540
+ "Africa/Dakar",
28541
+ "Africa/Dar_es_Salaam",
28542
+ "Africa/Djibouti",
28543
+ "Africa/Douala",
28544
+ "Africa/El_Aaiun",
28545
+ "Africa/Freetown",
28546
+ "Africa/Gaborone",
28547
+ "Africa/Harare",
28548
+ "Africa/Johannesburg",
28549
+ "Africa/Juba",
28550
+ "Africa/Kampala",
28551
+ "Africa/Khartoum",
28552
+ "Africa/Kigali",
28553
+ "Africa/Kinshasa",
28554
+ "Africa/Lagos",
28555
+ "Africa/Libreville",
28556
+ "Africa/Lome",
28557
+ "Africa/Luanda",
28558
+ "Africa/Lubumbashi",
28559
+ "Africa/Lusaka",
28560
+ "Africa/Malabo",
28561
+ "Africa/Maputo",
28562
+ "Africa/Maseru",
28563
+ "Africa/Mbabane",
28564
+ "Africa/Mogadishu",
28565
+ "Africa/Monrovia",
28566
+ "Africa/Nairobi",
28567
+ "Africa/Ndjamena",
28568
+ "Africa/Niamey",
28569
+ "Africa/Nouakchott",
28570
+ "Africa/Ouagadougou",
28571
+ "Africa/Porto-Novo",
28572
+ "Africa/Sao_Tome",
28573
+ "Africa/Tripoli",
28574
+ "Africa/Tunis",
28575
+ "Africa/Windhoek",
28576
+ "America/Adak",
28577
+ "America/Anchorage",
28578
+ "America/Anguilla",
28579
+ "America/Antigua",
28580
+ "America/Araguaina",
28581
+ "America/Argentina/La_Rioja",
28582
+ "America/Argentina/Rio_Gallegos",
28583
+ "America/Argentina/Salta",
28584
+ "America/Argentina/San_Juan",
28585
+ "America/Argentina/San_Luis",
28586
+ "America/Argentina/Tucuman",
28587
+ "America/Argentina/Ushuaia",
28588
+ "America/Aruba",
28589
+ "America/Asuncion",
28590
+ "America/Bahia",
28591
+ "America/Bahia_Banderas",
28592
+ "America/Barbados",
28593
+ "America/Belem",
28594
+ "America/Belize",
28595
+ "America/Blanc-Sablon",
28596
+ "America/Boa_Vista",
28597
+ "America/Bogota",
28598
+ "America/Boise",
28599
+ "America/Buenos_Aires",
28600
+ "America/Cambridge_Bay",
28601
+ "America/Campo_Grande",
28602
+ "America/Cancun",
28603
+ "America/Caracas",
28604
+ "America/Catamarca",
28605
+ "America/Cayenne",
28606
+ "America/Cayman",
28607
+ "America/Chicago",
28608
+ "America/Chihuahua",
28609
+ "America/Ciudad_Juarez",
28610
+ "America/Coral_Harbour",
28611
+ "America/Cordoba",
28612
+ "America/Costa_Rica",
28613
+ "America/Creston",
28614
+ "America/Cuiaba",
28615
+ "America/Curacao",
28616
+ "America/Danmarkshavn",
28617
+ "America/Dawson",
28618
+ "America/Dawson_Creek",
28619
+ "America/Denver",
28620
+ "America/Detroit",
28621
+ "America/Dominica",
28622
+ "America/Edmonton",
28623
+ "America/Eirunepe",
28624
+ "America/El_Salvador",
28625
+ "America/Fort_Nelson",
28626
+ "America/Fortaleza",
28627
+ "America/Glace_Bay",
28628
+ "America/Godthab",
28629
+ "America/Goose_Bay",
28630
+ "America/Grand_Turk",
28631
+ "America/Grenada",
28632
+ "America/Guadeloupe",
28633
+ "America/Guatemala",
28634
+ "America/Guayaquil",
28635
+ "America/Guyana",
28636
+ "America/Halifax",
28637
+ "America/Havana",
28638
+ "America/Hermosillo",
28639
+ "America/Indiana/Knox",
28640
+ "America/Indiana/Marengo",
28641
+ "America/Indiana/Petersburg",
28642
+ "America/Indiana/Tell_City",
28643
+ "America/Indiana/Vevay",
28644
+ "America/Indiana/Vincennes",
28645
+ "America/Indiana/Winamac",
28646
+ "America/Indianapolis",
28647
+ "America/Inuvik",
28648
+ "America/Iqaluit",
28649
+ "America/Jamaica",
28650
+ "America/Jujuy",
28651
+ "America/Juneau",
28652
+ "America/Kentucky/Monticello",
28653
+ "America/Kralendijk",
28654
+ "America/La_Paz",
28655
+ "America/Lima",
28656
+ "America/Los_Angeles",
28657
+ "America/Louisville",
28658
+ "America/Lower_Princes",
28659
+ "America/Maceio",
28660
+ "America/Managua",
28661
+ "America/Manaus",
28662
+ "America/Marigot",
28663
+ "America/Martinique",
28664
+ "America/Matamoros",
28665
+ "America/Mazatlan",
28666
+ "America/Mendoza",
28667
+ "America/Menominee",
28668
+ "America/Merida",
28669
+ "America/Metlakatla",
28670
+ "America/Mexico_City",
28671
+ "America/Miquelon",
28672
+ "America/Moncton",
28673
+ "America/Monterrey",
28674
+ "America/Montevideo",
28675
+ "America/Montserrat",
28676
+ "America/Nassau",
28677
+ "America/New_York",
28678
+ "America/Nome",
28679
+ "America/Noronha",
28680
+ "America/North_Dakota/Beulah",
28681
+ "America/North_Dakota/Center",
28682
+ "America/North_Dakota/New_Salem",
28683
+ "America/Ojinaga",
28684
+ "America/Panama",
28685
+ "America/Paramaribo",
28686
+ "America/Phoenix",
28687
+ "America/Port-au-Prince",
28688
+ "America/Port_of_Spain",
28689
+ "America/Porto_Velho",
28690
+ "America/Puerto_Rico",
28691
+ "America/Punta_Arenas",
28692
+ "America/Rankin_Inlet",
28693
+ "America/Recife",
28694
+ "America/Regina",
28695
+ "America/Resolute",
28696
+ "America/Rio_Branco",
28697
+ "America/Santarem",
28698
+ "America/Santiago",
28699
+ "America/Santo_Domingo",
28700
+ "America/Sao_Paulo",
28701
+ "America/Scoresbysund",
28702
+ "America/Sitka",
28703
+ "America/St_Barthelemy",
28704
+ "America/St_Johns",
28705
+ "America/St_Kitts",
28706
+ "America/St_Lucia",
28707
+ "America/St_Thomas",
28708
+ "America/St_Vincent",
28709
+ "America/Swift_Current",
28710
+ "America/Tegucigalpa",
28711
+ "America/Thule",
28712
+ "America/Tijuana",
28713
+ "America/Toronto",
28714
+ "America/Tortola",
28715
+ "America/Vancouver",
28716
+ "America/Whitehorse",
28717
+ "America/Winnipeg",
28718
+ "America/Yakutat",
28719
+ "Antarctica/Casey",
28720
+ "Antarctica/Davis",
28721
+ "Antarctica/DumontDUrville",
28722
+ "Antarctica/Macquarie",
28723
+ "Antarctica/Mawson",
28724
+ "Antarctica/McMurdo",
28725
+ "Antarctica/Palmer",
28726
+ "Antarctica/Rothera",
28727
+ "Antarctica/Syowa",
28728
+ "Antarctica/Troll",
28729
+ "Antarctica/Vostok",
28730
+ "Arctic/Longyearbyen",
28731
+ "Asia/Aden",
28732
+ "Asia/Almaty",
28733
+ "Asia/Amman",
28734
+ "Asia/Anadyr",
28735
+ "Asia/Aqtau",
28736
+ "Asia/Aqtobe",
28737
+ "Asia/Ashgabat",
28738
+ "Asia/Atyrau",
28739
+ "Asia/Baghdad",
28740
+ "Asia/Bahrain",
28741
+ "Asia/Baku",
28742
+ "Asia/Bangkok",
28743
+ "Asia/Barnaul",
28744
+ "Asia/Beirut",
28745
+ "Asia/Bishkek",
28746
+ "Asia/Brunei",
28747
+ "Asia/Calcutta",
28748
+ "Asia/Chita",
28749
+ "Asia/Colombo",
28750
+ "Asia/Damascus",
28751
+ "Asia/Dhaka",
28752
+ "Asia/Dili",
28753
+ "Asia/Dubai",
28754
+ "Asia/Dushanbe",
28755
+ "Asia/Famagusta",
28756
+ "Asia/Gaza",
28757
+ "Asia/Hebron",
28758
+ "Asia/Hong_Kong",
28759
+ "Asia/Hovd",
28760
+ "Asia/Irkutsk",
28761
+ "Asia/Jakarta",
28762
+ "Asia/Jayapura",
28763
+ "Asia/Jerusalem",
28764
+ "Asia/Kabul",
28765
+ "Asia/Kamchatka",
28766
+ "Asia/Karachi",
28767
+ "Asia/Katmandu",
28768
+ "Asia/Khandyga",
28769
+ "Asia/Krasnoyarsk",
28770
+ "Asia/Kuala_Lumpur",
28771
+ "Asia/Kuching",
28772
+ "Asia/Kuwait",
28773
+ "Asia/Macau",
28774
+ "Asia/Magadan",
28775
+ "Asia/Makassar",
28776
+ "Asia/Manila",
28777
+ "Asia/Muscat",
28778
+ "Asia/Nicosia",
28779
+ "Asia/Novokuznetsk",
28780
+ "Asia/Novosibirsk",
28781
+ "Asia/Omsk",
28782
+ "Asia/Oral",
28783
+ "Asia/Phnom_Penh",
28784
+ "Asia/Pontianak",
28785
+ "Asia/Pyongyang",
28786
+ "Asia/Qatar",
28787
+ "Asia/Qostanay",
28788
+ "Asia/Qyzylorda",
28789
+ "Asia/Rangoon",
28790
+ "Asia/Riyadh",
28791
+ "Asia/Saigon",
28792
+ "Asia/Sakhalin",
28793
+ "Asia/Samarkand",
28794
+ "Asia/Seoul",
28795
+ "Asia/Shanghai",
28796
+ "Asia/Singapore",
28797
+ "Asia/Srednekolymsk",
28798
+ "Asia/Taipei",
28799
+ "Asia/Tashkent",
28800
+ "Asia/Tbilisi",
28801
+ "Asia/Tehran",
28802
+ "Asia/Thimphu",
28803
+ "Asia/Tokyo",
28804
+ "Asia/Tomsk",
28805
+ "Asia/Ulaanbaatar",
28806
+ "Asia/Urumqi",
28807
+ "Asia/Ust-Nera",
28808
+ "Asia/Vientiane",
28809
+ "Asia/Vladivostok",
28810
+ "Asia/Yakutsk",
28811
+ "Asia/Yekaterinburg",
28812
+ "Asia/Yerevan",
28813
+ "Atlantic/Azores",
28814
+ "Atlantic/Bermuda",
28815
+ "Atlantic/Canary",
28816
+ "Atlantic/Cape_Verde",
28817
+ "Atlantic/Faeroe",
28818
+ "Atlantic/Madeira",
28819
+ "Atlantic/Reykjavik",
28820
+ "Atlantic/South_Georgia",
28821
+ "Atlantic/St_Helena",
28822
+ "Atlantic/Stanley",
28823
+ "Australia/Adelaide",
28824
+ "Australia/Brisbane",
28825
+ "Australia/Broken_Hill",
28826
+ "Australia/Darwin",
28827
+ "Australia/Eucla",
28828
+ "Australia/Hobart",
28829
+ "Australia/Lindeman",
28830
+ "Australia/Lord_Howe",
28831
+ "Australia/Melbourne",
28832
+ "Australia/Perth",
28833
+ "Australia/Sydney",
28834
+ "Europe/Amsterdam",
28835
+ "Europe/Andorra",
28836
+ "Europe/Astrakhan",
28837
+ "Europe/Athens",
28838
+ "Europe/Belgrade",
28839
+ "Europe/Berlin",
28840
+ "Europe/Bratislava",
28841
+ "Europe/Brussels",
28842
+ "Europe/Bucharest",
28843
+ "Europe/Budapest",
28844
+ "Europe/Busingen",
28845
+ "Europe/Chisinau",
28846
+ "Europe/Copenhagen",
28847
+ "Europe/Dublin",
28848
+ "Europe/Gibraltar",
28849
+ "Europe/Guernsey",
28850
+ "Europe/Helsinki",
28851
+ "Europe/Isle_of_Man",
28852
+ "Europe/Istanbul",
28853
+ "Europe/Jersey",
28854
+ "Europe/Kaliningrad",
28855
+ "Europe/Kiev",
28856
+ "Europe/Kirov",
28857
+ "Europe/Lisbon",
28858
+ "Europe/Ljubljana",
28859
+ "Europe/London",
28860
+ "Europe/Luxembourg",
28861
+ "Europe/Madrid",
28862
+ "Europe/Malta",
28863
+ "Europe/Mariehamn",
28864
+ "Europe/Minsk",
28865
+ "Europe/Monaco",
28866
+ "Europe/Moscow",
28867
+ "Europe/Oslo",
28868
+ "Europe/Paris",
28869
+ "Europe/Podgorica",
28870
+ "Europe/Prague",
28871
+ "Europe/Riga",
28872
+ "Europe/Rome",
28873
+ "Europe/Samara",
28874
+ "Europe/San_Marino",
28875
+ "Europe/Sarajevo",
28876
+ "Europe/Saratov",
28877
+ "Europe/Simferopol",
28878
+ "Europe/Skopje",
28879
+ "Europe/Sofia",
28880
+ "Europe/Stockholm",
28881
+ "Europe/Tallinn",
28882
+ "Europe/Tirane",
28883
+ "Europe/Ulyanovsk",
28884
+ "Europe/Vaduz",
28885
+ "Europe/Vatican",
28886
+ "Europe/Vienna",
28887
+ "Europe/Vilnius",
28888
+ "Europe/Volgograd",
28889
+ "Europe/Warsaw",
28890
+ "Europe/Zagreb",
28891
+ "Europe/Zurich",
28892
+ "Indian/Antananarivo",
28893
+ "Indian/Chagos",
28894
+ "Indian/Christmas",
28895
+ "Indian/Cocos",
28896
+ "Indian/Comoro",
28897
+ "Indian/Kerguelen",
28898
+ "Indian/Mahe",
28899
+ "Indian/Maldives",
28900
+ "Indian/Mauritius",
28901
+ "Indian/Mayotte",
28902
+ "Indian/Reunion",
28903
+ "Pacific/Apia",
28904
+ "Pacific/Auckland",
28905
+ "Pacific/Bougainville",
28906
+ "Pacific/Chatham",
28907
+ "Pacific/Easter",
28908
+ "Pacific/Efate",
28909
+ "Pacific/Enderbury",
28910
+ "Pacific/Fakaofo",
28911
+ "Pacific/Fiji",
28912
+ "Pacific/Funafuti",
28913
+ "Pacific/Galapagos",
28914
+ "Pacific/Gambier",
28915
+ "Pacific/Guadalcanal",
28916
+ "Pacific/Guam",
28917
+ "Pacific/Honolulu",
28918
+ "Pacific/Kiritimati",
28919
+ "Pacific/Kosrae",
28920
+ "Pacific/Kwajalein",
28921
+ "Pacific/Majuro",
28922
+ "Pacific/Marquesas",
28923
+ "Pacific/Midway",
28924
+ "Pacific/Nauru",
28925
+ "Pacific/Niue",
28926
+ "Pacific/Norfolk",
28927
+ "Pacific/Noumea",
28928
+ "Pacific/Pago_Pago",
28929
+ "Pacific/Palau",
28930
+ "Pacific/Pitcairn",
28931
+ "Pacific/Ponape",
28932
+ "Pacific/Port_Moresby",
28933
+ "Pacific/Rarotonga",
28934
+ "Pacific/Saipan",
28935
+ "Pacific/Tahiti",
28936
+ "Pacific/Tarawa",
28937
+ "Pacific/Tongatapu",
28938
+ "Pacific/Truk",
28939
+ "Pacific/Wake",
28940
+ "Pacific/Wallis",
28941
+ ]);
28942
+ function parseOffsetMinutes(rawOffset) {
28943
+ if (rawOffset === "GMT" || rawOffset === "UTC") {
28944
+ return 0;
28945
+ }
28946
+ const match = rawOffset.match(/^(?:GMT|UTC)([+-])(\d{1,2})(?::?(\d{2}))?$/);
28947
+ if (!match) {
28948
+ return 0;
28949
+ }
28950
+ const sign = match[1] === "-" ? -1 : 1;
28951
+ const hours = Number.parseInt(match[2] || "0", 10);
28952
+ const minutes = Number.parseInt(match[3] || "0", 10);
28953
+ return sign * (hours * 60 + minutes);
28954
+ }
28955
+ function getTimezoneOffsetMinutes(timezoneId, date = new Date()) {
28956
+ try {
28957
+ const parts = new Intl.DateTimeFormat("en-US", {
28958
+ timeZone: timezoneId,
28959
+ timeZoneName: "shortOffset",
28960
+ hour: "2-digit",
28961
+ minute: "2-digit",
28962
+ hourCycle: "h23",
28963
+ }).formatToParts(date);
28964
+ const offsetPart = parts.find((part) => part.type === "timeZoneName")?.value;
28965
+ return offsetPart ? parseOffsetMinutes(offsetPart) : 0;
28966
+ }
28967
+ catch {
28968
+ return 0;
28969
+ }
28970
+ }
28971
+ function formatUtcOffset(offsetMinutes) {
28972
+ const sign = offsetMinutes >= 0 ? "+" : "-";
28973
+ const absoluteMinutes = Math.abs(offsetMinutes);
28974
+ const hours = String(Math.floor(absoluteMinutes / 60)).padStart(2, "0");
28975
+ const minutes = String(absoluteMinutes % 60).padStart(2, "0");
28976
+ return `UTC${sign}${hours}:${minutes}`;
28977
+ }
28978
+ const IANA_TIMEZONE_OPTIONS = Object.freeze([...IANA_TIMEZONE_IDS]
28979
+ .map((timezoneId) => {
28980
+ const offsetMinutes = getTimezoneOffsetMinutes(timezoneId);
28981
+ return {
28982
+ value: timezoneId,
28983
+ label: `${formatUtcOffset(offsetMinutes)} • ${timezoneId}`,
28984
+ offsetMinutes,
28985
+ };
28986
+ })
28987
+ .sort((left, right) => left.offsetMinutes - right.offsetMinutes || left.value.localeCompare(right.value)));
28988
+
28989
+ const GOOGLE_IDENTITY_SCOPES = [
28990
+ 'openid',
28991
+ 'https://www.googleapis.com/auth/userinfo.email',
28992
+ 'https://www.googleapis.com/auth/userinfo.profile',
28993
+ ];
28994
+ const GOOGLE_CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar';
28995
+ const GOOGLE_SHEETS_SCOPE = 'https://www.googleapis.com/auth/spreadsheets';
28996
+ const GOOGLE_GMAIL_READ_SCOPE = 'https://www.googleapis.com/auth/gmail.readonly';
28997
+ const GOOGLE_GMAIL_SEND_SCOPE = 'https://www.googleapis.com/auth/gmail.send';
28998
+ const GOOGLE_CALENDAR_OPERATION_OPTIONS = [
28999
+ {
29000
+ value: 'google_calendar_check_availability',
29001
+ label: 'Check availability',
29002
+ description: 'Check whether a calendar is free during a time window.',
29003
+ },
29004
+ {
29005
+ value: 'google_calendar_list_upcoming_events',
29006
+ label: 'List upcoming',
29007
+ description: 'Read the next upcoming events from the calendar.',
29008
+ },
29009
+ {
29010
+ value: 'google_calendar_create_event',
29011
+ label: 'Create event',
29012
+ description: 'Create a new calendar event.',
29013
+ },
29014
+ {
29015
+ value: 'google_calendar_update_event',
29016
+ label: 'Update event',
29017
+ description: 'Modify an existing calendar event.',
29018
+ },
29019
+ {
29020
+ value: 'google_calendar_cancel_event',
29021
+ label: 'Cancel event',
29022
+ description: 'Cancel or delete an existing calendar event.',
29023
+ },
29024
+ ];
29025
+ const GOOGLE_SHEETS_OPERATION_OPTIONS = [
29026
+ {
29027
+ value: 'google_sheets_append_row',
29028
+ label: 'Append row',
29029
+ description: 'Write a new row into a spreadsheet.',
29030
+ },
29031
+ {
29032
+ value: 'google_sheets_list_sheets',
29033
+ label: 'List sheets',
29034
+ description: 'List worksheet tabs and spreadsheet metadata.',
29035
+ },
29036
+ {
29037
+ value: 'google_sheets_read_rows',
29038
+ label: 'Read rows',
29039
+ description: 'Read rows from a worksheet range.',
29040
+ },
29041
+ ];
29042
+ const GOOGLE_GMAIL_OPERATION_OPTIONS = [
29043
+ {
29044
+ value: 'google_gmail_search_messages',
29045
+ label: 'Search messages',
29046
+ description: 'Search Gmail and return readable message summaries.',
29047
+ },
29048
+ {
29049
+ value: 'google_gmail_get_message',
29050
+ label: 'Get message',
29051
+ description: 'Fetch a specific Gmail message by ID.',
29052
+ },
29053
+ {
29054
+ value: 'google_gmail_send_email',
29055
+ label: 'Send email',
29056
+ description: 'Send an email from the connected Gmail account.',
29057
+ },
29058
+ ];
29059
+ const GOOGLE_MANAGED_OPERATION_OPTIONS = {
29060
+ google_calendar: GOOGLE_CALENDAR_OPERATION_OPTIONS,
29061
+ google_sheets: GOOGLE_SHEETS_OPERATION_OPTIONS,
29062
+ google_gmail: GOOGLE_GMAIL_OPERATION_OPTIONS,
29063
+ };
29064
+ const GOOGLE_GMAIL_REQUIRED_SCOPES_BY_OPERATION = {
29065
+ google_gmail_search_messages: [GOOGLE_GMAIL_READ_SCOPE],
29066
+ google_gmail_get_message: [GOOGLE_GMAIL_READ_SCOPE],
29067
+ google_gmail_send_email: [GOOGLE_GMAIL_SEND_SCOPE],
29068
+ };
29069
+ const GOOGLE_CALENDAR_REQUIRED_SCOPES_BY_OPERATION = {
29070
+ google_calendar_check_availability: [GOOGLE_CALENDAR_SCOPE],
29071
+ google_calendar_list_upcoming_events: [GOOGLE_CALENDAR_SCOPE],
29072
+ google_calendar_create_event: [GOOGLE_CALENDAR_SCOPE],
29073
+ google_calendar_update_event: [GOOGLE_CALENDAR_SCOPE],
29074
+ google_calendar_cancel_event: [GOOGLE_CALENDAR_SCOPE],
29075
+ };
29076
+ const GOOGLE_SHEETS_REQUIRED_SCOPES_BY_OPERATION = {
29077
+ google_sheets_append_row: [GOOGLE_SHEETS_SCOPE],
29078
+ google_sheets_list_sheets: [GOOGLE_SHEETS_SCOPE],
29079
+ google_sheets_read_rows: [GOOGLE_SHEETS_SCOPE],
29080
+ };
29081
+ function uniqueStrings(values) {
29082
+ const orderedValues = [];
29083
+ const seen = new Set();
29084
+ for (const value of values) {
29085
+ if (typeof value !== 'string' || !value.trim() || seen.has(value)) {
29086
+ continue;
29087
+ }
29088
+ seen.add(value);
29089
+ orderedValues.push(value);
29090
+ }
29091
+ return orderedValues;
29092
+ }
29093
+ function getManagedToolSelectedOperations(selectedOperations, options) {
29094
+ const orderedValues = options.map((option) => option.value);
29095
+ if (!Array.isArray(selectedOperations)) {
29096
+ return orderedValues;
29097
+ }
29098
+ const allowed = new Set(orderedValues);
29099
+ const selected = new Set();
29100
+ for (const operation of selectedOperations) {
29101
+ if (typeof operation === 'string' && allowed.has(operation)) {
29102
+ selected.add(operation);
29103
+ }
29104
+ }
29105
+ return orderedValues.filter((operation) => selected.has(operation));
29106
+ }
29107
+ function toggleManagedToolOperation(selectedOperations, options, operation, checked) {
29108
+ const nextSelected = new Set(getManagedToolSelectedOperations(selectedOperations, options));
29109
+ if (checked) {
29110
+ nextSelected.add(operation);
29111
+ }
29112
+ else {
29113
+ nextSelected.delete(operation);
29114
+ }
29115
+ return options
29116
+ .map((option) => option.value)
29117
+ .filter((value) => nextSelected.has(value));
29118
+ }
29119
+ function getRequiredGoogleScopes(managedTools) {
29120
+ const requiredScopes = [...GOOGLE_IDENTITY_SCOPES];
29121
+ if (managedTools?.google_calendar?.enabled) {
29122
+ for (const operation of getManagedToolSelectedOperations(managedTools.google_calendar.selected_operations, GOOGLE_CALENDAR_OPERATION_OPTIONS)) {
29123
+ requiredScopes.push(...(GOOGLE_CALENDAR_REQUIRED_SCOPES_BY_OPERATION[operation] || []));
29124
+ }
29125
+ }
29126
+ if (managedTools?.google_sheets?.enabled) {
29127
+ for (const operation of getManagedToolSelectedOperations(managedTools.google_sheets.selected_operations, GOOGLE_SHEETS_OPERATION_OPTIONS)) {
29128
+ requiredScopes.push(...(GOOGLE_SHEETS_REQUIRED_SCOPES_BY_OPERATION[operation] || []));
29129
+ }
29130
+ }
29131
+ if (managedTools?.google_gmail?.enabled) {
29132
+ for (const operation of getManagedToolSelectedOperations(managedTools.google_gmail.selected_operations, GOOGLE_GMAIL_OPERATION_OPTIONS)) {
29133
+ requiredScopes.push(...(GOOGLE_GMAIL_REQUIRED_SCOPES_BY_OPERATION[operation] || []));
29134
+ }
29135
+ }
29136
+ return uniqueStrings(requiredScopes);
29137
+ }
29138
+ function getMissingGoogleScopes(requiredScopes, grantedScopes) {
29139
+ const grantedScopeSet = new Set(uniqueStrings(grantedScopes || []));
29140
+ return uniqueStrings(requiredScopes || []).filter((scope) => !grantedScopeSet.has(scope));
29141
+ }
29142
+ function getMissingGoogleScopesForManagedTools(managedTools, grantedScopes) {
29143
+ return getMissingGoogleScopes(getRequiredGoogleScopes(managedTools), grantedScopes);
29144
+ }
29145
+ function isGoogleReconnectRequired(managedTools, grantedScopes, connected) {
29146
+ return connected && getMissingGoogleScopesForManagedTools(managedTools, grantedScopes).length > 0;
29147
+ }
29148
+ function getGoogleReconnectState(managedTools, status) {
29149
+ const required_scopes = getRequiredGoogleScopes(managedTools);
29150
+ const granted_scopes = status?.granted_scopes || [];
29151
+ const missing_scopes = getMissingGoogleScopes(required_scopes, granted_scopes);
29152
+ return {
29153
+ required_scopes,
29154
+ missing_scopes,
29155
+ reconnect_required: (Boolean(status?.connected) && missing_scopes.length > 0) ||
29156
+ Boolean(status?.reconnect_required),
29157
+ };
29158
+ }
29159
+ function hasEnabledGoogleManagedTools(managedTools) {
29160
+ return Boolean(managedTools?.google_calendar?.enabled ||
29161
+ managedTools?.google_sheets?.enabled ||
29162
+ managedTools?.google_gmail?.enabled);
29163
+ }
29164
+
28442
29165
  /**
28443
29166
  * VoiceAgentWidget - UI widget for Voice.ai
28444
29167
  *
@@ -28901,33 +29624,39 @@ class VoiceAI {
28901
29624
  /** Agent management - create, update, deploy, pause, delete agents. */
28902
29625
  get agents() {
28903
29626
  if (!this._agents)
28904
- throw new VoiceAIError('API key required for agents API. Pass apiKey in the constructor.');
29627
+ throw new VoiceAIError('API key required for agents API, or pass authToken/getAuthToken in the constructor.');
28905
29628
  return this._agents;
28906
29629
  }
28907
29630
  /** Analytics - call history, transcripts, stats. */
28908
29631
  get analytics() {
28909
29632
  if (!this._analytics)
28910
- throw new VoiceAIError('API key required for analytics API. Pass apiKey in the constructor.');
29633
+ throw new VoiceAIError('API key required for analytics API, or pass authToken/getAuthToken in the constructor.');
28911
29634
  return this._analytics;
28912
29635
  }
28913
29636
  /** Knowledge Base - manage RAG documents. */
28914
29637
  get knowledgeBase() {
28915
29638
  if (!this._knowledgeBase)
28916
- throw new VoiceAIError('API key required for knowledgeBase API. Pass apiKey in the constructor.');
29639
+ throw new VoiceAIError('API key required for knowledgeBase API, or pass authToken/getAuthToken in the constructor.');
28917
29640
  return this._knowledgeBase;
28918
29641
  }
28919
29642
  /** Phone Numbers - search, select, release phone numbers. */
28920
29643
  get phoneNumbers() {
28921
29644
  if (!this._phoneNumbers)
28922
- throw new VoiceAIError('API key required for phoneNumbers API. Pass apiKey in the constructor.');
29645
+ throw new VoiceAIError('API key required for phoneNumbers API, or pass authToken/getAuthToken in the constructor.');
28923
29646
  return this._phoneNumbers;
28924
29647
  }
28925
29648
  /** Text-to-Speech - generate speech, manage voices, clone voices. */
28926
29649
  get tts() {
28927
29650
  if (!this._tts)
28928
- throw new VoiceAIError('API key required for tts API. Pass apiKey in the constructor.');
29651
+ throw new VoiceAIError('API key required for tts API, or pass authToken/getAuthToken in the constructor.');
28929
29652
  return this._tts;
28930
29653
  }
29654
+ /** Managed tools - provider-specific OAuth/status/disconnect helpers. */
29655
+ get managedTools() {
29656
+ if (!this._managedTools)
29657
+ throw new VoiceAIError('API key required for managedTools API, or pass authToken/getAuthToken in the constructor.');
29658
+ return this._managedTools;
29659
+ }
28931
29660
  /**
28932
29661
  * Create a new VoiceAI client
28933
29662
  *
@@ -28953,15 +29682,23 @@ class VoiceAI {
28953
29682
  this.agentParticipantId = null;
28954
29683
  this.audioLevelInterval = null;
28955
29684
  this.apiKey = config.apiKey || '';
29685
+ this.authToken = config.authToken;
29686
+ this.getAuthToken = config.getAuthToken;
28956
29687
  this.apiUrl = config.apiUrl || DEFAULT_API_URL;
28957
- // Initialize API clients when apiKey is provided
28958
- if (this.apiKey) {
28959
- const clientConfig = { apiKey: this.apiKey, apiUrl: this.apiUrl };
29688
+ // Initialize API clients when auth is provided
29689
+ if (this.apiKey || this.authToken || this.getAuthToken) {
29690
+ const clientConfig = {
29691
+ apiKey: this.apiKey,
29692
+ authToken: this.authToken,
29693
+ getAuthToken: this.getAuthToken,
29694
+ apiUrl: this.apiUrl,
29695
+ };
28960
29696
  this._agents = new AgentClient(clientConfig);
28961
29697
  this._analytics = new AnalyticsClient(clientConfig);
28962
29698
  this._knowledgeBase = new KnowledgeBaseClient(clientConfig);
28963
29699
  this._phoneNumbers = new PhoneNumberClient(clientConfig);
28964
29700
  this._tts = new TTSClient(clientConfig);
29701
+ this._managedTools = new ManagedToolsClient(clientConfig);
28965
29702
  }
28966
29703
  }
28967
29704
  // ==========================================================================
@@ -29307,11 +30044,11 @@ class VoiceAI {
29307
30044
  if (this.cachedConnectionDetails && !this.isTokenExpired(this.cachedConnectionDetails.participantToken)) {
29308
30045
  return this.cachedConnectionDetails;
29309
30046
  }
29310
- // Mode 2: Direct API call with API key (constructor or per-call)
29311
- if (!this.apiKey && !options.apiKey) {
30047
+ // Mode 2: Direct API call with API key or bearer auth (constructor or per-call)
30048
+ if (!(await this.resolveAuthToken(options))) {
29312
30049
  throw new Error('No authentication method configured. Use one of:\n' +
29313
30050
  '1. connectRoom() with pre-fetched details from your backend\n' +
29314
- '2. API key: new VoiceAI({ apiKey: "vk_..." }) or connect({ apiKey: "vk_..." })');
30051
+ '2. API key or auth token: new VoiceAI({ apiKey: "vk_..." }) / new VoiceAI({ authToken: "..." })');
29315
30052
  }
29316
30053
  const connectionDetails = await this.fetchConnectionDetails(options);
29317
30054
  this.cachedConnectionDetails = connectionDetails;
@@ -29326,11 +30063,28 @@ class VoiceAI {
29326
30063
  * Used by the public getConnectionDetails() method.
29327
30064
  */
29328
30065
  async fetchConnectionDetails(options) {
29329
- if (!this.apiKey && !options.apiKey) {
29330
- throw new Error('API key is required for getConnectionDetails(). Pass { apiKey: "vk_..." } to the constructor or options.');
30066
+ if (!(await this.resolveAuthToken(options))) {
30067
+ throw new Error('API key is required for getConnectionDetails(), or pass authToken/getAuthToken to the constructor.');
29331
30068
  }
29332
30069
  return this.fetchConnectionDetailsFromApi(options);
29333
30070
  }
30071
+ async resolveAuthToken(options = {}) {
30072
+ if (options.apiKey)
30073
+ return options.apiKey;
30074
+ if (options.authToken)
30075
+ return options.authToken;
30076
+ if (this.apiKey)
30077
+ return this.apiKey;
30078
+ if (this.authToken)
30079
+ return this.authToken;
30080
+ if (this.getAuthToken) {
30081
+ const resolvedToken = await this.getAuthToken();
30082
+ if (typeof resolvedToken === 'string' && resolvedToken.trim()) {
30083
+ return resolvedToken;
30084
+ }
30085
+ }
30086
+ return '';
30087
+ }
29334
30088
  async fetchConnectionDetailsFromApi(options) {
29335
30089
  const url = options.apiUrl || this.apiUrl;
29336
30090
  // Use test-connection-details for testMode (allows testing paused/undeployed agents)
@@ -29347,13 +30101,13 @@ class VoiceAI {
29347
30101
  if (options.dynamicVariables) {
29348
30102
  requestData.dynamic_variables = options.dynamicVariables;
29349
30103
  }
29350
- const apiKey = options.apiKey || this.apiKey;
29351
- this.effectiveApiKey = apiKey;
30104
+ const authToken = await this.resolveAuthToken(options);
30105
+ this.effectiveApiKey = authToken;
29352
30106
  const response = await fetch(endpoint, {
29353
30107
  method: 'POST',
29354
30108
  headers: {
29355
30109
  'Content-Type': 'application/json',
29356
- 'Authorization': `Bearer ${apiKey}`
30110
+ 'Authorization': `Bearer ${authToken}`
29357
30111
  },
29358
30112
  body: JSON.stringify(requestData)
29359
30113
  });
@@ -29627,5 +30381,5 @@ function generateOptimalAudioOptions(options) {
29627
30381
  return { ...optimalOptions, ...options };
29628
30382
  }
29629
30383
 
29630
- export { VoiceAI, VoiceAIError, VoiceAgentWidget, VoiceAI as default, generateOptimalAudioOptions };
30384
+ export { GOOGLE_CALENDAR_OPERATION_OPTIONS, GOOGLE_CALENDAR_SCOPE, GOOGLE_GMAIL_OPERATION_OPTIONS, GOOGLE_GMAIL_READ_SCOPE, GOOGLE_GMAIL_SEND_SCOPE, GOOGLE_IDENTITY_SCOPES, GOOGLE_MANAGED_OPERATION_OPTIONS, GOOGLE_SHEETS_OPERATION_OPTIONS, GOOGLE_SHEETS_SCOPE, IANA_TIMEZONE_OPTIONS, VoiceAI, VoiceAIError, VoiceAgentWidget, VoiceAI as default, generateOptimalAudioOptions, getGoogleReconnectState, getManagedToolSelectedOperations, getMissingGoogleScopes, getMissingGoogleScopesForManagedTools, getRequiredGoogleScopes, hasEnabledGoogleManagedTools, isGoogleReconnectRequired, toggleManagedToolOperation };
29631
30385
  //# sourceMappingURL=index.esm.js.map