@anganyai/voice-sdk 0.0.2 → 0.0.4
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/dist/index.cjs +309 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -6
- package/dist/index.d.ts +75 -6
- package/dist/index.js +309 -32
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
package/dist/index.cjs
CHANGED
|
@@ -3461,8 +3461,18 @@ var AuthManager = class extends EventEmitter {
|
|
|
3461
3461
|
}
|
|
3462
3462
|
/**
|
|
3463
3463
|
* Get ephemeral credentials for WebRTC connections
|
|
3464
|
+
*
|
|
3465
|
+
* Returns cached credentials if available and valid, otherwise fetches new ones.
|
|
3466
|
+
* This supports both:
|
|
3467
|
+
* - Externally-provided credentials (via setEphemeralCredentials)
|
|
3468
|
+
* - SDK-managed credentials (fetched using access token)
|
|
3464
3469
|
*/
|
|
3465
3470
|
async getEphemeralCredentials() {
|
|
3471
|
+
const cached = this.credentialManager.getCachedEphemeralCredentials();
|
|
3472
|
+
if (cached) {
|
|
3473
|
+
this.logger.debug("Using cached ephemeral credentials");
|
|
3474
|
+
return cached;
|
|
3475
|
+
}
|
|
3466
3476
|
const accessToken = await this.getAccessToken();
|
|
3467
3477
|
if (!accessToken) {
|
|
3468
3478
|
throw new AuthenticationError("No access token available for ephemeral credentials");
|
|
@@ -16601,7 +16611,9 @@ var SipManager = class extends EventEmitter {
|
|
|
16601
16611
|
this.remoteStream = event.streams[0];
|
|
16602
16612
|
this.logger.info("Remote audio stream received via ontrack fallback");
|
|
16603
16613
|
if (isReactNative()) {
|
|
16604
|
-
this.logger.info(
|
|
16614
|
+
this.logger.info(
|
|
16615
|
+
"React Native: Remote audio from ontrack will be played automatically"
|
|
16616
|
+
);
|
|
16605
16617
|
} else {
|
|
16606
16618
|
this.createAudioElement();
|
|
16607
16619
|
if (this.audioElement && this.remoteStream) {
|
|
@@ -16646,7 +16658,9 @@ var SipManager = class extends EventEmitter {
|
|
|
16646
16658
|
*/
|
|
16647
16659
|
createAudioElement() {
|
|
16648
16660
|
if (isReactNative()) {
|
|
16649
|
-
this.logger.debug(
|
|
16661
|
+
this.logger.debug(
|
|
16662
|
+
"React Native detected - skipping audio element creation (handled by WebRTC)"
|
|
16663
|
+
);
|
|
16650
16664
|
return;
|
|
16651
16665
|
}
|
|
16652
16666
|
if (!isBrowser()) {
|
|
@@ -16923,7 +16937,7 @@ var TranscriptionService = class extends EventEmitter {
|
|
|
16923
16937
|
* Connect to SSE endpoint with token refresh support
|
|
16924
16938
|
*/
|
|
16925
16939
|
async connectToSSE(accessToken, isRetry = false) {
|
|
16926
|
-
const sseUrl = `${this.apiUrl}/api/v1/events?event_types=transcription`;
|
|
16940
|
+
const sseUrl = `${this.apiUrl}/api/v1/events?event_types=transcription,call_event`;
|
|
16927
16941
|
this.logger.debug("Connecting to SSE endpoint", { url: sseUrl, isRetry });
|
|
16928
16942
|
const response = await fetch(sseUrl, {
|
|
16929
16943
|
method: "GET",
|
|
@@ -17053,13 +17067,36 @@ var TranscriptionService = class extends EventEmitter {
|
|
|
17053
17067
|
}
|
|
17054
17068
|
}
|
|
17055
17069
|
handleMessage(data) {
|
|
17056
|
-
this.logger.debug("Handling SSE message", { data });
|
|
17057
|
-
if (data.
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17070
|
+
this.logger.debug("Handling SSE message", { type: data.type, data });
|
|
17071
|
+
if (data.call_id && !this.currentCallId) {
|
|
17072
|
+
this.currentCallId = data.call_id;
|
|
17073
|
+
this.emit("callId", data.call_id);
|
|
17074
|
+
this.logger.info("Captured call ID", { callId: data.call_id, messageType: data.type });
|
|
17075
|
+
}
|
|
17076
|
+
if (data.type === "connection" || data.type === "welcome" || data.type === "connected") {
|
|
17077
|
+
this.logger.info("Received connection event", {
|
|
17078
|
+
type: data.type,
|
|
17079
|
+
status: data.status
|
|
17080
|
+
});
|
|
17081
|
+
if (data.type === "connected" || data.status === "connected") {
|
|
17082
|
+
this.emit("connected");
|
|
17062
17083
|
}
|
|
17084
|
+
} else if (data.type === "call_started") {
|
|
17085
|
+
this.logger.info("Received call_started event", {
|
|
17086
|
+
callId: data.call_id,
|
|
17087
|
+
organizationId: data.organization_id
|
|
17088
|
+
});
|
|
17089
|
+
if (data.call_id) {
|
|
17090
|
+
this.emit("callStarted", data.call_id);
|
|
17091
|
+
}
|
|
17092
|
+
} else if (data.type === "call_ended") {
|
|
17093
|
+
this.logger.info("Received call_ended event", {
|
|
17094
|
+
callId: data.call_id
|
|
17095
|
+
});
|
|
17096
|
+
if (data.call_id) {
|
|
17097
|
+
this.emit("callEnded", data.call_id);
|
|
17098
|
+
}
|
|
17099
|
+
} else if (data.type === "transcription" && data.text) {
|
|
17063
17100
|
const event = {
|
|
17064
17101
|
speaker: data.direction === "agent" ? "agent" : "user",
|
|
17065
17102
|
text: data.text.trim(),
|
|
@@ -17067,6 +17104,9 @@ var TranscriptionService = class extends EventEmitter {
|
|
|
17067
17104
|
isFinal: true,
|
|
17068
17105
|
// Assume final for now
|
|
17069
17106
|
callId: data.call_id,
|
|
17107
|
+
humanTurnId: data.human_turn_id ?? void 0,
|
|
17108
|
+
agentTurnId: data.agent_turn_id ?? void 0,
|
|
17109
|
+
speakerId: data.speaker_id ?? void 0,
|
|
17070
17110
|
metadata: data
|
|
17071
17111
|
};
|
|
17072
17112
|
this.logger.debug("Emitting transcription event", { event });
|
|
@@ -17138,7 +17178,8 @@ var ApiService = class {
|
|
|
17138
17178
|
this.logger.debug("Sending voice text", {
|
|
17139
17179
|
callId: options.callId,
|
|
17140
17180
|
textLength: options.text.length,
|
|
17141
|
-
|
|
17181
|
+
interruptsConversation: options.interruptsConversation,
|
|
17182
|
+
queueWhenSpeaking: options.queueWhenSpeaking,
|
|
17142
17183
|
muteAgent: options.muteAgent
|
|
17143
17184
|
});
|
|
17144
17185
|
try {
|
|
@@ -17152,10 +17193,10 @@ var ApiService = class {
|
|
|
17152
17193
|
},
|
|
17153
17194
|
body: JSON.stringify({
|
|
17154
17195
|
text: options.text,
|
|
17155
|
-
interrupts_conversation: options.
|
|
17156
|
-
|
|
17157
|
-
|
|
17158
|
-
|
|
17196
|
+
interrupts_conversation: options.interruptsConversation ?? true,
|
|
17197
|
+
queue_when_speaking: options.queueWhenSpeaking ?? false,
|
|
17198
|
+
mute_agent: options.muteAgent ?? null,
|
|
17199
|
+
voice_settings: options.voiceSettings ?? null
|
|
17159
17200
|
})
|
|
17160
17201
|
}
|
|
17161
17202
|
);
|
|
@@ -17241,11 +17282,103 @@ var ApiService = class {
|
|
|
17241
17282
|
throw new NetworkError("Failed to set agent mute status", { cause: error });
|
|
17242
17283
|
}
|
|
17243
17284
|
}
|
|
17285
|
+
/**
|
|
17286
|
+
* Mute a call (POST /calls/{call_id}/mute)
|
|
17287
|
+
*/
|
|
17288
|
+
async muteCall(callId, accessToken) {
|
|
17289
|
+
this.logger.debug("Muting call", { callId });
|
|
17290
|
+
try {
|
|
17291
|
+
const response = await fetch(`${this.apiUrl}/api/v1/conversations/calls/${callId}/mute`, {
|
|
17292
|
+
method: "POST",
|
|
17293
|
+
headers: {
|
|
17294
|
+
Authorization: `Bearer ${accessToken}`
|
|
17295
|
+
}
|
|
17296
|
+
});
|
|
17297
|
+
if (response.status === 401) {
|
|
17298
|
+
throw new AuthenticationError("Authentication failed");
|
|
17299
|
+
}
|
|
17300
|
+
if (!response.ok) {
|
|
17301
|
+
const errorText = await response.text();
|
|
17302
|
+
throw new NetworkError(
|
|
17303
|
+
`Failed to mute call: ${response.status} ${response.statusText} - ${errorText}`
|
|
17304
|
+
);
|
|
17305
|
+
}
|
|
17306
|
+
this.logger.info("Call muted successfully", { callId });
|
|
17307
|
+
} catch (error) {
|
|
17308
|
+
if (error instanceof AuthenticationError || error instanceof NetworkError) {
|
|
17309
|
+
throw error;
|
|
17310
|
+
}
|
|
17311
|
+
this.logger.error("Failed to mute call", { error });
|
|
17312
|
+
throw new NetworkError("Failed to mute call", { cause: error });
|
|
17313
|
+
}
|
|
17314
|
+
}
|
|
17315
|
+
/**
|
|
17316
|
+
* Unmute a call (DELETE /calls/{call_id}/mute)
|
|
17317
|
+
*/
|
|
17318
|
+
async unmuteCall(callId, accessToken) {
|
|
17319
|
+
this.logger.debug("Unmuting call", { callId });
|
|
17320
|
+
try {
|
|
17321
|
+
const response = await fetch(`${this.apiUrl}/api/v1/conversations/calls/${callId}/mute`, {
|
|
17322
|
+
method: "DELETE",
|
|
17323
|
+
headers: {
|
|
17324
|
+
Authorization: `Bearer ${accessToken}`
|
|
17325
|
+
}
|
|
17326
|
+
});
|
|
17327
|
+
if (response.status === 401) {
|
|
17328
|
+
throw new AuthenticationError("Authentication failed");
|
|
17329
|
+
}
|
|
17330
|
+
if (!response.ok) {
|
|
17331
|
+
const errorText = await response.text();
|
|
17332
|
+
throw new NetworkError(
|
|
17333
|
+
`Failed to unmute call: ${response.status} ${response.statusText} - ${errorText}`
|
|
17334
|
+
);
|
|
17335
|
+
}
|
|
17336
|
+
this.logger.info("Call unmuted successfully", { callId });
|
|
17337
|
+
} catch (error) {
|
|
17338
|
+
if (error instanceof AuthenticationError || error instanceof NetworkError) {
|
|
17339
|
+
throw error;
|
|
17340
|
+
}
|
|
17341
|
+
this.logger.error("Failed to unmute call", { error });
|
|
17342
|
+
throw new NetworkError("Failed to unmute call", { cause: error });
|
|
17343
|
+
}
|
|
17344
|
+
}
|
|
17345
|
+
/**
|
|
17346
|
+
* Get call mute status (GET /calls/{call_id}/mute)
|
|
17347
|
+
*/
|
|
17348
|
+
async getCallMuteStatus(callId, accessToken) {
|
|
17349
|
+
this.logger.debug("Getting call mute status", { callId });
|
|
17350
|
+
try {
|
|
17351
|
+
const response = await fetch(`${this.apiUrl}/api/v1/conversations/calls/${callId}/mute`, {
|
|
17352
|
+
method: "GET",
|
|
17353
|
+
headers: {
|
|
17354
|
+
Authorization: `Bearer ${accessToken}`
|
|
17355
|
+
}
|
|
17356
|
+
});
|
|
17357
|
+
if (response.status === 401) {
|
|
17358
|
+
throw new AuthenticationError("Authentication failed");
|
|
17359
|
+
}
|
|
17360
|
+
if (!response.ok) {
|
|
17361
|
+
const errorText = await response.text();
|
|
17362
|
+
throw new NetworkError(
|
|
17363
|
+
`Failed to get call mute status: ${response.status} ${response.statusText} - ${errorText}`
|
|
17364
|
+
);
|
|
17365
|
+
}
|
|
17366
|
+
const data = await response.json();
|
|
17367
|
+
this.logger.debug("Call mute status retrieved", { callId, muted: data.muted });
|
|
17368
|
+
return data;
|
|
17369
|
+
} catch (error) {
|
|
17370
|
+
if (error instanceof AuthenticationError || error instanceof NetworkError) {
|
|
17371
|
+
throw error;
|
|
17372
|
+
}
|
|
17373
|
+
this.logger.error("Failed to get call mute status", { error });
|
|
17374
|
+
throw new NetworkError("Failed to get call mute status", { cause: error });
|
|
17375
|
+
}
|
|
17376
|
+
}
|
|
17244
17377
|
};
|
|
17245
17378
|
|
|
17246
17379
|
// src/conversation/Conversation.ts
|
|
17247
17380
|
var Conversation = class extends EventEmitter {
|
|
17248
|
-
constructor(id, options, authManager,
|
|
17381
|
+
constructor(id, options, authManager, urls) {
|
|
17249
17382
|
super();
|
|
17250
17383
|
this.logger = getLogger(["angany", "sdk", "conversation"]);
|
|
17251
17384
|
this.state = "idle";
|
|
@@ -17254,12 +17387,17 @@ var Conversation = class extends EventEmitter {
|
|
|
17254
17387
|
this.id = id;
|
|
17255
17388
|
this.options = options;
|
|
17256
17389
|
this.authManager = authManager;
|
|
17257
|
-
this.
|
|
17390
|
+
this.urls = urls;
|
|
17391
|
+
const sseUrl = urls.sseUrl || urls.apiUrl;
|
|
17258
17392
|
this.sipManager = new SipManager();
|
|
17259
|
-
this.transcriptionService = new TranscriptionService(
|
|
17260
|
-
this.apiService = new ApiService(apiUrl);
|
|
17393
|
+
this.transcriptionService = new TranscriptionService(sseUrl);
|
|
17394
|
+
this.apiService = new ApiService(urls.apiUrl);
|
|
17261
17395
|
this.logger = this.logger.with({ conversationId: id, resource: options.resource });
|
|
17262
|
-
this.logger.debug("Conversation created"
|
|
17396
|
+
this.logger.debug("Conversation created", {
|
|
17397
|
+
apiUrl: urls.apiUrl,
|
|
17398
|
+
sseUrl,
|
|
17399
|
+
sipUrl: urls.sipUrl || "will be derived"
|
|
17400
|
+
});
|
|
17263
17401
|
}
|
|
17264
17402
|
/**
|
|
17265
17403
|
* Initialize and start the conversation
|
|
@@ -17356,8 +17494,12 @@ var Conversation = class extends EventEmitter {
|
|
|
17356
17494
|
if (this.ephemeralCredentials.sip.realm) {
|
|
17357
17495
|
sipConfig.realm = this.ephemeralCredentials.sip.realm;
|
|
17358
17496
|
}
|
|
17359
|
-
if (this.
|
|
17497
|
+
if (this.urls.sipUrl) {
|
|
17498
|
+
sipConfig.websocketUrl = this.urls.sipUrl;
|
|
17499
|
+
this.logger.debug("Using configured SIP URL", { url: sipConfig.websocketUrl });
|
|
17500
|
+
} else if (this.ephemeralCredentials.sip.websocketUrl) {
|
|
17360
17501
|
sipConfig.websocketUrl = this.ephemeralCredentials.sip.websocketUrl;
|
|
17502
|
+
this.logger.debug("Using platform-provided WebSocket URL", { url: sipConfig.websocketUrl });
|
|
17361
17503
|
} else if (this.ephemeralCredentials.sip.uris && this.ephemeralCredentials.sip.uris.length > 0) {
|
|
17362
17504
|
const wssUris = this.ephemeralCredentials.sip.uris.filter(
|
|
17363
17505
|
(uri) => uri.includes("transport=wss")
|
|
@@ -17368,7 +17510,7 @@ var Conversation = class extends EventEmitter {
|
|
|
17368
17510
|
const wsUri = publicUri || wssUris[0];
|
|
17369
17511
|
if (wsUri) {
|
|
17370
17512
|
this.logger.debug("Selected SIP URI", { uri: wsUri, isPublic: !!publicUri });
|
|
17371
|
-
const apiDomain = new URL(this.apiUrl).hostname;
|
|
17513
|
+
const apiDomain = new URL(this.urls.apiUrl).hostname;
|
|
17372
17514
|
sipConfig.websocketUrl = `wss://${apiDomain}/api/webrtc/`;
|
|
17373
17515
|
this.logger.debug("Derived WebSocket URL from API domain", {
|
|
17374
17516
|
apiDomain,
|
|
@@ -17376,15 +17518,18 @@ var Conversation = class extends EventEmitter {
|
|
|
17376
17518
|
});
|
|
17377
17519
|
}
|
|
17378
17520
|
}
|
|
17379
|
-
if (sipConfig.websocketUrl) {
|
|
17521
|
+
if (sipConfig.websocketUrl && !this.urls.sipUrl) {
|
|
17380
17522
|
this.logger.debug("Original WebSocket URL", { url: sipConfig.websocketUrl });
|
|
17381
17523
|
const privateIpPattern = /wss?:\/\/(192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.)/;
|
|
17382
17524
|
if (privateIpPattern.test(sipConfig.websocketUrl)) {
|
|
17383
17525
|
this.logger.warn("Detected private IP in WebSocket URL, replacing with API domain");
|
|
17384
|
-
const apiDomain = new URL(this.apiUrl).hostname;
|
|
17526
|
+
const apiDomain = new URL(this.urls.apiUrl).hostname;
|
|
17385
17527
|
const originalUrl = sipConfig.websocketUrl;
|
|
17386
17528
|
const correctedUrl = `wss://${apiDomain}/api/webrtc/`;
|
|
17387
|
-
this.logger.debug("Extracted API domain", {
|
|
17529
|
+
this.logger.debug("Extracted API domain", {
|
|
17530
|
+
domain: apiDomain,
|
|
17531
|
+
apiUrl: this.urls.apiUrl
|
|
17532
|
+
});
|
|
17388
17533
|
sipConfig.websocketUrl = correctedUrl;
|
|
17389
17534
|
this.logger.info("Corrected WebSocket URL", {
|
|
17390
17535
|
original: originalUrl,
|
|
@@ -17456,11 +17601,11 @@ var Conversation = class extends EventEmitter {
|
|
|
17456
17601
|
const sendVoiceOptions = {
|
|
17457
17602
|
text,
|
|
17458
17603
|
callId: this.callId,
|
|
17459
|
-
|
|
17604
|
+
interruptsConversation: options?.interruptsConversation,
|
|
17605
|
+
queueWhenSpeaking: options?.queueWhenSpeaking,
|
|
17606
|
+
muteAgent: options?.muteAgent,
|
|
17607
|
+
voiceSettings: options?.voiceSettings
|
|
17460
17608
|
};
|
|
17461
|
-
if (options?.interrupt !== void 0) {
|
|
17462
|
-
sendVoiceOptions.interrupt = options.interrupt;
|
|
17463
|
-
}
|
|
17464
17609
|
try {
|
|
17465
17610
|
await this.apiService.sendVoice(sendVoiceOptions, this.accessToken);
|
|
17466
17611
|
} catch (error) {
|
|
@@ -17547,6 +17692,118 @@ var Conversation = class extends EventEmitter {
|
|
|
17547
17692
|
isAgentMuted() {
|
|
17548
17693
|
return this.agentMuted;
|
|
17549
17694
|
}
|
|
17695
|
+
/**
|
|
17696
|
+
* Get the current call ID (available after connection)
|
|
17697
|
+
*/
|
|
17698
|
+
getCallId() {
|
|
17699
|
+
return this.callId;
|
|
17700
|
+
}
|
|
17701
|
+
/**
|
|
17702
|
+
* Mute the call via API (POST /calls/{call_id}/mute)
|
|
17703
|
+
*/
|
|
17704
|
+
async muteCall() {
|
|
17705
|
+
this.logger.debug("Muting call via API");
|
|
17706
|
+
if (!this.callId) {
|
|
17707
|
+
throw new ConversationError("No call ID available - call may not be connected yet");
|
|
17708
|
+
}
|
|
17709
|
+
if (!this.accessToken) {
|
|
17710
|
+
throw new AuthenticationError("No access token available");
|
|
17711
|
+
}
|
|
17712
|
+
try {
|
|
17713
|
+
await this.apiService.muteCall(this.callId, this.accessToken);
|
|
17714
|
+
this.logger.info("Call muted via API", { callId: this.callId });
|
|
17715
|
+
} catch (error) {
|
|
17716
|
+
if (error instanceof AuthenticationError) {
|
|
17717
|
+
this.logger.debug("API call failed with auth error, attempting token refresh");
|
|
17718
|
+
try {
|
|
17719
|
+
const freshToken = await this.authManager.getAccessToken();
|
|
17720
|
+
if (freshToken && freshToken !== this.accessToken) {
|
|
17721
|
+
this.accessToken = freshToken;
|
|
17722
|
+
this.logger.debug("Token refreshed, retrying mute call operation");
|
|
17723
|
+
await this.apiService.muteCall(this.callId, this.accessToken);
|
|
17724
|
+
return;
|
|
17725
|
+
}
|
|
17726
|
+
throw error;
|
|
17727
|
+
} catch (refreshError) {
|
|
17728
|
+
this.logger.error("Failed to refresh token for mute call operation", {
|
|
17729
|
+
error: refreshError
|
|
17730
|
+
});
|
|
17731
|
+
throw error;
|
|
17732
|
+
}
|
|
17733
|
+
}
|
|
17734
|
+
throw error;
|
|
17735
|
+
}
|
|
17736
|
+
}
|
|
17737
|
+
/**
|
|
17738
|
+
* Unmute the call via API (DELETE /calls/{call_id}/mute)
|
|
17739
|
+
*/
|
|
17740
|
+
async unmuteCall() {
|
|
17741
|
+
this.logger.debug("Unmuting call via API");
|
|
17742
|
+
if (!this.callId) {
|
|
17743
|
+
throw new ConversationError("No call ID available - call may not be connected yet");
|
|
17744
|
+
}
|
|
17745
|
+
if (!this.accessToken) {
|
|
17746
|
+
throw new AuthenticationError("No access token available");
|
|
17747
|
+
}
|
|
17748
|
+
try {
|
|
17749
|
+
await this.apiService.unmuteCall(this.callId, this.accessToken);
|
|
17750
|
+
this.logger.info("Call unmuted via API", { callId: this.callId });
|
|
17751
|
+
} catch (error) {
|
|
17752
|
+
if (error instanceof AuthenticationError) {
|
|
17753
|
+
this.logger.debug("API call failed with auth error, attempting token refresh");
|
|
17754
|
+
try {
|
|
17755
|
+
const freshToken = await this.authManager.getAccessToken();
|
|
17756
|
+
if (freshToken && freshToken !== this.accessToken) {
|
|
17757
|
+
this.accessToken = freshToken;
|
|
17758
|
+
this.logger.debug("Token refreshed, retrying unmute call operation");
|
|
17759
|
+
await this.apiService.unmuteCall(this.callId, this.accessToken);
|
|
17760
|
+
return;
|
|
17761
|
+
}
|
|
17762
|
+
throw error;
|
|
17763
|
+
} catch (refreshError) {
|
|
17764
|
+
this.logger.error("Failed to refresh token for unmute call operation", {
|
|
17765
|
+
error: refreshError
|
|
17766
|
+
});
|
|
17767
|
+
throw error;
|
|
17768
|
+
}
|
|
17769
|
+
}
|
|
17770
|
+
throw error;
|
|
17771
|
+
}
|
|
17772
|
+
}
|
|
17773
|
+
/**
|
|
17774
|
+
* Get call mute status via API (GET /calls/{call_id}/mute)
|
|
17775
|
+
*/
|
|
17776
|
+
async getCallMuteStatus() {
|
|
17777
|
+
this.logger.debug("Getting call mute status via API");
|
|
17778
|
+
if (!this.callId) {
|
|
17779
|
+
throw new ConversationError("No call ID available - call may not be connected yet");
|
|
17780
|
+
}
|
|
17781
|
+
if (!this.accessToken) {
|
|
17782
|
+
throw new AuthenticationError("No access token available");
|
|
17783
|
+
}
|
|
17784
|
+
try {
|
|
17785
|
+
return await this.apiService.getCallMuteStatus(this.callId, this.accessToken);
|
|
17786
|
+
} catch (error) {
|
|
17787
|
+
if (error instanceof AuthenticationError) {
|
|
17788
|
+
this.logger.debug("API call failed with auth error, attempting token refresh");
|
|
17789
|
+
try {
|
|
17790
|
+
const freshToken = await this.authManager.getAccessToken();
|
|
17791
|
+
if (freshToken && freshToken !== this.accessToken) {
|
|
17792
|
+
this.accessToken = freshToken;
|
|
17793
|
+
this.logger.debug("Token refreshed, retrying get call mute status operation");
|
|
17794
|
+
return await this.apiService.getCallMuteStatus(this.callId, this.accessToken);
|
|
17795
|
+
}
|
|
17796
|
+
throw error;
|
|
17797
|
+
} catch (refreshError) {
|
|
17798
|
+
this.logger.error("Failed to refresh token for get call mute status operation", {
|
|
17799
|
+
error: refreshError
|
|
17800
|
+
});
|
|
17801
|
+
throw error;
|
|
17802
|
+
}
|
|
17803
|
+
}
|
|
17804
|
+
throw error;
|
|
17805
|
+
}
|
|
17806
|
+
}
|
|
17550
17807
|
/**
|
|
17551
17808
|
* Get conversation status
|
|
17552
17809
|
*/
|
|
@@ -17702,12 +17959,24 @@ var Conversation = class extends EventEmitter {
|
|
|
17702
17959
|
speaker: event.speaker,
|
|
17703
17960
|
text: event.text,
|
|
17704
17961
|
timestamp: event.timestamp,
|
|
17705
|
-
isFinal: event.isFinal
|
|
17962
|
+
isFinal: event.isFinal,
|
|
17963
|
+
humanTurnId: event.humanTurnId,
|
|
17964
|
+
agentTurnId: event.agentTurnId,
|
|
17965
|
+
speakerId: event.speakerId
|
|
17706
17966
|
});
|
|
17707
17967
|
});
|
|
17708
17968
|
this.transcriptionService.on("callId", (callId) => {
|
|
17709
17969
|
this.logger.info("Received call ID", { callId });
|
|
17710
17970
|
this.callId = callId;
|
|
17971
|
+
this.emit("callId", callId);
|
|
17972
|
+
});
|
|
17973
|
+
this.transcriptionService.on("callStarted", (callId) => {
|
|
17974
|
+
this.logger.info("Call started", { callId });
|
|
17975
|
+
this.emit("callStarted", callId);
|
|
17976
|
+
});
|
|
17977
|
+
this.transcriptionService.on("callEnded", (callId) => {
|
|
17978
|
+
this.logger.info("Call ended", { callId });
|
|
17979
|
+
this.emit("callEnded", callId);
|
|
17711
17980
|
});
|
|
17712
17981
|
this.transcriptionService.on("error", (error) => {
|
|
17713
17982
|
this.logger.error("Transcription error", { error });
|
|
@@ -17765,8 +18034,12 @@ var AnganyVoice = class extends EventEmitter {
|
|
|
17765
18034
|
this.logger = getLogger(["angany", "sdk", "core"]);
|
|
17766
18035
|
this.conversations = /* @__PURE__ */ new Map();
|
|
17767
18036
|
this.config = config;
|
|
17768
|
-
this.auth = new AuthManager(config.apiUrl, config.apiUrl);
|
|
17769
|
-
this.logger.debug("AnganyVoice initialized", {
|
|
18037
|
+
this.auth = new AuthManager(config.apiUrl, config.issuer || config.apiUrl);
|
|
18038
|
+
this.logger.debug("AnganyVoice initialized", {
|
|
18039
|
+
apiUrl: config.apiUrl,
|
|
18040
|
+
sseUrl: config.sseUrl || config.apiUrl,
|
|
18041
|
+
sipUrl: config.sipUrl || "derived from apiUrl"
|
|
18042
|
+
});
|
|
17770
18043
|
}
|
|
17771
18044
|
/**
|
|
17772
18045
|
* Get the current configuration
|
|
@@ -17801,7 +18074,11 @@ var AnganyVoice = class extends EventEmitter {
|
|
|
17801
18074
|
...options
|
|
17802
18075
|
},
|
|
17803
18076
|
this.auth,
|
|
17804
|
-
|
|
18077
|
+
{
|
|
18078
|
+
apiUrl: this.config.apiUrl,
|
|
18079
|
+
sseUrl: this.config.sseUrl,
|
|
18080
|
+
sipUrl: this.config.sipUrl
|
|
18081
|
+
}
|
|
17805
18082
|
);
|
|
17806
18083
|
conversation.on("ended", () => {
|
|
17807
18084
|
this.conversations.delete(conversationId);
|