@hivegpt/hiveai-angular 0.0.576 → 0.0.577

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/bundles/hivegpt-hiveai-angular.umd.js +214 -356
  2. package/bundles/hivegpt-hiveai-angular.umd.js.map +1 -1
  3. package/bundles/hivegpt-hiveai-angular.umd.min.js +1 -1
  4. package/bundles/hivegpt-hiveai-angular.umd.min.js.map +1 -1
  5. package/esm2015/lib/components/voice-agent/components/voice-agent-modal/voice-agent-modal.component.js +54 -85
  6. package/esm2015/lib/components/voice-agent/services/daily-voice-client.service.js +14 -22
  7. package/esm2015/lib/components/voice-agent/services/voice-agent.service.js +60 -90
  8. package/esm2015/lib/components/voice-agent/services/websocket-voice-client.service.js +34 -81
  9. package/fesm2015/hivegpt-hiveai-angular.js +157 -272
  10. package/fesm2015/hivegpt-hiveai-angular.js.map +1 -1
  11. package/hivegpt-hiveai-angular.metadata.json +1 -1
  12. package/lib/components/voice-agent/components/voice-agent-modal/voice-agent-modal.component.d.ts +1 -7
  13. package/lib/components/voice-agent/components/voice-agent-modal/voice-agent-modal.component.d.ts.map +1 -1
  14. package/lib/components/voice-agent/services/daily-voice-client.service.d.ts +3 -5
  15. package/lib/components/voice-agent/services/daily-voice-client.service.d.ts.map +1 -1
  16. package/lib/components/voice-agent/services/voice-agent.service.d.ts +1 -3
  17. package/lib/components/voice-agent/services/voice-agent.service.d.ts.map +1 -1
  18. package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts +12 -5
  19. package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts.map +1 -1
  20. package/package.json +1 -1
@@ -1349,7 +1349,6 @@
1349
1349
  * - Emit roomCreated$, userTranscript$, botTranscript$
1350
1350
  * - NO audio logic, NO mic logic. Audio is handled by Daily.js (WebRTC).
1351
1351
  */
1352
- var WS_CONNECT_TIMEOUT_MS = 10000;
1353
1352
  var WebSocketVoiceClientService = /** @class */ (function () {
1354
1353
  function WebSocketVoiceClientService() {
1355
1354
  this.ws = null;
@@ -1363,101 +1362,55 @@
1363
1362
  /** Emits bot transcript updates. */
1364
1363
  this.botTranscript$ = this.botTranscriptSubject.asObservable();
1365
1364
  }
1366
- /**
1367
- * Connect to signaling WebSocket. No audio over this connection.
1368
- * Resolves when the socket is open; rejects if the connection fails.
1369
- */
1365
+ /** Connect to signaling WebSocket. No audio over this connection. */
1370
1366
  WebSocketVoiceClientService.prototype.connect = function (wsUrl) {
1371
1367
  var _this = this;
1372
1368
  var _a;
1373
1369
  if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
1374
- return Promise.resolve();
1370
+ return;
1375
1371
  }
1376
1372
  if (this.ws) {
1377
1373
  this.ws.close();
1378
1374
  this.ws = null;
1379
1375
  }
1380
- return new Promise(function (resolve, reject) {
1381
- var settled = false;
1382
- var timeout = setTimeout(function () {
1376
+ try {
1377
+ this.ws = new WebSocket(wsUrl);
1378
+ this.ws.onmessage = function (event) {
1383
1379
  var _a;
1384
- if (settled)
1385
- return;
1386
- settled = true;
1387
1380
  try {
1388
- (_a = _this.ws) === null || _a === void 0 ? void 0 : _a.close();
1389
- }
1390
- catch (_b) {
1391
- /* ignore */
1392
- }
1393
- _this.ws = null;
1394
- reject(new Error('WebSocket connection timed out'));
1395
- }, WS_CONNECT_TIMEOUT_MS);
1396
- var clear = function () {
1397
- clearTimeout(timeout);
1398
- };
1399
- try {
1400
- var ws = new WebSocket(wsUrl);
1401
- _this.ws = ws;
1402
- ws.onopen = function () {
1403
- if (settled)
1404
- return;
1405
- settled = true;
1406
- clear();
1407
- resolve();
1408
- };
1409
- ws.onmessage = function (event) {
1410
- var _a;
1411
- try {
1412
- var msg = JSON.parse(event.data);
1413
- if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'room_created') {
1414
- var roomUrl = ((_a = msg.room_url) !== null && _a !== void 0 ? _a : msg.roomUrl);
1415
- if (typeof roomUrl === 'string') {
1416
- _this.roomCreatedSubject.next(roomUrl);
1417
- }
1381
+ var msg = JSON.parse(event.data);
1382
+ if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'room_created') {
1383
+ var roomUrl = ((_a = msg.room_url) !== null && _a !== void 0 ? _a : msg.roomUrl);
1384
+ if (typeof roomUrl === 'string') {
1385
+ _this.roomCreatedSubject.next(roomUrl);
1418
1386
  }
1419
- else if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'user_transcript' && typeof msg.text === 'string') {
1420
- _this.userTranscriptSubject.next({
1421
- text: msg.text,
1422
- final: msg.final === true,
1423
- });
1424
- }
1425
- else if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'bot_transcript' && typeof msg.text === 'string') {
1426
- _this.botTranscriptSubject.next(msg.text);
1427
- }
1428
- }
1429
- catch (_b) {
1430
- // Ignore non-JSON or unknown messages
1431
1387
  }
1432
- };
1433
- ws.onerror = function () {
1434
- if (!settled) {
1435
- settled = true;
1436
- clear();
1437
- _this.disconnect();
1438
- reject(new Error('WebSocket connection failed'));
1439
- return;
1388
+ else if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'user_transcript' && typeof msg.text === 'string') {
1389
+ _this.userTranscriptSubject.next({
1390
+ text: msg.text,
1391
+ final: msg.final === true,
1392
+ });
1440
1393
  }
1441
- // After onopen, some environments fire onerror spuriously; closing here can
1442
- // kill the socket before room_created is delivered. Let onclose clean up.
1443
- console.warn('WebSocketVoiceClient: onerror after open (not forcing disconnect)');
1444
- };
1445
- ws.onclose = function () {
1446
- _this.ws = null;
1447
- if (!settled) {
1448
- settled = true;
1449
- clear();
1450
- reject(new Error('WebSocket connection failed'));
1394
+ else if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'bot_transcript' && typeof msg.text === 'string') {
1395
+ _this.botTranscriptSubject.next(msg.text);
1451
1396
  }
1452
- };
1453
- }
1454
- catch (err) {
1455
- clear();
1456
- console.error('WebSocketVoiceClient: connect failed', err);
1397
+ }
1398
+ catch (_b) {
1399
+ // Ignore non-JSON or unknown messages
1400
+ }
1401
+ };
1402
+ this.ws.onerror = function () {
1403
+ _this.disconnect();
1404
+ };
1405
+ this.ws.onclose = function () {
1457
1406
  _this.ws = null;
1458
- reject(err instanceof Error ? err : new Error(String(err)));
1459
- }
1460
- });
1407
+ };
1408
+ }
1409
+ catch (err) {
1410
+ console.error('WebSocketVoiceClient: connect failed', err);
1411
+ this.ws = null;
1412
+ throw err;
1413
+ }
1461
1414
  };
1462
1415
  /** Disconnect and cleanup. */
1463
1416
  WebSocketVoiceClientService.prototype.disconnect = function () {
@@ -1505,8 +1458,7 @@
1505
1458
  this.remoteAudioElement = null;
1506
1459
  /** AnalyserNode-based remote audio monitor for instant bot speaking detection. */
1507
1460
  this.remoteAudioContext = null;
1508
- /** Poll interval id (~100ms); named historically when RAF was used. */
1509
- this.remoteSpeakingPollId = null;
1461
+ this.remoteSpeakingRAF = null;
1510
1462
  this.speakingSubject = new rxjs.BehaviorSubject(false);
1511
1463
  this.userSpeakingSubject = new rxjs.BehaviorSubject(false);
1512
1464
  this.micMutedSubject = new rxjs.BehaviorSubject(false);
@@ -1524,31 +1476,23 @@
1524
1476
  * Connect to Daily room. Acquires mic first for waveform, then joins with audio.
1525
1477
  * @param roomUrl Daily room URL (from room_created)
1526
1478
  * @param token Optional meeting token
1527
- * @param existingStream Optional pre-acquired mic (avoids a second getUserMedia / extra prompts on some browsers)
1528
1479
  */
1529
- DailyVoiceClientService.prototype.connect = function (roomUrl, token, existingStream) {
1480
+ DailyVoiceClientService.prototype.connect = function (roomUrl, token) {
1530
1481
  return __awaiter(this, void 0, void 0, function () {
1531
- var hasLiveTrack, stream, _e, audioTrack, callObject, joinOptions, participants, err_1;
1532
- return __generator(this, function (_f) {
1533
- switch (_f.label) {
1482
+ var stream, audioTrack, callObject, joinOptions, participants, err_1;
1483
+ return __generator(this, function (_e) {
1484
+ switch (_e.label) {
1534
1485
  case 0:
1535
1486
  if (!this.callObject) return [3 /*break*/, 2];
1536
1487
  return [4 /*yield*/, this.disconnect()];
1537
1488
  case 1:
1538
- _f.sent();
1539
- _f.label = 2;
1489
+ _e.sent();
1490
+ _e.label = 2;
1540
1491
  case 2:
1541
- _f.trys.push([2, 7, , 8]);
1542
- hasLiveTrack = !!(existingStream === null || existingStream === void 0 ? void 0 : existingStream.getAudioTracks().some(function (t) { return t.readyState === 'live'; }));
1543
- if (!hasLiveTrack) return [3 /*break*/, 3];
1544
- _e = existingStream;
1545
- return [3 /*break*/, 5];
1546
- case 3: return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
1547
- case 4:
1548
- _e = _f.sent();
1549
- _f.label = 5;
1550
- case 5:
1551
- stream = _e;
1492
+ _e.trys.push([2, 5, , 6]);
1493
+ return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
1494
+ case 3:
1495
+ stream = _e.sent();
1552
1496
  audioTrack = stream.getAudioTracks()[0];
1553
1497
  if (!audioTrack) {
1554
1498
  stream.getTracks().forEach(function (t) { return t.stop(); });
@@ -1567,8 +1511,8 @@
1567
1511
  joinOptions.token = token;
1568
1512
  }
1569
1513
  return [4 /*yield*/, callObject.join(joinOptions)];
1570
- case 6:
1571
- _f.sent();
1514
+ case 4:
1515
+ _e.sent();
1572
1516
  console.log("[VoiceDebug] Room connected (Daily join complete) \u2014 " + new Date().toISOString());
1573
1517
  participants = callObject.participants();
1574
1518
  if (participants === null || participants === void 0 ? void 0 : participants.local) {
@@ -1576,12 +1520,12 @@
1576
1520
  }
1577
1521
  // Initial mute state: Daily starts with audio on
1578
1522
  this.micMutedSubject.next(!callObject.localAudio());
1579
- return [3 /*break*/, 8];
1580
- case 7:
1581
- err_1 = _f.sent();
1523
+ return [3 /*break*/, 6];
1524
+ case 5:
1525
+ err_1 = _e.sent();
1582
1526
  this.cleanup();
1583
1527
  throw err_1;
1584
- case 8: return [2 /*return*/];
1528
+ case 6: return [2 /*return*/];
1585
1529
  }
1586
1530
  });
1587
1531
  });
@@ -1682,7 +1626,7 @@
1682
1626
  };
1683
1627
  /**
1684
1628
  * Monitor remote audio track energy via AnalyserNode.
1685
- * Polls at ~10Hz; sufficient for speaking detection vs ~60fps RAF.
1629
+ * Polls at ~60fps and flips speakingSubject based on actual audio energy.
1686
1630
  */
1687
1631
  DailyVoiceClientService.prototype.monitorRemoteAudio = function (track) {
1688
1632
  var _this = this;
@@ -1697,17 +1641,11 @@
1697
1641
  var dataArray_1 = new Uint8Array(analyser_1.frequencyBinCount);
1698
1642
  var THRESHOLD_1 = 5;
1699
1643
  var SILENCE_MS_1 = 1500;
1700
- var POLL_MS = 100;
1701
1644
  var lastSoundTime_1 = 0;
1702
1645
  var isSpeaking_1 = false;
1703
- this.remoteSpeakingPollId = setInterval(function () {
1704
- if (!_this.remoteAudioContext) {
1705
- if (_this.remoteSpeakingPollId) {
1706
- clearInterval(_this.remoteSpeakingPollId);
1707
- _this.remoteSpeakingPollId = null;
1708
- }
1646
+ var poll_1 = function () {
1647
+ if (!_this.remoteAudioContext)
1709
1648
  return;
1710
- }
1711
1649
  analyser_1.getByteFrequencyData(dataArray_1);
1712
1650
  var sum = 0;
1713
1651
  for (var i = 0; i < dataArray_1.length; i++) {
@@ -1731,16 +1669,18 @@
1731
1669
  console.log("[VoiceDebug] Bot audio silence detected (speaking=false) \u2014 " + new Date().toISOString());
1732
1670
  _this.ngZone.run(function () { return _this.speakingSubject.next(false); });
1733
1671
  }
1734
- }, POLL_MS);
1672
+ _this.remoteSpeakingRAF = requestAnimationFrame(poll_1);
1673
+ };
1674
+ this.remoteSpeakingRAF = requestAnimationFrame(poll_1);
1735
1675
  }
1736
1676
  catch (err) {
1737
1677
  console.warn('DailyVoiceClient: failed to create remote audio monitor', err);
1738
1678
  }
1739
1679
  };
1740
1680
  DailyVoiceClientService.prototype.stopRemoteAudioMonitor = function () {
1741
- if (this.remoteSpeakingPollId !== null) {
1742
- clearInterval(this.remoteSpeakingPollId);
1743
- this.remoteSpeakingPollId = null;
1681
+ if (this.remoteSpeakingRAF) {
1682
+ cancelAnimationFrame(this.remoteSpeakingRAF);
1683
+ this.remoteSpeakingRAF = null;
1744
1684
  }
1745
1685
  if (this.remoteAudioContext) {
1746
1686
  this.remoteAudioContext.close().catch(function () { });
@@ -1853,8 +1793,6 @@
1853
1793
  this.callStartTime = 0;
1854
1794
  this.durationInterval = null;
1855
1795
  this.subscriptions = new rxjs.Subscription();
1856
- /** Per-call only; cleared on disconnect / reset / new room so handlers do not stack. */
1857
- this.callSubscriptions = new rxjs.Subscription();
1858
1796
  this.destroy$ = new rxjs.Subject();
1859
1797
  this.callState$ = this.callStateSubject.asObservable();
1860
1798
  this.statusText$ = this.statusTextSubject.asObservable();
@@ -1866,15 +1804,6 @@
1866
1804
  this.botTranscript$ = this.botTranscriptSubject.asObservable();
1867
1805
  // Waveform visualization only - do NOT use for speaking state
1868
1806
  this.subscriptions.add(this.audioAnalyzer.audioLevels$.subscribe(function (levels) { return _this.audioLevelsSubject.next(levels); }));
1869
- // Transcripts: single subscription for service lifetime (avoid stacking on each connect()).
1870
- // WebSocket is disconnected between calls; no replay — new subscribers (setupVoiceTranscripts)
1871
- // only receive messages from the new WS after connect.
1872
- this.subscriptions.add(this.wsClient.userTranscript$
1873
- .pipe(operators.takeUntil(this.destroy$))
1874
- .subscribe(function (t) { return _this.userTranscriptSubject.next(t); }));
1875
- this.subscriptions.add(this.wsClient.botTranscript$
1876
- .pipe(operators.takeUntil(this.destroy$))
1877
- .subscribe(function (t) { return _this.botTranscriptSubject.next(t); }));
1878
1807
  }
1879
1808
  VoiceAgentService.prototype.ngOnDestroy = function () {
1880
1809
  this.destroy$.next();
@@ -1885,8 +1814,6 @@
1885
1814
  VoiceAgentService.prototype.resetToIdle = function () {
1886
1815
  if (this.callStateSubject.value === 'idle')
1887
1816
  return;
1888
- this.callSubscriptions.unsubscribe();
1889
- this.callSubscriptions = new rxjs.Subscription();
1890
1817
  this.stopDurationTimer();
1891
1818
  this.audioAnalyzer.stop();
1892
1819
  this.wsClient.disconnect();
@@ -1896,60 +1823,44 @@
1896
1823
  this.statusTextSubject.next('');
1897
1824
  this.durationSubject.next('0:00');
1898
1825
  };
1899
- VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl, existingMicStream) {
1900
- var _a;
1826
+ VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl) {
1901
1827
  return __awaiter(this, void 0, void 0, function () {
1902
- var tokenPromise, prepPromise, micPromise, _b, accessToken, _c, postUrl, body, micStream_1, headers, res, json, wsUrl, roomCreatedSub_1, roomJoined, e_1, error_1;
1828
+ var accessToken, ensured, e_1, baseUrl, postUrl, headers, res, json, wsUrl, error_1;
1903
1829
  var _this = this;
1904
- return __generator(this, function (_d) {
1905
- switch (_d.label) {
1830
+ return __generator(this, function (_a) {
1831
+ switch (_a.label) {
1906
1832
  case 0:
1907
1833
  if (this.callStateSubject.value !== 'idle') {
1908
1834
  console.warn('Call already in progress');
1909
1835
  return [2 /*return*/];
1910
1836
  }
1911
- _d.label = 1;
1837
+ _a.label = 1;
1912
1838
  case 1:
1913
- _d.trys.push([1, 10, , 12]);
1839
+ _a.trys.push([1, 8, , 10]);
1914
1840
  this.callStateSubject.next('connecting');
1915
1841
  this.statusTextSubject.next('Connecting...');
1916
- tokenPromise = usersApiUrl && common.isPlatformBrowser(this.platformId)
1917
- ? this.platformTokenRefresh
1842
+ accessToken = token;
1843
+ if (!(usersApiUrl && common.isPlatformBrowser(this.platformId))) return [3 /*break*/, 5];
1844
+ _a.label = 2;
1845
+ case 2:
1846
+ _a.trys.push([2, 4, , 5]);
1847
+ return [4 /*yield*/, this.platformTokenRefresh
1918
1848
  .ensureValidAccessToken(token, usersApiUrl)
1919
1849
  .pipe(operators.take(1))
1920
- .toPromise()
1921
- .then(function (ensured) { var _a; return (_a = ensured === null || ensured === void 0 ? void 0 : ensured.accessToken) !== null && _a !== void 0 ? _a : token; })
1922
- .catch(function (e) {
1923
- console.warn('[HiveGpt Voice] Token refresh before connect failed', e);
1924
- return token;
1925
- })
1926
- : Promise.resolve(token);
1927
- prepPromise = Promise.resolve().then(function () {
1928
- var baseUrl = apiUrl.replace(/\/$/, '');
1929
- return {
1930
- postUrl: baseUrl + "/ai/ask-voice",
1931
- body: JSON.stringify({
1932
- bot_id: botId,
1933
- conversation_id: conversationId,
1934
- voice: 'alloy',
1935
- }),
1936
- };
1937
- });
1938
- micPromise = (existingMicStream === null || existingMicStream === void 0 ? void 0 : existingMicStream.getAudioTracks().some(function (t) { return t.readyState === 'live'; }))
1939
- ? Promise.resolve(existingMicStream)
1940
- : common.isPlatformBrowser(this.platformId) &&
1941
- ((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia)
1942
- ? navigator.mediaDevices
1943
- .getUserMedia({ audio: true })
1944
- .catch(function () { return undefined; })
1945
- : Promise.resolve(undefined);
1946
- return [4 /*yield*/, Promise.all([
1947
- tokenPromise,
1948
- prepPromise,
1949
- micPromise,
1950
- ])];
1951
- case 2:
1952
- _b = __read.apply(void 0, [_d.sent(), 3]), accessToken = _b[0], _c = _b[1], postUrl = _c.postUrl, body = _c.body, micStream_1 = _b[2];
1850
+ .toPromise()];
1851
+ case 3:
1852
+ ensured = _a.sent();
1853
+ if (ensured === null || ensured === void 0 ? void 0 : ensured.accessToken) {
1854
+ accessToken = ensured.accessToken;
1855
+ }
1856
+ return [3 /*break*/, 5];
1857
+ case 4:
1858
+ e_1 = _a.sent();
1859
+ console.warn('[HiveGpt Voice] Token refresh before connect failed', e_1);
1860
+ return [3 /*break*/, 5];
1861
+ case 5:
1862
+ baseUrl = apiUrl.replace(/\/$/, '');
1863
+ postUrl = baseUrl + "/ai/ask-voice";
1953
1864
  headers = {
1954
1865
  'Content-Type': 'application/json',
1955
1866
  Authorization: "Bearer " + accessToken,
@@ -1964,95 +1875,97 @@
1964
1875
  return [4 /*yield*/, fetch(postUrl, {
1965
1876
  method: 'POST',
1966
1877
  headers: headers,
1967
- body: body,
1878
+ body: JSON.stringify({
1879
+ bot_id: botId,
1880
+ conversation_id: conversationId,
1881
+ voice: 'alloy',
1882
+ }),
1968
1883
  })];
1969
- case 3:
1970
- res = _d.sent();
1884
+ case 6:
1885
+ res = _a.sent();
1971
1886
  if (!res.ok) {
1972
1887
  throw new Error("HTTP " + res.status);
1973
1888
  }
1974
1889
  return [4 /*yield*/, res.json()];
1975
- case 4:
1976
- json = _d.sent();
1890
+ case 7:
1891
+ json = _a.sent();
1977
1892
  wsUrl = json === null || json === void 0 ? void 0 : json.rn_ws_url;
1978
1893
  if (!wsUrl || typeof wsUrl !== 'string') {
1979
1894
  throw new Error('No ws_url in response');
1980
1895
  }
1981
- roomJoined = new Promise(function (resolve, reject) {
1982
- roomCreatedSub_1 = _this.wsClient.roomCreated$
1983
- .pipe(operators.take(1), operators.takeUntil(_this.destroy$))
1984
- .subscribe(function (roomUrl) { return __awaiter(_this, void 0, void 0, function () {
1985
- var err_1;
1986
- return __generator(this, function (_b) {
1987
- switch (_b.label) {
1988
- case 0:
1989
- _b.trys.push([0, 2, , 3]);
1990
- return [4 /*yield*/, this.onRoomCreated(roomUrl, micStream_1 !== null && micStream_1 !== void 0 ? micStream_1 : undefined)];
1991
- case 1:
1992
- _b.sent();
1993
- resolve();
1994
- return [3 /*break*/, 3];
1995
- case 2:
1996
- err_1 = _b.sent();
1997
- console.error('Daily join failed:', err_1);
1998
- reject(err_1);
1999
- return [3 /*break*/, 3];
2000
- case 3: return [2 /*return*/];
2001
- }
2002
- });
2003
- }); }, function (err) { return reject(err); });
2004
- });
2005
- _d.label = 5;
2006
- case 5:
2007
- _d.trys.push([5, 8, , 9]);
2008
- return [4 /*yield*/, this.wsClient.connect(wsUrl)];
2009
- case 6:
2010
- _d.sent();
2011
- return [4 /*yield*/, roomJoined];
2012
- case 7:
2013
- _d.sent();
2014
- return [3 /*break*/, 9];
1896
+ // Subscribe to room_created BEFORE connecting to avoid race
1897
+ this.wsClient.roomCreated$
1898
+ .pipe(operators.take(1), operators.takeUntil(this.destroy$))
1899
+ .subscribe(function (roomUrl) { return __awaiter(_this, void 0, void 0, function () {
1900
+ var err_1;
1901
+ return __generator(this, function (_a) {
1902
+ switch (_a.label) {
1903
+ case 0:
1904
+ _a.trys.push([0, 2, , 4]);
1905
+ return [4 /*yield*/, this.onRoomCreated(roomUrl)];
1906
+ case 1:
1907
+ _a.sent();
1908
+ return [3 /*break*/, 4];
1909
+ case 2:
1910
+ err_1 = _a.sent();
1911
+ console.error('Daily join failed:', err_1);
1912
+ this.callStateSubject.next('ended');
1913
+ this.statusTextSubject.next('Connection failed');
1914
+ return [4 /*yield*/, this.disconnect()];
1915
+ case 3:
1916
+ _a.sent();
1917
+ throw err_1;
1918
+ case 4: return [2 /*return*/];
1919
+ }
1920
+ });
1921
+ }); });
1922
+ // Forward transcripts from WebSocket
1923
+ this.subscriptions.add(this.wsClient.userTranscript$
1924
+ .pipe(operators.takeUntil(this.destroy$))
1925
+ .subscribe(function (t) { return _this.userTranscriptSubject.next(t); }));
1926
+ this.subscriptions.add(this.wsClient.botTranscript$
1927
+ .pipe(operators.takeUntil(this.destroy$))
1928
+ .subscribe(function (t) { return _this.botTranscriptSubject.next(t); }));
1929
+ // Connect signaling WebSocket (no audio over WS)
1930
+ this.wsClient.connect(wsUrl);
1931
+ return [3 /*break*/, 10];
2015
1932
  case 8:
2016
- e_1 = _d.sent();
2017
- roomCreatedSub_1 === null || roomCreatedSub_1 === void 0 ? void 0 : roomCreatedSub_1.unsubscribe();
2018
- throw e_1;
2019
- case 9: return [3 /*break*/, 12];
2020
- case 10:
2021
- error_1 = _d.sent();
1933
+ error_1 = _a.sent();
2022
1934
  console.error('Error connecting voice agent:', error_1);
2023
1935
  this.callStateSubject.next('ended');
2024
1936
  return [4 /*yield*/, this.disconnect()];
2025
- case 11:
2026
- _d.sent();
1937
+ case 9:
1938
+ _a.sent();
2027
1939
  this.statusTextSubject.next('Connection failed');
2028
1940
  throw error_1;
2029
- case 12: return [2 /*return*/];
1941
+ case 10: return [2 /*return*/];
2030
1942
  }
2031
1943
  });
2032
1944
  });
2033
1945
  };
2034
- VoiceAgentService.prototype.onRoomCreated = function (roomUrl, micStream) {
1946
+ VoiceAgentService.prototype.onRoomCreated = function (roomUrl) {
2035
1947
  return __awaiter(this, void 0, void 0, function () {
2036
1948
  var _this = this;
2037
- return __generator(this, function (_b) {
2038
- switch (_b.label) {
2039
- case 0: return [4 /*yield*/, this.dailyClient.connect(roomUrl, undefined, micStream)];
1949
+ return __generator(this, function (_a) {
1950
+ switch (_a.label) {
1951
+ case 0:
1952
+ // Connect Daily.js for WebRTC audio
1953
+ return [4 /*yield*/, this.dailyClient.connect(roomUrl)];
2040
1954
  case 1:
2041
- _b.sent();
2042
- this.callSubscriptions.unsubscribe();
2043
- this.callSubscriptions = new rxjs.Subscription();
1955
+ // Connect Daily.js for WebRTC audio
1956
+ _a.sent();
2044
1957
  // Waveform: use local mic stream from Daily client
2045
- this.callSubscriptions.add(this.dailyClient.localStream$
1958
+ this.dailyClient.localStream$
2046
1959
  .pipe(operators.filter(function (s) { return s != null; }), operators.take(1))
2047
1960
  .subscribe(function (stream) {
2048
1961
  _this.audioAnalyzer.start(stream);
2049
- }));
2050
- this.callSubscriptions.add(this.dailyClient.userSpeaking$.subscribe(function (s) { return _this.isUserSpeakingSubject.next(s); }));
2051
- this.callSubscriptions.add(rxjs.combineLatest([
1962
+ });
1963
+ this.subscriptions.add(this.dailyClient.userSpeaking$.subscribe(function (s) { return _this.isUserSpeakingSubject.next(s); }));
1964
+ this.subscriptions.add(rxjs.combineLatest([
2052
1965
  this.dailyClient.speaking$,
2053
1966
  this.dailyClient.userSpeaking$,
2054
- ]).subscribe(function (_b) {
2055
- var _c = __read(_b, 2), bot = _c[0], user = _c[1];
1967
+ ]).subscribe(function (_a) {
1968
+ var _b = __read(_a, 2), bot = _b[0], user = _b[1];
2056
1969
  var current = _this.callStateSubject.value;
2057
1970
  if (current === 'connecting' && !bot) {
2058
1971
  return;
@@ -2069,12 +1982,11 @@
2069
1982
  else if (bot) {
2070
1983
  _this.callStateSubject.next('talking');
2071
1984
  }
2072
- else {
2073
- // Between bot turns: stay on listening to avoid flicker via 'connected'
2074
- _this.callStateSubject.next('listening');
1985
+ else if (current === 'talking' || current === 'listening') {
1986
+ _this.callStateSubject.next('connected');
2075
1987
  }
2076
1988
  }));
2077
- this.callSubscriptions.add(this.dailyClient.micMuted$.subscribe(function (muted) { return _this.isMicMutedSubject.next(muted); }));
1989
+ this.subscriptions.add(this.dailyClient.micMuted$.subscribe(function (muted) { return _this.isMicMutedSubject.next(muted); }));
2078
1990
  this.statusTextSubject.next('Connecting...');
2079
1991
  return [2 /*return*/];
2080
1992
  }
@@ -2083,18 +1995,16 @@
2083
1995
  };
2084
1996
  VoiceAgentService.prototype.disconnect = function () {
2085
1997
  return __awaiter(this, void 0, void 0, function () {
2086
- return __generator(this, function (_b) {
2087
- switch (_b.label) {
1998
+ return __generator(this, function (_a) {
1999
+ switch (_a.label) {
2088
2000
  case 0:
2089
- this.callSubscriptions.unsubscribe();
2090
- this.callSubscriptions = new rxjs.Subscription();
2091
2001
  this.stopDurationTimer();
2092
2002
  this.audioAnalyzer.stop();
2093
2003
  // Daily first, then WebSocket
2094
2004
  return [4 /*yield*/, this.dailyClient.disconnect()];
2095
2005
  case 1:
2096
2006
  // Daily first, then WebSocket
2097
- _b.sent();
2007
+ _a.sent();
2098
2008
  this.wsClient.disconnect();
2099
2009
  this.callStateSubject.next('ended');
2100
2010
  this.statusTextSubject.next('Call Ended');
@@ -2146,11 +2056,10 @@
2146
2056
  var VOICE_MODAL_CLOSE_CALLBACK = new i0.InjectionToken('VOICE_MODAL_CLOSE_CALLBACK');
2147
2057
 
2148
2058
  var VoiceAgentModalComponent = /** @class */ (function () {
2149
- function VoiceAgentModalComponent(voiceAgentService, audioAnalyzer, injector, platformId) {
2059
+ function VoiceAgentModalComponent(voiceAgentService, audioAnalyzer, injector) {
2150
2060
  this.voiceAgentService = voiceAgentService;
2151
2061
  this.audioAnalyzer = audioAnalyzer;
2152
2062
  this.injector = injector;
2153
- this.platformId = platformId;
2154
2063
  this.close = new i0.EventEmitter();
2155
2064
  this.apiKey = '';
2156
2065
  this.eventToken = '';
@@ -2162,8 +2071,6 @@
2162
2071
  this.usersApiUrl = '';
2163
2072
  this.injectedConfig = null;
2164
2073
  this.onCloseCallback = null;
2165
- /** Held until destroy; passed to Daily so we do not stop/re-acquire (avoids extra prompts on some browsers). */
2166
- this.warmMicStream = null;
2167
2074
  /** Hardcoded voice agent avatar (Nia). */
2168
2075
  this.displayAvatarUrl = 'https://www.jotform.com/uploads/mehmetkarakasli/form_files/1564593667676a8e85f23758.86945537_icon.png';
2169
2076
  this.callState = 'idle';
@@ -2179,108 +2086,63 @@
2179
2086
  this.isConnecting = false;
2180
2087
  }
2181
2088
  VoiceAgentModalComponent.prototype.ngOnInit = function () {
2182
- void this.bootstrap();
2183
- };
2184
- VoiceAgentModalComponent.prototype.bootstrap = function () {
2089
+ var _this = this;
2185
2090
  var _a, _b, _c, _d, _e, _f, _g, _h;
2186
- return __awaiter(this, void 0, void 0, function () {
2187
- var _this = this;
2188
- return __generator(this, function (_j) {
2189
- switch (_j.label) {
2190
- case 0:
2191
- this.injectedConfig = this.injector.get(VOICE_MODAL_CONFIG, null);
2192
- this.onCloseCallback = this.injector.get(VOICE_MODAL_CLOSE_CALLBACK, null);
2193
- if (this.injectedConfig) {
2194
- this.apiUrl = this.injectedConfig.apiUrl;
2195
- this.token = this.injectedConfig.token;
2196
- this.botId = this.injectedConfig.botId;
2197
- this.conversationId = this.injectedConfig.conversationId;
2198
- this.apiKey = (_a = this.injectedConfig.apiKey) !== null && _a !== void 0 ? _a : '';
2199
- this.eventToken = (_b = this.injectedConfig.eventToken) !== null && _b !== void 0 ? _b : '';
2200
- this.eventId = (_c = this.injectedConfig.eventId) !== null && _c !== void 0 ? _c : '';
2201
- this.eventUrl = (_d = this.injectedConfig.eventUrl) !== null && _d !== void 0 ? _d : '';
2202
- this.domainAuthority = (_e = this.injectedConfig.domainAuthority) !== null && _e !== void 0 ? _e : 'prod-lite';
2203
- this.agentName = (_f = this.injectedConfig.agentName) !== null && _f !== void 0 ? _f : this.agentName;
2204
- this.agentRole = (_g = this.injectedConfig.agentRole) !== null && _g !== void 0 ? _g : this.agentRole;
2205
- this.agentAvatar = this.injectedConfig.agentAvatar;
2206
- this.usersApiUrl = (_h = this.injectedConfig.usersApiUrl) !== null && _h !== void 0 ? _h : this.usersApiUrl;
2207
- }
2208
- // Subscribe to observables
2209
- this.subscriptions.push(this.voiceAgentService.callState$.subscribe(function (state) {
2210
- _this.callState = state;
2211
- _this.isSpeaking = state === 'talking';
2212
- if (state === 'listening' || state === 'talking') {
2213
- _this.hasLeftConnectedOnce = true;
2214
- }
2215
- if (state === 'idle' || state === 'ended') {
2216
- _this.hasLeftConnectedOnce = false;
2217
- }
2218
- }));
2219
- this.subscriptions.push(this.voiceAgentService.statusText$.subscribe(function (text) {
2220
- _this.statusText = text;
2221
- }));
2222
- this.subscriptions.push(this.voiceAgentService.duration$.subscribe(function (duration) {
2223
- _this.duration = duration;
2224
- }));
2225
- this.subscriptions.push(this.voiceAgentService.isMicMuted$.subscribe(function (muted) {
2226
- _this.isMicMuted = muted;
2227
- }));
2228
- this.subscriptions.push(this.voiceAgentService.isUserSpeaking$.subscribe(function (speaking) {
2229
- _this.isUserSpeaking = speaking;
2230
- }));
2231
- this.subscriptions.push(this.voiceAgentService.audioLevels$.subscribe(function (levels) {
2232
- _this.audioLevels = levels;
2233
- }));
2234
- this.voiceAgentService.resetToIdle();
2235
- return [4 /*yield*/, this.startCall()];
2236
- case 1:
2237
- _j.sent();
2238
- return [2 /*return*/];
2239
- }
2240
- });
2241
- });
2091
+ // When opened via Overlay, config is provided by injection
2092
+ this.injectedConfig = this.injector.get(VOICE_MODAL_CONFIG, null);
2093
+ this.onCloseCallback = this.injector.get(VOICE_MODAL_CLOSE_CALLBACK, null);
2094
+ if (this.injectedConfig) {
2095
+ this.apiUrl = this.injectedConfig.apiUrl;
2096
+ this.token = this.injectedConfig.token;
2097
+ this.botId = this.injectedConfig.botId;
2098
+ this.conversationId = this.injectedConfig.conversationId;
2099
+ this.apiKey = (_a = this.injectedConfig.apiKey) !== null && _a !== void 0 ? _a : '';
2100
+ this.eventToken = (_b = this.injectedConfig.eventToken) !== null && _b !== void 0 ? _b : '';
2101
+ this.eventId = (_c = this.injectedConfig.eventId) !== null && _c !== void 0 ? _c : '';
2102
+ this.eventUrl = (_d = this.injectedConfig.eventUrl) !== null && _d !== void 0 ? _d : '';
2103
+ this.domainAuthority = (_e = this.injectedConfig.domainAuthority) !== null && _e !== void 0 ? _e : 'prod-lite';
2104
+ this.agentName = (_f = this.injectedConfig.agentName) !== null && _f !== void 0 ? _f : this.agentName;
2105
+ this.agentRole = (_g = this.injectedConfig.agentRole) !== null && _g !== void 0 ? _g : this.agentRole;
2106
+ this.agentAvatar = this.injectedConfig.agentAvatar;
2107
+ this.usersApiUrl = (_h = this.injectedConfig.usersApiUrl) !== null && _h !== void 0 ? _h : this.usersApiUrl;
2108
+ }
2109
+ // Subscribe to observables
2110
+ this.subscriptions.push(this.voiceAgentService.callState$.subscribe(function (state) {
2111
+ _this.callState = state;
2112
+ _this.isSpeaking = state === 'talking';
2113
+ if (state === 'listening' || state === 'talking') {
2114
+ _this.hasLeftConnectedOnce = true;
2115
+ }
2116
+ if (state === 'idle' || state === 'ended') {
2117
+ _this.hasLeftConnectedOnce = false;
2118
+ }
2119
+ }));
2120
+ this.subscriptions.push(this.voiceAgentService.statusText$.subscribe(function (text) {
2121
+ _this.statusText = text;
2122
+ }));
2123
+ this.subscriptions.push(this.voiceAgentService.duration$.subscribe(function (duration) {
2124
+ _this.duration = duration;
2125
+ }));
2126
+ this.subscriptions.push(this.voiceAgentService.isMicMuted$.subscribe(function (muted) {
2127
+ _this.isMicMuted = muted;
2128
+ }));
2129
+ this.subscriptions.push(this.voiceAgentService.isUserSpeaking$.subscribe(function (speaking) {
2130
+ _this.isUserSpeaking = speaking;
2131
+ }));
2132
+ this.subscriptions.push(this.voiceAgentService.audioLevels$.subscribe(function (levels) {
2133
+ _this.audioLevels = levels;
2134
+ }));
2135
+ // Modal opens in idle state, then immediately starts connecting.
2136
+ this.voiceAgentService.resetToIdle();
2137
+ void this.startCall();
2242
2138
  };
2243
2139
  VoiceAgentModalComponent.prototype.ngOnDestroy = function () {
2244
- var _this = this;
2245
2140
  this.subscriptions.forEach(function (sub) { return sub.unsubscribe(); });
2246
- void this.disconnect().finally(function () {
2247
- var _a;
2248
- (_a = _this.warmMicStream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(function (t) { return t.stop(); });
2249
- _this.warmMicStream = null;
2250
- });
2251
- };
2252
- /** Ensures a live mic stream for this call (re-acquire after Daily stops tracks on hang-up). */
2253
- VoiceAgentModalComponent.prototype.ensureMicForCall = function () {
2254
- var _a;
2255
- return __awaiter(this, void 0, void 0, function () {
2256
- var _j, _b_1;
2257
- return __generator(this, function (_k) {
2258
- switch (_k.label) {
2259
- case 0:
2260
- if (!common.isPlatformBrowser(this.platformId))
2261
- return [2 /*return*/, undefined];
2262
- if ((_a = this.warmMicStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().some(function (t) { return t.readyState === 'live'; })) {
2263
- return [2 /*return*/, this.warmMicStream];
2264
- }
2265
- _k.label = 1;
2266
- case 1:
2267
- _k.trys.push([1, 3, , 4]);
2268
- _j = this;
2269
- return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
2270
- case 2:
2271
- _j.warmMicStream = _k.sent();
2272
- return [2 /*return*/, this.warmMicStream];
2273
- case 3:
2274
- _b_1 = _k.sent();
2275
- return [2 /*return*/, undefined];
2276
- case 4: return [2 /*return*/];
2277
- }
2278
- });
2279
- });
2141
+ this.disconnect();
2280
2142
  };
2281
2143
  VoiceAgentModalComponent.prototype.startCall = function () {
2282
2144
  return __awaiter(this, void 0, void 0, function () {
2283
- var mic, error_1;
2145
+ var error_1;
2284
2146
  return __generator(this, function (_j) {
2285
2147
  switch (_j.label) {
2286
2148
  case 0:
@@ -2289,22 +2151,19 @@
2289
2151
  this.isConnecting = true;
2290
2152
  _j.label = 1;
2291
2153
  case 1:
2292
- _j.trys.push([1, 4, 5, 6]);
2293
- return [4 /*yield*/, this.ensureMicForCall()];
2154
+ _j.trys.push([1, 3, 4, 5]);
2155
+ return [4 /*yield*/, this.voiceAgentService.connect(this.apiUrl, this.token, this.botId, this.conversationId, this.apiKey, this.eventToken, this.eventId, this.eventUrl, this.domainAuthority, this.usersApiUrl || undefined)];
2294
2156
  case 2:
2295
- mic = _j.sent();
2296
- return [4 /*yield*/, this.voiceAgentService.connect(this.apiUrl, this.token, this.botId, this.conversationId, this.apiKey, this.eventToken, this.eventId, this.eventUrl, this.domainAuthority, this.usersApiUrl || undefined, mic)];
2297
- case 3:
2298
2157
  _j.sent();
2299
- return [3 /*break*/, 6];
2300
- case 4:
2158
+ return [3 /*break*/, 5];
2159
+ case 3:
2301
2160
  error_1 = _j.sent();
2302
2161
  console.error('Failed to connect voice agent:', error_1);
2303
- return [3 /*break*/, 6];
2304
- case 5:
2162
+ return [3 /*break*/, 5];
2163
+ case 4:
2305
2164
  this.isConnecting = false;
2306
2165
  return [7 /*endfinally*/];
2307
- case 6: return [2 /*return*/];
2166
+ case 5: return [2 /*return*/];
2308
2167
  }
2309
2168
  });
2310
2169
  });
@@ -2349,7 +2208,7 @@
2349
2208
  /** Call Again: reset to idle then start a new call. */
2350
2209
  VoiceAgentModalComponent.prototype.callAgain = function () {
2351
2210
  this.voiceAgentService.resetToIdle();
2352
- void this.startCall();
2211
+ this.startCall();
2353
2212
  };
2354
2213
  /** Back to Chat: close modal and disconnect. */
2355
2214
  VoiceAgentModalComponent.prototype.backToChat = function () {
@@ -2377,8 +2236,7 @@
2377
2236
  VoiceAgentModalComponent.ctorParameters = function () { return [
2378
2237
  { type: VoiceAgentService },
2379
2238
  { type: AudioAnalyzerService },
2380
- { type: i0.Injector },
2381
- { type: Object, decorators: [{ type: i0.Inject, args: [i0.PLATFORM_ID,] }] }
2239
+ { type: i0.Injector }
2382
2240
  ]; };
2383
2241
  VoiceAgentModalComponent.propDecorators = {
2384
2242
  close: [{ type: i0.Output }],