@hivegpt/hiveai-angular 0.0.582 → 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,184 +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
- /** Fires once each time the WebSocket reaches OPEN. */
1361
- this.opened$ = this.openedSubject.asObservable();
1362
- /** Fires when the socket closes without a client-initiated {@link disconnect}. */
1363
- this.remoteClose$ = this.remoteCloseSubject.asObservable();
1364
- this.userTranscript$ = this.userTranscriptSubject.asObservable();
1365
- this.botTranscript$ = this.botTranscriptSubject.asObservable();
1366
- /** Assistant/bot speaking, when the server sends explicit events (see {@link handleJsonMessage}). */
1367
- this.assistantSpeaking$ = this.assistantSpeakingSubject.asObservable();
1368
- /** User speaking from server-side VAD, if provided. */
1369
- this.serverUserSpeaking$ = this.serverUserSpeakingSubject.asObservable();
1370
- }
1371
- WebSocketVoiceClientService.prototype.connect = function (wsUrl) {
1372
- var _this = this;
1373
- var _a;
1374
- if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
1375
- return;
1376
- }
1377
- if (this.ws) {
1378
- this.closeInitiatedByClient = true;
1379
- this.ws.close();
1380
- }
1381
- try {
1382
- var socket_1 = new WebSocket(wsUrl);
1383
- this.ws = socket_1;
1384
- socket_1.onopen = function () {
1385
- if (_this.ws !== socket_1)
1386
- return;
1387
- _this.ngZone.run(function () { return _this.openedSubject.next(); });
1388
- };
1389
- socket_1.onmessage = function (event) {
1390
- if (_this.ws !== socket_1)
1391
- return;
1392
- if (typeof event.data !== 'string') {
1393
- return;
1394
- }
1395
- try {
1396
- var msg_1 = JSON.parse(event.data);
1397
- _this.ngZone.run(function () { return _this.handleJsonMessage(msg_1); });
1398
- }
1399
- catch (_a) {
1400
- // Ignore non-JSON
1401
- }
1402
- };
1403
- socket_1.onerror = function () {
1404
- _this.ngZone.run(function () {
1405
- if (_this.ws === socket_1 && socket_1.readyState !== WebSocket.CLOSED) {
1406
- socket_1.close();
1407
- }
1408
- });
1409
- };
1410
- socket_1.onclose = function () {
1411
- if (_this.ws === socket_1) {
1412
- _this.ws = null;
1413
- }
1414
- var client = _this.closeInitiatedByClient;
1415
- _this.closeInitiatedByClient = false;
1416
- if (!client) {
1417
- _this.ngZone.run(function () { return _this.remoteCloseSubject.next(); });
1418
- }
1419
- };
1420
- }
1421
- catch (err) {
1422
- console.error('WebSocketVoiceClient: connect failed', err);
1423
- this.ws = null;
1424
- throw err;
1425
- }
1426
- };
1427
- WebSocketVoiceClientService.prototype.handleJsonMessage = function (msg) {
1428
- var type = msg.type;
1429
- var typeStr = typeof type === 'string' ? type : '';
1430
- if (typeStr === 'session_ready' || typeStr === 'connected' || typeStr === 'voice_session_started') {
1431
- return;
1432
- }
1433
- if (typeStr === 'assistant_speaking' ||
1434
- typeStr === 'bot_speaking') {
1435
- if (msg.active === true || msg.speaking === true) {
1436
- this.assistantSpeakingSubject.next(true);
1437
- }
1438
- else if (msg.active === false || msg.speaking === false) {
1439
- this.assistantSpeakingSubject.next(false);
1440
- }
1441
- return;
1442
- }
1443
- if (typeStr === 'user_speaking') {
1444
- if (msg.active === true || msg.speaking === true) {
1445
- this.serverUserSpeakingSubject.next(true);
1446
- }
1447
- else if (msg.active === false || msg.speaking === false) {
1448
- this.serverUserSpeakingSubject.next(false);
1449
- }
1450
- return;
1451
- }
1452
- if (typeStr === 'input_audio_buffer.speech_started') {
1453
- this.serverUserSpeakingSubject.next(true);
1454
- return;
1455
- }
1456
- if (typeStr === 'input_audio_buffer.speech_stopped') {
1457
- this.serverUserSpeakingSubject.next(false);
1458
- return;
1459
- }
1460
- if (typeStr === 'response.audio.delta') {
1461
- this.assistantSpeakingSubject.next(true);
1462
- return;
1463
- }
1464
- if (typeStr === 'response.audio.done' ||
1465
- typeStr === 'response.output_audio.done') {
1466
- this.assistantSpeakingSubject.next(false);
1467
- return;
1468
- }
1469
- if (typeStr === 'user_transcript' && typeof msg.text === 'string') {
1470
- this.userTranscriptSubject.next({
1471
- text: msg.text,
1472
- final: msg.final === true,
1473
- });
1474
- return;
1475
- }
1476
- if (typeStr === 'bot_transcript' && typeof msg.text === 'string') {
1477
- this.botTranscriptSubject.next(msg.text);
1478
- }
1479
- };
1480
- WebSocketVoiceClientService.prototype.disconnect = function () {
1481
- if (!this.ws) {
1482
- return;
1483
- }
1484
- this.closeInitiatedByClient = true;
1485
- this.ws.close();
1486
- };
1487
- Object.defineProperty(WebSocketVoiceClientService.prototype, "isConnected", {
1488
- get: function () {
1489
- var _a;
1490
- return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN;
1491
- },
1492
- enumerable: false,
1493
- configurable: true
1494
- });
1495
- return WebSocketVoiceClientService;
1496
- }());
1497
- WebSocketVoiceClientService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function WebSocketVoiceClientService_Factory() { return new WebSocketVoiceClientService(i0__namespace.ɵɵinject(i0__namespace.NgZone)); }, token: WebSocketVoiceClientService, providedIn: "root" });
1498
- WebSocketVoiceClientService.decorators = [
1499
- { type: i0.Injectable, args: [{
1500
- providedIn: 'root',
1501
- },] }
1502
- ];
1503
- WebSocketVoiceClientService.ctorParameters = function () { return [
1504
- { type: i0.NgZone }
1505
- ]; };
1506
-
1507
- /**
1508
- * Voice agent orchestrator: single WebSocket (`ws_url` from POST /ai/ask-voice-socket)
1509
- * for session events, transcripts, and optional speaking hints; local mic for capture
1510
- * 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.
1511
1351
  */
1512
1352
  var VoiceAgentService = /** @class */ (function () {
1513
- function VoiceAgentService(audioAnalyzer, wsClient, platformTokenRefresh,
1353
+ function VoiceAgentService(audioAnalyzer, platformTokenRefresh, ngZone,
1514
1354
  /** `Object` not `object` — ngc metadata collection rejects the `object` type in DI params. */
1515
1355
  platformId) {
1516
1356
  var _this = this;
1517
1357
  this.audioAnalyzer = audioAnalyzer;
1518
- this.wsClient = wsClient;
1519
1358
  this.platformTokenRefresh = platformTokenRefresh;
1359
+ this.ngZone = ngZone;
1520
1360
  this.platformId = platformId;
1521
1361
  this.callStateSubject = new rxjs.BehaviorSubject('idle');
1522
1362
  this.statusTextSubject = new rxjs.BehaviorSubject('');
@@ -1528,8 +1368,8 @@
1528
1368
  this.botTranscriptSubject = new rxjs.Subject();
1529
1369
  this.callStartTime = 0;
1530
1370
  this.durationInterval = null;
1531
- this.localMicStream = null;
1532
- this.endCall$ = new rxjs.Subject();
1371
+ this.pcClient = null;
1372
+ this.botAudioElement = null;
1533
1373
  this.subscriptions = new rxjs.Subscription();
1534
1374
  this.destroy$ = new rxjs.Subject();
1535
1375
  this.callState$ = this.callStateSubject.asObservable();
@@ -1541,119 +1381,131 @@
1541
1381
  this.userTranscript$ = this.userTranscriptSubject.asObservable();
1542
1382
  this.botTranscript$ = this.botTranscriptSubject.asObservable();
1543
1383
  this.subscriptions.add(this.audioAnalyzer.audioLevels$.subscribe(function (levels) { return _this.audioLevelsSubject.next(levels); }));
1544
- this.subscriptions.add(this.wsClient.remoteClose$
1545
- .pipe(operators.takeUntil(this.destroy$))
1546
- .subscribe(function () { return void _this.handleRemoteClose(); }));
1547
1384
  }
1548
1385
  VoiceAgentService.prototype.ngOnDestroy = function () {
1549
1386
  this.destroy$.next();
1550
1387
  this.subscriptions.unsubscribe();
1551
- this.disconnect();
1388
+ void this.disconnect();
1552
1389
  };
1553
- /** 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). */
1554
1391
  VoiceAgentService.prototype.resetToIdle = function () {
1555
1392
  if (this.callStateSubject.value === 'idle')
1556
1393
  return;
1557
- this.endCall$.next();
1558
- this.stopDurationTimer();
1559
- this.callStartTime = 0;
1560
- this.audioAnalyzer.stop();
1561
- this.stopLocalMic();
1562
- this.wsClient.disconnect();
1394
+ void this.disconnect();
1563
1395
  this.callStateSubject.next('idle');
1564
1396
  this.statusTextSubject.next('');
1565
1397
  this.durationSubject.next('0:00');
1566
1398
  };
1567
1399
  VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl) {
1568
1400
  return __awaiter(this, void 0, void 0, function () {
1569
- 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;
1570
1402
  var _this = this;
1571
- return __generator(this, function (_b) {
1572
- switch (_b.label) {
1403
+ return __generator(this, function (_d) {
1404
+ switch (_d.label) {
1573
1405
  case 0:
1574
1406
  if (this.callStateSubject.value !== 'idle') {
1575
- console.warn('Call already in progress');
1407
+ console.warn('[HiveGpt Voice] Call already in progress');
1576
1408
  return [2 /*return*/];
1577
1409
  }
1578
- _b.label = 1;
1410
+ _d.label = 1;
1579
1411
  case 1:
1580
- _b.trys.push([1, 8, , 10]);
1412
+ _d.trys.push([1, 8, , 10]);
1581
1413
  this.callStateSubject.next('connecting');
1582
1414
  this.statusTextSubject.next('Connecting...');
1583
1415
  accessToken = token;
1584
1416
  if (!(usersApiUrl && common.isPlatformBrowser(this.platformId))) return [3 /*break*/, 5];
1585
- _b.label = 2;
1417
+ _d.label = 2;
1586
1418
  case 2:
1587
- _b.trys.push([2, 4, , 5]);
1419
+ _d.trys.push([2, 4, , 5]);
1588
1420
  return [4 /*yield*/, this.platformTokenRefresh
1589
1421
  .ensureValidAccessToken(token, usersApiUrl)
1590
1422
  .pipe(operators.take(1))
1591
1423
  .toPromise()];
1592
1424
  case 3:
1593
- ensured = _b.sent();
1594
- 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)
1595
1427
  accessToken = ensured.accessToken;
1596
- }
1597
1428
  return [3 /*break*/, 5];
1598
1429
  case 4:
1599
- e_1 = _b.sent();
1600
- 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);
1601
1432
  return [3 /*break*/, 5];
1602
1433
  case 5:
1603
1434
  baseUrl = apiUrl.replace(/\/$/, '');
1604
- postUrl = baseUrl + "/ai/ask-voice-socket";
1605
- headers = {
1606
- 'Content-Type': 'application/json',
1607
- Authorization: "Bearer " + accessToken,
1608
- 'x-api-key': apiKey,
1609
- 'hive-bot-id': botId,
1610
- 'domain-authority': domainAuthority,
1611
- eventUrl: eventUrl,
1612
- eventId: eventId,
1613
- eventToken: eventToken,
1614
- 'ngrok-skip-browser-warning': 'true',
1615
- };
1616
- return [4 /*yield*/, fetch(postUrl, {
1617
- method: 'POST',
1618
- headers: headers,
1619
- 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: {
1620
1493
  bot_id: botId,
1621
1494
  conversation_id: conversationId,
1622
1495
  voice: 'alloy',
1623
- }),
1496
+ },
1624
1497
  })];
1625
- case 6:
1626
- res = _b.sent();
1627
- if (!res.ok) {
1628
- throw new Error("HTTP " + res.status);
1629
- }
1630
- return [4 /*yield*/, res.json()];
1631
1498
  case 7:
1632
- json = _b.sent();
1633
- wsUrl = (typeof (json === null || json === void 0 ? void 0 : json.ws_url) === 'string' && json.ws_url) ||
1634
- (typeof (json === null || json === void 0 ? void 0 : json.rn_ws_url) === 'string' && json.rn_ws_url);
1635
- if (!wsUrl) {
1636
- throw new Error('No ws_url in response');
1637
- }
1638
- untilCallEnds$ = rxjs.merge(this.destroy$, this.endCall$);
1639
- this.subscriptions.add(this.wsClient.userTranscript$
1640
- .pipe(operators.takeUntil(untilCallEnds$))
1641
- .subscribe(function (t) { return _this.userTranscriptSubject.next(t); }));
1642
- this.subscriptions.add(this.wsClient.botTranscript$
1643
- .pipe(operators.takeUntil(untilCallEnds$))
1644
- .subscribe(function (t) { return _this.botTranscriptSubject.next(t); }));
1645
- this.subscriptions.add(this.wsClient.opened$
1646
- .pipe(operators.takeUntil(untilCallEnds$), operators.take(1))
1647
- .subscribe(function () { return void _this.onWebsocketOpened(); }));
1648
- this.wsClient.connect(wsUrl);
1499
+ // POST to /ai/ask-voice-socket → receives { ws_url } → WebSocketTransport connects
1500
+ _d.sent();
1649
1501
  return [3 /*break*/, 10];
1650
1502
  case 8:
1651
- error_1 = _b.sent();
1652
- console.error('Error connecting voice agent:', error_1);
1503
+ error_1 = _d.sent();
1504
+ console.error('[HiveGpt Voice] connect failed', error_1);
1653
1505
  this.callStateSubject.next('ended');
1654
- return [4 /*yield*/, this.disconnect()];
1506
+ return [4 /*yield*/, this.cleanupPipecatClient()];
1655
1507
  case 9:
1656
- _b.sent();
1508
+ _d.sent();
1657
1509
  this.statusTextSubject.next('Connection failed');
1658
1510
  throw error_1;
1659
1511
  case 10: return [2 /*return*/];
@@ -1661,161 +1513,138 @@
1661
1513
  });
1662
1514
  });
1663
1515
  };
1664
- VoiceAgentService.prototype.onWebsocketOpened = function () {
1665
- return __awaiter(this, void 0, void 0, function () {
1666
- var err_1;
1667
- return __generator(this, function (_b) {
1668
- switch (_b.label) {
1669
- case 0:
1670
- if (this.callStateSubject.value !== 'connecting') {
1671
- return [2 /*return*/];
1672
- }
1673
- _b.label = 1;
1674
- case 1:
1675
- _b.trys.push([1, 3, , 5]);
1676
- return [4 /*yield*/, this.startLocalMic()];
1677
- case 2:
1678
- _b.sent();
1679
- this.statusTextSubject.next('Connected');
1680
- this.callStateSubject.next('connected');
1681
- this.wireSpeakingState();
1682
- return [3 /*break*/, 5];
1683
- case 3:
1684
- err_1 = _b.sent();
1685
- console.error('[HiveGpt Voice] Mic or session setup failed', err_1);
1686
- this.callStateSubject.next('ended');
1687
- this.statusTextSubject.next('Microphone unavailable');
1688
- return [4 /*yield*/, this.disconnect()];
1689
- case 4:
1690
- _b.sent();
1691
- return [3 /*break*/, 5];
1692
- case 5: return [2 /*return*/];
1693
- }
1694
- });
1695
- });
1516
+ VoiceAgentService.prototype.onPipecatConnected = function () {
1517
+ this.callStateSubject.next('connected');
1518
+ this.statusTextSubject.next('Connected');
1519
+ this.isMicMutedSubject.next(false);
1520
+ this.startLocalMicAnalyzer();
1696
1521
  };
1697
- VoiceAgentService.prototype.wireSpeakingState = function () {
1698
- var _this = this;
1699
- var untilCallEnds$ = rxjs.merge(this.destroy$, this.endCall$);
1700
- 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());
1701
- var assistantTalking$ = rxjs.merge(this.wsClient.assistantSpeaking$, transcriptDrivenAssistant$).pipe(operators.distinctUntilChanged(), operators.startWith(false));
1702
- var userTalking$ = rxjs.combineLatest([
1703
- this.audioAnalyzer.isUserSpeaking$,
1704
- this.wsClient.serverUserSpeaking$.pipe(operators.startWith(false)),
1705
- ]).pipe(operators.map(function (_b) {
1706
- var _c = __read(_b, 2), local = _c[0], server = _c[1];
1707
- return local || server;
1708
- }), operators.distinctUntilChanged(), operators.startWith(false));
1709
- this.subscriptions.add(rxjs.combineLatest([assistantTalking$, userTalking$])
1710
- .pipe(operators.takeUntil(untilCallEnds$))
1711
- .subscribe(function (_b) {
1712
- var _c = __read(_b, 2), bot = _c[0], user = _c[1];
1713
- var current = _this.callStateSubject.value;
1714
- if (user) {
1715
- _this.isUserSpeakingSubject.next(true);
1716
- _this.callStateSubject.next('listening');
1717
- }
1718
- else {
1719
- _this.isUserSpeakingSubject.next(false);
1720
- }
1721
- if (user) {
1722
- return;
1723
- }
1724
- if (bot) {
1725
- if (_this.callStartTime === 0) {
1726
- _this.callStartTime = Date.now();
1727
- _this.startDurationTimer();
1728
- }
1729
- _this.callStateSubject.next('talking');
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');
1529
+ };
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);
1537
+ };
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]));
1543
+ }
1544
+ };
1545
+ VoiceAgentService.prototype.onBotStartedSpeaking = function () {
1546
+ if (this.callStartTime === 0) {
1547
+ this.callStartTime = Date.now();
1548
+ this.startDurationTimer();
1549
+ }
1550
+ this.callStateSubject.next('talking');
1551
+ };
1552
+ VoiceAgentService.prototype.onBotStoppedSpeaking = function () {
1553
+ if (this.callStateSubject.value === 'talking') {
1554
+ this.callStateSubject.next('connected');
1555
+ }
1556
+ };
1557
+ VoiceAgentService.prototype.setupBotAudioTrack = function (track) {
1558
+ var _a;
1559
+ if (!this.botAudioElement) {
1560
+ this.botAudioElement = new Audio();
1561
+ this.botAudioElement.autoplay = true;
1562
+ }
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); });
1568
+ };
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;
1730
1576
  }
1731
- else if (current === 'talking' || current === 'listening') {
1732
- _this.callStateSubject.next('connected');
1577
+ catch (_b) {
1578
+ // ignore
1733
1579
  }
1734
- }));
1580
+ this.botAudioElement = null;
1581
+ }
1735
1582
  };
1736
- VoiceAgentService.prototype.startLocalMic = function () {
1583
+ VoiceAgentService.prototype.disconnect = function () {
1737
1584
  return __awaiter(this, void 0, void 0, function () {
1738
- var stream, track;
1739
- return __generator(this, function (_b) {
1740
- switch (_b.label) {
1585
+ return __generator(this, function (_d) {
1586
+ switch (_d.label) {
1741
1587
  case 0:
1742
- this.stopLocalMic();
1743
- return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
1588
+ this.stopDurationTimer();
1589
+ this.callStartTime = 0;
1590
+ this.audioAnalyzer.stop();
1591
+ this.stopBotAudio();
1592
+ return [4 /*yield*/, this.cleanupPipecatClient()];
1744
1593
  case 1:
1745
- stream = _b.sent();
1746
- track = stream.getAudioTracks()[0];
1747
- if (!track) {
1748
- stream.getTracks().forEach(function (t) { return t.stop(); });
1749
- throw new Error('No audio track');
1750
- }
1751
- this.localMicStream = stream;
1752
- this.isMicMutedSubject.next(!track.enabled);
1753
- this.audioAnalyzer.start(stream);
1594
+ _d.sent();
1595
+ this.callStateSubject.next('ended');
1596
+ this.statusTextSubject.next('Call Ended');
1754
1597
  return [2 /*return*/];
1755
1598
  }
1756
1599
  });
1757
1600
  });
1758
1601
  };
1759
- VoiceAgentService.prototype.stopLocalMic = function () {
1760
- if (this.localMicStream) {
1761
- this.localMicStream.getTracks().forEach(function (t) { return t.stop(); });
1762
- this.localMicStream = null;
1763
- }
1764
- };
1765
- VoiceAgentService.prototype.handleRemoteClose = function () {
1602
+ VoiceAgentService.prototype.cleanupPipecatClient = function () {
1766
1603
  return __awaiter(this, void 0, void 0, function () {
1767
- var state;
1768
- return __generator(this, function (_b) {
1769
- state = this.callStateSubject.value;
1770
- if (state === 'idle' || state === 'ended')
1771
- return [2 /*return*/];
1772
- this.endCall$.next();
1773
- this.stopDurationTimer();
1774
- this.callStartTime = 0;
1775
- this.audioAnalyzer.stop();
1776
- this.stopLocalMic();
1777
- this.callStateSubject.next('ended');
1778
- this.statusTextSubject.next('Connection lost');
1779
- return [2 /*return*/];
1780
- });
1781
- });
1782
- };
1783
- VoiceAgentService.prototype.disconnect = function () {
1784
- return __awaiter(this, void 0, void 0, function () {
1785
- return __generator(this, function (_b) {
1786
- this.endCall$.next();
1787
- this.stopDurationTimer();
1788
- this.callStartTime = 0;
1789
- this.audioAnalyzer.stop();
1790
- this.stopLocalMic();
1791
- this.wsClient.disconnect();
1792
- this.callStateSubject.next('ended');
1793
- this.statusTextSubject.next('Call Ended');
1794
- 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
+ }
1795
1624
  });
1796
1625
  });
1797
1626
  };
1798
1627
  VoiceAgentService.prototype.toggleMic = function () {
1799
- var _a;
1628
+ if (!this.pcClient)
1629
+ return;
1800
1630
  var nextMuted = !this.isMicMutedSubject.value;
1801
- var track = (_a = this.localMicStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];
1802
- if (track) {
1803
- track.enabled = !nextMuted;
1804
- }
1631
+ this.pcClient.enableMic(!nextMuted);
1805
1632
  this.isMicMutedSubject.next(nextMuted);
1633
+ if (nextMuted)
1634
+ this.isUserSpeakingSubject.next(false);
1806
1635
  };
1807
1636
  VoiceAgentService.prototype.startDurationTimer = function () {
1808
1637
  var _this = this;
1809
- var updateDuration = function () {
1638
+ var tick = function () {
1810
1639
  if (_this.callStartTime > 0) {
1811
1640
  var elapsed = Math.floor((Date.now() - _this.callStartTime) / 1000);
1812
- var minutes = Math.floor(elapsed / 60);
1813
- var seconds = elapsed % 60;
1814
- _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'));
1815
1644
  }
1816
1645
  };
1817
- updateDuration();
1818
- this.durationInterval = setInterval(updateDuration, 1000);
1646
+ tick();
1647
+ this.durationInterval = setInterval(tick, 1000);
1819
1648
  };
1820
1649
  VoiceAgentService.prototype.stopDurationTimer = function () {
1821
1650
  if (this.durationInterval) {
@@ -1825,7 +1654,7 @@
1825
1654
  };
1826
1655
  return VoiceAgentService;
1827
1656
  }());
1828
- 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" });
1829
1658
  VoiceAgentService.decorators = [
1830
1659
  { type: i0.Injectable, args: [{
1831
1660
  providedIn: 'root',
@@ -1833,8 +1662,8 @@
1833
1662
  ];
1834
1663
  VoiceAgentService.ctorParameters = function () { return [
1835
1664
  { type: AudioAnalyzerService },
1836
- { type: WebSocketVoiceClientService },
1837
1665
  { type: PlatformTokenRefreshService },
1666
+ { type: i0.NgZone },
1838
1667
  { type: Object, decorators: [{ type: i0.Inject, args: [i0.PLATFORM_ID,] }] }
1839
1668
  ]; };
1840
1669
 
@@ -6015,8 +5844,8 @@
6015
5844
  };
6016
5845
 
6017
5846
  /**
6018
- * Voice agent module. Uses native WebSocket for the voice session.
6019
- * 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.
6020
5849
  */
6021
5850
  var VoiceAgentModule = /** @class */ (function () {
6022
5851
  function VoiceAgentModule() {
@@ -6034,7 +5863,6 @@
6034
5863
  providers: [
6035
5864
  VoiceAgentService,
6036
5865
  AudioAnalyzerService,
6037
- WebSocketVoiceClientService
6038
5866
  ],
6039
5867
  exports: [
6040
5868
  VoiceAgentModalComponent
@@ -6335,10 +6163,9 @@
6335
6163
  exports["ɵc"] = ConversationService;
6336
6164
  exports["ɵd"] = NotificationSocket;
6337
6165
  exports["ɵe"] = TranslationService;
6338
- exports["ɵf"] = WebSocketVoiceClientService;
6339
- exports["ɵg"] = VideoPlayerComponent;
6340
- exports["ɵh"] = SafeHtmlPipe;
6341
- exports["ɵi"] = BotHtmlEditorComponent;
6166
+ exports["ɵf"] = VideoPlayerComponent;
6167
+ exports["ɵg"] = SafeHtmlPipe;
6168
+ exports["ɵh"] = BotHtmlEditorComponent;
6342
6169
 
6343
6170
  Object.defineProperty(exports, '__esModule', { value: true });
6344
6171