@hivegpt/hiveai-angular 0.0.575 → 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 +195 -328
  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 +45 -67
  8. package/esm2015/lib/components/voice-agent/services/websocket-voice-client.service.js +34 -79
  9. package/fesm2015/hivegpt-hiveai-angular.js +142 -247
  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,99 +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
- _this.disconnect();
1442
- };
1443
- ws.onclose = function () {
1444
- _this.ws = null;
1445
- if (!settled) {
1446
- settled = true;
1447
- clear();
1448
- 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);
1449
1396
  }
1450
- };
1451
- }
1452
- catch (err) {
1453
- clear();
1454
- 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 () {
1455
1406
  _this.ws = null;
1456
- reject(err instanceof Error ? err : new Error(String(err)));
1457
- }
1458
- });
1407
+ };
1408
+ }
1409
+ catch (err) {
1410
+ console.error('WebSocketVoiceClient: connect failed', err);
1411
+ this.ws = null;
1412
+ throw err;
1413
+ }
1459
1414
  };
1460
1415
  /** Disconnect and cleanup. */
1461
1416
  WebSocketVoiceClientService.prototype.disconnect = function () {
@@ -1503,8 +1458,7 @@
1503
1458
  this.remoteAudioElement = null;
1504
1459
  /** AnalyserNode-based remote audio monitor for instant bot speaking detection. */
1505
1460
  this.remoteAudioContext = null;
1506
- /** Poll interval id (~100ms); named historically when RAF was used. */
1507
- this.remoteSpeakingPollId = null;
1461
+ this.remoteSpeakingRAF = null;
1508
1462
  this.speakingSubject = new rxjs.BehaviorSubject(false);
1509
1463
  this.userSpeakingSubject = new rxjs.BehaviorSubject(false);
1510
1464
  this.micMutedSubject = new rxjs.BehaviorSubject(false);
@@ -1522,31 +1476,23 @@
1522
1476
  * Connect to Daily room. Acquires mic first for waveform, then joins with audio.
1523
1477
  * @param roomUrl Daily room URL (from room_created)
1524
1478
  * @param token Optional meeting token
1525
- * @param existingStream Optional pre-acquired mic (avoids a second getUserMedia / extra prompts on some browsers)
1526
1479
  */
1527
- DailyVoiceClientService.prototype.connect = function (roomUrl, token, existingStream) {
1480
+ DailyVoiceClientService.prototype.connect = function (roomUrl, token) {
1528
1481
  return __awaiter(this, void 0, void 0, function () {
1529
- var hasLiveTrack, stream, _e, audioTrack, callObject, joinOptions, participants, err_1;
1530
- return __generator(this, function (_f) {
1531
- switch (_f.label) {
1482
+ var stream, audioTrack, callObject, joinOptions, participants, err_1;
1483
+ return __generator(this, function (_e) {
1484
+ switch (_e.label) {
1532
1485
  case 0:
1533
1486
  if (!this.callObject) return [3 /*break*/, 2];
1534
1487
  return [4 /*yield*/, this.disconnect()];
1535
1488
  case 1:
1536
- _f.sent();
1537
- _f.label = 2;
1489
+ _e.sent();
1490
+ _e.label = 2;
1538
1491
  case 2:
1539
- _f.trys.push([2, 7, , 8]);
1540
- hasLiveTrack = !!(existingStream === null || existingStream === void 0 ? void 0 : existingStream.getAudioTracks().some(function (t) { return t.readyState === 'live'; }));
1541
- if (!hasLiveTrack) return [3 /*break*/, 3];
1542
- _e = existingStream;
1543
- return [3 /*break*/, 5];
1544
- case 3: return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
1545
- case 4:
1546
- _e = _f.sent();
1547
- _f.label = 5;
1548
- case 5:
1549
- 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();
1550
1496
  audioTrack = stream.getAudioTracks()[0];
1551
1497
  if (!audioTrack) {
1552
1498
  stream.getTracks().forEach(function (t) { return t.stop(); });
@@ -1565,8 +1511,8 @@
1565
1511
  joinOptions.token = token;
1566
1512
  }
1567
1513
  return [4 /*yield*/, callObject.join(joinOptions)];
1568
- case 6:
1569
- _f.sent();
1514
+ case 4:
1515
+ _e.sent();
1570
1516
  console.log("[VoiceDebug] Room connected (Daily join complete) \u2014 " + new Date().toISOString());
1571
1517
  participants = callObject.participants();
1572
1518
  if (participants === null || participants === void 0 ? void 0 : participants.local) {
@@ -1574,12 +1520,12 @@
1574
1520
  }
1575
1521
  // Initial mute state: Daily starts with audio on
1576
1522
  this.micMutedSubject.next(!callObject.localAudio());
1577
- return [3 /*break*/, 8];
1578
- case 7:
1579
- err_1 = _f.sent();
1523
+ return [3 /*break*/, 6];
1524
+ case 5:
1525
+ err_1 = _e.sent();
1580
1526
  this.cleanup();
1581
1527
  throw err_1;
1582
- case 8: return [2 /*return*/];
1528
+ case 6: return [2 /*return*/];
1583
1529
  }
1584
1530
  });
1585
1531
  });
@@ -1680,7 +1626,7 @@
1680
1626
  };
1681
1627
  /**
1682
1628
  * Monitor remote audio track energy via AnalyserNode.
1683
- * Polls at ~10Hz; sufficient for speaking detection vs ~60fps RAF.
1629
+ * Polls at ~60fps and flips speakingSubject based on actual audio energy.
1684
1630
  */
1685
1631
  DailyVoiceClientService.prototype.monitorRemoteAudio = function (track) {
1686
1632
  var _this = this;
@@ -1695,17 +1641,11 @@
1695
1641
  var dataArray_1 = new Uint8Array(analyser_1.frequencyBinCount);
1696
1642
  var THRESHOLD_1 = 5;
1697
1643
  var SILENCE_MS_1 = 1500;
1698
- var POLL_MS = 100;
1699
1644
  var lastSoundTime_1 = 0;
1700
1645
  var isSpeaking_1 = false;
1701
- this.remoteSpeakingPollId = setInterval(function () {
1702
- if (!_this.remoteAudioContext) {
1703
- if (_this.remoteSpeakingPollId) {
1704
- clearInterval(_this.remoteSpeakingPollId);
1705
- _this.remoteSpeakingPollId = null;
1706
- }
1646
+ var poll_1 = function () {
1647
+ if (!_this.remoteAudioContext)
1707
1648
  return;
1708
- }
1709
1649
  analyser_1.getByteFrequencyData(dataArray_1);
1710
1650
  var sum = 0;
1711
1651
  for (var i = 0; i < dataArray_1.length; i++) {
@@ -1729,16 +1669,18 @@
1729
1669
  console.log("[VoiceDebug] Bot audio silence detected (speaking=false) \u2014 " + new Date().toISOString());
1730
1670
  _this.ngZone.run(function () { return _this.speakingSubject.next(false); });
1731
1671
  }
1732
- }, POLL_MS);
1672
+ _this.remoteSpeakingRAF = requestAnimationFrame(poll_1);
1673
+ };
1674
+ this.remoteSpeakingRAF = requestAnimationFrame(poll_1);
1733
1675
  }
1734
1676
  catch (err) {
1735
1677
  console.warn('DailyVoiceClient: failed to create remote audio monitor', err);
1736
1678
  }
1737
1679
  };
1738
1680
  DailyVoiceClientService.prototype.stopRemoteAudioMonitor = function () {
1739
- if (this.remoteSpeakingPollId !== null) {
1740
- clearInterval(this.remoteSpeakingPollId);
1741
- this.remoteSpeakingPollId = null;
1681
+ if (this.remoteSpeakingRAF) {
1682
+ cancelAnimationFrame(this.remoteSpeakingRAF);
1683
+ this.remoteSpeakingRAF = null;
1742
1684
  }
1743
1685
  if (this.remoteAudioContext) {
1744
1686
  this.remoteAudioContext.close().catch(function () { });
@@ -1851,8 +1793,6 @@
1851
1793
  this.callStartTime = 0;
1852
1794
  this.durationInterval = null;
1853
1795
  this.subscriptions = new rxjs.Subscription();
1854
- /** Per-call only; cleared on disconnect / reset / new room so handlers do not stack. */
1855
- this.callSubscriptions = new rxjs.Subscription();
1856
1796
  this.destroy$ = new rxjs.Subject();
1857
1797
  this.callState$ = this.callStateSubject.asObservable();
1858
1798
  this.statusText$ = this.statusTextSubject.asObservable();
@@ -1864,15 +1804,6 @@
1864
1804
  this.botTranscript$ = this.botTranscriptSubject.asObservable();
1865
1805
  // Waveform visualization only - do NOT use for speaking state
1866
1806
  this.subscriptions.add(this.audioAnalyzer.audioLevels$.subscribe(function (levels) { return _this.audioLevelsSubject.next(levels); }));
1867
- // Transcripts: single subscription for service lifetime (avoid stacking on each connect()).
1868
- // WebSocket is disconnected between calls; no replay — new subscribers (setupVoiceTranscripts)
1869
- // only receive messages from the new WS after connect.
1870
- this.subscriptions.add(this.wsClient.userTranscript$
1871
- .pipe(operators.takeUntil(this.destroy$))
1872
- .subscribe(function (t) { return _this.userTranscriptSubject.next(t); }));
1873
- this.subscriptions.add(this.wsClient.botTranscript$
1874
- .pipe(operators.takeUntil(this.destroy$))
1875
- .subscribe(function (t) { return _this.botTranscriptSubject.next(t); }));
1876
1807
  }
1877
1808
  VoiceAgentService.prototype.ngOnDestroy = function () {
1878
1809
  this.destroy$.next();
@@ -1883,8 +1814,6 @@
1883
1814
  VoiceAgentService.prototype.resetToIdle = function () {
1884
1815
  if (this.callStateSubject.value === 'idle')
1885
1816
  return;
1886
- this.callSubscriptions.unsubscribe();
1887
- this.callSubscriptions = new rxjs.Subscription();
1888
1817
  this.stopDurationTimer();
1889
1818
  this.audioAnalyzer.stop();
1890
1819
  this.wsClient.disconnect();
@@ -1894,60 +1823,44 @@
1894
1823
  this.statusTextSubject.next('');
1895
1824
  this.durationSubject.next('0:00');
1896
1825
  };
1897
- VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl, existingMicStream) {
1898
- var _a;
1826
+ VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl) {
1899
1827
  return __awaiter(this, void 0, void 0, function () {
1900
- var tokenPromise, prepPromise, micPromise, _b, accessToken, _c, postUrl, body, micStream_1, headers, res, json, wsUrl, error_1;
1828
+ var accessToken, ensured, e_1, baseUrl, postUrl, headers, res, json, wsUrl, error_1;
1901
1829
  var _this = this;
1902
- return __generator(this, function (_d) {
1903
- switch (_d.label) {
1830
+ return __generator(this, function (_a) {
1831
+ switch (_a.label) {
1904
1832
  case 0:
1905
1833
  if (this.callStateSubject.value !== 'idle') {
1906
1834
  console.warn('Call already in progress');
1907
1835
  return [2 /*return*/];
1908
1836
  }
1909
- _d.label = 1;
1837
+ _a.label = 1;
1910
1838
  case 1:
1911
- _d.trys.push([1, 6, , 8]);
1839
+ _a.trys.push([1, 8, , 10]);
1912
1840
  this.callStateSubject.next('connecting');
1913
1841
  this.statusTextSubject.next('Connecting...');
1914
- tokenPromise = usersApiUrl && common.isPlatformBrowser(this.platformId)
1915
- ? 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
1916
1848
  .ensureValidAccessToken(token, usersApiUrl)
1917
1849
  .pipe(operators.take(1))
1918
- .toPromise()
1919
- .then(function (ensured) { var _a; return (_a = ensured === null || ensured === void 0 ? void 0 : ensured.accessToken) !== null && _a !== void 0 ? _a : token; })
1920
- .catch(function (e) {
1921
- console.warn('[HiveGpt Voice] Token refresh before connect failed', e);
1922
- return token;
1923
- })
1924
- : Promise.resolve(token);
1925
- prepPromise = Promise.resolve().then(function () {
1926
- var baseUrl = apiUrl.replace(/\/$/, '');
1927
- return {
1928
- postUrl: baseUrl + "/ai/ask-voice",
1929
- body: JSON.stringify({
1930
- bot_id: botId,
1931
- conversation_id: conversationId,
1932
- voice: 'alloy',
1933
- }),
1934
- };
1935
- });
1936
- micPromise = (existingMicStream === null || existingMicStream === void 0 ? void 0 : existingMicStream.getAudioTracks().some(function (t) { return t.readyState === 'live'; }))
1937
- ? Promise.resolve(existingMicStream)
1938
- : common.isPlatformBrowser(this.platformId) &&
1939
- ((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia)
1940
- ? navigator.mediaDevices
1941
- .getUserMedia({ audio: true })
1942
- .catch(function () { return undefined; })
1943
- : Promise.resolve(undefined);
1944
- return [4 /*yield*/, Promise.all([
1945
- tokenPromise,
1946
- prepPromise,
1947
- micPromise,
1948
- ])];
1949
- case 2:
1950
- _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";
1951
1864
  headers = {
1952
1865
  'Content-Type': 'application/json',
1953
1866
  Authorization: "Bearer " + accessToken,
@@ -1962,16 +1875,20 @@
1962
1875
  return [4 /*yield*/, fetch(postUrl, {
1963
1876
  method: 'POST',
1964
1877
  headers: headers,
1965
- body: body,
1878
+ body: JSON.stringify({
1879
+ bot_id: botId,
1880
+ conversation_id: conversationId,
1881
+ voice: 'alloy',
1882
+ }),
1966
1883
  })];
1967
- case 3:
1968
- res = _d.sent();
1884
+ case 6:
1885
+ res = _a.sent();
1969
1886
  if (!res.ok) {
1970
1887
  throw new Error("HTTP " + res.status);
1971
1888
  }
1972
1889
  return [4 /*yield*/, res.json()];
1973
- case 4:
1974
- json = _d.sent();
1890
+ case 7:
1891
+ json = _a.sent();
1975
1892
  wsUrl = json === null || json === void 0 ? void 0 : json.rn_ws_url;
1976
1893
  if (!wsUrl || typeof wsUrl !== 'string') {
1977
1894
  throw new Error('No ws_url in response');
@@ -1981,69 +1898,74 @@
1981
1898
  .pipe(operators.take(1), operators.takeUntil(this.destroy$))
1982
1899
  .subscribe(function (roomUrl) { return __awaiter(_this, void 0, void 0, function () {
1983
1900
  var err_1;
1984
- return __generator(this, function (_b) {
1985
- switch (_b.label) {
1901
+ return __generator(this, function (_a) {
1902
+ switch (_a.label) {
1986
1903
  case 0:
1987
- _b.trys.push([0, 2, , 4]);
1988
- return [4 /*yield*/, this.onRoomCreated(roomUrl, micStream_1 !== null && micStream_1 !== void 0 ? micStream_1 : undefined)];
1904
+ _a.trys.push([0, 2, , 4]);
1905
+ return [4 /*yield*/, this.onRoomCreated(roomUrl)];
1989
1906
  case 1:
1990
- _b.sent();
1907
+ _a.sent();
1991
1908
  return [3 /*break*/, 4];
1992
1909
  case 2:
1993
- err_1 = _b.sent();
1910
+ err_1 = _a.sent();
1994
1911
  console.error('Daily join failed:', err_1);
1995
1912
  this.callStateSubject.next('ended');
1996
1913
  this.statusTextSubject.next('Connection failed');
1997
1914
  return [4 /*yield*/, this.disconnect()];
1998
1915
  case 3:
1999
- _b.sent();
1916
+ _a.sent();
2000
1917
  throw err_1;
2001
1918
  case 4: return [2 /*return*/];
2002
1919
  }
2003
1920
  });
2004
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); }));
2005
1929
  // Connect signaling WebSocket (no audio over WS)
2006
- return [4 /*yield*/, this.wsClient.connect(wsUrl)];
2007
- case 5:
2008
- // Connect signaling WebSocket (no audio over WS)
2009
- _d.sent();
2010
- return [3 /*break*/, 8];
2011
- case 6:
2012
- error_1 = _d.sent();
1930
+ this.wsClient.connect(wsUrl);
1931
+ return [3 /*break*/, 10];
1932
+ case 8:
1933
+ error_1 = _a.sent();
2013
1934
  console.error('Error connecting voice agent:', error_1);
2014
1935
  this.callStateSubject.next('ended');
2015
1936
  return [4 /*yield*/, this.disconnect()];
2016
- case 7:
2017
- _d.sent();
1937
+ case 9:
1938
+ _a.sent();
2018
1939
  this.statusTextSubject.next('Connection failed');
2019
1940
  throw error_1;
2020
- case 8: return [2 /*return*/];
1941
+ case 10: return [2 /*return*/];
2021
1942
  }
2022
1943
  });
2023
1944
  });
2024
1945
  };
2025
- VoiceAgentService.prototype.onRoomCreated = function (roomUrl, micStream) {
1946
+ VoiceAgentService.prototype.onRoomCreated = function (roomUrl) {
2026
1947
  return __awaiter(this, void 0, void 0, function () {
2027
1948
  var _this = this;
2028
- return __generator(this, function (_b) {
2029
- switch (_b.label) {
2030
- 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)];
2031
1954
  case 1:
2032
- _b.sent();
2033
- this.callSubscriptions.unsubscribe();
2034
- this.callSubscriptions = new rxjs.Subscription();
1955
+ // Connect Daily.js for WebRTC audio
1956
+ _a.sent();
2035
1957
  // Waveform: use local mic stream from Daily client
2036
- this.callSubscriptions.add(this.dailyClient.localStream$
1958
+ this.dailyClient.localStream$
2037
1959
  .pipe(operators.filter(function (s) { return s != null; }), operators.take(1))
2038
1960
  .subscribe(function (stream) {
2039
1961
  _this.audioAnalyzer.start(stream);
2040
- }));
2041
- this.callSubscriptions.add(this.dailyClient.userSpeaking$.subscribe(function (s) { return _this.isUserSpeakingSubject.next(s); }));
2042
- 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([
2043
1965
  this.dailyClient.speaking$,
2044
1966
  this.dailyClient.userSpeaking$,
2045
- ]).subscribe(function (_b) {
2046
- 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];
2047
1969
  var current = _this.callStateSubject.value;
2048
1970
  if (current === 'connecting' && !bot) {
2049
1971
  return;
@@ -2060,12 +1982,11 @@
2060
1982
  else if (bot) {
2061
1983
  _this.callStateSubject.next('talking');
2062
1984
  }
2063
- else {
2064
- // Between bot turns: stay on listening to avoid flicker via 'connected'
2065
- _this.callStateSubject.next('listening');
1985
+ else if (current === 'talking' || current === 'listening') {
1986
+ _this.callStateSubject.next('connected');
2066
1987
  }
2067
1988
  }));
2068
- 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); }));
2069
1990
  this.statusTextSubject.next('Connecting...');
2070
1991
  return [2 /*return*/];
2071
1992
  }
@@ -2074,18 +1995,16 @@
2074
1995
  };
2075
1996
  VoiceAgentService.prototype.disconnect = function () {
2076
1997
  return __awaiter(this, void 0, void 0, function () {
2077
- return __generator(this, function (_b) {
2078
- switch (_b.label) {
1998
+ return __generator(this, function (_a) {
1999
+ switch (_a.label) {
2079
2000
  case 0:
2080
- this.callSubscriptions.unsubscribe();
2081
- this.callSubscriptions = new rxjs.Subscription();
2082
2001
  this.stopDurationTimer();
2083
2002
  this.audioAnalyzer.stop();
2084
2003
  // Daily first, then WebSocket
2085
2004
  return [4 /*yield*/, this.dailyClient.disconnect()];
2086
2005
  case 1:
2087
2006
  // Daily first, then WebSocket
2088
- _b.sent();
2007
+ _a.sent();
2089
2008
  this.wsClient.disconnect();
2090
2009
  this.callStateSubject.next('ended');
2091
2010
  this.statusTextSubject.next('Call Ended');
@@ -2137,11 +2056,10 @@
2137
2056
  var VOICE_MODAL_CLOSE_CALLBACK = new i0.InjectionToken('VOICE_MODAL_CLOSE_CALLBACK');
2138
2057
 
2139
2058
  var VoiceAgentModalComponent = /** @class */ (function () {
2140
- function VoiceAgentModalComponent(voiceAgentService, audioAnalyzer, injector, platformId) {
2059
+ function VoiceAgentModalComponent(voiceAgentService, audioAnalyzer, injector) {
2141
2060
  this.voiceAgentService = voiceAgentService;
2142
2061
  this.audioAnalyzer = audioAnalyzer;
2143
2062
  this.injector = injector;
2144
- this.platformId = platformId;
2145
2063
  this.close = new i0.EventEmitter();
2146
2064
  this.apiKey = '';
2147
2065
  this.eventToken = '';
@@ -2153,8 +2071,6 @@
2153
2071
  this.usersApiUrl = '';
2154
2072
  this.injectedConfig = null;
2155
2073
  this.onCloseCallback = null;
2156
- /** Held until destroy; passed to Daily so we do not stop/re-acquire (avoids extra prompts on some browsers). */
2157
- this.warmMicStream = null;
2158
2074
  /** Hardcoded voice agent avatar (Nia). */
2159
2075
  this.displayAvatarUrl = 'https://www.jotform.com/uploads/mehmetkarakasli/form_files/1564593667676a8e85f23758.86945537_icon.png';
2160
2076
  this.callState = 'idle';
@@ -2170,108 +2086,63 @@
2170
2086
  this.isConnecting = false;
2171
2087
  }
2172
2088
  VoiceAgentModalComponent.prototype.ngOnInit = function () {
2173
- void this.bootstrap();
2174
- };
2175
- VoiceAgentModalComponent.prototype.bootstrap = function () {
2089
+ var _this = this;
2176
2090
  var _a, _b, _c, _d, _e, _f, _g, _h;
2177
- return __awaiter(this, void 0, void 0, function () {
2178
- var _this = this;
2179
- return __generator(this, function (_j) {
2180
- switch (_j.label) {
2181
- case 0:
2182
- this.injectedConfig = this.injector.get(VOICE_MODAL_CONFIG, null);
2183
- this.onCloseCallback = this.injector.get(VOICE_MODAL_CLOSE_CALLBACK, null);
2184
- if (this.injectedConfig) {
2185
- this.apiUrl = this.injectedConfig.apiUrl;
2186
- this.token = this.injectedConfig.token;
2187
- this.botId = this.injectedConfig.botId;
2188
- this.conversationId = this.injectedConfig.conversationId;
2189
- this.apiKey = (_a = this.injectedConfig.apiKey) !== null && _a !== void 0 ? _a : '';
2190
- this.eventToken = (_b = this.injectedConfig.eventToken) !== null && _b !== void 0 ? _b : '';
2191
- this.eventId = (_c = this.injectedConfig.eventId) !== null && _c !== void 0 ? _c : '';
2192
- this.eventUrl = (_d = this.injectedConfig.eventUrl) !== null && _d !== void 0 ? _d : '';
2193
- this.domainAuthority = (_e = this.injectedConfig.domainAuthority) !== null && _e !== void 0 ? _e : 'prod-lite';
2194
- this.agentName = (_f = this.injectedConfig.agentName) !== null && _f !== void 0 ? _f : this.agentName;
2195
- this.agentRole = (_g = this.injectedConfig.agentRole) !== null && _g !== void 0 ? _g : this.agentRole;
2196
- this.agentAvatar = this.injectedConfig.agentAvatar;
2197
- this.usersApiUrl = (_h = this.injectedConfig.usersApiUrl) !== null && _h !== void 0 ? _h : this.usersApiUrl;
2198
- }
2199
- // Subscribe to observables
2200
- this.subscriptions.push(this.voiceAgentService.callState$.subscribe(function (state) {
2201
- _this.callState = state;
2202
- _this.isSpeaking = state === 'talking';
2203
- if (state === 'listening' || state === 'talking') {
2204
- _this.hasLeftConnectedOnce = true;
2205
- }
2206
- if (state === 'idle' || state === 'ended') {
2207
- _this.hasLeftConnectedOnce = false;
2208
- }
2209
- }));
2210
- this.subscriptions.push(this.voiceAgentService.statusText$.subscribe(function (text) {
2211
- _this.statusText = text;
2212
- }));
2213
- this.subscriptions.push(this.voiceAgentService.duration$.subscribe(function (duration) {
2214
- _this.duration = duration;
2215
- }));
2216
- this.subscriptions.push(this.voiceAgentService.isMicMuted$.subscribe(function (muted) {
2217
- _this.isMicMuted = muted;
2218
- }));
2219
- this.subscriptions.push(this.voiceAgentService.isUserSpeaking$.subscribe(function (speaking) {
2220
- _this.isUserSpeaking = speaking;
2221
- }));
2222
- this.subscriptions.push(this.voiceAgentService.audioLevels$.subscribe(function (levels) {
2223
- _this.audioLevels = levels;
2224
- }));
2225
- this.voiceAgentService.resetToIdle();
2226
- return [4 /*yield*/, this.startCall()];
2227
- case 1:
2228
- _j.sent();
2229
- return [2 /*return*/];
2230
- }
2231
- });
2232
- });
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();
2233
2138
  };
2234
2139
  VoiceAgentModalComponent.prototype.ngOnDestroy = function () {
2235
- var _this = this;
2236
2140
  this.subscriptions.forEach(function (sub) { return sub.unsubscribe(); });
2237
- void this.disconnect().finally(function () {
2238
- var _a;
2239
- (_a = _this.warmMicStream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(function (t) { return t.stop(); });
2240
- _this.warmMicStream = null;
2241
- });
2242
- };
2243
- /** Ensures a live mic stream for this call (re-acquire after Daily stops tracks on hang-up). */
2244
- VoiceAgentModalComponent.prototype.ensureMicForCall = function () {
2245
- var _a;
2246
- return __awaiter(this, void 0, void 0, function () {
2247
- var _j, _b_1;
2248
- return __generator(this, function (_k) {
2249
- switch (_k.label) {
2250
- case 0:
2251
- if (!common.isPlatformBrowser(this.platformId))
2252
- return [2 /*return*/, undefined];
2253
- if ((_a = this.warmMicStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().some(function (t) { return t.readyState === 'live'; })) {
2254
- return [2 /*return*/, this.warmMicStream];
2255
- }
2256
- _k.label = 1;
2257
- case 1:
2258
- _k.trys.push([1, 3, , 4]);
2259
- _j = this;
2260
- return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
2261
- case 2:
2262
- _j.warmMicStream = _k.sent();
2263
- return [2 /*return*/, this.warmMicStream];
2264
- case 3:
2265
- _b_1 = _k.sent();
2266
- return [2 /*return*/, undefined];
2267
- case 4: return [2 /*return*/];
2268
- }
2269
- });
2270
- });
2141
+ this.disconnect();
2271
2142
  };
2272
2143
  VoiceAgentModalComponent.prototype.startCall = function () {
2273
2144
  return __awaiter(this, void 0, void 0, function () {
2274
- var mic, error_1;
2145
+ var error_1;
2275
2146
  return __generator(this, function (_j) {
2276
2147
  switch (_j.label) {
2277
2148
  case 0:
@@ -2280,22 +2151,19 @@
2280
2151
  this.isConnecting = true;
2281
2152
  _j.label = 1;
2282
2153
  case 1:
2283
- _j.trys.push([1, 4, 5, 6]);
2284
- 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)];
2285
2156
  case 2:
2286
- mic = _j.sent();
2287
- 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)];
2288
- case 3:
2289
2157
  _j.sent();
2290
- return [3 /*break*/, 6];
2291
- case 4:
2158
+ return [3 /*break*/, 5];
2159
+ case 3:
2292
2160
  error_1 = _j.sent();
2293
2161
  console.error('Failed to connect voice agent:', error_1);
2294
- return [3 /*break*/, 6];
2295
- case 5:
2162
+ return [3 /*break*/, 5];
2163
+ case 4:
2296
2164
  this.isConnecting = false;
2297
2165
  return [7 /*endfinally*/];
2298
- case 6: return [2 /*return*/];
2166
+ case 5: return [2 /*return*/];
2299
2167
  }
2300
2168
  });
2301
2169
  });
@@ -2340,7 +2208,7 @@
2340
2208
  /** Call Again: reset to idle then start a new call. */
2341
2209
  VoiceAgentModalComponent.prototype.callAgain = function () {
2342
2210
  this.voiceAgentService.resetToIdle();
2343
- void this.startCall();
2211
+ this.startCall();
2344
2212
  };
2345
2213
  /** Back to Chat: close modal and disconnect. */
2346
2214
  VoiceAgentModalComponent.prototype.backToChat = function () {
@@ -2368,8 +2236,7 @@
2368
2236
  VoiceAgentModalComponent.ctorParameters = function () { return [
2369
2237
  { type: VoiceAgentService },
2370
2238
  { type: AudioAnalyzerService },
2371
- { type: i0.Injector },
2372
- { type: Object, decorators: [{ type: i0.Inject, args: [i0.PLATFORM_ID,] }] }
2239
+ { type: i0.Injector }
2373
2240
  ]; };
2374
2241
  VoiceAgentModalComponent.propDecorators = {
2375
2242
  close: [{ type: i0.Output }],