@layercode/js-sdk 2.1.6 → 2.2.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/README.md CHANGED
@@ -16,4 +16,4 @@ Or you can use a script tag in your HTML:
16
16
 
17
17
  ## Usage
18
18
 
19
- Read the docs: [https://docs.layercode.com/sdk-reference/vanilla_js_sdk](https://docs.layercode.com/sdk-reference/vanilla_js_sdk)
19
+ Read the docs: [https://docs.layercode.com/sdk-reference/vanilla-js-sdk](https://docs.layercode.com/sdk-reference/vanilla-js-sdk)
@@ -1303,54 +1303,14 @@ class WavRecorder {
1303
1303
  * @returns {Promise<true>}
1304
1304
  */
1305
1305
  async requestPermission() {
1306
- const ensureUserMediaAccess = async () => {
1307
- const stream = await navigator.mediaDevices.getUserMedia({
1308
- audio: true,
1309
- });
1310
- const tracks = stream.getTracks();
1311
- tracks.forEach((track) => track.stop());
1312
- };
1313
-
1314
- const permissionsUnsupported =
1315
- !navigator.permissions ||
1316
- typeof navigator.permissions.query !== 'function';
1317
-
1318
- if (permissionsUnsupported) {
1319
- try {
1320
- await ensureUserMediaAccess();
1321
- } catch (error) {
1322
- window.alert('You must grant microphone access to use this feature.');
1323
- throw error;
1324
- }
1325
- return true;
1326
- }
1327
-
1328
1306
  try {
1329
- const permissionStatus = await navigator.permissions.query({
1330
- name: 'microphone',
1307
+ console.log('ensureUserMediaAccess');
1308
+ await navigator.mediaDevices.getUserMedia({
1309
+ audio: true,
1331
1310
  });
1332
-
1333
- if (permissionStatus.state === 'denied') {
1334
- window.alert('You must grant microphone access to use this feature.');
1335
- return true;
1336
- }
1337
-
1338
- if (permissionStatus.state === 'prompt') {
1339
- try {
1340
- await ensureUserMediaAccess();
1341
- } catch (error) {
1342
- window.alert('You must grant microphone access to use this feature.');
1343
- throw error;
1344
- }
1345
- }
1346
- } catch (error) {
1347
- // Firefox rejects permissions.query with NotSupportedError – fall back to getUserMedia directly
1348
- try {
1349
- await ensureUserMediaAccess();
1350
- } catch (fallbackError) {
1351
- window.alert('You must grant microphone access to use this feature.');
1352
- throw fallbackError;
1353
- }
1311
+ } catch (fallbackError) {
1312
+ window.alert('You must grant microphone access to use this feature.');
1313
+ throw fallbackError;
1354
1314
  }
1355
1315
  return true;
1356
1316
  }
@@ -1367,10 +1327,9 @@ class WavRecorder {
1367
1327
  throw new Error('Could not request user devices');
1368
1328
  }
1369
1329
  await this.requestPermission();
1330
+
1370
1331
  const devices = await navigator.mediaDevices.enumerateDevices();
1371
- const audioDevices = devices.filter(
1372
- (device) => device.kind === 'audioinput',
1373
- );
1332
+ const audioDevices = devices.filter((device) => device.kind === 'audioinput');
1374
1333
  const defaultDeviceIndex = audioDevices.findIndex(
1375
1334
  (device) => device.deviceId === 'default',
1376
1335
  );
@@ -3655,7 +3614,7 @@ function arrayBufferToBase64(arrayBuffer) {
3655
3614
  const NOOP = () => { };
3656
3615
  const DEFAULT_WS_URL = 'wss://api.layercode.com/v1/agents/web/websocket';
3657
3616
  // SDK version - updated when publishing
3658
- const SDK_VERSION = '2.1.3';
3617
+ const SDK_VERSION = '2.2.0';
3659
3618
  /**
3660
3619
  * @class LayercodeClient
3661
3620
  * @classdesc Core client for Layercode audio agent that manages audio recording, WebSocket communication, and speech processing.
@@ -3666,7 +3625,7 @@ class LayercodeClient {
3666
3625
  * @param {Object} options - Configuration options
3667
3626
  */
3668
3627
  constructor(options) {
3669
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
3628
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
3670
3629
  this.deviceId = null;
3671
3630
  this.options = {
3672
3631
  agentId: options.agentId,
@@ -3678,13 +3637,14 @@ class LayercodeClient {
3678
3637
  onDisconnect: (_e = options.onDisconnect) !== null && _e !== void 0 ? _e : NOOP,
3679
3638
  onError: (_f = options.onError) !== null && _f !== void 0 ? _f : NOOP,
3680
3639
  onDeviceSwitched: (_g = options.onDeviceSwitched) !== null && _g !== void 0 ? _g : NOOP,
3681
- onDataMessage: (_h = options.onDataMessage) !== null && _h !== void 0 ? _h : NOOP,
3682
- onMessage: (_j = options.onMessage) !== null && _j !== void 0 ? _j : NOOP,
3683
- onUserAmplitudeChange: (_k = options.onUserAmplitudeChange) !== null && _k !== void 0 ? _k : NOOP,
3684
- onAgentAmplitudeChange: (_l = options.onAgentAmplitudeChange) !== null && _l !== void 0 ? _l : NOOP,
3685
- onStatusChange: (_m = options.onStatusChange) !== null && _m !== void 0 ? _m : NOOP,
3686
- onUserIsSpeakingChange: (_o = options.onUserIsSpeakingChange) !== null && _o !== void 0 ? _o : NOOP,
3687
- onMuteStateChange: (_p = options.onMuteStateChange) !== null && _p !== void 0 ? _p : NOOP,
3640
+ onDevicesChanged: (_h = options.onDevicesChanged) !== null && _h !== void 0 ? _h : NOOP,
3641
+ onDataMessage: (_j = options.onDataMessage) !== null && _j !== void 0 ? _j : NOOP,
3642
+ onMessage: (_k = options.onMessage) !== null && _k !== void 0 ? _k : NOOP,
3643
+ onUserAmplitudeChange: (_l = options.onUserAmplitudeChange) !== null && _l !== void 0 ? _l : NOOP,
3644
+ onAgentAmplitudeChange: (_m = options.onAgentAmplitudeChange) !== null && _m !== void 0 ? _m : NOOP,
3645
+ onStatusChange: (_o = options.onStatusChange) !== null && _o !== void 0 ? _o : NOOP,
3646
+ onUserIsSpeakingChange: (_p = options.onUserIsSpeakingChange) !== null && _p !== void 0 ? _p : NOOP,
3647
+ onMuteStateChange: (_q = options.onMuteStateChange) !== null && _q !== void 0 ? _q : NOOP,
3688
3648
  };
3689
3649
  this.AMPLITUDE_MONITORING_SAMPLE_RATE = 2;
3690
3650
  this._websocketUrl = DEFAULT_WS_URL;
@@ -3842,6 +3802,10 @@ class LayercodeClient {
3842
3802
  this._wsSend({ type: 'trigger.turn.end', role: 'user' });
3843
3803
  }
3844
3804
  }
3805
+ async sendClientResponseText(text) {
3806
+ await this._clientInterruptAssistantReplay();
3807
+ this._wsSend({ type: 'client.response.text', content: text });
3808
+ }
3845
3809
  /**
3846
3810
  * Handles incoming WebSocket messages
3847
3811
  * @param {MessageEvent} event - The WebSocket message event
@@ -4026,7 +3990,6 @@ class LayercodeClient {
4026
3990
  // Reset turn tracking for clean start
4027
3991
  this._resetTurnTracking();
4028
3992
  this._stopAmplitudeMonitoring();
4029
- this._setupDeviceChangeListener();
4030
3993
  // Get conversation key from server
4031
3994
  let authorizeSessionRequestBody = {
4032
3995
  agent_id: this.options.agentId,
@@ -4050,6 +4013,8 @@ class LayercodeClient {
4050
4013
  const authorizeSessionResponseBody = await authorizeSessionResponse.json();
4051
4014
  this.conversationId = authorizeSessionResponseBody.conversation_id; // Save the conversation_id for use in future reconnects
4052
4015
  this.options.conversationId = this.conversationId;
4016
+ await this.wavRecorder.requestPermission();
4017
+ this._setupDeviceChangeListener();
4053
4018
  // Connect WebSocket
4054
4019
  this.ws = new WebSocket(`${this._websocketUrl}?${new URLSearchParams({
4055
4020
  client_session_key: authorizeSessionResponseBody.client_session_key,
@@ -4102,7 +4067,6 @@ class LayercodeClient {
4102
4067
  console.error('Error connecting to Layercode agent:', error);
4103
4068
  this._setStatus('error');
4104
4069
  this.options.onError(error instanceof Error ? error : new Error(String(error)));
4105
- throw error;
4106
4070
  }
4107
4071
  }
4108
4072
  _resetTurnTracking() {
@@ -4130,6 +4094,13 @@ class LayercodeClient {
4130
4094
  getStream() {
4131
4095
  return this.wavRecorder.getStream();
4132
4096
  }
4097
+ /**
4098
+ * List all available audio input devices
4099
+ * @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}
4100
+ */
4101
+ async listDevices() {
4102
+ return this.wavRecorder.listDevices();
4103
+ }
4133
4104
  /**
4134
4105
  * Switches the input device for the microphone and restarts recording
4135
4106
  * @param {string} deviceId - The deviceId of the new microphone
@@ -4222,6 +4193,8 @@ class LayercodeClient {
4222
4193
  if (!this.deviceChangeListener) {
4223
4194
  this.deviceChangeListener = async (devices) => {
4224
4195
  try {
4196
+ // Notify user that devices have changed
4197
+ this.options.onDevicesChanged(devices);
4225
4198
  const defaultDevice = devices.find((device) => device.default);
4226
4199
  const usingDefaultDevice = this.useSystemDefaultDevice;
4227
4200
  const previousDefaultDeviceKey = this.lastKnownSystemDefaultDeviceKey;