ttp-agent-sdk 2.48.7 → 2.48.9

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:16:40.754Z";
12050
+ helloMessage.lastBuildTime = "2026-08-20T23:15:39.613Z";
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.7";
21361
- var BUILD_TIME = "2026-08-20T21:16:40.754Z";
21360
+ var VERSION = "2.48.9";
21361
+ var BUILD_TIME = "2026-08-20T23:15:39.613Z";
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
@@ -28415,6 +28415,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28415
28415
 
28416
28416
  /** Set when hangup() runs with an open socket — skip resume-key clear in destroy() */
28417
28417
  _this._gracefulHangupInitiated = false;
28418
+
28419
+ /**
28420
+ * First-playback gate: while active, incoming TTS audio is held in
28421
+ * _gatedAudioChunks until the mic/AEC capture stack is up. Starting capture
28422
+ * mid-playback makes Chrome reconfigure the output device for echo cancellation,
28423
+ * which warps the MediaStream→<audio> hop clock (greeting plays fast/slow).
28424
+ */
28425
+ _this._playbackGateActive = false;
28426
+ _this._gatedAudioChunks = [];
28427
+ _this._playbackGateTimer = null;
28428
+ /** True while held chunks replay — late arrivals keep queueing so order is preserved */
28429
+ _this._playbackGateFlushing = false;
28418
28430
  _this.isRecording = false;
28419
28431
  _this.isPlaying = false;
28420
28432
  _this.isDestroyed = false;
@@ -29126,6 +29138,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29126
29138
  _this5.helloAckReceived = false;
29127
29139
  _this5.disclaimersPending = false;
29128
29140
 
29141
+ // Discard any audio still held by the first-playback gate
29142
+ _this5._dropPlaybackGate();
29143
+
29129
29144
  // Stop keepalive pings
29130
29145
  if (_this5._keepAliveInterval) {
29131
29146
  clearInterval(_this5._keepAliveInterval);
@@ -29249,7 +29264,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29249
29264
  // iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
29250
29265
  var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
29251
29266
  var env = {
29252
- sdkVersion: true ? "2.48.7" : 0,
29267
+ sdkVersion: true ? "2.48.9" : 0,
29253
29268
  ua: ua,
29254
29269
  platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
29255
29270
  mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
@@ -29264,7 +29279,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29264
29279
  } catch (e) {
29265
29280
  console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
29266
29281
  return {
29267
- sdkVersion: true ? "2.48.7" : 0
29282
+ sdkVersion: true ? "2.48.9" : 0
29268
29283
  };
29269
29284
  }
29270
29285
  }
@@ -29406,7 +29421,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29406
29421
 
29407
29422
  // Include SDK build time for debugging
29408
29423
  if (true) {
29409
- helloMessage.lastBuildTime = "2026-08-20T21:16:40.754Z";
29424
+ helloMessage.lastBuildTime = "2026-08-20T23:15:39.613Z";
29410
29425
  }
29411
29426
 
29412
29427
  // Client environment (device/browser/webview) for backend logs + Langfuse metadata
@@ -29915,6 +29930,100 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29915
29930
  }
29916
29931
  }
29917
29932
 
29933
+ /**
29934
+ * Arm the first-playback gate. While armed, handleBinaryMessage holds raw audio
29935
+ * instead of playing it. Released when the recorder is up (startRecording), on
29936
+ * failure/teardown (dropped instead), or by a safety timeout so a stuck gate can
29937
+ * never mute a live call (e.g. integrators that play audio without startRecording).
29938
+ */
29939
+ }, {
29940
+ key: "_armPlaybackGate",
29941
+ value: function _armPlaybackGate() {
29942
+ var _this7 = this;
29943
+ if (this._playbackGateActive) return;
29944
+ if (this.isMonitor && this.isMonitor()) return; // listen-only: no mic ever comes up
29945
+
29946
+ if (this.isRecording) return; // mic already up (reconnect)
29947
+
29948
+ this._playbackGateActive = true;
29949
+ this._gatedAudioChunks = [];
29950
+ clearTimeout(this._playbackGateTimer);
29951
+ this._playbackGateTimer = setTimeout(function () {
29952
+ if (_this7._playbackGateActive) {
29953
+ console.warn('⚠️ VoiceSDK v2: playback gate timed out — releasing held audio');
29954
+ _this7._releasePlaybackGate();
29955
+ }
29956
+ }, 10000);
29957
+ }
29958
+
29959
+ /**
29960
+ * Open the gate and replay held chunks through the normal audio path, in order.
29961
+ * While the drain runs, late-arriving chunks keep queueing (and are picked up by
29962
+ * the drain loop) so they can never overtake the held greeting audio.
29963
+ */
29964
+ }, {
29965
+ key: "_releasePlaybackGate",
29966
+ value: function _releasePlaybackGate() {
29967
+ var _this8 = this;
29968
+ if (!this._playbackGateActive) return;
29969
+ this._playbackGateActive = false;
29970
+ clearTimeout(this._playbackGateTimer);
29971
+ this._playbackGateTimer = null;
29972
+ if (this._gatedAudioChunks.length === 0) return;
29973
+ console.log("\uD83D\uDD13 VoiceSDK v2: playback gate released \u2014 flushing ".concat(this._gatedAudioChunks.length, " held audio chunk(s)"));
29974
+ this._playbackGateFlushing = true;
29975
+ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
29976
+ var data, _t2;
29977
+ return _regenerator().w(function (_context3) {
29978
+ while (1) switch (_context3.p = _context3.n) {
29979
+ case 0:
29980
+ _context3.p = 0;
29981
+ case 1:
29982
+ if (!(_this8._playbackGateFlushing && _this8._gatedAudioChunks.length > 0)) {
29983
+ _context3.n = 6;
29984
+ break;
29985
+ }
29986
+ data = _this8._gatedAudioChunks.shift();
29987
+ _context3.p = 2;
29988
+ _context3.n = 3;
29989
+ return _this8.handleBinaryMessage(data, true);
29990
+ case 3:
29991
+ _context3.n = 5;
29992
+ break;
29993
+ case 4:
29994
+ _context3.p = 4;
29995
+ _t2 = _context3.v;
29996
+ console.error('❌ VoiceSDK v2: gated chunk replay failed:', _t2);
29997
+ case 5:
29998
+ _context3.n = 1;
29999
+ break;
30000
+ case 6:
30001
+ _context3.p = 6;
30002
+ _this8._playbackGateFlushing = false;
30003
+ return _context3.f(6);
30004
+ case 7:
30005
+ return _context3.a(2);
30006
+ }
30007
+ }, _callee3, null, [[2, 4], [0,, 6, 7]]);
30008
+ }))();
30009
+ }
30010
+
30011
+ /** Open the gate and discard held chunks (teardown / recording failure paths). */
30012
+ }, {
30013
+ key: "_dropPlaybackGate",
30014
+ value: function _dropPlaybackGate() {
30015
+ if (!this._playbackGateActive && !this._playbackGateFlushing && this._gatedAudioChunks.length === 0) return;
30016
+ this._playbackGateActive = false;
30017
+ this._playbackGateFlushing = false; // stops any in-flight drain loop
30018
+
30019
+ clearTimeout(this._playbackGateTimer);
30020
+ this._playbackGateTimer = null;
30021
+ if (this._gatedAudioChunks.length > 0) {
30022
+ console.log("\uD83D\uDDD1\uFE0F VoiceSDK v2: playback gate dropped ".concat(this._gatedAudioChunks.length, " held chunk(s)"));
30023
+ }
30024
+ this._gatedAudioChunks = [];
30025
+ }
30026
+
29918
30027
  /**
29919
30028
  * Handle hello_ack message
29920
30029
  */
@@ -29985,6 +30094,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29985
30094
  // so config.sampleRate must be finalized first.
29986
30095
  this.helloAckReceived = true;
29987
30096
 
30097
+ // Hold the greeting until the mic/AEC stack is up — starting capture mid-playback
30098
+ // warps the MediaStream→<audio> hop clock (greeting plays fast/slow).
30099
+ this._armPlaybackGate();
30100
+
29988
30101
  // Silently re-apply the user's persisted in-call settings (voice speed /
29989
30102
  // response length) now that the session is ready — the backend applies
29990
30103
  // them without a spoken acknowledgement (announce:false).
@@ -30134,40 +30247,40 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30134
30247
  }, {
30135
30248
  key: "_dispatchSyncedActions",
30136
30249
  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) {
30250
+ var _dispatchSyncedActions2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(actions) {
30251
+ var _iterator, _step, raw, _t3;
30252
+ return _regenerator().w(function (_context4) {
30253
+ while (1) switch (_context4.p = _context4.n) {
30141
30254
  case 0:
30142
30255
  _iterator = _createForOfIteratorHelper(actions);
30143
- _context3.p = 1;
30256
+ _context4.p = 1;
30144
30257
  _iterator.s();
30145
30258
  case 2:
30146
30259
  if ((_step = _iterator.n()).done) {
30147
- _context3.n = 4;
30260
+ _context4.n = 4;
30148
30261
  break;
30149
30262
  }
30150
30263
  raw = _step.value;
30151
- _context3.n = 3;
30264
+ _context4.n = 3;
30152
30265
  return this._dispatchSyncedAction(raw);
30153
30266
  case 3:
30154
- _context3.n = 2;
30267
+ _context4.n = 2;
30155
30268
  break;
30156
30269
  case 4:
30157
- _context3.n = 6;
30270
+ _context4.n = 6;
30158
30271
  break;
30159
30272
  case 5:
30160
- _context3.p = 5;
30161
- _t2 = _context3.v;
30162
- _iterator.e(_t2);
30273
+ _context4.p = 5;
30274
+ _t3 = _context4.v;
30275
+ _iterator.e(_t3);
30163
30276
  case 6:
30164
- _context3.p = 6;
30277
+ _context4.p = 6;
30165
30278
  _iterator.f();
30166
- return _context3.f(6);
30279
+ return _context4.f(6);
30167
30280
  case 7:
30168
- return _context3.a(2);
30281
+ return _context4.a(2);
30169
30282
  }
30170
- }, _callee3, this, [[1, 5, 6, 7]]);
30283
+ }, _callee4, this, [[1, 5, 6, 7]]);
30171
30284
  }));
30172
30285
  function _dispatchSyncedActions(_x) {
30173
30286
  return _dispatchSyncedActions2.apply(this, arguments);
@@ -30177,46 +30290,46 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30177
30290
  }, {
30178
30291
  key: "_dispatchSyncedAction",
30179
30292
  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) {
30293
+ var _dispatchSyncedAction2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(action) {
30294
+ var message, _t4;
30295
+ return _regenerator().w(function (_context5) {
30296
+ while (1) switch (_context5.p = _context5.n) {
30184
30297
  case 0:
30185
30298
  message = action;
30186
30299
  if (!(typeof message === 'string')) {
30187
- _context4.n = 3;
30300
+ _context5.n = 3;
30188
30301
  break;
30189
30302
  }
30190
- _context4.p = 1;
30303
+ _context5.p = 1;
30191
30304
  message = JSON.parse(message);
30192
- _context4.n = 3;
30305
+ _context5.n = 3;
30193
30306
  break;
30194
30307
  case 2:
30195
- _context4.p = 2;
30196
- _t3 = _context4.v;
30308
+ _context5.p = 2;
30309
+ _t4 = _context5.v;
30197
30310
  console.warn('⚠️ VoiceSDK v2: Ignoring invalid synced action string:', action);
30198
- return _context4.a(2);
30311
+ return _context5.a(2);
30199
30312
  case 3:
30200
30313
  if (!(!message || _typeof(message) !== 'object')) {
30201
- _context4.n = 4;
30314
+ _context5.n = 4;
30202
30315
  break;
30203
30316
  }
30204
- return _context4.a(2);
30317
+ return _context5.a(2);
30205
30318
  case 4:
30206
30319
  if (!(message.t === 'run_partner_script')) {
30207
- _context4.n = 6;
30320
+ _context5.n = 6;
30208
30321
  break;
30209
30322
  }
30210
- _context4.n = 5;
30323
+ _context5.n = 5;
30211
30324
  return this._handleRunPartnerScript(message);
30212
30325
  case 5:
30213
- return _context4.a(2);
30326
+ return _context5.a(2);
30214
30327
  case 6:
30215
30328
  this.emit('message', message);
30216
30329
  case 7:
30217
- return _context4.a(2);
30330
+ return _context5.a(2);
30218
30331
  }
30219
- }, _callee4, this, [[1, 2]]);
30332
+ }, _callee5, this, [[1, 2]]);
30220
30333
  }));
30221
30334
  function _dispatchSyncedAction(_x2) {
30222
30335
  return _dispatchSyncedAction2.apply(this, arguments);
@@ -30241,30 +30354,30 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30241
30354
  }, {
30242
30355
  key: "_handleRunPartnerScript",
30243
30356
  value: (function () {
30244
- var _handleRunPartnerScript2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(message) {
30357
+ var _handleRunPartnerScript2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(message) {
30245
30358
  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) {
30359
+ var _ref2, requestId, partnerId, action, args, handler, startedAt, result, elapsedMs, ok, reason, extras, preview, _elapsedMs, _t5;
30360
+ return _regenerator().w(function (_context6) {
30361
+ while (1) switch (_context6.p = _context6.n) {
30249
30362
  case 0:
30250
- _ref = message || {}, requestId = _ref.requestId, partnerId = _ref.partnerId, action = _ref.action;
30363
+ _ref2 = message || {}, requestId = _ref2.requestId, partnerId = _ref2.partnerId, action = _ref2.action;
30251
30364
  args = message && message.args || {};
30252
30365
  if (requestId) {
30253
- _context5.n = 1;
30366
+ _context6.n = 1;
30254
30367
  break;
30255
30368
  }
30256
30369
  console.warn('⚠️ VoiceSDK v2: run_partner_script missing requestId; dropping', message);
30257
- return _context5.a(2);
30370
+ return _context6.a(2);
30258
30371
  case 1:
30259
30372
  if (!_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsScript(message)) {
30260
- _context5.n = 2;
30373
+ _context6.n = 2;
30261
30374
  break;
30262
30375
  }
30263
- return _context5.a(2, this.clientScriptManager.handleRunPartnerScript(message));
30376
+ return _context6.a(2, this.clientScriptManager.handleRunPartnerScript(message));
30264
30377
  case 2:
30265
30378
  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
30379
  if (handler) {
30267
- _context5.n = 3;
30380
+ _context6.n = 3;
30268
30381
  break;
30269
30382
  }
30270
30383
  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 +30389,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30276
30389
  ok: false,
30277
30390
  error: 'no_handler_registered'
30278
30391
  });
30279
- return _context5.a(2);
30392
+ return _context6.a(2);
30280
30393
  case 3:
30281
30394
  startedAt = Date.now();
30282
30395
  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;
30396
+ _context6.p = 4;
30397
+ _context6.n = 5;
30285
30398
  return handler({
30286
30399
  action: action,
30287
30400
  args: args
30288
30401
  });
30289
30402
  case 5:
30290
- result = _context5.v;
30403
+ result = _context6.v;
30291
30404
  elapsedMs = Date.now() - startedAt; // Adapters return { ok, ... } themselves. Honor that flag if present;
30292
30405
  // otherwise treat presence-of-result as success.
30293
30406
  ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true; // Surface the failure reason inline so debugging doesn't require pulling
@@ -30314,25 +30427,25 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30314
30427
  ok: ok,
30315
30428
  result: result
30316
30429
  });
30317
- _context5.n = 7;
30430
+ _context6.n = 7;
30318
30431
  break;
30319
30432
  case 6:
30320
- _context5.p = 6;
30321
- _t4 = _context5.v;
30433
+ _context6.p = 6;
30434
+ _t5 = _context6.v;
30322
30435
  _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);
30436
+ console.error("\uD83E\uDDE9 VoiceSDK v2: run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t5);
30324
30437
  this.sendMessage({
30325
30438
  t: 'run_partner_script_result',
30326
30439
  requestId: requestId,
30327
30440
  partnerId: partnerId,
30328
30441
  action: action,
30329
30442
  ok: false,
30330
- error: _t4 && _t4.message || String(_t4)
30443
+ error: _t5 && _t5.message || String(_t5)
30331
30444
  });
30332
30445
  case 7:
30333
- return _context5.a(2);
30446
+ return _context6.a(2);
30334
30447
  }
30335
- }, _callee5, this, [[4, 6]]);
30448
+ }, _callee6, this, [[4, 6]]);
30336
30449
  }));
30337
30450
  function _handleRunPartnerScript(_x3) {
30338
30451
  return _handleRunPartnerScript2.apply(this, arguments);
@@ -30379,8 +30492,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30379
30492
  }, {
30380
30493
  key: "injectData",
30381
30494
  value: (function () {
30382
- var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
30383
- var _this7 = this;
30495
+ var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(data) {
30496
+ var _this9 = this;
30384
30497
  var options,
30385
30498
  trimmedData,
30386
30499
  error,
@@ -30390,15 +30503,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30390
30503
  timeout,
30391
30504
  source,
30392
30505
  message,
30393
- _args6 = arguments;
30394
- return _regenerator().w(function (_context6) {
30395
- while (1) switch (_context6.n) {
30506
+ _args7 = arguments;
30507
+ return _regenerator().w(function (_context7) {
30508
+ while (1) switch (_context7.n) {
30396
30509
  case 0:
30397
- options = _args6.length > 1 && _args6[1] !== undefined ? _args6[1] : {};
30510
+ options = _args7.length > 1 && _args7[1] !== undefined ? _args7[1] : {};
30398
30511
  // Validation: data content
30399
30512
  trimmedData = data === null || data === void 0 ? void 0 : data.trim();
30400
30513
  if (trimmedData) {
30401
- _context6.n = 1;
30514
+ _context7.n = 1;
30402
30515
  break;
30403
30516
  }
30404
30517
  error = {
@@ -30410,7 +30523,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30410
30523
  throw new Error(error.message);
30411
30524
  case 1:
30412
30525
  if (!(!this.isConnected || !this.websocket)) {
30413
- _context6.n = 2;
30526
+ _context7.n = 2;
30414
30527
  break;
30415
30528
  }
30416
30529
  _error = {
@@ -30422,7 +30535,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30422
30535
  throw new Error(_error.message);
30423
30536
  case 2:
30424
30537
  if (!(trimmedData.length > 5000)) {
30425
- _context6.n = 3;
30538
+ _context7.n = 3;
30426
30539
  break;
30427
30540
  }
30428
30541
  _error2 = {
@@ -30451,11 +30564,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30451
30564
  }
30452
30565
 
30453
30566
  // Return Promise that resolves when acknowledgment is received
30454
- return _context6.a(2, new Promise(function (_resolve, _reject) {
30567
+ return _context7.a(2, new Promise(function (_resolve, _reject) {
30455
30568
  // Set up timeout
30456
30569
  var timeoutId = setTimeout(function () {
30457
30570
  // Remove from pending map
30458
- _this7.pendingInjections.delete(correlationId);
30571
+ _this9.pendingInjections.delete(correlationId);
30459
30572
  var error = {
30460
30573
  error: 'TIMEOUT',
30461
30574
  message: 'Data injection timeout - no response from server',
@@ -30463,12 +30576,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30463
30576
  timeout: timeout
30464
30577
  };
30465
30578
  console.warn('VoiceSDK v2: Data injection timeout:', correlationId);
30466
- _this7.emit('dataInjectionError', error);
30579
+ _this9.emit('dataInjectionError', error);
30467
30580
  _reject(new Error(error.message));
30468
30581
  }, timeout);
30469
30582
 
30470
30583
  // Store Promise handlers BEFORE sending message (prevents race conditions)
30471
- _this7.pendingInjections.set(correlationId, {
30584
+ _this9.pendingInjections.set(correlationId, {
30472
30585
  resolve: function resolve(ackMessage) {
30473
30586
  clearTimeout(timeoutId);
30474
30587
  _resolve(ackMessage);
@@ -30481,17 +30594,17 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30481
30594
  });
30482
30595
 
30483
30596
  // Send message
30484
- var sent = _this7.sendMessage(message);
30597
+ var sent = _this9.sendMessage(message);
30485
30598
  if (!sent) {
30486
30599
  // Failed to send - clean up immediately
30487
- _this7.pendingInjections.delete(correlationId);
30600
+ _this9.pendingInjections.delete(correlationId);
30488
30601
  clearTimeout(timeoutId);
30489
30602
  var _error3 = {
30490
30603
  error: 'SEND_FAILED',
30491
30604
  message: 'Failed to send data injection message'
30492
30605
  };
30493
30606
  console.warn('VoiceSDK v2: Failed to send data injection message');
30494
- _this7.emit('dataInjectionError', _error3);
30607
+ _this9.emit('dataInjectionError', _error3);
30495
30608
  _reject(new Error(_error3.message));
30496
30609
  } else {
30497
30610
  console.log('📤 VoiceSDK v2: Sent data_inject message:', {
@@ -30502,7 +30615,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30502
30615
  }
30503
30616
  }));
30504
30617
  }
30505
- }, _callee6, this);
30618
+ }, _callee7, this);
30506
30619
  }));
30507
30620
  function injectData(_x4) {
30508
30621
  return _injectData.apply(this, arguments);
@@ -30634,11 +30747,29 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30634
30747
  }, {
30635
30748
  key: "handleBinaryMessage",
30636
30749
  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) {
30750
+ var _handleBinaryMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8(data) {
30751
+ var _fromGateDrain,
30752
+ arrayBuffer,
30753
+ processedAudio,
30754
+ playbackFormat,
30755
+ container,
30756
+ encoding,
30757
+ pcmData,
30758
+ codec,
30759
+ decoded,
30760
+ _args8 = arguments,
30761
+ _t6;
30762
+ return _regenerator().w(function (_context8) {
30763
+ while (1) switch (_context8.p = _context8.n) {
30641
30764
  case 0:
30765
+ _fromGateDrain = _args8.length > 1 && _args8[1] !== undefined ? _args8[1] : false;
30766
+ if (!(!_fromGateDrain && (this._playbackGateActive || this._playbackGateFlushing))) {
30767
+ _context8.n = 1;
30768
+ break;
30769
+ }
30770
+ this._gatedAudioChunks.push(data);
30771
+ return _context8.a(2);
30772
+ case 1:
30642
30773
  // Monitor (listen-only supervision): no audio_start messages exist to clear the
30643
30774
  // player's stopped flag after a relayed stop_playing (caller barge-in flush), so
30644
30775
  // resume on the next binary frame — the server emits in order, so anything after
@@ -30649,39 +30780,39 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30649
30780
 
30650
30781
  // Convert Blob to ArrayBuffer if needed
30651
30782
  if (!(data instanceof Blob)) {
30652
- _context7.n = 2;
30783
+ _context8.n = 3;
30653
30784
  break;
30654
30785
  }
30655
- _context7.n = 1;
30786
+ _context8.n = 2;
30656
30787
  return data.arrayBuffer();
30657
- case 1:
30658
- arrayBuffer = _context7.v;
30659
- _context7.n = 3;
30660
- break;
30661
30788
  case 2:
30662
- arrayBuffer = data;
30789
+ arrayBuffer = _context8.v;
30790
+ _context8.n = 4;
30791
+ break;
30663
30792
  case 3:
30793
+ arrayBuffer = data;
30794
+ case 4:
30664
30795
  // Convert format if needed (before passing to AudioPlayer)
30665
30796
  processedAudio = arrayBuffer;
30666
30797
  if (!(this.formatConverter && this.formatConverter.needsConversion())) {
30667
- _context7.n = 7;
30798
+ _context8.n = 8;
30668
30799
  break;
30669
30800
  }
30670
- _context7.p = 4;
30671
- _context7.n = 5;
30801
+ _context8.p = 5;
30802
+ _context8.n = 6;
30672
30803
  return this.formatConverter.convert(arrayBuffer);
30673
- case 5:
30674
- processedAudio = _context7.v;
30804
+ case 6:
30805
+ processedAudio = _context8.v;
30675
30806
  console.log('✅ VoiceSDK v2: Audio converted to requested format');
30676
- _context7.n = 7;
30807
+ _context8.n = 8;
30677
30808
  break;
30678
- case 6:
30679
- _context7.p = 6;
30680
- _t5 = _context7.v;
30681
- console.error('❌ VoiceSDK v2: Format conversion failed:', _t5);
30809
+ case 7:
30810
+ _context8.p = 7;
30811
+ _t6 = _context8.v;
30812
+ console.error('❌ VoiceSDK v2: Format conversion failed:', _t6);
30682
30813
  console.warn(' Using backend format without conversion');
30683
30814
  processedAudio = arrayBuffer; // Fall back to original
30684
- case 7:
30815
+ case 8:
30685
30816
  // Determine format for playback (use requested format if converter was used, otherwise use negotiated)
30686
30817
  playbackFormat = this.formatConverter && this.formatConverter.needsConversion() ? this.requestedOutputFormat : this.outputAudioFormat || {
30687
30818
  container: this.config.outputContainer || 'raw',
@@ -30727,10 +30858,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30727
30858
  this.audioFrameCount++;
30728
30859
  this.audioPlayer.playAudio(processedAudio);
30729
30860
  }
30730
- case 8:
30731
- return _context7.a(2);
30861
+ case 9:
30862
+ return _context8.a(2);
30732
30863
  }
30733
- }, _callee7, this, [[4, 6]]);
30864
+ }, _callee8, this, [[5, 7]]);
30734
30865
  }));
30735
30866
  function handleBinaryMessage(_x5) {
30736
30867
  return _handleBinaryMessage.apply(this, arguments);
@@ -30936,7 +31067,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30936
31067
  }, {
30937
31068
  key: "hangup",
30938
31069
  value: function hangup() {
30939
- var _this8 = this;
31070
+ var _this0 = this;
30940
31071
  if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
30941
31072
  // Silent return - WebSocket already closed (expected when server rejects or already disconnected)
30942
31073
  return;
@@ -30969,11 +31100,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30969
31100
  // Close WebSocket after short delay to ensure message is sent
30970
31101
  // Backend will also close from its side after receiving hangup
30971
31102
  setTimeout(function () {
30972
- if (_this8.websocket && _this8.websocket.readyState === WebSocket.OPEN) {
30973
- _this8.websocket.close(1000, 'User hangup');
30974
- _this8.websocket = null;
31103
+ if (_this0.websocket && _this0.websocket.readyState === WebSocket.OPEN) {
31104
+ _this0.websocket.close(1000, 'User hangup');
31105
+ _this0.websocket = null;
30975
31106
  }
30976
- _this8.isConnected = false;
31107
+ _this0.isConnected = false;
30977
31108
  }, 100);
30978
31109
  } catch (error) {
30979
31110
  console.error('VoiceSDK v2: Error sending hangup message:', error);
@@ -30993,30 +31124,32 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30993
31124
  }, {
30994
31125
  key: "startRecording",
30995
31126
  value: (function () {
30996
- var _startRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
31127
+ var _startRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
31128
+ var _this1 = this;
30997
31129
  var existingMediaStream,
30998
31130
  error,
30999
31131
  _error4,
31132
+ _this$config$playback,
31000
31133
  _error5,
31001
31134
  permissionError,
31002
- _args8 = arguments,
31003
- _t6,
31135
+ _args9 = arguments,
31004
31136
  _t7,
31005
- _t8;
31006
- return _regenerator().w(function (_context8) {
31007
- while (1) switch (_context8.p = _context8.n) {
31137
+ _t8,
31138
+ _t9;
31139
+ return _regenerator().w(function (_context9) {
31140
+ while (1) switch (_context9.p = _context9.n) {
31008
31141
  case 0:
31009
- existingMediaStream = _args8.length > 0 && _args8[0] !== undefined ? _args8[0] : null;
31142
+ existingMediaStream = _args9.length > 0 && _args9[0] !== undefined ? _args9[0] : null;
31010
31143
  if (!this.isMonitor()) {
31011
- _context8.n = 1;
31144
+ _context9.n = 1;
31012
31145
  break;
31013
31146
  }
31014
31147
  // Listen-only supervision: never open the mic / never uplink audio.
31015
31148
  console.warn('VoiceSDK v2: startRecording ignored in monitor (listen-only) mode');
31016
- return _context8.a(2, false);
31149
+ return _context9.a(2, false);
31017
31150
  case 1:
31018
31151
  if (this.isConnected) {
31019
- _context8.n = 2;
31152
+ _context9.n = 2;
31020
31153
  break;
31021
31154
  }
31022
31155
  error = new Error('Not connected to voice server');
@@ -31024,14 +31157,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31024
31157
  throw error;
31025
31158
  case 2:
31026
31159
  if (!this.isRecording) {
31027
- _context8.n = 3;
31160
+ _context9.n = 3;
31028
31161
  break;
31029
31162
  }
31030
31163
  console.warn('VoiceSDK v2: Already recording');
31031
- return _context8.a(2, true);
31164
+ this._releasePlaybackGate();
31165
+ return _context9.a(2, true);
31032
31166
  case 3:
31033
31167
  if (!this.disclaimersPending) {
31034
- _context8.n = 4;
31168
+ _context9.n = 4;
31035
31169
  break;
31036
31170
  }
31037
31171
  _error4 = new Error('Server disclaimers must be accepted before recording. Call sendDisclaimerAck(true) first.');
@@ -31040,28 +31174,28 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31040
31174
  throw _error4;
31041
31175
  case 4:
31042
31176
  console.log('🎤 VoiceSDK v2: Starting recording...');
31043
- _context8.p = 5;
31177
+ _context9.p = 5;
31044
31178
  // CRITICAL for mobile browsers: Request microphone permission FIRST
31045
31179
  // iOS: Pass existingMediaStream to avoid second getUserMedia (can fail on iOS)
31046
31180
  console.log('🎤 VoiceSDK v2: Requesting microphone permission (mobile-friendly)...');
31047
- _context8.n = 6;
31181
+ _context9.n = 6;
31048
31182
  return this.audioRecorder.start(existingMediaStream ? {
31049
31183
  existingMediaStream: existingMediaStream
31050
31184
  } : {});
31051
31185
  case 6:
31052
31186
  if (!(!this.isConnected || !this.websocket || this.websocket.readyState !== WebSocket.OPEN)) {
31053
- _context8.n = 11;
31187
+ _context9.n = 11;
31054
31188
  break;
31055
31189
  }
31056
- _context8.p = 7;
31057
- _context8.n = 8;
31190
+ _context9.p = 7;
31191
+ _context9.n = 8;
31058
31192
  return this.audioRecorder.stop();
31059
31193
  case 8:
31060
- _context8.n = 10;
31194
+ _context9.n = 10;
31061
31195
  break;
31062
31196
  case 9:
31063
- _context8.p = 9;
31064
- _t6 = _context8.v;
31197
+ _context9.p = 9;
31198
+ _t7 = _context9.v;
31065
31199
  case 10:
31066
31200
  _error5 = new Error('Connection lost - server may have rejected the call');
31067
31201
  _error5.name = 'ServerRejected';
@@ -31070,9 +31204,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31070
31204
  // The error handler in VoiceInterface will handle it appropriately
31071
31205
  throw _error5;
31072
31206
  case 11:
31073
- _context8.n = 12;
31207
+ _context9.n = 12;
31074
31208
  return this.audioPlayer.resumeAudioContext();
31075
31209
  case 12:
31210
+ // Mic/AEC is up — release held greeting audio on a settled output clock.
31211
+ // Short settle delay lets Chrome's AEC output reconfiguration fully land.
31212
+ setTimeout(function () {
31213
+ return _this1._releasePlaybackGate();
31214
+ }, (_this$config$playback = this.config.playbackGateSettleMs) !== null && _this$config$playback !== void 0 ? _this$config$playback : 120);
31215
+
31076
31216
  // After permission is granted, send start_continuous_mode message to server
31077
31217
  this.sendMessage({
31078
31218
  t: 'start_continuous_mode',
@@ -31081,45 +31221,49 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31081
31221
 
31082
31222
  // Request screen wake lock to keep screen on during voice call (mobile)
31083
31223
  this._requestWakeLock();
31084
- return _context8.a(2, true);
31224
+ return _context9.a(2, true);
31085
31225
  case 13:
31086
- _context8.p = 13;
31087
- _t7 = _context8.v;
31226
+ _context9.p = 13;
31227
+ _t8 = _context9.v;
31228
+ // Recording never came up — discard any greeting audio held by the playback gate
31229
+ this._dropPlaybackGate();
31230
+
31231
+ // CRITICAL: If AudioRecorder started but connection was lost, stop it immediately
31088
31232
  if (!(this.audioRecorder && this.audioRecorder.isRecording)) {
31089
- _context8.n = 17;
31233
+ _context9.n = 17;
31090
31234
  break;
31091
31235
  }
31092
- _context8.p = 14;
31093
- _context8.n = 15;
31236
+ _context9.p = 14;
31237
+ _context9.n = 15;
31094
31238
  return this.audioRecorder.stop();
31095
31239
  case 15:
31096
- _context8.n = 17;
31240
+ _context9.n = 17;
31097
31241
  break;
31098
31242
  case 16:
31099
- _context8.p = 16;
31100
- _t8 = _context8.v;
31243
+ _context9.p = 16;
31244
+ _t9 = _context9.v;
31101
31245
  case 17:
31102
- if (!(_t7.isServerRejection || _t7.name === 'ServerRejected')) {
31103
- _context8.n = 18;
31246
+ if (!(_t8.isServerRejection || _t8.name === 'ServerRejected')) {
31247
+ _context9.n = 18;
31104
31248
  break;
31105
31249
  }
31106
- return _context8.a(2, false);
31250
+ return _context9.a(2, false);
31107
31251
  case 18:
31108
31252
  // Log and emit other errors (permission denied, etc.)
31109
- console.error('❌ VoiceSDK v2: Failed to start recording:', _t7);
31253
+ console.error('❌ VoiceSDK v2: Failed to start recording:', _t8);
31110
31254
 
31111
31255
  // Provide helpful error messages for permission issues
31112
- if (_t7.name === 'NotAllowedError' || _t7.name === 'PermissionDeniedError') {
31256
+ if (_t8.name === 'NotAllowedError' || _t8.name === 'PermissionDeniedError') {
31113
31257
  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;
31258
+ permissionError.name = _t8.name;
31259
+ permissionError.originalError = _t8;
31116
31260
  this.emit('error', permissionError);
31117
31261
  } else {
31118
- this.emit('error', _t7);
31262
+ this.emit('error', _t8);
31119
31263
  }
31120
- return _context8.a(2, false);
31264
+ return _context9.a(2, false);
31121
31265
  }
31122
- }, _callee8, this, [[14, 16], [7, 9], [5, 13]]);
31266
+ }, _callee9, this, [[14, 16], [7, 9], [5, 13]]);
31123
31267
  }));
31124
31268
  function startRecording() {
31125
31269
  return _startRecording.apply(this, arguments);
@@ -31133,20 +31277,23 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31133
31277
  }, {
31134
31278
  key: "stopRecording",
31135
31279
  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) {
31280
+ var _stopRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
31281
+ var _t0;
31282
+ return _regenerator().w(function (_context0) {
31283
+ while (1) switch (_context0.p = _context0.n) {
31140
31284
  case 0:
31141
31285
  if (this.isRecording) {
31142
- _context9.n = 1;
31286
+ _context0.n = 1;
31143
31287
  break;
31144
31288
  }
31145
31289
  console.warn('VoiceSDK v2: Not recording');
31146
- return _context9.a(2, true);
31290
+
31291
+ // Mic never came up — discard any greeting audio still held by the playback gate
31292
+ this._dropPlaybackGate();
31293
+ return _context0.a(2, true);
31147
31294
  case 1:
31148
31295
  console.log('🛑 VoiceSDK v2: Stopping recording...');
31149
- _context9.p = 2;
31296
+ _context0.p = 2;
31150
31297
  // Send stop_continuous_mode message to server
31151
31298
 
31152
31299
  this.sendMessage({
@@ -31164,21 +31311,24 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31164
31311
  this._releaseWakeLock();
31165
31312
 
31166
31313
  // Stop capturing audio from microphone
31167
- _context9.n = 3;
31314
+ _context0.n = 3;
31168
31315
  return this.audioRecorder.stop();
31169
31316
  case 3:
31317
+ // Discard any audio still held by the first-playback gate
31318
+ this._dropPlaybackGate();
31319
+
31170
31320
  // Stop any playing audio
31171
31321
 
31172
31322
  this.stopAudioPlayback();
31173
- return _context9.a(2, true);
31323
+ return _context0.a(2, true);
31174
31324
  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);
31325
+ _context0.p = 4;
31326
+ _t0 = _context0.v;
31327
+ console.error('VoiceSDK v2: Error stopping recording:', _t0);
31328
+ this.emit('error', _t0);
31329
+ return _context0.a(2, false);
31180
31330
  }
31181
- }, _callee9, this, [[2, 4]]);
31331
+ }, _callee0, this, [[2, 4]]);
31182
31332
  }));
31183
31333
  function stopRecording() {
31184
31334
  return _stopRecording.apply(this, arguments);
@@ -31192,27 +31342,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31192
31342
  }, {
31193
31343
  key: "toggleRecording",
31194
31344
  value: (function () {
31195
- var _toggleRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
31196
- return _regenerator().w(function (_context0) {
31197
- while (1) switch (_context0.n) {
31345
+ var _toggleRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
31346
+ return _regenerator().w(function (_context1) {
31347
+ while (1) switch (_context1.n) {
31198
31348
  case 0:
31199
31349
  if (!this.isRecording) {
31200
- _context0.n = 2;
31350
+ _context1.n = 2;
31201
31351
  break;
31202
31352
  }
31203
- _context0.n = 1;
31353
+ _context1.n = 1;
31204
31354
  return this.stopRecording();
31205
31355
  case 1:
31206
- return _context0.a(2, _context0.v);
31356
+ return _context1.a(2, _context1.v);
31207
31357
  case 2:
31208
- _context0.n = 3;
31358
+ _context1.n = 3;
31209
31359
  return this.startRecording();
31210
31360
  case 3:
31211
- return _context0.a(2, _context0.v);
31361
+ return _context1.a(2, _context1.v);
31212
31362
  case 4:
31213
- return _context0.a(2);
31363
+ return _context1.a(2);
31214
31364
  }
31215
- }, _callee0, this);
31365
+ }, _callee1, this);
31216
31366
  }));
31217
31367
  function toggleRecording() {
31218
31368
  return _toggleRecording.apply(this, arguments);
@@ -31243,57 +31393,57 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31243
31393
  }, {
31244
31394
  key: "_requestWakeLock",
31245
31395
  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) {
31396
+ var _requestWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
31397
+ var _this10 = this;
31398
+ var _t1;
31399
+ return _regenerator().w(function (_context10) {
31400
+ while (1) switch (_context10.p = _context10.n) {
31251
31401
  case 0:
31252
31402
  if ('wakeLock' in navigator) {
31253
- _context1.n = 1;
31403
+ _context10.n = 1;
31254
31404
  break;
31255
31405
  }
31256
31406
  console.log('📱 VoiceSDK v2: Wake Lock API not available in this browser');
31257
- return _context1.a(2);
31407
+ return _context10.a(2);
31258
31408
  case 1:
31259
31409
  if (!(this.wakeLock !== null || this._wakeLockRequesting)) {
31260
- _context1.n = 2;
31410
+ _context10.n = 2;
31261
31411
  break;
31262
31412
  }
31263
31413
  console.log('📱 VoiceSDK v2: Wake lock already active or being requested');
31264
- return _context1.a(2);
31414
+ return _context10.a(2);
31265
31415
  case 2:
31266
- _context1.p = 2;
31416
+ _context10.p = 2;
31267
31417
  this._wakeLockRequesting = true;
31268
31418
  // Request wake lock (screen type keeps screen on)
31269
- _context1.n = 3;
31419
+ _context10.n = 3;
31270
31420
  return navigator.wakeLock.request('screen');
31271
31421
  case 3:
31272
- this.wakeLock = _context1.v;
31422
+ this.wakeLock = _context10.v;
31273
31423
  console.log('📱 VoiceSDK v2: Screen wake lock activated - screen will stay on during call');
31274
31424
 
31275
31425
  // Handle wake lock release (e.g., user switches tabs, locks device)
31276
31426
  this.wakeLock.addEventListener('release', function () {
31277
31427
  console.log('📱 VoiceSDK v2: Wake lock released by system');
31278
- _this9.wakeLock = null;
31428
+ _this10.wakeLock = null;
31279
31429
  });
31280
- _context1.n = 5;
31430
+ _context10.n = 5;
31281
31431
  break;
31282
31432
  case 4:
31283
- _context1.p = 4;
31284
- _t0 = _context1.v;
31433
+ _context10.p = 4;
31434
+ _t1 = _context10.v;
31285
31435
  // Wake lock request failed (e.g., permission denied, battery saver mode)
31286
- console.warn('📱 VoiceSDK v2: Failed to request wake lock:', _t0.message);
31436
+ console.warn('📱 VoiceSDK v2: Failed to request wake lock:', _t1.message);
31287
31437
  this.wakeLock = null;
31288
31438
  // Don't throw - this is a nice-to-have feature, not critical
31289
31439
  case 5:
31290
- _context1.p = 5;
31440
+ _context10.p = 5;
31291
31441
  this._wakeLockRequesting = false;
31292
- return _context1.f(5);
31442
+ return _context10.f(5);
31293
31443
  case 6:
31294
- return _context1.a(2);
31444
+ return _context10.a(2);
31295
31445
  }
31296
- }, _callee1, this, [[2, 4, 5, 6]]);
31446
+ }, _callee10, this, [[2, 4, 5, 6]]);
31297
31447
  }));
31298
31448
  function _requestWakeLock() {
31299
31449
  return _requestWakeLock2.apply(this, arguments);
@@ -31308,34 +31458,34 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31308
31458
  }, {
31309
31459
  key: "_releaseWakeLock",
31310
31460
  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) {
31461
+ var _releaseWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
31462
+ var _t10;
31463
+ return _regenerator().w(function (_context11) {
31464
+ while (1) switch (_context11.p = _context11.n) {
31315
31465
  case 0:
31316
31466
  if (!(this.wakeLock === null)) {
31317
- _context10.n = 1;
31467
+ _context11.n = 1;
31318
31468
  break;
31319
31469
  }
31320
- return _context10.a(2);
31470
+ return _context11.a(2);
31321
31471
  case 1:
31322
- _context10.p = 1;
31323
- _context10.n = 2;
31472
+ _context11.p = 1;
31473
+ _context11.n = 2;
31324
31474
  return this.wakeLock.release();
31325
31475
  case 2:
31326
31476
  this.wakeLock = null;
31327
31477
  console.log('📱 VoiceSDK v2: Screen wake lock released');
31328
- _context10.n = 4;
31478
+ _context11.n = 4;
31329
31479
  break;
31330
31480
  case 3:
31331
- _context10.p = 3;
31332
- _t1 = _context10.v;
31333
- console.warn('📱 VoiceSDK v2: Error releasing wake lock:', _t1.message);
31481
+ _context11.p = 3;
31482
+ _t10 = _context11.v;
31483
+ console.warn('📱 VoiceSDK v2: Error releasing wake lock:', _t10.message);
31334
31484
  this.wakeLock = null;
31335
31485
  case 4:
31336
- return _context10.a(2);
31486
+ return _context11.a(2);
31337
31487
  }
31338
- }, _callee10, this, [[1, 3]]);
31488
+ }, _callee11, this, [[1, 3]]);
31339
31489
  }));
31340
31490
  function _releaseWakeLock() {
31341
31491
  return _releaseWakeLock2.apply(this, arguments);
@@ -31359,21 +31509,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
31359
31509
  }, {
31360
31510
  key: "handleBargeIn",
31361
31511
  value: (function () {
31362
- var _handleBargeIn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
31363
- return _regenerator().w(function (_context11) {
31364
- while (1) switch (_context11.n) {
31512
+ var _handleBargeIn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee12() {
31513
+ return _regenerator().w(function (_context12) {
31514
+ while (1) switch (_context12.n) {
31365
31515
  case 0:
31366
31516
  this.stopAudioPlayback();
31367
31517
  if (this.isRecording) {
31368
- _context11.n = 1;
31518
+ _context12.n = 1;
31369
31519
  break;
31370
31520
  }
31371
- _context11.n = 1;
31521
+ _context12.n = 1;
31372
31522
  return this.startRecording();
31373
31523
  case 1:
31374
- return _context11.a(2);
31524
+ return _context12.a(2);
31375
31525
  }
31376
- }, _callee11, this);
31526
+ }, _callee12, this);
31377
31527
  }));
31378
31528
  function handleBargeIn() {
31379
31529
  return _handleBargeIn.apply(this, arguments);
@@ -35456,7 +35606,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35456
35606
  return;
35457
35607
  }
35458
35608
  this._ensureAboutStyles();
35459
- var version = true ? "2.48.7" : 0;
35609
+ var version = true ? "2.48.9" : 0;
35460
35610
  var convId = this._getLastConversationId();
35461
35611
  var t = function t(k, fb) {
35462
35612
  try {
@@ -39645,7 +39795,7 @@ var VoiceInterface = /*#__PURE__*/function () {
39645
39795
  value: function () {
39646
39796
  var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
39647
39797
  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;
39798
+ 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
39799
  return _regenerator().w(function (_context4) {
39650
39800
  while (1) switch (_context4.p = _context4.n) {
39651
39801
  case 0:
@@ -39838,8 +39988,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39838
39988
  // --- Disclaimer modal (after hello_ack, before greeting) ---
39839
39989
  vs = this.sdk.voiceSDK;
39840
39990
  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;
39991
+ /** 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. */
39992
+ sendDisclaimerAckAfterMicReady = false;
39843
39993
  if (!hasDisclaimers) {
39844
39994
  _context4.n = 14;
39845
39995
  break;
@@ -39852,13 +40002,10 @@ var VoiceInterface = /*#__PURE__*/function () {
39852
40002
  texts: disclaimerTexts,
39853
40003
  direction: _this3.config.direction || 'ltr',
39854
40004
  onAccept: function onAccept() {
39855
- if (_this3.isMobile) {
39856
- sendDisclaimerAckAfterMobileMicReady = true;
39857
- resolve();
39858
- } else {
39859
- vs.sendDisclaimerAck(true);
39860
- resolve();
39861
- }
40005
+ // All platforms: ack only after the mic is up — the server starts the
40006
+ // greeting on disclaimer_ack, and capture must not start mid-greeting.
40007
+ sendDisclaimerAckAfterMicReady = true;
40008
+ resolve();
39862
40009
  },
39863
40010
  onCancel: function onCancel() {
39864
40011
  vs.sendDisclaimerAck(false);
@@ -39885,16 +40032,15 @@ var VoiceInterface = /*#__PURE__*/function () {
39885
40032
  // --- Request microphone and start listening ---
39886
40033
  console.log('✅ Proceeding to start listening...');
39887
40034
 
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),
40035
+ // Acquire the mic BEFORE the greeting can play, on all platforms.
40036
+ // Mobile: this preliminary stream is REUSED as the capture stream (iOS single-stream fix),
39895
40037
  // so it must carry the same AEC constraints as the desktop path — bare { audio: true }
39896
40038
  // leaves echoCancellation/noiseSuppression/autoGainControl (and iOS voiceIsolation) to
39897
40039
  // browser defaults, which can let speakerphone TTS echo cross VAD and trigger barge-in.
40040
+ // Desktop: getUserMedia starting mid-playback reconfigures the output device for AEC and
40041
+ // warps the MediaStream→<audio> hop clock (greeting plays fast/slow). The SDK additionally
40042
+ // gates first playback until recorder-ready; acquiring early keeps that gate window short.
40043
+ _context4.p = 15;
39898
40044
  primeSr = ((_this$config$inputFor = this.config.inputFormat) === null || _this$config$inputFor === void 0 ? void 0 : _this$config$inputFor.sampleRate) || this.config.sampleRate || 16000;
39899
40045
  audioConstraints = (0,_core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_1__.buildAudioConstraints)(primeSr, this.config.audioConstraints || {});
39900
40046
  _context4.p = 16;
@@ -39911,7 +40057,7 @@ var VoiceInterface = /*#__PURE__*/function () {
39911
40057
  _t2 = _context4.v;
39912
40058
  // Some devices/webviews reject specific constraints (OverconstrainedError etc.).
39913
40059
  // 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);
40060
+ console.warn('⚠️ getUserMedia with AEC constraints failed, retrying minimal:', (_t2 === null || _t2 === void 0 ? void 0 : _t2.name) || _t2);
39915
40061
  _context4.n = 19;
39916
40062
  return navigator.mediaDevices.getUserMedia({
39917
40063
  audio: true
@@ -39925,6 +40071,10 @@ var VoiceInterface = /*#__PURE__*/function () {
39925
40071
  _context4.n = 21;
39926
40072
  return _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_1__["default"].primeSharedContext(primeSr);
39927
40073
  case 21:
40074
+ if (!this.isMobile) {
40075
+ _context4.n = 22;
40076
+ break;
40077
+ }
39928
40078
  // Hide floating button now that permission is granted
39929
40079
  floatingButton = this.shadowRoot.getElementById('text-chat-button') || document.getElementById('text-chat-button');
39930
40080
  if (floatingButton) floatingButton.style.display = 'none';
@@ -39940,9 +40090,9 @@ var VoiceInterface = /*#__PURE__*/function () {
39940
40090
  _context4.n = 22;
39941
40091
  return (0,_shared_applyDelay_js__WEBPACK_IMPORTED_MODULE_3__.applyDelay)(delayConfig);
39942
40092
  case 22:
39943
- if (sendDisclaimerAckAfterMobileMicReady) {
40093
+ if (sendDisclaimerAckAfterMicReady) {
39944
40094
  vs.sendDisclaimerAck(true);
39945
- sendDisclaimerAckAfterMobileMicReady = false;
40095
+ sendDisclaimerAckAfterMicReady = false;
39946
40096
  }
39947
40097
  _context4.n = 24;
39948
40098
  break;