ttp-agent-sdk 2.48.8 → 2.48.10

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.
@@ -12047,7 +12047,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
12047
12047
 
12048
12048
  // SDK build time for debugging
12049
12049
  if (true) {
12050
- helloMessage.lastBuildTime = "2026-08-20T21:32:52.088Z";
12050
+ helloMessage.lastBuildTime = "2026-08-20T23:29:16.481Z";
12051
12051
  }
12052
12052
  try {
12053
12053
  this.ws.send(JSON.stringify(helloMessage));
@@ -21357,8 +21357,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
21357
21357
 
21358
21358
 
21359
21359
  // Version - injected at build time from package.json via webpack DefinePlugin
21360
- var VERSION = "2.48.8";
21361
- var BUILD_TIME = "2026-08-20T21:32:52.088Z";
21360
+ var VERSION = "2.48.10";
21361
+ var BUILD_TIME = "2026-08-20T23:29:16.481Z";
21362
21362
  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;');
21363
21363
 
21364
21364
  // Named exports
@@ -28389,7 +28389,19 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28389
28389
  audioConstraints: config.audioConstraints || null,
28390
28390
  // Last-hop TTS playback via hidden <audio> (AEC far-end). Default on.
28391
28391
  // Set false to fall back to AudioContext.destination (old path).
28392
- htmlAudioPlayback: config.htmlAudioPlayback !== false,
28392
+ // QA/debug override wins over config: ?ttp_hop=0 / localStorage ttp_hop=0
28393
+ // forces AudioContext.destination; =1 forces the HTML hop.
28394
+ htmlAudioPlayback: function () {
28395
+ try {
28396
+ var _URLSearchParams$get;
28397
+ var flag = (_URLSearchParams$get = new URLSearchParams(window.location.search).get('ttp_hop')) !== null && _URLSearchParams$get !== void 0 ? _URLSearchParams$get : window.localStorage.getItem('ttp_hop');
28398
+ if (flag === '0' || flag === '1') {
28399
+ console.log("\uD83D\uDD27 VoiceSDK v2: ttp_hop override=".concat(flag, " \u2192 htmlAudioPlayback=").concat(flag === '1'));
28400
+ return flag === '1';
28401
+ }
28402
+ } catch (e) {/* non-browser env */}
28403
+ return config.htmlAudioPlayback !== false;
28404
+ }(),
28393
28405
  // Protocol version
28394
28406
 
28395
28407
  protocolVersion: config.protocolVersion || 2,
@@ -28415,6 +28427,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28415
28427
 
28416
28428
  /** Set when hangup() runs with an open socket — skip resume-key clear in destroy() */
28417
28429
  _this._gracefulHangupInitiated = false;
28430
+
28431
+ /**
28432
+ * First-playback gate: while active, incoming TTS audio is held in
28433
+ * _gatedAudioChunks until the mic/AEC capture stack is up. Starting capture
28434
+ * mid-playback makes Chrome reconfigure the output device for echo cancellation,
28435
+ * which warps the MediaStream→<audio> hop clock (greeting plays fast/slow).
28436
+ */
28437
+ _this._playbackGateActive = false;
28438
+ _this._gatedAudioChunks = [];
28439
+ _this._playbackGateTimer = null;
28440
+ /** True while held chunks replay — late arrivals keep queueing so order is preserved */
28441
+ _this._playbackGateFlushing = false;
28418
28442
  _this.isRecording = false;
28419
28443
  _this.isPlaying = false;
28420
28444
  _this.isDestroyed = false;
@@ -29126,6 +29150,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29126
29150
  _this5.helloAckReceived = false;
29127
29151
  _this5.disclaimersPending = false;
29128
29152
 
29153
+ // Discard any audio still held by the first-playback gate
29154
+ _this5._dropPlaybackGate();
29155
+
29129
29156
  // Stop keepalive pings
29130
29157
  if (_this5._keepAliveInterval) {
29131
29158
  clearInterval(_this5._keepAliveInterval);
@@ -29249,7 +29276,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29249
29276
  // iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
29250
29277
  var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
29251
29278
  var env = {
29252
- sdkVersion: true ? "2.48.8" : 0,
29279
+ sdkVersion: true ? "2.48.10" : 0,
29253
29280
  ua: ua,
29254
29281
  platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
29255
29282
  mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
@@ -29264,7 +29291,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29264
29291
  } catch (e) {
29265
29292
  console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
29266
29293
  return {
29267
- sdkVersion: true ? "2.48.8" : 0
29294
+ sdkVersion: true ? "2.48.10" : 0
29268
29295
  };
29269
29296
  }
29270
29297
  }
@@ -29406,7 +29433,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29406
29433
 
29407
29434
  // Include SDK build time for debugging
29408
29435
  if (true) {
29409
- helloMessage.lastBuildTime = "2026-08-20T21:32:52.088Z";
29436
+ helloMessage.lastBuildTime = "2026-08-20T23:29:16.481Z";
29410
29437
  }
29411
29438
 
29412
29439
  // Client environment (device/browser/webview) for backend logs + Langfuse metadata
@@ -29915,6 +29942,100 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29915
29942
  }
29916
29943
  }
29917
29944
 
29945
+ /**
29946
+ * Arm the first-playback gate. While armed, handleBinaryMessage holds raw audio
29947
+ * instead of playing it. Released when the recorder is up (startRecording), on
29948
+ * failure/teardown (dropped instead), or by a safety timeout so a stuck gate can
29949
+ * never mute a live call (e.g. integrators that play audio without startRecording).
29950
+ */
29951
+ }, {
29952
+ key: "_armPlaybackGate",
29953
+ value: function _armPlaybackGate() {
29954
+ var _this7 = this;
29955
+ if (this._playbackGateActive) return;
29956
+ if (this.isMonitor && this.isMonitor()) return; // listen-only: no mic ever comes up
29957
+
29958
+ if (this.isRecording) return; // mic already up (reconnect)
29959
+
29960
+ this._playbackGateActive = true;
29961
+ this._gatedAudioChunks = [];
29962
+ clearTimeout(this._playbackGateTimer);
29963
+ this._playbackGateTimer = setTimeout(function () {
29964
+ if (_this7._playbackGateActive) {
29965
+ console.warn('⚠️ VoiceSDK v2: playback gate timed out — releasing held audio');
29966
+ _this7._releasePlaybackGate();
29967
+ }
29968
+ }, 10000);
29969
+ }
29970
+
29971
+ /**
29972
+ * Open the gate and replay held chunks through the normal audio path, in order.
29973
+ * While the drain runs, late-arriving chunks keep queueing (and are picked up by
29974
+ * the drain loop) so they can never overtake the held greeting audio.
29975
+ */
29976
+ }, {
29977
+ key: "_releasePlaybackGate",
29978
+ value: function _releasePlaybackGate() {
29979
+ var _this8 = this;
29980
+ if (!this._playbackGateActive) return;
29981
+ this._playbackGateActive = false;
29982
+ clearTimeout(this._playbackGateTimer);
29983
+ this._playbackGateTimer = null;
29984
+ if (this._gatedAudioChunks.length === 0) return;
29985
+ console.log("\uD83D\uDD13 VoiceSDK v2: playback gate released \u2014 flushing ".concat(this._gatedAudioChunks.length, " held audio chunk(s)"));
29986
+ this._playbackGateFlushing = true;
29987
+ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
29988
+ var data, _t2;
29989
+ return _regenerator().w(function (_context3) {
29990
+ while (1) switch (_context3.p = _context3.n) {
29991
+ case 0:
29992
+ _context3.p = 0;
29993
+ case 1:
29994
+ if (!(_this8._playbackGateFlushing && _this8._gatedAudioChunks.length > 0)) {
29995
+ _context3.n = 6;
29996
+ break;
29997
+ }
29998
+ data = _this8._gatedAudioChunks.shift();
29999
+ _context3.p = 2;
30000
+ _context3.n = 3;
30001
+ return _this8.handleBinaryMessage(data, true);
30002
+ case 3:
30003
+ _context3.n = 5;
30004
+ break;
30005
+ case 4:
30006
+ _context3.p = 4;
30007
+ _t2 = _context3.v;
30008
+ console.error('❌ VoiceSDK v2: gated chunk replay failed:', _t2);
30009
+ case 5:
30010
+ _context3.n = 1;
30011
+ break;
30012
+ case 6:
30013
+ _context3.p = 6;
30014
+ _this8._playbackGateFlushing = false;
30015
+ return _context3.f(6);
30016
+ case 7:
30017
+ return _context3.a(2);
30018
+ }
30019
+ }, _callee3, null, [[2, 4], [0,, 6, 7]]);
30020
+ }))();
30021
+ }
30022
+
30023
+ /** Open the gate and discard held chunks (teardown / recording failure paths). */
30024
+ }, {
30025
+ key: "_dropPlaybackGate",
30026
+ value: function _dropPlaybackGate() {
30027
+ if (!this._playbackGateActive && !this._playbackGateFlushing && this._gatedAudioChunks.length === 0) return;
30028
+ this._playbackGateActive = false;
30029
+ this._playbackGateFlushing = false; // stops any in-flight drain loop
30030
+
30031
+ clearTimeout(this._playbackGateTimer);
30032
+ this._playbackGateTimer = null;
30033
+ if (this._gatedAudioChunks.length > 0) {
30034
+ console.log("\uD83D\uDDD1\uFE0F VoiceSDK v2: playback gate dropped ".concat(this._gatedAudioChunks.length, " held chunk(s)"));
30035
+ }
30036
+ this._gatedAudioChunks = [];
30037
+ }
30038
+
29918
30039
  /**
29919
30040
  * Handle hello_ack message
29920
30041
  */
@@ -29985,6 +30106,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29985
30106
  // so config.sampleRate must be finalized first.
29986
30107
  this.helloAckReceived = true;
29987
30108
 
30109
+ // Hold the greeting until the mic/AEC stack is up — starting capture mid-playback
30110
+ // warps the MediaStream→<audio> hop clock (greeting plays fast/slow).
30111
+ this._armPlaybackGate();
30112
+
29988
30113
  // Silently re-apply the user's persisted in-call settings (voice speed /
29989
30114
  // response length) now that the session is ready — the backend applies
29990
30115
  // them without a spoken acknowledgement (announce:false).
@@ -30134,40 +30259,40 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30134
30259
  }, {
30135
30260
  key: "_dispatchSyncedActions",
30136
30261
  value: (function () {
30137
- var _dispatchSyncedActions2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(actions) {
30138
- var _iterator, _step, raw, _t2;
30139
- return _regenerator().w(function (_context3) {
30140
- while (1) switch (_context3.p = _context3.n) {
30262
+ var _dispatchSyncedActions2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(actions) {
30263
+ var _iterator, _step, raw, _t3;
30264
+ return _regenerator().w(function (_context4) {
30265
+ while (1) switch (_context4.p = _context4.n) {
30141
30266
  case 0:
30142
30267
  _iterator = _createForOfIteratorHelper(actions);
30143
- _context3.p = 1;
30268
+ _context4.p = 1;
30144
30269
  _iterator.s();
30145
30270
  case 2:
30146
30271
  if ((_step = _iterator.n()).done) {
30147
- _context3.n = 4;
30272
+ _context4.n = 4;
30148
30273
  break;
30149
30274
  }
30150
30275
  raw = _step.value;
30151
- _context3.n = 3;
30276
+ _context4.n = 3;
30152
30277
  return this._dispatchSyncedAction(raw);
30153
30278
  case 3:
30154
- _context3.n = 2;
30279
+ _context4.n = 2;
30155
30280
  break;
30156
30281
  case 4:
30157
- _context3.n = 6;
30282
+ _context4.n = 6;
30158
30283
  break;
30159
30284
  case 5:
30160
- _context3.p = 5;
30161
- _t2 = _context3.v;
30162
- _iterator.e(_t2);
30285
+ _context4.p = 5;
30286
+ _t3 = _context4.v;
30287
+ _iterator.e(_t3);
30163
30288
  case 6:
30164
- _context3.p = 6;
30289
+ _context4.p = 6;
30165
30290
  _iterator.f();
30166
- return _context3.f(6);
30291
+ return _context4.f(6);
30167
30292
  case 7:
30168
- return _context3.a(2);
30293
+ return _context4.a(2);
30169
30294
  }
30170
- }, _callee3, this, [[1, 5, 6, 7]]);
30295
+ }, _callee4, this, [[1, 5, 6, 7]]);
30171
30296
  }));
30172
30297
  function _dispatchSyncedActions(_x) {
30173
30298
  return _dispatchSyncedActions2.apply(this, arguments);
@@ -30177,46 +30302,46 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30177
30302
  }, {
30178
30303
  key: "_dispatchSyncedAction",
30179
30304
  value: function () {
30180
- var _dispatchSyncedAction2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(action) {
30181
- var message, _t3;
30182
- return _regenerator().w(function (_context4) {
30183
- while (1) switch (_context4.p = _context4.n) {
30305
+ var _dispatchSyncedAction2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(action) {
30306
+ var message, _t4;
30307
+ return _regenerator().w(function (_context5) {
30308
+ while (1) switch (_context5.p = _context5.n) {
30184
30309
  case 0:
30185
30310
  message = action;
30186
30311
  if (!(typeof message === 'string')) {
30187
- _context4.n = 3;
30312
+ _context5.n = 3;
30188
30313
  break;
30189
30314
  }
30190
- _context4.p = 1;
30315
+ _context5.p = 1;
30191
30316
  message = JSON.parse(message);
30192
- _context4.n = 3;
30317
+ _context5.n = 3;
30193
30318
  break;
30194
30319
  case 2:
30195
- _context4.p = 2;
30196
- _t3 = _context4.v;
30320
+ _context5.p = 2;
30321
+ _t4 = _context5.v;
30197
30322
  console.warn('⚠️ VoiceSDK v2: Ignoring invalid synced action string:', action);
30198
- return _context4.a(2);
30323
+ return _context5.a(2);
30199
30324
  case 3:
30200
30325
  if (!(!message || _typeof(message) !== 'object')) {
30201
- _context4.n = 4;
30326
+ _context5.n = 4;
30202
30327
  break;
30203
30328
  }
30204
- return _context4.a(2);
30329
+ return _context5.a(2);
30205
30330
  case 4:
30206
30331
  if (!(message.t === 'run_partner_script')) {
30207
- _context4.n = 6;
30332
+ _context5.n = 6;
30208
30333
  break;
30209
30334
  }
30210
- _context4.n = 5;
30335
+ _context5.n = 5;
30211
30336
  return this._handleRunPartnerScript(message);
30212
30337
  case 5:
30213
- return _context4.a(2);
30338
+ return _context5.a(2);
30214
30339
  case 6:
30215
30340
  this.emit('message', message);
30216
30341
  case 7:
30217
- return _context4.a(2);
30342
+ return _context5.a(2);
30218
30343
  }
30219
- }, _callee4, this, [[1, 2]]);
30344
+ }, _callee5, this, [[1, 2]]);
30220
30345
  }));
30221
30346
  function _dispatchSyncedAction(_x2) {
30222
30347
  return _dispatchSyncedAction2.apply(this, arguments);
@@ -30241,30 +30366,30 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30241
30366
  }, {
30242
30367
  key: "_handleRunPartnerScript",
30243
30368
  value: (function () {
30244
- var _handleRunPartnerScript2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(message) {
30369
+ var _handleRunPartnerScript2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(message) {
30245
30370
  var _this$clientToolsRegi;
30246
- var _ref, requestId, partnerId, action, args, handler, startedAt, result, elapsedMs, ok, reason, extras, preview, _elapsedMs, _t4;
30247
- return _regenerator().w(function (_context5) {
30248
- while (1) switch (_context5.p = _context5.n) {
30371
+ var _ref2, requestId, partnerId, action, args, handler, startedAt, result, elapsedMs, ok, reason, extras, preview, _elapsedMs, _t5;
30372
+ return _regenerator().w(function (_context6) {
30373
+ while (1) switch (_context6.p = _context6.n) {
30249
30374
  case 0:
30250
- _ref = message || {}, requestId = _ref.requestId, partnerId = _ref.partnerId, action = _ref.action;
30375
+ _ref2 = message || {}, requestId = _ref2.requestId, partnerId = _ref2.partnerId, action = _ref2.action;
30251
30376
  args = message && message.args || {};
30252
30377
  if (requestId) {
30253
- _context5.n = 1;
30378
+ _context6.n = 1;
30254
30379
  break;
30255
30380
  }
30256
30381
  console.warn('⚠️ VoiceSDK v2: run_partner_script missing requestId; dropping', message);
30257
- return _context5.a(2);
30382
+ return _context6.a(2);
30258
30383
  case 1:
30259
30384
  if (!_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsScript(message)) {
30260
- _context5.n = 2;
30385
+ _context6.n = 2;
30261
30386
  break;
30262
30387
  }
30263
- return _context5.a(2, this.clientScriptManager.handleRunPartnerScript(message));
30388
+ return _context6.a(2, this.clientScriptManager.handleRunPartnerScript(message));
30264
30389
  case 2:
30265
30390
  handler = (_this$clientToolsRegi = this.clientToolsRegistry) === null || _this$clientToolsRegi === void 0 || (_this$clientToolsRegi = _this$clientToolsRegi.handlers) === null || _this$clientToolsRegi === void 0 ? void 0 : _this$clientToolsRegi.get('run_partner_script');
30266
30391
  if (handler) {
30267
- _context5.n = 3;
30392
+ _context6.n = 3;
30268
30393
  break;
30269
30394
  }
30270
30395
  console.error("\uD83E\uDDE9 VoiceSDK v2: no run_partner_script handler registered (ecommerce flavor not loaded?); requestId=".concat(requestId, " partner=").concat(partnerId, " action=").concat(action));
@@ -30276,18 +30401,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30276
30401
  ok: false,
30277
30402
  error: 'no_handler_registered'
30278
30403
  });
30279
- return _context5.a(2);
30404
+ return _context6.a(2);
30280
30405
  case 3:
30281
30406
  startedAt = Date.now();
30282
30407
  console.log("\uD83E\uDDE9 VoiceSDK v2: run_partner_script dispatch partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId));
30283
- _context5.p = 4;
30284
- _context5.n = 5;
30408
+ _context6.p = 4;
30409
+ _context6.n = 5;
30285
30410
  return handler({
30286
30411
  action: action,
30287
30412
  args: args
30288
30413
  });
30289
30414
  case 5:
30290
- result = _context5.v;
30415
+ result = _context6.v;
30291
30416
  elapsedMs = Date.now() - startedAt; // Adapters return { ok, ... } themselves. Honor that flag if present;
30292
30417
  // otherwise treat presence-of-result as success.
30293
30418
  ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true; // Surface the failure reason inline so debugging doesn't require pulling
@@ -30314,25 +30439,25 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30314
30439
  ok: ok,
30315
30440
  result: result
30316
30441
  });
30317
- _context5.n = 7;
30442
+ _context6.n = 7;
30318
30443
  break;
30319
30444
  case 6:
30320
- _context5.p = 6;
30321
- _t4 = _context5.v;
30445
+ _context6.p = 6;
30446
+ _t5 = _context6.v;
30322
30447
  _elapsedMs = Date.now() - startedAt;
30323
- console.error("\uD83E\uDDE9 VoiceSDK v2: run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t4);
30448
+ console.error("\uD83E\uDDE9 VoiceSDK v2: run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t5);
30324
30449
  this.sendMessage({
30325
30450
  t: 'run_partner_script_result',
30326
30451
  requestId: requestId,
30327
30452
  partnerId: partnerId,
30328
30453
  action: action,
30329
30454
  ok: false,
30330
- error: _t4 && _t4.message || String(_t4)
30455
+ error: _t5 && _t5.message || String(_t5)
30331
30456
  });
30332
30457
  case 7:
30333
- return _context5.a(2);
30458
+ return _context6.a(2);
30334
30459
  }
30335
- }, _callee5, this, [[4, 6]]);
30460
+ }, _callee6, this, [[4, 6]]);
30336
30461
  }));
30337
30462
  function _handleRunPartnerScript(_x3) {
30338
30463
  return _handleRunPartnerScript2.apply(this, arguments);
@@ -30379,8 +30504,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30379
30504
  }, {
30380
30505
  key: "injectData",
30381
30506
  value: (function () {
30382
- var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
30383
- var _this7 = this;
30507
+ var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(data) {
30508
+ var _this9 = this;
30384
30509
  var options,
30385
30510
  trimmedData,
30386
30511
  error,
@@ -30390,15 +30515,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30390
30515
  timeout,
30391
30516
  source,
30392
30517
  message,
30393
- _args6 = arguments;
30394
- return _regenerator().w(function (_context6) {
30395
- while (1) switch (_context6.n) {
30518
+ _args7 = arguments;
30519
+ return _regenerator().w(function (_context7) {
30520
+ while (1) switch (_context7.n) {
30396
30521
  case 0:
30397
- options = _args6.length > 1 && _args6[1] !== undefined ? _args6[1] : {};
30522
+ options = _args7.length > 1 && _args7[1] !== undefined ? _args7[1] : {};
30398
30523
  // Validation: data content
30399
30524
  trimmedData = data === null || data === void 0 ? void 0 : data.trim();
30400
30525
  if (trimmedData) {
30401
- _context6.n = 1;
30526
+ _context7.n = 1;
30402
30527
  break;
30403
30528
  }
30404
30529
  error = {
@@ -30410,7 +30535,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30410
30535
  throw new Error(error.message);
30411
30536
  case 1:
30412
30537
  if (!(!this.isConnected || !this.websocket)) {
30413
- _context6.n = 2;
30538
+ _context7.n = 2;
30414
30539
  break;
30415
30540
  }
30416
30541
  _error = {
@@ -30422,7 +30547,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30422
30547
  throw new Error(_error.message);
30423
30548
  case 2:
30424
30549
  if (!(trimmedData.length > 5000)) {
30425
- _context6.n = 3;
30550
+ _context7.n = 3;
30426
30551
  break;
30427
30552
  }
30428
30553
  _error2 = {
@@ -30451,11 +30576,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30451
30576
  }
30452
30577
 
30453
30578
  // Return Promise that resolves when acknowledgment is received
30454
- return _context6.a(2, new Promise(function (_resolve, _reject) {
30579
+ return _context7.a(2, new Promise(function (_resolve, _reject) {
30455
30580
  // Set up timeout
30456
30581
  var timeoutId = setTimeout(function () {
30457
30582
  // Remove from pending map
30458
- _this7.pendingInjections.delete(correlationId);
30583
+ _this9.pendingInjections.delete(correlationId);
30459
30584
  var error = {
30460
30585
  error: 'TIMEOUT',
30461
30586
  message: 'Data injection timeout - no response from server',
@@ -30463,12 +30588,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30463
30588
  timeout: timeout
30464
30589
  };
30465
30590
  console.warn('VoiceSDK v2: Data injection timeout:', correlationId);
30466
- _this7.emit('dataInjectionError', error);
30591
+ _this9.emit('dataInjectionError', error);
30467
30592
  _reject(new Error(error.message));
30468
30593
  }, timeout);
30469
30594
 
30470
30595
  // Store Promise handlers BEFORE sending message (prevents race conditions)
30471
- _this7.pendingInjections.set(correlationId, {
30596
+ _this9.pendingInjections.set(correlationId, {
30472
30597
  resolve: function resolve(ackMessage) {
30473
30598
  clearTimeout(timeoutId);
30474
30599
  _resolve(ackMessage);
@@ -30481,17 +30606,17 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30481
30606
  });
30482
30607
 
30483
30608
  // Send message
30484
- var sent = _this7.sendMessage(message);
30609
+ var sent = _this9.sendMessage(message);
30485
30610
  if (!sent) {
30486
30611
  // Failed to send - clean up immediately
30487
- _this7.pendingInjections.delete(correlationId);
30612
+ _this9.pendingInjections.delete(correlationId);
30488
30613
  clearTimeout(timeoutId);
30489
30614
  var _error3 = {
30490
30615
  error: 'SEND_FAILED',
30491
30616
  message: 'Failed to send data injection message'
30492
30617
  };
30493
30618
  console.warn('VoiceSDK v2: Failed to send data injection message');
30494
- _this7.emit('dataInjectionError', _error3);
30619
+ _this9.emit('dataInjectionError', _error3);
30495
30620
  _reject(new Error(_error3.message));
30496
30621
  } else {
30497
30622
  console.log('📤 VoiceSDK v2: Sent data_inject message:', {
@@ -30502,7 +30627,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30502
30627
  }
30503
30628
  }));
30504
30629
  }
30505
- }, _callee6, this);
30630
+ }, _callee7, this);
30506
30631
  }));
30507
30632
  function injectData(_x4) {
30508
30633
  return _injectData.apply(this, arguments);
@@ -30634,11 +30759,29 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30634
30759
  }, {
30635
30760
  key: "handleBinaryMessage",
30636
30761
  value: (function () {
30637
- var _handleBinaryMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(data) {
30638
- var arrayBuffer, processedAudio, playbackFormat, container, encoding, pcmData, codec, decoded, _t5;
30639
- return _regenerator().w(function (_context7) {
30640
- while (1) switch (_context7.p = _context7.n) {
30762
+ var _handleBinaryMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8(data) {
30763
+ var _fromGateDrain,
30764
+ arrayBuffer,
30765
+ processedAudio,
30766
+ playbackFormat,
30767
+ container,
30768
+ encoding,
30769
+ pcmData,
30770
+ codec,
30771
+ decoded,
30772
+ _args8 = arguments,
30773
+ _t6;
30774
+ return _regenerator().w(function (_context8) {
30775
+ while (1) switch (_context8.p = _context8.n) {
30641
30776
  case 0:
30777
+ _fromGateDrain = _args8.length > 1 && _args8[1] !== undefined ? _args8[1] : false;
30778
+ if (!(!_fromGateDrain && (this._playbackGateActive || this._playbackGateFlushing))) {
30779
+ _context8.n = 1;
30780
+ break;
30781
+ }
30782
+ this._gatedAudioChunks.push(data);
30783
+ return _context8.a(2);
30784
+ case 1:
30642
30785
  // Monitor (listen-only supervision): no audio_start messages exist to clear the
30643
30786
  // player's stopped flag after a relayed stop_playing (caller barge-in flush), so
30644
30787
  // resume on the next binary frame — the server emits in order, so anything after
@@ -30649,39 +30792,39 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30649
30792
 
30650
30793
  // Convert Blob to ArrayBuffer if needed
30651
30794
  if (!(data instanceof Blob)) {
30652
- _context7.n = 2;
30795
+ _context8.n = 3;
30653
30796
  break;
30654
30797
  }
30655
- _context7.n = 1;
30798
+ _context8.n = 2;
30656
30799
  return data.arrayBuffer();
30657
- case 1:
30658
- arrayBuffer = _context7.v;
30659
- _context7.n = 3;
30660
- break;
30661
30800
  case 2:
30662
- arrayBuffer = data;
30801
+ arrayBuffer = _context8.v;
30802
+ _context8.n = 4;
30803
+ break;
30663
30804
  case 3:
30805
+ arrayBuffer = data;
30806
+ case 4:
30664
30807
  // Convert format if needed (before passing to AudioPlayer)
30665
30808
  processedAudio = arrayBuffer;
30666
30809
  if (!(this.formatConverter && this.formatConverter.needsConversion())) {
30667
- _context7.n = 7;
30810
+ _context8.n = 8;
30668
30811
  break;
30669
30812
  }
30670
- _context7.p = 4;
30671
- _context7.n = 5;
30813
+ _context8.p = 5;
30814
+ _context8.n = 6;
30672
30815
  return this.formatConverter.convert(arrayBuffer);
30673
- case 5:
30674
- processedAudio = _context7.v;
30816
+ case 6:
30817
+ processedAudio = _context8.v;
30675
30818
  console.log('✅ VoiceSDK v2: Audio converted to requested format');
30676
- _context7.n = 7;
30819
+ _context8.n = 8;
30677
30820
  break;
30678
- case 6:
30679
- _context7.p = 6;
30680
- _t5 = _context7.v;
30681
- console.error('❌ VoiceSDK v2: Format conversion failed:', _t5);
30821
+ case 7:
30822
+ _context8.p = 7;
30823
+ _t6 = _context8.v;
30824
+ console.error('❌ VoiceSDK v2: Format conversion failed:', _t6);
30682
30825
  console.warn(' Using backend format without conversion');
30683
30826
  processedAudio = arrayBuffer; // Fall back to original
30684
- case 7:
30827
+ case 8:
30685
30828
  // Determine format for playback (use requested format if converter was used, otherwise use negotiated)
30686
30829
  playbackFormat = this.formatConverter && this.formatConverter.needsConversion() ? this.requestedOutputFormat : this.outputAudioFormat || {
30687
30830
  container: this.config.outputContainer || 'raw',
@@ -30727,10 +30870,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30727
30870
  this.audioFrameCount++;
30728
30871
  this.audioPlayer.playAudio(processedAudio);
30729
30872
  }
30730
- case 8:
30731
- return _context7.a(2);
30873
+ case 9:
30874
+ return _context8.a(2);
30732
30875
  }
30733
- }, _callee7, this, [[4, 6]]);
30876
+ }, _callee8, this, [[5, 7]]);
30734
30877
  }));
30735
30878
  function handleBinaryMessage(_x5) {
30736
30879
  return _handleBinaryMessage.apply(this, arguments);
@@ -30936,7 +31079,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30936
31079
  }, {
30937
31080
  key: "hangup",
30938
31081
  value: function hangup() {
30939
- var _this8 = this;
31082
+ var _this0 = this;
30940
31083
  if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
30941
31084
  // Silent return - WebSocket already closed (expected when server rejects or already disconnected)
30942
31085
  return;
@@ -30969,11 +31112,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30969
31112
  // Close WebSocket after short delay to ensure message is sent
30970
31113
  // Backend will also close from its side after receiving hangup
30971
31114
  setTimeout(function () {
30972
- if (_this8.websocket && _this8.websocket.readyState === WebSocket.OPEN) {
30973
- _this8.websocket.close(1000, 'User hangup');
30974
- _this8.websocket = null;
31115
+ if (_this0.websocket && _this0.websocket.readyState === WebSocket.OPEN) {
31116
+ _this0.websocket.close(1000, 'User hangup');
31117
+ _this0.websocket = null;
30975
31118
  }
30976
- _this8.isConnected = false;
31119
+ _this0.isConnected = false;
30977
31120
  }, 100);
30978
31121
  } catch (error) {
30979
31122
  console.error('VoiceSDK v2: Error sending hangup message:', error);
@@ -30993,30 +31136,32 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30993
31136
  }, {
30994
31137
  key: "startRecording",
30995
31138
  value: (function () {
30996
- var _startRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
31139
+ var _startRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
31140
+ var _this1 = this;
30997
31141
  var existingMediaStream,
30998
31142
  error,
30999
31143
  _error4,
31144
+ _this$config$playback,
31000
31145
  _error5,
31001
31146
  permissionError,
31002
- _args8 = arguments,
31003
- _t6,
31147
+ _args9 = arguments,
31004
31148
  _t7,
31005
- _t8;
31006
- return _regenerator().w(function (_context8) {
31007
- while (1) switch (_context8.p = _context8.n) {
31149
+ _t8,
31150
+ _t9;
31151
+ return _regenerator().w(function (_context9) {
31152
+ while (1) switch (_context9.p = _context9.n) {
31008
31153
  case 0:
31009
- existingMediaStream = _args8.length > 0 && _args8[0] !== undefined ? _args8[0] : null;
31154
+ existingMediaStream = _args9.length > 0 && _args9[0] !== undefined ? _args9[0] : null;
31010
31155
  if (!this.isMonitor()) {
31011
- _context8.n = 1;
31156
+ _context9.n = 1;
31012
31157
  break;
31013
31158
  }
31014
31159
  // Listen-only supervision: never open the mic / never uplink audio.
31015
31160
  console.warn('VoiceSDK v2: startRecording ignored in monitor (listen-only) mode');
31016
- return _context8.a(2, false);
31161
+ return _context9.a(2, false);
31017
31162
  case 1:
31018
31163
  if (this.isConnected) {
31019
- _context8.n = 2;
31164
+ _context9.n = 2;
31020
31165
  break;
31021
31166
  }
31022
31167
  error = new Error('Not connected to voice server');
@@ -31024,14 +31169,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31024
31169
  throw error;
31025
31170
  case 2:
31026
31171
  if (!this.isRecording) {
31027
- _context8.n = 3;
31172
+ _context9.n = 3;
31028
31173
  break;
31029
31174
  }
31030
31175
  console.warn('VoiceSDK v2: Already recording');
31031
- return _context8.a(2, true);
31176
+ this._releasePlaybackGate();
31177
+ return _context9.a(2, true);
31032
31178
  case 3:
31033
31179
  if (!this.disclaimersPending) {
31034
- _context8.n = 4;
31180
+ _context9.n = 4;
31035
31181
  break;
31036
31182
  }
31037
31183
  _error4 = new Error('Server disclaimers must be accepted before recording. Call sendDisclaimerAck(true) first.');
@@ -31040,28 +31186,28 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31040
31186
  throw _error4;
31041
31187
  case 4:
31042
31188
  console.log('🎤 VoiceSDK v2: Starting recording...');
31043
- _context8.p = 5;
31189
+ _context9.p = 5;
31044
31190
  // CRITICAL for mobile browsers: Request microphone permission FIRST
31045
31191
  // iOS: Pass existingMediaStream to avoid second getUserMedia (can fail on iOS)
31046
31192
  console.log('🎤 VoiceSDK v2: Requesting microphone permission (mobile-friendly)...');
31047
- _context8.n = 6;
31193
+ _context9.n = 6;
31048
31194
  return this.audioRecorder.start(existingMediaStream ? {
31049
31195
  existingMediaStream: existingMediaStream
31050
31196
  } : {});
31051
31197
  case 6:
31052
31198
  if (!(!this.isConnected || !this.websocket || this.websocket.readyState !== WebSocket.OPEN)) {
31053
- _context8.n = 11;
31199
+ _context9.n = 11;
31054
31200
  break;
31055
31201
  }
31056
- _context8.p = 7;
31057
- _context8.n = 8;
31202
+ _context9.p = 7;
31203
+ _context9.n = 8;
31058
31204
  return this.audioRecorder.stop();
31059
31205
  case 8:
31060
- _context8.n = 10;
31206
+ _context9.n = 10;
31061
31207
  break;
31062
31208
  case 9:
31063
- _context8.p = 9;
31064
- _t6 = _context8.v;
31209
+ _context9.p = 9;
31210
+ _t7 = _context9.v;
31065
31211
  case 10:
31066
31212
  _error5 = new Error('Connection lost - server may have rejected the call');
31067
31213
  _error5.name = 'ServerRejected';
@@ -31070,9 +31216,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31070
31216
  // The error handler in VoiceInterface will handle it appropriately
31071
31217
  throw _error5;
31072
31218
  case 11:
31073
- _context8.n = 12;
31219
+ _context9.n = 12;
31074
31220
  return this.audioPlayer.resumeAudioContext();
31075
31221
  case 12:
31222
+ // Mic/AEC is up — release held greeting audio on a settled output clock.
31223
+ // Short settle delay lets Chrome's AEC output reconfiguration fully land.
31224
+ setTimeout(function () {
31225
+ return _this1._releasePlaybackGate();
31226
+ }, (_this$config$playback = this.config.playbackGateSettleMs) !== null && _this$config$playback !== void 0 ? _this$config$playback : 120);
31227
+
31076
31228
  // After permission is granted, send start_continuous_mode message to server
31077
31229
  this.sendMessage({
31078
31230
  t: 'start_continuous_mode',
@@ -31081,45 +31233,49 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31081
31233
 
31082
31234
  // Request screen wake lock to keep screen on during voice call (mobile)
31083
31235
  this._requestWakeLock();
31084
- return _context8.a(2, true);
31236
+ return _context9.a(2, true);
31085
31237
  case 13:
31086
- _context8.p = 13;
31087
- _t7 = _context8.v;
31238
+ _context9.p = 13;
31239
+ _t8 = _context9.v;
31240
+ // Recording never came up — discard any greeting audio held by the playback gate
31241
+ this._dropPlaybackGate();
31242
+
31243
+ // CRITICAL: If AudioRecorder started but connection was lost, stop it immediately
31088
31244
  if (!(this.audioRecorder && this.audioRecorder.isRecording)) {
31089
- _context8.n = 17;
31245
+ _context9.n = 17;
31090
31246
  break;
31091
31247
  }
31092
- _context8.p = 14;
31093
- _context8.n = 15;
31248
+ _context9.p = 14;
31249
+ _context9.n = 15;
31094
31250
  return this.audioRecorder.stop();
31095
31251
  case 15:
31096
- _context8.n = 17;
31252
+ _context9.n = 17;
31097
31253
  break;
31098
31254
  case 16:
31099
- _context8.p = 16;
31100
- _t8 = _context8.v;
31255
+ _context9.p = 16;
31256
+ _t9 = _context9.v;
31101
31257
  case 17:
31102
- if (!(_t7.isServerRejection || _t7.name === 'ServerRejected')) {
31103
- _context8.n = 18;
31258
+ if (!(_t8.isServerRejection || _t8.name === 'ServerRejected')) {
31259
+ _context9.n = 18;
31104
31260
  break;
31105
31261
  }
31106
- return _context8.a(2, false);
31262
+ return _context9.a(2, false);
31107
31263
  case 18:
31108
31264
  // Log and emit other errors (permission denied, etc.)
31109
- console.error('❌ VoiceSDK v2: Failed to start recording:', _t7);
31265
+ console.error('❌ VoiceSDK v2: Failed to start recording:', _t8);
31110
31266
 
31111
31267
  // Provide helpful error messages for permission issues
31112
- if (_t7.name === 'NotAllowedError' || _t7.name === 'PermissionDeniedError') {
31268
+ if (_t8.name === 'NotAllowedError' || _t8.name === 'PermissionDeniedError') {
31113
31269
  permissionError = new Error('Microphone permission denied. Please allow microphone access in your browser settings and try again.');
31114
- permissionError.name = _t7.name;
31115
- permissionError.originalError = _t7;
31270
+ permissionError.name = _t8.name;
31271
+ permissionError.originalError = _t8;
31116
31272
  this.emit('error', permissionError);
31117
31273
  } else {
31118
- this.emit('error', _t7);
31274
+ this.emit('error', _t8);
31119
31275
  }
31120
- return _context8.a(2, false);
31276
+ return _context9.a(2, false);
31121
31277
  }
31122
- }, _callee8, this, [[14, 16], [7, 9], [5, 13]]);
31278
+ }, _callee9, this, [[14, 16], [7, 9], [5, 13]]);
31123
31279
  }));
31124
31280
  function startRecording() {
31125
31281
  return _startRecording.apply(this, arguments);
@@ -31133,20 +31289,23 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31133
31289
  }, {
31134
31290
  key: "stopRecording",
31135
31291
  value: (function () {
31136
- var _stopRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
31137
- var _t9;
31138
- return _regenerator().w(function (_context9) {
31139
- while (1) switch (_context9.p = _context9.n) {
31292
+ var _stopRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
31293
+ var _t0;
31294
+ return _regenerator().w(function (_context0) {
31295
+ while (1) switch (_context0.p = _context0.n) {
31140
31296
  case 0:
31141
31297
  if (this.isRecording) {
31142
- _context9.n = 1;
31298
+ _context0.n = 1;
31143
31299
  break;
31144
31300
  }
31145
31301
  console.warn('VoiceSDK v2: Not recording');
31146
- return _context9.a(2, true);
31302
+
31303
+ // Mic never came up — discard any greeting audio still held by the playback gate
31304
+ this._dropPlaybackGate();
31305
+ return _context0.a(2, true);
31147
31306
  case 1:
31148
31307
  console.log('🛑 VoiceSDK v2: Stopping recording...');
31149
- _context9.p = 2;
31308
+ _context0.p = 2;
31150
31309
  // Send stop_continuous_mode message to server
31151
31310
 
31152
31311
  this.sendMessage({
@@ -31164,21 +31323,24 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31164
31323
  this._releaseWakeLock();
31165
31324
 
31166
31325
  // Stop capturing audio from microphone
31167
- _context9.n = 3;
31326
+ _context0.n = 3;
31168
31327
  return this.audioRecorder.stop();
31169
31328
  case 3:
31329
+ // Discard any audio still held by the first-playback gate
31330
+ this._dropPlaybackGate();
31331
+
31170
31332
  // Stop any playing audio
31171
31333
 
31172
31334
  this.stopAudioPlayback();
31173
- return _context9.a(2, true);
31335
+ return _context0.a(2, true);
31174
31336
  case 4:
31175
- _context9.p = 4;
31176
- _t9 = _context9.v;
31177
- console.error('VoiceSDK v2: Error stopping recording:', _t9);
31178
- this.emit('error', _t9);
31179
- return _context9.a(2, false);
31337
+ _context0.p = 4;
31338
+ _t0 = _context0.v;
31339
+ console.error('VoiceSDK v2: Error stopping recording:', _t0);
31340
+ this.emit('error', _t0);
31341
+ return _context0.a(2, false);
31180
31342
  }
31181
- }, _callee9, this, [[2, 4]]);
31343
+ }, _callee0, this, [[2, 4]]);
31182
31344
  }));
31183
31345
  function stopRecording() {
31184
31346
  return _stopRecording.apply(this, arguments);
@@ -31192,27 +31354,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31192
31354
  }, {
31193
31355
  key: "toggleRecording",
31194
31356
  value: (function () {
31195
- var _toggleRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
31196
- return _regenerator().w(function (_context0) {
31197
- while (1) switch (_context0.n) {
31357
+ var _toggleRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
31358
+ return _regenerator().w(function (_context1) {
31359
+ while (1) switch (_context1.n) {
31198
31360
  case 0:
31199
31361
  if (!this.isRecording) {
31200
- _context0.n = 2;
31362
+ _context1.n = 2;
31201
31363
  break;
31202
31364
  }
31203
- _context0.n = 1;
31365
+ _context1.n = 1;
31204
31366
  return this.stopRecording();
31205
31367
  case 1:
31206
- return _context0.a(2, _context0.v);
31368
+ return _context1.a(2, _context1.v);
31207
31369
  case 2:
31208
- _context0.n = 3;
31370
+ _context1.n = 3;
31209
31371
  return this.startRecording();
31210
31372
  case 3:
31211
- return _context0.a(2, _context0.v);
31373
+ return _context1.a(2, _context1.v);
31212
31374
  case 4:
31213
- return _context0.a(2);
31375
+ return _context1.a(2);
31214
31376
  }
31215
- }, _callee0, this);
31377
+ }, _callee1, this);
31216
31378
  }));
31217
31379
  function toggleRecording() {
31218
31380
  return _toggleRecording.apply(this, arguments);
@@ -31243,57 +31405,57 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31243
31405
  }, {
31244
31406
  key: "_requestWakeLock",
31245
31407
  value: (function () {
31246
- var _requestWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
31247
- var _this9 = this;
31248
- var _t0;
31249
- return _regenerator().w(function (_context1) {
31250
- while (1) switch (_context1.p = _context1.n) {
31408
+ var _requestWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
31409
+ var _this10 = this;
31410
+ var _t1;
31411
+ return _regenerator().w(function (_context10) {
31412
+ while (1) switch (_context10.p = _context10.n) {
31251
31413
  case 0:
31252
31414
  if ('wakeLock' in navigator) {
31253
- _context1.n = 1;
31415
+ _context10.n = 1;
31254
31416
  break;
31255
31417
  }
31256
31418
  console.log('📱 VoiceSDK v2: Wake Lock API not available in this browser');
31257
- return _context1.a(2);
31419
+ return _context10.a(2);
31258
31420
  case 1:
31259
31421
  if (!(this.wakeLock !== null || this._wakeLockRequesting)) {
31260
- _context1.n = 2;
31422
+ _context10.n = 2;
31261
31423
  break;
31262
31424
  }
31263
31425
  console.log('📱 VoiceSDK v2: Wake lock already active or being requested');
31264
- return _context1.a(2);
31426
+ return _context10.a(2);
31265
31427
  case 2:
31266
- _context1.p = 2;
31428
+ _context10.p = 2;
31267
31429
  this._wakeLockRequesting = true;
31268
31430
  // Request wake lock (screen type keeps screen on)
31269
- _context1.n = 3;
31431
+ _context10.n = 3;
31270
31432
  return navigator.wakeLock.request('screen');
31271
31433
  case 3:
31272
- this.wakeLock = _context1.v;
31434
+ this.wakeLock = _context10.v;
31273
31435
  console.log('📱 VoiceSDK v2: Screen wake lock activated - screen will stay on during call');
31274
31436
 
31275
31437
  // Handle wake lock release (e.g., user switches tabs, locks device)
31276
31438
  this.wakeLock.addEventListener('release', function () {
31277
31439
  console.log('📱 VoiceSDK v2: Wake lock released by system');
31278
- _this9.wakeLock = null;
31440
+ _this10.wakeLock = null;
31279
31441
  });
31280
- _context1.n = 5;
31442
+ _context10.n = 5;
31281
31443
  break;
31282
31444
  case 4:
31283
- _context1.p = 4;
31284
- _t0 = _context1.v;
31445
+ _context10.p = 4;
31446
+ _t1 = _context10.v;
31285
31447
  // Wake lock request failed (e.g., permission denied, battery saver mode)
31286
- console.warn('📱 VoiceSDK v2: Failed to request wake lock:', _t0.message);
31448
+ console.warn('📱 VoiceSDK v2: Failed to request wake lock:', _t1.message);
31287
31449
  this.wakeLock = null;
31288
31450
  // Don't throw - this is a nice-to-have feature, not critical
31289
31451
  case 5:
31290
- _context1.p = 5;
31452
+ _context10.p = 5;
31291
31453
  this._wakeLockRequesting = false;
31292
- return _context1.f(5);
31454
+ return _context10.f(5);
31293
31455
  case 6:
31294
- return _context1.a(2);
31456
+ return _context10.a(2);
31295
31457
  }
31296
- }, _callee1, this, [[2, 4, 5, 6]]);
31458
+ }, _callee10, this, [[2, 4, 5, 6]]);
31297
31459
  }));
31298
31460
  function _requestWakeLock() {
31299
31461
  return _requestWakeLock2.apply(this, arguments);
@@ -31308,34 +31470,34 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31308
31470
  }, {
31309
31471
  key: "_releaseWakeLock",
31310
31472
  value: (function () {
31311
- var _releaseWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
31312
- var _t1;
31313
- return _regenerator().w(function (_context10) {
31314
- while (1) switch (_context10.p = _context10.n) {
31473
+ var _releaseWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
31474
+ var _t10;
31475
+ return _regenerator().w(function (_context11) {
31476
+ while (1) switch (_context11.p = _context11.n) {
31315
31477
  case 0:
31316
31478
  if (!(this.wakeLock === null)) {
31317
- _context10.n = 1;
31479
+ _context11.n = 1;
31318
31480
  break;
31319
31481
  }
31320
- return _context10.a(2);
31482
+ return _context11.a(2);
31321
31483
  case 1:
31322
- _context10.p = 1;
31323
- _context10.n = 2;
31484
+ _context11.p = 1;
31485
+ _context11.n = 2;
31324
31486
  return this.wakeLock.release();
31325
31487
  case 2:
31326
31488
  this.wakeLock = null;
31327
31489
  console.log('📱 VoiceSDK v2: Screen wake lock released');
31328
- _context10.n = 4;
31490
+ _context11.n = 4;
31329
31491
  break;
31330
31492
  case 3:
31331
- _context10.p = 3;
31332
- _t1 = _context10.v;
31333
- console.warn('📱 VoiceSDK v2: Error releasing wake lock:', _t1.message);
31493
+ _context11.p = 3;
31494
+ _t10 = _context11.v;
31495
+ console.warn('📱 VoiceSDK v2: Error releasing wake lock:', _t10.message);
31334
31496
  this.wakeLock = null;
31335
31497
  case 4:
31336
- return _context10.a(2);
31498
+ return _context11.a(2);
31337
31499
  }
31338
- }, _callee10, this, [[1, 3]]);
31500
+ }, _callee11, this, [[1, 3]]);
31339
31501
  }));
31340
31502
  function _releaseWakeLock() {
31341
31503
  return _releaseWakeLock2.apply(this, arguments);
@@ -31359,21 +31521,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31359
31521
  }, {
31360
31522
  key: "handleBargeIn",
31361
31523
  value: (function () {
31362
- var _handleBargeIn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
31363
- return _regenerator().w(function (_context11) {
31364
- while (1) switch (_context11.n) {
31524
+ var _handleBargeIn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee12() {
31525
+ return _regenerator().w(function (_context12) {
31526
+ while (1) switch (_context12.n) {
31365
31527
  case 0:
31366
31528
  this.stopAudioPlayback();
31367
31529
  if (this.isRecording) {
31368
- _context11.n = 1;
31530
+ _context12.n = 1;
31369
31531
  break;
31370
31532
  }
31371
- _context11.n = 1;
31533
+ _context12.n = 1;
31372
31534
  return this.startRecording();
31373
31535
  case 1:
31374
- return _context11.a(2);
31536
+ return _context12.a(2);
31375
31537
  }
31376
- }, _callee11, this);
31538
+ }, _callee12, this);
31377
31539
  }));
31378
31540
  function handleBargeIn() {
31379
31541
  return _handleBargeIn.apply(this, arguments);
@@ -35456,7 +35618,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35456
35618
  return;
35457
35619
  }
35458
35620
  this._ensureAboutStyles();
35459
- var version = true ? "2.48.8" : 0;
35621
+ var version = true ? "2.48.10" : 0;
35460
35622
  var convId = this._getLastConversationId();
35461
35623
  var t = function t(k, fb) {
35462
35624
  try {
@@ -39645,7 +39807,7 @@ var VoiceInterface = /*#__PURE__*/function () {
39645
39807
  value: function () {
39646
39808
  var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
39647
39809
  var _this3 = this;
39648
- var isResumeCall, panel, header, toggleText, originalSection, compactSection, idleState, _idleState, activeState, stripOnly, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _vs, vs, hasDisclaimers, sendDisclaimerAckAfterMobileMicReady, _vs$lastDisclaimerPay, disclaimerTexts, _this$config$inputFor, _this$config$connecti, primeSr, audioConstraints, micStream, floatingButton, mobileFab, delayConfig, streamToUse, _this$sdk$voiceSDK, error, existingBar, _existingBar, _idleState2, _floatingButton, updateTimer, isDisclaimerDeclined, deviceInfo, _t, _t2, _t3, _t4, _t5, _t6;
39810
+ var isResumeCall, panel, header, toggleText, originalSection, compactSection, idleState, _idleState, activeState, stripOnly, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _vs, vs, hasDisclaimers, sendDisclaimerAckAfterMicReady, _vs$lastDisclaimerPay, disclaimerTexts, _this$config$inputFor, primeSr, audioConstraints, micStream, _this$config$connecti, floatingButton, mobileFab, delayConfig, streamToUse, _this$sdk$voiceSDK, error, existingBar, _existingBar, _idleState2, _floatingButton, updateTimer, isDisclaimerDeclined, deviceInfo, _t, _t2, _t3, _t4, _t5, _t6;
39649
39811
  return _regenerator().w(function (_context4) {
39650
39812
  while (1) switch (_context4.p = _context4.n) {
39651
39813
  case 0:
@@ -39838,8 +40000,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39838
40000
  // --- Disclaimer modal (after hello_ack, before greeting) ---
39839
40001
  vs = this.sdk.voiceSDK;
39840
40002
  hasDisclaimers = vs.disclaimersPending === true;
39841
- /** Mobile: server starts greeting on disclaimer_ack — must not ack until after mic + audio session are ready, or greeting plays over the OS permission sheet and gets cut off when capture starts. */
39842
- sendDisclaimerAckAfterMobileMicReady = false;
40003
+ /** Server starts the greeting on disclaimer_ack — must not ack until the mic/AEC stack is ready, or the greeting plays while capture starts: cut off by the OS permission sheet on mobile, output-clock warped by the AEC reconfiguration on desktop. */
40004
+ sendDisclaimerAckAfterMicReady = false;
39843
40005
  if (!hasDisclaimers) {
39844
40006
  _context4.n = 14;
39845
40007
  break;
@@ -39852,13 +40014,10 @@ var VoiceInterface = /*#__PURE__*/function () {
39852
40014
  texts: disclaimerTexts,
39853
40015
  direction: _this3.config.direction || 'ltr',
39854
40016
  onAccept: function onAccept() {
39855
- if (_this3.isMobile) {
39856
- sendDisclaimerAckAfterMobileMicReady = true;
39857
- resolve();
39858
- } else {
39859
- vs.sendDisclaimerAck(true);
39860
- resolve();
39861
- }
40017
+ // All platforms: ack only after the mic is up — the server starts the
40018
+ // greeting on disclaimer_ack, and capture must not start mid-greeting.
40019
+ sendDisclaimerAckAfterMicReady = true;
40020
+ resolve();
39862
40021
  },
39863
40022
  onCancel: function onCancel() {
39864
40023
  vs.sendDisclaimerAck(false);
@@ -39885,16 +40044,15 @@ var VoiceInterface = /*#__PURE__*/function () {
39885
40044
  // --- Request microphone and start listening ---
39886
40045
  console.log('✅ Proceeding to start listening...');
39887
40046
 
39888
- // On mobile: get getUserMedia stream once, pass to startListening to avoid double-call
39889
- if (!this.isMobile) {
39890
- _context4.n = 24;
39891
- break;
39892
- }
39893
- _context4.p = 15;
39894
- // This preliminary stream is REUSED as the capture stream (iOS single-stream fix),
40047
+ // Acquire the mic BEFORE the greeting can play, on all platforms.
40048
+ // Mobile: this preliminary stream is REUSED as the capture stream (iOS single-stream fix),
39895
40049
  // so it must carry the same AEC constraints as the desktop path — bare { audio: true }
39896
40050
  // leaves echoCancellation/noiseSuppression/autoGainControl (and iOS voiceIsolation) to
39897
40051
  // browser defaults, which can let speakerphone TTS echo cross VAD and trigger barge-in.
40052
+ // Desktop: getUserMedia starting mid-playback reconfigures the output device for AEC and
40053
+ // warps the MediaStream→<audio> hop clock (greeting plays fast/slow). The SDK additionally
40054
+ // gates first playback until recorder-ready; acquiring early keeps that gate window short.
40055
+ _context4.p = 15;
39898
40056
  primeSr = ((_this$config$inputFor = this.config.inputFormat) === null || _this$config$inputFor === void 0 ? void 0 : _this$config$inputFor.sampleRate) || this.config.sampleRate || 16000;
39899
40057
  audioConstraints = (0,_core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_1__.buildAudioConstraints)(primeSr, this.config.audioConstraints || {});
39900
40058
  _context4.p = 16;
@@ -39911,7 +40069,7 @@ var VoiceInterface = /*#__PURE__*/function () {
39911
40069
  _t2 = _context4.v;
39912
40070
  // Some devices/webviews reject specific constraints (OverconstrainedError etc.).
39913
40071
  // Fall back to minimal so permission still succeeds, matching prior behavior.
39914
- console.warn('⚠️ Mobile getUserMedia with AEC constraints failed, retrying minimal:', (_t2 === null || _t2 === void 0 ? void 0 : _t2.name) || _t2);
40072
+ console.warn('⚠️ getUserMedia with AEC constraints failed, retrying minimal:', (_t2 === null || _t2 === void 0 ? void 0 : _t2.name) || _t2);
39915
40073
  _context4.n = 19;
39916
40074
  return navigator.mediaDevices.getUserMedia({
39917
40075
  audio: true
@@ -39925,6 +40083,10 @@ var VoiceInterface = /*#__PURE__*/function () {
39925
40083
  _context4.n = 21;
39926
40084
  return _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_1__["default"].primeSharedContext(primeSr);
39927
40085
  case 21:
40086
+ if (!this.isMobile) {
40087
+ _context4.n = 22;
40088
+ break;
40089
+ }
39928
40090
  // Hide floating button now that permission is granted
39929
40091
  floatingButton = this.shadowRoot.getElementById('text-chat-button') || document.getElementById('text-chat-button');
39930
40092
  if (floatingButton) floatingButton.style.display = 'none';
@@ -39940,9 +40102,9 @@ var VoiceInterface = /*#__PURE__*/function () {
39940
40102
  _context4.n = 22;
39941
40103
  return (0,_shared_applyDelay_js__WEBPACK_IMPORTED_MODULE_3__.applyDelay)(delayConfig);
39942
40104
  case 22:
39943
- if (sendDisclaimerAckAfterMobileMicReady) {
40105
+ if (sendDisclaimerAckAfterMicReady) {
39944
40106
  vs.sendDisclaimerAck(true);
39945
- sendDisclaimerAckAfterMobileMicReady = false;
40107
+ sendDisclaimerAckAfterMicReady = false;
39946
40108
  }
39947
40109
  _context4.n = 24;
39948
40110
  break;