@sayna-ai/node-sdk 0.0.11 → 0.0.13

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.
package/README.md CHANGED
@@ -138,7 +138,7 @@ Retrieves all configured SIP webhook hooks from the runtime cache.
138
138
  ```typescript
139
139
  const response = await client.getSipHooks();
140
140
  for (const hook of response.hooks) {
141
- console.log(`Host: ${hook.host}, URL: ${hook.url}`);
141
+ console.log(`Host: ${hook.host}, URL: ${hook.url}, Auth ID: ${hook.auth_id}`);
142
142
  }
143
143
  ```
144
144
 
@@ -152,10 +152,11 @@ Sets or updates SIP webhook hooks in the runtime cache. Hooks with matching host
152
152
 
153
153
  Each `SipHook` object contains:
154
154
 
155
- | field | type | description |
156
- | ------ | -------- | --------------------------------------- |
157
- | `host` | `string` | SIP domain pattern (case-insensitive). |
158
- | `url` | `string` | HTTPS URL to forward webhook events to. |
155
+ | field | type | description |
156
+ | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
157
+ | `host` | `string` | SIP domain pattern (case-insensitive). |
158
+ | `url` | `string` | HTTPS URL to forward webhook events to. |
159
+ | `auth_id` | `string` | Tenant identifier for this hook. Required but may be empty for unauthenticated mode. Treat as opaque; pass unchanged. |
159
160
 
160
161
  **Returns**: `Promise<SipHooksResponse>` - Object containing the merged list of all configured hooks.
161
162
 
@@ -163,14 +164,40 @@ Each `SipHook` object contains:
163
164
 
164
165
  ```typescript
165
166
  const response = await client.setSipHooks([
166
- { host: "example.com", url: "https://webhook.example.com/events" },
167
- { host: "another.com", url: "https://webhook.another.com/events" },
167
+ { host: "example.com", url: "https://webhook.example.com/events", auth_id: "tenant-123" },
168
+ { host: "another.com", url: "https://webhook.another.com/events", auth_id: "" }, // Empty for unauthenticated mode
168
169
  ]);
169
170
  console.log("Total hooks configured:", response.hooks.length);
170
171
  ```
171
172
 
172
173
  ---
173
174
 
175
+ ### Room Ownership and Access
176
+
177
+ When authentication is enabled, the server enforces room-level access control:
178
+
179
+ - **Room names are clean**: The SDK does not rewrite or prefix room names. Pass room names as-is.
180
+ - **Room listings are scoped**: `getLiveKitRooms()` returns only rooms accessible to your authenticated context.
181
+ - **403 on token requests**: `getLiveKitToken()` returns a 403 error if the room exists but is owned by another tenant. Do not retry with a modified room name.
182
+ - **404 masks access denial**: For room-scoped operations (`getLiveKitRoom()`, `removeLiveKitParticipant()`, `muteLiveKitParticipantTrack()`, `sipTransferRest()`), a 404 response can mean "not found" or "not accessible."
183
+ - **Inbound SIP rooms**: Rooms created by inbound SIP calls are owned by the routing configuration's `auth_id`. Access depends on your authentication context.
184
+ - **WebSocket errors**: Ownership/access errors during WebSocket configuration are surfaced via the error callback. Retry with the correct room name if needed.
185
+
186
+ Errors include HTTP status and endpoint information for easier debugging:
187
+
188
+ ```typescript
189
+ try {
190
+ await client.getLiveKitToken("some-room", "user", "user-123");
191
+ } catch (error) {
192
+ if (error instanceof SaynaServerError) {
193
+ console.log(`Status: ${error.status}, Endpoint: ${error.endpoint}`);
194
+ // Status: 403, Endpoint: livekit/token
195
+ }
196
+ }
197
+ ```
198
+
199
+ ---
200
+
174
201
  ### WebSocket API Methods
175
202
 
176
203
  These methods require an active WebSocket connection:
package/dist/errors.d.ts CHANGED
@@ -30,9 +30,25 @@ export declare class SaynaValidationError extends SaynaError {
30
30
  constructor(message: string);
31
31
  }
32
32
  /**
33
- * Error thrown when the server returns an error.
33
+ * Error thrown when the server returns an error response.
34
+ *
35
+ * For REST API calls, the `status` and `endpoint` fields provide context about
36
+ * which request failed and with what HTTP status code. Common status codes:
37
+ * - 403: Access denied (e.g., room owned by another tenant on /livekit/token)
38
+ * - 404: Not found or not accessible (masked access denial for room-scoped operations)
39
+ * - 500: Internal server error
34
40
  */
35
41
  export declare class SaynaServerError extends SaynaError {
36
- constructor(message: string);
42
+ /**
43
+ * HTTP status code returned by the server, if available.
44
+ * Set for REST API errors; undefined for WebSocket errors.
45
+ */
46
+ readonly status?: number;
47
+ /**
48
+ * API endpoint that returned the error, if available.
49
+ * Set for REST API errors; undefined for WebSocket errors.
50
+ */
51
+ readonly endpoint?: string;
52
+ constructor(message: string, status?: number, endpoint?: string);
37
53
  }
38
54
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAK5B;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,UAAU;gBACxC,OAAO,GAAE,MAA2C;CAKjE;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;gBAE9C,OAAO,GAAE,MAA0F;CAMtG;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,SAAyB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAE7B,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,OAAO,EAAE,MAAM;CAK5B;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;gBAClC,OAAO,EAAE,MAAM;CAK5B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAK5B;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,UAAU;gBACxC,OAAO,GAAE,MAA2C;CAKjE;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;gBAE9C,OAAO,GAAE,MAA0F;CAMtG;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,SAAyB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAE7B,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,OAAO,EAAE,MAAM;CAK5B;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C;;;OAGG;IACH,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAEtB,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAOhE"}
package/dist/index.js CHANGED
@@ -42,9 +42,13 @@ class SaynaValidationError extends SaynaError {
42
42
  }
43
43
 
44
44
  class SaynaServerError extends SaynaError {
45
- constructor(message) {
45
+ status;
46
+ endpoint;
47
+ constructor(message, status, endpoint) {
46
48
  super(message);
47
49
  this.name = "SaynaServerError";
50
+ this.status = status;
51
+ this.endpoint = endpoint;
48
52
  Object.setPrototypeOf(this, SaynaServerError.prototype);
49
53
  }
50
54
  }
@@ -271,7 +275,8 @@ class SaynaClient {
271
275
  }
272
276
  async fetchFromSayna(endpoint, options = {}, responseType = "json") {
273
277
  const httpUrl = this.getHttpUrl();
274
- const url = `${httpUrl}${httpUrl.endsWith("/") ? "" : "/"}${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`;
278
+ const normalizedEndpoint = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint;
279
+ const url = `${httpUrl}${httpUrl.endsWith("/") ? "" : "/"}${normalizedEndpoint}`;
275
280
  const headers = {
276
281
  ...options.headers
277
282
  };
@@ -279,7 +284,7 @@ class SaynaClient {
279
284
  if (this.apiKey && !hasAuthHeader) {
280
285
  headers["Authorization"] = `Bearer ${this.apiKey}`;
281
286
  }
282
- if (options.method === "POST" && options.body && !headers["Content-Type"]) {
287
+ if (options.body && !headers["Content-Type"]) {
283
288
  headers["Content-Type"] = "application/json";
284
289
  }
285
290
  try {
@@ -295,7 +300,12 @@ class SaynaClient {
295
300
  } catch {
296
301
  errorMessage = `Request failed: ${response.status} ${response.statusText}`;
297
302
  }
298
- throw new SaynaServerError(errorMessage);
303
+ if (response.status === 403) {
304
+ errorMessage = `Access denied: ${errorMessage}`;
305
+ } else if (response.status === 404) {
306
+ errorMessage = `Not found or not accessible: ${errorMessage}`;
307
+ }
308
+ throw new SaynaServerError(errorMessage, response.status, normalizedEndpoint);
299
309
  }
300
310
  if (responseType === "arrayBuffer") {
301
311
  return await response.arrayBuffer();
@@ -486,6 +496,73 @@ class SaynaClient {
486
496
  })
487
497
  });
488
498
  }
499
+ async getLiveKitRooms() {
500
+ return this.fetchFromSayna("livekit/rooms");
501
+ }
502
+ async getLiveKitRoom(roomName) {
503
+ if (!roomName || roomName.trim().length === 0) {
504
+ throw new SaynaValidationError("room_name cannot be empty");
505
+ }
506
+ const encoded = encodeURIComponent(roomName.trim());
507
+ return this.fetchFromSayna(`livekit/rooms/${encoded}`);
508
+ }
509
+ async removeLiveKitParticipant(roomName, participantIdentity) {
510
+ if (!roomName || roomName.trim().length === 0) {
511
+ throw new SaynaValidationError("room_name cannot be empty");
512
+ }
513
+ if (!participantIdentity || participantIdentity.trim().length === 0) {
514
+ throw new SaynaValidationError("participant_identity cannot be empty");
515
+ }
516
+ return this.fetchFromSayna("livekit/participant", {
517
+ method: "DELETE",
518
+ body: JSON.stringify({
519
+ room_name: roomName.trim(),
520
+ participant_identity: participantIdentity.trim()
521
+ })
522
+ });
523
+ }
524
+ async muteLiveKitParticipantTrack(roomName, participantIdentity, trackSid, muted) {
525
+ if (!roomName || roomName.trim().length === 0) {
526
+ throw new SaynaValidationError("room_name cannot be empty");
527
+ }
528
+ if (!participantIdentity || participantIdentity.trim().length === 0) {
529
+ throw new SaynaValidationError("participant_identity cannot be empty");
530
+ }
531
+ if (!trackSid || trackSid.trim().length === 0) {
532
+ throw new SaynaValidationError("track_sid cannot be empty");
533
+ }
534
+ if (typeof muted !== "boolean") {
535
+ throw new SaynaValidationError("muted must be a boolean");
536
+ }
537
+ return this.fetchFromSayna("livekit/participant/mute", {
538
+ method: "POST",
539
+ body: JSON.stringify({
540
+ room_name: roomName.trim(),
541
+ participant_identity: participantIdentity.trim(),
542
+ track_sid: trackSid.trim(),
543
+ muted
544
+ })
545
+ });
546
+ }
547
+ async sipTransferRest(roomName, participantIdentity, transferTo) {
548
+ if (!roomName || roomName.trim().length === 0) {
549
+ throw new SaynaValidationError("room_name cannot be empty");
550
+ }
551
+ if (!participantIdentity || participantIdentity.trim().length === 0) {
552
+ throw new SaynaValidationError("participant_identity cannot be empty");
553
+ }
554
+ if (!transferTo || transferTo.trim().length === 0) {
555
+ throw new SaynaValidationError("transfer_to cannot be empty");
556
+ }
557
+ return this.fetchFromSayna("sip/transfer", {
558
+ method: "POST",
559
+ body: JSON.stringify({
560
+ room_name: roomName.trim(),
561
+ participant_identity: participantIdentity.trim(),
562
+ transfer_to: transferTo.trim()
563
+ })
564
+ });
565
+ }
489
566
  async getRecording(streamId) {
490
567
  if (!streamId || streamId.trim().length === 0) {
491
568
  throw new SaynaValidationError("streamId cannot be empty");
@@ -509,6 +586,9 @@ class SaynaClient {
509
586
  if (!hook.url || typeof hook.url !== "string" || hook.url.trim().length === 0) {
510
587
  throw new SaynaValidationError(`hooks[${i}].url must be a non-empty string`);
511
588
  }
589
+ if (typeof hook.auth_id !== "string") {
590
+ throw new SaynaValidationError(`hooks[${i}].auth_id must be a string`);
591
+ }
512
592
  }
513
593
  return this.fetchFromSayna("sip/hooks", {
514
594
  method: "POST",
@@ -696,4 +776,4 @@ export {
696
776
  SaynaClient
697
777
  };
698
778
 
699
- //# debugId=5692EE242C83976364756E2164756E21
779
+ //# debugId=15E631B45E4A4D4464756E2164756E21
package/dist/index.js.map CHANGED
@@ -2,12 +2,12 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/errors.ts", "../src/sayna-client.ts", "../src/webhook-receiver.ts", "../src/index.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Base error class for all Sayna SDK errors.\n */\nexport class SaynaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaError\";\n Object.setPrototypeOf(this, SaynaError.prototype);\n }\n}\n\n/**\n * Error thrown when attempting to use the client before it's connected.\n */\nexport class SaynaNotConnectedError extends SaynaError {\n constructor(message: string = \"Not connected to Sayna WebSocket\") {\n super(message);\n this.name = \"SaynaNotConnectedError\";\n Object.setPrototypeOf(this, SaynaNotConnectedError.prototype);\n }\n}\n\n/**\n * Error thrown when attempting operations before the client is ready.\n */\nexport class SaynaNotReadyError extends SaynaError {\n constructor(\n message: string = \"Sayna voice providers are not ready. Wait for the connection to be established.\"\n ) {\n super(message);\n this.name = \"SaynaNotReadyError\";\n Object.setPrototypeOf(this, SaynaNotReadyError.prototype);\n }\n}\n\n/**\n * Error thrown when WebSocket connection fails.\n */\nexport class SaynaConnectionError extends SaynaError {\n public override readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = \"SaynaConnectionError\";\n this.cause = cause;\n Object.setPrototypeOf(this, SaynaConnectionError.prototype);\n }\n}\n\n/**\n * Error thrown when invalid parameters are provided.\n */\nexport class SaynaValidationError extends SaynaError {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaValidationError\";\n Object.setPrototypeOf(this, SaynaValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when the server returns an error.\n */\nexport class SaynaServerError extends SaynaError {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaServerError\";\n Object.setPrototypeOf(this, SaynaServerError.prototype);\n }\n}\n",
6
- "import type {\n STTConfig,\n TTSConfig,\n LiveKitConfig,\n ConfigMessage,\n SpeakMessage,\n ClearMessage,\n SendMessageMessage,\n STTResultMessage,\n ErrorMessage,\n SipTransferErrorMessage,\n SipTransferMessage,\n OutgoingMessage,\n SaynaMessage,\n Participant,\n VoicesResponse,\n HealthResponse,\n LiveKitTokenResponse,\n SipHook,\n SipHooksResponse,\n} from \"./types\";\nimport {\n SaynaNotConnectedError,\n SaynaNotReadyError,\n SaynaConnectionError,\n SaynaValidationError,\n SaynaServerError,\n} from \"./errors\";\n\n// Node.js 18+ has native fetch support\ndeclare const fetch: typeof globalThis.fetch;\n\n/**\n * Event handler for speech-to-text results.\n */\nexport type STTResultHandler = (\n result: STTResultMessage\n) => void | Promise<void>;\n\n/**\n * Event handler for text-to-speech audio data.\n */\nexport type TTSAudioHandler = (audio: ArrayBuffer) => void | Promise<void>;\n\n/**\n * Event handler for error messages.\n */\nexport type ErrorHandler = (error: ErrorMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant messages.\n */\nexport type MessageHandler = (message: SaynaMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant disconnections.\n */\nexport type ParticipantDisconnectedHandler = (\n participant: Participant\n) => void | Promise<void>;\n\n/**\n * Event handler for TTS playback completion.\n */\nexport type TTSPlaybackCompleteHandler = (\n timestamp: number\n) => void | Promise<void>;\n\n/**\n * Event handler for SIP transfer specific errors.\n */\nexport type SipTransferErrorHandler = (\n error: SipTransferErrorMessage\n) => void | Promise<void>;\n\n/**\n * Client for connecting to Sayna WebSocket server for real-time voice interactions.\n *\n * @example\n * ```typescript\n * const client = await saynaConnect(\n * \"https://api.sayna.ai\",\n * sttConfig,\n * ttsConfig\n * );\n *\n * client.registerOnSttResult((result) => {\n * console.log(\"Transcription:\", result.transcript);\n * });\n *\n * await client.speak(\"Hello, world!\");\n * ```\n */\nexport class SaynaClient {\n private url: string;\n private sttConfig?: STTConfig;\n private ttsConfig?: TTSConfig;\n private livekitConfig?: LiveKitConfig;\n private withoutAudio: boolean;\n private apiKey?: string;\n private websocket?: WebSocket;\n private isConnected: boolean = false;\n private isReady: boolean = false;\n private _livekitRoomName?: string;\n private _livekitUrl?: string;\n private _saynaParticipantIdentity?: string;\n private _saynaParticipantName?: string;\n private _streamId?: string;\n private inputStreamId?: string;\n private sttCallback?: STTResultHandler;\n private ttsCallback?: TTSAudioHandler;\n private errorCallback?: ErrorHandler;\n private messageCallback?: MessageHandler;\n private participantDisconnectedCallback?: ParticipantDisconnectedHandler;\n private ttsPlaybackCompleteCallback?: TTSPlaybackCompleteHandler;\n private sipTransferErrorCallback?: SipTransferErrorHandler;\n private readyPromiseResolve?: () => void;\n private readyPromiseReject?: (error: Error) => void;\n\n /**\n * Creates a new SaynaClient instance.\n *\n * @param url - The Sayna server URL (e.g., \"https://api.sayna.ai\")\n * @param sttConfig - Speech-to-text configuration (required when withoutAudio=false)\n * @param ttsConfig - Text-to-speech configuration (required when withoutAudio=false)\n * @param livekitConfig - Optional LiveKit room configuration\n * @param withoutAudio - If true, disables audio streaming (default: false)\n * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)\n * @param streamId - Optional session identifier for recording paths; server generates a UUID when omitted\n *\n * @throws {SaynaValidationError} If URL is invalid or if audio configs are missing when audio is enabled\n */\n constructor(\n url: string,\n sttConfig?: STTConfig,\n ttsConfig?: TTSConfig,\n livekitConfig?: LiveKitConfig,\n withoutAudio: boolean = false,\n apiKey?: string,\n streamId?: string\n ) {\n // Validate URL\n if (!url || typeof url !== \"string\") {\n throw new SaynaValidationError(\"URL must be a non-empty string\");\n }\n if (\n !url.startsWith(\"http://\") &&\n !url.startsWith(\"https://\") &&\n !url.startsWith(\"ws://\") &&\n !url.startsWith(\"wss://\")\n ) {\n throw new SaynaValidationError(\n \"URL must start with http://, https://, ws://, or wss://\"\n );\n }\n\n // Validate audio config requirements\n if (!withoutAudio) {\n if (!sttConfig || !ttsConfig) {\n throw new SaynaValidationError(\n \"sttConfig and ttsConfig are required when withoutAudio=false (audio streaming enabled). \" +\n \"Either provide both configs or set withoutAudio=true for non-audio use cases.\"\n );\n }\n }\n\n this.url = url;\n this.sttConfig = sttConfig;\n this.ttsConfig = ttsConfig;\n this.livekitConfig = livekitConfig;\n this.withoutAudio = withoutAudio;\n this.apiKey = apiKey ?? process.env.SAYNA_API_KEY;\n this.inputStreamId = streamId;\n }\n\n /**\n * Establishes connection to the Sayna WebSocket server.\n *\n * @throws {SaynaConnectionError} If connection fails\n * @returns Promise that resolves when the connection is ready\n */\n async connect(): Promise<void> {\n if (this.isConnected) {\n return;\n }\n\n // Convert HTTP(S) URL to WebSocket URL\n const wsUrl =\n this.url.replace(/^https:\\/\\//, \"wss://\").replace(/^http:\\/\\//, \"ws://\") +\n (this.url.endsWith(\"/\") ? \"ws\" : \"/ws\");\n\n return new Promise((resolve, reject) => {\n this.readyPromiseResolve = resolve;\n this.readyPromiseReject = reject;\n\n try {\n const headers = this.apiKey\n ? { Authorization: `Bearer ${this.apiKey}` }\n : undefined;\n\n const WebSocketConstructor = WebSocket as unknown as {\n new (\n url: string,\n protocols?: string | string[],\n options?: { headers?: Record<string, string> }\n ): WebSocket;\n };\n\n this.websocket = headers\n ? new WebSocketConstructor(wsUrl, undefined, { headers })\n : new WebSocket(wsUrl);\n\n this.websocket.onopen = () => {\n this.isConnected = true;\n\n // Send initial configuration\n const configMessage: ConfigMessage = {\n type: \"config\",\n stream_id: this.inputStreamId,\n stt_config: this.sttConfig,\n tts_config: this.ttsConfig,\n livekit: this.livekitConfig,\n audio: !this.withoutAudio,\n };\n\n try {\n if (this.websocket) {\n this.websocket.send(JSON.stringify(configMessage));\n }\n } catch (error) {\n this.cleanup();\n const err = new SaynaConnectionError(\n \"Failed to send configuration\",\n error\n );\n if (this.readyPromiseReject) {\n this.readyPromiseReject(err);\n }\n }\n };\n\n this.websocket.onmessage = async (event) => {\n try {\n if (\n event.data instanceof Blob ||\n event.data instanceof ArrayBuffer\n ) {\n // Binary TTS audio data\n const buffer =\n event.data instanceof Blob\n ? await event.data.arrayBuffer()\n : event.data;\n if (this.ttsCallback) {\n await this.ttsCallback(buffer);\n }\n } else {\n // JSON control messages\n if (typeof event.data !== \"string\") {\n throw new Error(\"Expected string data for JSON messages\");\n }\n const data = JSON.parse(event.data) as OutgoingMessage;\n await this.handleJsonMessage(data);\n }\n } catch (error) {\n // Log parse errors but don't break the connection\n if (this.errorCallback) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n await this.errorCallback({\n type: \"error\",\n message: `Failed to process message: ${errorMessage}`,\n });\n }\n }\n };\n\n this.websocket.onerror = () => {\n const error = new SaynaConnectionError(\"WebSocket connection error\");\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(error);\n }\n };\n\n this.websocket.onclose = (event) => {\n const wasReady = this.isReady;\n this.cleanup();\n\n // If connection closed before ready, reject the promise\n if (!wasReady && this.readyPromiseReject) {\n this.readyPromiseReject(\n new SaynaConnectionError(\n `WebSocket closed before ready (code: ${event.code}, reason: ${event.reason || \"none\"})`\n )\n );\n }\n };\n } catch (error) {\n reject(new SaynaConnectionError(\"Failed to create WebSocket\", error));\n }\n });\n }\n\n /**\n * Handles incoming JSON messages from the WebSocket.\n * @internal\n */\n private async handleJsonMessage(data: OutgoingMessage): Promise<void> {\n const messageType = data.type;\n\n try {\n switch (messageType) {\n case \"ready\": {\n const readyMsg = data;\n this.isReady = true;\n this._livekitRoomName = readyMsg.livekit_room_name;\n this._livekitUrl = readyMsg.livekit_url;\n this._saynaParticipantIdentity = readyMsg.sayna_participant_identity;\n this._saynaParticipantName = readyMsg.sayna_participant_name;\n this._streamId = readyMsg.stream_id;\n if (this.readyPromiseResolve) {\n this.readyPromiseResolve();\n }\n break;\n }\n\n case \"stt_result\": {\n const sttResult = data;\n if (this.sttCallback) {\n await this.sttCallback(sttResult);\n }\n break;\n }\n\n case \"error\": {\n const errorMsg = data;\n if (this.errorCallback) {\n await this.errorCallback(errorMsg);\n }\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(new SaynaServerError(errorMsg.message));\n }\n break;\n }\n\n case \"sip_transfer_error\": {\n const sipTransferError = data;\n if (this.sipTransferErrorCallback) {\n await this.sipTransferErrorCallback(sipTransferError);\n } else if (this.errorCallback) {\n await this.errorCallback({\n type: \"error\",\n message: sipTransferError.message,\n });\n }\n break;\n }\n\n case \"message\": {\n const messageData = data;\n if (this.messageCallback) {\n await this.messageCallback(messageData.message);\n }\n break;\n }\n\n case \"participant_disconnected\": {\n const participantMsg = data;\n if (this.participantDisconnectedCallback) {\n await this.participantDisconnectedCallback(\n participantMsg.participant\n );\n }\n break;\n }\n\n case \"tts_playback_complete\": {\n const ttsPlaybackCompleteMsg = data;\n if (this.ttsPlaybackCompleteCallback) {\n await this.ttsPlaybackCompleteCallback(\n ttsPlaybackCompleteMsg.timestamp\n );\n }\n break;\n }\n\n default: {\n const unknownMessage = data as { type: string };\n const errorMessage = `Unknown message type received: ${unknownMessage.type}`;\n if (this.errorCallback) {\n await this.errorCallback({ type: \"error\", message: errorMessage });\n } else {\n console.warn(errorMessage);\n }\n }\n }\n } catch (error) {\n // Notify error callback if handler fails\n if (this.errorCallback) {\n await this.errorCallback({\n type: \"error\",\n message: `Handler error: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n }\n\n /**\n * Cleans up internal state.\n * @internal\n */\n private cleanup(): void {\n this.isConnected = false;\n this.isReady = false;\n this._livekitRoomName = undefined;\n this._livekitUrl = undefined;\n this._saynaParticipantIdentity = undefined;\n this._saynaParticipantName = undefined;\n this._streamId = undefined;\n }\n\n /**\n * Converts WebSocket URL to HTTP URL for REST API calls.\n * @internal\n */\n private getHttpUrl(): string {\n return this.url\n .replace(/^ws:\\/\\//, \"http://\")\n .replace(/^wss:\\/\\//, \"https://\");\n }\n\n /**\n * Generic fetch helper for making REST API calls to Sayna server.\n * Handles URL construction, headers, error responses, and type conversion.\n * @internal\n *\n * @param endpoint - API endpoint path (e.g., \"/voices\", \"/speak\")\n * @param options - Fetch options including method, body, headers, etc.\n * @param responseType - Expected response type: \"json\" or \"arrayBuffer\"\n * @returns Promise resolving to the parsed response\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the server returns an error response\n */\n private async fetchFromSayna<T>(\n endpoint: string,\n options: RequestInit = {},\n responseType: \"json\" | \"arrayBuffer\" = \"json\"\n ): Promise<T> {\n const httpUrl = this.getHttpUrl();\n const url = `${httpUrl}${httpUrl.endsWith(\"/\") ? \"\" : \"/\"}${endpoint.startsWith(\"/\") ? endpoint.slice(1) : endpoint}`;\n\n // Merge default headers with user-provided headers\n const headers: Record<string, string> = {\n ...(options.headers as Record<string, string>),\n };\n\n // Add Authorization header when an API key is provided, unless user supplied one\n const hasAuthHeader = Object.keys(headers).some(\n (key) => key.toLowerCase() === \"authorization\"\n );\n if (this.apiKey && !hasAuthHeader) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n // Add Content-Type for JSON requests if not already set\n if (options.method === \"POST\" && options.body && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n // Try to parse error message from JSON response\n let errorMessage: string;\n try {\n const errorData: unknown = await response.json();\n errorMessage =\n errorData &&\n typeof errorData === \"object\" &&\n \"error\" in errorData &&\n typeof errorData.error === \"string\"\n ? errorData.error\n : `Request failed: ${response.status} ${response.statusText}`;\n } catch {\n errorMessage = `Request failed: ${response.status} ${response.statusText}`;\n }\n throw new SaynaServerError(errorMessage);\n }\n\n // Parse response based on expected type\n if (responseType === \"arrayBuffer\") {\n return (await response.arrayBuffer()) as T;\n } else {\n return (await response.json()) as T;\n }\n } catch (error) {\n // Re-throw SaynaServerError as-is\n if (error instanceof SaynaServerError) {\n throw error;\n }\n // Wrap other errors in SaynaConnectionError\n throw new SaynaConnectionError(`Failed to fetch from ${endpoint}`, error);\n }\n }\n\n /**\n * Disconnects from the Sayna WebSocket server and cleans up resources.\n */\n disconnect(): void {\n if (this.websocket) {\n // Remove event handlers to prevent memory leaks\n this.websocket.onopen = null;\n this.websocket.onmessage = null;\n this.websocket.onerror = null;\n this.websocket.onclose = null;\n\n if (this.websocket.readyState === WebSocket.OPEN) {\n this.websocket.close(1000, \"Client disconnect\");\n }\n\n this.websocket = undefined;\n }\n\n this.cleanup();\n }\n\n /**\n * Sends audio data to the server for speech recognition.\n *\n * @param audioData - Raw audio data as ArrayBuffer\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n onAudioInput(audioData: ArrayBuffer): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (!(audioData instanceof ArrayBuffer)) {\n throw new SaynaValidationError(\"audioData must be an ArrayBuffer\");\n }\n\n if (audioData.byteLength === 0) {\n throw new SaynaValidationError(\"audioData cannot be empty\");\n }\n\n try {\n this.websocket.send(audioData);\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send audio data\", error);\n }\n }\n\n /**\n * Registers a callback for speech-to-text results.\n *\n * @param callback - Function to call when STT results are received\n */\n registerOnSttResult(callback: STTResultHandler): void {\n this.sttCallback = callback;\n }\n\n /**\n * Registers a callback for text-to-speech audio data.\n *\n * @param callback - Function to call when TTS audio is received\n */\n registerOnTtsAudio(callback: TTSAudioHandler): void {\n this.ttsCallback = callback;\n }\n\n /**\n * Registers a callback for error messages.\n *\n * @param callback - Function to call when errors occur\n */\n registerOnError(callback: ErrorHandler): void {\n this.errorCallback = callback;\n }\n\n /**\n * Registers a callback for participant messages.\n *\n * @param callback - Function to call when messages are received\n */\n registerOnMessage(callback: MessageHandler): void {\n this.messageCallback = callback;\n }\n\n /**\n * Registers a callback for participant disconnection events.\n *\n * @param callback - Function to call when a participant disconnects\n */\n registerOnParticipantDisconnected(\n callback: ParticipantDisconnectedHandler\n ): void {\n this.participantDisconnectedCallback = callback;\n }\n\n /**\n * Registers a callback for TTS playback completion.\n *\n * @param callback - Function to call when TTS playback is complete\n */\n registerOnTtsPlaybackComplete(callback: TTSPlaybackCompleteHandler): void {\n this.ttsPlaybackCompleteCallback = callback;\n }\n\n /**\n * Registers a callback for SIP transfer specific errors.\n *\n * @param callback - Function to call when a SIP transfer error message is received\n */\n registerOnSipTransferError(callback: SipTransferErrorHandler): void {\n this.sipTransferErrorCallback = callback;\n }\n\n /**\n * Sends text to be synthesized as speech.\n *\n * @param text - Text to synthesize\n * @param flush - Whether to flush the TTS queue before speaking (default: true)\n * @param allowInterruption - Whether this speech can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If text is not a string\n */\n speak(\n text: string,\n flush: boolean = true,\n allowInterruption: boolean = true\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof text !== \"string\") {\n throw new SaynaValidationError(\"text must be a string\");\n }\n\n try {\n const speakMessage: SpeakMessage = {\n type: \"speak\",\n text,\n flush,\n allow_interruption: allowInterruption,\n };\n this.websocket.send(JSON.stringify(speakMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send speak command\", error);\n }\n }\n\n /**\n * Clears the text-to-speech queue.\n *\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n clear(): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n try {\n const clearMessage: ClearMessage = {\n type: \"clear\",\n };\n this.websocket.send(JSON.stringify(clearMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send clear command\", error);\n }\n }\n\n /**\n * Flushes the TTS queue by sending an empty speak command.\n *\n * @param allowInterruption - Whether the flush can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n ttsFlush(allowInterruption: boolean = true): void {\n this.speak(\"\", true, allowInterruption);\n }\n\n /**\n * Sends a message to the Sayna session.\n *\n * @param message - Message content\n * @param role - Message role (e.g., \"user\", \"assistant\")\n * @param topic - Optional topic identifier\n * @param debug - Optional debug metadata\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If parameters are invalid\n */\n sendMessage(\n message: string,\n role: string,\n topic?: string,\n debug?: Record<string, unknown>\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof message !== \"string\") {\n throw new SaynaValidationError(\"message must be a string\");\n }\n\n if (typeof role !== \"string\") {\n throw new SaynaValidationError(\"role must be a string\");\n }\n\n try {\n const sendMsg: SendMessageMessage = {\n type: \"send_message\",\n message,\n role,\n topic,\n debug,\n };\n this.websocket.send(JSON.stringify(sendMsg));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send message\", error);\n }\n }\n\n /**\n * Initiates a SIP transfer for the active LiveKit session.\n *\n * @param transferTo - Destination phone number or extension to transfer to\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If transferTo is not a non-empty string\n */\n sipTransfer(transferTo: string): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof transferTo !== \"string\" || transferTo.trim().length === 0) {\n throw new SaynaValidationError(\"transfer_to must be a non-empty string\");\n }\n\n try {\n const sipTransferMessage: SipTransferMessage = {\n type: \"sip_transfer\",\n transfer_to: transferTo.trim(),\n };\n this.websocket.send(JSON.stringify(sipTransferMessage));\n } catch (error) {\n throw new SaynaConnectionError(\n \"Failed to send SIP transfer command\",\n error\n );\n }\n }\n\n /**\n * Performs a health check on the Sayna server.\n *\n * @returns Promise that resolves with the health status\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const health = await client.health();\n * console.log(health.status); // \"OK\"\n * ```\n */\n async health(): Promise<HealthResponse> {\n return this.fetchFromSayna<HealthResponse>(\"\");\n }\n\n /**\n * Retrieves the catalogue of text-to-speech voices grouped by provider.\n *\n * @returns Promise that resolves with voices organized by provider\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const voices = await client.getVoices();\n * for (const [provider, voiceList] of Object.entries(voices)) {\n * console.log(`${provider}:`, voiceList.map(v => v.name));\n * }\n * ```\n */\n async getVoices(): Promise<VoicesResponse> {\n return this.fetchFromSayna<VoicesResponse>(\"voices\");\n }\n\n /**\n * Synthesizes text into audio using the REST API endpoint.\n * This is a standalone synthesis method that doesn't require an active WebSocket connection.\n *\n * @param text - Text to synthesize\n * @param ttsConfig - Text-to-speech configuration\n * @returns Promise that resolves with the audio data as ArrayBuffer\n * @throws {SaynaValidationError} If text is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const audioBuffer = await client.speakRest(\"Hello, world!\", {\n * provider: \"elevenlabs\",\n * voice_id: \"21m00Tcm4TlvDq8ikWAM\",\n * model: \"eleven_turbo_v2\",\n * speaking_rate: 1.0,\n * audio_format: \"mp3\",\n * sample_rate: 24000,\n * connection_timeout: 30,\n * request_timeout: 60,\n * pronunciations: []\n * });\n * ```\n */\n async speakRest(text: string, ttsConfig: TTSConfig): Promise<ArrayBuffer> {\n if (!text || text.trim().length === 0) {\n throw new SaynaValidationError(\"Text cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n \"speak\",\n {\n method: \"POST\",\n body: JSON.stringify({\n text,\n tts_config: ttsConfig,\n }),\n },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Issues a LiveKit access token for a participant.\n *\n * @param roomName - LiveKit room to join or create\n * @param participantName - Display name assigned to the participant\n * @param participantIdentity - Unique identifier for the participant\n * @returns Promise that resolves with the LiveKit token and connection details\n * @throws {SaynaValidationError} If any parameter is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const tokenInfo = await client.getLiveKitToken(\n * \"my-room\",\n * \"John Doe\",\n * \"user-123\"\n * );\n * console.log(\"Token:\", tokenInfo.token);\n * console.log(\"LiveKit URL:\", tokenInfo.livekit_url);\n * ```\n */\n async getLiveKitToken(\n roomName: string,\n participantName: string,\n participantIdentity: string\n ): Promise<LiveKitTokenResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantName || participantName.trim().length === 0) {\n throw new SaynaValidationError(\"participant_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n return this.fetchFromSayna<LiveKitTokenResponse>(\"livekit/token\", {\n method: \"POST\",\n body: JSON.stringify({\n room_name: roomName,\n participant_name: participantName,\n participant_identity: participantIdentity,\n }),\n });\n }\n\n /**\n * Downloads the recorded audio file for a completed session.\n *\n * @param streamId - The session identifier (obtained from the `streamId` getter after connection)\n * @returns Promise that resolves with the audio data as ArrayBuffer (OGG format)\n * @throws {SaynaValidationError} If streamId is empty\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the recording is not found or server returns an error\n *\n * @example\n * ```typescript\n * // After a session completes, download the recording\n * const audioBuffer = await client.getRecording(client.streamId!);\n *\n * // Save to file (Node.js)\n * import { writeFile } from \"fs/promises\";\n * await writeFile(\"recording.ogg\", Buffer.from(audioBuffer));\n * ```\n */\n async getRecording(streamId: string): Promise<ArrayBuffer> {\n if (!streamId || streamId.trim().length === 0) {\n throw new SaynaValidationError(\"streamId cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n `recording/${encodeURIComponent(streamId)}`,\n { method: \"GET\" },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Retrieves all configured SIP webhook hooks from the runtime cache.\n *\n * @returns Promise that resolves with the list of configured SIP hooks\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 500 if reading cache fails)\n *\n * @example\n * ```typescript\n * const response = await client.getSipHooks();\n * for (const hook of response.hooks) {\n * console.log(`Host: ${hook.host}, URL: ${hook.url}`);\n * }\n * ```\n */\n async getSipHooks(): Promise<SipHooksResponse> {\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\");\n }\n\n /**\n * Sets or updates SIP webhook hooks in the runtime cache.\n *\n * Hooks with matching hosts will be replaced; new hosts will be added.\n * The response contains the merged list of all hooks (existing + new).\n *\n * @param hooks - Array of SIP hook configurations to add or replace\n * @returns Promise that resolves with the merged list of all configured hooks\n * @throws {SaynaValidationError} If hooks array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for duplicate hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.setSipHooks([\n * { host: \"example.com\", url: \"https://webhook.example.com/events\" },\n * { host: \"another.com\", url: \"https://webhook.another.com/events\" }\n * ]);\n * console.log(\"Total hooks configured:\", response.hooks.length);\n * ```\n */\n async setSipHooks(hooks: SipHook[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hooks)) {\n throw new SaynaValidationError(\"hooks must be an array\");\n }\n\n if (hooks.length === 0) {\n throw new SaynaValidationError(\"hooks array cannot be empty\");\n }\n\n for (const [i, hook] of hooks.entries()) {\n if (\n !hook.host ||\n typeof hook.host !== \"string\" ||\n hook.host.trim().length === 0\n ) {\n throw new SaynaValidationError(\n `hooks[${i}].host must be a non-empty string`\n );\n }\n if (\n !hook.url ||\n typeof hook.url !== \"string\" ||\n hook.url.trim().length === 0\n ) {\n throw new SaynaValidationError(\n `hooks[${i}].url must be a non-empty string`\n );\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"POST\",\n body: JSON.stringify({ hooks }),\n });\n }\n\n /**\n * Deletes SIP webhook hooks by host name from the runtime cache.\n *\n * If a deleted host exists in the original server configuration,\n * it will revert to its config value after deletion.\n *\n * @param hosts - Array of host names to remove (case-insensitive)\n * @returns Promise that resolves with the updated list of hooks after deletion\n * @throws {SaynaValidationError} If hosts array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for empty hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.deleteSipHooks([\"example.com\", \"another.com\"]);\n * console.log(\"Remaining hooks:\", response.hooks.length);\n * ```\n */\n async deleteSipHooks(hosts: string[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hosts)) {\n throw new SaynaValidationError(\"hosts must be an array\");\n }\n\n if (hosts.length === 0) {\n throw new SaynaValidationError(\"hosts array cannot be empty\");\n }\n\n for (const [i, host] of hosts.entries()) {\n if (!host || typeof host !== \"string\" || host.trim().length === 0) {\n throw new SaynaValidationError(\n `hosts[${i}] must be a non-empty string`\n );\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"DELETE\",\n body: JSON.stringify({ hosts }),\n });\n }\n\n /**\n * Whether the client is ready to send/receive data.\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Whether the client is connected to the WebSocket.\n */\n get connected(): boolean {\n return this.isConnected;\n }\n\n /**\n * LiveKit room name acknowledged by the server, if available.\n */\n get livekitRoomName(): string | undefined {\n return this._livekitRoomName;\n }\n\n /**\n * LiveKit WebSocket URL configured on the server, if available.\n */\n get livekitUrl(): string | undefined {\n return this._livekitUrl;\n }\n\n /**\n * Identity assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantIdentity(): string | undefined {\n return this._saynaParticipantIdentity;\n }\n\n /**\n * Display name assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantName(): string | undefined {\n return this._saynaParticipantName;\n }\n\n /**\n * Session identifier returned by the server.\n * This can be used to download recordings or correlate session data.\n * The value is available after the connection is ready.\n */\n get streamId(): string | undefined {\n return this._streamId;\n }\n}\n",
5
+ "/**\n * Base error class for all Sayna SDK errors.\n */\nexport class SaynaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaError\";\n Object.setPrototypeOf(this, SaynaError.prototype);\n }\n}\n\n/**\n * Error thrown when attempting to use the client before it's connected.\n */\nexport class SaynaNotConnectedError extends SaynaError {\n constructor(message: string = \"Not connected to Sayna WebSocket\") {\n super(message);\n this.name = \"SaynaNotConnectedError\";\n Object.setPrototypeOf(this, SaynaNotConnectedError.prototype);\n }\n}\n\n/**\n * Error thrown when attempting operations before the client is ready.\n */\nexport class SaynaNotReadyError extends SaynaError {\n constructor(\n message: string = \"Sayna voice providers are not ready. Wait for the connection to be established.\"\n ) {\n super(message);\n this.name = \"SaynaNotReadyError\";\n Object.setPrototypeOf(this, SaynaNotReadyError.prototype);\n }\n}\n\n/**\n * Error thrown when WebSocket connection fails.\n */\nexport class SaynaConnectionError extends SaynaError {\n public override readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = \"SaynaConnectionError\";\n this.cause = cause;\n Object.setPrototypeOf(this, SaynaConnectionError.prototype);\n }\n}\n\n/**\n * Error thrown when invalid parameters are provided.\n */\nexport class SaynaValidationError extends SaynaError {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaValidationError\";\n Object.setPrototypeOf(this, SaynaValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when the server returns an error response.\n *\n * For REST API calls, the `status` and `endpoint` fields provide context about\n * which request failed and with what HTTP status code. Common status codes:\n * - 403: Access denied (e.g., room owned by another tenant on /livekit/token)\n * - 404: Not found or not accessible (masked access denial for room-scoped operations)\n * - 500: Internal server error\n */\nexport class SaynaServerError extends SaynaError {\n /**\n * HTTP status code returned by the server, if available.\n * Set for REST API errors; undefined for WebSocket errors.\n */\n public readonly status?: number;\n\n /**\n * API endpoint that returned the error, if available.\n * Set for REST API errors; undefined for WebSocket errors.\n */\n public readonly endpoint?: string;\n\n constructor(message: string, status?: number, endpoint?: string) {\n super(message);\n this.name = \"SaynaServerError\";\n this.status = status;\n this.endpoint = endpoint;\n Object.setPrototypeOf(this, SaynaServerError.prototype);\n }\n}\n",
6
+ "import type {\n STTConfig,\n TTSConfig,\n LiveKitConfig,\n ConfigMessage,\n SpeakMessage,\n ClearMessage,\n SendMessageMessage,\n STTResultMessage,\n ErrorMessage,\n SipTransferErrorMessage,\n SipTransferMessage,\n OutgoingMessage,\n SaynaMessage,\n Participant,\n VoicesResponse,\n HealthResponse,\n LiveKitTokenResponse,\n LiveKitRoomsResponse,\n LiveKitRoomDetails,\n SipHook,\n SipHooksResponse,\n RemoveLiveKitParticipantResponse,\n MuteLiveKitParticipantResponse,\n SipTransferResponse,\n} from \"./types\";\nimport {\n SaynaNotConnectedError,\n SaynaNotReadyError,\n SaynaConnectionError,\n SaynaValidationError,\n SaynaServerError,\n} from \"./errors\";\n\n// Node.js 18+ has native fetch support\ndeclare const fetch: typeof globalThis.fetch;\n\n/**\n * Event handler for speech-to-text results.\n */\nexport type STTResultHandler = (\n result: STTResultMessage\n) => void | Promise<void>;\n\n/**\n * Event handler for text-to-speech audio data.\n */\nexport type TTSAudioHandler = (audio: ArrayBuffer) => void | Promise<void>;\n\n/**\n * Event handler for error messages.\n */\nexport type ErrorHandler = (error: ErrorMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant messages.\n */\nexport type MessageHandler = (message: SaynaMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant disconnections.\n */\nexport type ParticipantDisconnectedHandler = (\n participant: Participant\n) => void | Promise<void>;\n\n/**\n * Event handler for TTS playback completion.\n */\nexport type TTSPlaybackCompleteHandler = (\n timestamp: number\n) => void | Promise<void>;\n\n/**\n * Event handler for SIP transfer specific errors.\n */\nexport type SipTransferErrorHandler = (\n error: SipTransferErrorMessage\n) => void | Promise<void>;\n\n/**\n * Client for connecting to Sayna WebSocket server for real-time voice interactions.\n *\n * @example\n * ```typescript\n * const client = await saynaConnect(\n * \"https://api.sayna.ai\",\n * sttConfig,\n * ttsConfig\n * );\n *\n * client.registerOnSttResult((result) => {\n * console.log(\"Transcription:\", result.transcript);\n * });\n *\n * await client.speak(\"Hello, world!\");\n * ```\n */\nexport class SaynaClient {\n private url: string;\n private sttConfig?: STTConfig;\n private ttsConfig?: TTSConfig;\n private livekitConfig?: LiveKitConfig;\n private withoutAudio: boolean;\n private apiKey?: string;\n private websocket?: WebSocket;\n private isConnected: boolean = false;\n private isReady: boolean = false;\n private _livekitRoomName?: string;\n private _livekitUrl?: string;\n private _saynaParticipantIdentity?: string;\n private _saynaParticipantName?: string;\n private _streamId?: string;\n private inputStreamId?: string;\n private sttCallback?: STTResultHandler;\n private ttsCallback?: TTSAudioHandler;\n private errorCallback?: ErrorHandler;\n private messageCallback?: MessageHandler;\n private participantDisconnectedCallback?: ParticipantDisconnectedHandler;\n private ttsPlaybackCompleteCallback?: TTSPlaybackCompleteHandler;\n private sipTransferErrorCallback?: SipTransferErrorHandler;\n private readyPromiseResolve?: () => void;\n private readyPromiseReject?: (error: Error) => void;\n\n /**\n * Creates a new SaynaClient instance.\n *\n * @param url - The Sayna server URL (e.g., \"https://api.sayna.ai\")\n * @param sttConfig - Speech-to-text configuration (required when withoutAudio=false)\n * @param ttsConfig - Text-to-speech configuration (required when withoutAudio=false)\n * @param livekitConfig - Optional LiveKit room configuration\n * @param withoutAudio - If true, disables audio streaming (default: false)\n * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)\n * @param streamId - Optional session identifier for recording paths; server generates a UUID when omitted\n *\n * @throws {SaynaValidationError} If URL is invalid or if audio configs are missing when audio is enabled\n */\n constructor(\n url: string,\n sttConfig?: STTConfig,\n ttsConfig?: TTSConfig,\n livekitConfig?: LiveKitConfig,\n withoutAudio: boolean = false,\n apiKey?: string,\n streamId?: string\n ) {\n // Validate URL\n if (!url || typeof url !== \"string\") {\n throw new SaynaValidationError(\"URL must be a non-empty string\");\n }\n if (\n !url.startsWith(\"http://\") &&\n !url.startsWith(\"https://\") &&\n !url.startsWith(\"ws://\") &&\n !url.startsWith(\"wss://\")\n ) {\n throw new SaynaValidationError(\n \"URL must start with http://, https://, ws://, or wss://\"\n );\n }\n\n // Validate audio config requirements\n if (!withoutAudio) {\n if (!sttConfig || !ttsConfig) {\n throw new SaynaValidationError(\n \"sttConfig and ttsConfig are required when withoutAudio=false (audio streaming enabled). \" +\n \"Either provide both configs or set withoutAudio=true for non-audio use cases.\"\n );\n }\n }\n\n this.url = url;\n this.sttConfig = sttConfig;\n this.ttsConfig = ttsConfig;\n this.livekitConfig = livekitConfig;\n this.withoutAudio = withoutAudio;\n this.apiKey = apiKey ?? process.env.SAYNA_API_KEY;\n this.inputStreamId = streamId;\n }\n\n /**\n * Establishes connection to the Sayna WebSocket server.\n *\n * @throws {SaynaConnectionError} If connection fails\n * @returns Promise that resolves when the connection is ready\n */\n async connect(): Promise<void> {\n if (this.isConnected) {\n return;\n }\n\n // Convert HTTP(S) URL to WebSocket URL\n const wsUrl =\n this.url.replace(/^https:\\/\\//, \"wss://\").replace(/^http:\\/\\//, \"ws://\") +\n (this.url.endsWith(\"/\") ? \"ws\" : \"/ws\");\n\n return new Promise((resolve, reject) => {\n this.readyPromiseResolve = resolve;\n this.readyPromiseReject = reject;\n\n try {\n const headers = this.apiKey\n ? { Authorization: `Bearer ${this.apiKey}` }\n : undefined;\n\n const WebSocketConstructor = WebSocket as unknown as {\n new (\n url: string,\n protocols?: string | string[],\n options?: { headers?: Record<string, string> }\n ): WebSocket;\n };\n\n this.websocket = headers\n ? new WebSocketConstructor(wsUrl, undefined, { headers })\n : new WebSocket(wsUrl);\n\n this.websocket.onopen = () => {\n this.isConnected = true;\n\n // Send initial configuration\n const configMessage: ConfigMessage = {\n type: \"config\",\n stream_id: this.inputStreamId,\n stt_config: this.sttConfig,\n tts_config: this.ttsConfig,\n livekit: this.livekitConfig,\n audio: !this.withoutAudio,\n };\n\n try {\n if (this.websocket) {\n this.websocket.send(JSON.stringify(configMessage));\n }\n } catch (error) {\n this.cleanup();\n const err = new SaynaConnectionError(\n \"Failed to send configuration\",\n error\n );\n if (this.readyPromiseReject) {\n this.readyPromiseReject(err);\n }\n }\n };\n\n this.websocket.onmessage = async (event) => {\n try {\n if (\n event.data instanceof Blob ||\n event.data instanceof ArrayBuffer\n ) {\n // Binary TTS audio data\n const buffer =\n event.data instanceof Blob\n ? await event.data.arrayBuffer()\n : event.data;\n if (this.ttsCallback) {\n await this.ttsCallback(buffer);\n }\n } else {\n // JSON control messages\n if (typeof event.data !== \"string\") {\n throw new Error(\"Expected string data for JSON messages\");\n }\n const data = JSON.parse(event.data) as OutgoingMessage;\n await this.handleJsonMessage(data);\n }\n } catch (error) {\n // Log parse errors but don't break the connection\n if (this.errorCallback) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n await this.errorCallback({\n type: \"error\",\n message: `Failed to process message: ${errorMessage}`,\n });\n }\n }\n };\n\n this.websocket.onerror = () => {\n const error = new SaynaConnectionError(\"WebSocket connection error\");\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(error);\n }\n };\n\n this.websocket.onclose = (event) => {\n const wasReady = this.isReady;\n this.cleanup();\n\n // If connection closed before ready, reject the promise\n if (!wasReady && this.readyPromiseReject) {\n this.readyPromiseReject(\n new SaynaConnectionError(\n `WebSocket closed before ready (code: ${event.code}, reason: ${event.reason || \"none\"})`\n )\n );\n }\n };\n } catch (error) {\n reject(new SaynaConnectionError(\"Failed to create WebSocket\", error));\n }\n });\n }\n\n /**\n * Handles incoming JSON messages from the WebSocket.\n * @internal\n */\n private async handleJsonMessage(data: OutgoingMessage): Promise<void> {\n const messageType = data.type;\n\n try {\n switch (messageType) {\n case \"ready\": {\n const readyMsg = data;\n this.isReady = true;\n this._livekitRoomName = readyMsg.livekit_room_name;\n this._livekitUrl = readyMsg.livekit_url;\n this._saynaParticipantIdentity = readyMsg.sayna_participant_identity;\n this._saynaParticipantName = readyMsg.sayna_participant_name;\n this._streamId = readyMsg.stream_id;\n if (this.readyPromiseResolve) {\n this.readyPromiseResolve();\n }\n break;\n }\n\n case \"stt_result\": {\n const sttResult = data;\n if (this.sttCallback) {\n await this.sttCallback(sttResult);\n }\n break;\n }\n\n case \"error\": {\n const errorMsg = data;\n if (this.errorCallback) {\n await this.errorCallback(errorMsg);\n }\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(new SaynaServerError(errorMsg.message));\n }\n break;\n }\n\n case \"sip_transfer_error\": {\n const sipTransferError = data;\n if (this.sipTransferErrorCallback) {\n await this.sipTransferErrorCallback(sipTransferError);\n } else if (this.errorCallback) {\n await this.errorCallback({\n type: \"error\",\n message: sipTransferError.message,\n });\n }\n break;\n }\n\n case \"message\": {\n const messageData = data;\n if (this.messageCallback) {\n await this.messageCallback(messageData.message);\n }\n break;\n }\n\n case \"participant_disconnected\": {\n const participantMsg = data;\n if (this.participantDisconnectedCallback) {\n await this.participantDisconnectedCallback(\n participantMsg.participant\n );\n }\n break;\n }\n\n case \"tts_playback_complete\": {\n const ttsPlaybackCompleteMsg = data;\n if (this.ttsPlaybackCompleteCallback) {\n await this.ttsPlaybackCompleteCallback(\n ttsPlaybackCompleteMsg.timestamp\n );\n }\n break;\n }\n\n default: {\n const unknownMessage = data as { type: string };\n const errorMessage = `Unknown message type received: ${unknownMessage.type}`;\n if (this.errorCallback) {\n await this.errorCallback({ type: \"error\", message: errorMessage });\n } else {\n console.warn(errorMessage);\n }\n }\n }\n } catch (error) {\n // Notify error callback if handler fails\n if (this.errorCallback) {\n await this.errorCallback({\n type: \"error\",\n message: `Handler error: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n }\n\n /**\n * Cleans up internal state.\n * @internal\n */\n private cleanup(): void {\n this.isConnected = false;\n this.isReady = false;\n this._livekitRoomName = undefined;\n this._livekitUrl = undefined;\n this._saynaParticipantIdentity = undefined;\n this._saynaParticipantName = undefined;\n this._streamId = undefined;\n }\n\n /**\n * Converts WebSocket URL to HTTP URL for REST API calls.\n * @internal\n */\n private getHttpUrl(): string {\n return this.url\n .replace(/^ws:\\/\\//, \"http://\")\n .replace(/^wss:\\/\\//, \"https://\");\n }\n\n /**\n * Generic fetch helper for making REST API calls to Sayna server.\n * Handles URL construction, headers, error responses, and type conversion.\n * @internal\n *\n * @param endpoint - API endpoint path (e.g., \"/voices\", \"/speak\")\n * @param options - Fetch options including method, body, headers, etc.\n * @param responseType - Expected response type: \"json\" or \"arrayBuffer\"\n * @returns Promise resolving to the parsed response\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the server returns an error response (includes status and endpoint)\n */\n private async fetchFromSayna<T>(\n endpoint: string,\n options: RequestInit = {},\n responseType: \"json\" | \"arrayBuffer\" = \"json\"\n ): Promise<T> {\n const httpUrl = this.getHttpUrl();\n const normalizedEndpoint = endpoint.startsWith(\"/\")\n ? endpoint.slice(1)\n : endpoint;\n const url = `${httpUrl}${httpUrl.endsWith(\"/\") ? \"\" : \"/\"}${normalizedEndpoint}`;\n\n // Merge default headers with user-provided headers\n const headers: Record<string, string> = {\n ...(options.headers as Record<string, string>),\n };\n\n // Add Authorization header when an API key is provided, unless user supplied one\n const hasAuthHeader = Object.keys(headers).some(\n (key) => key.toLowerCase() === \"authorization\"\n );\n if (this.apiKey && !hasAuthHeader) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n // Add Content-Type for JSON requests with body if not already set\n if (options.body && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n // Try to parse error message from JSON response\n let errorMessage: string;\n try {\n const errorData: unknown = await response.json();\n errorMessage =\n errorData &&\n typeof errorData === \"object\" &&\n \"error\" in errorData &&\n typeof errorData.error === \"string\"\n ? errorData.error\n : `Request failed: ${response.status} ${response.statusText}`;\n } catch {\n errorMessage = `Request failed: ${response.status} ${response.statusText}`;\n }\n\n // Enhance error messages for specific status codes\n if (response.status === 403) {\n errorMessage = `Access denied: ${errorMessage}`;\n } else if (response.status === 404) {\n errorMessage = `Not found or not accessible: ${errorMessage}`;\n }\n\n throw new SaynaServerError(\n errorMessage,\n response.status,\n normalizedEndpoint\n );\n }\n\n // Parse response based on expected type\n if (responseType === \"arrayBuffer\") {\n return (await response.arrayBuffer()) as T;\n } else {\n return (await response.json()) as T;\n }\n } catch (error) {\n // Re-throw SaynaServerError as-is\n if (error instanceof SaynaServerError) {\n throw error;\n }\n // Wrap other errors in SaynaConnectionError\n throw new SaynaConnectionError(`Failed to fetch from ${endpoint}`, error);\n }\n }\n\n /**\n * Disconnects from the Sayna WebSocket server and cleans up resources.\n */\n disconnect(): void {\n if (this.websocket) {\n // Remove event handlers to prevent memory leaks\n this.websocket.onopen = null;\n this.websocket.onmessage = null;\n this.websocket.onerror = null;\n this.websocket.onclose = null;\n\n if (this.websocket.readyState === WebSocket.OPEN) {\n this.websocket.close(1000, \"Client disconnect\");\n }\n\n this.websocket = undefined;\n }\n\n this.cleanup();\n }\n\n /**\n * Sends audio data to the server for speech recognition.\n *\n * @param audioData - Raw audio data as ArrayBuffer\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n onAudioInput(audioData: ArrayBuffer): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (!(audioData instanceof ArrayBuffer)) {\n throw new SaynaValidationError(\"audioData must be an ArrayBuffer\");\n }\n\n if (audioData.byteLength === 0) {\n throw new SaynaValidationError(\"audioData cannot be empty\");\n }\n\n try {\n this.websocket.send(audioData);\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send audio data\", error);\n }\n }\n\n /**\n * Registers a callback for speech-to-text results.\n *\n * @param callback - Function to call when STT results are received\n */\n registerOnSttResult(callback: STTResultHandler): void {\n this.sttCallback = callback;\n }\n\n /**\n * Registers a callback for text-to-speech audio data.\n *\n * @param callback - Function to call when TTS audio is received\n */\n registerOnTtsAudio(callback: TTSAudioHandler): void {\n this.ttsCallback = callback;\n }\n\n /**\n * Registers a callback for error messages.\n *\n * @param callback - Function to call when errors occur\n */\n registerOnError(callback: ErrorHandler): void {\n this.errorCallback = callback;\n }\n\n /**\n * Registers a callback for participant messages.\n *\n * @param callback - Function to call when messages are received\n */\n registerOnMessage(callback: MessageHandler): void {\n this.messageCallback = callback;\n }\n\n /**\n * Registers a callback for participant disconnection events.\n *\n * @param callback - Function to call when a participant disconnects\n */\n registerOnParticipantDisconnected(\n callback: ParticipantDisconnectedHandler\n ): void {\n this.participantDisconnectedCallback = callback;\n }\n\n /**\n * Registers a callback for TTS playback completion.\n *\n * @param callback - Function to call when TTS playback is complete\n */\n registerOnTtsPlaybackComplete(callback: TTSPlaybackCompleteHandler): void {\n this.ttsPlaybackCompleteCallback = callback;\n }\n\n /**\n * Registers a callback for SIP transfer specific errors.\n *\n * @param callback - Function to call when a SIP transfer error message is received\n */\n registerOnSipTransferError(callback: SipTransferErrorHandler): void {\n this.sipTransferErrorCallback = callback;\n }\n\n /**\n * Sends text to be synthesized as speech.\n *\n * @param text - Text to synthesize\n * @param flush - Whether to flush the TTS queue before speaking (default: true)\n * @param allowInterruption - Whether this speech can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If text is not a string\n */\n speak(\n text: string,\n flush: boolean = true,\n allowInterruption: boolean = true\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof text !== \"string\") {\n throw new SaynaValidationError(\"text must be a string\");\n }\n\n try {\n const speakMessage: SpeakMessage = {\n type: \"speak\",\n text,\n flush,\n allow_interruption: allowInterruption,\n };\n this.websocket.send(JSON.stringify(speakMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send speak command\", error);\n }\n }\n\n /**\n * Clears the text-to-speech queue.\n *\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n clear(): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n try {\n const clearMessage: ClearMessage = {\n type: \"clear\",\n };\n this.websocket.send(JSON.stringify(clearMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send clear command\", error);\n }\n }\n\n /**\n * Flushes the TTS queue by sending an empty speak command.\n *\n * @param allowInterruption - Whether the flush can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n ttsFlush(allowInterruption: boolean = true): void {\n this.speak(\"\", true, allowInterruption);\n }\n\n /**\n * Sends a message to the Sayna session.\n *\n * @param message - Message content\n * @param role - Message role (e.g., \"user\", \"assistant\")\n * @param topic - Optional topic identifier\n * @param debug - Optional debug metadata\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If parameters are invalid\n */\n sendMessage(\n message: string,\n role: string,\n topic?: string,\n debug?: Record<string, unknown>\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof message !== \"string\") {\n throw new SaynaValidationError(\"message must be a string\");\n }\n\n if (typeof role !== \"string\") {\n throw new SaynaValidationError(\"role must be a string\");\n }\n\n try {\n const sendMsg: SendMessageMessage = {\n type: \"send_message\",\n message,\n role,\n topic,\n debug,\n };\n this.websocket.send(JSON.stringify(sendMsg));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send message\", error);\n }\n }\n\n /**\n * Initiates a SIP transfer for the active LiveKit session.\n *\n * @param transferTo - Destination phone number or extension to transfer to\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If transferTo is not a non-empty string\n */\n sipTransfer(transferTo: string): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof transferTo !== \"string\" || transferTo.trim().length === 0) {\n throw new SaynaValidationError(\"transfer_to must be a non-empty string\");\n }\n\n try {\n const sipTransferMessage: SipTransferMessage = {\n type: \"sip_transfer\",\n transfer_to: transferTo.trim(),\n };\n this.websocket.send(JSON.stringify(sipTransferMessage));\n } catch (error) {\n throw new SaynaConnectionError(\n \"Failed to send SIP transfer command\",\n error\n );\n }\n }\n\n /**\n * Performs a health check on the Sayna server.\n *\n * @returns Promise that resolves with the health status\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const health = await client.health();\n * console.log(health.status); // \"OK\"\n * ```\n */\n async health(): Promise<HealthResponse> {\n return this.fetchFromSayna<HealthResponse>(\"\");\n }\n\n /**\n * Retrieves the catalogue of text-to-speech voices grouped by provider.\n *\n * @returns Promise that resolves with voices organized by provider\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const voices = await client.getVoices();\n * for (const [provider, voiceList] of Object.entries(voices)) {\n * console.log(`${provider}:`, voiceList.map(v => v.name));\n * }\n * ```\n */\n async getVoices(): Promise<VoicesResponse> {\n return this.fetchFromSayna<VoicesResponse>(\"voices\");\n }\n\n /**\n * Synthesizes text into audio using the REST API endpoint.\n * This is a standalone synthesis method that doesn't require an active WebSocket connection.\n *\n * @param text - Text to synthesize\n * @param ttsConfig - Text-to-speech configuration\n * @returns Promise that resolves with the audio data as ArrayBuffer\n * @throws {SaynaValidationError} If text is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const audioBuffer = await client.speakRest(\"Hello, world!\", {\n * provider: \"elevenlabs\",\n * voice_id: \"21m00Tcm4TlvDq8ikWAM\",\n * model: \"eleven_turbo_v2\",\n * speaking_rate: 1.0,\n * audio_format: \"mp3\",\n * sample_rate: 24000,\n * connection_timeout: 30,\n * request_timeout: 60,\n * pronunciations: []\n * });\n * ```\n */\n async speakRest(text: string, ttsConfig: TTSConfig): Promise<ArrayBuffer> {\n if (!text || text.trim().length === 0) {\n throw new SaynaValidationError(\"Text cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n \"speak\",\n {\n method: \"POST\",\n body: JSON.stringify({\n text,\n tts_config: ttsConfig,\n }),\n },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Issues a LiveKit access token for a participant.\n *\n * Room names are used as-is; the SDK does not rewrite or prefix them. When authentication\n * is enabled, this endpoint creates the room if missing and sets room ownership metadata.\n *\n * @param roomName - LiveKit room to join or create. Provide the clean room name without any prefix.\n * @param participantName - Display name assigned to the participant\n * @param participantIdentity - Unique identifier for the participant\n * @returns Promise that resolves with the LiveKit token and connection details\n * @throws {SaynaValidationError} If any parameter is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error. A 403 status indicates the room\n * is owned by another tenant; do not retry with a modified room name.\n *\n * @example\n * ```typescript\n * const tokenInfo = await client.getLiveKitToken(\n * \"my-room\",\n * \"John Doe\",\n * \"user-123\"\n * );\n * console.log(\"Token:\", tokenInfo.token);\n * console.log(\"LiveKit URL:\", tokenInfo.livekit_url);\n * ```\n */\n async getLiveKitToken(\n roomName: string,\n participantName: string,\n participantIdentity: string\n ): Promise<LiveKitTokenResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantName || participantName.trim().length === 0) {\n throw new SaynaValidationError(\"participant_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n return this.fetchFromSayna<LiveKitTokenResponse>(\"livekit/token\", {\n method: \"POST\",\n body: JSON.stringify({\n room_name: roomName,\n participant_name: participantName,\n participant_identity: participantIdentity,\n }),\n });\n }\n\n /**\n * Lists LiveKit rooms accessible to the authenticated context.\n *\n * Room listings are scoped server-side based on authentication. When authentication is\n * enabled, this endpoint may return fewer rooms than before (only those you have access to).\n * Room names are not modified by the SDK.\n *\n * @returns Promise that resolves with the list of accessible rooms\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., LiveKit not configured)\n *\n * @example\n * ```typescript\n * const response = await client.getLiveKitRooms();\n * for (const room of response.rooms) {\n * console.log(`Room: ${room.name}, Participants: ${room.num_participants}`);\n * }\n * ```\n */\n async getLiveKitRooms(): Promise<LiveKitRoomsResponse> {\n return this.fetchFromSayna<LiveKitRoomsResponse>(\"livekit/rooms\");\n }\n\n /**\n * Retrieves detailed information about a specific LiveKit room including participants.\n *\n * Room names are used as-is; the SDK does not rewrite or prefix them. Access is\n * enforced server-side based on room ownership metadata.\n *\n * @param roomName - Name of the room to retrieve\n * @returns Promise that resolves with detailed room information including participants\n * @throws {SaynaValidationError} If roomName is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error. A 404 status can mean \"not found\"\n * or \"not accessible\" when room ownership is enforced.\n *\n * @example\n * ```typescript\n * const room = await client.getLiveKitRoom(\"my-room\");\n * console.log(`Room: ${room.name}, SID: ${room.sid}`);\n * console.log(`Participants: ${room.num_participants}/${room.max_participants}`);\n * for (const participant of room.participants) {\n * console.log(` - ${participant.name} (${participant.identity}): ${participant.state}`);\n * }\n * ```\n */\n async getLiveKitRoom(roomName: string): Promise<LiveKitRoomDetails> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n const encoded = encodeURIComponent(roomName.trim());\n return this.fetchFromSayna<LiveKitRoomDetails>(`livekit/rooms/${encoded}`);\n }\n\n /**\n * Removes a participant from a LiveKit room, forcibly disconnecting them.\n *\n * Room names are used as-is; the SDK does not rewrite or prefix them. Access is\n * enforced server-side based on room ownership metadata.\n *\n * **Important:** This does not invalidate the participant's token. To prevent\n * rejoining, use short-lived tokens and avoid issuing new tokens to removed participants.\n *\n * @param roomName - Name of the room where the participant is connected\n * @param participantIdentity - The identity of the participant to remove\n * @returns Promise that resolves with the removal confirmation\n * @throws {SaynaValidationError} If roomName or participantIdentity is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error. A 404 status can mean \"not found\"\n * or \"not accessible\" when room ownership is enforced.\n *\n * @example\n * ```typescript\n * const result = await client.removeLiveKitParticipant(\"my-room\", \"user-alice-456\");\n * console.log(`Status: ${result.status}`);\n * console.log(`Removed from room: ${result.room_name}`);\n * console.log(`Participant: ${result.participant_identity}`);\n * ```\n */\n async removeLiveKitParticipant(\n roomName: string,\n participantIdentity: string\n ): Promise<RemoveLiveKitParticipantResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n return this.fetchFromSayna<RemoveLiveKitParticipantResponse>(\n \"livekit/participant\",\n {\n method: \"DELETE\",\n body: JSON.stringify({\n room_name: roomName.trim(),\n participant_identity: participantIdentity.trim(),\n }),\n }\n );\n }\n\n /**\n * Mutes or unmutes a participant's published track in a LiveKit room.\n *\n * Room names are used as-is; the SDK does not rewrite or prefix them. Access is\n * enforced server-side based on room ownership metadata.\n *\n * @param roomName - Name of the room where the participant is connected\n * @param participantIdentity - The identity of the participant whose track to mute\n * @param trackSid - The session ID of the track to mute/unmute\n * @param muted - True to mute, false to unmute\n * @returns Promise that resolves with the mute operation result\n * @throws {SaynaValidationError} If roomName, participantIdentity, or trackSid is empty, or if muted is not a boolean\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error. A 404 status can mean \"not found\"\n * or \"not accessible\" when room ownership is enforced.\n *\n * @example\n * ```typescript\n * // Mute a participant's track\n * const result = await client.muteLiveKitParticipantTrack(\n * \"my-room\",\n * \"user-alice-456\",\n * \"TR_abc123\",\n * true\n * );\n * console.log(`Track ${result.track_sid} muted: ${result.muted}`);\n *\n * // Unmute the track\n * const unmuteResult = await client.muteLiveKitParticipantTrack(\n * \"my-room\",\n * \"user-alice-456\",\n * \"TR_abc123\",\n * false\n * );\n * ```\n */\n async muteLiveKitParticipantTrack(\n roomName: string,\n participantIdentity: string,\n trackSid: string,\n muted: boolean\n ): Promise<MuteLiveKitParticipantResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n if (!trackSid || trackSid.trim().length === 0) {\n throw new SaynaValidationError(\"track_sid cannot be empty\");\n }\n\n if (typeof muted !== \"boolean\") {\n throw new SaynaValidationError(\"muted must be a boolean\");\n }\n\n return this.fetchFromSayna<MuteLiveKitParticipantResponse>(\n \"livekit/participant/mute\",\n {\n method: \"POST\",\n body: JSON.stringify({\n room_name: roomName.trim(),\n participant_identity: participantIdentity.trim(),\n track_sid: trackSid.trim(),\n muted,\n }),\n }\n );\n }\n\n /**\n * Initiates a SIP call transfer via the REST API endpoint.\n *\n * This is distinct from the WebSocket `sipTransfer()` method. Use this REST endpoint\n * when you need to transfer a SIP call from outside the active WebSocket session,\n * or when you want to specify a particular room and participant explicitly.\n *\n * Room names are used as-is; the SDK does not rewrite or prefix them. Access is\n * enforced server-side based on room ownership metadata.\n *\n * **Important Notes:**\n * - Only SIP participants can be transferred\n * - A successful response indicates the transfer has been **initiated**, not necessarily completed\n * - The actual transfer may take several seconds\n *\n * @param roomName - Name of the room where the SIP participant is connected\n * @param participantIdentity - The identity of the SIP participant to transfer\n * @param transferTo - The phone number to transfer to (international, national, or extension format)\n * @returns Promise that resolves with the transfer status\n * @throws {SaynaValidationError} If roomName, participantIdentity, or transferTo is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error. A 404 status can mean \"not found\"\n * or \"not accessible\" when room ownership is enforced.\n *\n * @example\n * ```typescript\n * // Transfer a SIP participant to another phone number\n * const result = await client.sipTransferRest(\n * \"call-room-123\",\n * \"sip_participant_456\",\n * \"+15551234567\"\n * );\n * console.log(`Transfer status: ${result.status}`);\n * console.log(`Transferred to: ${result.transfer_to}`);\n * ```\n */\n async sipTransferRest(\n roomName: string,\n participantIdentity: string,\n transferTo: string\n ): Promise<SipTransferResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n if (!transferTo || transferTo.trim().length === 0) {\n throw new SaynaValidationError(\"transfer_to cannot be empty\");\n }\n\n return this.fetchFromSayna<SipTransferResponse>(\"sip/transfer\", {\n method: \"POST\",\n body: JSON.stringify({\n room_name: roomName.trim(),\n participant_identity: participantIdentity.trim(),\n transfer_to: transferTo.trim(),\n }),\n });\n }\n\n /**\n * Downloads the recorded audio file for a completed session.\n *\n * @param streamId - The session identifier (obtained from the `streamId` getter after connection)\n * @returns Promise that resolves with the audio data as ArrayBuffer (OGG format)\n * @throws {SaynaValidationError} If streamId is empty\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the recording is not found or server returns an error\n *\n * @example\n * ```typescript\n * // After a session completes, download the recording\n * const audioBuffer = await client.getRecording(client.streamId!);\n *\n * // Save to file (Node.js)\n * import { writeFile } from \"fs/promises\";\n * await writeFile(\"recording.ogg\", Buffer.from(audioBuffer));\n * ```\n */\n async getRecording(streamId: string): Promise<ArrayBuffer> {\n if (!streamId || streamId.trim().length === 0) {\n throw new SaynaValidationError(\"streamId cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n `recording/${encodeURIComponent(streamId)}`,\n { method: \"GET\" },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Retrieves all configured SIP webhook hooks from the runtime cache.\n *\n * @returns Promise that resolves with the list of configured SIP hooks\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 500 if reading cache fails)\n *\n * @example\n * ```typescript\n * const response = await client.getSipHooks();\n * for (const hook of response.hooks) {\n * console.log(`Host: ${hook.host}, URL: ${hook.url}`);\n * }\n * ```\n */\n async getSipHooks(): Promise<SipHooksResponse> {\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\");\n }\n\n /**\n * Sets or updates SIP webhook hooks in the runtime cache.\n *\n * Hooks with matching hosts will be replaced; new hosts will be added.\n * The response contains the merged list of all hooks (existing + new).\n *\n * @param hooks - Array of SIP hook configurations to add or replace\n * @returns Promise that resolves with the merged list of all configured hooks\n * @throws {SaynaValidationError} If hooks array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for duplicate hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.setSipHooks([\n * { host: \"example.com\", url: \"https://webhook.example.com/events\", auth_id: \"tenant-123\" },\n * { host: \"another.com\", url: \"https://webhook.another.com/events\", auth_id: \"\" } // Empty for unauthenticated mode\n * ]);\n * console.log(\"Total hooks configured:\", response.hooks.length);\n * ```\n */\n async setSipHooks(hooks: SipHook[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hooks)) {\n throw new SaynaValidationError(\"hooks must be an array\");\n }\n\n if (hooks.length === 0) {\n throw new SaynaValidationError(\"hooks array cannot be empty\");\n }\n\n for (const [i, hook] of hooks.entries()) {\n if (\n !hook.host ||\n typeof hook.host !== \"string\" ||\n hook.host.trim().length === 0\n ) {\n throw new SaynaValidationError(\n `hooks[${i}].host must be a non-empty string`\n );\n }\n if (\n !hook.url ||\n typeof hook.url !== \"string\" ||\n hook.url.trim().length === 0\n ) {\n throw new SaynaValidationError(\n `hooks[${i}].url must be a non-empty string`\n );\n }\n // auth_id is required but may be an empty string for unauthenticated mode\n if (typeof hook.auth_id !== \"string\") {\n throw new SaynaValidationError(\n `hooks[${i}].auth_id must be a string`\n );\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"POST\",\n body: JSON.stringify({ hooks }),\n });\n }\n\n /**\n * Deletes SIP webhook hooks by host name from the runtime cache.\n *\n * If a deleted host exists in the original server configuration,\n * it will revert to its config value after deletion.\n *\n * @param hosts - Array of host names to remove (case-insensitive)\n * @returns Promise that resolves with the updated list of hooks after deletion\n * @throws {SaynaValidationError} If hosts array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for empty hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.deleteSipHooks([\"example.com\", \"another.com\"]);\n * console.log(\"Remaining hooks:\", response.hooks.length);\n * ```\n */\n async deleteSipHooks(hosts: string[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hosts)) {\n throw new SaynaValidationError(\"hosts must be an array\");\n }\n\n if (hosts.length === 0) {\n throw new SaynaValidationError(\"hosts array cannot be empty\");\n }\n\n for (const [i, host] of hosts.entries()) {\n if (!host || typeof host !== \"string\" || host.trim().length === 0) {\n throw new SaynaValidationError(\n `hosts[${i}] must be a non-empty string`\n );\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"DELETE\",\n body: JSON.stringify({ hosts }),\n });\n }\n\n /**\n * Whether the client is ready to send/receive data.\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Whether the client is connected to the WebSocket.\n */\n get connected(): boolean {\n return this.isConnected;\n }\n\n /**\n * LiveKit room name acknowledged by the server, if available.\n */\n get livekitRoomName(): string | undefined {\n return this._livekitRoomName;\n }\n\n /**\n * LiveKit WebSocket URL configured on the server, if available.\n */\n get livekitUrl(): string | undefined {\n return this._livekitUrl;\n }\n\n /**\n * Identity assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantIdentity(): string | undefined {\n return this._saynaParticipantIdentity;\n }\n\n /**\n * Display name assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantName(): string | undefined {\n return this._saynaParticipantName;\n }\n\n /**\n * Session identifier returned by the server.\n * This can be used to download recordings or correlate session data.\n * The value is available after the connection is ready.\n */\n get streamId(): string | undefined {\n return this._streamId;\n }\n}\n",
7
7
  "import { createHmac, timingSafeEqual } from \"crypto\";\nimport { SaynaValidationError } from \"./errors\";\nimport type { WebhookSIPOutput } from \"./types\";\n\n/**\n * Minimum required secret length in characters for security.\n * @internal\n */\nconst MIN_SECRET_LENGTH = 16;\n\n/**\n * Maximum allowed time difference in seconds for replay protection.\n * Webhooks with timestamps outside this window will be rejected.\n * @internal\n */\nconst TIMESTAMP_TOLERANCE_SECONDS = 300; // 5 minutes\n\n/**\n * Receives and verifies cryptographically signed webhooks from Sayna SIP service.\n *\n * This class handles the secure verification of webhook signatures using HMAC-SHA256,\n * validates timestamp freshness to prevent replay attacks, and parses the webhook\n * payload into a strongly-typed WebhookSIPOutput object.\n *\n * ## Security Features\n *\n * - **HMAC-SHA256 Signature Verification**: Ensures webhook authenticity\n * - **Constant-Time Comparison**: Prevents timing attack vulnerabilities\n * - **Replay Protection**: 5-minute timestamp window prevents replay attacks\n * - **Strict Validation**: Comprehensive checks on all required fields\n *\n * ## Usage\n *\n * ### Basic Example\n *\n * ```typescript\n * import { WebhookReceiver } from \"@sayna-ai/node-sdk\";\n *\n * // Initialize with secret (or uses SAYNA_WEBHOOK_SECRET env variable)\n * const receiver = new WebhookReceiver(\"your-secret-key-min-16-chars\");\n *\n * // In your Express route handler\n * app.post('/webhook', express.json({ verify: (req, res, buf) => {\n * req.rawBody = buf.toString('utf8');\n * }}), (req, res) => {\n * try {\n * const webhook = receiver.receive(req.headers, req.rawBody);\n *\n * console.log('Valid webhook received:');\n * console.log(' From:', webhook.from_phone_number);\n * console.log(' To:', webhook.to_phone_number);\n * console.log(' Room:', webhook.room.name);\n * console.log(' SIP Host:', webhook.sip_host);\n * console.log(' Participant:', webhook.participant.identity);\n *\n * res.status(200).json({ received: true });\n * } catch (error) {\n * console.error('Webhook verification failed:', error.message);\n * res.status(401).json({ error: 'Invalid signature' });\n * }\n * });\n * ```\n *\n * ### With Environment Variable\n *\n * ```typescript\n * // Set environment variable\n * process.env.SAYNA_WEBHOOK_SECRET = \"your-secret-key\";\n *\n * // Receiver automatically uses env variable\n * const receiver = new WebhookReceiver();\n * ```\n *\n * ### Fastify Example\n *\n * ```typescript\n * import Fastify from 'fastify';\n * import { WebhookReceiver } from \"@sayna-ai/node-sdk\";\n *\n * const fastify = Fastify();\n * const receiver = new WebhookReceiver();\n *\n * fastify.post('/webhook', {\n * config: {\n * rawBody: true\n * }\n * }, async (request, reply) => {\n * try {\n * const webhook = receiver.receive(\n * request.headers,\n * request.rawBody\n * );\n *\n * // Process webhook...\n *\n * return { received: true };\n * } catch (error) {\n * reply.code(401);\n * return { error: error.message };\n * }\n * });\n * ```\n *\n * ## Important Notes\n *\n * - **Raw Body Required**: You MUST pass the raw request body string, not the parsed JSON object.\n * The signature is computed over the exact bytes received, so any formatting changes will\n * cause verification to fail.\n *\n * - **Case-Insensitive Headers**: Header names are case-insensitive in HTTP. This class handles\n * both `X-Sayna-Signature` and `x-sayna-signature` correctly.\n *\n * - **Secret Security**: Never commit secrets to version control. Use environment variables\n * or a secret management system.\n *\n * @see WebhookSIPOutput\n */\nexport class WebhookReceiver {\n private readonly secret: string;\n\n /**\n * Creates a new webhook receiver with the specified signing secret.\n *\n * @param secret - HMAC signing secret (min 16 chars, 32+ recommended).\n * If not provided, uses SAYNA_WEBHOOK_SECRET environment variable.\n *\n * @throws {SaynaValidationError} If secret is missing or too short\n *\n * @example\n * ```typescript\n * // Explicit secret\n * const receiver = new WebhookReceiver(\"my-secret-key-at-least-16-chars\");\n *\n * // From environment variable\n * const receiver = new WebhookReceiver();\n * ```\n */\n constructor(secret?: string) {\n const effectiveSecret = secret ?? process.env.SAYNA_WEBHOOK_SECRET;\n\n if (!effectiveSecret) {\n throw new SaynaValidationError(\n \"Webhook secret is required. Provide it as a constructor parameter or set SAYNA_WEBHOOK_SECRET environment variable.\"\n );\n }\n\n const trimmedSecret = effectiveSecret.trim();\n\n if (trimmedSecret.length < MIN_SECRET_LENGTH) {\n throw new SaynaValidationError(\n `Webhook secret must be at least ${MIN_SECRET_LENGTH} characters long. ` +\n `Received ${trimmedSecret.length} characters. ` +\n `Generate a secure secret with: openssl rand -hex 32`\n );\n }\n\n this.secret = trimmedSecret;\n }\n\n /**\n * Verifies and parses an incoming SIP webhook from Sayna.\n *\n * This method performs the following security checks:\n * 1. Validates presence of required headers\n * 2. Verifies timestamp is within acceptable window (prevents replay attacks)\n * 3. Computes HMAC-SHA256 signature over canonical string\n * 4. Performs constant-time comparison to prevent timing attacks\n * 5. Parses and validates the webhook payload structure\n *\n * @param headers - HTTP request headers (case-insensitive)\n * @param body - Raw request body as string (not parsed JSON)\n *\n * @returns Parsed and validated webhook payload\n *\n * @throws {SaynaValidationError} If signature verification fails or payload is invalid\n *\n * @example\n * ```typescript\n * const receiver = new WebhookReceiver(\"your-secret\");\n *\n * // Express example\n * app.post('/webhook', express.json({ verify: (req, res, buf) => {\n * req.rawBody = buf.toString();\n * }}), (req, res) => {\n * const webhook = receiver.receive(req.headers, req.rawBody);\n * // webhook is now a validated WebhookSIPOutput object\n * });\n * ```\n */\n receive(\n headers: Record<string, string | string[] | undefined>,\n body: string\n ): WebhookSIPOutput {\n // Normalize headers to lowercase for case-insensitive lookup\n const normalizedHeaders = this.normalizeHeaders(headers);\n\n // Extract required headers\n const signature = this.getRequiredHeader(\n normalizedHeaders,\n \"x-sayna-signature\"\n );\n const timestamp = this.getRequiredHeader(\n normalizedHeaders,\n \"x-sayna-timestamp\"\n );\n const eventId = this.getRequiredHeader(\n normalizedHeaders,\n \"x-sayna-event-id\"\n );\n\n // Parse and validate signature format\n if (!signature.startsWith(\"v1=\")) {\n throw new SaynaValidationError(\n \"Invalid signature format. Expected 'v1=<hex>' but got: \" +\n signature.substring(0, 10) +\n \"...\"\n );\n }\n const signatureHex = signature.substring(3);\n\n // Validate signature is valid hex (64 chars for SHA256)\n if (!/^[0-9a-f]{64}$/i.test(signatureHex)) {\n throw new SaynaValidationError(\n \"Invalid signature: must be 64 hex characters (HMAC-SHA256)\"\n );\n }\n\n // Validate and check timestamp\n this.validateTimestamp(timestamp);\n\n // Build canonical string for signature verification\n const canonical = `v1:${timestamp}:${eventId}:${body}`;\n\n // Compute expected signature\n const hmac = createHmac(\"sha256\", this.secret);\n hmac.update(canonical, \"utf8\");\n const expectedSignature = hmac.digest(\"hex\");\n\n // Constant-time comparison to prevent timing attacks\n if (!this.constantTimeEqual(signatureHex, expectedSignature)) {\n throw new SaynaValidationError(\n \"Signature verification failed. The webhook may have been tampered with or the secret is incorrect.\"\n );\n }\n\n // Parse and validate the webhook payload\n return this.parseAndValidatePayload(body);\n }\n\n /**\n * Normalizes HTTP headers to lowercase for case-insensitive access.\n * Handles both single string values and arrays of strings.\n *\n * @internal\n */\n private normalizeHeaders(\n headers: Record<string, string | string[] | undefined>\n ): Record<string, string> {\n const normalized: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n // Handle array values (take first element)\n const stringValue = Array.isArray(value) ? value[0] : value;\n if (stringValue) {\n normalized[key.toLowerCase()] = stringValue;\n }\n }\n }\n\n return normalized;\n }\n\n /**\n * Retrieves a required header value or throws a validation error.\n *\n * @internal\n */\n private getRequiredHeader(\n headers: Record<string, string>,\n name: string\n ): string {\n const value = headers[name.toLowerCase()];\n\n if (!value) {\n throw new SaynaValidationError(`Missing required header: ${name}`);\n }\n\n return value;\n }\n\n /**\n * Validates the timestamp is within the acceptable window.\n *\n * @internal\n */\n private validateTimestamp(timestampStr: string): void {\n // Parse timestamp\n const timestamp = Number(timestampStr);\n\n if (isNaN(timestamp)) {\n throw new SaynaValidationError(\n `Invalid timestamp format: expected Unix seconds but got '${timestampStr}'`\n );\n }\n\n // Check if timestamp is within acceptable range\n const now = Math.floor(Date.now() / 1000);\n const diff = Math.abs(now - timestamp);\n\n if (diff > TIMESTAMP_TOLERANCE_SECONDS) {\n throw new SaynaValidationError(\n `Timestamp outside replay protection window. ` +\n `Difference: ${diff} seconds (max allowed: ${TIMESTAMP_TOLERANCE_SECONDS}). ` +\n `This webhook may be a replay attack or there may be significant clock skew.`\n );\n }\n }\n\n /**\n * Performs constant-time string comparison to prevent timing attacks.\n *\n * @internal\n */\n private constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n const bufA = Buffer.from(a, \"utf8\");\n const bufB = Buffer.from(b, \"utf8\");\n\n return timingSafeEqual(bufA, bufB);\n }\n\n /**\n * Parses and validates the webhook payload structure.\n *\n * @internal\n */\n private parseAndValidatePayload(body: string): WebhookSIPOutput {\n let payload: unknown;\n\n // Parse JSON\n try {\n payload = JSON.parse(body);\n } catch (error) {\n throw new SaynaValidationError(\n `Invalid JSON payload: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n // Validate payload is an object\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new SaynaValidationError(\"Webhook payload must be a JSON object\");\n }\n\n const data = payload as Record<string, unknown>;\n\n // Validate required fields\n this.validateParticipant(data.participant);\n this.validateRoom(data.room);\n this.validateStringField(data, \"from_phone_number\", \"from_phone_number\");\n this.validateStringField(data, \"to_phone_number\", \"to_phone_number\");\n this.validateStringField(data, \"room_prefix\", \"room_prefix\");\n this.validateStringField(data, \"sip_host\", \"sip_host\");\n\n // TypeScript type assertion is safe here because we've validated all fields\n // We use double assertion through unknown to satisfy strict type checking\n return data as unknown as WebhookSIPOutput;\n }\n\n /**\n * Validates the participant object structure.\n *\n * @internal\n */\n private validateParticipant(participant: unknown): void {\n if (\n !participant ||\n typeof participant !== \"object\" ||\n Array.isArray(participant)\n ) {\n throw new SaynaValidationError(\n \"Webhook payload missing required field 'participant' (must be an object)\"\n );\n }\n\n const p = participant as Record<string, unknown>;\n\n this.validateStringField(p, \"identity\", \"participant.identity\");\n this.validateStringField(p, \"sid\", \"participant.sid\");\n\n // name is optional, but if present must be a string\n if (p.name !== undefined && typeof p.name !== \"string\") {\n throw new SaynaValidationError(\n \"Field 'participant.name' must be a string if present\"\n );\n }\n }\n\n /**\n * Validates the room object structure.\n *\n * @internal\n */\n private validateRoom(room: unknown): void {\n if (!room || typeof room !== \"object\" || Array.isArray(room)) {\n throw new SaynaValidationError(\n \"Webhook payload missing required field 'room' (must be an object)\"\n );\n }\n\n const r = room as Record<string, unknown>;\n\n this.validateStringField(r, \"name\", \"room.name\");\n this.validateStringField(r, \"sid\", \"room.sid\");\n }\n\n /**\n * Validates a required string field.\n *\n * @internal\n */\n private validateStringField(\n obj: Record<string, unknown>,\n field: string,\n displayName: string\n ): void {\n const value = obj[field];\n\n if (typeof value !== \"string\" || value.length === 0) {\n throw new SaynaValidationError(\n `Webhook payload missing required field '${displayName}' (must be a non-empty string)`\n );\n }\n }\n}\n",
8
8
  "import { SaynaClient } from \"./sayna-client\";\nimport type { STTConfig, TTSConfig, LiveKitConfig } from \"./types\";\n\nexport * from \"./sayna-client\";\nexport * from \"./types\";\nexport * from \"./errors\";\nexport * from \"./webhook-receiver\";\n\n/**\n * Creates and connects a new SaynaClient instance.\n *\n * This is the recommended way to create a Sayna client. It handles both\n * instantiation and connection, returning a ready-to-use client.\n *\n * @param url - The Sayna server URL (e.g., \"https://api.sayna.ai\")\n * @param sttConfig - Speech-to-text configuration (required when withoutAudio=false)\n * @param ttsConfig - Text-to-speech configuration (required when withoutAudio=false)\n * @param livekitConfig - Optional LiveKit room configuration\n * @param withoutAudio - If true, disables audio streaming (default: false)\n * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)\n *\n * @returns Promise that resolves to a connected SaynaClient\n *\n * @throws {SaynaValidationError} If parameters are invalid\n * @throws {SaynaConnectionError} If connection fails\n * @throws {SaynaServerError} If server returns an error during setup\n *\n * @example\n * ```typescript\n * import { saynaConnect } from \"@sayna/node-sdk\";\n *\n * const client = await saynaConnect(\n * \"https://api.sayna.ai\",\n * {\n * provider: \"deepgram\",\n * language: \"en-US\",\n * sample_rate: 16000,\n * channels: 1,\n * punctuation: true,\n * encoding: \"linear16\",\n * model: \"nova-2\"\n * },\n * {\n * provider: \"elevenlabs\",\n * voice_id: \"21m00Tcm4TlvDq8ikWAM\",\n * speaking_rate: 1.0,\n * audio_format: \"pcm\",\n * sample_rate: 16000,\n * connection_timeout: 5000,\n * request_timeout: 10000,\n * model: \"eleven_turbo_v2\",\n * pronunciations: []\n * }\n * );\n *\n * // Register event handlers\n * client.registerOnSttResult((result) => {\n * console.log(\"Transcription:\", result.transcript);\n * });\n *\n * // Send text to be spoken\n * await client.speak(\"Hello, world!\");\n *\n * // Clean up\n * await client.disconnect();\n * ```\n */\nexport async function saynaConnect(\n url: string,\n sttConfig?: STTConfig,\n ttsConfig?: TTSConfig,\n livekitConfig?: LiveKitConfig,\n withoutAudio: boolean = false,\n apiKey?: string\n): Promise<SaynaClient> {\n const client = new SaynaClient(\n url,\n sttConfig,\n ttsConfig,\n livekitConfig,\n withoutAudio,\n apiKey\n );\n await client.connect();\n return client;\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "5692EE242C83976364756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "15E631B45E4A4D4464756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -1,4 +1,4 @@
1
- import type { STTConfig, TTSConfig, LiveKitConfig, STTResultMessage, ErrorMessage, SipTransferErrorMessage, SaynaMessage, Participant, VoicesResponse, HealthResponse, LiveKitTokenResponse, SipHook, SipHooksResponse } from "./types";
1
+ import type { STTConfig, TTSConfig, LiveKitConfig, STTResultMessage, ErrorMessage, SipTransferErrorMessage, SaynaMessage, Participant, VoicesResponse, HealthResponse, LiveKitTokenResponse, LiveKitRoomsResponse, LiveKitRoomDetails, SipHook, SipHooksResponse, RemoveLiveKitParticipantResponse, MuteLiveKitParticipantResponse, SipTransferResponse } from "./types";
2
2
  /**
3
3
  * Event handler for speech-to-text results.
4
4
  */
@@ -116,7 +116,7 @@ export declare class SaynaClient {
116
116
  * @param responseType - Expected response type: "json" or "arrayBuffer"
117
117
  * @returns Promise resolving to the parsed response
118
118
  * @throws {SaynaConnectionError} If the network request fails
119
- * @throws {SaynaServerError} If the server returns an error response
119
+ * @throws {SaynaServerError} If the server returns an error response (includes status and endpoint)
120
120
  */
121
121
  private fetchFromSayna;
122
122
  /**
@@ -280,13 +280,17 @@ export declare class SaynaClient {
280
280
  /**
281
281
  * Issues a LiveKit access token for a participant.
282
282
  *
283
- * @param roomName - LiveKit room to join or create
283
+ * Room names are used as-is; the SDK does not rewrite or prefix them. When authentication
284
+ * is enabled, this endpoint creates the room if missing and sets room ownership metadata.
285
+ *
286
+ * @param roomName - LiveKit room to join or create. Provide the clean room name without any prefix.
284
287
  * @param participantName - Display name assigned to the participant
285
288
  * @param participantIdentity - Unique identifier for the participant
286
289
  * @returns Promise that resolves with the LiveKit token and connection details
287
290
  * @throws {SaynaValidationError} If any parameter is empty
288
291
  * @throws {SaynaConnectionError} If the request fails
289
- * @throws {SaynaServerError} If server returns an error
292
+ * @throws {SaynaServerError} If server returns an error. A 403 status indicates the room
293
+ * is owned by another tenant; do not retry with a modified room name.
290
294
  *
291
295
  * @example
292
296
  * ```typescript
@@ -300,6 +304,150 @@ export declare class SaynaClient {
300
304
  * ```
301
305
  */
302
306
  getLiveKitToken(roomName: string, participantName: string, participantIdentity: string): Promise<LiveKitTokenResponse>;
307
+ /**
308
+ * Lists LiveKit rooms accessible to the authenticated context.
309
+ *
310
+ * Room listings are scoped server-side based on authentication. When authentication is
311
+ * enabled, this endpoint may return fewer rooms than before (only those you have access to).
312
+ * Room names are not modified by the SDK.
313
+ *
314
+ * @returns Promise that resolves with the list of accessible rooms
315
+ * @throws {SaynaConnectionError} If the request fails
316
+ * @throws {SaynaServerError} If server returns an error (e.g., LiveKit not configured)
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * const response = await client.getLiveKitRooms();
321
+ * for (const room of response.rooms) {
322
+ * console.log(`Room: ${room.name}, Participants: ${room.num_participants}`);
323
+ * }
324
+ * ```
325
+ */
326
+ getLiveKitRooms(): Promise<LiveKitRoomsResponse>;
327
+ /**
328
+ * Retrieves detailed information about a specific LiveKit room including participants.
329
+ *
330
+ * Room names are used as-is; the SDK does not rewrite or prefix them. Access is
331
+ * enforced server-side based on room ownership metadata.
332
+ *
333
+ * @param roomName - Name of the room to retrieve
334
+ * @returns Promise that resolves with detailed room information including participants
335
+ * @throws {SaynaValidationError} If roomName is empty
336
+ * @throws {SaynaConnectionError} If the request fails
337
+ * @throws {SaynaServerError} If server returns an error. A 404 status can mean "not found"
338
+ * or "not accessible" when room ownership is enforced.
339
+ *
340
+ * @example
341
+ * ```typescript
342
+ * const room = await client.getLiveKitRoom("my-room");
343
+ * console.log(`Room: ${room.name}, SID: ${room.sid}`);
344
+ * console.log(`Participants: ${room.num_participants}/${room.max_participants}`);
345
+ * for (const participant of room.participants) {
346
+ * console.log(` - ${participant.name} (${participant.identity}): ${participant.state}`);
347
+ * }
348
+ * ```
349
+ */
350
+ getLiveKitRoom(roomName: string): Promise<LiveKitRoomDetails>;
351
+ /**
352
+ * Removes a participant from a LiveKit room, forcibly disconnecting them.
353
+ *
354
+ * Room names are used as-is; the SDK does not rewrite or prefix them. Access is
355
+ * enforced server-side based on room ownership metadata.
356
+ *
357
+ * **Important:** This does not invalidate the participant's token. To prevent
358
+ * rejoining, use short-lived tokens and avoid issuing new tokens to removed participants.
359
+ *
360
+ * @param roomName - Name of the room where the participant is connected
361
+ * @param participantIdentity - The identity of the participant to remove
362
+ * @returns Promise that resolves with the removal confirmation
363
+ * @throws {SaynaValidationError} If roomName or participantIdentity is empty
364
+ * @throws {SaynaConnectionError} If the request fails
365
+ * @throws {SaynaServerError} If server returns an error. A 404 status can mean "not found"
366
+ * or "not accessible" when room ownership is enforced.
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * const result = await client.removeLiveKitParticipant("my-room", "user-alice-456");
371
+ * console.log(`Status: ${result.status}`);
372
+ * console.log(`Removed from room: ${result.room_name}`);
373
+ * console.log(`Participant: ${result.participant_identity}`);
374
+ * ```
375
+ */
376
+ removeLiveKitParticipant(roomName: string, participantIdentity: string): Promise<RemoveLiveKitParticipantResponse>;
377
+ /**
378
+ * Mutes or unmutes a participant's published track in a LiveKit room.
379
+ *
380
+ * Room names are used as-is; the SDK does not rewrite or prefix them. Access is
381
+ * enforced server-side based on room ownership metadata.
382
+ *
383
+ * @param roomName - Name of the room where the participant is connected
384
+ * @param participantIdentity - The identity of the participant whose track to mute
385
+ * @param trackSid - The session ID of the track to mute/unmute
386
+ * @param muted - True to mute, false to unmute
387
+ * @returns Promise that resolves with the mute operation result
388
+ * @throws {SaynaValidationError} If roomName, participantIdentity, or trackSid is empty, or if muted is not a boolean
389
+ * @throws {SaynaConnectionError} If the request fails
390
+ * @throws {SaynaServerError} If server returns an error. A 404 status can mean "not found"
391
+ * or "not accessible" when room ownership is enforced.
392
+ *
393
+ * @example
394
+ * ```typescript
395
+ * // Mute a participant's track
396
+ * const result = await client.muteLiveKitParticipantTrack(
397
+ * "my-room",
398
+ * "user-alice-456",
399
+ * "TR_abc123",
400
+ * true
401
+ * );
402
+ * console.log(`Track ${result.track_sid} muted: ${result.muted}`);
403
+ *
404
+ * // Unmute the track
405
+ * const unmuteResult = await client.muteLiveKitParticipantTrack(
406
+ * "my-room",
407
+ * "user-alice-456",
408
+ * "TR_abc123",
409
+ * false
410
+ * );
411
+ * ```
412
+ */
413
+ muteLiveKitParticipantTrack(roomName: string, participantIdentity: string, trackSid: string, muted: boolean): Promise<MuteLiveKitParticipantResponse>;
414
+ /**
415
+ * Initiates a SIP call transfer via the REST API endpoint.
416
+ *
417
+ * This is distinct from the WebSocket `sipTransfer()` method. Use this REST endpoint
418
+ * when you need to transfer a SIP call from outside the active WebSocket session,
419
+ * or when you want to specify a particular room and participant explicitly.
420
+ *
421
+ * Room names are used as-is; the SDK does not rewrite or prefix them. Access is
422
+ * enforced server-side based on room ownership metadata.
423
+ *
424
+ * **Important Notes:**
425
+ * - Only SIP participants can be transferred
426
+ * - A successful response indicates the transfer has been **initiated**, not necessarily completed
427
+ * - The actual transfer may take several seconds
428
+ *
429
+ * @param roomName - Name of the room where the SIP participant is connected
430
+ * @param participantIdentity - The identity of the SIP participant to transfer
431
+ * @param transferTo - The phone number to transfer to (international, national, or extension format)
432
+ * @returns Promise that resolves with the transfer status
433
+ * @throws {SaynaValidationError} If roomName, participantIdentity, or transferTo is empty
434
+ * @throws {SaynaConnectionError} If the request fails
435
+ * @throws {SaynaServerError} If server returns an error. A 404 status can mean "not found"
436
+ * or "not accessible" when room ownership is enforced.
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * // Transfer a SIP participant to another phone number
441
+ * const result = await client.sipTransferRest(
442
+ * "call-room-123",
443
+ * "sip_participant_456",
444
+ * "+15551234567"
445
+ * );
446
+ * console.log(`Transfer status: ${result.status}`);
447
+ * console.log(`Transferred to: ${result.transfer_to}`);
448
+ * ```
449
+ */
450
+ sipTransferRest(roomName: string, participantIdentity: string, transferTo: string): Promise<SipTransferResponse>;
303
451
  /**
304
452
  * Downloads the recorded audio file for a completed session.
305
453
  *
@@ -351,8 +499,8 @@ export declare class SaynaClient {
351
499
  * @example
352
500
  * ```typescript
353
501
  * const response = await client.setSipHooks([
354
- * { host: "example.com", url: "https://webhook.example.com/events" },
355
- * { host: "another.com", url: "https://webhook.another.com/events" }
502
+ * { host: "example.com", url: "https://webhook.example.com/events", auth_id: "tenant-123" },
503
+ * { host: "another.com", url: "https://webhook.another.com/events", auth_id: "" } // Empty for unauthenticated mode
356
504
  * ]);
357
505
  * console.log("Total hooks configured:", response.hooks.length);
358
506
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"sayna-client.d.ts","sourceRoot":"","sources":["../src/sayna-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sayna-client.d.ts","sourceRoot":"","sources":["../src/sayna-client.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -343,6 +343,16 @@ export interface SipHook {
343
343
  host: string;
344
344
  /** HTTPS URL to forward webhook events to */
345
345
  url: string;
346
+ /**
347
+ * Tenant identifier for this hook. This value is written to LiveKit room metadata
348
+ * for inbound SIP calls routed through this hook.
349
+ *
350
+ * - When AUTH_REQUIRED=true on the server: `auth_id` must be non-empty.
351
+ * - When AUTH_REQUIRED=false: `auth_id` may be an empty string (unauthenticated mode).
352
+ *
353
+ * Treat this as an opaque string; pass it through unchanged without client-side interpretation.
354
+ */
355
+ auth_id: string;
346
356
  }
347
357
  /**
348
358
  * Response from the GET /sip/hooks and POST /sip/hooks endpoints.
@@ -365,4 +375,177 @@ export interface DeleteSipHooksRequest {
365
375
  /** List of host names to remove (case-insensitive) */
366
376
  hosts: string[];
367
377
  }
378
+ /**
379
+ * Summary information for a LiveKit room.
380
+ * Returned by the GET /livekit/rooms endpoint.
381
+ *
382
+ * Room listings are scoped to the authenticated context; this endpoint may return
383
+ * fewer rooms than before when authentication is enabled.
384
+ */
385
+ export interface LiveKitRoomSummary {
386
+ /** The room name. Room names are not rewritten by the SDK; scoping is enforced server-side. */
387
+ name: string;
388
+ /** Number of current participants in the room */
389
+ num_participants: number;
390
+ /** Room creation time (Unix timestamp in seconds) */
391
+ creation_time: number;
392
+ }
393
+ /**
394
+ * Response from the GET /livekit/rooms endpoint.
395
+ *
396
+ * Room listings are scoped to the authenticated context. When authentication is enabled,
397
+ * this endpoint returns only rooms accessible to the current credentials.
398
+ */
399
+ export interface LiveKitRoomsResponse {
400
+ /** List of rooms accessible to the authenticated client */
401
+ rooms: LiveKitRoomSummary[];
402
+ }
403
+ /**
404
+ * Participant connection state in a LiveKit room.
405
+ */
406
+ export type LiveKitParticipantState = "JOINING" | "JOINED" | "ACTIVE" | "DISCONNECTED" | "UNKNOWN";
407
+ /**
408
+ * Type/kind of participant in a LiveKit room.
409
+ */
410
+ export type LiveKitParticipantKind = "STANDARD" | "AGENT" | "SIP" | "EGRESS" | "INGRESS" | "UNKNOWN";
411
+ /**
412
+ * Detailed information about a participant in a LiveKit room.
413
+ * Returned as part of the GET /livekit/rooms/{room_name} response.
414
+ */
415
+ export interface LiveKitParticipantInfo {
416
+ /** Unique session ID for this participant (generated by LiveKit) */
417
+ sid: string;
418
+ /** Unique identifier provided when connecting */
419
+ identity: string;
420
+ /** Display name of the participant */
421
+ name: string;
422
+ /** Current connection state of the participant */
423
+ state: LiveKitParticipantState;
424
+ /** Type/kind of the participant (e.g., STANDARD, AGENT, SIP) */
425
+ kind: LiveKitParticipantKind;
426
+ /** Timestamp when participant joined (Unix timestamp in seconds) */
427
+ joined_at: number;
428
+ /** User-specified metadata for the participant */
429
+ metadata: string;
430
+ /** User-specified attributes for the participant */
431
+ attributes: Record<string, string>;
432
+ /** Whether the participant is currently publishing audio/video */
433
+ is_publisher: boolean;
434
+ }
435
+ /**
436
+ * Detailed information about a LiveKit room including participants.
437
+ * Returned by the GET /livekit/rooms/{room_name} endpoint.
438
+ *
439
+ * A 404 response can mean "not found" or "not accessible" when room ownership
440
+ * is enforced server-side.
441
+ */
442
+ export interface LiveKitRoomDetails {
443
+ /** Unique session ID for the room (generated by LiveKit) */
444
+ sid: string;
445
+ /** The room name. Room names are not rewritten by the SDK; scoping is enforced server-side. */
446
+ name: string;
447
+ /** Number of current participants in the room */
448
+ num_participants: number;
449
+ /** Maximum allowed participants (0 = no limit) */
450
+ max_participants: number;
451
+ /** Room creation time (Unix timestamp in seconds) */
452
+ creation_time: number;
453
+ /**
454
+ * Room metadata as an opaque JSON string. Do not parse or rely on this for access decisions;
455
+ * access is enforced server-side.
456
+ */
457
+ metadata: string;
458
+ /** Whether a recording is currently active */
459
+ active_recording: boolean;
460
+ /** List of participants currently in the room */
461
+ participants: LiveKitParticipantInfo[];
462
+ }
463
+ /**
464
+ * Request body for the DELETE /livekit/participant endpoint.
465
+ * Used to remove a participant from a LiveKit room.
466
+ *
467
+ * A 404 response can mean "not found" or "not accessible" when room ownership is enforced.
468
+ */
469
+ export interface RemoveLiveKitParticipantRequest {
470
+ /** The LiveKit room name where the participant is connected. Room names are clean; the SDK does not rewrite them. */
471
+ room_name: string;
472
+ /** The identity of the participant to remove */
473
+ participant_identity: string;
474
+ }
475
+ /**
476
+ * Response from the DELETE /livekit/participant endpoint.
477
+ * Confirms the participant removal operation.
478
+ */
479
+ export interface RemoveLiveKitParticipantResponse {
480
+ /** Status of the removal operation (e.g., "removed") */
481
+ status: string;
482
+ /** The room name where the participant was removed */
483
+ room_name: string;
484
+ /** The identity of the removed participant */
485
+ participant_identity: string;
486
+ }
487
+ /**
488
+ * Request body for the POST /livekit/participant/mute endpoint.
489
+ * Used to mute or unmute a participant's track in a LiveKit room.
490
+ *
491
+ * A 404 response can mean "not found" or "not accessible" when room ownership is enforced.
492
+ */
493
+ export interface MuteLiveKitParticipantRequest {
494
+ /** The LiveKit room name where the participant is connected. Room names are clean; the SDK does not rewrite them. */
495
+ room_name: string;
496
+ /** The identity of the participant whose track to mute */
497
+ participant_identity: string;
498
+ /** The session ID of the track to mute/unmute */
499
+ track_sid: string;
500
+ /** True to mute, false to unmute */
501
+ muted: boolean;
502
+ }
503
+ /**
504
+ * Response from the POST /livekit/participant/mute endpoint.
505
+ * Confirms the mute/unmute operation.
506
+ */
507
+ export interface MuteLiveKitParticipantResponse {
508
+ /** The room name where the operation was performed */
509
+ room_name: string;
510
+ /** The identity of the participant */
511
+ participant_identity: string;
512
+ /** The session ID of the track */
513
+ track_sid: string;
514
+ /** Current muted state after the operation */
515
+ muted: boolean;
516
+ }
517
+ /**
518
+ * Status of a SIP transfer operation.
519
+ * - "initiated": Transfer has been started but not yet confirmed complete
520
+ * - "completed": Transfer has been successfully completed
521
+ */
522
+ export type SipTransferStatus = "initiated" | "completed";
523
+ /**
524
+ * Request body for the POST /sip/transfer endpoint.
525
+ * Used to initiate a SIP call transfer for a participant in a LiveKit room.
526
+ *
527
+ * A 404 response can mean "not found" or "not accessible" when room ownership is enforced.
528
+ */
529
+ export interface SipTransferRequest {
530
+ /** The LiveKit room name where the SIP participant is connected. Room names are clean; the SDK does not rewrite them. */
531
+ room_name: string;
532
+ /** The identity of the SIP participant to transfer */
533
+ participant_identity: string;
534
+ /** The phone number to transfer the call to (international, national, or extension format) */
535
+ transfer_to: string;
536
+ }
537
+ /**
538
+ * Response from the POST /sip/transfer endpoint.
539
+ * Confirms the SIP transfer operation status.
540
+ */
541
+ export interface SipTransferResponse {
542
+ /** Status of the transfer request ("initiated" or "completed") */
543
+ status: SipTransferStatus;
544
+ /** The room name where the transfer was initiated */
545
+ room_name: string;
546
+ /** The identity of the participant being transferred */
547
+ participant_identity: string;
548
+ /** The normalized phone number with tel: prefix */
549
+ transfer_to: string;
550
+ }
368
551
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayna-ai/node-sdk",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Node.js SDK for Sayna.ai server-side WebSocket connections",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",