@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.
@@ -1309,54 +1309,14 @@ registerProcessor('audio_processor', AudioProcessor);
1309
1309
  * @returns {Promise<true>}
1310
1310
  */
1311
1311
  async requestPermission() {
1312
- const ensureUserMediaAccess = async () => {
1313
- const stream = await navigator.mediaDevices.getUserMedia({
1314
- audio: true,
1315
- });
1316
- const tracks = stream.getTracks();
1317
- tracks.forEach((track) => track.stop());
1318
- };
1319
-
1320
- const permissionsUnsupported =
1321
- !navigator.permissions ||
1322
- typeof navigator.permissions.query !== 'function';
1323
-
1324
- if (permissionsUnsupported) {
1325
- try {
1326
- await ensureUserMediaAccess();
1327
- } catch (error) {
1328
- window.alert('You must grant microphone access to use this feature.');
1329
- throw error;
1330
- }
1331
- return true;
1332
- }
1333
-
1334
1312
  try {
1335
- const permissionStatus = await navigator.permissions.query({
1336
- name: 'microphone',
1313
+ console.log('ensureUserMediaAccess');
1314
+ await navigator.mediaDevices.getUserMedia({
1315
+ audio: true,
1337
1316
  });
1338
-
1339
- if (permissionStatus.state === 'denied') {
1340
- window.alert('You must grant microphone access to use this feature.');
1341
- return true;
1342
- }
1343
-
1344
- if (permissionStatus.state === 'prompt') {
1345
- try {
1346
- await ensureUserMediaAccess();
1347
- } catch (error) {
1348
- window.alert('You must grant microphone access to use this feature.');
1349
- throw error;
1350
- }
1351
- }
1352
- } catch (error) {
1353
- // Firefox rejects permissions.query with NotSupportedError – fall back to getUserMedia directly
1354
- try {
1355
- await ensureUserMediaAccess();
1356
- } catch (fallbackError) {
1357
- window.alert('You must grant microphone access to use this feature.');
1358
- throw fallbackError;
1359
- }
1317
+ } catch (fallbackError) {
1318
+ window.alert('You must grant microphone access to use this feature.');
1319
+ throw fallbackError;
1360
1320
  }
1361
1321
  return true;
1362
1322
  }
@@ -1373,10 +1333,9 @@ registerProcessor('audio_processor', AudioProcessor);
1373
1333
  throw new Error('Could not request user devices');
1374
1334
  }
1375
1335
  await this.requestPermission();
1336
+
1376
1337
  const devices = await navigator.mediaDevices.enumerateDevices();
1377
- const audioDevices = devices.filter(
1378
- (device) => device.kind === 'audioinput',
1379
- );
1338
+ const audioDevices = devices.filter((device) => device.kind === 'audioinput');
1380
1339
  const defaultDeviceIndex = audioDevices.findIndex(
1381
1340
  (device) => device.deviceId === 'default',
1382
1341
  );
@@ -3661,7 +3620,7 @@ registerProcessor('audio_processor', AudioProcessor);
3661
3620
  const NOOP = () => { };
3662
3621
  const DEFAULT_WS_URL = 'wss://api.layercode.com/v1/agents/web/websocket';
3663
3622
  // SDK version - updated when publishing
3664
- const SDK_VERSION = '2.1.3';
3623
+ const SDK_VERSION = '2.2.0';
3665
3624
  /**
3666
3625
  * @class LayercodeClient
3667
3626
  * @classdesc Core client for Layercode audio agent that manages audio recording, WebSocket communication, and speech processing.
@@ -3672,7 +3631,7 @@ registerProcessor('audio_processor', AudioProcessor);
3672
3631
  * @param {Object} options - Configuration options
3673
3632
  */
3674
3633
  constructor(options) {
3675
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
3634
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
3676
3635
  this.deviceId = null;
3677
3636
  this.options = {
3678
3637
  agentId: options.agentId,
@@ -3684,13 +3643,14 @@ registerProcessor('audio_processor', AudioProcessor);
3684
3643
  onDisconnect: (_e = options.onDisconnect) !== null && _e !== void 0 ? _e : NOOP,
3685
3644
  onError: (_f = options.onError) !== null && _f !== void 0 ? _f : NOOP,
3686
3645
  onDeviceSwitched: (_g = options.onDeviceSwitched) !== null && _g !== void 0 ? _g : NOOP,
3687
- onDataMessage: (_h = options.onDataMessage) !== null && _h !== void 0 ? _h : NOOP,
3688
- onMessage: (_j = options.onMessage) !== null && _j !== void 0 ? _j : NOOP,
3689
- onUserAmplitudeChange: (_k = options.onUserAmplitudeChange) !== null && _k !== void 0 ? _k : NOOP,
3690
- onAgentAmplitudeChange: (_l = options.onAgentAmplitudeChange) !== null && _l !== void 0 ? _l : NOOP,
3691
- onStatusChange: (_m = options.onStatusChange) !== null && _m !== void 0 ? _m : NOOP,
3692
- onUserIsSpeakingChange: (_o = options.onUserIsSpeakingChange) !== null && _o !== void 0 ? _o : NOOP,
3693
- onMuteStateChange: (_p = options.onMuteStateChange) !== null && _p !== void 0 ? _p : NOOP,
3646
+ onDevicesChanged: (_h = options.onDevicesChanged) !== null && _h !== void 0 ? _h : NOOP,
3647
+ onDataMessage: (_j = options.onDataMessage) !== null && _j !== void 0 ? _j : NOOP,
3648
+ onMessage: (_k = options.onMessage) !== null && _k !== void 0 ? _k : NOOP,
3649
+ onUserAmplitudeChange: (_l = options.onUserAmplitudeChange) !== null && _l !== void 0 ? _l : NOOP,
3650
+ onAgentAmplitudeChange: (_m = options.onAgentAmplitudeChange) !== null && _m !== void 0 ? _m : NOOP,
3651
+ onStatusChange: (_o = options.onStatusChange) !== null && _o !== void 0 ? _o : NOOP,
3652
+ onUserIsSpeakingChange: (_p = options.onUserIsSpeakingChange) !== null && _p !== void 0 ? _p : NOOP,
3653
+ onMuteStateChange: (_q = options.onMuteStateChange) !== null && _q !== void 0 ? _q : NOOP,
3694
3654
  };
3695
3655
  this.AMPLITUDE_MONITORING_SAMPLE_RATE = 2;
3696
3656
  this._websocketUrl = DEFAULT_WS_URL;
@@ -3848,6 +3808,10 @@ registerProcessor('audio_processor', AudioProcessor);
3848
3808
  this._wsSend({ type: 'trigger.turn.end', role: 'user' });
3849
3809
  }
3850
3810
  }
3811
+ async sendClientResponseText(text) {
3812
+ await this._clientInterruptAssistantReplay();
3813
+ this._wsSend({ type: 'client.response.text', content: text });
3814
+ }
3851
3815
  /**
3852
3816
  * Handles incoming WebSocket messages
3853
3817
  * @param {MessageEvent} event - The WebSocket message event
@@ -4032,7 +3996,6 @@ registerProcessor('audio_processor', AudioProcessor);
4032
3996
  // Reset turn tracking for clean start
4033
3997
  this._resetTurnTracking();
4034
3998
  this._stopAmplitudeMonitoring();
4035
- this._setupDeviceChangeListener();
4036
3999
  // Get conversation key from server
4037
4000
  let authorizeSessionRequestBody = {
4038
4001
  agent_id: this.options.agentId,
@@ -4056,6 +4019,8 @@ registerProcessor('audio_processor', AudioProcessor);
4056
4019
  const authorizeSessionResponseBody = await authorizeSessionResponse.json();
4057
4020
  this.conversationId = authorizeSessionResponseBody.conversation_id; // Save the conversation_id for use in future reconnects
4058
4021
  this.options.conversationId = this.conversationId;
4022
+ await this.wavRecorder.requestPermission();
4023
+ this._setupDeviceChangeListener();
4059
4024
  // Connect WebSocket
4060
4025
  this.ws = new WebSocket(`${this._websocketUrl}?${new URLSearchParams({
4061
4026
  client_session_key: authorizeSessionResponseBody.client_session_key,
@@ -4108,7 +4073,6 @@ registerProcessor('audio_processor', AudioProcessor);
4108
4073
  console.error('Error connecting to Layercode agent:', error);
4109
4074
  this._setStatus('error');
4110
4075
  this.options.onError(error instanceof Error ? error : new Error(String(error)));
4111
- throw error;
4112
4076
  }
4113
4077
  }
4114
4078
  _resetTurnTracking() {
@@ -4136,6 +4100,13 @@ registerProcessor('audio_processor', AudioProcessor);
4136
4100
  getStream() {
4137
4101
  return this.wavRecorder.getStream();
4138
4102
  }
4103
+ /**
4104
+ * List all available audio input devices
4105
+ * @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}
4106
+ */
4107
+ async listDevices() {
4108
+ return this.wavRecorder.listDevices();
4109
+ }
4139
4110
  /**
4140
4111
  * Switches the input device for the microphone and restarts recording
4141
4112
  * @param {string} deviceId - The deviceId of the new microphone
@@ -4228,6 +4199,8 @@ registerProcessor('audio_processor', AudioProcessor);
4228
4199
  if (!this.deviceChangeListener) {
4229
4200
  this.deviceChangeListener = async (devices) => {
4230
4201
  try {
4202
+ // Notify user that devices have changed
4203
+ this.options.onDevicesChanged(devices);
4231
4204
  const defaultDevice = devices.find((device) => device.default);
4232
4205
  const usingDefaultDevice = this.useSystemDefaultDevice;
4233
4206
  const previousDefaultDeviceKey = this.lastKnownSystemDefaultDeviceKey;