@layercode/js-sdk 2.8.5 → 2.8.7

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.
@@ -5289,7 +5289,7 @@ registerProcessor('audio_processor', AudioProcessor);
5289
5289
  .join(',');
5290
5290
  const cb = async () => {
5291
5291
  let id = ++lastId;
5292
- const devices = await this.listDevices();
5292
+ const devices = await this.listDevices({ requestPermission: false });
5293
5293
  if (id === lastId) {
5294
5294
  if (serializeDevices(lastDevices) !== serializeDevices(devices)) {
5295
5295
  lastDevices = devices;
@@ -5342,14 +5342,22 @@ registerProcessor('audio_processor', AudioProcessor);
5342
5342
  }
5343
5343
 
5344
5344
  /**
5345
- * List all eligible devices for recording, will request permission to use microphone
5345
+ * List all eligible devices for recording.
5346
+ *
5347
+ * By default this will *not* request mic permission; labels may be empty until
5348
+ * the user has granted permission via begin()/getUserMedia.
5349
+ * Pass { requestPermission: true } to explicitly trigger a permission prompt.
5350
+ *
5351
+ * @param {{ requestPermission?: boolean }} [options]
5346
5352
  * @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}
5347
5353
  */
5348
- async listDevices() {
5354
+ async listDevices({ requestPermission = false } = {}) {
5349
5355
  if (!navigator.mediaDevices || !('enumerateDevices' in navigator.mediaDevices)) {
5350
5356
  throw new Error('Could not request user devices');
5351
5357
  }
5352
- await this.requestPermission();
5358
+ if (requestPermission) {
5359
+ await this.requestPermission();
5360
+ }
5353
5361
 
5354
5362
  const devices = await navigator.mediaDevices.enumerateDevices();
5355
5363
  const audioDevices = devices.filter((device) => device.kind === 'audioinput');
@@ -5399,7 +5407,7 @@ registerProcessor('audio_processor', AudioProcessor);
5399
5407
  config.audio.deviceId = { exact: deviceId };
5400
5408
  }
5401
5409
  this.stream = await navigator.mediaDevices.getUserMedia(config);
5402
- // Mark permission as granted so listDevices() won't call requestPermission() again
5410
+ // Mark permission as granted so requestPermission() can skip expensive redundant getUserMedia calls
5403
5411
  this._hasPermission = true;
5404
5412
  } catch (err) {
5405
5413
  throw err;
@@ -5752,6 +5760,29 @@ registerProcessor('audio_processor', AudioProcessor);
5752
5760
  /* eslint-env browser */
5753
5761
  // import { env as ortEnv } from 'onnxruntime-web';
5754
5762
  // @ts-ignore - VAD package does not provide TypeScript types
5763
+ /**
5764
+ * Layercode Web SDK notes / gotchas
5765
+ *
5766
+ * Modes:
5767
+ * - audioInput=false => text-only mode. Do NOT touch getUserMedia or request mic permissions.
5768
+ * - audioInput=true => capture/send mic audio (may trigger permission prompt).
5769
+ * - audioOutput=false => do not play audio, but MUST still send
5770
+ * `trigger.response.audio.replay_finished` once per assistant turn so the server can advance.
5771
+ *
5772
+ * Transcription:
5773
+ * - trigger='push_to_talk' => no VAD; only send audio between triggerUserTurnStarted/Finished.
5774
+ * - trigger='automatic' => VAD drives userSpeaking + optional audio gating.
5775
+ *
5776
+ * Performance:
5777
+ * - Mobile browsers e.g. iOS Safari media APIs can be slow; avoid multiple sequential getUserMedia calls.
5778
+ * This file starts the recorder first, then sets up device listeners.
5779
+ *
5780
+ * Compatibility:
5781
+ * - This is consumed by the Layercode React SDK; treat public API/event shape changes as breaking.
5782
+ *
5783
+ * Assets:
5784
+ * - VAD/ORT assets are loaded from assets.layercode.com (Cloudflare).
5785
+ */
5755
5786
  const NOOP = () => { };
5756
5787
  const DEFAULT_WS_URL = 'wss://api.layercode.com/v1/agents/web/websocket';
5757
5788
  // SDK version - updated when publishing
@@ -5769,12 +5800,12 @@ registerProcessor('audio_processor', AudioProcessor);
5769
5800
  }
5770
5801
  return cloned;
5771
5802
  };
5772
- const listAudioInputDevices = async () => {
5803
+ const listAudioInputDevices = async (options = {}) => {
5773
5804
  if (!hasMediaDevicesSupport()) {
5774
5805
  throw new Error('Media devices are not available in this environment');
5775
5806
  }
5776
5807
  const recorder = new WavRecorder({ sampleRate: DEFAULT_RECORDER_SAMPLE_RATE });
5777
- const devices = (await recorder.listDevices());
5808
+ const devices = (await recorder.listDevices({ requestPermission: Boolean(options.requestPermission) }));
5778
5809
  return devices.map(toLayercodeAudioInputDevice);
5779
5810
  };
5780
5811
  const watchAudioInputDevices = (callback) => {
@@ -6717,8 +6748,8 @@ registerProcessor('audio_processor', AudioProcessor);
6717
6748
  * List all available audio input devices
6718
6749
  * @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}
6719
6750
  */
6720
- async listDevices() {
6721
- return this.wavRecorder.listDevices();
6751
+ async listDevices(options) {
6752
+ return this.wavRecorder.listDevices(options);
6722
6753
  }
6723
6754
  /**
6724
6755
  * Switches the input device for the microphone and restarts recording