@hamsa-ai/voice-agents-sdk 0.3.1 → 0.4.0-beta.2

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.
@@ -0,0 +1,607 @@
1
+ /**
2
+ * LiveKitToolRegistry - Advanced client-side tool management and RPC system
3
+ *
4
+ * This class provides comprehensive management of client-side tools that voice agents
5
+ * can execute during conversations. It handles tool registration as RPC methods,
6
+ * processes data messages from agents, manages transcription events, and provides
7
+ * local audio stream access for advanced integrations.
8
+ *
9
+ * Key Features:
10
+ * - **Dynamic Tool Registration**: Real-time registration and updating of client-side tools
11
+ * - **RPC Method Management**: Secure execution of agent-requested functions
12
+ * - **Event Processing**: Intelligent parsing and routing of agent messages
13
+ * - **Transcription Handling**: Real-time speech-to-text processing and forwarding
14
+ * - **Audio Stream Integration**: Local microphone stream access for external processing
15
+ * - **Error Resilience**: Robust error handling for tool execution and message processing
16
+ * - **Flexible Tool Definition**: Support for complex tool parameters and return values
17
+ *
18
+ * Tool Architecture:
19
+ * 1. **Tool Definition**: Client defines functions with metadata (name, description, parameters)
20
+ * 2. **Registration**: Tools are registered as LiveKit RPC methods
21
+ * 3. **Agent Calls**: Voice agents invoke tools during conversations
22
+ * 4. **Execution**: Client-side functions execute with provided parameters
23
+ * 5. **Response**: Results are returned to the agent for continued conversation
24
+ *
25
+ * Message Processing Pipeline:
26
+ * 1. **Raw Data Reception**: Binary data from LiveKit room participants
27
+ * 2. **Message Parsing**: JSON decoding and structure validation
28
+ * 3. **Event Classification**: Categorization (answer, transcription, custom)
29
+ * 4. **Event Emission**: Structured events for application consumption
30
+ * 5. **Error Handling**: Graceful handling of malformed or invalid messages
31
+ *
32
+ * @example Basic Tool Registration
33
+ * ```typescript
34
+ * const weatherTool = {
35
+ * function_name: 'getCurrentWeather',
36
+ * description: 'Gets current weather for a location',
37
+ * parameters: [
38
+ * { name: 'location', type: 'string', description: 'City name' }
39
+ * ],
40
+ * required: ['location'],
41
+ * fn: async (location) => {
42
+ * const response = await fetch(`/api/weather?city=${location}`);
43
+ * return response.json();
44
+ * }
45
+ * };
46
+ *
47
+ * const registry = new LiveKitToolRegistry([weatherTool]);
48
+ * registry.setRoom(liveKitRoom);
49
+ *
50
+ * // Tool is now available for agent to call
51
+ * registry.on('toolsRegistered', (count) => {
52
+ * console.log(`${count} tools registered and ready`);
53
+ * });
54
+ * ```
55
+ *
56
+ * @example Advanced Tool with Complex Parameters
57
+ * ```typescript
58
+ * const userDataTool = {
59
+ * function_name: 'getUserProfile',
60
+ * description: 'Retrieves comprehensive user profile data',
61
+ * parameters: [
62
+ * { name: 'userId', type: 'string', description: 'Unique user identifier' },
63
+ * { name: 'includeHistory', type: 'boolean', description: 'Include purchase history' },
64
+ * { name: 'fields', type: 'array', description: 'Specific fields to retrieve' }
65
+ * ],
66
+ * required: ['userId'],
67
+ * fn: async (userId, includeHistory = false, fields = []) => {
68
+ * const profile = await userService.getProfile(userId);
69
+ *
70
+ * if (includeHistory) {
71
+ * profile.purchaseHistory = await orderService.getHistory(userId);
72
+ * }
73
+ *
74
+ * if (fields.length > 0) {
75
+ * return pickFields(profile, fields);
76
+ * }
77
+ *
78
+ * return profile;
79
+ * }
80
+ * };
81
+ *
82
+ * registry.setTools([userDataTool]);
83
+ * ```
84
+ *
85
+ * @example Event Processing and Custom Messages
86
+ * ```typescript
87
+ * const registry = new LiveKitToolRegistry();
88
+ *
89
+ * // Standard agent responses
90
+ * registry.on('answerReceived', (text) => {
91
+ * console.log('Agent said:', text);
92
+ * updateChatInterface(text, 'agent');
93
+ * });
94
+ *
95
+ * // Real-time transcription
96
+ * registry.on('transcriptionReceived', (text) => {
97
+ * console.log('User said:', text);
98
+ * updateChatInterface(text, 'user');
99
+ * });
100
+ *
101
+ * // Custom application events
102
+ * registry.on('customEvent', (eventType, eventData, metadata) => {
103
+ * switch (eventType) {
104
+ * case 'sentiment_analysis':
105
+ * updateSentimentIndicator(eventData.sentiment);
106
+ * break;
107
+ * case 'conversation_summary':
108
+ * displaySummary(eventData.summary);
109
+ * break;
110
+ * case 'agent_thinking':
111
+ * showThinkingIndicator(eventData.status);
112
+ * break;
113
+ * default:
114
+ * logCustomEvent(eventType, eventData, metadata);
115
+ * }
116
+ * });
117
+ *
118
+ * // Raw data access for advanced processing
119
+ * registry.on('dataReceived', (message, participant) => {
120
+ * analyticsService.trackMessage({
121
+ * participant,
122
+ * messageType: message.event,
123
+ * timestamp: Date.now(),
124
+ * content: message
125
+ * });
126
+ * });
127
+ * ```
128
+ *
129
+ * @example Dynamic Tool Management
130
+ * ```typescript
131
+ * const registry = new LiveKitToolRegistry();
132
+ *
133
+ * // Add tools based on user permissions
134
+ * const addUserTools = (userRole) => {
135
+ * const baseToos = [getWeatherTool, getTimeTool];
136
+ *
137
+ * if (userRole === 'admin') {
138
+ * baseTools.push(getUserDataTool, modifySettingsTool);
139
+ * } else if (userRole === 'customer') {
140
+ * baseTools.push(getOrderStatusTool, updateProfileTool);
141
+ * }
142
+ *
143
+ * registry.setTools(baseTools);
144
+ * console.log(`Registered ${baseTools.length} tools for ${userRole}`);
145
+ * };
146
+ *
147
+ * // Update tools when user context changes
148
+ * userAuth.on('roleChanged', (newRole) => {
149
+ * addUserTools(newRole);
150
+ * });
151
+ * ```
152
+ *
153
+ * @example Audio Stream Integration
154
+ * ```typescript
155
+ * registry.on('localAudioStreamAvailable', (stream) => {
156
+ * // Send to external audio processor
157
+ * audioProcessor.setInputStream(stream);
158
+ *
159
+ * // Enable real-time audio analysis
160
+ * const audioAnalyzer = new AudioAnalyzer(stream);
161
+ * audioAnalyzer.on('volumeLevel', (level) => {
162
+ * updateVolumeIndicator(level);
163
+ * });
164
+ *
165
+ * // Record for quality assurance
166
+ * if (recordingEnabled) {
167
+ * mediaRecorder.start(stream);
168
+ * }
169
+ * });
170
+ * ```
171
+ *
172
+ * Technical Implementation:
173
+ * - Uses LiveKit's RPC system for secure tool execution
174
+ * - Implements JSON-based message protocol for structured communication
175
+ * - Provides comprehensive error handling and recovery
176
+ * - Supports dynamic tool registration and deregistration
177
+ * - Maintains tool metadata for debugging and analytics
178
+ * - Includes automatic cleanup and resource management
179
+ */
180
+ import { EventEmitter } from 'events';
181
+ import { type Room } from 'livekit-client';
182
+ import type { Tool } from './types';
183
+ /**
184
+ * LiveKitToolRegistry class for client-side tool management and RPC handling
185
+ *
186
+ * Extends EventEmitter to provide real-time notifications for tool registration,
187
+ * agent responses, transcriptions, and custom events from voice agents.
188
+ */
189
+ export declare class LiveKitToolRegistry extends EventEmitter {
190
+ /** Reference to the LiveKit room for RPC method registration */
191
+ private room;
192
+ /** Array of client-side tools available for agent execution */
193
+ private tools;
194
+ /**
195
+ * Creates a new LiveKitToolRegistry instance
196
+ *
197
+ * Initializes the tool registry with an optional array of tools that will
198
+ * be available for voice agents to call during conversations. Tools can be
199
+ * added later using setTools() or updated dynamically based on context.
200
+ *
201
+ * @param tools - Initial array of tools to register (optional)
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * // Initialize with no tools
206
+ * const registry = new LiveKitToolRegistry();
207
+ *
208
+ * // Initialize with predefined tools
209
+ * const registry = new LiveKitToolRegistry([
210
+ * weatherTool,
211
+ * calculatorTool,
212
+ * databaseQueryTool
213
+ * ]);
214
+ *
215
+ * // Tools will be automatically registered when room is set
216
+ * registry.setRoom(liveKitRoom);
217
+ * ```
218
+ */
219
+ constructor(tools?: Tool[]);
220
+ /**
221
+ * Configures the LiveKit room for tool registration and RPC setup
222
+ *
223
+ * Provides the tool registry with access to the LiveKit room instance for
224
+ * registering client-side tools as RPC methods. When a room is set and tools
225
+ * are available, automatic registration occurs immediately. This method is
226
+ * typically called by LiveKitManager during connection establishment.
227
+ *
228
+ * @param room - LiveKit room instance or null to clear the reference
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * const room = new Room();
233
+ * await room.connect(url, token);
234
+ *
235
+ * // Enable tool registration
236
+ * registry.setRoom(room);
237
+ *
238
+ * // Tools are automatically registered if available
239
+ * registry.on('toolsRegistered', (count) => {
240
+ * console.log(`${count} tools are now available to the agent`);
241
+ * });
242
+ *
243
+ * // Clear room reference when disconnecting
244
+ * registry.setRoom(null);
245
+ * ```
246
+ */
247
+ setRoom(room: Room | null): void;
248
+ /**
249
+ * Updates the available tools and re-registers them with the room
250
+ *
251
+ * Replaces the current tool array with a new set of tools and automatically
252
+ * re-registers them if a room connection is active. This enables dynamic
253
+ * tool management based on user context, permissions, or conversation state.
254
+ *
255
+ * @param tools - Array of Tool objects to make available to agents
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * // Initial tool set
260
+ * registry.setTools([weatherTool, timeTool]);
261
+ *
262
+ * // Update based on user authentication
263
+ * user.on('login', (userData) => {
264
+ * if (userData.role === 'admin') {
265
+ * registry.setTools([...baseToos, adminTool, userManagementTool]);
266
+ * } else {
267
+ * registry.setTools([...baseToos, profileTool]);
268
+ * }
269
+ * });
270
+ *
271
+ * // Context-aware tool updates
272
+ * conversation.on('topicChanged', (topic) => {
273
+ * if (topic === 'support') {
274
+ * registry.setTools([...baseToos, ticketTool, knowledgeBaseTool]);
275
+ * } else if (topic === 'sales') {
276
+ * registry.setTools([...baseToos, productTool, pricingTool]);
277
+ * }
278
+ * });
279
+ *
280
+ * // Remove all tools
281
+ * registry.setTools([]);
282
+ * ```
283
+ */
284
+ setTools(tools: Tool[]): void;
285
+ /**
286
+ * Registers all tools as RPC methods with the LiveKit room
287
+ *
288
+ * Processes each tool in the tools array and registers it as an RPC method
289
+ * that voice agents can call during conversations. Each tool function is
290
+ * wrapped with error handling and JSON serialization for secure, reliable
291
+ * execution. Registration only occurs when both room and tools are available.
292
+ *
293
+ * @fires toolsRegistered When tools are successfully registered with count
294
+ *
295
+ * @example
296
+ * ```typescript
297
+ * // Manual registration (usually automatic)
298
+ * registry.registerTools();
299
+ *
300
+ * // Listen for registration confirmation
301
+ * registry.on('toolsRegistered', (count) => {
302
+ * console.log(`${count} tools registered successfully`);
303
+ *
304
+ * // Notify agent about available tools
305
+ * sendAgentMessage({
306
+ * type: 'tools_ready',
307
+ * count: count,
308
+ * tools: registry.getTools().map(t => ({
309
+ * name: t.function_name,
310
+ * description: t.description
311
+ * }))
312
+ * });
313
+ * });
314
+ * ```
315
+ *
316
+ * Registration Process:
317
+ * 1. Validates tool structure (function_name, fn)
318
+ * 2. Creates RPC method wrapper with error handling
319
+ * 3. Registers with LiveKit room's local participant
320
+ * 4. Emits toolsRegistered event with count
321
+ * 5. Tools become immediately available for agent calls
322
+ */
323
+ registerTools(): void;
324
+ /**
325
+ * Processes incoming data messages from voice agents and participants
326
+ *
327
+ * Handles binary data received through the LiveKit room, parsing JSON messages
328
+ * and routing them to appropriate event handlers. This method processes various
329
+ * message types including agent responses, transcriptions, and custom events,
330
+ * providing a unified interface for all agent communication.
331
+ *
332
+ * @param payload - Binary data payload received from LiveKit
333
+ * @param participant - Identity of the participant who sent the message
334
+ *
335
+ * @fires answerReceived When agent provides a response
336
+ * @fires transcriptionReceived When speech-to-text data arrives
337
+ * @fires customEvent When custom application events are received
338
+ * @fires dataReceived Raw message data for advanced processing
339
+ *
340
+ * @example
341
+ * ```typescript
342
+ * // This method is called automatically by LiveKitManager
343
+ * // when RoomEvent.DataReceived is triggered
344
+ *
345
+ * // Listen for different message types
346
+ * registry.on('answerReceived', (text) => {
347
+ * console.log('Agent response:', text);
348
+ * displayAgentMessage(text);
349
+ * });
350
+ *
351
+ * registry.on('transcriptionReceived', (text) => {
352
+ * console.log('User transcription:', text);
353
+ * displayUserMessage(text);
354
+ * });
355
+ *
356
+ * registry.on('customEvent', (eventType, data, metadata) => {
357
+ * console.log(`Custom event: ${eventType}`, data);
358
+ *
359
+ * // Handle application-specific events
360
+ * switch (eventType) {
361
+ * case 'agent_thinking':
362
+ * showThinkingIndicator(data.status);
363
+ * break;
364
+ * case 'conversation_summary':
365
+ * updateSummaryPanel(data.summary);
366
+ * break;
367
+ * case 'emotion_detected':
368
+ * updateEmotionIndicator(data.emotion, data.confidence);
369
+ * break;
370
+ * }
371
+ * });
372
+ * ```
373
+ *
374
+ * Supported Message Formats:
375
+ * ```json
376
+ * // Agent response
377
+ * { "event": "answer", "content": "Hello, how can I help you?" }
378
+ *
379
+ * // Transcription
380
+ * { "event": "transcription", "content": "I need help with my order" }
381
+ *
382
+ * // Custom event
383
+ * { "event": "sentiment_analysis", "data": { "sentiment": "positive", "score": 0.8 } }
384
+ * ```
385
+ */
386
+ handleDataReceived(payload: Uint8Array, participant?: string): void;
387
+ /**
388
+ * Processes real-time transcription data from LiveKit's speech-to-text system
389
+ *
390
+ * Handles transcription segments received from LiveKit's built-in speech recognition,
391
+ * extracting text content and emitting structured transcription events. This method
392
+ * processes both partial and final transcription segments, enabling real-time
393
+ * speech-to-text display and conversation logging.
394
+ *
395
+ * @param transcriptions - Array of transcription segments from LiveKit
396
+ * @param transcriptions[].text - Transcribed text content
397
+ * @param transcriptions[].final - Whether this is a final transcription segment
398
+ *
399
+ * @fires transcriptionReceived When valid text content is extracted
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * // This method is called automatically by LiveKitManager
404
+ * // when RoomEvent.TranscriptionReceived is triggered
405
+ *
406
+ * // Listen for transcription updates
407
+ * registry.on('transcriptionReceived', (text) => {
408
+ * console.log('Transcription:', text);
409
+ *
410
+ * // Update real-time transcript display
411
+ * updateTranscriptDisplay(text);
412
+ *
413
+ * // Log conversation for analytics
414
+ * conversationLogger.logUserSpeech(text, Date.now());
415
+ *
416
+ * // Trigger intent recognition
417
+ * if (text.length > 10) {
418
+ * intentRecognizer.analyze(text);
419
+ * }
420
+ * });
421
+ *
422
+ * // Handle transcription processing
423
+ * let transcriptBuffer = '';
424
+ * registry.on('transcriptionReceived', (text) => {
425
+ * transcriptBuffer += text + ' ';
426
+ *
427
+ * // Process complete sentences
428
+ * if (text.endsWith('.') || text.endsWith('?') || text.endsWith('!')) {
429
+ * processCompleteSentence(transcriptBuffer.trim());
430
+ * transcriptBuffer = '';
431
+ * }
432
+ * });
433
+ * ```
434
+ *
435
+ * LiveKit Transcription Format:
436
+ * ```typescript
437
+ * [
438
+ * { text: "Hello", final: false },
439
+ * { text: "Hello there", final: false },
440
+ * { text: "Hello there, how are you?", final: true }
441
+ * ]
442
+ * ```
443
+ */
444
+ handleTranscriptionReceived(transcriptions: Array<{
445
+ text?: string;
446
+ final?: boolean;
447
+ }>): void;
448
+ /**
449
+ * Provides access to the local microphone audio stream for external processing
450
+ *
451
+ * Retrieves the local participant's microphone track and creates a MediaStream
452
+ * for external audio processing applications. This enables advanced integrations
453
+ * such as real-time audio analysis, recording, or additional audio effects
454
+ * processing outside of the LiveKit pipeline.
455
+ *
456
+ * @fires localAudioStreamAvailable When local audio stream is successfully extracted
457
+ *
458
+ * @example
459
+ * ```typescript
460
+ * // Listen for local audio stream availability
461
+ * registry.on('localAudioStreamAvailable', (stream) => {
462
+ * console.log('Local audio stream is available');
463
+ *
464
+ * // Set up real-time audio analysis
465
+ * const audioAnalyzer = new AudioAnalyzer(stream);
466
+ * audioAnalyzer.on('volumeLevel', (level) => {
467
+ * updateVolumeIndicator(level);
468
+ * });
469
+ *
470
+ * // Enable noise gate functionality
471
+ * const noiseGate = new NoiseGate(stream);
472
+ * noiseGate.threshold = -40; // dB
473
+ *
474
+ * // Record conversation for quality assurance
475
+ * if (recordingEnabled) {
476
+ * const mediaRecorder = new MediaRecorder(stream);
477
+ * mediaRecorder.start();
478
+ *
479
+ * mediaRecorder.ondataavailable = (event) => {
480
+ * saveAudioChunk(event.data);
481
+ * };
482
+ * }
483
+ * });
484
+ *
485
+ * // Call after connection is established
486
+ * registry.emitLocalAudioStream();
487
+ * ```
488
+ *
489
+ * @example Audio Processing Pipeline
490
+ * ```typescript
491
+ * registry.on('localAudioStreamAvailable', (stream) => {
492
+ * // Create audio context for advanced processing
493
+ * const audioContext = new AudioContext();
494
+ * const source = audioContext.createMediaStreamSource(stream);
495
+ *
496
+ * // Add audio effects
497
+ * const gainNode = audioContext.createGain();
498
+ * const filterNode = audioContext.createBiquadFilter();
499
+ *
500
+ * // Connect processing chain
501
+ * source.connect(gainNode);
502
+ * gainNode.connect(filterNode);
503
+ * filterNode.connect(audioContext.destination);
504
+ *
505
+ * // Configure effects
506
+ * gainNode.gain.value = 1.2;
507
+ * filterNode.type = 'highpass';
508
+ * filterNode.frequency.value = 80;
509
+ * });
510
+ * ```
511
+ */
512
+ emitLocalAudioStream(): void;
513
+ /**
514
+ * Returns the count of currently registered tools
515
+ *
516
+ * Provides a simple way to check how many tools are available for agent
517
+ * execution. Useful for validation, debugging, and analytics reporting.
518
+ *
519
+ * @returns Number of tools in the registry
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * const toolCount = registry.getToolCount();
524
+ * console.log(`${toolCount} tools available`);
525
+ *
526
+ * // Validate tool registration
527
+ * if (toolCount === 0) {
528
+ * console.warn('No tools registered - agent functionality may be limited');
529
+ * }
530
+ *
531
+ * // Update UI indicator
532
+ * updateToolCountDisplay(toolCount);
533
+ *
534
+ * // Analytics tracking
535
+ * analytics.track('tools_registered', { count: toolCount });
536
+ * ```
537
+ */
538
+ getToolCount(): number;
539
+ /**
540
+ * Returns the complete array of registered tools
541
+ *
542
+ * Provides access to all currently registered tools including their metadata,
543
+ * parameters, and function references. Useful for debugging, validation,
544
+ * and dynamic tool management scenarios.
545
+ *
546
+ * @returns Array of Tool objects currently in the registry
547
+ *
548
+ * @example
549
+ * ```typescript
550
+ * const tools = registry.getTools();
551
+ *
552
+ * // List available tools
553
+ * console.log('Available tools:');
554
+ * tools.forEach(tool => {
555
+ * console.log(`- ${tool.function_name}: ${tool.description}`);
556
+ * });
557
+ *
558
+ * // Validate tool configuration
559
+ * const invalidTools = tools.filter(tool => !tool.fn);
560
+ * if (invalidTools.length > 0) {
561
+ * console.error('Tools missing implementation:', invalidTools);
562
+ * }
563
+ *
564
+ * // Create tool documentation
565
+ * const toolDocs = tools.map(tool => ({
566
+ * name: tool.function_name,
567
+ * description: tool.description,
568
+ * parameters: tool.parameters,
569
+ * required: tool.required
570
+ * }));
571
+ *
572
+ * // Send tool capabilities to external system
573
+ * apiService.updateToolCapabilities(toolDocs);
574
+ * ```
575
+ */
576
+ getTools(): Tool[];
577
+ /**
578
+ * Performs cleanup of tool registry resources and state
579
+ *
580
+ * Resets the tool registry to its initial state, clearing any cached data
581
+ * and preparing for safe disposal or reinitialization. LiveKit automatically
582
+ * handles RPC method unregistration when the room disconnects, so minimal
583
+ * cleanup is required.
584
+ *
585
+ * @example
586
+ * ```typescript
587
+ * // Cleanup when component unmounts
588
+ * useEffect(() => {
589
+ * return () => {
590
+ * registry.cleanup();
591
+ * };
592
+ * }, []);
593
+ *
594
+ * // Cleanup before reinitializing
595
+ * const reinitializeRegistry = () => {
596
+ * registry.cleanup();
597
+ * registry = new LiveKitToolRegistry(newTools);
598
+ * setupEventListeners();
599
+ * };
600
+ *
601
+ * // Cleanup is safe to call multiple times
602
+ * registry.cleanup();
603
+ * registry.cleanup(); // Safe
604
+ * ```
605
+ */
606
+ cleanup(): void;
607
+ }
@@ -20,27 +20,27 @@
20
20
  export default class ScreenWakeLock {
21
21
  /**
22
22
  * The wake lock sentinel that controls the screen wake lock.
23
- * @type {WakeLockSentinel|null}
24
23
  * @private
25
24
  */
26
25
  private _wakeLock;
26
+ constructor();
27
27
  /**
28
28
  * Attempts to acquire a screen wake lock.
29
29
  * If the Wake Lock API is not supported, a warning is logged.
30
30
  *
31
- * @returns {Promise<void>}
31
+ * @returns Promise<void>
32
32
  */
33
33
  acquire(): Promise<void>;
34
34
  /**
35
35
  * Releases the screen wake lock if it is active.
36
36
  *
37
- * @returns {Promise<void>}
37
+ * @returns Promise<void>
38
38
  */
39
39
  release(): Promise<void>;
40
40
  /**
41
41
  * Indicates whether the wake lock is currently active.
42
42
  *
43
- * @returns {boolean} True if the wake lock is active, false otherwise.
43
+ * @returns True if the wake lock is active, false otherwise.
44
44
  */
45
45
  isActive(): boolean;
46
46
  }