@wix/legends-platform-sdk 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +3 -2
  2. package/dist/cjs/hooks/index.js +6 -0
  3. package/dist/cjs/hooks/index.js.map +1 -1
  4. package/dist/cjs/hooks/useConversation/livekit/constants.js +3 -1
  5. package/dist/cjs/hooks/useConversation/livekit/constants.js.map +1 -1
  6. package/dist/cjs/hooks/useConversation/livekit/useConversation.js +82 -35
  7. package/dist/cjs/hooks/useConversation/livekit/useConversation.js.map +1 -1
  8. package/dist/cjs/hooks/useCreateConversation/useCreateConversation.js +16 -8
  9. package/dist/cjs/hooks/useCreateConversation/useCreateConversation.js.map +1 -1
  10. package/dist/cjs/hooks/useSpeechActivityDetector/helpers.js +87 -0
  11. package/dist/cjs/hooks/useSpeechActivityDetector/helpers.js.map +1 -0
  12. package/dist/cjs/hooks/useSpeechActivityDetector/index.js +16 -0
  13. package/dist/cjs/hooks/useSpeechActivityDetector/index.js.map +1 -0
  14. package/dist/cjs/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js +55 -0
  15. package/dist/cjs/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js.map +1 -0
  16. package/dist/cjs/hooks/useSpeechActivityDetector/types.js +4 -0
  17. package/dist/cjs/hooks/useSpeechActivityDetector/types.js.map +1 -0
  18. package/dist/cjs/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js +52 -0
  19. package/dist/cjs/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js.map +1 -0
  20. package/dist/cjs/sdk.js +6 -0
  21. package/dist/cjs/sdk.js.map +1 -1
  22. package/dist/cjs/types/service.js.map +1 -1
  23. package/dist/esm/hooks/index.js +1 -0
  24. package/dist/esm/hooks/index.js.map +1 -1
  25. package/dist/esm/hooks/useConversation/livekit/constants.js +2 -0
  26. package/dist/esm/hooks/useConversation/livekit/constants.js.map +1 -1
  27. package/dist/esm/hooks/useConversation/livekit/useConversation.js +82 -35
  28. package/dist/esm/hooks/useConversation/livekit/useConversation.js.map +1 -1
  29. package/dist/esm/hooks/useCreateConversation/useCreateConversation.js +16 -8
  30. package/dist/esm/hooks/useCreateConversation/useCreateConversation.js.map +1 -1
  31. package/dist/esm/hooks/useSpeechActivityDetector/helpers.js +83 -0
  32. package/dist/esm/hooks/useSpeechActivityDetector/helpers.js.map +1 -0
  33. package/dist/esm/hooks/useSpeechActivityDetector/index.js +3 -0
  34. package/dist/esm/hooks/useSpeechActivityDetector/index.js.map +1 -0
  35. package/dist/esm/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js +51 -0
  36. package/dist/esm/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js.map +1 -0
  37. package/dist/esm/hooks/useSpeechActivityDetector/types.js +2 -0
  38. package/dist/esm/hooks/useSpeechActivityDetector/types.js.map +1 -0
  39. package/dist/esm/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js +44 -0
  40. package/dist/esm/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js.map +1 -0
  41. package/dist/esm/sdk.js +1 -0
  42. package/dist/esm/sdk.js.map +1 -1
  43. package/dist/esm/types/service.js.map +1 -1
  44. package/dist/types/hooks/index.d.ts +1 -0
  45. package/dist/types/hooks/index.d.ts.map +1 -1
  46. package/dist/types/hooks/useConversation/livekit/constants.d.ts +2 -0
  47. package/dist/types/hooks/useConversation/livekit/constants.d.ts.map +1 -1
  48. package/dist/types/hooks/useConversation/livekit/useConversation.d.ts.map +1 -1
  49. package/dist/types/hooks/useCreateConversation/useCreateConversation.d.ts +4 -0
  50. package/dist/types/hooks/useCreateConversation/useCreateConversation.d.ts.map +1 -1
  51. package/dist/types/hooks/useSpeechActivityDetector/helpers.d.ts +19 -0
  52. package/dist/types/hooks/useSpeechActivityDetector/helpers.d.ts.map +1 -0
  53. package/dist/types/hooks/useSpeechActivityDetector/index.d.ts +3 -0
  54. package/dist/types/hooks/useSpeechActivityDetector/index.d.ts.map +1 -0
  55. package/dist/types/hooks/useSpeechActivityDetector/speechActivityProcessorSource.d.ts +3 -0
  56. package/dist/types/hooks/useSpeechActivityDetector/speechActivityProcessorSource.d.ts.map +1 -0
  57. package/dist/types/hooks/useSpeechActivityDetector/types.d.ts +14 -0
  58. package/dist/types/hooks/useSpeechActivityDetector/types.d.ts.map +1 -0
  59. package/dist/types/hooks/useSpeechActivityDetector/useSpeechActivityDetector.d.ts +10 -0
  60. package/dist/types/hooks/useSpeechActivityDetector/useSpeechActivityDetector.d.ts.map +1 -0
  61. package/dist/types/sdk.d.ts +1 -0
  62. package/dist/types/sdk.d.ts.map +1 -1
  63. package/dist/types/types/service.d.ts +4 -0
  64. package/dist/types/types/service.d.ts.map +1 -1
  65. package/package.json +3 -3
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.setupSpeechActivityDetector = setupSpeechActivityDetector;
5
+ var _speechActivityProcessorSource = require("./speechActivityProcessorSource");
6
+ const DEFAULT_THRESHOLD_RMS = 0.015;
7
+ const DEFAULT_HANGOVER_MS = 300;
8
+ const WORKLET_BLOCK_SIZE = 128;
9
+ /**
10
+ * Attaches an AudioWorkletNode-based RMS threshold+hangover detector to a
11
+ * LiveKit track's underlying MediaStreamTrack. Runs on the dedicated audio
12
+ * rendering thread — unlike requestAnimationFrame+AnalyserNode, it keeps
13
+ * running when the tab is backgrounded.
14
+ */
15
+ function setupSpeechActivityDetector(track, options) {
16
+ if (!track) {
17
+ return {
18
+ cleanup: () => {}
19
+ };
20
+ }
21
+ let cancelled = false;
22
+ let audioContext = null;
23
+ let mediaStreamSource = null;
24
+ let node = null;
25
+ const thresholdRms = options.thresholdRms ?? DEFAULT_THRESHOLD_RMS;
26
+ const hangoverMs = options.hangoverMs ?? DEFAULT_HANGOVER_MS;
27
+ const setup = async () => {
28
+ const ctx = new AudioContext();
29
+ audioContext = ctx;
30
+ if (ctx.state === 'suspended') {
31
+ await ctx.resume();
32
+ }
33
+
34
+ // Hangover is expressed in ms; convert to a block count using this
35
+ // context's actual sample rate (block size is fixed at 128 samples by
36
+ // the Web Audio spec, but wall-clock duration per block varies with
37
+ // sample rate across devices/browsers).
38
+ const hangoverBlocks = Math.max(1, Math.round(hangoverMs / 1000 * (ctx.sampleRate / WORKLET_BLOCK_SIZE)));
39
+ const blob = new Blob([_speechActivityProcessorSource.SPEECH_ACTIVITY_PROCESSOR_SOURCE], {
40
+ type: 'application/javascript'
41
+ });
42
+ const blobUrl = URL.createObjectURL(blob);
43
+ try {
44
+ await ctx.audioWorklet.addModule(blobUrl);
45
+ } finally {
46
+ URL.revokeObjectURL(blobUrl);
47
+ }
48
+ if (cancelled) {
49
+ await ctx.close();
50
+ return;
51
+ }
52
+ mediaStreamSource = ctx.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));
53
+ node = new AudioWorkletNode(ctx, _speechActivityProcessorSource.SPEECH_ACTIVITY_PROCESSOR_NAME, {
54
+ processorOptions: {
55
+ thresholdRms,
56
+ hangoverBlocks
57
+ }
58
+ });
59
+ node.port.onmessage = event => {
60
+ var _event$data, _event$data2;
61
+ const timestampMs = performance.now();
62
+ if (((_event$data = event.data) == null ? void 0 : _event$data.type) === 'onset') {
63
+ options.onSpeechStart == null || options.onSpeechStart(timestampMs);
64
+ } else if (((_event$data2 = event.data) == null ? void 0 : _event$data2.type) === 'offset') {
65
+ options.onSpeechEnd == null || options.onSpeechEnd(timestampMs);
66
+ }
67
+ };
68
+ mediaStreamSource.connect(node);
69
+ };
70
+ setup().catch(error => {
71
+ // eslint-disable-next-line no-console
72
+ console.error('[useSpeechActivityDetector] failed to set up AudioWorklet', error);
73
+ });
74
+ return {
75
+ cleanup: () => {
76
+ var _mediaStreamSource, _audioContext;
77
+ cancelled = true;
78
+ if (node) {
79
+ node.port.onmessage = null;
80
+ node.disconnect();
81
+ }
82
+ (_mediaStreamSource = mediaStreamSource) == null || _mediaStreamSource.disconnect();
83
+ void ((_audioContext = audioContext) == null ? void 0 : _audioContext.close());
84
+ }
85
+ };
86
+ }
87
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_speechActivityProcessorSource","require","DEFAULT_THRESHOLD_RMS","DEFAULT_HANGOVER_MS","WORKLET_BLOCK_SIZE","setupSpeechActivityDetector","track","options","cleanup","cancelled","audioContext","mediaStreamSource","node","thresholdRms","hangoverMs","setup","ctx","AudioContext","state","resume","hangoverBlocks","Math","max","round","sampleRate","blob","Blob","SPEECH_ACTIVITY_PROCESSOR_SOURCE","type","blobUrl","URL","createObjectURL","audioWorklet","addModule","revokeObjectURL","close","createMediaStreamSource","MediaStream","mediaStreamTrack","AudioWorkletNode","SPEECH_ACTIVITY_PROCESSOR_NAME","processorOptions","port","onmessage","event","_event$data","_event$data2","timestampMs","performance","now","data","onSpeechStart","onSpeechEnd","connect","catch","error","console","_mediaStreamSource","_audioContext","disconnect"],"sources":["../../../../src/hooks/useSpeechActivityDetector/helpers.ts"],"sourcesContent":["import { Track } from 'livekit-client';\nimport {\n SPEECH_ACTIVITY_PROCESSOR_NAME,\n SPEECH_ACTIVITY_PROCESSOR_SOURCE,\n} from './speechActivityProcessorSource';\n\nconst DEFAULT_THRESHOLD_RMS = 0.015;\nconst DEFAULT_HANGOVER_MS = 300;\nconst WORKLET_BLOCK_SIZE = 128;\n\nexport type SetupSpeechActivityDetectorOptions = {\n thresholdRms?: number;\n hangoverMs?: number;\n minSpeechMs?: number;\n onSpeechStart?: (timestampMs: number) => void;\n onSpeechEnd?: (timestampMs: number) => void;\n onMisfire?: () => void;\n};\n\n/**\n * Attaches an AudioWorkletNode-based RMS threshold+hangover detector to a\n * LiveKit track's underlying MediaStreamTrack. Runs on the dedicated audio\n * rendering thread — unlike requestAnimationFrame+AnalyserNode, it keeps\n * running when the tab is backgrounded.\n */\nexport function setupSpeechActivityDetector(\n track: Track | undefined,\n options: SetupSpeechActivityDetectorOptions,\n): { cleanup: () => void } {\n if (!track) {\n return { cleanup: () => {} };\n }\n\n let cancelled = false;\n let audioContext: AudioContext | null = null;\n let mediaStreamSource: MediaStreamAudioSourceNode | null = null;\n let node: AudioWorkletNode | null = null;\n\n const thresholdRms = options.thresholdRms ?? DEFAULT_THRESHOLD_RMS;\n const hangoverMs = options.hangoverMs ?? DEFAULT_HANGOVER_MS;\n\n const setup = async () => {\n const ctx = new AudioContext();\n audioContext = ctx;\n if (ctx.state === 'suspended') {\n await ctx.resume();\n }\n\n // Hangover is expressed in ms; convert to a block count using this\n // context's actual sample rate (block size is fixed at 128 samples by\n // the Web Audio spec, but wall-clock duration per block varies with\n // sample rate across devices/browsers).\n const hangoverBlocks = Math.max(\n 1,\n Math.round((hangoverMs / 1000) * (ctx.sampleRate / WORKLET_BLOCK_SIZE)),\n );\n\n const blob = new Blob([SPEECH_ACTIVITY_PROCESSOR_SOURCE], {\n type: 'application/javascript',\n });\n const blobUrl = URL.createObjectURL(blob);\n try {\n await ctx.audioWorklet.addModule(blobUrl);\n } finally {\n URL.revokeObjectURL(blobUrl);\n }\n\n if (cancelled) {\n await ctx.close();\n return;\n }\n\n mediaStreamSource = ctx.createMediaStreamSource(\n new MediaStream([track.mediaStreamTrack]),\n );\n node = new AudioWorkletNode(ctx, SPEECH_ACTIVITY_PROCESSOR_NAME, {\n processorOptions: { thresholdRms, hangoverBlocks },\n });\n node.port.onmessage = (event: MessageEvent) => {\n const timestampMs = performance.now();\n if (event.data?.type === 'onset') {\n options.onSpeechStart?.(timestampMs);\n } else if (event.data?.type === 'offset') {\n options.onSpeechEnd?.(timestampMs);\n }\n };\n mediaStreamSource.connect(node);\n };\n\n setup().catch((error) => {\n // eslint-disable-next-line no-console\n console.error(\n '[useSpeechActivityDetector] failed to set up AudioWorklet',\n error,\n );\n });\n\n return {\n cleanup: () => {\n cancelled = true;\n if (node) {\n node.port.onmessage = null;\n node.disconnect();\n }\n mediaStreamSource?.disconnect();\n void audioContext?.close();\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ var _useSpeechActivityDetector = require("./useSpeechActivityDetector");
5
+ Object.keys(_useSpeechActivityDetector).forEach(function (key) {
6
+ if (key === "default" || key === "__esModule") return;
7
+ if (key in exports && exports[key] === _useSpeechActivityDetector[key]) return;
8
+ exports[key] = _useSpeechActivityDetector[key];
9
+ });
10
+ var _types = require("./types");
11
+ Object.keys(_types).forEach(function (key) {
12
+ if (key === "default" || key === "__esModule") return;
13
+ if (key in exports && exports[key] === _types[key]) return;
14
+ exports[key] = _types[key];
15
+ });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_useSpeechActivityDetector","require","Object","keys","forEach","key","exports","_types"],"sources":["../../../../src/hooks/useSpeechActivityDetector/index.ts"],"sourcesContent":["export * from './useSpeechActivityDetector';\nexport * from './types';\n"],"mappings":";;;AAAA,IAAAA,0BAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,0BAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,0BAAA,CAAAK,GAAA;EAAAC,OAAA,CAAAD,GAAA,IAAAL,0BAAA,CAAAK,GAAA;AAAA;AACA,IAAAE,MAAA,GAAAN,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAI,MAAA,EAAAH,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAE,MAAA,CAAAF,GAAA;EAAAC,OAAA,CAAAD,GAAA,IAAAE,MAAA,CAAAF,GAAA;AAAA","ignoreList":[]}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.SPEECH_ACTIVITY_PROCESSOR_SOURCE = exports.SPEECH_ACTIVITY_PROCESSOR_NAME = void 0;
5
+ // AudioWorkletProcessor source, kept as a plain string and loaded via a Blob
6
+ // URL (see helpers.ts). AudioWorkletGlobalScope runs isolated code on the
7
+ // dedicated audio rendering thread — it is not throttled when the tab is
8
+ // backgrounded, unlike requestAnimationFrame-driven analysis. Threshold
9
+ // crossings are posted back to the main thread via `port.postMessage`.
10
+ const SPEECH_ACTIVITY_PROCESSOR_NAME = exports.SPEECH_ACTIVITY_PROCESSOR_NAME = 'speech-activity-processor';
11
+ const SPEECH_ACTIVITY_PROCESSOR_SOURCE = exports.SPEECH_ACTIVITY_PROCESSOR_SOURCE = `
12
+ class SpeechActivityProcessor extends AudioWorkletProcessor {
13
+ constructor(options) {
14
+ super();
15
+ const opts = (options && options.processorOptions) || {};
16
+ this.threshold = typeof opts.thresholdRms === 'number' ? opts.thresholdRms : 0.015;
17
+ this.hangoverBlocks = typeof opts.hangoverBlocks === 'number' ? opts.hangoverBlocks : 8;
18
+ this.speaking = false;
19
+ this.silentBlockCount = 0;
20
+ }
21
+
22
+ process(inputs) {
23
+ const input = inputs[0];
24
+ const channel = input && input[0];
25
+ if (!channel || channel.length === 0) {
26
+ return true;
27
+ }
28
+
29
+ let sumSquares = 0;
30
+ for (let i = 0; i < channel.length; i++) {
31
+ sumSquares += channel[i] * channel[i];
32
+ }
33
+ const rms = Math.sqrt(sumSquares / channel.length);
34
+ const isLoud = rms > this.threshold;
35
+
36
+ if (isLoud) {
37
+ this.silentBlockCount = 0;
38
+ if (!this.speaking) {
39
+ this.speaking = true;
40
+ this.port.postMessage({ type: 'onset', rms: rms });
41
+ }
42
+ } else if (this.speaking) {
43
+ this.silentBlockCount += 1;
44
+ if (this.silentBlockCount >= this.hangoverBlocks) {
45
+ this.speaking = false;
46
+ this.port.postMessage({ type: 'offset', rms: rms });
47
+ }
48
+ }
49
+ return true;
50
+ }
51
+ }
52
+
53
+ registerProcessor('${SPEECH_ACTIVITY_PROCESSOR_NAME}', SpeechActivityProcessor);
54
+ `;
55
+ //# sourceMappingURL=speechActivityProcessorSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SPEECH_ACTIVITY_PROCESSOR_NAME","exports","SPEECH_ACTIVITY_PROCESSOR_SOURCE"],"sources":["../../../../src/hooks/useSpeechActivityDetector/speechActivityProcessorSource.ts"],"sourcesContent":["// AudioWorkletProcessor source, kept as a plain string and loaded via a Blob\n// URL (see helpers.ts). AudioWorkletGlobalScope runs isolated code on the\n// dedicated audio rendering thread — it is not throttled when the tab is\n// backgrounded, unlike requestAnimationFrame-driven analysis. Threshold\n// crossings are posted back to the main thread via `port.postMessage`.\nexport const SPEECH_ACTIVITY_PROCESSOR_NAME = 'speech-activity-processor';\n\nexport const SPEECH_ACTIVITY_PROCESSOR_SOURCE = `\nclass SpeechActivityProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const opts = (options && options.processorOptions) || {};\n this.threshold = typeof opts.thresholdRms === 'number' ? opts.thresholdRms : 0.015;\n this.hangoverBlocks = typeof opts.hangoverBlocks === 'number' ? opts.hangoverBlocks : 8;\n this.speaking = false;\n this.silentBlockCount = 0;\n }\n\n process(inputs) {\n const input = inputs[0];\n const channel = input && input[0];\n if (!channel || channel.length === 0) {\n return true;\n }\n\n let sumSquares = 0;\n for (let i = 0; i < channel.length; i++) {\n sumSquares += channel[i] * channel[i];\n }\n const rms = Math.sqrt(sumSquares / channel.length);\n const isLoud = rms > this.threshold;\n\n if (isLoud) {\n this.silentBlockCount = 0;\n if (!this.speaking) {\n this.speaking = true;\n this.port.postMessage({ type: 'onset', rms: rms });\n }\n } else if (this.speaking) {\n this.silentBlockCount += 1;\n if (this.silentBlockCount >= this.hangoverBlocks) {\n this.speaking = false;\n this.port.postMessage({ type: 'offset', rms: rms });\n }\n }\n return true;\n }\n}\n\nregisterProcessor('${SPEECH_ACTIVITY_PROCESSOR_NAME}', SpeechActivityProcessor);\n`;\n"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACO,MAAMA,8BAA8B,GAAAC,OAAA,CAAAD,8BAAA,GAAG,2BAA2B;AAElE,MAAME,gCAAgC,GAAAD,OAAA,CAAAC,gCAAA,GAAG;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqBF,8BAA8B;AACnD,CAAC","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["../../../../src/hooks/useSpeechActivityDetector/types.ts"],"sourcesContent":["export type UseSpeechActivityDetectorOptions = {\n source: 'user' | 'agent';\n onSpeechStart?: (timestampMs: number) => void;\n onSpeechEnd?: (timestampMs: number) => void;\n /** Fired instead of onSpeechEnd when a segment was shorter than minSpeechMs. */\n onMisfire?: () => void;\n /** RMS threshold (0-1) above which a block counts as \"loud\". Default 0.015. */\n thresholdRms?: number;\n /** Consecutive silence duration before an \"offset\" fires. Default 300ms. */\n hangoverMs?: number;\n /** Minimum onset-to-offset duration to count as real speech, not a blip/echo. Default 0 (no filtering). */\n minSpeechMs?: number;\n};\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.useSpeechActivityDetector = useSpeechActivityDetector;
5
+ var _react = require("react");
6
+ var _useParticipantTrack = require("../useParticipantTrack");
7
+ var _helpers = require("./helpers");
8
+ /**
9
+ * Acoustic speech onset/offset detector for a participant's microphone
10
+ * track, driven by AudioWorkletNode rather than LiveKit's speaking-state
11
+ * events. Useful when the caller needs a timestamp tied to the actual audio
12
+ * signal (e.g. perceived end-to-end latency measurement) rather than a
13
+ * VAD/turn-detection-derived event.
14
+ */
15
+ function useSpeechActivityDetector(options) {
16
+ const {
17
+ source,
18
+ onSpeechStart,
19
+ onSpeechEnd,
20
+ thresholdRms,
21
+ hangoverMs
22
+ } = options;
23
+ const {
24
+ track
25
+ } = (0, _useParticipantTrack.useParticipantTrack)({
26
+ type: source,
27
+ source: _useParticipantTrack.Source.Microphone
28
+ });
29
+
30
+ // Keep latest callbacks available to the effect without retriggering
31
+ // AudioContext/AudioWorkletNode setup on every render.
32
+ const callbacksRef = (0, _react.useRef)({
33
+ onSpeechStart,
34
+ onSpeechEnd
35
+ });
36
+ callbacksRef.current = {
37
+ onSpeechStart,
38
+ onSpeechEnd
39
+ };
40
+ (0, _react.useEffect)(() => {
41
+ const {
42
+ cleanup
43
+ } = (0, _helpers.setupSpeechActivityDetector)(track, {
44
+ thresholdRms,
45
+ hangoverMs,
46
+ onSpeechStart: timestampMs => callbacksRef.current.onSpeechStart == null ? void 0 : callbacksRef.current.onSpeechStart(timestampMs),
47
+ onSpeechEnd: timestampMs => callbacksRef.current.onSpeechEnd == null ? void 0 : callbacksRef.current.onSpeechEnd(timestampMs)
48
+ });
49
+ return cleanup;
50
+ }, [track == null ? void 0 : track.mediaStreamTrack, thresholdRms, hangoverMs]);
51
+ }
52
+ //# sourceMappingURL=useSpeechActivityDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_useParticipantTrack","_helpers","useSpeechActivityDetector","options","source","onSpeechStart","onSpeechEnd","thresholdRms","hangoverMs","track","useParticipantTrack","type","Source","Microphone","callbacksRef","useRef","current","useEffect","cleanup","setupSpeechActivityDetector","timestampMs","mediaStreamTrack"],"sources":["../../../../src/hooks/useSpeechActivityDetector/useSpeechActivityDetector.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useParticipantTrack, Source } from '../useParticipantTrack';\nimport { setupSpeechActivityDetector } from './helpers';\nimport { type UseSpeechActivityDetectorOptions } from './types';\n\n/**\n * Acoustic speech onset/offset detector for a participant's microphone\n * track, driven by AudioWorkletNode rather than LiveKit's speaking-state\n * events. Useful when the caller needs a timestamp tied to the actual audio\n * signal (e.g. perceived end-to-end latency measurement) rather than a\n * VAD/turn-detection-derived event.\n */\nexport function useSpeechActivityDetector(\n options: UseSpeechActivityDetectorOptions,\n): void {\n const { source, onSpeechStart, onSpeechEnd, thresholdRms, hangoverMs } =\n options;\n const { track } = useParticipantTrack({\n type: source,\n source: Source.Microphone,\n });\n\n // Keep latest callbacks available to the effect without retriggering\n // AudioContext/AudioWorkletNode setup on every render.\n const callbacksRef = useRef({ onSpeechStart, onSpeechEnd });\n callbacksRef.current = { onSpeechStart, onSpeechEnd };\n\n useEffect(() => {\n const { cleanup } = setupSpeechActivityDetector(track, {\n thresholdRms,\n hangoverMs,\n onSpeechStart: (timestampMs) =>\n callbacksRef.current.onSpeechStart?.(timestampMs),\n onSpeechEnd: (timestampMs) =>\n callbacksRef.current.onSpeechEnd?.(timestampMs),\n });\n return cleanup;\n }, [track?.mediaStreamTrack, thresholdRms, hangoverMs]);\n}\n"],"mappings":";;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,oBAAA,GAAAD,OAAA;AACA,IAAAE,QAAA,GAAAF,OAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,yBAAyBA,CACvCC,OAAyC,EACnC;EACN,MAAM;IAAEC,MAAM;IAAEC,aAAa;IAAEC,WAAW;IAAEC,YAAY;IAAEC;EAAW,CAAC,GACpEL,OAAO;EACT,MAAM;IAAEM;EAAM,CAAC,GAAG,IAAAC,wCAAmB,EAAC;IACpCC,IAAI,EAAEP,MAAM;IACZA,MAAM,EAAEQ,2BAAM,CAACC;EACjB,CAAC,CAAC;;EAEF;EACA;EACA,MAAMC,YAAY,GAAG,IAAAC,aAAM,EAAC;IAAEV,aAAa;IAAEC;EAAY,CAAC,CAAC;EAC3DQ,YAAY,CAACE,OAAO,GAAG;IAAEX,aAAa;IAAEC;EAAY,CAAC;EAErD,IAAAW,gBAAS,EAAC,MAAM;IACd,MAAM;MAAEC;IAAQ,CAAC,GAAG,IAAAC,oCAA2B,EAACV,KAAK,EAAE;MACrDF,YAAY;MACZC,UAAU;MACVH,aAAa,EAAGe,WAAW,IACzBN,YAAY,CAACE,OAAO,CAACX,aAAa,oBAAlCS,YAAY,CAACE,OAAO,CAACX,aAAa,CAAGe,WAAW,CAAC;MACnDd,WAAW,EAAGc,WAAW,IACvBN,YAAY,CAACE,OAAO,CAACV,WAAW,oBAAhCQ,YAAY,CAACE,OAAO,CAACV,WAAW,CAAGc,WAAW;IAClD,CAAC,CAAC;IACF,OAAOF,OAAO;EAChB,CAAC,EAAE,CAACT,KAAK,oBAALA,KAAK,CAAEY,gBAAgB,EAAEd,YAAY,EAAEC,UAAU,CAAC,CAAC;AACzD","ignoreList":[]}
package/dist/cjs/sdk.js CHANGED
@@ -139,6 +139,12 @@ Object.keys(_useParticipantTrack).forEach(function (key) {
139
139
  if (key in exports && exports[key] === _useParticipantTrack[key]) return;
140
140
  exports[key] = _useParticipantTrack[key];
141
141
  });
142
+ var _useSpeechActivityDetector = require("./hooks/useSpeechActivityDetector");
143
+ Object.keys(_useSpeechActivityDetector).forEach(function (key) {
144
+ if (key === "default" || key === "__esModule") return;
145
+ if (key in exports && exports[key] === _useSpeechActivityDetector[key]) return;
146
+ exports[key] = _useSpeechActivityDetector[key];
147
+ });
142
148
  var _types2 = require("./conversation/types");
143
149
  Object.keys(_types2).forEach(function (key) {
144
150
  if (key === "default" || key === "__esModule") return;
@@ -1 +1 @@
1
- {"version":3,"names":["_types","require","Object","keys","forEach","key","exports","_LegendsPlatform","_LegendsPlatformQLess","_useTextChat","_useConversation","_useTranscriptions","_useConversationStatus","_useMicrophone","_useSpeaker","_useCamera","_useMediaRecorder","_useConversationTimer","_useAudioLevel","_useConversationEvents","_useUserActivity","_useAudio","_useScreenShare","_useConnectionState","_useActivityState","_usePictureInPicture","_useMediaPermissions","_useTools","_useParticipantTrack","_types2"],"sources":["../../src/sdk.ts"],"sourcesContent":["export * from './types';\nexport * from './components/LegendsPlatform';\nexport * from './components/LegendsPlatformQLess';\n\nexport * from './hooks/useTextChat';\nexport * from './hooks/useConversation';\nexport * from './hooks/useTranscriptions';\nexport * from './hooks/useConversationStatus';\nexport * from './hooks/useMicrophone';\nexport * from './hooks/useSpeaker';\nexport * from './hooks/useCamera';\nexport * from './hooks/useMediaRecorder';\nexport * from './hooks/useConversationTimer';\nexport * from './hooks/useAudioLevel';\nexport * from './hooks/useConversationEvents';\nexport * from './hooks/useUserActivity';\nexport * from './hooks/useAudio';\nexport * from './hooks/useScreenShare';\nexport * from './hooks/useConnectionState';\nexport * from './hooks/useActivityState';\nexport * from './hooks/usePictureInPicture';\nexport * from './hooks/useMediaPermissions';\nexport * from './hooks/useTools';\nexport * from './hooks/useParticipantTrack';\n\nexport * from './conversation/types';\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_types","require","Object","keys","forEach","key","exports","_LegendsPlatform","_LegendsPlatformQLess","_useTextChat","_useConversation","_useTranscriptions","_useConversationStatus","_useMicrophone","_useSpeaker","_useCamera","_useMediaRecorder","_useConversationTimer","_useAudioLevel","_useConversationEvents","_useUserActivity","_useAudio","_useScreenShare","_useConnectionState","_useActivityState","_usePictureInPicture","_useMediaPermissions","_useTools","_useParticipantTrack","_useSpeechActivityDetector","_types2"],"sources":["../../src/sdk.ts"],"sourcesContent":["export * from './types';\nexport * from './components/LegendsPlatform';\nexport * from './components/LegendsPlatformQLess';\n\nexport * from './hooks/useTextChat';\nexport * from './hooks/useConversation';\nexport * from './hooks/useTranscriptions';\nexport * from './hooks/useConversationStatus';\nexport * from './hooks/useMicrophone';\nexport * from './hooks/useSpeaker';\nexport * from './hooks/useCamera';\nexport * from './hooks/useMediaRecorder';\nexport * from './hooks/useConversationTimer';\nexport * from './hooks/useAudioLevel';\nexport * from './hooks/useConversationEvents';\nexport * from './hooks/useUserActivity';\nexport * from './hooks/useAudio';\nexport * from './hooks/useScreenShare';\nexport * from './hooks/useConnectionState';\nexport * from './hooks/useActivityState';\nexport * from './hooks/usePictureInPicture';\nexport * from './hooks/useMediaPermissions';\nexport * from './hooks/useTools';\nexport * from './hooks/useParticipantTrack';\nexport * from './hooks/useSpeechActivityDetector';\n\nexport * from './conversation/types';\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../../src/types/service.ts"],"sourcesContent":["export type FunctionCall = {\n functionCallId: string;\n name: string;\n arguments: string;\n};\n\nexport type FunctionCallResult = {\n functionCallId: string;\n result: string;\n error?: string;\n};\n\nexport type LimitationType = 'MINUTES' | 'SECONDS' | 'TOKENS';\n\nexport interface Limitation {\n type?: LimitationType;\n limit?: number;\n}\n\nexport interface ConversationResult {\n url: string;\n id: string;\n limit?: number;\n}\n\nexport interface CreateConversationParams {\n chatId: string;\n namespace: string;\n chatConfig?: object;\n}\n\nexport interface EndConversationParams {\n chatId: string;\n conversationId: string;\n namespace: string;\n}\n\nexport interface ChatMessageAttachment {\n fileName?: string;\n url?: string;\n mimeType?: string;\n textFileContent?: string;\n}\n\nexport interface SendMessageParams {\n chatId: string;\n namespace: string;\n message: string;\n functionCallResults?: FunctionCallResult[];\n chatConfig?: object;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface ChatMessage {\n id: string;\n content: string;\n role: 'user' | 'assistant';\n createdAt?: string;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface QueryMessagesParams {\n chatId: string;\n namespace: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface QueryMessagesResult {\n messages: ChatMessage[];\n nextCursor?: string;\n}\n\nexport interface IPersonaChatService {\n createVoiceConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n createInteractiveConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n endInteractiveConversation(params: EndConversationParams): Promise<void>;\n sendChatMessage(\n params: SendMessageParams,\n ): Promise<{ functionCalls?: FunctionCall[]; messages?: ChatMessage[] }>;\n queryChatMessages(params: QueryMessagesParams): Promise<QueryMessagesResult>;\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../../src/types/service.ts"],"sourcesContent":["export type FunctionCall = {\n functionCallId: string;\n name: string;\n arguments: string;\n};\n\nexport type FunctionCallResult = {\n functionCallId: string;\n result: string;\n error?: string;\n};\n\nexport type LimitationType = 'MINUTES' | 'SECONDS' | 'TOKENS';\n\nexport interface Limitation {\n type?: LimitationType;\n limit?: number;\n}\n\nexport interface ConversationResult {\n url: string;\n id: string;\n limit?: number;\n}\n\nexport interface CreateConversationParams {\n chatId: string;\n namespace: string;\n chatConfig?: object;\n /** Whether this is the visitor's first-ever conversation with the persona (drives the opening message). */\n firstConversation?: boolean;\n /** Title of the visitor's last meaningful conversation, for a returning-visitor greeting. */\n lastConversationTopic?: string;\n}\n\nexport interface EndConversationParams {\n chatId: string;\n conversationId: string;\n namespace: string;\n}\n\nexport interface ChatMessageAttachment {\n fileName?: string;\n url?: string;\n mimeType?: string;\n textFileContent?: string;\n}\n\nexport interface SendMessageParams {\n chatId: string;\n namespace: string;\n message: string;\n functionCallResults?: FunctionCallResult[];\n chatConfig?: object;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface ChatMessage {\n id: string;\n content: string;\n role: 'user' | 'assistant';\n createdAt?: string;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface QueryMessagesParams {\n chatId: string;\n namespace: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface QueryMessagesResult {\n messages: ChatMessage[];\n nextCursor?: string;\n}\n\nexport interface IPersonaChatService {\n createVoiceConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n createInteractiveConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n endInteractiveConversation(params: EndConversationParams): Promise<void>;\n sendChatMessage(\n params: SendMessageParams,\n ): Promise<{ functionCalls?: FunctionCall[]; messages?: ChatMessage[] }>;\n queryChatMessages(params: QueryMessagesParams): Promise<QueryMessagesResult>;\n}\n"],"mappings":"","ignoreList":[]}
@@ -7,4 +7,5 @@ export * from './useUserActivity';
7
7
  export * from './useScreenShare';
8
8
  export * from './usePictureInPicture';
9
9
  export * from './useTools';
10
+ export * from './useSpeechActivityDetector';
10
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../../src/hooks/index.ts"],"sourcesContent":["export * from './useConversation';\nexport * from './useCreateConversation';\nexport * from './useConversationStatus';\nexport * from './useTextChat';\nexport * from './useConversationEvents';\nexport * from './useUserActivity';\nexport * from './useScreenShare';\nexport * from './usePictureInPicture';\nexport * from './useTools';\n"],"mappings":"AAAA,cAAc,mBAAmB;AACjC,cAAc,yBAAyB;AACvC,cAAc,yBAAyB;AACvC,cAAc,eAAe;AAC7B,cAAc,yBAAyB;AACvC,cAAc,mBAAmB;AACjC,cAAc,kBAAkB;AAChC,cAAc,uBAAuB;AACrC,cAAc,YAAY","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../../src/hooks/index.ts"],"sourcesContent":["export * from './useConversation';\nexport * from './useCreateConversation';\nexport * from './useConversationStatus';\nexport * from './useTextChat';\nexport * from './useConversationEvents';\nexport * from './useUserActivity';\nexport * from './useScreenShare';\nexport * from './usePictureInPicture';\nexport * from './useTools';\nexport * from './useSpeechActivityDetector';\n"],"mappings":"AAAA,cAAc,mBAAmB;AACjC,cAAc,yBAAyB;AACvC,cAAc,yBAAyB;AACvC,cAAc,eAAe;AAC7B,cAAc,yBAAyB;AACvC,cAAc,mBAAmB;AACjC,cAAc,kBAAkB;AAChC,cAAc,uBAAuB;AACrC,cAAc,YAAY;AAC1B,cAAc,6BAA6B","ignoreList":[]}
@@ -8,4 +8,6 @@ export const SPEAKING_STATE_EVENT_MAP = {
8
8
  listening: 'agentSpeakStop'
9
9
  }
10
10
  };
11
+ export const ACOUSTIC_USER_THRESHOLD_RMS = 0.025;
12
+ export const ACOUSTIC_USER_HANGOVER_MS = 400;
11
13
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["SPEAKING_STATE_EVENT_MAP","STANDARD","speaking","listening","AGENT"],"sources":["../../../../../src/hooks/useConversation/livekit/constants.ts"],"sourcesContent":["import type { TurnDetectionData } from './types';\nimport type { ConversationEvent } from '../../useConversationEvents';\n\nexport const SPEAKING_STATE_EVENT_MAP: Record<\n TurnDetectionData['participantKind'],\n Record<TurnDetectionData['state'], ConversationEvent>\n> = {\n STANDARD: {\n speaking: 'userSpeakStart',\n listening: 'userSpeakStop',\n },\n AGENT: {\n speaking: 'agentSpeakStart',\n listening: 'agentSpeakStop',\n },\n};\n"],"mappings":"AAGA,OAAO,MAAMA,wBAGZ,GAAG;EACFC,QAAQ,EAAE;IACRC,QAAQ,EAAE,gBAAgB;IAC1BC,SAAS,EAAE;EACb,CAAC;EACDC,KAAK,EAAE;IACLF,QAAQ,EAAE,iBAAiB;IAC3BC,SAAS,EAAE;EACb;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["SPEAKING_STATE_EVENT_MAP","STANDARD","speaking","listening","AGENT","ACOUSTIC_USER_THRESHOLD_RMS","ACOUSTIC_USER_HANGOVER_MS"],"sources":["../../../../../src/hooks/useConversation/livekit/constants.ts"],"sourcesContent":["import type { TurnDetectionData } from './types';\nimport type { ConversationEvent } from '../../useConversationEvents';\n\nexport const SPEAKING_STATE_EVENT_MAP: Record<\n TurnDetectionData['participantKind'],\n Record<TurnDetectionData['state'], ConversationEvent>\n> = {\n STANDARD: {\n speaking: 'userSpeakStart',\n listening: 'userSpeakStop',\n },\n AGENT: {\n speaking: 'agentSpeakStart',\n listening: 'agentSpeakStop',\n },\n};\n\nexport const ACOUSTIC_USER_THRESHOLD_RMS = 0.025;\nexport const ACOUSTIC_USER_HANGOVER_MS = 400;\n"],"mappings":"AAGA,OAAO,MAAMA,wBAGZ,GAAG;EACFC,QAAQ,EAAE;IACRC,QAAQ,EAAE,gBAAgB;IAC1BC,SAAS,EAAE;EACb,CAAC;EACDC,KAAK,EAAE;IACLF,QAAQ,EAAE,iBAAiB;IAC3BC,SAAS,EAAE;EACb;AACF,CAAC;AAED,OAAO,MAAME,2BAA2B,GAAG,KAAK;AAChD,OAAO,MAAMC,yBAAyB,GAAG,GAAG","ignoreList":[]}
@@ -7,9 +7,11 @@ import { useEnsureRoom, useMediaDeviceSelect } from '@livekit/components-react';
7
7
  import { useAtom, useAtomValue, useSetAtom } from 'jotai';
8
8
  import { state } from '../../../conversation';
9
9
  import { useCreateConversation } from '../../useCreateConversation';
10
+ import { useSpeechActivityDetector } from '../../useSpeechActivityDetector';
10
11
  import { useEmitConversationEvent } from '../../useConversationEvents/useEmitConversationEvent';
11
12
  import { useToolCallingRuntime } from '../../useTools/runtime/useToolCallingRuntime';
12
13
  import { checkIsTurnDetectionData, checkIsInactivityWarningData, checkIsInactivityTimeoutData, checkIsToolCallsData, createLivekitToolAdapter, decodeDataPayload, findJoinedAgentParticipant, mergeTranscriptions, publishConversationDataMessage, safeStringify, shouldHandleParticipantConnected } from './utils';
14
+ import { ACOUSTIC_USER_HANGOVER_MS, ACOUSTIC_USER_THRESHOLD_RMS } from './constants';
13
15
  export function useConversation(_ref) {
14
16
  var _conversation$data2;
15
17
  let _ref$autoJoin = _ref.autoJoin,
@@ -68,8 +70,11 @@ export function useConversation(_ref) {
68
70
  const setAgentActivityState = useSetAtom(state.agentActivityState);
69
71
  const setUserActivityState = useSetAtom(state.userActivityState);
70
72
  const emitEvent = useEmitConversationEvent();
71
- const userStopRef = useRef(null);
72
73
  const inactivityTimeoutRef = useRef(false);
74
+ const acousticPhaseRef = useRef('idle');
75
+ const acousticT0Ref = useRef(null);
76
+ const latestTurnMetadataRef = useRef({});
77
+ const isConnectedRef = useRef(false);
73
78
  const timer = useTimer({
74
79
  autoStart: false,
75
80
  onExpire: handleExpire,
@@ -176,14 +181,11 @@ export function useConversation(_ref) {
176
181
  switch (participantKind) {
177
182
  case 'STANDARD':
178
183
  if (eventState === 'listening') {
179
- userStopRef.current = {
180
- ts: performance.now(),
184
+ latestTurnMetadataRef.current = {
181
185
  turnDetectionStrategy: data.turnDetectionStrategy,
182
186
  sequence: data.sequence,
183
187
  speechId: data.speechId
184
188
  };
185
- } else {
186
- userStopRef.current = null;
187
189
  }
188
190
  emitEvent(eventState === 'speaking' ? 'userSpeakStart' : 'userSpeakStop', {
189
191
  conversationId: conversationId
@@ -191,16 +193,11 @@ export function useConversation(_ref) {
191
193
  setUserActivityState(eventState);
192
194
  break;
193
195
  case 'AGENT':
194
- if (eventState === 'speaking' && userStopRef.current) {
195
- const latencyMs = performance.now() - userStopRef.current.ts;
196
- emitEvent('responseLatency', {
197
- conversationId: conversationId,
198
- latencyMs,
199
- turnDetectionStrategy: userStopRef.current.turnDetectionStrategy,
200
- sequence: userStopRef.current.sequence,
201
- speechId: data.speechId ?? userStopRef.current.speechId
202
- });
203
- userStopRef.current = null;
196
+ if (data.speechId) {
197
+ latestTurnMetadataRef.current = {
198
+ ...latestTurnMetadataRef.current,
199
+ speechId: data.speechId
200
+ };
204
201
  }
205
202
  emitEvent(eventState === 'speaking' ? 'agentSpeakStart' : 'agentSpeakStop', {
206
203
  conversationId: conversationId
@@ -209,18 +206,64 @@ export function useConversation(_ref) {
209
206
  break;
210
207
  }
211
208
  }
212
- function handleInactivityWarningEvent(data) {
209
+ function handleAcousticUserOnset() {
210
+ if (!isConnectedRef.current) {
211
+ return;
212
+ }
213
+ if (acousticPhaseRef.current === 'waitingForAgent') {
214
+ acousticPhaseRef.current = 'idle';
215
+ acousticT0Ref.current = null;
216
+ }
217
+ }
218
+ function handleAcousticUserOffset(timestampMs) {
219
+ if (!isConnectedRef.current || acousticPhaseRef.current !== 'idle') {
220
+ return;
221
+ }
222
+ acousticT0Ref.current = timestampMs - ACOUSTIC_USER_HANGOVER_MS;
223
+ acousticPhaseRef.current = 'waitingForAgent';
224
+ }
225
+ function handleAcousticAgentOnset(timestampMs) {
213
226
  var _conversationRef$curr2;
214
- emitEvent('inactivityWarning', {
227
+ if (!isConnectedRef.current || acousticPhaseRef.current !== 'waitingForAgent' || acousticT0Ref.current == null) {
228
+ return;
229
+ }
230
+ const latencyMs = timestampMs - acousticT0Ref.current;
231
+ acousticT0Ref.current = null;
232
+ acousticPhaseRef.current = 'busy';
233
+ if (latencyMs < 0 || latencyMs > 15000) {
234
+ return;
235
+ }
236
+ emitEvent('responseLatency', {
215
237
  conversationId: (_conversationRef$curr2 = conversationRef.current) == null ? void 0 : _conversationRef$curr2.id,
238
+ latencyMs,
239
+ turnDetectionStrategy: latestTurnMetadataRef.current.turnDetectionStrategy,
240
+ sequence: latestTurnMetadataRef.current.sequence,
241
+ speechId: latestTurnMetadataRef.current.speechId
242
+ });
243
+ }
244
+ useSpeechActivityDetector({
245
+ source: 'user',
246
+ onSpeechStart: handleAcousticUserOnset,
247
+ onSpeechEnd: handleAcousticUserOffset,
248
+ thresholdRms: ACOUSTIC_USER_THRESHOLD_RMS,
249
+ hangoverMs: ACOUSTIC_USER_HANGOVER_MS
250
+ });
251
+ useSpeechActivityDetector({
252
+ source: 'agent',
253
+ onSpeechStart: handleAcousticAgentOnset
254
+ });
255
+ function handleInactivityWarningEvent(data) {
256
+ var _conversationRef$curr3;
257
+ emitEvent('inactivityWarning', {
258
+ conversationId: (_conversationRef$curr3 = conversationRef.current) == null ? void 0 : _conversationRef$curr3.id,
216
259
  gracePeriodSec: data.grace_period_sec
217
260
  });
218
261
  }
219
262
  function handleInactivityTimeoutEvent(_data) {
220
- var _conversationRef$curr3;
263
+ var _conversationRef$curr4;
221
264
  inactivityTimeoutRef.current = true;
222
265
  emitEvent('inactivityTimeout', {
223
- conversationId: (_conversationRef$curr3 = conversationRef.current) == null ? void 0 : _conversationRef$curr3.id
266
+ conversationId: (_conversationRef$curr4 = conversationRef.current) == null ? void 0 : _conversationRef$curr4.id
224
267
  });
225
268
  }
226
269
  async function handleToolCallsEvent(data) {
@@ -256,11 +299,12 @@ export function useConversation(_ref) {
256
299
  }
257
300
  }
258
301
  function handleConnectedEvent() {
259
- var _conversationRef$curr4;
302
+ var _conversationRef$curr5;
303
+ isConnectedRef.current = true;
260
304
  setUserConnectionState('joined');
261
305
  setAgentConnectionState('joining');
262
306
  emitEvent('callStarted', {
263
- conversationId: (_conversationRef$curr4 = conversationRef.current) == null ? void 0 : _conversationRef$curr4.id
307
+ conversationId: (_conversationRef$curr5 = conversationRef.current) == null ? void 0 : _conversationRef$curr5.id
264
308
  });
265
309
  }
266
310
  async function handleEncryptionErrorEvent(error) {
@@ -268,7 +312,10 @@ export function useConversation(_ref) {
268
312
  setConversationStatus('error');
269
313
  }
270
314
  function handleDisconnectedEvent(reason) {
271
- userStopRef.current = null;
315
+ isConnectedRef.current = false;
316
+ acousticPhaseRef.current = 'idle';
317
+ acousticT0Ref.current = null;
318
+ latestTurnMetadataRef.current = {};
272
319
  setAgentActivityState('idle');
273
320
  setAgentConnectionState('idle');
274
321
  setUserActivityState('idle');
@@ -296,18 +343,18 @@ export function useConversation(_ref) {
296
343
  }
297
344
  }
298
345
  function handleAgentJoin(participant) {
299
- var _conversationRef$curr5;
346
+ var _conversationRef$curr6;
300
347
  setAgentConnectionState('joined');
301
348
  emitEvent('agentJoined', {
302
- conversationId: (_conversationRef$curr5 = conversationRef.current) == null ? void 0 : _conversationRef$curr5.id,
349
+ conversationId: (_conversationRef$curr6 = conversationRef.current) == null ? void 0 : _conversationRef$curr6.id,
303
350
  participant
304
351
  });
305
352
  }
306
353
  function handleParticipantDisconnectedEvent(participant) {
307
- var _conversationRef$curr6;
354
+ var _conversationRef$curr7;
308
355
  setAgentConnectionState('left');
309
356
  emitEvent('agentLeft', {
310
- conversationId: (_conversationRef$curr6 = conversationRef.current) == null ? void 0 : _conversationRef$curr6.id,
357
+ conversationId: (_conversationRef$curr7 = conversationRef.current) == null ? void 0 : _conversationRef$curr7.id,
311
358
  participant
312
359
  });
313
360
  }
@@ -316,13 +363,13 @@ export function useConversation(_ref) {
316
363
  return;
317
364
  }
318
365
  try {
319
- var _conversationRef$curr7;
366
+ var _conversationRef$curr8;
320
367
  setUserConnectionState('leaving');
321
368
  await (room == null ? void 0 : room.disconnect(true));
322
369
  setConversationStatus('destroyed');
323
370
  setJoinedAt(undefined);
324
371
  emitEvent('callEnded', {
325
- conversationId: (_conversationRef$curr7 = conversationRef.current) == null ? void 0 : _conversationRef$curr7.id
372
+ conversationId: (_conversationRef$curr8 = conversationRef.current) == null ? void 0 : _conversationRef$curr8.id
326
373
  });
327
374
  } catch (error) {
328
375
  console.error('Failed to leave', error);
@@ -330,18 +377,18 @@ export function useConversation(_ref) {
330
377
  }
331
378
  }
332
379
  async function handleExpire() {
333
- var _conversationRef$curr8, _conversationRef$curr9;
380
+ var _conversationRef$curr9, _conversationRef$curr0;
334
381
  emitEvent('timeExceeded', {
335
- conversationId: (_conversationRef$curr8 = conversationRef.current) == null ? void 0 : _conversationRef$curr8.id,
336
- limit: (_conversationRef$curr9 = conversationRef.current) == null ? void 0 : _conversationRef$curr9.limit
382
+ conversationId: (_conversationRef$curr9 = conversationRef.current) == null ? void 0 : _conversationRef$curr9.id,
383
+ limit: (_conversationRef$curr0 = conversationRef.current) == null ? void 0 : _conversationRef$curr0.limit
337
384
  });
338
385
  await leave();
339
386
  setConversationStatus('exceeded');
340
387
  }
341
388
  async function interrupt() {
342
- var _conversationRef$curr0;
389
+ var _conversationRef$curr1;
343
390
  emitEvent('interrupt', {
344
- conversationId: (_conversationRef$curr0 = conversationRef.current) == null ? void 0 : _conversationRef$curr0.id
391
+ conversationId: (_conversationRef$curr1 = conversationRef.current) == null ? void 0 : _conversationRef$curr1.id
345
392
  });
346
393
  await publishConversationDataMessage(room, {}, 'interrupt');
347
394
  }
@@ -351,7 +398,7 @@ export function useConversation(_ref) {
351
398
  }, 'inactivity_dismiss');
352
399
  }
353
400
  async function send(message, attachments) {
354
- var _conversationRef$curr1;
401
+ var _conversationRef$curr10;
355
402
  if (attachments === void 0) {
356
403
  attachments = [];
357
404
  }
@@ -362,7 +409,7 @@ export function useConversation(_ref) {
362
409
  }
363
410
  }));
364
411
  emitEvent('userSendMessage', {
365
- conversationId: (_conversationRef$curr1 = conversationRef.current) == null ? void 0 : _conversationRef$curr1.id,
412
+ conversationId: (_conversationRef$curr10 = conversationRef.current) == null ? void 0 : _conversationRef$curr10.id,
366
413
  message
367
414
  });
368
415
  }