ttp-agent-sdk 2.48.12 → 2.48.14

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.
@@ -9170,7 +9170,7 @@ if (false) // removed by dead control flow
9170
9170
  /***/ ((module) => {
9171
9171
 
9172
9172
  "use strict";
9173
- module.exports = "/**\n * AudioProcessor - AudioWorklet for real-time audio processing\n * \n * This AudioWorklet processes audio data in real-time and sends it to the main thread\n * for transmission to the WebSocket server.\n */\n\nclass AudioProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n \n // Configuration\n this.config = options.processorOptions || {};\n // Use AudioContext sampleRate (available as global 'sampleRate' in AudioWorkletProcessor)\n // Fall back to config if sampleRate not available, default to 24 kHz to match typical server/TTS output\n this.sampleRate = typeof sampleRate !== 'undefined' ? sampleRate : (this.config.sampleRate || this.config.outputSampleRate || 24000);\n this.bufferSize = 128; // Process 128 samples at a time (256 bytes = 8ms at 16kHz)\n this.buffer = new Float32Array(this.bufferSize);\n this.bufferIndex = 0;\n \n // VAD (Voice Activity Detection) parameters\n this.silenceThreshold = 0.02; // RMS threshold for voice detection (increased to reduce ambient noise sensitivity)\n this.VOICE_FRAMES_REQUIRED = 2; // Require 2 consecutive frames above threshold before activating\n this.minVoiceDuration = 100; // ms - minimum speech duration\n this.pauseThreshold = 3000; // ms - longer pause before processing\n \n // VAD state\n this.isVoiceActive = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.silenceFramesThreshold = 5; // More frames needed for silence detection\n this.voiceFrameCount = 0; // Count consecutive frames above threshold\n \n // Audio quality tracking\n this.frameCount = 0;\n this.lastLogTime = 0;\n \n // Continuous recording mode\n this.continuousMode = true; // Always send audio when voice is detected\n this.forceContinuous = true; // Force continuous for toggle button behavior\n this.isCurrentlyStreaming = false; // Track if we're currently sending audio\n \n // Batching buffer\n this.sendBuffer = null;\n this.sendBufferBytes = 0;\n\n // Start true so the first process() call cannot terminate the worklet before\n // the main thread posts setForceContinuous (WebKit/iOS often renders audio before port messages are handled).\n this.isProcessing = true;\n \n // Handle messages from main thread\n this.port.onmessage = (event) => {\n const { type, data } = event.data;\n \n switch (type) {\n case 'start':\n this.isProcessing = true;\n this.isCurrentlyStreaming = true;\n break;\n \n case 'stop':\n this.isProcessing = false;\n this.isCurrentlyStreaming = false;\n this.isVoiceActive = false;\n this.forceContinuous = false;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Flush any remaining data\n this.flushBuffer();\n break;\n \n case 'setForceContinuous':\n this.forceContinuous = data.enabled;\n this.isProcessing = true;\n // iOS: bypass client VAD - always send. Desktop: keep VAD (saves bandwidth, reduces noise).\n this.isCurrentlyStreaming = data.enabled && (data.bypassVad === true);\n break;\n \n case 'flush':\n this.flushBuffer();\n break;\n \n case 'config':\n Object.assign(this.config, data);\n break;\n }\n };\n }\n \n /**\n * Process audio data\n */\n process(inputs, outputs, parameters) {\n // CRITICAL: If processing is stopped, terminate the processor\n if (!this.isProcessing) {\n // Return false to terminate the AudioWorklet processor\n // This stops all VAD processing immediately\n return false;\n }\n \n const input = inputs[0];\n const output = outputs[0];\n \n // Copy input to output (pass-through)\n if (input.length > 0 && output.length > 0) {\n output[0].set(input[0]);\n }\n \n // Process audio for PCM recording and VAD\n if (input.length > 0 && input[0].length > 0) {\n this.processAudioData(input[0]);\n }\n \n // Keep the processor alive\n return true;\n }\n \n processAudioData(audioData) {\n // CRITICAL: Early return if processing is stopped\n // This prevents VAD calculations and logging when stopped\n if (!this.isProcessing) {\n return;\n }\n \n this.frameCount++;\n \n // Process audio in consistent 128-sample chunks (256 bytes)\n for (let i = 0; i < audioData.length; i += this.bufferSize) {\n const chunkSize = Math.min(this.bufferSize, audioData.length - i);\n \n // Copy chunk to buffer\n for (let j = 0; j < chunkSize; j++) {\n this.buffer[j] = audioData[i + j];\n }\n \n // Pad with zeros if needed\n for (let j = chunkSize; j < this.bufferSize; j++) {\n this.buffer[j] = 0;\n }\n \n // Calculate RMS for VAD on this chunk\n let sum = 0;\n for (let j = 0; j < this.bufferSize; j++) {\n sum += this.buffer[j] * this.buffer[j];\n }\n const rms = Math.sqrt(sum / this.bufferSize);\n \n // Calculate additional features for better VAD\n let variation = 0;\n let highFreqCount = 0;\n for (let j = 1; j < this.bufferSize; j++) {\n const diff = Math.abs(this.buffer[j] - this.buffer[j-1]);\n variation += diff;\n if (diff > 0.1) highFreqCount++;\n }\n variation = variation / this.bufferSize;\n const highFreqRatio = highFreqCount / this.bufferSize;\n \n const currentTime = Date.now();\n \n // VAD with reduced sensitivity - require consecutive frames above threshold\n let hasVoice = rms > this.silenceThreshold;\n // Calculate time since last voice detection\n const timeSinceLastVoice = currentTime - this.lastVoiceTime;\n\n // Voice detection logic - require consecutive frames above threshold\n if (hasVoice) {\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount++; // Increment consecutive voice frame count\n\n // Only activate streaming after required consecutive frames\n if (this.voiceFrameCount >= this.VOICE_FRAMES_REQUIRED) {\n // Start voice if needed\n if (!this.isVoiceActive) {\n this.isVoiceActive = true;\n this.voiceStartTime = currentTime;\n this.isCurrentlyStreaming = true;\n // Log voice detection (every 50 frames = ~400ms to avoid spam)\n if (this.frameCount % 50 === 0) {\n console.log(`🎤 VAD: VOICE DETECTED (RMS: ${rms.toFixed(4)}, frames: ${this.voiceFrameCount})`);\n }\n }\n }\n\n this.lastVoiceTime = currentTime;\n } else {\n // Silence detected - reset voice frame count\n this.voiceFrameCount = 0;\n this.consecutiveSilenceFrames++;\n \n // In continuous mode, we still use VAD but require longer silence before stopping\n // In non-continuous mode, stop quickly\n const silenceThreshold = this.forceContinuous ? 3000 : 200; // 3s for continuous, 200ms otherwise\n \n // FIXED: Stop condition - also check isCurrentlyStreaming (not just isVoiceActive)\n // This handles case where setForceContinuous set streaming=true but no voice was detected yet\n if (!hasVoice && (this.isVoiceActive || this.isCurrentlyStreaming) && timeSinceLastVoice >= silenceThreshold) {\n this.isVoiceActive = false;\n this.isCurrentlyStreaming = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Log silence detection\n console.log(`🔇 VAD: SILENCE DETECTED (${timeSinceLastVoice}ms silence, RMS: ${rms.toFixed(4)})`);\n }\n }\n\n // Send PCM **only if streaming and processing** - hard gate\n // This ensures we only send audio when voice is detected, even in continuous mode\n if (this.isCurrentlyStreaming && this.isProcessing) {\n // Log occasionally when sending (every 200 frames = ~1.6 seconds to avoid spam)\n if (this.frameCount % 200 === 0) {\n console.log(`📤 VAD: Sending audio (isVoiceActive: ${this.isVoiceActive}, RMS: ${rms.toFixed(4)})`);\n }\n this.sendPCMAudioData(this.buffer);\n } else {\n // Log occasionally when blocking (every 200 frames)\n if (this.frameCount % 200 === 0 && this.isProcessing) {\n console.log(`🚫 VAD: Blocking audio (isCurrentlyStreaming: ${this.isCurrentlyStreaming}, RMS: ${rms.toFixed(4)})`);\n }\n }\n }\n }\n \n sendPCMAudioData(float32Data) {\n // Convert Float32Array (-1.0 to 1.0) to Int16Array (-32768 to 32767)\n const pcmData = new Int16Array(float32Data.length);\n \n for (let i = 0; i < float32Data.length; i++) {\n // Clamp and convert to 16-bit PCM\n const sample = Math.max(-1.0, Math.min(1.0, float32Data[i]));\n pcmData[i] = Math.round(sample * 32767);\n }\n \n // Initialize send buffer if not exists\n if (!this.sendBuffer) {\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n \n // Accumulate chunks in buffer\n this.sendBuffer.push(pcmData);\n this.sendBufferBytes += pcmData.byteLength;\n \n // Send in ~4 KB batches (≈128 ms of audio at 16kHz)\n // Use sliding window approach to maintain continuous flow\n while (this.sendBufferBytes >= 4096) {\n // Calculate how many chunks we need for ~4KB\n let chunksToSend = 0;\n let bytesToSend = 0;\n \n for (let i = 0; i < this.sendBuffer.length; i++) {\n const chunkBytes = this.sendBuffer[i].byteLength;\n if (bytesToSend + chunkBytes <= 4096) {\n chunksToSend++;\n bytesToSend += chunkBytes;\n } else {\n break;\n }\n }\n\n // Create merged buffer from selected chunks\n const chunksForBatch = this.sendBuffer.slice(0, chunksToSend);\n const totalSamples = chunksForBatch.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of chunksForBatch) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send batched PCM data to main thread\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: chunksToSend,\n totalBytes: merged.byteLength\n });\n \n // Remove sent chunks from buffer (sliding window)\n this.sendBuffer = this.sendBuffer.slice(chunksToSend);\n this.sendBufferBytes -= bytesToSend;\n }\n }\n \n // Flush any remaining buffered data\n flushBuffer() {\n if (this.sendBuffer && this.sendBuffer.length > 0) {\n // Merge remaining chunks\n const totalSamples = this.sendBuffer.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of this.sendBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send remaining data\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: this.sendBuffer.length,\n totalBytes: merged.byteLength,\n isFlush: true\n });\n \n // Reset buffer\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n }\n}\n\n// Register the processor\nregisterProcessor('audio-processor', AudioProcessor);\n";
9173
+ module.exports = "/**\n * AudioProcessor - AudioWorklet for real-time audio processing\n * \n * This AudioWorklet processes audio data in real-time and sends it to the main thread\n * for transmission to the WebSocket server.\n */\n\nclass AudioProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n \n // Configuration\n this.config = options.processorOptions || {};\n // Use AudioContext sampleRate (available as global 'sampleRate' in AudioWorkletProcessor)\n // Fall back to config if sampleRate not available, default to 24 kHz to match typical server/TTS output\n this.sampleRate = typeof sampleRate !== 'undefined' ? sampleRate : (this.config.sampleRate || this.config.outputSampleRate || 24000);\n this.bufferSize = 128; // Process 128 samples at a time (256 bytes = 8ms at 16kHz)\n this.buffer = new Float32Array(this.bufferSize);\n this.bufferIndex = 0;\n \n // VAD (Voice Activity Detection) parameters\n this.silenceThreshold = 0.02; // Base RMS threshold for voice detection (scaled by micSensitivity below)\n this.VOICE_FRAMES_REQUIRED = 2; // Require 2 consecutive frames above threshold before activating\n this.minVoiceDuration = 100; // ms - minimum speech duration\n this.pauseThreshold = 3000; // ms - longer pause before processing\n\n // Mic sensitivity: user/agent-adjustable multiplier on the VAD energy gate.\n // 1.0 = default; 2.0 = twice as sensitive (half the RMS floor); 0.5 = half as\n // sensitive (double the floor — only close/loud speech triggers, e.g. TV in the room).\n this.micSensitivity = (typeof this.config.micSensitivity === 'number' && this.config.micSensitivity > 0)\n ? this.config.micSensitivity : 1.0;\n // While agent audio plays, the gate rises by this factor (stricter barge-in: background\n // TV/chatter must not interrupt playback). The SDK main thread tracks playback state.\n this.playbackActive = false;\n this.PLAYBACK_GATE_FACTOR = 1.6;\n // Hysteresis: once voice is active the floor drops, so a borderline signal isn't\n // chopped on every frame (quiet speech tails stay under the trigger level).\n this.HYSTERESIS_FACTOR = 0.75;\n \n // VAD state\n this.isVoiceActive = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.silenceFramesThreshold = 5; // More frames needed for silence detection\n this.voiceFrameCount = 0; // Count consecutive frames above threshold\n \n // Audio quality tracking\n this.frameCount = 0;\n this.lastLogTime = 0;\n \n // Continuous recording mode\n this.continuousMode = true; // Always send audio when voice is detected\n this.forceContinuous = true; // Force continuous for toggle button behavior\n this.isCurrentlyStreaming = false; // Track if we're currently sending audio\n \n // Batching buffer\n this.sendBuffer = null;\n this.sendBufferBytes = 0;\n\n // Start true so the first process() call cannot terminate the worklet before\n // the main thread posts setForceContinuous (WebKit/iOS often renders audio before port messages are handled).\n this.isProcessing = true;\n \n // Handle messages from main thread\n this.port.onmessage = (event) => {\n const { type, data } = event.data;\n \n switch (type) {\n case 'start':\n this.isProcessing = true;\n this.isCurrentlyStreaming = true;\n break;\n \n case 'stop':\n this.isProcessing = false;\n this.isCurrentlyStreaming = false;\n this.isVoiceActive = false;\n this.forceContinuous = false;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Flush any remaining data\n this.flushBuffer();\n break;\n \n case 'setForceContinuous':\n this.forceContinuous = data.enabled;\n this.isProcessing = true;\n // iOS: bypass client VAD - always send. Desktop: keep VAD (saves bandwidth, reduces noise).\n this.isCurrentlyStreaming = data.enabled && (data.bypassVad === true);\n break;\n\n case 'setMicSensitivity': {\n const v = Number(data && data.value);\n if (isFinite(v) && v > 0) {\n this.micSensitivity = Math.max(0.25, Math.min(4.0, v));\n }\n break;\n }\n\n case 'setPlaybackActive':\n this.playbackActive = !!(data && data.active);\n break;\n \n case 'flush':\n this.flushBuffer();\n break;\n \n case 'config':\n Object.assign(this.config, data);\n break;\n }\n };\n }\n \n /**\n * Process audio data\n */\n process(inputs, outputs, parameters) {\n // CRITICAL: If processing is stopped, terminate the processor\n if (!this.isProcessing) {\n // Return false to terminate the AudioWorklet processor\n // This stops all VAD processing immediately\n return false;\n }\n \n const input = inputs[0];\n const output = outputs[0];\n \n // Copy input to output (pass-through)\n if (input.length > 0 && output.length > 0) {\n output[0].set(input[0]);\n }\n \n // Process audio for PCM recording and VAD\n if (input.length > 0 && input[0].length > 0) {\n this.processAudioData(input[0]);\n }\n \n // Keep the processor alive\n return true;\n }\n \n processAudioData(audioData) {\n // CRITICAL: Early return if processing is stopped\n // This prevents VAD calculations and logging when stopped\n if (!this.isProcessing) {\n return;\n }\n \n this.frameCount++;\n \n // Process audio in consistent 128-sample chunks (256 bytes)\n for (let i = 0; i < audioData.length; i += this.bufferSize) {\n const chunkSize = Math.min(this.bufferSize, audioData.length - i);\n \n // Copy chunk to buffer\n for (let j = 0; j < chunkSize; j++) {\n this.buffer[j] = audioData[i + j];\n }\n \n // Pad with zeros if needed\n for (let j = chunkSize; j < this.bufferSize; j++) {\n this.buffer[j] = 0;\n }\n \n // Calculate RMS for VAD on this chunk\n let sum = 0;\n for (let j = 0; j < this.bufferSize; j++) {\n sum += this.buffer[j] * this.buffer[j];\n }\n const rms = Math.sqrt(sum / this.bufferSize);\n \n // Calculate additional features for better VAD\n let variation = 0;\n let highFreqCount = 0;\n for (let j = 1; j < this.bufferSize; j++) {\n const diff = Math.abs(this.buffer[j] - this.buffer[j-1]);\n variation += diff;\n if (diff > 0.1) highFreqCount++;\n }\n variation = variation / this.bufferSize;\n const highFreqRatio = highFreqCount / this.bufferSize;\n \n const currentTime = Date.now();\n\n // Effective VAD floor: base threshold scaled by the mic-sensitivity multiplier,\n // raised during agent playback (stricter barge-in), lowered while voice is\n // already active (hysteresis, so speech tails aren't clipped).\n let effectiveThreshold = this.silenceThreshold / this.micSensitivity;\n if (this.playbackActive) effectiveThreshold *= this.PLAYBACK_GATE_FACTOR;\n if (this.isVoiceActive) effectiveThreshold *= this.HYSTERESIS_FACTOR;\n\n // VAD with reduced sensitivity - require consecutive frames above threshold\n let hasVoice = rms > effectiveThreshold;\n // Calculate time since last voice detection\n const timeSinceLastVoice = currentTime - this.lastVoiceTime;\n\n // Voice detection logic - require consecutive frames above threshold\n if (hasVoice) {\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount++; // Increment consecutive voice frame count\n\n // Only activate streaming after required consecutive frames\n if (this.voiceFrameCount >= this.VOICE_FRAMES_REQUIRED) {\n // Start voice if needed\n if (!this.isVoiceActive) {\n this.isVoiceActive = true;\n this.voiceStartTime = currentTime;\n this.isCurrentlyStreaming = true;\n // Log voice detection (every 50 frames = ~400ms to avoid spam)\n if (this.frameCount % 50 === 0) {\n console.log(`🎤 VAD: VOICE DETECTED (RMS: ${rms.toFixed(4)}, frames: ${this.voiceFrameCount})`);\n }\n }\n }\n\n this.lastVoiceTime = currentTime;\n } else {\n // Silence detected - reset voice frame count\n this.voiceFrameCount = 0;\n this.consecutiveSilenceFrames++;\n \n // In continuous mode, we still use VAD but require longer silence before stopping\n // In non-continuous mode, stop quickly\n const silenceThreshold = this.forceContinuous ? 3000 : 200; // 3s for continuous, 200ms otherwise\n \n // FIXED: Stop condition - also check isCurrentlyStreaming (not just isVoiceActive)\n // This handles case where setForceContinuous set streaming=true but no voice was detected yet\n if (!hasVoice && (this.isVoiceActive || this.isCurrentlyStreaming) && timeSinceLastVoice >= silenceThreshold) {\n this.isVoiceActive = false;\n this.isCurrentlyStreaming = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Log silence detection\n console.log(`🔇 VAD: SILENCE DETECTED (${timeSinceLastVoice}ms silence, RMS: ${rms.toFixed(4)})`);\n }\n }\n\n // Send PCM **only if streaming and processing** - hard gate\n // This ensures we only send audio when voice is detected, even in continuous mode\n if (this.isCurrentlyStreaming && this.isProcessing) {\n // Log occasionally when sending (every 200 frames = ~1.6 seconds to avoid spam)\n if (this.frameCount % 200 === 0) {\n console.log(`📤 VAD: Sending audio (isVoiceActive: ${this.isVoiceActive}, RMS: ${rms.toFixed(4)})`);\n }\n this.sendPCMAudioData(this.buffer);\n } else {\n // Log occasionally when blocking (every 200 frames)\n if (this.frameCount % 200 === 0 && this.isProcessing) {\n console.log(`🚫 VAD: Blocking audio (isCurrentlyStreaming: ${this.isCurrentlyStreaming}, RMS: ${rms.toFixed(4)})`);\n }\n }\n }\n }\n \n sendPCMAudioData(float32Data) {\n // Convert Float32Array (-1.0 to 1.0) to Int16Array (-32768 to 32767)\n const pcmData = new Int16Array(float32Data.length);\n \n for (let i = 0; i < float32Data.length; i++) {\n // Clamp and convert to 16-bit PCM\n const sample = Math.max(-1.0, Math.min(1.0, float32Data[i]));\n pcmData[i] = Math.round(sample * 32767);\n }\n \n // Initialize send buffer if not exists\n if (!this.sendBuffer) {\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n \n // Accumulate chunks in buffer\n this.sendBuffer.push(pcmData);\n this.sendBufferBytes += pcmData.byteLength;\n \n // Send in ~4 KB batches (≈128 ms of audio at 16kHz)\n // Use sliding window approach to maintain continuous flow\n while (this.sendBufferBytes >= 4096) {\n // Calculate how many chunks we need for ~4KB\n let chunksToSend = 0;\n let bytesToSend = 0;\n \n for (let i = 0; i < this.sendBuffer.length; i++) {\n const chunkBytes = this.sendBuffer[i].byteLength;\n if (bytesToSend + chunkBytes <= 4096) {\n chunksToSend++;\n bytesToSend += chunkBytes;\n } else {\n break;\n }\n }\n\n // Create merged buffer from selected chunks\n const chunksForBatch = this.sendBuffer.slice(0, chunksToSend);\n const totalSamples = chunksForBatch.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of chunksForBatch) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send batched PCM data to main thread\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: chunksToSend,\n totalBytes: merged.byteLength\n });\n \n // Remove sent chunks from buffer (sliding window)\n this.sendBuffer = this.sendBuffer.slice(chunksToSend);\n this.sendBufferBytes -= bytesToSend;\n }\n }\n \n // Flush any remaining buffered data\n flushBuffer() {\n if (this.sendBuffer && this.sendBuffer.length > 0) {\n // Merge remaining chunks\n const totalSamples = this.sendBuffer.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of this.sendBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send remaining data\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: this.sendBuffer.length,\n totalBytes: merged.byteLength,\n isFlush: true\n });\n \n // Reset buffer\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n }\n}\n\n// Register the processor\nregisterProcessor('audio-processor', AudioProcessor);\n";
9174
9174
 
9175
9175
  /***/ }),
9176
9176
 
@@ -10211,7 +10211,8 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
10211
10211
  this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audio-processor', {
10212
10212
  processorOptions: {
10213
10213
  sampleRate: sampleRate,
10214
- outputSampleRate: this.config.outputSampleRate
10214
+ outputSampleRate: this.config.outputSampleRate,
10215
+ micSensitivity: this.config.micSensitivity || 1.0
10215
10216
  }
10216
10217
  });
10217
10218
 
@@ -10333,9 +10334,48 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
10333
10334
  return start;
10334
10335
  }()
10335
10336
  /**
10336
- * Stop audio recording
10337
+ * Mic sensitivity (0.5–2.0, 1.0 = default). Scales the worklet's VAD energy floor:
10338
+ * lower values make the mic LESS trigger-happy (background TV/chatter stops opening
10339
+ * the gate); higher values pick up quieter speech. Applies live when recording and
10340
+ * is carried into processorOptions on the next start().
10337
10341
  */
10338
10342
  )
10343
+ }, {
10344
+ key: "setMicSensitivity",
10345
+ value: function setMicSensitivity(value) {
10346
+ var v = Number(value);
10347
+ var clamped = isFinite(v) && v > 0 ? Math.max(0.25, Math.min(4.0, v)) : 1.0;
10348
+ this.config.micSensitivity = clamped;
10349
+ if (this.audioWorkletNode) {
10350
+ this.audioWorkletNode.port.postMessage({
10351
+ type: 'setMicSensitivity',
10352
+ data: {
10353
+ value: clamped
10354
+ }
10355
+ });
10356
+ }
10357
+ }
10358
+
10359
+ /**
10360
+ * Tell the worklet whether agent audio is currently playing. While playing, the VAD
10361
+ * floor rises (stricter barge-in) so room noise doesn't interrupt the agent.
10362
+ */
10363
+ }, {
10364
+ key: "setPlaybackActive",
10365
+ value: function setPlaybackActive(active) {
10366
+ if (this.audioWorkletNode) {
10367
+ this.audioWorkletNode.port.postMessage({
10368
+ type: 'setPlaybackActive',
10369
+ data: {
10370
+ active: active === true
10371
+ }
10372
+ });
10373
+ }
10374
+ }
10375
+
10376
+ /**
10377
+ * Stop audio recording
10378
+ */
10339
10379
  }, {
10340
10380
  key: "stop",
10341
10381
  value: (function () {
@@ -12047,7 +12087,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
12047
12087
 
12048
12088
  // SDK build time for debugging
12049
12089
  if (true) {
12050
- helloMessage.lastBuildTime = "2026-08-21T00:00:00.470Z";
12090
+ helloMessage.lastBuildTime = "2026-08-21T10:23:43.383Z";
12051
12091
  }
12052
12092
  try {
12053
12093
  this.ws.send(JSON.stringify(helloMessage));
@@ -21357,8 +21397,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
21357
21397
 
21358
21398
 
21359
21399
  // Version - injected at build time from package.json via webpack DefinePlugin
21360
- var VERSION = "2.48.12";
21361
- var BUILD_TIME = "2026-08-21T00:00:00.470Z";
21400
+ var VERSION = "2.48.14";
21401
+ var BUILD_TIME = "2026-08-21T10:23:43.383Z";
21362
21402
  console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "), 'background: #4f46e5; color: white; font-size: 12px; font-weight: bold; padding: 2px 6px; border-radius: 4px;');
21363
21403
 
21364
21404
  // Named exports
@@ -28480,6 +28520,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28480
28520
  _this._playbackGateFlushing = false;
28481
28521
  _this.isRecording = false;
28482
28522
  _this.isPlaying = false;
28523
+
28524
+ /**
28525
+ * Mic sensitivity (VAD energy-gate multiplier, 0.5–2.0, 1.0 = default).
28526
+ * _agentMicSensitivity comes from hello_ack (agent configuration default);
28527
+ * _micSensitivity is the effective value actually applied to the recorder —
28528
+ * a user override from the widget settings popover wins over the agent default.
28529
+ */
28530
+ _this._agentMicSensitivity = 1.0;
28531
+ _this._micSensitivity = 1.0;
28483
28532
  _this.isDestroyed = false;
28484
28533
  _this.isPaused = false;
28485
28534
  _this.outputAudioFormat = null;
@@ -28902,6 +28951,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28902
28951
  var _this3 = this;
28903
28952
  this.audioPlayer.on('playbackStarted', function (info) {
28904
28953
  _this3.isPlaying = true;
28954
+
28955
+ // Raise the mic VAD floor while the agent speaks (stricter barge-in gate —
28956
+ // room noise / TV must not interrupt playback)
28957
+ if (_this3.audioRecorder && typeof _this3.audioRecorder.setPlaybackActive === 'function') {
28958
+ _this3.audioRecorder.setPlaybackActive(true);
28959
+ }
28905
28960
  _this3.emit('playbackStarted');
28906
28961
 
28907
28962
  // Transcripts are displayed when they arrive (between sentences)
@@ -28946,6 +29001,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28946
29001
  });
28947
29002
  this.audioPlayer.on('playbackStopped', function (info) {
28948
29003
  _this3.isPlaying = false;
29004
+
29005
+ // Back to the idle mic VAD floor
29006
+ if (_this3.audioRecorder && typeof _this3.audioRecorder.setPlaybackActive === 'function') {
29007
+ _this3.audioRecorder.setPlaybackActive(false);
29008
+ }
28949
29009
  _this3.emit('playbackStopped');
28950
29010
 
28951
29011
  // When playback stops, keep currentPlayingSentenceId to track which sentence just finished
@@ -29315,7 +29375,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29315
29375
  // iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
29316
29376
  var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
29317
29377
  var env = {
29318
- sdkVersion: true ? "2.48.12" : 0,
29378
+ sdkVersion: true ? "2.48.14" : 0,
29319
29379
  ua: ua,
29320
29380
  platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
29321
29381
  mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
@@ -29330,7 +29390,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29330
29390
  } catch (e) {
29331
29391
  console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
29332
29392
  return {
29333
- sdkVersion: true ? "2.48.12" : 0
29393
+ sdkVersion: true ? "2.48.14" : 0
29334
29394
  };
29335
29395
  }
29336
29396
  }
@@ -29472,7 +29532,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29472
29532
 
29473
29533
  // Include SDK build time for debugging
29474
29534
  if (true) {
29475
- helloMessage.lastBuildTime = "2026-08-21T00:00:00.470Z";
29535
+ helloMessage.lastBuildTime = "2026-08-21T10:23:43.383Z";
29476
29536
  }
29477
29537
 
29478
29538
  // Client environment (device/browser/webview) for backend logs + Langfuse metadata
@@ -30136,6 +30196,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30136
30196
  this.emit('inputFormatNegotiated', message.inputAudioFormat);
30137
30197
  }
30138
30198
 
30199
+ // Agent-configured mic sensitivity default (0.5–2.0). Applies only when the user
30200
+ // has no explicit/persisted override — _syncCallSettingsAfterHello() below
30201
+ // reapplies the override on top when one exists.
30202
+ if (typeof message.micSensitivity === 'number' && isFinite(message.micSensitivity) && message.micSensitivity > 0) {
30203
+ this._agentMicSensitivity = Math.max(0.5, Math.min(2.0, message.micSensitivity));
30204
+ if (!this._callSettings || typeof this._callSettings.micSensitivity !== 'number') {
30205
+ this._applyMicSensitivity(this._agentMicSensitivity);
30206
+ }
30207
+ }
30208
+
30139
30209
  // Server-driven disclaimer gate (voice): when true, client must send disclaimer_ack before using the session.
30140
30210
  this.disclaimersPending = message.disclaimersRequired === true;
30141
30211
  this.disclaimersHash = this.disclaimersPending ? message.disclaimersHash || null : null;
@@ -31006,7 +31076,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31006
31076
  * Called before hello_ack, the values are kept and flushed silently by
31007
31077
  * _syncCallSettingsAfterHello() once the session is ready.
31008
31078
  *
31009
- * @param {{voiceSpeedFactor?: number, responseLength?: string}} settings
31079
+ * `micSensitivity` (0.5–2.0, 1.0 = default) scales the mic VAD energy gate and is
31080
+ * enforced LOCALLY in the recorder (it also rides the wire so the backend can log it).
31081
+ *
31082
+ * @param {{voiceSpeedFactor?: number, responseLength?: string, micSensitivity?: number}} settings
31010
31083
  * @param {{announce?: boolean}} [opts]
31011
31084
  * @returns {boolean} true if the message was sent now
31012
31085
  */
@@ -31016,6 +31089,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31016
31089
  var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
31017
31090
  var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
31018
31091
  this._callSettings = _objectSpread(_objectSpread({}, this._callSettings || {}), settings);
31092
+ if (typeof settings.micSensitivity === 'number') {
31093
+ this._applyMicSensitivity(settings.micSensitivity);
31094
+ }
31019
31095
  if (!this.helloAckReceived || !this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
31020
31096
  return false; // flushed after hello_ack
31021
31097
  }
@@ -31029,9 +31105,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31029
31105
  if (this._callSettings.responseLength) {
31030
31106
  msg.responseLength = this._callSettings.responseLength;
31031
31107
  }
31108
+ if (typeof this._callSettings.micSensitivity === 'number') {
31109
+ msg.micSensitivity = this._callSettings.micSensitivity;
31110
+ }
31032
31111
  return this.sendMessage(msg);
31033
31112
  }
31034
31113
 
31114
+ /**
31115
+ * Apply mic sensitivity to the capture path immediately (clamped 0.5–2.0).
31116
+ * The recorder carries it in its config, so it also survives a mid-call
31117
+ * recording restart.
31118
+ */
31119
+ }, {
31120
+ key: "_applyMicSensitivity",
31121
+ value: function _applyMicSensitivity(value) {
31122
+ var v = Number(value);
31123
+ this._micSensitivity = isFinite(v) && v > 0 ? Math.max(0.5, Math.min(2.0, v)) : 1.0;
31124
+ if (this.audioRecorder && typeof this.audioRecorder.setMicSensitivity === 'function') {
31125
+ this.audioRecorder.setMicSensitivity(this._micSensitivity);
31126
+ }
31127
+ }
31128
+
31035
31129
  /**
31036
31130
  * Flush persisted call settings right after hello_ack (silent — no spoken ack).
31037
31131
  * Values come from an earlier setCallSettings() call or, when none, from
@@ -31050,16 +31144,25 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31050
31144
  this._callSettings = _objectSpread({}, s);
31051
31145
  var factor = typeof s.voiceSpeedFactor === 'number' ? s.voiceSpeedFactor : 1.0;
31052
31146
  var length = s.responseLength || 'normal';
31053
- if (Math.abs(factor - 1.0) < 0.001 && length === 'normal') return;
31054
- this.sendMessage({
31147
+ var sens = typeof s.micSensitivity === 'number' ? s.micSensitivity : 1.0;
31148
+ if (typeof s.micSensitivity === 'number') {
31149
+ this._applyMicSensitivity(sens);
31150
+ }
31151
+ if (Math.abs(factor - 1.0) < 0.001 && length === 'normal' && Math.abs(sens - 1.0) < 0.001) return;
31152
+ var msg = {
31055
31153
  t: 'call_settings',
31056
31154
  voiceSpeedFactor: factor,
31057
31155
  responseLength: length,
31058
31156
  announce: false
31059
- });
31157
+ };
31158
+ if (typeof s.micSensitivity === 'number') {
31159
+ msg.micSensitivity = sens;
31160
+ }
31161
+ this.sendMessage(msg);
31060
31162
  console.log('⚙️ VoiceSDK v2: Synced persisted call settings:', {
31061
31163
  voiceSpeedFactor: factor,
31062
- responseLength: length
31164
+ responseLength: length,
31165
+ micSensitivity: sens
31063
31166
  });
31064
31167
  } catch (e) {
31065
31168
  console.warn('⚙️ VoiceSDK v2: Failed to sync call settings (ignored):', e);
@@ -35658,7 +35761,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35658
35761
  return;
35659
35762
  }
35660
35763
  this._ensureAboutStyles();
35661
- var version = true ? "2.48.12" : 0;
35764
+ var version = true ? "2.48.14" : 0;
35662
35765
  var convId = this._getLastConversationId();
35663
35766
  var t = function t(k, fb) {
35664
35767
  try {
@@ -35729,16 +35832,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
35729
35832
  }
35730
35833
 
35731
35834
  /**
35732
- * Load persisted call settings. Defaults: agent-configured speed (factor 1.0)
35733
- * and "normal" response length.
35734
- * @returns {{voiceSpeedFactor: number, responseLength: string}}
35835
+ * Load persisted call settings. Defaults: agent-configured speed (factor 1.0),
35836
+ * "normal" response length, and default mic sensitivity (1.0).
35837
+ * @returns {{voiceSpeedFactor: number, responseLength: string, micSensitivity: number}}
35735
35838
  */
35736
35839
  }, {
35737
35840
  key: "_loadCallSettings",
35738
35841
  value: function _loadCallSettings() {
35739
35842
  var defaults = {
35740
35843
  voiceSpeedFactor: 1.0,
35741
- responseLength: 'normal'
35844
+ responseLength: 'normal',
35845
+ micSensitivity: 1.0
35742
35846
  };
35743
35847
  try {
35744
35848
  var raw = localStorage.getItem(this._callSettingsStorageKey());
@@ -35746,9 +35850,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
35746
35850
  var s = JSON.parse(raw);
35747
35851
  var factor = typeof s.voiceSpeedFactor === 'number' ? Math.max(0.5, Math.min(2.0, s.voiceSpeedFactor)) : 1.0;
35748
35852
  var length = ['detailed', 'normal', 'brief', 'very_brief'].includes(s.responseLength) ? s.responseLength : 'normal';
35853
+ var sens = typeof s.micSensitivity === 'number' ? Math.max(0.5, Math.min(2.0, s.micSensitivity)) : 1.0;
35749
35854
  return {
35750
35855
  voiceSpeedFactor: factor,
35751
- responseLength: length
35856
+ responseLength: length,
35857
+ micSensitivity: sens
35752
35858
  };
35753
35859
  } catch (_) {
35754
35860
  return defaults;
@@ -35779,7 +35885,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
35779
35885
  if (voice !== null && voice !== void 0 && voice.setCallSettings) {
35780
35886
  voice.setCallSettings({
35781
35887
  voiceSpeedFactor: merged.voiceSpeedFactor,
35782
- responseLength: merged.responseLength
35888
+ responseLength: merged.responseLength,
35889
+ micSensitivity: merged.micSensitivity
35783
35890
  }, {
35784
35891
  announce: announce
35785
35892
  });
@@ -35829,6 +35936,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
35829
35936
  var title = t('settingsTitle', 'Call settings');
35830
35937
  var speedLabel = t('settingsVoiceSpeed', 'Voice speed');
35831
35938
  var lengthLabel = t('settingsResponseLength', 'Response length');
35939
+ var sensLabel = t('settingsMicSensitivity', 'Mic sensitivity');
35940
+ var sensLow = t('sensitivityLow', 'Low');
35941
+ var sensHigh = t('sensitivityHigh', 'High');
35832
35942
  // Slider order: very brief (left) → detailed (right); higher = more detail.
35833
35943
  var LEVELS = ['very_brief', 'brief', 'normal', 'detailed'];
35834
35944
  var levelLabels = {
@@ -35841,23 +35951,27 @@ var TTPChatWidget = /*#__PURE__*/function () {
35841
35951
  var levelIdx = Math.max(0, LEVELS.indexOf(saved.responseLength));
35842
35952
  var overlay = document.createElement('div');
35843
35953
  overlay.className = 'ttp-about-overlay';
35844
- overlay.innerHTML = "\n <div class=\"ttp-about-card\" role=\"dialog\" aria-modal=\"true\" aria-label=\"".concat(title, "\">\n <button type=\"button\" class=\"ttp-about-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n <div class=\"ttp-about-title\">").concat(title, "</div>\n <div class=\"ttp-about-rows\">\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(speedLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-speed-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-speed\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.voiceSpeedFactor, "\"\n aria-label=\"").concat(speedLabel, "\">\n <div class=\"ttp-cs-ends\"><span>0.5\xD7</span><span>2\xD7</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(lengthLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-length-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-length\n min=\"0\" max=\"3\" step=\"1\" value=\"").concat(levelIdx, "\"\n aria-label=\"").concat(lengthLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(levelLabels.very_brief, "</span><span>").concat(levelLabels.detailed, "</span></div>\n </div>\n </div>\n </div>");
35954
+ overlay.innerHTML = "\n <div class=\"ttp-about-card\" role=\"dialog\" aria-modal=\"true\" aria-label=\"".concat(title, "\">\n <button type=\"button\" class=\"ttp-about-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n <div class=\"ttp-about-title\">").concat(title, "</div>\n <div class=\"ttp-about-rows\">\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(speedLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-speed-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-speed\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.voiceSpeedFactor, "\"\n aria-label=\"").concat(speedLabel, "\">\n <div class=\"ttp-cs-ends\"><span>0.5\xD7</span><span>2\xD7</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(lengthLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-length-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-length\n min=\"0\" max=\"3\" step=\"1\" value=\"").concat(levelIdx, "\"\n aria-label=\"").concat(lengthLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(levelLabels.very_brief, "</span><span>").concat(levelLabels.detailed, "</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(sensLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-sens-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-sens\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.micSensitivity, "\"\n aria-label=\"").concat(sensLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(sensLow, "</span><span>").concat(sensHigh, "</span></div>\n </div>\n </div>\n </div>");
35845
35955
  var speedSlider = overlay.querySelector('[data-cs-speed]');
35846
35956
  var speedVal = overlay.querySelector('[data-cs-speed-val]');
35847
35957
  var lengthSlider = overlay.querySelector('[data-cs-length]');
35848
35958
  var lengthVal = overlay.querySelector('[data-cs-length-val]');
35959
+ var sensSlider = overlay.querySelector('[data-cs-sens]');
35960
+ var sensVal = overlay.querySelector('[data-cs-sens-val]');
35849
35961
  var fmtSpeed = function fmtSpeed(v) {
35850
35962
  return "".concat(Number(v).toFixed(2).replace(/0$/, ''), "\xD7");
35851
35963
  };
35852
35964
  var renderVals = function renderVals() {
35853
35965
  if (speedVal) speedVal.textContent = fmtSpeed(speedSlider.value);
35854
35966
  if (lengthVal) lengthVal.textContent = levelLabels[LEVELS[Number(lengthSlider.value)]] || '';
35967
+ if (sensVal) sensVal.textContent = fmtSpeed(sensSlider.value);
35855
35968
  };
35856
35969
  renderVals();
35857
35970
 
35858
35971
  // Live label updates while dragging; persist + push on release only.
35859
35972
  speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('input', renderVals);
35860
35973
  lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('input', renderVals);
35974
+ sensSlider === null || sensSlider === void 0 || sensSlider.addEventListener('input', renderVals);
35861
35975
  speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('change', function () {
35862
35976
  _this9._applyCallSettings({
35863
35977
  voiceSpeedFactor: Number(speedSlider.value)
@@ -35868,6 +35982,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
35868
35982
  responseLength: LEVELS[Number(lengthSlider.value)]
35869
35983
  }, true);
35870
35984
  });
35985
+ // Mic sensitivity is enforced locally in the capture path — no spoken ack needed.
35986
+ sensSlider === null || sensSlider === void 0 || sensSlider.addEventListener('change', function () {
35987
+ _this9._applyCallSettings({
35988
+ micSensitivity: Number(sensSlider.value)
35989
+ }, false);
35990
+ });
35871
35991
  var close = function close() {
35872
35992
  return _this9._closeCallSettingsModal();
35873
35993
  };
@@ -45283,7 +45403,10 @@ __webpack_require__.r(__webpack_exports__);
45283
45403
  "lengthDetailed": "Detailed",
45284
45404
  "lengthNormal": "Normal",
45285
45405
  "lengthBrief": "Brief",
45286
- "lengthVeryBrief": "Very brief"
45406
+ "lengthVeryBrief": "Very brief",
45407
+ "settingsMicSensitivity": "Mic sensitivity",
45408
+ "sensitivityLow": "Low",
45409
+ "sensitivityHigh": "High"
45287
45410
  },
45288
45411
  "he": {
45289
45412
  "landingTitle": "איך תרצה לתקשר?",
@@ -45329,7 +45452,10 @@ __webpack_require__.r(__webpack_exports__);
45329
45452
  "lengthDetailed": "מפורט",
45330
45453
  "lengthNormal": "רגיל",
45331
45454
  "lengthBrief": "קצר",
45332
- "lengthVeryBrief": "קצר מאוד"
45455
+ "lengthVeryBrief": "קצר מאוד",
45456
+ "settingsMicSensitivity": "רגישות מיקרופון",
45457
+ "sensitivityLow": "נמוכה",
45458
+ "sensitivityHigh": "גבוהה"
45333
45459
  },
45334
45460
  "ar": {
45335
45461
  "landingTitle": "كيف تريد التواصل؟",
@@ -45375,7 +45501,10 @@ __webpack_require__.r(__webpack_exports__);
45375
45501
  "lengthDetailed": "مفصّل",
45376
45502
  "lengthNormal": "عادي",
45377
45503
  "lengthBrief": "مختصر",
45378
- "lengthVeryBrief": "مختصر جداً"
45504
+ "lengthVeryBrief": "مختصر جداً",
45505
+ "settingsMicSensitivity": "حساسية الميكروفون",
45506
+ "sensitivityLow": "منخفضة",
45507
+ "sensitivityHigh": "مرتفعة"
45379
45508
  },
45380
45509
  "ru": {
45381
45510
  "landingTitle": "Как вы хотите общаться?",
@@ -45421,7 +45550,10 @@ __webpack_require__.r(__webpack_exports__);
45421
45550
  "lengthDetailed": "Подробно",
45422
45551
  "lengthNormal": "Обычно",
45423
45552
  "lengthBrief": "Кратко",
45424
- "lengthVeryBrief": "Очень кратко"
45553
+ "lengthVeryBrief": "Очень кратко",
45554
+ "settingsMicSensitivity": "Чувствительность микрофона",
45555
+ "sensitivityLow": "Низкая",
45556
+ "sensitivityHigh": "Высокая"
45425
45557
  },
45426
45558
  "es": {
45427
45559
  "landingTitle": "¿Cómo te gustaría comunicarte?",
@@ -45467,7 +45599,10 @@ __webpack_require__.r(__webpack_exports__);
45467
45599
  "lengthDetailed": "Detallada",
45468
45600
  "lengthNormal": "Normal",
45469
45601
  "lengthBrief": "Breve",
45470
- "lengthVeryBrief": "Muy breve"
45602
+ "lengthVeryBrief": "Muy breve",
45603
+ "settingsMicSensitivity": "Sensibilidad del micrófono",
45604
+ "sensitivityLow": "Baja",
45605
+ "sensitivityHigh": "Alta"
45471
45606
  },
45472
45607
  "fr": {
45473
45608
  "landingTitle": "Comment souhaitez-vous communiquer?",
@@ -45513,7 +45648,10 @@ __webpack_require__.r(__webpack_exports__);
45513
45648
  "lengthDetailed": "Détaillée",
45514
45649
  "lengthNormal": "Normale",
45515
45650
  "lengthBrief": "Brève",
45516
- "lengthVeryBrief": "Très brève"
45651
+ "lengthVeryBrief": "Très brève",
45652
+ "settingsMicSensitivity": "Sensibilité du micro",
45653
+ "sensitivityLow": "Faible",
45654
+ "sensitivityHigh": "Élevée"
45517
45655
  },
45518
45656
  "de": {
45519
45657
  "landingTitle": "Wie möchten Sie kommunizieren?",
@@ -45559,7 +45697,10 @@ __webpack_require__.r(__webpack_exports__);
45559
45697
  "lengthDetailed": "Ausführlich",
45560
45698
  "lengthNormal": "Normal",
45561
45699
  "lengthBrief": "Kurz",
45562
- "lengthVeryBrief": "Sehr kurz"
45700
+ "lengthVeryBrief": "Sehr kurz",
45701
+ "settingsMicSensitivity": "Mikrofonempfindlichkeit",
45702
+ "sensitivityLow": "Niedrig",
45703
+ "sensitivityHigh": "Hoch"
45563
45704
  }
45564
45705
  });
45565
45706