agents
Version:
A home for your AI agents
932 lines (925 loc) • 47.7 kB
JavaScript
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../classPrivateFieldGet2-DZBYAB34.js";
import { t as _classPrivateMethodInitSpec } from "../classPrivateMethodInitSpec-qMjJ6sHQ.js";
import { VOICE_PROTOCOL_VERSION } from "./types.js";
import { toVoiceError } from "./errors.js";
import { t as ClientDiagnostics } from "../diagnostics-C4jcz3VK.js";
import { PartySocket } from "partysocket";
//#region src/voice/client.ts
function camelCaseToKebabCase(str) {
if (str === str.toUpperCase() && str !== str.toLowerCase()) return str.toLowerCase().replace(/_/g, "-");
let kebabified = str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
kebabified = kebabified.startsWith("-") ? kebabified.slice(1) : kebabified;
return kebabified.replace(/_/g, "-").replace(/-$/, "");
}
const UNSUPPORTED_OUTPUT_DEVICE_ERROR = "Audio output device selection is not supported in this browser.";
const OUTPUT_DEVICE_SWITCH_ERROR = "Could not switch audio output device.";
const WORKLET_PROCESSOR = `
class AudioCaptureProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.buffer = [];
this.sampleRate = sampleRate;
this.targetRate = 16000;
this.ratio = this.sampleRate / this.targetRate;
}
process(inputs) {
const input = inputs[0];
if (!input || !input[0]) return true;
const channelData = input[0];
// Linear interpolation resampling (e.g. 48kHz → 16kHz).
// Nearest-neighbor (picking every Nth sample) introduces aliasing
// artifacts, especially on sibilants (s, f, th). Linear interpolation
// blends adjacent samples, acting as a basic low-pass filter.
for (let i = 0; i < channelData.length; i += this.ratio) {
const idx = Math.floor(i);
const frac = i - idx;
if (idx + 1 < channelData.length) {
this.buffer.push(channelData[idx] * (1 - frac) + channelData[idx + 1] * frac);
} else if (idx < channelData.length) {
this.buffer.push(channelData[idx]);
}
}
if (this.buffer.length >= 1600) {
const chunk = new Float32Array(this.buffer);
this.port.postMessage({ type: 'audio', samples: chunk }, [chunk.buffer]);
this.buffer = [];
}
return true;
}
}
registerProcessor('audio-capture-processor', AudioCaptureProcessor);
`;
function floatTo16BitPCM(samples) {
const buffer = /* @__PURE__ */ new ArrayBuffer(samples.length * 2);
const view = new DataView(buffer);
for (let i = 0; i < samples.length; i++) {
const s = Math.max(-1, Math.min(1, samples[i]));
view.setInt16(i * 2, s < 0 ? s * 32768 : s * 32767, true);
}
return buffer;
}
function computeRMS(samples) {
let sum = 0;
for (let i = 0; i < samples.length; i++) sum += samples[i] * samples[i];
return Math.sqrt(sum / samples.length);
}
var _socket = /* @__PURE__ */ new WeakMap();
var _options = /* @__PURE__ */ new WeakMap();
/**
* Default VoiceTransport backed by PartySocket (reconnecting WebSocket).
* Created automatically when no custom transport is provided.
*/
var WebSocketVoiceTransport = class {
constructor(options) {
_classPrivateFieldInitSpec(this, _socket, null);
_classPrivateFieldInitSpec(this, _options, void 0);
this.onopen = null;
this.onclose = null;
this.onerror = null;
this.onmessage = null;
_classPrivateFieldSet2(_options, this, options);
}
get connected() {
return _classPrivateFieldGet2(_socket, this)?.readyState === WebSocket.OPEN;
}
sendJSON(data) {
if (_classPrivateFieldGet2(_socket, this)?.readyState === WebSocket.OPEN) _classPrivateFieldGet2(_socket, this).send(JSON.stringify(data));
}
sendBinary(data) {
if (_classPrivateFieldGet2(_socket, this)?.readyState === WebSocket.OPEN) _classPrivateFieldGet2(_socket, this).send(data);
}
connect() {
if (_classPrivateFieldGet2(_socket, this)) return;
const socket = new PartySocket({
party: camelCaseToKebabCase(_classPrivateFieldGet2(_options, this).agent),
room: _classPrivateFieldGet2(_options, this).name ?? "default",
host: _classPrivateFieldGet2(_options, this).host ?? window.location.host,
prefix: "agents",
query: _classPrivateFieldGet2(_options, this).query
});
socket.onopen = () => this.onopen?.();
socket.onclose = (event) => this.onclose?.(event ? {
code: event.code,
reason: event.reason,
wasClean: event.wasClean
} : void 0);
socket.onerror = (event) => this.onerror?.(event);
socket.onmessage = (event) => {
this.onmessage?.(event.data);
};
_classPrivateFieldSet2(_socket, this, socket);
}
disconnect() {
_classPrivateFieldGet2(_socket, this)?.close();
_classPrivateFieldSet2(_socket, this, null);
}
};
var _status = /* @__PURE__ */ new WeakMap();
var _transcript = /* @__PURE__ */ new WeakMap();
var _metrics = /* @__PURE__ */ new WeakMap();
var _turnMetrics = /* @__PURE__ */ new WeakMap();
var _audioLevel = /* @__PURE__ */ new WeakMap();
var _isMuted = /* @__PURE__ */ new WeakMap();
var _connected = /* @__PURE__ */ new WeakMap();
var _error = /* @__PURE__ */ new WeakMap();
var _outputDeviceError = /* @__PURE__ */ new WeakMap();
var _lastCustomMessage = /* @__PURE__ */ new WeakMap();
var _audioFormat = /* @__PURE__ */ new WeakMap();
var _sampleRate = /* @__PURE__ */ new WeakMap();
var _interimTranscript = /* @__PURE__ */ new WeakMap();
var _serverProtocolVersion = /* @__PURE__ */ new WeakMap();
var _inCall = /* @__PURE__ */ new WeakMap();
var _callGeneration = /* @__PURE__ */ new WeakMap();
var _serverCallAcknowledged = /* @__PURE__ */ new WeakMap();
var _diagnostics = /* @__PURE__ */ new WeakMap();
var _audioReceivedForCurrentTurn = /* @__PURE__ */ new WeakMap();
var _diagnosticTurnId = /* @__PURE__ */ new WeakMap();
var _silenceThreshold = /* @__PURE__ */ new WeakMap();
var _silenceDurationMs = /* @__PURE__ */ new WeakMap();
var _interruptThreshold = /* @__PURE__ */ new WeakMap();
var _interruptChunks = /* @__PURE__ */ new WeakMap();
var _maxTranscriptMessages = /* @__PURE__ */ new WeakMap();
var _transport = /* @__PURE__ */ new WeakMap();
var _options2 = /* @__PURE__ */ new WeakMap();
var _audioContext = /* @__PURE__ */ new WeakMap();
var _workletRegistered = /* @__PURE__ */ new WeakMap();
var _workletNode = /* @__PURE__ */ new WeakMap();
var _stream = /* @__PURE__ */ new WeakMap();
var _silenceTimer = /* @__PURE__ */ new WeakMap();
var _isSpeaking = /* @__PURE__ */ new WeakMap();
var _playbackQueue = /* @__PURE__ */ new WeakMap();
var _isPlaying = /* @__PURE__ */ new WeakMap();
var _isScheduling = /* @__PURE__ */ new WeakMap();
var _scheduledSources = /* @__PURE__ */ new WeakMap();
var _playbackCursor = /* @__PURE__ */ new WeakMap();
var _lastPlaybackEnd = /* @__PURE__ */ new WeakMap();
var _playbackElement = /* @__PURE__ */ new WeakMap();
var _playbackDestination = /* @__PURE__ */ new WeakMap();
var _playbackDestinationPromise = /* @__PURE__ */ new WeakMap();
var _useDefaultPlaybackDestination = /* @__PURE__ */ new WeakMap();
var _outputDeviceId = /* @__PURE__ */ new WeakMap();
var _outputDeviceSwitchGeneration = /* @__PURE__ */ new WeakMap();
var _playbackOutputGeneration = /* @__PURE__ */ new WeakMap();
var _playbackGeneration = /* @__PURE__ */ new WeakMap();
var _interruptChunkCount = /* @__PURE__ */ new WeakMap();
var _listeners = /* @__PURE__ */ new WeakMap();
var _VoiceClient_brand = /* @__PURE__ */ new WeakSet();
var VoiceClient = class {
constructor(options) {
_classPrivateMethodInitSpec(this, _VoiceClient_brand);
_classPrivateFieldInitSpec(this, _status, "idle");
_classPrivateFieldInitSpec(this, _transcript, []);
_classPrivateFieldInitSpec(this, _metrics, null);
_classPrivateFieldInitSpec(this, _turnMetrics, null);
_classPrivateFieldInitSpec(this, _audioLevel, 0);
_classPrivateFieldInitSpec(this, _isMuted, false);
_classPrivateFieldInitSpec(this, _connected, false);
_classPrivateFieldInitSpec(this, _error, null);
_classPrivateFieldInitSpec(this, _outputDeviceError, null);
_classPrivateFieldInitSpec(this, _lastCustomMessage, null);
_classPrivateFieldInitSpec(this, _audioFormat, null);
_classPrivateFieldInitSpec(this, _sampleRate, 16e3);
_classPrivateFieldInitSpec(this, _interimTranscript, null);
_classPrivateFieldInitSpec(this, _serverProtocolVersion, null);
_classPrivateFieldInitSpec(this, _inCall, false);
_classPrivateFieldInitSpec(this, _callGeneration, 0);
_classPrivateFieldInitSpec(this, _serverCallAcknowledged, false);
_classPrivateFieldInitSpec(this, _diagnostics, new ClientDiagnostics());
_classPrivateFieldInitSpec(this, _audioReceivedForCurrentTurn, false);
_classPrivateFieldInitSpec(this, _diagnosticTurnId, null);
_classPrivateFieldInitSpec(this, _silenceThreshold, void 0);
_classPrivateFieldInitSpec(this, _silenceDurationMs, void 0);
_classPrivateFieldInitSpec(this, _interruptThreshold, void 0);
_classPrivateFieldInitSpec(this, _interruptChunks, void 0);
_classPrivateFieldInitSpec(this, _maxTranscriptMessages, void 0);
_classPrivateFieldInitSpec(this, _transport, null);
_classPrivateFieldInitSpec(this, _options2, void 0);
_classPrivateFieldInitSpec(this, _audioContext, null);
_classPrivateFieldInitSpec(this, _workletRegistered, false);
_classPrivateFieldInitSpec(this, _workletNode, null);
_classPrivateFieldInitSpec(this, _stream, null);
_classPrivateFieldInitSpec(this, _silenceTimer, null);
_classPrivateFieldInitSpec(this, _isSpeaking, false);
_classPrivateFieldInitSpec(this, _playbackQueue, []);
_classPrivateFieldInitSpec(this, _isPlaying, false);
_classPrivateFieldInitSpec(this, _isScheduling, false);
_classPrivateFieldInitSpec(this, _scheduledSources, /* @__PURE__ */ new Set());
_classPrivateFieldInitSpec(this, _playbackCursor, 0);
_classPrivateFieldInitSpec(this, _lastPlaybackEnd, null);
_classPrivateFieldInitSpec(this, _playbackElement, null);
_classPrivateFieldInitSpec(this, _playbackDestination, null);
_classPrivateFieldInitSpec(this, _playbackDestinationPromise, null);
_classPrivateFieldInitSpec(this, _useDefaultPlaybackDestination, false);
_classPrivateFieldInitSpec(this, _outputDeviceId, void 0);
_classPrivateFieldInitSpec(this, _outputDeviceSwitchGeneration, 0);
_classPrivateFieldInitSpec(this, _playbackOutputGeneration, 0);
_classPrivateFieldInitSpec(this, _playbackGeneration, 0);
_classPrivateFieldInitSpec(this, _interruptChunkCount, 0);
_classPrivateFieldInitSpec(this, _listeners, /* @__PURE__ */ new Map());
_classPrivateFieldSet2(_options2, this, options);
_classPrivateFieldSet2(_silenceThreshold, this, options.silenceThreshold ?? .04);
_classPrivateFieldSet2(_silenceDurationMs, this, options.silenceDurationMs ?? 500);
_classPrivateFieldSet2(_interruptThreshold, this, options.interruptThreshold ?? .05);
_classPrivateFieldSet2(_interruptChunks, this, options.interruptChunks ?? 2);
_classPrivateFieldSet2(_maxTranscriptMessages, this, options.maxTranscriptMessages ?? 200);
_classPrivateFieldSet2(_outputDeviceId, this, options.outputDeviceId ?? "default");
}
get status() {
return _classPrivateFieldGet2(_status, this);
}
get transcript() {
return _classPrivateFieldGet2(_transcript, this);
}
get metrics() {
return _classPrivateFieldGet2(_metrics, this);
}
/** Last stable terminal summary received for any speech or text turn. */
get turnMetrics() {
return _classPrivateFieldGet2(_turnMetrics, this);
}
get audioLevel() {
return _classPrivateFieldGet2(_audioLevel, this);
}
get isMuted() {
return _classPrivateFieldGet2(_isMuted, this);
}
get connected() {
return _classPrivateFieldGet2(_connected, this);
}
get error() {
return _classPrivateFieldGet2(_error, this);
}
get outputDeviceError() {
return _classPrivateFieldGet2(_outputDeviceError, this);
}
/**
* The current interim (partial) transcript from streaming STT.
* Updates in real time as the user speaks. Cleared when the final
* transcript is produced or the call reaches a terminal reset.
* null when no interim text is available.
*/
get interimTranscript() {
return _classPrivateFieldGet2(_interimTranscript, this);
}
/**
* The protocol version reported by the server.
* null until the server sends its welcome message.
*/
get serverProtocolVersion() {
return _classPrivateFieldGet2(_serverProtocolVersion, this);
}
addEventListener(event, listener) {
let set = _classPrivateFieldGet2(_listeners, this).get(event);
if (!set) {
set = /* @__PURE__ */ new Set();
_classPrivateFieldGet2(_listeners, this).set(event, set);
}
set.add(listener);
}
removeEventListener(event, listener) {
_classPrivateFieldGet2(_listeners, this).get(event)?.delete(listener);
}
connect() {
if (_classPrivateFieldGet2(_transport, this)) return;
const transport = _classPrivateFieldGet2(_options2, this).transport ?? new WebSocketVoiceTransport({
agent: _classPrivateFieldGet2(_options2, this).agent,
name: _classPrivateFieldGet2(_options2, this).name,
host: _classPrivateFieldGet2(_options2, this).host,
query: _classPrivateFieldGet2(_options2, this).query
});
transport.onopen = () => {
_classPrivateFieldSet2(_connected, this, true);
_classPrivateFieldSet2(_error, this, null);
transport.sendJSON({
type: "hello",
protocol_version: 1
});
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "connectionchange", true);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", null);
if (_classPrivateFieldGet2(_inCall, this)) {
_classPrivateFieldSet2(_serverCallAcknowledged, this, false);
transport.sendJSON({ type: "start_call" });
}
};
transport.onclose = (info) => {
_classPrivateFieldGet2(_diagnostics, this).emit("connection.closed", {
...info?.code === void 0 ? {} : { code: info.code },
clean: info?.wasClean ?? false
});
_classPrivateFieldSet2(_connected, this, false);
_assertClassBrand(_VoiceClient_brand, this, _clearInterimTranscript).call(this);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "connectionchange", false);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "connectiondiagnostic", {
type: "close",
...info
});
};
transport.onerror = (cause) => {
_classPrivateFieldGet2(_diagnostics, this).emit("connection.error", { error: toVoiceError(cause, "Connection failed") });
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "connectiondiagnostic", {
type: "error",
cause
});
_classPrivateFieldSet2(_error, this, "Connection lost. Reconnecting...");
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", _classPrivateFieldGet2(_error, this));
};
transport.onmessage = (data) => {
if (typeof data === "string") _assertClassBrand(_VoiceClient_brand, this, _handleJSONMessage).call(this, data);
else if (data instanceof Blob) data.arrayBuffer().then((buffer) => {
_assertClassBrand(_VoiceClient_brand, this, _recordAudioReceived).call(this, buffer.byteLength);
_classPrivateFieldGet2(_playbackQueue, this).push(buffer);
_assertClassBrand(_VoiceClient_brand, this, _processPlaybackQueue).call(this);
});
else if (data instanceof ArrayBuffer) {
_assertClassBrand(_VoiceClient_brand, this, _recordAudioReceived).call(this, data.byteLength);
_classPrivateFieldGet2(_playbackQueue, this).push(data);
_assertClassBrand(_VoiceClient_brand, this, _processPlaybackQueue).call(this);
}
};
_classPrivateFieldSet2(_transport, this, transport);
transport.connect();
}
disconnect() {
this.endCall();
_classPrivateFieldGet2(_diagnostics, this).emit("connection.disconnecting");
_classPrivateFieldGet2(_transport, this)?.disconnect();
_classPrivateFieldSet2(_transport, this, null);
_classPrivateFieldSet2(_connected, this, false);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "connectionchange", false);
}
async startCall() {
var _this$callGeneration;
if (!_classPrivateFieldGet2(_transport, this)?.connected) {
_classPrivateFieldSet2(_error, this, "Cannot start call: not connected. Call connect() first.");
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", _classPrivateFieldGet2(_error, this));
return;
}
if (_classPrivateFieldGet2(_inCall, this)) return;
_assertClassBrand(_VoiceClient_brand, this, _clearInterimTranscript).call(this);
const callGeneration = _classPrivateFieldSet2(_callGeneration, this, (_this$callGeneration = _classPrivateFieldGet2(_callGeneration, this), ++_this$callGeneration));
_classPrivateFieldSet2(_inCall, this, true);
_classPrivateFieldSet2(_serverCallAcknowledged, this, false);
_classPrivateFieldSet2(_error, this, null);
_classPrivateFieldSet2(_metrics, this, null);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", null);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "metricschange", null);
const startMsg = { type: "start_call" };
if (_classPrivateFieldGet2(_options2, this).preferredFormat) startMsg.preferred_format = _classPrivateFieldGet2(_options2, this).preferredFormat;
_classPrivateFieldGet2(_diagnostics, this).emit("call.starting");
_classPrivateFieldGet2(_transport, this).sendJSON(startMsg);
const ctx = await _assertClassBrand(_VoiceClient_brand, this, _getAudioContext).call(this);
if (_assertClassBrand(_VoiceClient_brand, this, _abortStaleCallStartup).call(this, callGeneration)) return;
await _assertClassBrand(_VoiceClient_brand, this, _getPlaybackDestination).call(this, ctx);
if (_assertClassBrand(_VoiceClient_brand, this, _abortStaleCallStartup).call(this, callGeneration)) return;
if (_classPrivateFieldGet2(_options2, this).audioInput) {
_classPrivateFieldGet2(_diagnostics, this).emit("microphone.starting", { source: "custom" });
_classPrivateFieldGet2(_options2, this).audioInput.onAudioLevel = (rms) => _assertClassBrand(_VoiceClient_brand, this, _processAudioLevel).call(this, rms);
_classPrivateFieldGet2(_options2, this).audioInput.onAudioData = (pcm) => {
if (_classPrivateFieldGet2(_transport, this)?.connected && !_classPrivateFieldGet2(_isMuted, this)) _classPrivateFieldGet2(_transport, this).sendBinary(pcm);
};
await _classPrivateFieldGet2(_options2, this).audioInput.start();
_classPrivateFieldGet2(_diagnostics, this).emit("microphone.ready", { source: "custom" });
} else await _assertClassBrand(_VoiceClient_brand, this, _startMic).call(this);
if (!_assertClassBrand(_VoiceClient_brand, this, _abortStaleCallStartup).call(this, callGeneration)) _classPrivateFieldGet2(_diagnostics, this).emit("call.local_ready");
}
endCall() {
var _this$callGeneration2;
const wasInCall = _classPrivateFieldGet2(_inCall, this);
_classPrivateFieldSet2(_callGeneration, this, (_this$callGeneration2 = _classPrivateFieldGet2(_callGeneration, this), _this$callGeneration2++, _this$callGeneration2));
_classPrivateFieldSet2(_inCall, this, false);
_classPrivateFieldSet2(_serverCallAcknowledged, this, false);
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON({ type: "end_call" });
_assertClassBrand(_VoiceClient_brand, this, _stopLocalCall).call(this);
_assertClassBrand(_VoiceClient_brand, this, _clearInterimTranscript).call(this);
_classPrivateFieldSet2(_status, this, "idle");
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "statuschange", "idle");
if (wasInCall) _classPrivateFieldGet2(_diagnostics, this).emit("call.ended", { reason: "requested" });
}
toggleMute() {
_classPrivateFieldSet2(_isMuted, this, !_classPrivateFieldGet2(_isMuted, this));
if (_classPrivateFieldGet2(_isMuted, this)) {
_classPrivateFieldSet2(_audioLevel, this, 0);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "audiolevelchange", 0);
}
if (_classPrivateFieldGet2(_isMuted, this) && _classPrivateFieldGet2(_isSpeaking, this)) {
_classPrivateFieldSet2(_isSpeaking, this, false);
if (_classPrivateFieldGet2(_silenceTimer, this)) {
clearTimeout(_classPrivateFieldGet2(_silenceTimer, this));
_classPrivateFieldSet2(_silenceTimer, this, null);
}
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON({ type: "end_of_speech" });
}
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "mutechange", _classPrivateFieldGet2(_isMuted, this));
}
/**
* Send a text message to the agent. The agent processes it through
* `onTurn()` (bypassing STT) and responds with text transcript and
* TTS audio (if in a call) or text-only (if not).
*/
sendText(text) {
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON({
type: "text_message",
text
});
}
/**
* Send arbitrary JSON to the agent. Use this for app-level messages
* that are not part of the voice protocol (e.g. `{ type: "kick_speaker" }`).
* The server receives these in the consumer's `onMessage()` handler.
*/
sendJSON(data) {
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON(data);
}
/**
* Set the preferred audio output device for assistant playback.
* Unsupported browsers continue playing through the default output.
*/
async setOutputDevice(outputDeviceId) {
var _this$outputDeviceSwi;
_classPrivateFieldSet2(_outputDeviceId, this, outputDeviceId ?? "default");
const generation = _classPrivateFieldSet2(_outputDeviceSwitchGeneration, this, (_this$outputDeviceSwi = _classPrivateFieldGet2(_outputDeviceSwitchGeneration, this), ++_this$outputDeviceSwi));
if (_classPrivateFieldGet2(_playbackElement, this)) await _assertClassBrand(_VoiceClient_brand, this, _applyOutputDevice).call(this, _classPrivateFieldGet2(_playbackElement, this), generation);
}
/**
* The last custom (non-voice-protocol) message received from the server.
* Listen for the `"custommessage"` event to be notified when this changes.
*/
get lastCustomMessage() {
return _classPrivateFieldGet2(_lastCustomMessage, this);
}
/**
* The audio format the server declared for binary payloads.
* Set when the server sends `audio_config` at call start.
*/
get audioFormat() {
return _classPrivateFieldGet2(_audioFormat, this);
}
/**
* The sample rate (Hz) the server declared for raw pcm16 payloads.
* Set when the server sends `audio_config` at call start. Defaults to 16000.
*/
get sampleRate() {
return _classPrivateFieldGet2(_sampleRate, this);
}
};
function _emit(event, data) {
const set = _classPrivateFieldGet2(_listeners, this).get(event);
if (set) for (const listener of set) listener(data);
}
function _trimTranscript() {
if (_classPrivateFieldGet2(_transcript, this).length > _classPrivateFieldGet2(_maxTranscriptMessages, this)) _classPrivateFieldSet2(_transcript, this, _classPrivateFieldGet2(_transcript, this).slice(-_classPrivateFieldGet2(_maxTranscriptMessages, this)));
}
function _setOutputDeviceError(error) {
if (_classPrivateFieldGet2(_outputDeviceError, this) === error) return;
_classPrivateFieldSet2(_outputDeviceError, this, error);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "outputdeviceerror", error);
}
function _clearInterimTranscript() {
_classPrivateFieldSet2(_interimTranscript, this, null);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "interimtranscript", null);
}
function _isCurrentCallStartup(callGeneration) {
return _classPrivateFieldGet2(_inCall, this) && _classPrivateFieldGet2(_callGeneration, this) === callGeneration;
}
function _abortStaleCallStartup(callGeneration) {
if (_assertClassBrand(_VoiceClient_brand, this, _isCurrentCallStartup).call(this, callGeneration)) return false;
if (!_classPrivateFieldGet2(_inCall, this)) _assertClassBrand(_VoiceClient_brand, this, _stopLocalCall).call(this);
return true;
}
function _stopLocalCall() {
if (_classPrivateFieldGet2(_options2, this).audioInput) {
_classPrivateFieldGet2(_options2, this).audioInput.stop();
_classPrivateFieldGet2(_options2, this).audioInput.onAudioLevel = null;
_classPrivateFieldGet2(_options2, this).audioInput.onAudioData = null;
} else _assertClassBrand(_VoiceClient_brand, this, _stopMic).call(this);
_assertClassBrand(_VoiceClient_brand, this, _stopPlayback).call(this);
_assertClassBrand(_VoiceClient_brand, this, _closeAudioContext).call(this);
_assertClassBrand(_VoiceClient_brand, this, _resetDetection).call(this);
}
function _recordAudioReceived(bytes) {
if (_classPrivateFieldGet2(_audioReceivedForCurrentTurn, this)) return;
_classPrivateFieldSet2(_audioReceivedForCurrentTurn, this, true);
_classPrivateFieldGet2(_diagnostics, this).emit("audio.received", {
bytes,
..._classPrivateFieldGet2(_diagnosticTurnId, this) === null ? {} : { turn_id: _classPrivateFieldGet2(_diagnosticTurnId, this) }
});
}
function _handleJSONMessage(data) {
let msg;
try {
msg = JSON.parse(data);
} catch {
return;
}
switch (msg.type) {
case "welcome": {
const diagnosticsEnabled = (typeof msg.diagnostics === "object" && msg.diagnostics !== null ? msg.diagnostics : null)?.browser_console === true;
_classPrivateFieldGet2(_diagnostics, this).setEnabled(diagnosticsEnabled);
if (diagnosticsEnabled) _classPrivateFieldGet2(_diagnostics, this).emit("connection.ready", { protocol_version: msg.protocol_version });
_classPrivateFieldSet2(_serverProtocolVersion, this, msg.protocol_version);
if (msg.protocol_version !== 1) console.warn(`[VoiceClient] Protocol version mismatch: client=1, server=${msg.protocol_version}`);
break;
}
case "diagnostic":
if (typeof msg.event === "string" && typeof msg.timestamp === "number" && Number.isFinite(msg.timestamp)) {
const data = typeof msg.data === "object" && msg.data !== null ? msg.data : void 0;
if (msg.event === "audio.first_sent" && typeof data?.turn_id === "string") {
_classPrivateFieldSet2(_diagnosticTurnId, this, data.turn_id);
_classPrivateFieldSet2(_audioReceivedForCurrentTurn, this, false);
}
_classPrivateFieldGet2(_diagnostics, this).receive({
event: msg.event,
timestamp: msg.timestamp,
...data === void 0 ? {} : { data }
});
}
break;
case "audio_config":
_classPrivateFieldSet2(_serverCallAcknowledged, this, true);
_classPrivateFieldSet2(_audioFormat, this, msg.format);
_classPrivateFieldSet2(_sampleRate, this, typeof msg.sampleRate === "number" && msg.sampleRate > 0 ? msg.sampleRate : 16e3);
break;
case "status":
_classPrivateFieldSet2(_status, this, msg.status);
if (msg.status === "thinking" || msg.status === "speaking") {
_classPrivateFieldSet2(_audioReceivedForCurrentTurn, this, false);
if (msg.status === "thinking") _classPrivateFieldSet2(_diagnosticTurnId, this, null);
}
if (msg.status === "idle" && _classPrivateFieldGet2(_inCall, this)) {
var _this$callGeneration4;
if (!(_classPrivateFieldGet2(_serverCallAcknowledged, this) || _classPrivateFieldGet2(_error, this) !== null)) {
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "statuschange", _classPrivateFieldGet2(_status, this));
break;
}
_classPrivateFieldSet2(_callGeneration, this, (_this$callGeneration4 = _classPrivateFieldGet2(_callGeneration, this), _this$callGeneration4++, _this$callGeneration4));
_classPrivateFieldSet2(_inCall, this, false);
_classPrivateFieldSet2(_serverCallAcknowledged, this, false);
_assertClassBrand(_VoiceClient_brand, this, _stopLocalCall).call(this);
_assertClassBrand(_VoiceClient_brand, this, _clearInterimTranscript).call(this);
}
if (msg.status === "listening") {
_classPrivateFieldSet2(_serverCallAcknowledged, this, true);
_classPrivateFieldSet2(_error, this, null);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", null);
} else if (msg.status === "thinking" || msg.status === "speaking") _classPrivateFieldSet2(_serverCallAcknowledged, this, true);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "statuschange", _classPrivateFieldGet2(_status, this));
break;
case "transcript_interim":
_classPrivateFieldSet2(_interimTranscript, this, msg.text);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "interimtranscript", _classPrivateFieldGet2(_interimTranscript, this));
break;
case "playback_interrupt":
_assertClassBrand(_VoiceClient_brand, this, _stopPlayback).call(this, "server_interrupt");
break;
case "transcript":
_classPrivateFieldSet2(_interimTranscript, this, null);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "interimtranscript", null);
if (msg.role === "user" && _classPrivateFieldGet2(_isPlaying, this)) _assertClassBrand(_VoiceClient_brand, this, _stopPlayback).call(this, "user_utterance");
_classPrivateFieldSet2(_transcript, this, [..._classPrivateFieldGet2(_transcript, this), {
role: msg.role,
text: msg.text,
timestamp: Date.now()
}]);
_assertClassBrand(_VoiceClient_brand, this, _trimTranscript).call(this);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "transcriptchange", _classPrivateFieldGet2(_transcript, this));
break;
case "transcript_start":
_classPrivateFieldSet2(_transcript, this, [..._classPrivateFieldGet2(_transcript, this), {
role: "assistant",
text: "",
timestamp: Date.now()
}]);
_assertClassBrand(_VoiceClient_brand, this, _trimTranscript).call(this);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "transcriptchange", _classPrivateFieldGet2(_transcript, this));
break;
case "transcript_delta": {
if (_classPrivateFieldGet2(_transcript, this).length === 0) break;
const updated = [..._classPrivateFieldGet2(_transcript, this)];
const last = updated[updated.length - 1];
if (last.role === "assistant") {
updated[updated.length - 1] = {
...last,
text: last.text + msg.text
};
_classPrivateFieldSet2(_transcript, this, updated);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "transcriptchange", _classPrivateFieldGet2(_transcript, this));
}
break;
}
case "transcript_end": {
if (_classPrivateFieldGet2(_transcript, this).length === 0) break;
const updated = [..._classPrivateFieldGet2(_transcript, this)];
const last = updated[updated.length - 1];
if (last.role === "assistant") {
updated[updated.length - 1] = {
...last,
text: msg.text
};
_classPrivateFieldSet2(_transcript, this, updated);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "transcriptchange", _classPrivateFieldGet2(_transcript, this));
}
break;
}
case "metrics":
_classPrivateFieldSet2(_metrics, this, {
llm_ms: msg.llm_ms,
tts_ms: msg.tts_ms,
first_audio_ms: msg.first_audio_ms,
total_ms: msg.total_ms
});
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "metricschange", _classPrivateFieldGet2(_metrics, this));
break;
case "turn_metrics": {
const { type: _type, ...payload } = msg;
const metrics = payload;
_classPrivateFieldSet2(_turnMetrics, this, metrics);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "turnmetrics", metrics);
break;
}
case "completion_outcome": {
const outcome = {
code: msg.code,
stage: "llm",
...msg.finishReason !== void 0 ? { finishReason: msg.finishReason } : {},
partialOutput: msg.partialOutput
};
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "completionoutcome", outcome);
break;
}
case "error": {
const voiceError = {
message: msg.message,
...msg.code !== void 0 ? { code: msg.code } : {},
...msg.stage !== void 0 ? { stage: msg.stage } : {},
...msg.retryable !== void 0 ? { retryable: msg.retryable } : {}
};
_classPrivateFieldSet2(_error, this, voiceError.message);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "voiceerror", voiceError);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", _classPrivateFieldGet2(_error, this));
break;
}
default:
_classPrivateFieldSet2(_lastCustomMessage, this, msg);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "custommessage", msg);
break;
}
}
/** Get or create the shared AudioContext. */
async function _getAudioContext() {
if (!_classPrivateFieldGet2(_audioContext, this)) _classPrivateFieldSet2(_audioContext, this, new AudioContext({ sampleRate: 48e3 }));
if (_classPrivateFieldGet2(_audioContext, this).state === "suspended") await _classPrivateFieldGet2(_audioContext, this).resume();
return _classPrivateFieldGet2(_audioContext, this);
}
/** Close the AudioContext and release resources. */
function _closeAudioContext() {
if (_classPrivateFieldGet2(_audioContext, this)) {
_assertClassBrand(_VoiceClient_brand, this, _closePlaybackOutput).call(this);
_classPrivateFieldGet2(_audioContext, this).close().catch(() => {});
_classPrivateFieldSet2(_audioContext, this, null);
_classPrivateFieldSet2(_workletRegistered, this, false);
}
}
async function _getPlaybackDestination(ctx) {
if (_classPrivateFieldGet2(_playbackDestinationPromise, this)) return _classPrivateFieldGet2(_playbackDestinationPromise, this);
if (_classPrivateFieldGet2(_playbackDestination, this)) return _classPrivateFieldGet2(_playbackDestination, this);
if (_classPrivateFieldGet2(_useDefaultPlaybackDestination, this)) return ctx.destination;
const outputGeneration = _classPrivateFieldGet2(_playbackOutputGeneration, this);
const promise = _assertClassBrand(_VoiceClient_brand, this, _initializePlaybackDestination).call(this, ctx, outputGeneration);
_classPrivateFieldSet2(_playbackDestinationPromise, this, promise);
try {
return await promise;
} finally {
if (_classPrivateFieldGet2(_playbackDestinationPromise, this) === promise) _classPrivateFieldSet2(_playbackDestinationPromise, this, null);
}
}
async function _initializePlaybackDestination(ctx, outputGeneration) {
try {
const destination = ctx.createMediaStreamDestination();
const audio = new Audio();
audio.autoplay = true;
audio.srcObject = destination.stream;
_classPrivateFieldSet2(_playbackElement, this, audio);
_classPrivateFieldSet2(_playbackDestination, this, destination);
await _assertClassBrand(_VoiceClient_brand, this, _applyOutputDevice).call(this, audio, _classPrivateFieldGet2(_outputDeviceSwitchGeneration, this));
if (!_assertClassBrand(_VoiceClient_brand, this, _isCurrentPlaybackOutput).call(this, audio, outputGeneration)) {
_assertClassBrand(_VoiceClient_brand, this, _releasePlaybackElement).call(this, audio);
return ctx.destination;
}
await audio.play();
if (!_assertClassBrand(_VoiceClient_brand, this, _isCurrentPlaybackOutput).call(this, audio, outputGeneration)) {
_assertClassBrand(_VoiceClient_brand, this, _releasePlaybackElement).call(this, audio);
return ctx.destination;
}
return destination;
} catch (err) {
console.warn("[VoiceClient] HTMLAudioElement playback output unavailable; using default AudioContext destination.", err);
_assertClassBrand(_VoiceClient_brand, this, _closePlaybackOutput).call(this);
_classPrivateFieldSet2(_useDefaultPlaybackDestination, this, true);
return ctx.destination;
}
}
function _isCurrentPlaybackOutput(audio, outputGeneration) {
return _classPrivateFieldGet2(_playbackElement, this) === audio && _classPrivateFieldGet2(_playbackOutputGeneration, this) === outputGeneration;
}
async function _applyOutputDevice(audio, generation) {
const sinkId = _classPrivateFieldGet2(_outputDeviceId, this);
const setSinkId = audio.setSinkId;
if (!setSinkId) {
if (sinkId === "default") {
_assertClassBrand(_VoiceClient_brand, this, _setOutputDeviceError).call(this, null);
return;
}
_assertClassBrand(_VoiceClient_brand, this, _setOutputDeviceError).call(this, UNSUPPORTED_OUTPUT_DEVICE_ERROR);
return;
}
try {
await setSinkId.call(audio, sinkId);
if (generation !== _classPrivateFieldGet2(_outputDeviceSwitchGeneration, this) || sinkId !== _classPrivateFieldGet2(_outputDeviceId, this)) {
if (_classPrivateFieldGet2(_playbackElement, this) === audio) await _assertClassBrand(_VoiceClient_brand, this, _applyOutputDevice).call(this, audio, _classPrivateFieldGet2(_outputDeviceSwitchGeneration, this));
return;
}
if (_classPrivateFieldGet2(_outputDeviceError, this) === UNSUPPORTED_OUTPUT_DEVICE_ERROR || _classPrivateFieldGet2(_outputDeviceError, this) === OUTPUT_DEVICE_SWITCH_ERROR) _assertClassBrand(_VoiceClient_brand, this, _setOutputDeviceError).call(this, null);
} catch {
if (generation !== _classPrivateFieldGet2(_outputDeviceSwitchGeneration, this) || sinkId !== _classPrivateFieldGet2(_outputDeviceId, this)) {
if (_classPrivateFieldGet2(_playbackElement, this) === audio) await _assertClassBrand(_VoiceClient_brand, this, _applyOutputDevice).call(this, audio, _classPrivateFieldGet2(_outputDeviceSwitchGeneration, this));
return;
}
_assertClassBrand(_VoiceClient_brand, this, _setOutputDeviceError).call(this, OUTPUT_DEVICE_SWITCH_ERROR);
}
}
function _closePlaybackOutput() {
var _this$playbackOutputG;
_classPrivateFieldSet2(_playbackOutputGeneration, this, (_this$playbackOutputG = _classPrivateFieldGet2(_playbackOutputGeneration, this), _this$playbackOutputG++, _this$playbackOutputG));
if (_classPrivateFieldGet2(_playbackElement, this)) {
_assertClassBrand(_VoiceClient_brand, this, _releasePlaybackElement).call(this, _classPrivateFieldGet2(_playbackElement, this));
_classPrivateFieldSet2(_playbackElement, this, null);
}
_classPrivateFieldSet2(_playbackDestination, this, null);
_classPrivateFieldSet2(_playbackDestinationPromise, this, null);
_classPrivateFieldSet2(_useDefaultPlaybackDestination, this, false);
}
function _releasePlaybackElement(audio) {
audio.pause();
audio.srcObject = null;
}
async function _playAudio(audioData, generation) {
try {
const ctx = await _assertClassBrand(_VoiceClient_brand, this, _getAudioContext).call(this);
let audioBuffer;
if (_classPrivateFieldGet2(_audioFormat, this) === "pcm16") {
const int16 = new Int16Array(audioData);
audioBuffer = ctx.createBuffer(1, int16.length, _classPrivateFieldGet2(_sampleRate, this));
const channel = audioBuffer.getChannelData(0);
for (let i = 0; i < int16.length; i++) channel[i] = int16[i] / 32768;
} else audioBuffer = await ctx.decodeAudioData(audioData.slice(0));
if (generation !== _classPrivateFieldGet2(_playbackGeneration, this)) return;
if (_classPrivateFieldGet2(_playbackElement, this) && _classPrivateFieldGet2(_scheduledSources, this).size === 0 && _classPrivateFieldGet2(_lastPlaybackEnd, this) !== null && ctx.currentTime - _classPrivateFieldGet2(_lastPlaybackEnd, this) > .3) _assertClassBrand(_VoiceClient_brand, this, _closePlaybackOutput).call(this);
const destination = await _assertClassBrand(_VoiceClient_brand, this, _getPlaybackDestination).call(this, ctx);
if (generation !== _classPrivateFieldGet2(_playbackGeneration, this)) return;
const source = ctx.createBufferSource();
source.buffer = audioBuffer;
source.connect(destination);
_classPrivateFieldGet2(_scheduledSources, this).add(source);
source.onended = () => {
_classPrivateFieldGet2(_scheduledSources, this).delete(source);
if (generation === _classPrivateFieldGet2(_playbackGeneration, this) && !_classPrivateFieldGet2(_isScheduling, this) && _classPrivateFieldGet2(_scheduledSources, this).size === 0 && _classPrivateFieldGet2(_playbackQueue, this).length === 0) {
_classPrivateFieldSet2(_isPlaying, this, false);
_classPrivateFieldGet2(_diagnostics, this).emit("playback.completed");
}
};
const startAt = Math.max(ctx.currentTime, _classPrivateFieldGet2(_playbackCursor, this));
_classPrivateFieldSet2(_playbackCursor, this, startAt + audioBuffer.duration);
_classPrivateFieldSet2(_lastPlaybackEnd, this, _classPrivateFieldGet2(_playbackCursor, this));
source.start(startAt);
} catch (err) {
_classPrivateFieldGet2(_diagnostics, this).emit("playback.error", { error: toVoiceError(err, "Playback failed") });
console.error("[VoiceClient] Audio playback error:", err);
}
}
async function _processPlaybackQueue() {
if (_classPrivateFieldGet2(_isScheduling, this) || _classPrivateFieldGet2(_playbackQueue, this).length === 0) return;
_classPrivateFieldSet2(_isScheduling, this, true);
_classPrivateFieldSet2(_isPlaying, this, true);
_classPrivateFieldGet2(_diagnostics, this).emit("playback.started", { chunks: _classPrivateFieldGet2(_playbackQueue, this).length });
const generation = _classPrivateFieldGet2(_playbackGeneration, this);
while (generation === _classPrivateFieldGet2(_playbackGeneration, this) && _classPrivateFieldGet2(_playbackQueue, this).length > 0) {
const audioData = _classPrivateFieldGet2(_playbackQueue, this).shift();
await _assertClassBrand(_VoiceClient_brand, this, _playAudio).call(this, audioData, generation);
}
if (generation === _classPrivateFieldGet2(_playbackGeneration, this)) {
_classPrivateFieldSet2(_isScheduling, this, false);
if (_classPrivateFieldGet2(_scheduledSources, this).size === 0) _classPrivateFieldSet2(_isPlaying, this, false);
}
}
function _stopPlayback(reason = "stopped") {
var _this$playbackGenerat;
const wasActive = _classPrivateFieldGet2(_isPlaying, this) || _classPrivateFieldGet2(_isScheduling, this) || _classPrivateFieldGet2(_playbackQueue, this).length > 0 || _classPrivateFieldGet2(_scheduledSources, this).size > 0;
_classPrivateFieldSet2(_playbackGeneration, this, (_this$playbackGenerat = _classPrivateFieldGet2(_playbackGeneration, this), _this$playbackGenerat++, _this$playbackGenerat));
const sources = [..._classPrivateFieldGet2(_scheduledSources, this)];
_classPrivateFieldGet2(_scheduledSources, this).clear();
for (const source of sources) try {
source.stop();
} catch {}
_classPrivateFieldSet2(_playbackQueue, this, []);
_classPrivateFieldSet2(_isPlaying, this, false);
_classPrivateFieldSet2(_isScheduling, this, false);
_classPrivateFieldSet2(_playbackCursor, this, 0);
_classPrivateFieldSet2(_lastPlaybackEnd, this, _classPrivateFieldGet2(_audioContext, this) ? _classPrivateFieldGet2(_audioContext, this).currentTime : null);
if (wasActive) _classPrivateFieldGet2(_diagnostics, this).emit("playback.stopped", { reason });
}
async function _startMic() {
_classPrivateFieldGet2(_diagnostics, this).emit("microphone.starting", { source: "browser" });
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: {
sampleRate: { ideal: 48e3 },
channelCount: 1,
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
} });
_classPrivateFieldSet2(_stream, this, stream);
const ctx = await _assertClassBrand(_VoiceClient_brand, this, _getAudioContext).call(this);
if (!_classPrivateFieldGet2(_workletRegistered, this)) {
const blob = new Blob([WORKLET_PROCESSOR], { type: "application/javascript" });
const workletUrl = URL.createObjectURL(blob);
await ctx.audioWorklet.addModule(workletUrl);
URL.revokeObjectURL(workletUrl);
_classPrivateFieldSet2(_workletRegistered, this, true);
}
const source = ctx.createMediaStreamSource(stream);
const workletNode = new AudioWorkletNode(ctx, "audio-capture-processor");
_classPrivateFieldSet2(_workletNode, this, workletNode);
workletNode.port.onmessage = (event) => {
if (event.data.type === "audio" && !_classPrivateFieldGet2(_isMuted, this)) {
const samples = event.data.samples;
const rms = computeRMS(samples);
const pcm = floatTo16BitPCM(samples);
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendBinary(pcm);
_assertClassBrand(_VoiceClient_brand, this, _processAudioLevel).call(this, rms);
}
};
source.connect(workletNode);
workletNode.connect(ctx.destination);
_classPrivateFieldGet2(_diagnostics, this).emit("microphone.ready", { source: "browser" });
} catch (err) {
_classPrivateFieldGet2(_diagnostics, this).emit("microphone.error", { error: toVoiceError(err, "Microphone failed") });
console.error("[VoiceClient] Mic error:", err);
_classPrivateFieldSet2(_error, this, "Microphone access denied. Please allow microphone access and try again.");
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "error", _classPrivateFieldGet2(_error, this));
}
}
function _stopMic() {
const wasActive = _classPrivateFieldGet2(_workletNode, this) !== null || _classPrivateFieldGet2(_stream, this) !== null;
_classPrivateFieldGet2(_workletNode, this)?.disconnect();
_classPrivateFieldSet2(_workletNode, this, null);
_classPrivateFieldGet2(_stream, this)?.getTracks().forEach((track) => track.stop());
_classPrivateFieldSet2(_stream, this, null);
if (wasActive) _classPrivateFieldGet2(_diagnostics, this).emit("microphone.stopped");
_assertClassBrand(_VoiceClient_brand, this, _resetDetection).call(this);
}
function _processAudioLevel(rms) {
if (_classPrivateFieldGet2(_isMuted, this)) return;
_classPrivateFieldSet2(_audioLevel, this, rms);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "audiolevelchange", rms);
if (_classPrivateFieldGet2(_isPlaying, this) && rms > _classPrivateFieldGet2(_interruptThreshold, this)) {
var _this$interruptChunkC;
_classPrivateFieldSet2(_interruptChunkCount, this, (_this$interruptChunkC = _classPrivateFieldGet2(_interruptChunkCount, this), _this$interruptChunkC++, _this$interruptChunkC));
if (_classPrivateFieldGet2(_interruptChunkCount, this) >= _classPrivateFieldGet2(_interruptChunks, this)) {
_classPrivateFieldGet2(_diagnostics, this).emit("speech.interrupt_detected");
_assertClassBrand(_VoiceClient_brand, this, _stopPlayback).call(this, "speech_interrupt");
_classPrivateFieldSet2(_interruptChunkCount, this, 0);
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON({ type: "interrupt" });
}
} else _classPrivateFieldSet2(_interruptChunkCount, this, 0);
if (rms > _classPrivateFieldGet2(_silenceThreshold, this)) {
if (!_classPrivateFieldGet2(_isSpeaking, this)) {
_classPrivateFieldSet2(_isSpeaking, this, true);
_classPrivateFieldGet2(_diagnostics, this).emit("speech.started");
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON({ type: "start_of_speech" });
}
if (_classPrivateFieldGet2(_silenceTimer, this)) {
clearTimeout(_classPrivateFieldGet2(_silenceTimer, this));
_classPrivateFieldSet2(_silenceTimer, this, null);
}
} else if (_classPrivateFieldGet2(_isSpeaking, this)) {
if (!_classPrivateFieldGet2(_silenceTimer, this)) _classPrivateFieldSet2(_silenceTimer, this, setTimeout(() => {
_classPrivateFieldSet2(_isSpeaking, this, false);
_classPrivateFieldSet2(_silenceTimer, this, null);
_classPrivateFieldGet2(_diagnostics, this).emit("speech.ended");
if (_classPrivateFieldGet2(_transport, this)?.connected) _classPrivateFieldGet2(_transport, this).sendJSON({ type: "end_of_speech" });
}, _classPrivateFieldGet2(_silenceDurationMs, this)));
}
}
function _resetDetection() {
if (_classPrivateFieldGet2(_silenceTimer, this)) {
clearTimeout(_classPrivateFieldGet2(_silenceTimer, this));
_classPrivateFieldSet2(_silenceTimer, this, null);
}
_classPrivateFieldSet2(_isSpeaking, this, false);
_classPrivateFieldSet2(_interruptChunkCount, this, 0);
_classPrivateFieldSet2(_audioLevel, this, 0);
_assertClassBrand(_VoiceClient_brand, this, _emit).call(this, "audiolevelchange", 0);
}
//#endregion
export { VOICE_PROTOCOL_VERSION, VoiceClient, WebSocketVoiceTransport };
//# sourceMappingURL=client.js.map