@telnyx/webrtc 2.27.4-beta.0 → 2.27.4-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,9 +25,6 @@ export default abstract class BrowserSession extends BaseSession {
25
25
  private _previousAudioConstraints;
26
26
  protected _videoConstraints: boolean | MediaTrackConstraints;
27
27
  protected _speaker: string;
28
- private _onlineHandler;
29
- private _offlineHandler;
30
- private _wasOffline;
31
28
  constructor(options: IVertoOptions);
32
29
  getIsRegistered(): Promise<boolean>;
33
30
  connect(): Promise<void>;
@@ -62,7 +59,5 @@ export default abstract class BrowserSession extends BaseSession {
62
59
  vertoBroadcast({ nodeId, channel: eventChannel, data, }: BroadcastParams): void;
63
60
  vertoSubscribe({ nodeId, channels: eventChannel, handler, }: SubscribeParams): Promise<any>;
64
61
  vertoUnsubscribe({ nodeId, channels: eventChannel, }: SubscribeParams): Promise<any>;
65
- private _setupNetworkListeners;
66
- private _cleanupNetworkListeners;
67
62
  static telnyxStateCall(call: Call): Call;
68
63
  }
@@ -6,6 +6,9 @@ export default class SignalingHealthMonitor {
6
6
  private _probeInFlight;
7
7
  private _intervalId;
8
8
  private _pendingMediaRecovery;
9
+ private _browserWasOffline;
10
+ private _onlineHandler;
11
+ private _offlineHandler;
9
12
  private static readonly CRITICAL_METHODS;
10
13
  static isCriticalMethod(method: string): boolean;
11
14
  constructor(_session: ISignalingHealthSession);
@@ -19,6 +22,8 @@ export default class SignalingHealthMonitor {
19
22
  onRequestTimeout(requestId: string, timeoutMs: number, method?: string): void;
20
23
  onPeerFailure(callId: string, evidence: PeerFailureEvidence): void;
21
24
  onNoRtp(callId: string, direction: 'inbound' | 'outbound'): void;
25
+ private _setupBrowserListeners;
26
+ private _cleanupBrowserListeners;
22
27
  onIceRestartFailed(callId: string): void;
23
28
  private _check;
24
29
  private _sendProbe;
@@ -33,6 +33,8 @@ export declare const TELNYX_WARNING_CODES: {
33
33
  readonly LOW_INBOUND_AUDIO: 31006;
34
34
  readonly LOW_BYTES_RECEIVED: 32001;
35
35
  readonly LOW_BYTES_SENT: 32002;
36
+ readonly RECORDING_UNAVAILABLE: 32003;
37
+ readonly RECORDING_BUFFER_OVERFLOW: 32004;
36
38
  readonly ICE_CONNECTIVITY_LOST: 33001;
37
39
  readonly ICE_GATHERING_TIMEOUT: 33002;
38
40
  readonly ICE_GATHERING_EMPTY: 33003;
@@ -50,5 +52,5 @@ export declare const TELNYX_WARNING_CODES: {
50
52
  readonly RECONNECTION_FAILED_WITH_NO_AUTO_RECONNECT: 36005;
51
53
  };
52
54
  export declare const SDP_CREATE_OFFER_FAILED: 40001, SDP_CREATE_ANSWER_FAILED: 40002, SDP_SET_LOCAL_DESCRIPTION_FAILED: 40003, SDP_SET_REMOTE_DESCRIPTION_FAILED: 40004, SDP_SEND_FAILED: 40005, MEDIA_MICROPHONE_PERMISSION_DENIED: 42001, MEDIA_DEVICE_NOT_FOUND: 42002, MEDIA_GET_USER_MEDIA_FAILED: 42003, HOLD_FAILED: 44001, INVALID_CALL_PARAMETERS: 44002, BYE_SEND_FAILED: 44003, SUBSCRIBE_FAILED: 44004, PEER_CLOSED_DURING_INIT: 44005, WEBSOCKET_CONNECTION_FAILED: 45001, WEBSOCKET_ERROR: 45002, RECONNECTION_EXHAUSTED: 45003, GATEWAY_FAILED: 45004, LOGIN_FAILED: 46001, INVALID_CREDENTIALS: 46002, AUTHENTICATION_REQUIRED: 46003, ICE_RESTART_FAILED: 47001, NETWORK_OFFLINE: 48001, SESSION_NOT_REATTACHED: 48501, UNEXPECTED_ERROR: 49001;
53
- export declare const HIGH_RTT: 31001, HIGH_JITTER: 31002, HIGH_PACKET_LOSS: 31003, LOW_MOS: 31004, LOW_LOCAL_AUDIO: 31005, LOW_INBOUND_AUDIO: 31006, LOW_BYTES_RECEIVED: 32001, LOW_BYTES_SENT: 32002, ICE_CONNECTIVITY_LOST: 33001, ICE_GATHERING_TIMEOUT: 33002, ICE_GATHERING_EMPTY: 33003, PEER_CONNECTION_FAILED: 33004, ONLY_HOST_ICE_CANDIDATES: 33005, ANSWER_WHILE_PEER_ACTIVE: 33006, ICE_CANDIDATE_PAIR_CHANGED: 33008, AUDIO_INPUT_DEVICE_CHANGE_SKIPPED: 33009, MULTIPLE_ACTIVE_CALLS_DETECTED: 33010, DUPLICATE_INBOUND_ANSWER: 33007, TOKEN_EXPIRING_SOON: 34001, UNKNOWN_REATTACHED_SESSION: 35002, SIGNALING_RECOVERY_REQUIRED: 36003, MEDIA_RECOVERY_REQUIRED: 36004, RECONNECTION_FAILED_WITH_NO_AUTO_RECONNECT: 36005;
55
+ export declare const HIGH_RTT: 31001, HIGH_JITTER: 31002, HIGH_PACKET_LOSS: 31003, LOW_MOS: 31004, LOW_LOCAL_AUDIO: 31005, LOW_INBOUND_AUDIO: 31006, LOW_BYTES_RECEIVED: 32001, LOW_BYTES_SENT: 32002, RECORDING_UNAVAILABLE: 32003, RECORDING_BUFFER_OVERFLOW: 32004, ICE_CONNECTIVITY_LOST: 33001, ICE_GATHERING_TIMEOUT: 33002, ICE_GATHERING_EMPTY: 33003, PEER_CONNECTION_FAILED: 33004, ONLY_HOST_ICE_CANDIDATES: 33005, ANSWER_WHILE_PEER_ACTIVE: 33006, ICE_CANDIDATE_PAIR_CHANGED: 33008, AUDIO_INPUT_DEVICE_CHANGE_SKIPPED: 33009, MULTIPLE_ACTIVE_CALLS_DETECTED: 33010, DUPLICATE_INBOUND_ANSWER: 33007, TOKEN_EXPIRING_SOON: 34001, UNKNOWN_REATTACHED_SESSION: 35002, SIGNALING_RECOVERY_REQUIRED: 36003, MEDIA_RECOVERY_REQUIRED: 36004, RECONNECTION_FAILED_WITH_NO_AUTO_RECONNECT: 36005;
54
56
  export declare const HAS_NON_HOST_ICE_CANDIDATE_REGEX: RegExp;
@@ -68,6 +68,20 @@ export declare const SDK_WARNINGS: {
68
68
  readonly causes: readonly ["Microphone muted or disconnected", "Network interruption", "Local media track ended"];
69
69
  readonly solutions: readonly ["Check that the microphone is not muted", "Verify the microphone is still connected", "Check network connectivity"];
70
70
  };
71
+ readonly 32003: {
72
+ readonly name: "RECORDING_UNAVAILABLE";
73
+ readonly message: "Call recording is not available in this browser";
74
+ readonly description: "Call recording was enabled (enableCallRecording is true), but the browser does not support MediaStreamTrackProcessor (required to capture raw audio PCM). Recording is disabled for this call; the call proceeds normally. Currently affects Firefox and older Chromium (< 94).";
75
+ readonly causes: readonly ["Browser does not implement MediaStreamTrackProcessor (Firefox, Safari)", "Chromium version older than 94", "Recording enabled on an unsupported platform"];
76
+ readonly solutions: readonly ["Use a recent Chromium-based browser (Chrome 94+, Edge 94+) to capture recordings", "Disable enableCallRecording if recording is not required for this deployment", "See the enableCallRecording JSDoc for platform support details"];
77
+ };
78
+ readonly 32004: {
79
+ readonly name: "RECORDING_BUFFER_OVERFLOW";
80
+ readonly message: "Call recording buffer overflow — oldest packets dropped";
81
+ readonly description: "The in-memory call recording buffer reached its maxBufferBytes cap before the next scheduled flush. The oldest captured RTP packets were dropped to keep memory bounded. This indicates the flush interval is too long for the call audio rate, or the upload endpoint is slow/unreachable. The recording continues with the remaining (newer) packets.";
82
+ readonly causes: readonly ["Flush interval (callRecordingFlushIntervalMs) is larger than the buffer can hold at the current audio rate", "Upload endpoint (/call_recording) is slow or unreachable, so intermediate flushes back up", "A very long call with high sample rate filling the buffer between flushes"];
83
+ readonly solutions: readonly ["Reduce callRecordingFlushIntervalMs so flushes happen more frequently", "Increase callRecordingMaxBufferBytes if memory headroom allows", "Check the /call_recording endpoint health and network path to voice-sdk-proxy"];
84
+ };
71
85
  readonly 33001: {
72
86
  readonly name: "ICE_CONNECTIVITY_LOST";
73
87
  readonly message: "Connection interrupted";
@@ -38,6 +38,12 @@ export interface IVertoOptions {
38
38
  enableCallReports?: boolean;
39
39
  callReportInterval?: number;
40
40
  callReportFlushInterval?: number;
41
+ enableCallRecording?: boolean;
42
+ callRecordingFlushIntervalMs?: number;
43
+ callRecordingMaxBufferBytes?: number;
44
+ callRecordingSampleRate?: number;
45
+ callRecordingTracks?: Array<'local' | 'remote'>;
46
+ callRecordingEndpoint?: string;
41
47
  debugLogLevel?: 'debug' | 'info' | 'warn' | 'error';
42
48
  debugLogMaxEntries?: number;
43
49
  skipLastVoiceSdkId?: boolean;
@@ -26,8 +26,6 @@ export declare type IAIConversationMessageEvent = {
26
26
  params: AIConversationParams;
27
27
  voice_sdk_id?: string;
28
28
  };
29
- export declare type ISendAIConversationMessageOptions = {
30
- item: FunctionCallOutputItem;
31
- };
29
+ export declare type ISendAIConversationMessageOptions = FunctionCallOutputItem;
32
30
  export declare function isFunctionCallParams(params: AIConversationParams): params is AIConversationFunctionCallParams;
33
31
  export declare function isFunctionCallOutputParams(params: AIConversationParams): params is AIConversationFunctionCallOutputParams;
@@ -8,6 +8,7 @@ export default abstract class BaseCall implements IWebRTCCall {
8
8
  protected session: BrowserSession;
9
9
  private _webRTCStats;
10
10
  private _callReportCollector;
11
+ private _callRecorder;
11
12
  private _mediaDeviceCollector;
12
13
  id: string;
13
14
  recoveredCallId: string;
@@ -125,9 +126,6 @@ export default abstract class BaseCall implements IWebRTCCall {
125
126
  private _dispatchNotification;
126
127
  private _execute;
127
128
  private _registerInboundAnswerAttempt;
128
- private _getSessionInboundAnswerCalls;
129
- private _isBlockingInboundAnswer;
130
- private _hasUsablePeerConnection;
131
129
  private _init;
132
130
  protected _finalize(): void;
133
131
  private _getCallReportVoiceSdkId;
@@ -0,0 +1,96 @@
1
+ import { type ITelnyxWarning } from '../util/constants/warnings';
2
+ export interface IRecordingPacket {
3
+ rtpSeq: number;
4
+ rtpTs: number;
5
+ rtpSsrc: number;
6
+ capturedAt: string;
7
+ payloadBytes: Uint8Array;
8
+ }
9
+ export declare type RecordingTrackKind = 'local' | 'remote';
10
+ export interface ICallRecordingOptions {
11
+ enabled: boolean;
12
+ flushIntervalMs?: number;
13
+ maxBufferBytes?: number;
14
+ sampleRate?: number;
15
+ tracks?: RecordingTrackKind[];
16
+ endpoint?: string;
17
+ fetchImpl?: typeof fetch;
18
+ }
19
+ export interface ICallRecordingContext {
20
+ callId: string;
21
+ sessionId?: string;
22
+ voiceSdkId?: string;
23
+ callReportId: string;
24
+ host?: string;
25
+ recordingId?: string;
26
+ }
27
+ export interface ICallRecordingEnvelope {
28
+ call_report_id: string;
29
+ call_id: string;
30
+ voice_sdk_id?: string;
31
+ recording_id: string;
32
+ segment: 'intermediate' | 'final';
33
+ track: RecordingTrackKind;
34
+ codec: 'pcm-f32-le';
35
+ sample_rate: number;
36
+ channels: 1;
37
+ started_at: string;
38
+ ended_at: string;
39
+ packet_count: number;
40
+ byte_count: number;
41
+ packets: Array<{
42
+ rtp_seq: number;
43
+ rtp_ts: number;
44
+ rtp_ssrc: number;
45
+ captured_at: string;
46
+ payload_b64: string;
47
+ }>;
48
+ }
49
+ export declare class CallRecorder {
50
+ private options;
51
+ private callContext;
52
+ private recordingId;
53
+ private _buffers;
54
+ private _bufferBytes;
55
+ private _trackStates;
56
+ private _processors;
57
+ private _readerCancellers;
58
+ private _flushTimerId;
59
+ private _flushing;
60
+ private _stopped;
61
+ private _started;
62
+ private _startedAt;
63
+ private _endedAt;
64
+ private _overflowWarnedThisWindow;
65
+ onWarning: ((warning: ITelnyxWarning) => void) | null;
66
+ private static readonly RETRY_DELAYS_MS;
67
+ private static readonly KEEPALIVE_BODY_LIMIT_BYTES;
68
+ private static readonly PACKET_OVERHEAD_BYTES;
69
+ constructor(options: ICallRecordingOptions, callContext: ICallRecordingContext);
70
+ start(localTrack?: MediaStreamTrack, remoteTrack?: MediaStreamTrack): void;
71
+ stop(): void;
72
+ postFinalReport(): Promise<void>;
73
+ cleanup(): void;
74
+ private _attach;
75
+ private _onFrame;
76
+ private _pushPacket;
77
+ private _flushIntervalMs;
78
+ private _maxBufferBytes;
79
+ private _sampleRate;
80
+ private _scheduleFlush;
81
+ private _clearFlushTimer;
82
+ private _cancelReaders;
83
+ private _periodicFlush;
84
+ private _drain;
85
+ private _resolveEndpointFromContext;
86
+ private _host;
87
+ _setHost(host: string): void;
88
+ private _callReportId;
89
+ _setCallReportId(callReportId: string): void;
90
+ private _resolveCallReportId;
91
+ private _resolveEndpoint;
92
+ private _buildEnvelope;
93
+ private _postRecording;
94
+ private _emitWarning;
95
+ private _toBase64;
96
+ }
@@ -236,7 +236,7 @@ export declare type SanitizedClientOption = string | number | boolean | null | S
236
236
  [key: string]: SanitizedClientOption;
237
237
  };
238
238
  export interface ICallReportFlushReason {
239
- type: 'buffer-limit' | 'manual' | 'socket-close' | 'socket-error';
239
+ type: 'buffer-limit' | 'manual' | 'socket-close' | 'socket-error' | 'page-unload';
240
240
  socketClose?: {
241
241
  code?: number;
242
242
  codeName?: string;
@@ -304,7 +304,7 @@ export declare class CallReportCollector {
304
304
  stop(): Promise<void>;
305
305
  flush(summary: ICallSummary, flushReason?: ICallReportFlushReason): ICallReportPayload | null;
306
306
  postReport(summary: ICallSummary, callReportId: string, host: string, voiceSdkId?: string): Promise<void>;
307
- sendPayload(payload: ICallReportPayload, callReportId: string, host: string, voiceSdkId?: string): Promise<void>;
307
+ sendPayload(payload: ICallReportPayload, callReportId: string, host: string, voiceSdkId?: string, forceKeepalive?: boolean): Promise<void>;
308
308
  private _sendPayload;
309
309
  getStatsBuffer(): IStatsInterval[];
310
310
  shouldForceRelayCandidateForRecovery(): boolean;
@@ -43,6 +43,9 @@ export declare const NOTIFICATION_TYPE: {
43
43
  export declare const ERROR_TYPE: {
44
44
  invalidCredentialsOptions: string;
45
45
  };
46
+ export declare const DEFAULT_CALL_RECORDING_FLUSH_INTERVAL_MS = 240000;
47
+ export declare const DEFAULT_CALL_RECORDING_MAX_BUFFER_BYTES = 8000000;
48
+ export declare const DEFAULT_CALL_RECORDING_SAMPLE_RATE = 48000;
46
49
  export declare const DEFAULT_CALL_OPTIONS: IVertoCallOptions;
47
50
  export declare enum State {
48
51
  New = 0,
@@ -41,6 +41,12 @@ export interface IClientOptions {
41
41
  enableCallReports?: boolean;
42
42
  callReportInterval?: number;
43
43
  callReportFlushInterval?: number;
44
+ enableCallRecording?: boolean;
45
+ callRecordingFlushIntervalMs?: number;
46
+ callRecordingMaxBufferBytes?: number;
47
+ callRecordingSampleRate?: number;
48
+ callRecordingTracks?: Array<'local' | 'remote'>;
49
+ callRecordingEndpoint?: string;
44
50
  mediaPermissionsRecovery?: {
45
51
  enabled: boolean;
46
52
  timeout: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telnyx/webrtc",
3
- "version": "2.27.4-beta.0",
3
+ "version": "2.27.4-beta.2",
4
4
  "description": "Telnyx WebRTC Client",
5
5
  "keywords": [
6
6
  "telnyx",