ttp-agent-sdk 2.48.20 → 2.48.21

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.
@@ -12129,7 +12129,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
12129
12129
 
12130
12130
  // SDK build time for debugging
12131
12131
  if (true) {
12132
- helloMessage.lastBuildTime = "2026-08-27T16:45:39.666Z";
12132
+ helloMessage.lastBuildTime = "2026-08-27T17:29:05.172Z";
12133
12133
  }
12134
12134
  try {
12135
12135
  this.ws.send(JSON.stringify(helloMessage));
@@ -21439,8 +21439,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
21439
21439
 
21440
21440
 
21441
21441
  // Version - injected at build time from package.json via webpack DefinePlugin
21442
- var VERSION = "2.48.20";
21443
- var BUILD_TIME = "2026-08-27T16:45:39.666Z";
21442
+ var VERSION = "2.48.21";
21443
+ var BUILD_TIME = "2026-08-27T17:29:05.172Z";
21444
21444
  console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "), 'background: #4f46e5; color: white; font-size: 12px; font-weight: bold; padding: 2px 6px; border-radius: 4px;');
21445
21445
 
21446
21446
  // Named exports
@@ -27274,18 +27274,78 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27274
27274
  sampleRate: audioDataSampleRate
27275
27275
  };
27276
27276
  }
27277
+
27278
+ /**
27279
+ * Read-only playback-route telemetry for hello (`client`) and client_audio_info.
27280
+ * Does not influence route selection — mirrors flags set by _connectGainToOutput.
27281
+ */
27277
27282
  }, {
27278
- key: "getHtmlAudioPlaybackInfo",
27279
- value: function getHtmlAudioPlaybackInfo() {
27283
+ key: "getPlaybackRouteReport",
27284
+ value: function getPlaybackRouteReport() {
27285
+ var htmlAudioPlayback = this.wantsHtmlAudioPlayback();
27286
+ var loopbackWanted = this.wantsLoopbackAec();
27287
+ var playbackRoute = this._reportEffectivePlaybackRoute();
27280
27288
  return {
27281
- htmlAudioPlayback: this.wantsHtmlAudioPlayback(),
27289
+ htmlAudioPlayback: htmlAudioPlayback,
27290
+ playbackRoute: playbackRoute,
27291
+ loopbackAttach: this._reportLoopbackAttach(loopbackWanted, playbackRoute),
27292
+ // Legacy booleans — kept for downstream consumers that predated tri-state.
27282
27293
  htmlAudioRouteActive: this._htmlAudioRouteActive,
27283
27294
  htmlAudioPlayOk: this._htmlAudioPlayOk,
27284
- loopbackAec: this.wantsLoopbackAec(),
27295
+ loopbackAec: loopbackWanted,
27285
27296
  loopbackRouteActive: this._loopbackReady,
27286
27297
  loopbackPlayOk: this._loopbackPlayOk
27287
27298
  };
27288
27299
  }
27300
+ }, {
27301
+ key: "getHtmlAudioPlaybackInfo",
27302
+ value: function getHtmlAudioPlaybackInfo() {
27303
+ return this.getPlaybackRouteReport();
27304
+ }
27305
+
27306
+ /** Effective last-hop route from established player state (not config intent). */
27307
+ }, {
27308
+ key: "_reportEffectivePlaybackRoute",
27309
+ value: function _reportEffectivePlaybackRoute() {
27310
+ if (this._loopbackReady && !this._loopbackFailed) {
27311
+ return 'loopback';
27312
+ }
27313
+ if (this._htmlAudioRouteActive) {
27314
+ return 'htmlHop';
27315
+ }
27316
+ return 'destination';
27317
+ }
27318
+ }, {
27319
+ key: "_reportLoopbackAttach",
27320
+ value: function _reportLoopbackAttach(loopbackWanted, playbackRoute) {
27321
+ if (!loopbackWanted) {
27322
+ return {
27323
+ outcome: 'not_attempted'
27324
+ };
27325
+ }
27326
+ if (this._loopbackFailed) {
27327
+ return {
27328
+ outcome: 'failed',
27329
+ fallbackTo: playbackRoute
27330
+ };
27331
+ }
27332
+ if (this._loopbackReady && this._loopbackPlayOk) {
27333
+ return {
27334
+ outcome: 'success'
27335
+ };
27336
+ }
27337
+ if (this._loopbackReady && !this._loopbackPlayOk) {
27338
+ // Peer path is up; element play() not yet confirmed.
27339
+ return {
27340
+ outcome: 'pending'
27341
+ };
27342
+ }
27343
+ // Negotiation in flight — audio may be on an interim hop/destination route.
27344
+ return {
27345
+ outcome: 'pending',
27346
+ fallbackTo: playbackRoute
27347
+ };
27348
+ }
27289
27349
 
27290
27350
  /**
27291
27351
  * Last hop: GainNode → MediaStreamDestination → hidden <audio>.
@@ -28868,6 +28928,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28868
28928
  _this.pendingInjections = new Map(); // Maps correlationId to { resolve, reject, timeout }
28869
28929
  _this.injectionCounter = 0; // Counter for generating unique correlation IDs
28870
28930
 
28931
+ // One settled playback-route report per call (first segment audible).
28932
+ _this._playbackRouteSettledSent = false;
28933
+
28871
28934
  // Barge-in detection is handled by the backend, not the frontend
28872
28935
  // The backend analyzes the audio stream and sends 'barge_in' message when user interrupts
28873
28936
  // Frontend only responds to backend barge-in messages
@@ -29280,6 +29343,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29280
29343
  }
29281
29344
  _this3.emit('playbackStarted');
29282
29345
 
29346
+ // Unconditional settled playback-route report on first audible segment —
29347
+ // even when the route matches hello-time telemetry and no htmlAudioRoute
29348
+ // event fired afterward (e.g. loopback succeeded quietly).
29349
+ if (!_this3._playbackRouteSettledSent) {
29350
+ var _info$segmentId;
29351
+ _this3._playbackRouteSettledSent = true;
29352
+ _this3._sendClientAudioInfo(null, {
29353
+ settled: true,
29354
+ segmentId: (_info$segmentId = info === null || info === void 0 ? void 0 : info.segmentId) !== null && _info$segmentId !== void 0 ? _info$segmentId : null
29355
+ });
29356
+ }
29357
+
29283
29358
  // Transcripts are displayed when they arrive (between sentences)
29284
29359
  // No need to handle transcript display here - it's handled in handleTranscript()
29285
29360
 
@@ -29569,6 +29644,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29569
29644
  _this5.isConnected = false;
29570
29645
  _this5.helloAckReceived = false;
29571
29646
  _this5.disclaimersPending = false;
29647
+ _this5._playbackRouteSettledSent = false;
29572
29648
 
29573
29649
  // Discard any audio still held by the first-playback gate
29574
29650
  _this5._dropPlaybackGate();
@@ -29686,7 +29762,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29686
29762
  key: "_buildClientEnv",
29687
29763
  value: function _buildClientEnv() {
29688
29764
  try {
29689
- var _uaData$mobile, _window$screen, _window$screen2;
29765
+ var _uaData$mobile, _window$screen, _window$screen2, _this$audioPlayer, _this$audioPlayer$get;
29690
29766
  var ua = navigator.userAgent || '';
29691
29767
  var uaData = navigator.userAgentData;
29692
29768
  var conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
@@ -29695,8 +29771,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29695
29771
  // Webview heuristic: Android WebView marks itself with "; wv)" or "Version/x.x Chrome";
29696
29772
  // iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
29697
29773
  var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
29698
- var env = {
29699
- sdkVersion: true ? "2.48.20" : 0,
29774
+ var env = _objectSpread({
29775
+ sdkVersion: true ? "2.48.21" : 0,
29700
29776
  ua: ua,
29701
29777
  platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
29702
29778
  mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
@@ -29706,34 +29782,43 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29706
29782
  cores: navigator.hardwareConcurrency || null,
29707
29783
  memoryGb: navigator.deviceMemory || null,
29708
29784
  connection: (conn === null || conn === void 0 ? void 0 : conn.effectiveType) || null
29709
- };
29785
+ }, ((_this$audioPlayer = this.audioPlayer) === null || _this$audioPlayer === void 0 || (_this$audioPlayer$get = _this$audioPlayer.getPlaybackRouteReport) === null || _this$audioPlayer$get === void 0 ? void 0 : _this$audioPlayer$get.call(_this$audioPlayer)) || {
29786
+ htmlAudioPlayback: null,
29787
+ playbackRoute: 'destination',
29788
+ loopbackAttach: {
29789
+ outcome: 'not_attempted'
29790
+ }
29791
+ });
29710
29792
  return env;
29711
29793
  } catch (e) {
29712
29794
  console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
29713
29795
  return {
29714
- sdkVersion: true ? "2.48.20" : 0
29796
+ sdkVersion: true ? "2.48.21" : 0
29715
29797
  };
29716
29798
  }
29717
29799
  }
29718
29800
 
29719
29801
  /**
29720
- * One-shot telemetry sent right after recording starts (t:'client_audio_info').
29721
- * Reports the capture track's ACTUAL settings from getSettings() — echoCancellation /
29722
- * noiseSuppression / autoGainControl as the browser really applied them (not what we
29723
- * requested), which is the ground truth for diagnosing speakerphone echo barge-in —
29724
- * plus mic label, AudioContext rates, and whether the minimal-constraints fallback fired.
29802
+ * Telemetry sent as t:'client_audio_info'. Interim snapshots (capture start,
29803
+ * htmlAudioRoute transitions) use routeReportKind:'interim'. The first audible
29804
+ * segment emits routeReportKind:'settled' exactly once per connection.
29725
29805
  */
29726
29806
  }, {
29727
29807
  key: "_sendClientAudioInfo",
29728
29808
  value: function _sendClientAudioInfo(routeUpdate) {
29809
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
29729
29810
  try {
29730
- var _this$audioRecorder, _this$audioRecorder$g, _s$echoCancellation, _s$noiseSuppression, _s$autoGainControl, _s$voiceIsolation, _s$sampleRate, _s$channelCount, _this$audioRecorder2, _this$audioPlayer, _this$audioRecorder3, _this$audioPlayer2, _this$audioPlayer2$ge;
29811
+ var _this$audioRecorder, _this$audioRecorder$g, _s$echoCancellation, _s$noiseSuppression, _s$autoGainControl, _s$voiceIsolation, _s$sampleRate, _s$channelCount, _this$audioRecorder2, _this$audioPlayer2, _this$audioRecorder3, _this$audioPlayer3, _this$audioPlayer3$ge;
29812
+ var settled = options.settled === true;
29731
29813
  var track = (_this$audioRecorder = this.audioRecorder) === null || _this$audioRecorder === void 0 || (_this$audioRecorder = _this$audioRecorder.mediaStream) === null || _this$audioRecorder === void 0 || (_this$audioRecorder$g = _this$audioRecorder.getAudioTracks) === null || _this$audioRecorder$g === void 0 ? void 0 : _this$audioRecorder$g.call(_this$audioRecorder)[0];
29732
29814
  // Route updates must go out even before the mic track exists — the hop
29733
- // state is the whole point of the follow-up.
29734
- if (!track && !routeUpdate) return;
29815
+ // state is the whole point of the follow-up. Settled reports go out even
29816
+ // if capture never started (monitor / playback-only paths).
29817
+ if (!track && !routeUpdate && !settled) return;
29735
29818
  var s = track && track.getSettings ? track.getSettings() : {};
29736
29819
  var audio = _objectSpread({
29820
+ sdkVersion: true ? "2.48.21" : 0,
29821
+ routeReportKind: settled ? 'settled' : 'interim',
29737
29822
  aec: (_s$echoCancellation = s.echoCancellation) !== null && _s$echoCancellation !== void 0 ? _s$echoCancellation : null,
29738
29823
  ns: (_s$noiseSuppression = s.noiseSuppression) !== null && _s$noiseSuppression !== void 0 ? _s$noiseSuppression : null,
29739
29824
  agc: (_s$autoGainControl = s.autoGainControl) !== null && _s$autoGainControl !== void 0 ? _s$autoGainControl : null,
@@ -29742,18 +29827,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29742
29827
  channels: (_s$channelCount = s.channelCount) !== null && _s$channelCount !== void 0 ? _s$channelCount : null,
29743
29828
  mic: track ? track.label || null : null,
29744
29829
  captureCtxRate: ((_this$audioRecorder2 = this.audioRecorder) === null || _this$audioRecorder2 === void 0 || (_this$audioRecorder2 = _this$audioRecorder2.audioContext) === null || _this$audioRecorder2 === void 0 ? void 0 : _this$audioRecorder2.sampleRate) || null,
29745
- playbackCtxRate: ((_this$audioPlayer = this.audioPlayer) === null || _this$audioPlayer === void 0 || (_this$audioPlayer = _this$audioPlayer.audioContext) === null || _this$audioPlayer === void 0 ? void 0 : _this$audioPlayer.sampleRate) || null,
29830
+ playbackCtxRate: ((_this$audioPlayer2 = this.audioPlayer) === null || _this$audioPlayer2 === void 0 || (_this$audioPlayer2 = _this$audioPlayer2.audioContext) === null || _this$audioPlayer2 === void 0 ? void 0 : _this$audioPlayer2.sampleRate) || null,
29746
29831
  constraintsFallback: ((_this$audioRecorder3 = this.audioRecorder) === null || _this$audioRecorder3 === void 0 || (_this$audioRecorder3 = _this$audioRecorder3.mediaStream) === null || _this$audioRecorder3 === void 0 ? void 0 : _this$audioRecorder3._ttpConstraintsFallback) === true
29747
- }, ((_this$audioPlayer2 = this.audioPlayer) === null || _this$audioPlayer2 === void 0 || (_this$audioPlayer2$ge = _this$audioPlayer2.getHtmlAudioPlaybackInfo) === null || _this$audioPlayer2$ge === void 0 ? void 0 : _this$audioPlayer2$ge.call(_this$audioPlayer2)) || {});
29832
+ }, ((_this$audioPlayer3 = this.audioPlayer) === null || _this$audioPlayer3 === void 0 || (_this$audioPlayer3$ge = _this$audioPlayer3.getPlaybackRouteReport) === null || _this$audioPlayer3$ge === void 0 ? void 0 : _this$audioPlayer3$ge.call(_this$audioPlayer3)) || {});
29833
+ if (settled && options.segmentId != null) {
29834
+ audio.settledAtSegmentId = options.segmentId;
29835
+ }
29748
29836
  if (routeUpdate && routeUpdate.state) {
29749
- audio.routeUpdate = routeUpdate.state; // 'established' | 'fallback' | 'play_pending'
29837
+ audio.routeUpdate = routeUpdate.state; // 'established' | 'fallback' | 'play_pending' | ...
29750
29838
  if (routeUpdate.reason) audio.routeFallbackReason = routeUpdate.reason;
29751
29839
  }
29752
29840
  this.sendMessage({
29753
29841
  t: 'client_audio_info',
29754
29842
  audio: audio
29755
29843
  });
29756
- console.log('🎛️ VoiceSDK v2: Sent client_audio_info:', audio);
29844
+ console.log("\uD83C\uDF9B\uFE0F VoiceSDK v2: Sent client_audio_info (".concat(audio.routeReportKind, "):"), audio);
29757
29845
  } catch (e) {
29758
29846
  console.warn('⚠️ VoiceSDK v2: Failed to send client_audio_info:', e);
29759
29847
  }
@@ -29859,7 +29947,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29859
29947
 
29860
29948
  // Include SDK build time for debugging
29861
29949
  if (true) {
29862
- helloMessage.lastBuildTime = "2026-08-27T16:45:39.666Z";
29950
+ helloMessage.lastBuildTime = "2026-08-27T17:29:05.172Z";
29863
29951
  }
29864
29952
 
29865
29953
  // Client environment (device/browser/webview) for backend logs + Langfuse metadata
@@ -36458,7 +36546,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
36458
36546
  return;
36459
36547
  }
36460
36548
  this._ensureAboutStyles();
36461
- var version = true ? "2.48.20" : 0;
36549
+ var version = true ? "2.48.21" : 0;
36462
36550
  var convId = this._getLastConversationId();
36463
36551
  var t = function t(k, fb) {
36464
36552
  try {