@hivegpt/hiveai-angular 0.0.583 → 0.0.584

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.
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/cdk/overlay'), require('@angular/cdk/portal'), require('@angular/common/http'), require('@angular/core'), require('@angular/platform-browser'), require('rxjs'), require('rxjs/operators'), require('@angular/common'), require('ngx-socket-io'), require('@angular/forms'), require('microsoft-cognitiveservices-speech-sdk'), require('marked'), require('@angular/material/icon'), require('@angular/material/sidenav'), require('ngx-quill')) :
3
- typeof define === 'function' && define.amd ? define('@hivegpt/hiveai-angular', ['exports', '@angular/cdk/overlay', '@angular/cdk/portal', '@angular/common/http', '@angular/core', '@angular/platform-browser', 'rxjs', 'rxjs/operators', '@angular/common', 'ngx-socket-io', '@angular/forms', 'microsoft-cognitiveservices-speech-sdk', 'marked', '@angular/material/icon', '@angular/material/sidenav', 'ngx-quill'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.hivegpt = global.hivegpt || {}, global.hivegpt["hiveai-angular"] = {}), global.ng.cdk.overlay, global.ng.cdk.portal, global.ng.common.http, global.ng.core, global.ng.platformBrowser, global.rxjs, global.rxjs.operators, global.ng.common, global.ngxSocketIo, global.ng.forms, global.SpeechSDK, global.marked, global.ng.material.icon, global.ng.material.sidenav, global.ngxQuill));
5
- })(this, (function (exports, overlay, portal, i1, i0, platformBrowser, rxjs, operators, common, ngxSocketIo, forms, SpeechSDK, marked, icon, sidenav, ngxQuill) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/cdk/overlay'), require('@angular/cdk/portal'), require('@angular/common/http'), require('@angular/core'), require('@angular/platform-browser'), require('rxjs'), require('rxjs/operators'), require('@angular/common'), require('ngx-socket-io'), require('@angular/forms'), require('microsoft-cognitiveservices-speech-sdk'), require('marked'), require('@pipecat-ai/client-js'), require('@pipecat-ai/websocket-transport'), require('@angular/material/icon'), require('@angular/material/sidenav'), require('ngx-quill')) :
3
+ typeof define === 'function' && define.amd ? define('@hivegpt/hiveai-angular', ['exports', '@angular/cdk/overlay', '@angular/cdk/portal', '@angular/common/http', '@angular/core', '@angular/platform-browser', 'rxjs', 'rxjs/operators', '@angular/common', 'ngx-socket-io', '@angular/forms', 'microsoft-cognitiveservices-speech-sdk', 'marked', '@pipecat-ai/client-js', '@pipecat-ai/websocket-transport', '@angular/material/icon', '@angular/material/sidenav', 'ngx-quill'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.hivegpt = global.hivegpt || {}, global.hivegpt["hiveai-angular"] = {}), global.ng.cdk.overlay, global.ng.cdk.portal, global.ng.common.http, global.ng.core, global.ng.platformBrowser, global.rxjs, global.rxjs.operators, global.ng.common, global.ngxSocketIo, global.ng.forms, global.SpeechSDK, global.marked, global.clientJs, global.websocketTransport, global.ng.material.icon, global.ng.material.sidenav, global.ngxQuill));
5
+ })(this, (function (exports, overlay, portal, i1, i0, platformBrowser, rxjs, operators, common, ngxSocketIo, forms, SpeechSDK, marked, clientJs, websocketTransport, icon, sidenav, ngxQuill) { 'use strict';
6
6
 
7
7
  function _interopNamespace(e) {
8
8
  if (e && e.__esModule) return e;
@@ -1339,237 +1339,24 @@
1339
1339
  ];
1340
1340
 
1341
1341
  /**
1342
- * Native WebSocket client for voice session (signaling, transcripts, speaking hints).
1343
- * CRITICAL: Uses native WebSocket only. NO Socket.IO, NO ngx-socket-io.
1342
+ * Voice agent orchestrator using the official PipecatClient SDK.
1344
1343
  *
1345
- * Connects to `ws_url` from `POST {baseUrl}/ai/ask-voice-socket`.
1346
- * Parses JSON messages for transcripts and optional assistant/user speaking flags.
1347
- */
1348
- var WebSocketVoiceClientService = /** @class */ (function () {
1349
- function WebSocketVoiceClientService(ngZone) {
1350
- this.ngZone = ngZone;
1351
- this.ws = null;
1352
- /** True when {@link disconnect} initiated the close (not counted as remote close). */
1353
- this.closeInitiatedByClient = false;
1354
- this.openedSubject = new rxjs.Subject();
1355
- this.remoteCloseSubject = new rxjs.Subject();
1356
- this.userTranscriptSubject = new rxjs.Subject();
1357
- this.botTranscriptSubject = new rxjs.Subject();
1358
- this.assistantSpeakingSubject = new rxjs.Subject();
1359
- this.serverUserSpeakingSubject = new rxjs.Subject();
1360
- this.audioChunkSubject = new rxjs.Subject();
1361
- /** Fires once each time the WebSocket reaches OPEN. */
1362
- this.opened$ = this.openedSubject.asObservable();
1363
- /** Fires when the socket closes without a client-initiated {@link disconnect}. */
1364
- this.remoteClose$ = this.remoteCloseSubject.asObservable();
1365
- this.userTranscript$ = this.userTranscriptSubject.asObservable();
1366
- this.botTranscript$ = this.botTranscriptSubject.asObservable();
1367
- /** Assistant/bot speaking, when the server sends explicit events (see {@link handleJsonMessage}). */
1368
- this.assistantSpeaking$ = this.assistantSpeakingSubject.asObservable();
1369
- /** User speaking from server-side VAD, if provided. */
1370
- this.serverUserSpeaking$ = this.serverUserSpeakingSubject.asObservable();
1371
- /** Binary audio frames from server (when backend streams bot audio over WS). */
1372
- this.audioChunk$ = this.audioChunkSubject.asObservable();
1373
- }
1374
- WebSocketVoiceClientService.prototype.connect = function (wsUrl) {
1375
- var _this = this;
1376
- var _a;
1377
- if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
1378
- return;
1379
- }
1380
- if (this.ws) {
1381
- this.closeInitiatedByClient = true;
1382
- this.ws.close();
1383
- }
1384
- try {
1385
- var socket_1 = new WebSocket(wsUrl);
1386
- this.ws = socket_1;
1387
- socket_1.onopen = function () {
1388
- if (_this.ws !== socket_1)
1389
- return;
1390
- _this.ngZone.run(function () { return _this.openedSubject.next(); });
1391
- };
1392
- socket_1.onmessage = function (event) {
1393
- if (_this.ws !== socket_1)
1394
- return;
1395
- void _this.handleIncomingMessage(event.data);
1396
- };
1397
- socket_1.onerror = function () {
1398
- _this.ngZone.run(function () {
1399
- if (_this.ws === socket_1 && socket_1.readyState !== WebSocket.CLOSED) {
1400
- socket_1.close();
1401
- }
1402
- });
1403
- };
1404
- socket_1.onclose = function () {
1405
- if (_this.ws === socket_1) {
1406
- _this.ws = null;
1407
- }
1408
- var client = _this.closeInitiatedByClient;
1409
- _this.closeInitiatedByClient = false;
1410
- if (!client) {
1411
- _this.ngZone.run(function () { return _this.remoteCloseSubject.next(); });
1412
- }
1413
- };
1414
- }
1415
- catch (err) {
1416
- console.error('WebSocketVoiceClient: connect failed', err);
1417
- this.ws = null;
1418
- throw err;
1419
- }
1420
- };
1421
- WebSocketVoiceClientService.prototype.handleIncomingMessage = function (payload) {
1422
- return __awaiter(this, void 0, void 0, function () {
1423
- var ab;
1424
- return __generator(this, function (_b) {
1425
- switch (_b.label) {
1426
- case 0:
1427
- if (typeof payload === 'string') {
1428
- this.handleJsonString(payload);
1429
- return [2 /*return*/];
1430
- }
1431
- if (payload instanceof ArrayBuffer) {
1432
- this.handleBinaryMessage(payload);
1433
- return [2 /*return*/];
1434
- }
1435
- if (!(payload instanceof Blob)) return [3 /*break*/, 2];
1436
- return [4 /*yield*/, payload.arrayBuffer()];
1437
- case 1:
1438
- ab = _b.sent();
1439
- this.handleBinaryMessage(ab);
1440
- _b.label = 2;
1441
- case 2: return [2 /*return*/];
1442
- }
1443
- });
1444
- });
1445
- };
1446
- WebSocketVoiceClientService.prototype.handleJsonString = function (jsonText) {
1447
- var _this = this;
1448
- try {
1449
- var msg_1 = JSON.parse(jsonText);
1450
- this.ngZone.run(function () { return _this.handleJsonMessage(msg_1); });
1451
- }
1452
- catch (_a) {
1453
- // Ignore non-JSON
1454
- }
1455
- };
1456
- WebSocketVoiceClientService.prototype.handleBinaryMessage = function (buffer) {
1457
- var _this = this;
1458
- // Some backends wrap JSON events inside binary WS frames.
1459
- var maybeText = this.tryDecodeUtf8(buffer);
1460
- if (maybeText !== null) {
1461
- this.handleJsonString(maybeText);
1462
- return;
1463
- }
1464
- // Otherwise treat binary as streamed assistant audio.
1465
- this.ngZone.run(function () { return _this.audioChunkSubject.next(buffer); });
1466
- };
1467
- WebSocketVoiceClientService.prototype.tryDecodeUtf8 = function (buffer) {
1468
- try {
1469
- var text = new TextDecoder('utf-8', { fatal: true }).decode(buffer);
1470
- var trimmed = text.trim();
1471
- if (!trimmed || (trimmed[0] !== '{' && trimmed[0] !== '[')) {
1472
- return null;
1473
- }
1474
- return trimmed;
1475
- }
1476
- catch (_a) {
1477
- return null;
1478
- }
1479
- };
1480
- WebSocketVoiceClientService.prototype.handleJsonMessage = function (msg) {
1481
- var type = msg.type;
1482
- var typeStr = typeof type === 'string' ? type : '';
1483
- if (typeStr === 'session_ready' || typeStr === 'connected' || typeStr === 'voice_session_started') {
1484
- return;
1485
- }
1486
- if (typeStr === 'assistant_speaking' ||
1487
- typeStr === 'bot_speaking') {
1488
- if (msg.active === true || msg.speaking === true) {
1489
- this.assistantSpeakingSubject.next(true);
1490
- }
1491
- else if (msg.active === false || msg.speaking === false) {
1492
- this.assistantSpeakingSubject.next(false);
1493
- }
1494
- return;
1495
- }
1496
- if (typeStr === 'user_speaking') {
1497
- if (msg.active === true || msg.speaking === true) {
1498
- this.serverUserSpeakingSubject.next(true);
1499
- }
1500
- else if (msg.active === false || msg.speaking === false) {
1501
- this.serverUserSpeakingSubject.next(false);
1502
- }
1503
- return;
1504
- }
1505
- if (typeStr === 'input_audio_buffer.speech_started') {
1506
- this.serverUserSpeakingSubject.next(true);
1507
- return;
1508
- }
1509
- if (typeStr === 'input_audio_buffer.speech_stopped') {
1510
- this.serverUserSpeakingSubject.next(false);
1511
- return;
1512
- }
1513
- if (typeStr === 'response.audio.delta') {
1514
- this.assistantSpeakingSubject.next(true);
1515
- return;
1516
- }
1517
- if (typeStr === 'response.audio.done' ||
1518
- typeStr === 'response.output_audio.done') {
1519
- this.assistantSpeakingSubject.next(false);
1520
- return;
1521
- }
1522
- if (typeStr === 'user_transcript' && typeof msg.text === 'string') {
1523
- this.userTranscriptSubject.next({
1524
- text: msg.text,
1525
- final: msg.final === true,
1526
- });
1527
- return;
1528
- }
1529
- if (typeStr === 'bot_transcript' && typeof msg.text === 'string') {
1530
- this.botTranscriptSubject.next(msg.text);
1531
- }
1532
- };
1533
- WebSocketVoiceClientService.prototype.disconnect = function () {
1534
- if (!this.ws) {
1535
- return;
1536
- }
1537
- this.closeInitiatedByClient = true;
1538
- this.ws.close();
1539
- };
1540
- Object.defineProperty(WebSocketVoiceClientService.prototype, "isConnected", {
1541
- get: function () {
1542
- var _a;
1543
- return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN;
1544
- },
1545
- enumerable: false,
1546
- configurable: true
1547
- });
1548
- return WebSocketVoiceClientService;
1549
- }());
1550
- WebSocketVoiceClientService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function WebSocketVoiceClientService_Factory() { return new WebSocketVoiceClientService(i0__namespace.ɵɵinject(i0__namespace.NgZone)); }, token: WebSocketVoiceClientService, providedIn: "root" });
1551
- WebSocketVoiceClientService.decorators = [
1552
- { type: i0.Injectable, args: [{
1553
- providedIn: 'root',
1554
- },] }
1555
- ];
1556
- WebSocketVoiceClientService.ctorParameters = function () { return [
1557
- { type: i0.NgZone }
1558
- ]; };
1559
-
1560
- /**
1561
- * Voice agent orchestrator: single WebSocket (`ws_url` from POST /ai/ask-voice-socket)
1562
- * for session events, transcripts, and optional speaking hints; local mic for capture
1563
- * and waveform only (no Daily/WebRTC room).
1344
+ * Audio flow (mirrors the React reference implementation):
1345
+ * - Local mic: acquired by PipecatClient.initDevices(); local track fed to
1346
+ * AudioAnalyzerService for waveform visualisation.
1347
+ * - Bot audio: received as a MediaStreamTrack via RTVIEvent.TrackStarted,
1348
+ * played through a hidden <audio> element.
1349
+ * - All binary protobuf framing / RTVI protocol handled by
1350
+ * @pipecat-ai/client-js + @pipecat-ai/websocket-transport.
1564
1351
  */
1565
1352
  var VoiceAgentService = /** @class */ (function () {
1566
- function VoiceAgentService(audioAnalyzer, wsClient, platformTokenRefresh,
1353
+ function VoiceAgentService(audioAnalyzer, platformTokenRefresh, ngZone,
1567
1354
  /** `Object` not `object` — ngc metadata collection rejects the `object` type in DI params. */
1568
1355
  platformId) {
1569
1356
  var _this = this;
1570
1357
  this.audioAnalyzer = audioAnalyzer;
1571
- this.wsClient = wsClient;
1572
1358
  this.platformTokenRefresh = platformTokenRefresh;
1359
+ this.ngZone = ngZone;
1573
1360
  this.platformId = platformId;
1574
1361
  this.callStateSubject = new rxjs.BehaviorSubject('idle');
1575
1362
  this.statusTextSubject = new rxjs.BehaviorSubject('');
@@ -1581,11 +1368,8 @@
1581
1368
  this.botTranscriptSubject = new rxjs.Subject();
1582
1369
  this.callStartTime = 0;
1583
1370
  this.durationInterval = null;
1584
- this.localMicStream = null;
1585
- this.remoteAudioContext = null;
1586
- this.pendingRemoteAudio = [];
1587
- this.remoteAudioPlaying = false;
1588
- this.endCall$ = new rxjs.Subject();
1371
+ this.pcClient = null;
1372
+ this.botAudioElement = null;
1589
1373
  this.subscriptions = new rxjs.Subscription();
1590
1374
  this.destroy$ = new rxjs.Subject();
1591
1375
  this.callState$ = this.callStateSubject.asObservable();
@@ -1597,123 +1381,131 @@
1597
1381
  this.userTranscript$ = this.userTranscriptSubject.asObservable();
1598
1382
  this.botTranscript$ = this.botTranscriptSubject.asObservable();
1599
1383
  this.subscriptions.add(this.audioAnalyzer.audioLevels$.subscribe(function (levels) { return _this.audioLevelsSubject.next(levels); }));
1600
- this.subscriptions.add(this.wsClient.remoteClose$
1601
- .pipe(operators.takeUntil(this.destroy$))
1602
- .subscribe(function () { return void _this.handleRemoteClose(); }));
1603
- this.subscriptions.add(this.wsClient.audioChunk$
1604
- .pipe(operators.takeUntil(this.destroy$))
1605
- .subscribe(function (chunk) { return _this.enqueueRemoteAudio(chunk); }));
1606
1384
  }
1607
1385
  VoiceAgentService.prototype.ngOnDestroy = function () {
1608
1386
  this.destroy$.next();
1609
1387
  this.subscriptions.unsubscribe();
1610
- this.disconnect();
1388
+ void this.disconnect();
1611
1389
  };
1612
- /** Reset to idle state (e.g. when modal opens so user can click Start Call). */
1390
+ /** Reset to idle (e.g. when modal re-opens so user can click Start Call). */
1613
1391
  VoiceAgentService.prototype.resetToIdle = function () {
1614
1392
  if (this.callStateSubject.value === 'idle')
1615
1393
  return;
1616
- this.endCall$.next();
1617
- this.stopDurationTimer();
1618
- this.callStartTime = 0;
1619
- this.audioAnalyzer.stop();
1620
- this.stopLocalMic();
1621
- this.resetRemoteAudioPlayback();
1622
- this.wsClient.disconnect();
1394
+ void this.disconnect();
1623
1395
  this.callStateSubject.next('idle');
1624
1396
  this.statusTextSubject.next('');
1625
1397
  this.durationSubject.next('0:00');
1626
1398
  };
1627
1399
  VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl) {
1628
1400
  return __awaiter(this, void 0, void 0, function () {
1629
- var accessToken, ensured, e_1, baseUrl, postUrl, headers, res, json, wsUrl, untilCallEnds$, error_1;
1401
+ var accessToken, ensured, e_1, baseUrl, pcClient, requestHeaders, error_1;
1630
1402
  var _this = this;
1631
- return __generator(this, function (_b) {
1632
- switch (_b.label) {
1403
+ return __generator(this, function (_d) {
1404
+ switch (_d.label) {
1633
1405
  case 0:
1634
1406
  if (this.callStateSubject.value !== 'idle') {
1635
- console.warn('Call already in progress');
1407
+ console.warn('[HiveGpt Voice] Call already in progress');
1636
1408
  return [2 /*return*/];
1637
1409
  }
1638
- _b.label = 1;
1410
+ _d.label = 1;
1639
1411
  case 1:
1640
- _b.trys.push([1, 8, , 10]);
1412
+ _d.trys.push([1, 8, , 10]);
1641
1413
  this.callStateSubject.next('connecting');
1642
1414
  this.statusTextSubject.next('Connecting...');
1643
1415
  accessToken = token;
1644
1416
  if (!(usersApiUrl && common.isPlatformBrowser(this.platformId))) return [3 /*break*/, 5];
1645
- _b.label = 2;
1417
+ _d.label = 2;
1646
1418
  case 2:
1647
- _b.trys.push([2, 4, , 5]);
1419
+ _d.trys.push([2, 4, , 5]);
1648
1420
  return [4 /*yield*/, this.platformTokenRefresh
1649
1421
  .ensureValidAccessToken(token, usersApiUrl)
1650
1422
  .pipe(operators.take(1))
1651
1423
  .toPromise()];
1652
1424
  case 3:
1653
- ensured = _b.sent();
1654
- if (ensured === null || ensured === void 0 ? void 0 : ensured.accessToken) {
1425
+ ensured = _d.sent();
1426
+ if (ensured === null || ensured === void 0 ? void 0 : ensured.accessToken)
1655
1427
  accessToken = ensured.accessToken;
1656
- }
1657
1428
  return [3 /*break*/, 5];
1658
1429
  case 4:
1659
- e_1 = _b.sent();
1660
- console.warn('[HiveGpt Voice] Token refresh before connect failed', e_1);
1430
+ e_1 = _d.sent();
1431
+ console.warn('[HiveGpt Voice] Token refresh failed', e_1);
1661
1432
  return [3 /*break*/, 5];
1662
1433
  case 5:
1663
1434
  baseUrl = apiUrl.replace(/\/$/, '');
1664
- postUrl = baseUrl + "/ai/ask-voice-socket";
1665
- headers = {
1666
- 'Content-Type': 'application/json',
1667
- Authorization: "Bearer " + accessToken,
1668
- 'x-api-key': apiKey,
1669
- 'hive-bot-id': botId,
1670
- 'domain-authority': domainAuthority,
1671
- eventUrl: eventUrl,
1672
- eventId: eventId,
1673
- eventToken: eventToken,
1674
- 'ngrok-skip-browser-warning': 'true',
1675
- };
1676
- return [4 /*yield*/, fetch(postUrl, {
1677
- method: 'POST',
1678
- headers: headers,
1679
- body: JSON.stringify({
1435
+ pcClient = new clientJs.PipecatClient({
1436
+ transport: new websocketTransport.WebSocketTransport(),
1437
+ enableMic: true,
1438
+ enableCam: false,
1439
+ callbacks: {
1440
+ onConnected: function () { return _this.ngZone.run(function () { return _this.onPipecatConnected(); }); },
1441
+ onDisconnected: function () { return _this.ngZone.run(function () { return _this.onPipecatDisconnected(); }); },
1442
+ onBotReady: function () { return _this.ngZone.run(function () { return _this.onBotReady(); }); },
1443
+ onUserTranscript: function (data) { return _this.ngZone.run(function () { return _this.userTranscriptSubject.next({ text: data.text, final: !!data.final }); }); },
1444
+ onBotTranscript: function (data) { return _this.ngZone.run(function () { return _this.botTranscriptSubject.next(data.text); }); },
1445
+ onError: function (err) {
1446
+ _this.ngZone.run(function () {
1447
+ console.error('[HiveGpt Voice] PipecatClient error', err);
1448
+ _this.callStateSubject.next('ended');
1449
+ _this.statusTextSubject.next('Connection failed');
1450
+ });
1451
+ },
1452
+ },
1453
+ });
1454
+ this.pcClient = pcClient;
1455
+ // Bot audio arrives as a MediaStreamTrack — wire to a hidden <audio> element
1456
+ pcClient.on(clientJs.RTVIEvent.TrackStarted, function (track, participant) {
1457
+ if (!(participant === null || participant === void 0 ? void 0 : participant.local) && track.kind === 'audio') {
1458
+ _this.ngZone.run(function () { return _this.setupBotAudioTrack(track); });
1459
+ }
1460
+ });
1461
+ // Speaking state comes straight from RTVI events
1462
+ pcClient.on(clientJs.RTVIEvent.BotStartedSpeaking, function () { return _this.ngZone.run(function () { return _this.onBotStartedSpeaking(); }); });
1463
+ pcClient.on(clientJs.RTVIEvent.BotStoppedSpeaking, function () { return _this.ngZone.run(function () { return _this.onBotStoppedSpeaking(); }); });
1464
+ pcClient.on(clientJs.RTVIEvent.UserStartedSpeaking, function () { return _this.ngZone.run(function () {
1465
+ _this.isUserSpeakingSubject.next(true);
1466
+ _this.callStateSubject.next('listening');
1467
+ }); });
1468
+ pcClient.on(clientJs.RTVIEvent.UserStoppedSpeaking, function () { return _this.ngZone.run(function () {
1469
+ _this.isUserSpeakingSubject.next(false);
1470
+ if (_this.callStateSubject.value === 'listening') {
1471
+ _this.callStateSubject.next('connected');
1472
+ }
1473
+ }); });
1474
+ // Acquire mic (triggers browser permission prompt)
1475
+ return [4 /*yield*/, pcClient.initDevices()];
1476
+ case 6:
1477
+ // Acquire mic (triggers browser permission prompt)
1478
+ _d.sent();
1479
+ requestHeaders = new Headers();
1480
+ requestHeaders.append('Authorization', "Bearer " + accessToken);
1481
+ requestHeaders.append('x-api-key', apiKey);
1482
+ requestHeaders.append('hive-bot-id', botId);
1483
+ requestHeaders.append('domain-authority', domainAuthority);
1484
+ requestHeaders.append('eventUrl', eventUrl);
1485
+ requestHeaders.append('eventId', eventId);
1486
+ requestHeaders.append('eventToken', eventToken);
1487
+ requestHeaders.append('ngrok-skip-browser-warning', 'true');
1488
+ // POST to /ai/ask-voice-socket → receives { ws_url } → WebSocketTransport connects
1489
+ return [4 /*yield*/, pcClient.startBotAndConnect({
1490
+ endpoint: baseUrl + "/ai/ask-voice-socket",
1491
+ headers: requestHeaders,
1492
+ requestData: {
1680
1493
  bot_id: botId,
1681
1494
  conversation_id: conversationId,
1682
1495
  voice: 'alloy',
1683
- }),
1496
+ },
1684
1497
  })];
1685
- case 6:
1686
- res = _b.sent();
1687
- if (!res.ok) {
1688
- throw new Error("HTTP " + res.status);
1689
- }
1690
- return [4 /*yield*/, res.json()];
1691
1498
  case 7:
1692
- json = _b.sent();
1693
- wsUrl = (typeof (json === null || json === void 0 ? void 0 : json.ws_url) === 'string' && json.ws_url) ||
1694
- (typeof (json === null || json === void 0 ? void 0 : json.rn_ws_url) === 'string' && json.rn_ws_url);
1695
- if (!wsUrl) {
1696
- throw new Error('No ws_url in response');
1697
- }
1698
- untilCallEnds$ = rxjs.merge(this.destroy$, this.endCall$);
1699
- this.subscriptions.add(this.wsClient.userTranscript$
1700
- .pipe(operators.takeUntil(untilCallEnds$))
1701
- .subscribe(function (t) { return _this.userTranscriptSubject.next(t); }));
1702
- this.subscriptions.add(this.wsClient.botTranscript$
1703
- .pipe(operators.takeUntil(untilCallEnds$))
1704
- .subscribe(function (t) { return _this.botTranscriptSubject.next(t); }));
1705
- this.subscriptions.add(this.wsClient.opened$
1706
- .pipe(operators.takeUntil(untilCallEnds$), operators.take(1))
1707
- .subscribe(function () { return void _this.onWebsocketOpened(); }));
1708
- this.wsClient.connect(wsUrl);
1499
+ // POST to /ai/ask-voice-socket → receives { ws_url } → WebSocketTransport connects
1500
+ _d.sent();
1709
1501
  return [3 /*break*/, 10];
1710
1502
  case 8:
1711
- error_1 = _b.sent();
1712
- console.error('Error connecting voice agent:', error_1);
1503
+ error_1 = _d.sent();
1504
+ console.error('[HiveGpt Voice] connect failed', error_1);
1713
1505
  this.callStateSubject.next('ended');
1714
- return [4 /*yield*/, this.disconnect()];
1506
+ return [4 /*yield*/, this.cleanupPipecatClient()];
1715
1507
  case 9:
1716
- _b.sent();
1508
+ _d.sent();
1717
1509
  this.statusTextSubject.next('Connection failed');
1718
1510
  throw error_1;
1719
1511
  case 10: return [2 /*return*/];
@@ -1721,249 +1513,138 @@
1721
1513
  });
1722
1514
  });
1723
1515
  };
1724
- VoiceAgentService.prototype.onWebsocketOpened = function () {
1725
- return __awaiter(this, void 0, void 0, function () {
1726
- var err_1;
1727
- return __generator(this, function (_b) {
1728
- switch (_b.label) {
1729
- case 0:
1730
- if (this.callStateSubject.value !== 'connecting') {
1731
- return [2 /*return*/];
1732
- }
1733
- _b.label = 1;
1734
- case 1:
1735
- _b.trys.push([1, 3, , 5]);
1736
- return [4 /*yield*/, this.startLocalMic()];
1737
- case 2:
1738
- _b.sent();
1739
- this.statusTextSubject.next('Connected');
1740
- this.callStateSubject.next('connected');
1741
- this.wireSpeakingState();
1742
- return [3 /*break*/, 5];
1743
- case 3:
1744
- err_1 = _b.sent();
1745
- console.error('[HiveGpt Voice] Mic or session setup failed', err_1);
1746
- this.callStateSubject.next('ended');
1747
- this.statusTextSubject.next('Microphone unavailable');
1748
- return [4 /*yield*/, this.disconnect()];
1749
- case 4:
1750
- _b.sent();
1751
- return [3 /*break*/, 5];
1752
- case 5: return [2 /*return*/];
1753
- }
1754
- });
1755
- });
1756
- };
1757
- VoiceAgentService.prototype.wireSpeakingState = function () {
1758
- var _this = this;
1759
- var untilCallEnds$ = rxjs.merge(this.destroy$, this.endCall$);
1760
- var transcriptDrivenAssistant$ = this.wsClient.botTranscript$.pipe(operators.switchMap(function () { return rxjs.concat(rxjs.of(true), rxjs.timer(800).pipe(operators.map(function () { return false; }))); }), operators.distinctUntilChanged());
1761
- var assistantTalking$ = rxjs.merge(this.wsClient.assistantSpeaking$, transcriptDrivenAssistant$).pipe(operators.distinctUntilChanged(), operators.startWith(false));
1762
- var userTalking$ = rxjs.combineLatest([
1763
- this.audioAnalyzer.isUserSpeaking$,
1764
- this.wsClient.serverUserSpeaking$.pipe(operators.startWith(false)),
1765
- ]).pipe(operators.map(function (_b) {
1766
- var _c = __read(_b, 2), local = _c[0], server = _c[1];
1767
- return local || server;
1768
- }), operators.distinctUntilChanged(), operators.startWith(false));
1769
- this.subscriptions.add(rxjs.combineLatest([assistantTalking$, userTalking$])
1770
- .pipe(operators.takeUntil(untilCallEnds$))
1771
- .subscribe(function (_b) {
1772
- var _c = __read(_b, 2), bot = _c[0], user = _c[1];
1773
- var current = _this.callStateSubject.value;
1774
- if (user) {
1775
- _this.isUserSpeakingSubject.next(true);
1776
- _this.callStateSubject.next('listening');
1777
- }
1778
- else {
1779
- _this.isUserSpeakingSubject.next(false);
1780
- }
1781
- if (user) {
1782
- return;
1783
- }
1784
- if (bot) {
1785
- if (_this.callStartTime === 0) {
1786
- _this.callStartTime = Date.now();
1787
- _this.startDurationTimer();
1788
- }
1789
- _this.callStateSubject.next('talking');
1790
- }
1791
- else if (current === 'talking' || current === 'listening') {
1792
- _this.callStateSubject.next('connected');
1793
- }
1794
- }));
1516
+ VoiceAgentService.prototype.onPipecatConnected = function () {
1517
+ this.callStateSubject.next('connected');
1518
+ this.statusTextSubject.next('Connected');
1519
+ this.isMicMutedSubject.next(false);
1520
+ this.startLocalMicAnalyzer();
1795
1521
  };
1796
- VoiceAgentService.prototype.startLocalMic = function () {
1797
- return __awaiter(this, void 0, void 0, function () {
1798
- var stream, track;
1799
- return __generator(this, function (_b) {
1800
- switch (_b.label) {
1801
- case 0:
1802
- this.stopLocalMic();
1803
- return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
1804
- case 1:
1805
- stream = _b.sent();
1806
- track = stream.getAudioTracks()[0];
1807
- if (!track) {
1808
- stream.getTracks().forEach(function (t) { return t.stop(); });
1809
- throw new Error('No audio track');
1810
- }
1811
- this.localMicStream = stream;
1812
- this.isMicMutedSubject.next(!track.enabled);
1813
- this.audioAnalyzer.start(stream);
1814
- return [2 /*return*/];
1815
- }
1816
- });
1817
- });
1818
- };
1819
- VoiceAgentService.prototype.stopLocalMic = function () {
1820
- if (this.localMicStream) {
1821
- this.localMicStream.getTracks().forEach(function (t) { return t.stop(); });
1822
- this.localMicStream = null;
1823
- }
1824
- };
1825
- VoiceAgentService.prototype.enqueueRemoteAudio = function (chunk) {
1826
- this.pendingRemoteAudio.push(chunk.slice(0));
1827
- if (!this.remoteAudioPlaying) {
1828
- void this.playRemoteAudioQueue();
1829
- }
1522
+ VoiceAgentService.prototype.onPipecatDisconnected = function () {
1523
+ this.stopDurationTimer();
1524
+ this.callStartTime = 0;
1525
+ this.audioAnalyzer.stop();
1526
+ this.stopBotAudio();
1527
+ this.callStateSubject.next('ended');
1528
+ this.statusTextSubject.next('Call Ended');
1830
1529
  };
1831
- VoiceAgentService.prototype.playRemoteAudioQueue = function () {
1832
- return __awaiter(this, void 0, void 0, function () {
1833
- var context, chunk, decoded, _a_1;
1834
- return __generator(this, function (_b) {
1835
- switch (_b.label) {
1836
- case 0:
1837
- this.remoteAudioPlaying = true;
1838
- context = this.getOrCreateRemoteAudioContext();
1839
- _b.label = 1;
1840
- case 1:
1841
- if (!(this.pendingRemoteAudio.length > 0)) return [3 /*break*/, 7];
1842
- chunk = this.pendingRemoteAudio.shift();
1843
- if (!chunk)
1844
- return [3 /*break*/, 1];
1845
- _b.label = 2;
1846
- case 2:
1847
- _b.trys.push([2, 5, , 6]);
1848
- return [4 /*yield*/, this.decodeAudioChunk(context, chunk)];
1849
- case 3:
1850
- decoded = _b.sent();
1851
- this.assistantAudioStarted();
1852
- return [4 /*yield*/, this.playDecodedBuffer(context, decoded)];
1853
- case 4:
1854
- _b.sent();
1855
- return [3 /*break*/, 6];
1856
- case 5:
1857
- _a_1 = _b.sent();
1858
- return [3 /*break*/, 6];
1859
- case 6: return [3 /*break*/, 1];
1860
- case 7:
1861
- this.remoteAudioPlaying = false;
1862
- this.assistantAudioStopped();
1863
- return [2 /*return*/];
1864
- }
1865
- });
1866
- });
1530
+ VoiceAgentService.prototype.onBotReady = function () {
1531
+ var _a, _b, _c;
1532
+ // Retry track wiring in case tracks weren't ready at onConnected
1533
+ this.startLocalMicAnalyzer();
1534
+ var botTrack = (_c = (_b = (_a = this.pcClient) === null || _a === void 0 ? void 0 : _a.tracks()) === null || _b === void 0 ? void 0 : _b.bot) === null || _c === void 0 ? void 0 : _c.audio;
1535
+ if (botTrack)
1536
+ this.setupBotAudioTrack(botTrack);
1867
1537
  };
1868
- VoiceAgentService.prototype.getOrCreateRemoteAudioContext = function () {
1869
- if (!this.remoteAudioContext || this.remoteAudioContext.state === 'closed') {
1870
- this.remoteAudioContext = new AudioContext();
1871
- }
1872
- if (this.remoteAudioContext.state === 'suspended') {
1873
- void this.remoteAudioContext.resume();
1538
+ VoiceAgentService.prototype.startLocalMicAnalyzer = function () {
1539
+ var _a, _b, _c;
1540
+ var localTrack = (_c = (_b = (_a = this.pcClient) === null || _a === void 0 ? void 0 : _a.tracks()) === null || _b === void 0 ? void 0 : _b.local) === null || _c === void 0 ? void 0 : _c.audio;
1541
+ if (localTrack) {
1542
+ this.audioAnalyzer.start(new MediaStream([localTrack]));
1874
1543
  }
1875
- return this.remoteAudioContext;
1876
- };
1877
- VoiceAgentService.prototype.decodeAudioChunk = function (context, chunk) {
1878
- return new Promise(function (resolve, reject) {
1879
- context.decodeAudioData(chunk.slice(0), resolve, reject);
1880
- });
1881
- };
1882
- VoiceAgentService.prototype.playDecodedBuffer = function (context, buffer) {
1883
- return new Promise(function (resolve) {
1884
- var source = context.createBufferSource();
1885
- source.buffer = buffer;
1886
- source.connect(context.destination);
1887
- source.onended = function () { return resolve(); };
1888
- source.start();
1889
- });
1890
1544
  };
1891
- VoiceAgentService.prototype.assistantAudioStarted = function () {
1545
+ VoiceAgentService.prototype.onBotStartedSpeaking = function () {
1892
1546
  if (this.callStartTime === 0) {
1893
1547
  this.callStartTime = Date.now();
1894
1548
  this.startDurationTimer();
1895
1549
  }
1896
1550
  this.callStateSubject.next('talking');
1897
1551
  };
1898
- VoiceAgentService.prototype.assistantAudioStopped = function () {
1552
+ VoiceAgentService.prototype.onBotStoppedSpeaking = function () {
1899
1553
  if (this.callStateSubject.value === 'talking') {
1900
1554
  this.callStateSubject.next('connected');
1901
1555
  }
1902
1556
  };
1903
- VoiceAgentService.prototype.resetRemoteAudioPlayback = function () {
1904
- this.pendingRemoteAudio = [];
1905
- this.remoteAudioPlaying = false;
1906
- if (this.remoteAudioContext && this.remoteAudioContext.state !== 'closed') {
1907
- this.remoteAudioContext.close().catch(function () { });
1557
+ VoiceAgentService.prototype.setupBotAudioTrack = function (track) {
1558
+ var _a;
1559
+ if (!this.botAudioElement) {
1560
+ this.botAudioElement = new Audio();
1561
+ this.botAudioElement.autoplay = true;
1908
1562
  }
1909
- this.remoteAudioContext = null;
1563
+ var existing = (_a = this.botAudioElement.srcObject) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
1564
+ if ((existing === null || existing === void 0 ? void 0 : existing.id) === track.id)
1565
+ return;
1566
+ this.botAudioElement.srcObject = new MediaStream([track]);
1567
+ this.botAudioElement.play().catch(function (err) { return console.warn('[HiveGpt Voice] Bot audio play blocked', err); });
1910
1568
  };
1911
- VoiceAgentService.prototype.handleRemoteClose = function () {
1569
+ VoiceAgentService.prototype.stopBotAudio = function () {
1570
+ var _a;
1571
+ if (this.botAudioElement) {
1572
+ try {
1573
+ this.botAudioElement.pause();
1574
+ (_a = this.botAudioElement.srcObject) === null || _a === void 0 ? void 0 : _a.getAudioTracks().forEach(function (t) { return t.stop(); });
1575
+ this.botAudioElement.srcObject = null;
1576
+ }
1577
+ catch (_b) {
1578
+ // ignore
1579
+ }
1580
+ this.botAudioElement = null;
1581
+ }
1582
+ };
1583
+ VoiceAgentService.prototype.disconnect = function () {
1912
1584
  return __awaiter(this, void 0, void 0, function () {
1913
- var state;
1914
- return __generator(this, function (_b) {
1915
- state = this.callStateSubject.value;
1916
- if (state === 'idle' || state === 'ended')
1917
- return [2 /*return*/];
1918
- this.endCall$.next();
1919
- this.stopDurationTimer();
1920
- this.callStartTime = 0;
1921
- this.audioAnalyzer.stop();
1922
- this.stopLocalMic();
1923
- this.resetRemoteAudioPlayback();
1924
- this.callStateSubject.next('ended');
1925
- this.statusTextSubject.next('Connection lost');
1926
- return [2 /*return*/];
1585
+ return __generator(this, function (_d) {
1586
+ switch (_d.label) {
1587
+ case 0:
1588
+ this.stopDurationTimer();
1589
+ this.callStartTime = 0;
1590
+ this.audioAnalyzer.stop();
1591
+ this.stopBotAudio();
1592
+ return [4 /*yield*/, this.cleanupPipecatClient()];
1593
+ case 1:
1594
+ _d.sent();
1595
+ this.callStateSubject.next('ended');
1596
+ this.statusTextSubject.next('Call Ended');
1597
+ return [2 /*return*/];
1598
+ }
1927
1599
  });
1928
1600
  });
1929
1601
  };
1930
- VoiceAgentService.prototype.disconnect = function () {
1602
+ VoiceAgentService.prototype.cleanupPipecatClient = function () {
1931
1603
  return __awaiter(this, void 0, void 0, function () {
1932
- return __generator(this, function (_b) {
1933
- this.endCall$.next();
1934
- this.stopDurationTimer();
1935
- this.callStartTime = 0;
1936
- this.audioAnalyzer.stop();
1937
- this.stopLocalMic();
1938
- this.resetRemoteAudioPlayback();
1939
- this.wsClient.disconnect();
1940
- this.callStateSubject.next('ended');
1941
- this.statusTextSubject.next('Call Ended');
1942
- return [2 /*return*/];
1604
+ var _a_1;
1605
+ return __generator(this, function (_d) {
1606
+ switch (_d.label) {
1607
+ case 0:
1608
+ if (!this.pcClient) return [3 /*break*/, 5];
1609
+ _d.label = 1;
1610
+ case 1:
1611
+ _d.trys.push([1, 3, , 4]);
1612
+ return [4 /*yield*/, this.pcClient.disconnect()];
1613
+ case 2:
1614
+ _d.sent();
1615
+ return [3 /*break*/, 4];
1616
+ case 3:
1617
+ _a_1 = _d.sent();
1618
+ return [3 /*break*/, 4];
1619
+ case 4:
1620
+ this.pcClient = null;
1621
+ _d.label = 5;
1622
+ case 5: return [2 /*return*/];
1623
+ }
1943
1624
  });
1944
1625
  });
1945
1626
  };
1946
1627
  VoiceAgentService.prototype.toggleMic = function () {
1947
- var _a;
1628
+ if (!this.pcClient)
1629
+ return;
1948
1630
  var nextMuted = !this.isMicMutedSubject.value;
1949
- var track = (_a = this.localMicStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
1950
- if (track) {
1951
- track.enabled = !nextMuted;
1952
- }
1631
+ this.pcClient.enableMic(!nextMuted);
1953
1632
  this.isMicMutedSubject.next(nextMuted);
1633
+ if (nextMuted)
1634
+ this.isUserSpeakingSubject.next(false);
1954
1635
  };
1955
1636
  VoiceAgentService.prototype.startDurationTimer = function () {
1956
1637
  var _this = this;
1957
- var updateDuration = function () {
1638
+ var tick = function () {
1958
1639
  if (_this.callStartTime > 0) {
1959
1640
  var elapsed = Math.floor((Date.now() - _this.callStartTime) / 1000);
1960
- var minutes = Math.floor(elapsed / 60);
1961
- var seconds = elapsed % 60;
1962
- _this.durationSubject.next(minutes + ":" + String(seconds).padStart(2, '0'));
1641
+ var m = Math.floor(elapsed / 60);
1642
+ var s = elapsed % 60;
1643
+ _this.durationSubject.next(m + ":" + String(s).padStart(2, '0'));
1963
1644
  }
1964
1645
  };
1965
- updateDuration();
1966
- this.durationInterval = setInterval(updateDuration, 1000);
1646
+ tick();
1647
+ this.durationInterval = setInterval(tick, 1000);
1967
1648
  };
1968
1649
  VoiceAgentService.prototype.stopDurationTimer = function () {
1969
1650
  if (this.durationInterval) {
@@ -1973,7 +1654,7 @@
1973
1654
  };
1974
1655
  return VoiceAgentService;
1975
1656
  }());
1976
- VoiceAgentService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function VoiceAgentService_Factory() { return new VoiceAgentService(i0__namespace.ɵɵinject(AudioAnalyzerService), i0__namespace.ɵɵinject(WebSocketVoiceClientService), i0__namespace.ɵɵinject(PlatformTokenRefreshService), i0__namespace.ɵɵinject(i0__namespace.PLATFORM_ID)); }, token: VoiceAgentService, providedIn: "root" });
1657
+ VoiceAgentService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function VoiceAgentService_Factory() { return new VoiceAgentService(i0__namespace.ɵɵinject(AudioAnalyzerService), i0__namespace.ɵɵinject(PlatformTokenRefreshService), i0__namespace.ɵɵinject(i0__namespace.NgZone), i0__namespace.ɵɵinject(i0__namespace.PLATFORM_ID)); }, token: VoiceAgentService, providedIn: "root" });
1977
1658
  VoiceAgentService.decorators = [
1978
1659
  { type: i0.Injectable, args: [{
1979
1660
  providedIn: 'root',
@@ -1981,8 +1662,8 @@
1981
1662
  ];
1982
1663
  VoiceAgentService.ctorParameters = function () { return [
1983
1664
  { type: AudioAnalyzerService },
1984
- { type: WebSocketVoiceClientService },
1985
1665
  { type: PlatformTokenRefreshService },
1666
+ { type: i0.NgZone },
1986
1667
  { type: Object, decorators: [{ type: i0.Inject, args: [i0.PLATFORM_ID,] }] }
1987
1668
  ]; };
1988
1669
 
@@ -6163,8 +5844,8 @@
6163
5844
  };
6164
5845
 
6165
5846
  /**
6166
- * Voice agent module. Uses native WebSocket for the voice session.
6167
- * Does NOT use Socket.IO or ngx-socket-io.
5847
+ * Voice agent module. Uses @pipecat-ai/client-js + @pipecat-ai/websocket-transport
5848
+ * (peer dependencies) for WebSocket transport, RTVI protocol, and audio.
6168
5849
  */
6169
5850
  var VoiceAgentModule = /** @class */ (function () {
6170
5851
  function VoiceAgentModule() {
@@ -6182,7 +5863,6 @@
6182
5863
  providers: [
6183
5864
  VoiceAgentService,
6184
5865
  AudioAnalyzerService,
6185
- WebSocketVoiceClientService
6186
5866
  ],
6187
5867
  exports: [
6188
5868
  VoiceAgentModalComponent
@@ -6483,10 +6163,9 @@
6483
6163
  exports["ɵc"] = ConversationService;
6484
6164
  exports["ɵd"] = NotificationSocket;
6485
6165
  exports["ɵe"] = TranslationService;
6486
- exports["ɵf"] = WebSocketVoiceClientService;
6487
- exports["ɵg"] = VideoPlayerComponent;
6488
- exports["ɵh"] = SafeHtmlPipe;
6489
- exports["ɵi"] = BotHtmlEditorComponent;
6166
+ exports["ɵf"] = VideoPlayerComponent;
6167
+ exports["ɵg"] = SafeHtmlPipe;
6168
+ exports["ɵh"] = BotHtmlEditorComponent;
6490
6169
 
6491
6170
  Object.defineProperty(exports, '__esModule', { value: true });
6492
6171