ttp-agent-sdk 2.48.0 → 2.48.2

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.
@@ -10748,7 +10748,7 @@ var ClientScriptManager = /*#__PURE__*/function () {
10748
10748
  _ref2,
10749
10749
  toolCallId,
10750
10750
  toolName,
10751
- _ref3,
10751
+ _ClientToolsRegistry$,
10752
10752
  action,
10753
10753
  args,
10754
10754
  startedAt,
@@ -10765,7 +10765,7 @@ var ClientScriptManager = /*#__PURE__*/function () {
10765
10765
  case 0:
10766
10766
  conversationId = _args.length > 1 && _args[1] !== undefined ? _args[1] : null;
10767
10767
  _ref2 = message || {}, toolCallId = _ref2.toolCallId, toolName = _ref2.toolName;
10768
- _ref3 = message && message.parameters || {}, action = _ref3.action, args = _ref3.args;
10768
+ _ClientToolsRegistry$ = _ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_0__["default"].resolveParameters(message), action = _ClientToolsRegistry$.action, args = _ClientToolsRegistry$.args;
10769
10769
  startedAt = Date.now();
10770
10770
  console.log("".concat(LOG).concat(this._tag(), " client_tool_call dispatch action=").concat(action, " toolCallId=").concat(toolCallId));
10771
10771
  sendError = function sendError(error, errorCode) {
@@ -10888,11 +10888,11 @@ var ClientScriptManager = /*#__PURE__*/function () {
10888
10888
  key: "handleRunPartnerScript",
10889
10889
  value: (function () {
10890
10890
  var _handleRunPartnerScript = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(message) {
10891
- var _ref4, requestId, partnerId, action, args, startedAt, result, elapsedMs, ok, serializedSize, sizeKB, limitKB, reason, _elapsedMs, _t2;
10891
+ var _ref3, requestId, partnerId, action, args, startedAt, result, elapsedMs, ok, serializedSize, sizeKB, limitKB, reason, _elapsedMs, _t2;
10892
10892
  return _regenerator().w(function (_context2) {
10893
10893
  while (1) switch (_context2.p = _context2.n) {
10894
10894
  case 0:
10895
- _ref4 = message || {}, requestId = _ref4.requestId, partnerId = _ref4.partnerId, action = _ref4.action;
10895
+ _ref3 = message || {}, requestId = _ref3.requestId, partnerId = _ref3.partnerId, action = _ref3.action;
10896
10896
  args = message && message.args || {};
10897
10897
  if (requestId) {
10898
10898
  _context2.n = 1;
@@ -11009,7 +11009,8 @@ var ClientScriptManager = /*#__PURE__*/function () {
11009
11009
  }, {
11010
11010
  key: "isClientToolsToolCall",
11011
11011
  value: function isClientToolsToolCall(message) {
11012
- return !!message && message.toolName === 'run_partner_script' && !!message.parameters && message.parameters.partner_id === CLIENT_TOOLS_PARTNER_ID;
11012
+ var parameters = _ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_0__["default"].resolveParameters(message);
11013
+ return !!message && message.toolName === 'run_partner_script' && parameters.partner_id === CLIENT_TOOLS_PARTNER_ID;
11013
11014
  }
11014
11015
  }]);
11015
11016
  }();
@@ -11127,6 +11128,20 @@ var ClientToolsRegistry = /*#__PURE__*/function () {
11127
11128
  return Array.from(this.handlers.keys());
11128
11129
  }
11129
11130
 
11131
+ /**
11132
+ * Resolve the object passed to a tool handler from a `client_tool_call` frame.
11133
+ *
11134
+ * The backend DTO (`ClientToolCallMessage`) serializes LLM args on
11135
+ * `parameters`. Handlers destructure that object (`params.selector`, …), so
11136
+ * `null` / a JSON string / an alternate field name would look like "params
11137
+ * aren't working". Always return a plain object.
11138
+ *
11139
+ * @param {Object} message
11140
+ * @returns {Object}
11141
+ */
11142
+ }, {
11143
+ key: "handleToolCall",
11144
+ value: (
11130
11145
  /**
11131
11146
  * Handle incoming client_tool_call message from backend
11132
11147
  * @param {Object} message - The tool call message
@@ -11134,19 +11149,19 @@ var ClientToolsRegistry = /*#__PURE__*/function () {
11134
11149
  * @param {string} message.toolName - Name of the tool to execute
11135
11150
  * @param {Object} message.parameters - Parameters for the tool
11136
11151
  */
11137
- }, {
11138
- key: "handleToolCall",
11139
- value: (function () {
11152
+ function () {
11140
11153
  var _handleToolCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(message) {
11141
- var toolCallId, toolName, parameters, startTime, receiveTimestamp, handler, registeredTools, result, executionTimeMs, _error$message, _error$message2, _executionTimeMs, errorCode, _t;
11154
+ var _ref, toolCallId, toolName, parameters, startTime, receiveTimestamp, handler, registeredTools, result, executionTimeMs, _error$message, _error$message2, _executionTimeMs, errorCode, _t;
11142
11155
  return _regenerator().w(function (_context) {
11143
11156
  while (1) switch (_context.p = _context.n) {
11144
11157
  case 0:
11145
- toolCallId = message.toolCallId, toolName = message.toolName, parameters = message.parameters;
11158
+ _ref = message || {}, toolCallId = _ref.toolCallId, toolName = _ref.toolName;
11159
+ parameters = ClientToolsRegistry.resolveParameters(message);
11146
11160
  startTime = Date.now();
11147
11161
  receiveTimestamp = new Date().toISOString();
11148
11162
  console.log("\uD83D\uDD27 ClientTools: [".concat(receiveTimestamp, "] Received tool call '").concat(toolName, "' (id: ").concat(toolCallId, ")"));
11149
11163
  console.log(" \uD83D\uDCCB Full message received:", JSON.stringify(message, null, 2));
11164
+ console.log(" \uD83D\uDCCB Resolved parameters:", parameters);
11150
11165
  console.log(" \u23F1\uFE0F Start time: ".concat(startTime, "ms (").concat(new Date(startTime).toISOString(), ")"));
11151
11166
 
11152
11167
  // Check if handler exists
@@ -11276,6 +11291,34 @@ var ClientToolsRegistry = /*#__PURE__*/function () {
11276
11291
  value: function setSendMessage(sendMessage) {
11277
11292
  this.sendMessage = sendMessage;
11278
11293
  }
11294
+ }], [{
11295
+ key: "resolveParameters",
11296
+ value: function resolveParameters(message) {
11297
+ if (!message || _typeof(message) !== 'object') return {};
11298
+ var raw = message.parameters;
11299
+ if (raw == null) raw = message.params;
11300
+ if (raw == null) raw = message.args;
11301
+ if (raw == null) return {};
11302
+ if (typeof raw === 'string') {
11303
+ var trimmed = raw.trim();
11304
+ if (!trimmed) return {};
11305
+ try {
11306
+ var parsed = JSON.parse(trimmed);
11307
+ if (parsed !== null && _typeof(parsed) === 'object') return parsed;
11308
+ return {
11309
+ value: parsed
11310
+ };
11311
+ } catch (_unused2) {
11312
+ return {
11313
+ value: raw
11314
+ };
11315
+ }
11316
+ }
11317
+ if (_typeof(raw) === 'object') return raw;
11318
+ return {
11319
+ value: raw
11320
+ };
11321
+ }
11279
11322
  }]);
11280
11323
  }();
11281
11324
  _defineProperty(ClientToolsRegistry, "MAX_RESULT_SIZE", 512000);
@@ -11384,6 +11427,7 @@ __webpack_require__.r(__webpack_exports__);
11384
11427
  /* harmony import */ var _EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./EventEmitter.js */ "./src/core/EventEmitter.js");
11385
11428
  /* harmony import */ var _ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ClientToolsRegistry.js */ "./src/core/ClientToolsRegistry.js");
11386
11429
  /* harmony import */ var _ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ClientScriptManager.js */ "./src/core/ClientScriptManager.js");
11430
+ /* harmony import */ var _helloFlavor_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./helloFlavor.js */ "./src/core/helloFlavor.js");
11387
11431
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
11388
11432
  function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
11389
11433
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
@@ -11416,9 +11460,12 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
11416
11460
 
11417
11461
 
11418
11462
 
11463
+
11419
11464
  var RECONNECT_DELAY_MS = 1000;
11420
11465
  var MAX_RECONNECT_ATTEMPTS = 5;
11421
11466
  var TEXT_CHAT_PATH = '/chat/text';
11467
+ /** Same interval as VoiceSDK — keeps proxies/LBs from dropping an idle text socket. */
11468
+ var KEEP_ALIVE_MS = 30000;
11422
11469
  /** Production voice/text base (path swapped to /chat/text); matches VoiceSDK v2 default. */
11423
11470
  var DEFAULT_WEBSOCKET_BASE_URL = 'wss://speech.talktopc.com/ws/conv';
11424
11471
  var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
@@ -11440,6 +11487,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11440
11487
  _this.connecting = false;
11441
11488
  _this.reconnectAttempts = 0;
11442
11489
  _this.intentionalClose = false;
11490
+ _this._keepAliveInterval = null;
11443
11491
  _this.fullResponseBuffer = '';
11444
11492
  _this.queue = [];
11445
11493
  _this.inFlight = false;
@@ -11604,6 +11652,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11604
11652
  _this2.connected = true;
11605
11653
  _this2.reconnectAttempts = 0;
11606
11654
  console.log('[TextChatSDK] Connected');
11655
+ _this2._startKeepAlive();
11607
11656
 
11608
11657
  // Send hello immediately on connect (same fields as voice SDK v2 hello).
11609
11658
  _this2.sendHelloMessage();
@@ -11719,13 +11768,16 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11719
11768
  // Non-JSON or parse error; ignore
11720
11769
  }
11721
11770
  };
11771
+
11772
+ // Browser WebSocket.onerror is an Event, not an Error — emitting it would
11773
+ // render as the chat bubble "[object Event]". onclose handles recovery.
11722
11774
  this.ws.onerror = function (e) {
11723
11775
  console.error('[TextChatSDK] WebSocket error:', e);
11724
- _this2.emit('error', e);
11725
11776
  };
11726
11777
  this.ws.onclose = function (event) {
11727
11778
  _this2.connected = false;
11728
11779
  _this2.connecting = false;
11780
+ _this2._stopKeepAlive();
11729
11781
  console.log('[TextChatSDK] Disconnected, code:', event.code, 'reason:', event.reason);
11730
11782
 
11731
11783
  // Domain whitelist error — don't reconnect
@@ -11963,9 +12015,10 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11963
12015
  v: 2
11964
12016
  };
11965
12017
 
11966
- // Flavor (partner tools: hotels, restaurants, ecommerce, etc.)
11967
- if (this.config.flavor) {
11968
- helloMessage.flavor = this.config.flavor;
12018
+ // Partner fields only — UI-only keys like callView stay local (backend Flavor DTO rejects them)
12019
+ var wireFlavor = (0,_helloFlavor_js__WEBPACK_IMPORTED_MODULE_3__.flavorForHello)(this.config.flavor);
12020
+ if (wireFlavor) {
12021
+ helloMessage.flavor = wireFlavor;
11969
12022
  }
11970
12023
 
11971
12024
  // Variables (template variables injected into agent prompt)
@@ -11994,7 +12047,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11994
12047
 
11995
12048
  // SDK build time for debugging
11996
12049
  if (true) {
11997
- helloMessage.lastBuildTime = "2026-08-02T10:38:16.158Z";
12050
+ helloMessage.lastBuildTime = "2026-08-20T14:41:57.957Z";
11998
12051
  }
11999
12052
  try {
12000
12053
  this.ws.send(JSON.stringify(helloMessage));
@@ -12023,6 +12076,27 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
12023
12076
  };
12024
12077
  }
12025
12078
  }
12079
+ }, {
12080
+ key: "_startKeepAlive",
12081
+ value: function _startKeepAlive() {
12082
+ var _this4 = this;
12083
+ this._stopKeepAlive();
12084
+ this._keepAliveInterval = setInterval(function () {
12085
+ if (_this4.connected && _this4.ws && _this4.ws.readyState === WebSocket.OPEN) {
12086
+ _this4.sendRaw({
12087
+ t: 'ping'
12088
+ });
12089
+ }
12090
+ }, KEEP_ALIVE_MS);
12091
+ }
12092
+ }, {
12093
+ key: "_stopKeepAlive",
12094
+ value: function _stopKeepAlive() {
12095
+ if (this._keepAliveInterval) {
12096
+ clearInterval(this._keepAliveInterval);
12097
+ this._keepAliveInterval = null;
12098
+ }
12099
+ }
12026
12100
  }, {
12027
12101
  key: "persistConversationId",
12028
12102
  value: function persistConversationId(conversationId) {
@@ -12055,6 +12129,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
12055
12129
  this.queue = [];
12056
12130
  this.inFlight = false;
12057
12131
  this._currentTask = null;
12132
+ this._stopKeepAlive();
12058
12133
  if (this.ws) {
12059
12134
  try {
12060
12135
  this.ws.close();
@@ -12622,6 +12697,35 @@ var webSocketSingleton = new WebSocketSingleton();
12622
12697
 
12623
12698
  /***/ }),
12624
12699
 
12700
+ /***/ "./src/core/helloFlavor.js":
12701
+ /*!*********************************!*\
12702
+ !*** ./src/core/helloFlavor.js ***!
12703
+ \*********************************/
12704
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
12705
+
12706
+ "use strict";
12707
+ __webpack_require__.r(__webpack_exports__);
12708
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
12709
+ /* harmony export */ flavorForHello: () => (/* binding */ flavorForHello)
12710
+ /* harmony export */ });
12711
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
12712
+ /**
12713
+ * Flavor fields the conversation backend Flavor DTO understands.
12714
+ * UI-only keys (callView, …) stay on the widget config and must not go on hello.
12715
+ */
12716
+ function flavorForHello(flavor) {
12717
+ if (!flavor || _typeof(flavor) !== 'object') return null;
12718
+ var out = {};
12719
+ if (flavor.type != null && String(flavor.type).trim() !== '') out.type = flavor.type;
12720
+ if (flavor.partnerId != null && String(flavor.partnerId).trim() !== '') out.partnerId = flavor.partnerId;
12721
+ if (flavor.shopifyDomain != null && String(flavor.shopifyDomain).trim() !== '') {
12722
+ out.shopifyDomain = flavor.shopifyDomain;
12723
+ }
12724
+ return Object.keys(out).length ? out : null;
12725
+ }
12726
+
12727
+ /***/ }),
12728
+
12625
12729
  /***/ "./src/ecommerce/CartSummary.js":
12626
12730
  /*!**************************************!*\
12627
12731
  !*** ./src/ecommerce/CartSummary.js ***!
@@ -21253,8 +21357,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
21253
21357
 
21254
21358
 
21255
21359
  // Version - injected at build time from package.json via webpack DefinePlugin
21256
- var VERSION = "2.48.0";
21257
- var BUILD_TIME = "2026-08-02T10:38:16.158Z";
21360
+ var VERSION = "2.48.2";
21361
+ var BUILD_TIME = "2026-08-20T14:41:57.957Z";
21258
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;');
21259
21363
 
21260
21364
  // Named exports
@@ -25430,6 +25534,9 @@ __webpack_require__.r(__webpack_exports__);
25430
25534
  /* harmony import */ var _codecs_PCMUCodec_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./codecs/PCMUCodec.js */ "./src/v2/codecs/PCMUCodec.js");
25431
25535
  /* harmony import */ var _codecs_PCMACodec_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./codecs/PCMACodec.js */ "./src/v2/codecs/PCMACodec.js");
25432
25536
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
25537
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
25538
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
25539
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
25433
25540
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
25434
25541
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
25435
25542
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
@@ -25505,6 +25612,15 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
25505
25612
  _this.gainNode = null; // GainNode for volume/mute control
25506
25613
  _this._audioContextPrimed = false; // Track if AudioContext has been primed with silent buffer
25507
25614
 
25615
+ // Last-hop AEC route (ElevenLabs WS pattern): play through a hidden
25616
+ // HTMLAudioElement instead of AudioContext.destination so Chrome/Safari
25617
+ // can use TTS as the echo-cancellation far-end. Scheduling / source.stop
25618
+ // still live on the AudioContext. Opt out with htmlAudioPlayback:false.
25619
+ _this._htmlAudioEl = null;
25620
+ _this._mediaStreamDest = null;
25621
+ _this._htmlAudioRouteActive = false;
25622
+ _this._htmlAudioPlayOk = false;
25623
+
25508
25624
  // Track temporary listener in waitForAudioContextReady() for cleanup
25509
25625
  _this._waitForReadyStateHandler = null;
25510
25626
  _this.audioQueue = [];
@@ -25611,9 +25727,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
25611
25727
  this.outputFormat = format;
25612
25728
  console.log('✅ AudioPlayer v2: Format set:', format);
25613
25729
 
25614
- // CRITICAL: If AudioContext already exists and sample rate changed, recreate it
25730
+ // Destination path matches AudioContext to the TTS rate. The HTML last-hop
25731
+ // keeps the context at hardware rate (48 kHz); only createBuffer() follows TTS.
25615
25732
 
25616
- if (this.audioContext && oldSampleRate && oldSampleRate !== newSampleRate) {
25733
+ if (this.audioContext && oldSampleRate && oldSampleRate !== newSampleRate && !this._htmlHopNeedsHardwareRate()) {
25617
25734
  console.warn('⚠️ AudioPlayer: Sample rate changed, recreating AudioContext');
25618
25735
  console.warn(" Old: ".concat(oldSampleRate, "Hz \u2192 New: ").concat(newSampleRate, "Hz"));
25619
25736
 
@@ -25794,7 +25911,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
25794
25911
  key: "prepareChunk",
25795
25912
  value: (function () {
25796
25913
  var _prepareChunk = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(pcmData) {
25797
- var _this$outputFormat2, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, chunkDuration, sampleCount, actualDuration, _t;
25914
+ var _this$outputFormat2, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, audioBuffer, actualDuration, _t;
25798
25915
  return _regenerator().w(function (_context2) {
25799
25916
  while (1) switch (_context2.p = _context2.n) {
25800
25917
  case 0:
@@ -25843,27 +25960,21 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
25843
25960
 
25844
25961
  // Create audio buffer with the ACTUAL sample rate of the audio data
25845
25962
  audioDataSampleRate = ((_this$outputFormat2 = this.outputFormat) === null || _this$outputFormat2 === void 0 ? void 0 : _this$outputFormat2.sampleRate) || this.audioContext.sampleRate;
25846
- contextSampleRate = this.audioContext.sampleRate;
25847
25963
  audioBuffer = this.audioContext.createBuffer(1,
25848
25964
  // mono
25849
25965
 
25850
25966
  float32Array.length, audioDataSampleRate);
25851
25967
  audioBuffer.getChannelData(0).set(float32Array);
25852
25968
 
25853
- // Calculate duration (handle browser resampling)
25854
- chunkDuration = audioBuffer.duration;
25855
- sampleCount = float32Array.length;
25856
- actualDuration = chunkDuration;
25857
- if (contextSampleRate !== audioDataSampleRate) {
25858
- actualDuration = sampleCount / contextSampleRate;
25859
- }
25860
-
25861
- // Return prepared frame
25969
+ // Wall-clock duration is sampleCount / TTS rate (audioBuffer.duration).
25970
+ // Dividing by contextSampleRate when the context is 48 kHz and TTS is
25971
+ // 24 kHz halves the schedule step and overlaps chunks.
25972
+ actualDuration = audioBuffer.duration; // Return prepared frame
25862
25973
  return _context2.a(2, {
25863
25974
  buffer: audioBuffer,
25864
25975
  duration: actualDuration,
25865
25976
  sampleRate: audioDataSampleRate,
25866
- contextSampleRate: contextSampleRate
25977
+ contextSampleRate: this.audioContext.sampleRate
25867
25978
  });
25868
25979
  case 4:
25869
25980
  _context2.p = 4;
@@ -25920,6 +26031,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
25920
26031
  return _context4.a(2);
25921
26032
  case 2:
25922
26033
  this.isSchedulingFrames = true;
26034
+ this._ensureHtmlAudioPlaying();
25923
26035
 
25924
26036
  // Schedule multiple frames ahead to ensure continuous playback
25925
26037
  // This prevents gaps when frames arrive slowly or there are timing delays
@@ -26219,7 +26331,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26219
26331
  value: (function () {
26220
26332
  var _processPcmQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
26221
26333
  var _this5 = this;
26222
- var _this$outputFormat3, _this$outputFormat4, pcmData, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, source, currentTime, chunkDuration, sampleCount, actualDuration, startTime, _t3;
26334
+ var _this$outputFormat3, _this$outputFormat4, pcmData, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, source, currentTime, actualDuration, startTime, _t3;
26223
26335
  return _regenerator().w(function (_context5) {
26224
26336
  while (1) switch (_context5.p = _context5.n) {
26225
26337
  case 0:
@@ -26306,16 +26418,11 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26306
26418
  }
26307
26419
  }
26308
26420
 
26309
- // Calculate frame duration - handle browser resampling correctly
26310
- chunkDuration = audioBuffer.duration;
26311
- sampleCount = float32Array.length;
26312
- actualDuration = chunkDuration;
26313
- if (contextSampleRate !== audioDataSampleRate) {
26314
- actualDuration = sampleCount / contextSampleRate;
26315
- if (this.scheduledBuffers < 3) {
26316
- console.log("\uD83D\uDD04 Resampling detected: ".concat(audioDataSampleRate, "Hz \u2192 ").concat(contextSampleRate, "Hz"));
26317
- console.log(" Buffer duration: ".concat(chunkDuration.toFixed(4), "s, Calculated: ").concat(actualDuration.toFixed(4), "s"));
26318
- }
26421
+ // Wall-clock duration is always sampleCount / TTS rate. Web Audio
26422
+ // resamples into the context; do not divide by contextSampleRate.
26423
+ actualDuration = audioBuffer.duration;
26424
+ if (contextSampleRate !== audioDataSampleRate && this.scheduledBuffers < 3) {
26425
+ console.log("\uD83D\uDD04 AudioPlayer: Buffer ".concat(audioDataSampleRate, "Hz in ").concat(contextSampleRate, "Hz context, duration ").concat(actualDuration.toFixed(4), "s"));
26319
26426
  }
26320
26427
 
26321
26428
  // Schedule this chunk to start at nextStartTime
@@ -26779,6 +26886,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26779
26886
  } catch (_) {/* ignore */}
26780
26887
  this.gainNode = null;
26781
26888
  }
26889
+ this._teardownHtmlAudioRoute();
26782
26890
  if (this.audioContext === _sharedPlayerContext) {
26783
26891
  _sharedPlayerContext = null;
26784
26892
  _sharedPlayerSampleRate = null;
@@ -26921,6 +27029,148 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26921
27029
  }
26922
27030
  return _waitForAudioContextReadyImpl;
26923
27031
  }()
27032
+ }, {
27033
+ key: "wantsHtmlAudioPlayback",
27034
+ value: function wantsHtmlAudioPlayback() {
27035
+ return this.config.htmlAudioPlayback !== false;
27036
+ }
27037
+ }, {
27038
+ key: "_ttsSampleRate",
27039
+ value: function _ttsSampleRate() {
27040
+ var _this$outputFormat5;
27041
+ return ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 24000;
27042
+ }
27043
+
27044
+ /**
27045
+ * Hidden <audio srcObject> clocks a MediaStream at the device rate
27046
+ * (typically 48 kHz). Forcing the AudioContext to the TTS rate (24 kHz)
27047
+ * makes Chrome play that hop ~2×. Destination-only playback can still
27048
+ * match the TTS rate.
27049
+ */
27050
+ }, {
27051
+ key: "_htmlHopNeedsHardwareRate",
27052
+ value: function _htmlHopNeedsHardwareRate() {
27053
+ return this.wantsHtmlAudioPlayback();
27054
+ }
27055
+ }, {
27056
+ key: "_isHardwarePlaybackRate",
27057
+ value: function _isHardwarePlaybackRate(rate) {
27058
+ return typeof rate === 'number' && rate >= 44100;
27059
+ }
27060
+ }, {
27061
+ key: "getHtmlAudioPlaybackInfo",
27062
+ value: function getHtmlAudioPlaybackInfo() {
27063
+ return {
27064
+ htmlAudioPlayback: this.wantsHtmlAudioPlayback(),
27065
+ htmlAudioRouteActive: this._htmlAudioRouteActive,
27066
+ htmlAudioPlayOk: this._htmlAudioPlayOk
27067
+ };
27068
+ }
27069
+
27070
+ /**
27071
+ * Last hop: GainNode → MediaStreamDestination → hidden <audio>.
27072
+ * Falls back to AudioContext.destination if the HTML route cannot start.
27073
+ */
27074
+ }, {
27075
+ key: "_connectGainToOutput",
27076
+ value: function _connectGainToOutput() {
27077
+ if (!this.gainNode || !this.audioContext) {
27078
+ return;
27079
+ }
27080
+ try {
27081
+ this.gainNode.disconnect();
27082
+ } catch (_) {/* ignore */}
27083
+ if (this.wantsHtmlAudioPlayback() && this._ensureHtmlAudioRoute()) {
27084
+ this.gainNode.connect(this._mediaStreamDest);
27085
+ return;
27086
+ }
27087
+ this.gainNode.connect(this.audioContext.destination);
27088
+ this._htmlAudioRouteActive = false;
27089
+ }
27090
+ }, {
27091
+ key: "_ensureHtmlAudioRoute",
27092
+ value: function _ensureHtmlAudioRoute() {
27093
+ if (this._htmlAudioRouteActive && this._htmlAudioEl && this._mediaStreamDest) {
27094
+ this._ensureHtmlAudioPlaying();
27095
+ return true;
27096
+ }
27097
+ if (!this.audioContext || typeof this.audioContext.createMediaStreamDestination !== 'function') {
27098
+ console.warn('⚠️ AudioPlayer: MediaStreamDestination unavailable — using AudioContext.destination');
27099
+ return false;
27100
+ }
27101
+ try {
27102
+ this._mediaStreamDest = this.audioContext.createMediaStreamDestination();
27103
+ var el = document.createElement('audio');
27104
+ el.setAttribute('playsinline', '');
27105
+ el.setAttribute('webkit-playsinline', '');
27106
+ el.autoplay = true;
27107
+ el.controls = false;
27108
+ el.preload = 'auto';
27109
+ el.playbackRate = 1;
27110
+ el.style.display = 'none';
27111
+ el.srcObject = this._mediaStreamDest.stream;
27112
+ document.body.appendChild(el);
27113
+ this._htmlAudioEl = el;
27114
+ this._htmlAudioRouteActive = true;
27115
+ this._ensureHtmlAudioPlaying();
27116
+ console.log('🔊 AudioPlayer: Playback last-hop is HTMLAudioElement (AEC far-end route)');
27117
+ return true;
27118
+ } catch (e) {
27119
+ console.warn('⚠️ AudioPlayer: HTML audio route failed, falling back to destination:', e);
27120
+ this._teardownHtmlAudioRoute();
27121
+ return false;
27122
+ }
27123
+ }
27124
+ }, {
27125
+ key: "_ensureHtmlAudioPlaying",
27126
+ value: function _ensureHtmlAudioPlaying() {
27127
+ var _this7 = this;
27128
+ var el = this._htmlAudioEl;
27129
+ if (!el) return;
27130
+ if (!el.paused && this._htmlAudioPlayOk) return;
27131
+ var playResult = el.play();
27132
+ if (playResult && typeof playResult.then === 'function') {
27133
+ playResult.then(function () {
27134
+ _this7._htmlAudioPlayOk = true;
27135
+ }).catch(function (err) {
27136
+ if (_this7._htmlAudioPlayOk || !el.paused) {
27137
+ return;
27138
+ }
27139
+ console.warn('⚠️ AudioPlayer: HTMLAudioElement.play() rejected — falling back to destination:', err);
27140
+ _this7._fallbackToDestination('play_rejected');
27141
+ });
27142
+ }
27143
+ }
27144
+ }, {
27145
+ key: "_fallbackToDestination",
27146
+ value: function _fallbackToDestination(reason) {
27147
+ if (!this.gainNode || !this.audioContext) return;
27148
+ try {
27149
+ this.gainNode.disconnect();
27150
+ } catch (_) {/* ignore */}
27151
+ this._teardownHtmlAudioRoute();
27152
+ this.gainNode.connect(this.audioContext.destination);
27153
+ console.warn("\u26A0\uFE0F AudioPlayer: Fell back to AudioContext.destination (".concat(reason, ")"));
27154
+ }
27155
+ }, {
27156
+ key: "_teardownHtmlAudioRoute",
27157
+ value: function _teardownHtmlAudioRoute() {
27158
+ if (this._htmlAudioEl) {
27159
+ try {
27160
+ this._htmlAudioEl.pause();
27161
+ } catch (_) {/* ignore */}
27162
+ try {
27163
+ this._htmlAudioEl.srcObject = null;
27164
+ } catch (_) {/* ignore */}
27165
+ if (this._htmlAudioEl.parentNode) {
27166
+ this._htmlAudioEl.parentNode.removeChild(this._htmlAudioEl);
27167
+ }
27168
+ this._htmlAudioEl = null;
27169
+ }
27170
+ this._mediaStreamDest = null;
27171
+ this._htmlAudioRouteActive = false;
27172
+ }
27173
+
26924
27174
  /**
26925
27175
  * Initialize audio context with correct sample rate
26926
27176
  */
@@ -26928,16 +27178,16 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26928
27178
  key: "initializeAudioContext",
26929
27179
  value: (function () {
26930
27180
  var _initializeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
26931
- var _this$outputFormat5,
26932
- _this7 = this,
26933
- _this$outputFormat6;
26934
- var desiredSampleRate, currentSampleRate, canReuseShared, setupAfterResume, _t4;
27181
+ var _this8 = this;
27182
+ var ttsSampleRate, useHardwareRate, desiredSampleRate, contextFits, canReuseShared, setupAfterResume, ctxOpts, _t4;
26935
27183
  return _regenerator().w(function (_context8) {
26936
27184
  while (1) switch (_context8.p = _context8.n) {
26937
27185
  case 0:
26938
- // Use negotiated sample rate, default to 24kHz to match typical server/TTS output
26939
- desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 24000;
26940
- if (![24000, 44100, 48000].includes(desiredSampleRate)) {
27186
+ ttsSampleRate = this._ttsSampleRate();
27187
+ useHardwareRate = this._htmlHopNeedsHardwareRate(); // Destination path: match TTS (24 kHz). HTML last-hop: omit so the
27188
+ // context (and MediaStream) run at the device rate — usually 48 kHz.
27189
+ desiredSampleRate = useHardwareRate ? null : ttsSampleRate;
27190
+ if (!useHardwareRate && ![24000, 44100, 48000].includes(desiredSampleRate)) {
26941
27191
  console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
26942
27192
  console.log(" Consider requesting 24000Hz, 44100Hz, or 48000Hz from backend to reduce resampling");
26943
27193
  }
@@ -26945,8 +27195,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26945
27195
  _sharedPlayerContext = null;
26946
27196
  _sharedPlayerSampleRate = null;
26947
27197
  }
26948
-
26949
- // Check if current instance AudioContext exists and matches
27198
+ contextFits = function contextFits(rate) {
27199
+ if (useHardwareRate) return _this8._isHardwarePlaybackRate(rate);
27200
+ return Math.abs(rate - desiredSampleRate) <= 100;
27201
+ }; // Check if current instance AudioContext exists and matches
26950
27202
  if (!this.audioContext) {
26951
27203
  _context8.n = 3;
26952
27204
  break;
@@ -26959,43 +27211,41 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26959
27211
  _context8.n = 3;
26960
27212
  break;
26961
27213
  case 1:
26962
- currentSampleRate = this.audioContext.sampleRate;
26963
- if (!(Math.abs(currentSampleRate - desiredSampleRate) > 100)) {
27214
+ if (!contextFits(this.audioContext.sampleRate)) {
26964
27215
  _context8.n = 2;
26965
27216
  break;
26966
27217
  }
26967
- console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
27218
+ this._ensureHtmlAudioPlaying();
27219
+ return _context8.a(2);
27220
+ case 2:
27221
+ console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(this.audioContext.sampleRate, "Hz) doesn't match playback hop, recreating..."));
26968
27222
  this.stopImmediate();
26969
27223
  this._cleanupAudioContext();
26970
- _context8.n = 3;
26971
- break;
26972
- case 2:
26973
- return _context8.a(2);
26974
27224
  case 3:
26975
27225
  // iOS FIX: Reuse shared AudioContext if available and compatible.
26976
27226
  // iOS WebKit doesn't release audio hardware synchronously on AudioContext.close(),
26977
27227
  // causing newly created AudioContexts to fail silently.
26978
- canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && _sharedPlayerSampleRate === desiredSampleRate;
27228
+ canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && contextFits(_sharedPlayerContext.sampleRate);
26979
27229
  if (!canReuseShared) {
26980
27230
  _context8.n = 8;
26981
27231
  break;
26982
27232
  }
26983
- console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(desiredSampleRate, "Hz (iOS-safe)"));
27233
+ console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(_sharedPlayerContext.sampleRate, "Hz (iOS-safe)"));
26984
27234
  this.audioContext = _sharedPlayerContext;
26985
27235
  setupAfterResume = function setupAfterResume() {
26986
- _this7.setupAudioContextStateMonitoring();
26987
- if (_this7.gainNode) {
27236
+ _this8.setupAudioContextStateMonitoring();
27237
+ if (_this8.gainNode) {
26988
27238
  try {
26989
- _this7.gainNode.disconnect();
27239
+ _this8.gainNode.disconnect();
26990
27240
  } catch (e) {
26991
27241
  console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
26992
27242
  }
26993
27243
  }
26994
- _this7.gainNode = _this7.audioContext.createGain();
26995
- _this7.gainNode.gain.value = 1.0;
26996
- _this7.gainNode.connect(_this7.audioContext.destination);
26997
- if (!_this7._audioContextPrimed) {
26998
- _this7._primeAudioContext();
27244
+ _this8.gainNode = _this8.audioContext.createGain();
27245
+ _this8.gainNode.gain.value = 1.0;
27246
+ _this8._connectGainToOutput();
27247
+ if (!_this8._audioContextPrimed) {
27248
+ _this8._primeAudioContext();
26999
27249
  }
27000
27250
  };
27001
27251
  if (!(this.audioContext.state === 'suspended')) {
@@ -27016,7 +27266,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27016
27266
  setupAfterResume();
27017
27267
  return _context8.a(2);
27018
27268
  case 8:
27019
- console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext at ".concat(desiredSampleRate, "Hz (from outputFormat: ").concat(((_this$outputFormat6 = this.outputFormat) === null || _this$outputFormat6 === void 0 ? void 0 : _this$outputFormat6.sampleRate) || 'not set', ")"));
27269
+ console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext (tts=".concat(ttsSampleRate, "Hz, htmlHop=").concat(useHardwareRate, ", requested=").concat(desiredSampleRate || 'hardware default', ")"));
27020
27270
 
27021
27271
  // Close old shared context if sample rate changed
27022
27272
  if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
@@ -27028,29 +27278,27 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27028
27278
  }
27029
27279
  }
27030
27280
  try {
27031
- this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
27032
- sampleRate: desiredSampleRate,
27281
+ ctxOpts = {
27033
27282
  latencyHint: 'playback'
27034
- });
27283
+ };
27284
+ if (desiredSampleRate) {
27285
+ ctxOpts.sampleRate = desiredSampleRate;
27286
+ }
27287
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)(ctxOpts);
27035
27288
 
27036
27289
  // Store as shared context
27037
27290
  _sharedPlayerContext = this.audioContext;
27038
- _sharedPlayerSampleRate = desiredSampleRate;
27039
- console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
27040
- if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
27041
- console.error("\u274C CRITICAL: Browser sample rate mismatch!");
27042
- console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
27043
- console.error(" Got: ".concat(this.audioContext.sampleRate, "Hz"));
27044
- console.error(" This WILL cause audio distortion/noise!");
27045
- console.error(" Solution: Backend should send ".concat(this.audioContext.sampleRate, "Hz audio instead"));
27046
- } else if (this.audioContext.sampleRate !== desiredSampleRate) {
27047
- console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
27048
- console.warn(" Browser will automatically resample audio.");
27291
+ _sharedPlayerSampleRate = this.audioContext.sampleRate;
27292
+ console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (tts=").concat(ttsSampleRate, "Hz, htmlHop=").concat(useHardwareRate, ")"));
27293
+ if (useHardwareRate && !this._isHardwarePlaybackRate(this.audioContext.sampleRate)) {
27294
+ console.warn("\u26A0\uFE0F AudioPlayer: HTML hop expected \u226544.1 kHz context, got ".concat(this.audioContext.sampleRate, "Hz"));
27295
+ } else if (!useHardwareRate && Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
27296
+ console.warn("\u26A0\uFE0F AudioPlayer: Browser sample rate ".concat(this.audioContext.sampleRate, "Hz \u2260 requested ").concat(desiredSampleRate, "Hz; Web Audio will resample"));
27049
27297
  }
27050
27298
  this.setupAudioContextStateMonitoring();
27051
27299
  this.gainNode = this.audioContext.createGain();
27052
27300
  this.gainNode.gain.value = 1.0;
27053
- this.gainNode.connect(this.audioContext.destination);
27301
+ this._connectGainToOutput();
27054
27302
  console.log('✅ AudioPlayer: GainNode created for volume control');
27055
27303
  this._primeAudioContext();
27056
27304
  } catch (error) {
@@ -27062,7 +27310,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27062
27310
  this.setupAudioContextStateMonitoring();
27063
27311
  this.gainNode = this.audioContext.createGain();
27064
27312
  this.gainNode.gain.value = 1.0;
27065
- this.gainNode.connect(this.audioContext.destination);
27313
+ this._connectGainToOutput();
27066
27314
  console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
27067
27315
  this._primeAudioContext();
27068
27316
  }
@@ -27100,8 +27348,11 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27100
27348
  silentBuffer = this.audioContext.createBuffer(1, sampleRate * 0.15, sampleRate);
27101
27349
  source = this.audioContext.createBufferSource();
27102
27350
  source.buffer = silentBuffer;
27103
- source.connect(this.audioContext.destination);
27351
+ // Prime through the same last hop as real TTS so iOS treats the hidden
27352
+ // <audio> as "playing media" (ElevenLabs maybePrimeIosPlayback).
27353
+ source.connect(this.gainNode || this.audioContext.destination);
27104
27354
  source.start();
27355
+ this._ensureHtmlAudioPlaying();
27105
27356
  _context9.n = 2;
27106
27357
  return new Promise(function (resolve) {
27107
27358
  return setTimeout(resolve, 150);
@@ -27135,7 +27386,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27135
27386
  value: function _cleanupAudioContext() {
27136
27387
  console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (iOS-safe: keeping shared context alive)');
27137
27388
 
27138
- // Disconnect and cleanup gainNode
27389
+ // Disconnect and cleanup gainNode + HTML audio last-hop
27139
27390
  if (this.gainNode) {
27140
27391
  try {
27141
27392
  this.gainNode.disconnect();
@@ -27144,6 +27395,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27144
27395
  }
27145
27396
  this.gainNode = null;
27146
27397
  }
27398
+ this._teardownHtmlAudioRoute();
27147
27399
 
27148
27400
  // Remove state change listener first
27149
27401
  if (this._audioContextStateChangeHandler && this.audioContext) {
@@ -27184,7 +27436,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27184
27436
  * Handles mic permission grants, tab switching, browser suspension, etc.
27185
27437
  */
27186
27438
  function setupAudioContextStateMonitoring() {
27187
- var _this8 = this;
27439
+ var _this9 = this;
27188
27440
  if (!this.audioContext) {
27189
27441
  return;
27190
27442
  }
@@ -27197,35 +27449,38 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27197
27449
  // Create handler that references this.audioContext dynamically
27198
27450
  this._audioContextStateChangeHandler = function () {
27199
27451
  // Null check required because audioContext may be cleaned up while handler is queued
27200
- if (!_this8.audioContext) {
27452
+ if (!_this9.audioContext) {
27201
27453
  console.warn('⚠️ AudioPlayer: State change handler fired but AudioContext is null');
27202
27454
  return;
27203
27455
  }
27204
- console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this8.audioContext.state));
27205
- if (_this8.audioContext.state === 'suspended' && _this8.isPlaying) {
27456
+ console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this9.audioContext.state));
27457
+ if (_this9.audioContext.state === 'running') {
27458
+ _this9._ensureHtmlAudioPlaying();
27459
+ }
27460
+ if (_this9.audioContext.state === 'suspended' && _this9.isPlaying) {
27206
27461
  // AudioContext was suspended during playback (tab switch, mic permission, etc.)
27207
27462
  console.warn('⚠️ AudioPlayer: AudioContext suspended during playback');
27208
27463
  // Note: Playback will pause automatically, but we should handle queue processing
27209
27464
  // The state change will be handled when we try to process next frame
27210
- } else if (_this8.audioContext.state === 'running' && !_this8.isPlaying && (_this8.audioQueue.length > 0 || _this8.pcmChunkQueue.length > 0 || _this8.preparedBuffer.length > 0)) {
27465
+ } else if (_this9.audioContext.state === 'running' && !_this9.isPlaying && (_this9.audioQueue.length > 0 || _this9.pcmChunkQueue.length > 0 || _this9.preparedBuffer.length > 0)) {
27211
27466
  // AudioContext resumed and we have queued frames
27212
27467
  // This handles: mic permission grant, tab switching back, browser resume, etc.
27213
27468
  console.log('✅ AudioPlayer: AudioContext resumed - resuming queue processing');
27214
27469
 
27215
27470
  // Resume queue processing if we have frames
27216
- if (_this8.audioQueue.length > 0 && !_this8.isProcessingQueue) {
27471
+ if (_this9.audioQueue.length > 0 && !_this9.isProcessingQueue) {
27217
27472
  setTimeout(function () {
27218
- return _this8.processQueue();
27473
+ return _this9.processQueue();
27219
27474
  }, 50);
27220
27475
  }
27221
- if (_this8.pcmChunkQueue.length > 0 && !_this8.isProcessingPcmQueue) {
27476
+ if (_this9.pcmChunkQueue.length > 0 && !_this9.isProcessingPcmQueue) {
27222
27477
  setTimeout(function () {
27223
- return _this8.processPcmQueue();
27478
+ return _this9.processPcmQueue();
27224
27479
  }, 50);
27225
27480
  }
27226
- if (_this8.preparedBuffer.length > 0 && !_this8.isSchedulingFrames) {
27481
+ if (_this9.preparedBuffer.length > 0 && !_this9.isSchedulingFrames) {
27227
27482
  setTimeout(function () {
27228
- return _this8.scheduleFrames();
27483
+ return _this9.scheduleFrames();
27229
27484
  }, 50);
27230
27485
  }
27231
27486
  }
@@ -27244,7 +27499,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27244
27499
  key: "processQueue",
27245
27500
  value: (function () {
27246
27501
  var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
27247
- var _this9 = this;
27502
+ var _this0 = this;
27248
27503
  var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t6;
27249
27504
  return _regenerator().w(function (_context0) {
27250
27505
  while (1) switch (_context0.p = _context0.n) {
@@ -27263,6 +27518,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27263
27518
  return _context0.a(2);
27264
27519
  case 2:
27265
27520
  this.isProcessingQueue = true;
27521
+ this._ensureHtmlAudioPlaying();
27266
27522
  audioBlob = this.audioQueue.shift();
27267
27523
  if (audioBlob) {
27268
27524
  _context0.n = 3;
@@ -27303,22 +27559,22 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27303
27559
  // Handle end
27304
27560
 
27305
27561
  source.onended = function () {
27306
- _this9.currentSource = null;
27307
- _this9.isProcessingQueue = false;
27562
+ _this0.currentSource = null;
27563
+ _this0.isProcessingQueue = false;
27308
27564
 
27309
27565
  // Process next chunk
27310
27566
 
27311
- if (_this9.audioQueue.length > 0) {
27567
+ if (_this0.audioQueue.length > 0) {
27312
27568
  setTimeout(function () {
27313
- return _this9.processQueue();
27569
+ return _this0.processQueue();
27314
27570
  }, 50);
27315
27571
  } else {
27316
27572
  // No more chunks - stop after delay
27317
27573
 
27318
27574
  setTimeout(function () {
27319
- if (_this9.audioQueue.length === 0 && !_this9.currentSource) {
27320
- _this9.isPlaying = false;
27321
- _this9.emit('playbackStopped');
27575
+ if (_this0.audioQueue.length === 0 && !_this0.currentSource) {
27576
+ _this0.isPlaying = false;
27577
+ _this0.emit('playbackStopped');
27322
27578
  }
27323
27579
  }, 100);
27324
27580
  }
@@ -27341,7 +27597,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27341
27597
  if (this.audioQueue.length > 0) {
27342
27598
  this.isProcessingQueue = false;
27343
27599
  setTimeout(function () {
27344
- return _this9.processQueue();
27600
+ return _this0.processQueue();
27345
27601
  }, 100);
27346
27602
  } else {
27347
27603
  this.isPlaying = false;
@@ -27564,7 +27820,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27564
27820
  }, {
27565
27821
  key: "markNewSentence",
27566
27822
  value: function markNewSentence(text, synced, segmentId) {
27567
- var _this0 = this;
27823
+ var _this1 = this;
27568
27824
  var wasStopped = this._isStopped;
27569
27825
  var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
27570
27826
 
@@ -27623,34 +27879,34 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27623
27879
  var sentenceText = text; // Capture for timeout callback
27624
27880
  this._emptySentenceTimeout = setTimeout(function () {
27625
27881
  // Check if this sentence still has no chunks after timeout
27626
- if (_this0.pendingSentenceText === sentenceText && _this0.scheduledBuffers === 0 && _this0.preparedBuffer.length === 0 && _this0.pcmChunkQueue.length === 0 && !_this0._isStopped) {
27882
+ if (_this1.pendingSentenceText === sentenceText && _this1.scheduledBuffers === 0 && _this1.preparedBuffer.length === 0 && _this1.pcmChunkQueue.length === 0 && !_this1._isStopped) {
27627
27883
  console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
27628
27884
  // If this empty sentence carried a segment id, report it done (nothing was heard) and
27629
27885
  // adopt it as current so the coarse stop below matches the backend's last-sent id.
27630
- if (_this0.pendingSegmentId != null) {
27631
- var emptySegId = _this0.pendingSegmentId;
27632
- _this0.currentSegmentId = emptySegId;
27633
- _this0.pendingSegmentId = null;
27634
- _this0.emit('segmentDone', {
27886
+ if (_this1.pendingSegmentId != null) {
27887
+ var emptySegId = _this1.pendingSegmentId;
27888
+ _this1.currentSegmentId = emptySegId;
27889
+ _this1.pendingSegmentId = null;
27890
+ _this1.emit('segmentDone', {
27635
27891
  segmentId: emptySegId,
27636
27892
  status: 'finished',
27637
27893
  playedMs: 0
27638
27894
  });
27639
27895
  }
27640
27896
  // Clear pending sentence to unblock next sentence
27641
- if (_this0.pendingSentenceText === sentenceText) {
27642
- _this0.pendingSentenceText = null;
27897
+ if (_this1.pendingSentenceText === sentenceText) {
27898
+ _this1.pendingSentenceText = null;
27643
27899
  }
27644
27900
  // Emit playbackStopped to allow next sentence to start
27645
27901
  // Only if we're not currently playing (to avoid interrupting real playback)
27646
- if (!_this0.isPlaying && _this0.scheduledSources.size === 0) {
27902
+ if (!_this1.isPlaying && _this1.scheduledSources.size === 0) {
27647
27903
  console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
27648
- _this0.emit('playbackStopped', {
27649
- segmentId: _this0.currentSegmentId
27904
+ _this1.emit('playbackStopped', {
27905
+ segmentId: _this1.currentSegmentId
27650
27906
  });
27651
27907
  }
27652
27908
  }
27653
- _this0._emptySentenceTimeout = null;
27909
+ _this1._emptySentenceTimeout = null;
27654
27910
  }, 5000); // 5 second timeout - adjust based on expected chunk arrival rate
27655
27911
  }
27656
27912
 
@@ -27660,14 +27916,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27660
27916
  }, {
27661
27917
  key: "startTranscriptChecker",
27662
27918
  value: function startTranscriptChecker() {
27663
- var _this1 = this;
27919
+ var _this10 = this;
27664
27920
  if (this.isCheckingTranscripts) return;
27665
27921
  this.isCheckingTranscripts = true;
27666
27922
  console.log('📝 AudioPlayer: Transcript checker started');
27667
27923
  var _checkLoop = function checkLoop() {
27668
- if (!_this1.isCheckingTranscripts || !_this1.audioContext) return;
27669
- var currentTime = _this1.audioContext.currentTime;
27670
- var _iterator2 = _createForOfIteratorHelper(_this1.sentenceTimings),
27924
+ if (!_this10.isCheckingTranscripts || !_this10.audioContext) return;
27925
+ var currentTime = _this10.audioContext.currentTime;
27926
+ var _iterator2 = _createForOfIteratorHelper(_this10.sentenceTimings),
27671
27927
  _step2;
27672
27928
  try {
27673
27929
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
@@ -27681,13 +27937,13 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27681
27937
  eventData.synced = _timing.synced;
27682
27938
  }
27683
27939
  console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(_timing.text.substring(0, 40), "...\" (synced: ").concat(_timing.synced ? _timing.synced.length : 0, ")"));
27684
- _this1.emit('transcriptDisplay', eventData);
27940
+ _this10.emit('transcriptDisplay', eventData);
27685
27941
  }
27686
27942
  // Per-segment natural finish: this segment's audio window has fully elapsed.
27687
27943
  if (!_timing.doneReported && _timing.endTime != null && currentTime >= _timing.endTime) {
27688
27944
  _timing.doneReported = true;
27689
27945
  var _playedMs = Math.round((_timing.endTime - _timing.startTime) * 1000);
27690
- _this1.emit('segmentDone', {
27946
+ _this10.emit('segmentDone', {
27691
27947
  segmentId: _timing.segmentId,
27692
27948
  status: 'finished',
27693
27949
  playedMs: _playedMs
@@ -27699,12 +27955,12 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27699
27955
  } finally {
27700
27956
  _iterator2.f();
27701
27957
  }
27702
- if (_this1.isPlaying || _this1.scheduledBuffers > 0) {
27958
+ if (_this10.isPlaying || _this10.scheduledBuffers > 0) {
27703
27959
  requestAnimationFrame(_checkLoop);
27704
27960
  } else {
27705
27961
  // Playback drained naturally — flush any segment whose finish tick we may have missed
27706
27962
  // (the last buffer's onended can flip isPlaying=false before this loop's next tick).
27707
- var _iterator3 = _createForOfIteratorHelper(_this1.sentenceTimings),
27963
+ var _iterator3 = _createForOfIteratorHelper(_this10.sentenceTimings),
27708
27964
  _step3;
27709
27965
  try {
27710
27966
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
@@ -27713,7 +27969,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27713
27969
  timing.doneReported = true;
27714
27970
  var end = timing.endTime != null ? timing.endTime : timing.startTime;
27715
27971
  var playedMs = Math.round((end - timing.startTime) * 1000);
27716
- _this1.emit('segmentDone', {
27972
+ _this10.emit('segmentDone', {
27717
27973
  segmentId: timing.segmentId,
27718
27974
  status: 'finished',
27719
27975
  playedMs: playedMs
@@ -27725,7 +27981,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27725
27981
  } finally {
27726
27982
  _iterator3.f();
27727
27983
  }
27728
- _this1.isCheckingTranscripts = false;
27984
+ _this10.isCheckingTranscripts = false;
27729
27985
  console.log('📝 AudioPlayer: Transcript checker stopped');
27730
27986
  }
27731
27987
  };
@@ -27831,7 +28087,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27831
28087
  }, {
27832
28088
  key: "getStatus",
27833
28089
  value: function getStatus() {
27834
- return {
28090
+ return _objectSpread({
27835
28091
  isPlaying: this.isPlaying,
27836
28092
  isProcessingQueue: this.isProcessingQueue,
27837
28093
  queueLength: this.audioQueue.length,
@@ -27840,7 +28096,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
27840
28096
  scheduledBuffers: this.scheduledBuffers,
27841
28097
  preparedBufferLength: this.preparedBuffer.length,
27842
28098
  scheduledSourcesCount: this.scheduledSources.size
27843
- };
28099
+ }, this.getHtmlAudioPlaybackInfo());
27844
28100
  }
27845
28101
 
27846
28102
  /**
@@ -27888,8 +28144,9 @@ __webpack_require__.r(__webpack_exports__);
27888
28144
  /* harmony import */ var _utils_AudioFormatConverter_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils/AudioFormatConverter.js */ "./src/v2/utils/AudioFormatConverter.js");
27889
28145
  /* harmony import */ var _core_ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/ClientToolsRegistry.js */ "./src/core/ClientToolsRegistry.js");
27890
28146
  /* harmony import */ var _core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/ClientScriptManager.js */ "./src/core/ClientScriptManager.js");
27891
- /* harmony import */ var _utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/screenshot.js */ "./src/utils/screenshot.js");
27892
- /* harmony import */ var _utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/visual-tools.js */ "./src/utils/visual-tools.js");
28147
+ /* harmony import */ var _core_helloFlavor_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../core/helloFlavor.js */ "./src/core/helloFlavor.js");
28148
+ /* harmony import */ var _utils_screenshot_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/screenshot.js */ "./src/utils/screenshot.js");
28149
+ /* harmony import */ var _utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../utils/visual-tools.js */ "./src/utils/visual-tools.js");
27893
28150
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
27894
28151
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
27895
28152
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
@@ -27924,6 +28181,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
27924
28181
 
27925
28182
 
27926
28183
 
28184
+
27927
28185
  /**
27928
28186
 
27929
28187
  * VoiceSDK v2 - Multi-codec speech-to-speech SDK
@@ -28021,6 +28279,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28021
28279
  // Audio constraints for getUserMedia (optional)
28022
28280
  // If not provided, defaults will be used: echoCancellation: true, noiseSuppression: true, autoGainControl: true
28023
28281
  audioConstraints: config.audioConstraints || null,
28282
+ // Last-hop TTS playback via hidden <audio> (AEC far-end). Default on.
28283
+ // Set false to fall back to AudioContext.destination (old path).
28284
+ htmlAudioPlayback: config.htmlAudioPlayback !== false,
28024
28285
  // Protocol version
28025
28286
 
28026
28287
  protocolVersion: config.protocolVersion || 2,
@@ -28142,7 +28403,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28142
28403
  key: "_registerBuiltInTools",
28143
28404
  value: function _registerBuiltInTools() {
28144
28405
  try {
28145
- (0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__.registerVisualTools)(this.clientToolsRegistry);
28406
+ (0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_8__.registerVisualTools)(this.clientToolsRegistry);
28146
28407
  } catch (error) {
28147
28408
  console.error('❌ VoiceSDK: Error registering built-in tools:', error);
28148
28409
  console.error(' Error details:', error.message, error.stack);
@@ -28880,7 +29141,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28880
29141
  // iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
28881
29142
  var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
28882
29143
  var env = {
28883
- sdkVersion: true ? "2.48.0" : 0,
29144
+ sdkVersion: true ? "2.48.2" : 0,
28884
29145
  ua: ua,
28885
29146
  platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
28886
29147
  mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
@@ -28895,7 +29156,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28895
29156
  } catch (e) {
28896
29157
  console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
28897
29158
  return {
28898
- sdkVersion: true ? "2.48.0" : 0
29159
+ sdkVersion: true ? "2.48.2" : 0
28899
29160
  };
28900
29161
  }
28901
29162
  }
@@ -28911,11 +29172,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28911
29172
  key: "_sendClientAudioInfo",
28912
29173
  value: function _sendClientAudioInfo() {
28913
29174
  try {
28914
- var _this$audioRecorder, _this$audioRecorder$g, _s$echoCancellation, _s$noiseSuppression, _s$autoGainControl, _s$voiceIsolation, _s$sampleRate, _s$channelCount, _this$audioRecorder2, _this$audioPlayer, _this$audioRecorder3;
29175
+ var _this$audioRecorder, _this$audioRecorder$g, _s$echoCancellation, _s$noiseSuppression, _s$autoGainControl, _s$voiceIsolation, _s$sampleRate, _s$channelCount, _this$audioRecorder2, _this$audioPlayer, _this$audioRecorder3, _this$audioPlayer2, _this$audioPlayer2$ge;
28915
29176
  var track = (_this$audioRecorder = this.audioRecorder) === null || _this$audioRecorder === void 0 || (_this$audioRecorder = _this$audioRecorder.mediaStream) === null || _this$audioRecorder === void 0 || (_this$audioRecorder$g = _this$audioRecorder.getAudioTracks) === null || _this$audioRecorder$g === void 0 ? void 0 : _this$audioRecorder$g.call(_this$audioRecorder)[0];
28916
29177
  if (!track) return;
28917
29178
  var s = track.getSettings ? track.getSettings() : {};
28918
- var audio = {
29179
+ var audio = _objectSpread({
28919
29180
  aec: (_s$echoCancellation = s.echoCancellation) !== null && _s$echoCancellation !== void 0 ? _s$echoCancellation : null,
28920
29181
  ns: (_s$noiseSuppression = s.noiseSuppression) !== null && _s$noiseSuppression !== void 0 ? _s$noiseSuppression : null,
28921
29182
  agc: (_s$autoGainControl = s.autoGainControl) !== null && _s$autoGainControl !== void 0 ? _s$autoGainControl : null,
@@ -28926,7 +29187,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28926
29187
  captureCtxRate: ((_this$audioRecorder2 = this.audioRecorder) === null || _this$audioRecorder2 === void 0 || (_this$audioRecorder2 = _this$audioRecorder2.audioContext) === null || _this$audioRecorder2 === void 0 ? void 0 : _this$audioRecorder2.sampleRate) || null,
28927
29188
  playbackCtxRate: ((_this$audioPlayer = this.audioPlayer) === null || _this$audioPlayer === void 0 || (_this$audioPlayer = _this$audioPlayer.audioContext) === null || _this$audioPlayer === void 0 ? void 0 : _this$audioPlayer.sampleRate) || null,
28928
29189
  constraintsFallback: ((_this$audioRecorder3 = this.audioRecorder) === null || _this$audioRecorder3 === void 0 || (_this$audioRecorder3 = _this$audioRecorder3.mediaStream) === null || _this$audioRecorder3 === void 0 ? void 0 : _this$audioRecorder3._ttpConstraintsFallback) === true
28929
- };
29190
+ }, ((_this$audioPlayer2 = this.audioPlayer) === null || _this$audioPlayer2 === void 0 || (_this$audioPlayer2$ge = _this$audioPlayer2.getHtmlAudioPlaybackInfo) === null || _this$audioPlayer2$ge === void 0 ? void 0 : _this$audioPlayer2$ge.call(_this$audioPlayer2)) || {});
28930
29191
  this.sendMessage({
28931
29192
  t: 'client_audio_info',
28932
29193
  audio: audio
@@ -28940,7 +29201,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28940
29201
  key: "sendHelloMessage",
28941
29202
  value: function () {
28942
29203
  var _sendHelloMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
28943
- var inputFormat, requestedOutputFormat, inputError, outputError, helloMessage;
29204
+ var inputFormat, requestedOutputFormat, inputError, outputError, helloMessage, wireFlavor;
28944
29205
  return _regenerator().w(function (_context2) {
28945
29206
  while (1) switch (_context2.n) {
28946
29207
  case 0:
@@ -29029,14 +29290,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29029
29290
  console.log('⚠️ VoiceSDK v2: Variables NOT included - condition failed');
29030
29291
  }
29031
29292
 
29032
- // Include flavor object if set (additive backward compatible)
29033
- if (this.config.flavor) {
29034
- helloMessage.flavor = this.config.flavor;
29293
+ // Partner fields only UI-only keys like callView stay local (backend Flavor DTO rejects them)
29294
+ wireFlavor = (0,_core_helloFlavor_js__WEBPACK_IMPORTED_MODULE_6__.flavorForHello)(this.config.flavor);
29295
+ if (wireFlavor) {
29296
+ helloMessage.flavor = wireFlavor;
29035
29297
  }
29036
29298
 
29037
29299
  // Include SDK build time for debugging
29038
29300
  if (true) {
29039
- helloMessage.lastBuildTime = "2026-08-02T10:38:16.158Z";
29301
+ helloMessage.lastBuildTime = "2026-08-20T14:41:57.957Z";
29040
29302
  }
29041
29303
 
29042
29304
  // Client environment (device/browser/webview) for backend logs + Langfuse metadata
@@ -29280,7 +29542,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29280
29542
  case 'get_page_context':
29281
29543
  // Handle get_page_context request from backend
29282
29544
  console.log('📖 [VISUAL ASSISTANT] Backend requested page context');
29283
- (0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__.extractPageContext)().then(function (pageContext) {
29545
+ (0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_8__.extractPageContext)().then(function (pageContext) {
29284
29546
  // Use existing DOM scanner
29285
29547
  _this6.sendMessage({
29286
29548
  t: 'page_context',
@@ -29301,7 +29563,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29301
29563
  console.log('📸 [VISUAL ASSISTANT] Backend requested screenshot');
29302
29564
  try {
29303
29565
  // Use existing screenshot capture function
29304
- (0,_utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__.captureScreenshot)().then(function (screenshot) {
29566
+ (0,_utils_screenshot_js__WEBPACK_IMPORTED_MODULE_7__.captureScreenshot)().then(function (screenshot) {
29305
29567
  _this6.sendMessage({
29306
29568
  t: 'screenshot',
29307
29569
  screenshot: {
@@ -32579,6 +32841,7 @@ var AgentSDK = /*#__PURE__*/function () {
32579
32841
  outputBitDepth: this.config.outputBitDepth || 16,
32580
32842
  // Default: 16-bit
32581
32843
  flavor: this.config.flavor || null,
32844
+ htmlAudioPlayback: this.config.htmlAudioPlayback !== false,
32582
32845
  // Shared client-tool handler map (set by TTPChatWidget so voice + text SDKs
32583
32846
  // both look up handlers in the same registration site).
32584
32847
  sharedToolHandlers: this.config.sharedToolHandlers || null,
@@ -34573,7 +34836,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34573
34836
  agentTextColor: ((_userConfig$messages8 = userConfig.messages) === null || _userConfig$messages8 === void 0 ? void 0 : _userConfig$messages8.agentTextColor) || ((_userConfig$messages9 = userConfig.messages) === null || _userConfig$messages9 === void 0 ? void 0 : _userConfig$messages9.textColor) || '#1F2937',
34574
34837
  userAvatarIcon: ((_userConfig$messages0 = userConfig.messages) === null || _userConfig$messages0 === void 0 ? void 0 : _userConfig$messages0.userAvatarIcon) || '👤',
34575
34838
  agentAvatarIcon: ((_userConfig$messages1 = userConfig.messages) === null || _userConfig$messages1 === void 0 ? void 0 : _userConfig$messages1.agentAvatarIcon) || '🤖',
34576
- fontSize: ((_userConfig$messages10 = userConfig.messages) === null || _userConfig$messages10 === void 0 ? void 0 : _userConfig$messages10.fontSize) || '12px',
34839
+ fontSize: ((_userConfig$messages10 = userConfig.messages) === null || _userConfig$messages10 === void 0 ? void 0 : _userConfig$messages10.fontSize) || '15px',
34577
34840
  borderRadius: ((_userConfig$messages11 = userConfig.messages) === null || _userConfig$messages11 === void 0 ? void 0 : _userConfig$messages11.borderRadius) || 16
34578
34841
  }, userConfig.messages),
34579
34842
  // Animation Configuration
@@ -34662,7 +34925,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
34662
34925
  if (error && (error.message === 'DOMAIN_NOT_WHITELISTED' || error.message && error.message.includes('Domain not whitelisted'))) {
34663
34926
  return; // Already handled by domainError event
34664
34927
  }
34665
- _this5.textInterface.showError(error.message || error);
34928
+ // WS onerror is a browser Event (no .message) do not paint "[object Event]"
34929
+ var text = typeof error === 'string' ? error : error && error.message;
34930
+ if (!text) return;
34931
+ _this5.textInterface.showError(text);
34666
34932
  _this5.textInterface.stopStreamingState();
34667
34933
  });
34668
34934
  this.sdk.on('chunk', function (chunk) {
@@ -35082,7 +35348,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35082
35348
  return;
35083
35349
  }
35084
35350
  this._ensureAboutStyles();
35085
- var version = true ? "2.48.0" : 0;
35351
+ var version = true ? "2.48.2" : 0;
35086
35352
  var convId = this._getLastConversationId();
35087
35353
  var t = function t(k, fb) {
35088
35354
  try {
@@ -37533,6 +37799,16 @@ var MESSAGE_COLLAPSE_THRESHOLD = 353;
37533
37799
 
37534
37800
  /** How often a streaming agent bubble is re-rendered from its markdown buffer (ms). */
37535
37801
  var STREAM_RENDER_INTERVAL_MS = 40;
37802
+
37803
+ /**
37804
+ * The time + delivery checks sit absolutely positioned in the bubble's bottom
37805
+ * corner, so the bubble reserves a physical gutter wide enough that the last
37806
+ * line of text can never run underneath them. GUTTER must stay comfortably
37807
+ * wider than INSET plus the rendered metadata ("23:59 ✓✓" — 24h time is the
37808
+ * widest case); `test/text-interface.test.mjs` asserts the two stay in step.
37809
+ */
37810
+ var MESSAGE_META_INSET_PX = 14;
37811
+ var MESSAGE_META_GUTTER_PX = 68;
37536
37812
  function collapseMessageText(text) {
37537
37813
  if (text.length <= MESSAGE_COLLAPSE_THRESHOLD) return text;
37538
37814
  return "".concat(text.slice(0, MESSAGE_COLLAPSE_THRESHOLD).trimEnd(), "\u2026");
@@ -37649,7 +37925,7 @@ var TextInterface = /*#__PURE__*/function () {
37649
37925
  var inputFontSize = this.config.inputFontSize || ((_this$config$panel0 = this.config.panel) === null || _this$config$panel0 === void 0 ? void 0 : _this$config$panel0.inputFontSize) || '16px';
37650
37926
  var inputBorderRadius = this.config.inputBorderRadius || ((_this$config$panel1 = this.config.panel) === null || _this$config$panel1 === void 0 ? void 0 : _this$config$panel1.inputBorderRadius) || 20;
37651
37927
  var inputPadding = this.config.inputPadding || ((_this$config$panel10 = this.config.panel) === null || _this$config$panel10 === void 0 ? void 0 : _this$config$panel10.inputPadding) || '9px 12px';
37652
- var messageFontSize = messages.fontSize || '12px';
37928
+ var messageFontSize = messages.fontSize || '15px';
37653
37929
  var panelLight;
37654
37930
  var topBarBg;
37655
37931
  var topBarBorder;
@@ -37769,7 +38045,7 @@ var TextInterface = /*#__PURE__*/function () {
37769
38045
 
37770
38046
  // Add !important to display rules when not using Shadow DOM (to override theme CSS)
37771
38047
  var important = this.config.useShadowDOM === false ? ' !important' : '';
37772
- return "\n .text-interface-top-bar {\n flex-shrink: 0".concat(important, ";\n padding: 12px 14px").concat(important, ";\n border-bottom: 1px solid ").concat(topBarBorder).concat(important, ";\n background: ").concat(topBarBg).concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: flex-start").concat(important, ";\n gap: 10px").concat(important, ";\n direction: ").concat(textDirection).concat(important, ";\n }\n .text-interface-agent-heading {\n display: flex").concat(important, ";\n flex-direction: row").concat(important, ";\n align-items: center").concat(important, ";\n gap: 9px").concat(important, ";\n min-width: 0").concat(important, ";\n flex: 1").concat(important, ";\n margin-right: 0").concat(important, ";\n }\n .text-interface-agent-avatar {\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex: 0 0 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: ").concat(useVoiceTheme ? avatarAgentBg : "linear-gradient(135deg, ".concat(sendButtonColor, ", ").concat(sendButtonHoverColor, ")")).concat(important, ";\n border: none").concat(important, ";\n box-shadow: none").concat(important, ";\n }\n .text-interface-agent-initial { color: #fff").concat(important, "; font-size: 14px").concat(important, "; font-weight: 500").concat(important, "; line-height: 1").concat(important, "; }\n .text-interface-agent-copy {\n display: flex").concat(important, ";\n flex-direction: column").concat(important, ";\n align-items: flex-start").concat(important, ";\n gap: 2px").concat(important, ";\n min-width: 0").concat(important, ";\n }\n .text-interface-agent-name {\n color: #f0eff8").concat(important, ";\n font-size: 14px").concat(important, ";\n font-weight: 500").concat(important, ";\n line-height: 1.15").concat(important, ";\n overflow: hidden").concat(important, ";\n text-overflow: ellipsis").concat(important, ";\n white-space: nowrap").concat(important, ";\n }\n .text-interface-agent-status {\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 6px").concat(important, ";\n color: rgba(255,255,255,0.7)").concat(important, ";\n font-size: 10px").concat(important, ";\n }\n .text-interface-agent-status > span {\n width: 7px").concat(important, ";\n height: 7px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: #4ade80").concat(important, ";\n box-shadow: 0 0 10px rgba(74,222,128,0.75)").concat(important, ";\n }\n .text-interface-top-actions { display: flex").concat(important, "; align-items: center").concat(important, "; gap: 4px").concat(important, "; margin-inline-start: auto").concat(important, "; }\n .text-interface-close-btn:hover,\n .text-interface-home-btn:hover,\n .text-interface-footer .ttp-info-btn:hover {\n background: rgba(255,255,255,0.22)").concat(important, ";\n color: #ffffff").concat(important, ";\n box-shadow: inset 0 1px 0 rgba(255,255,255,0.26), 0 4px 14px rgba(0,0,0,0.22)").concat(important, ";\n }\n .text-interface-close-btn,\n .text-interface-home-btn,\n .text-interface-footer .ttp-info-btn {\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n display: grid").concat(important, ";\n place-items: center").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n border: 1px solid rgba(255,255,255,0.14)").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(255,255,255,0.12)").concat(important, ";\n color: rgba(224,218,255,0.84)").concat(important, ";\n cursor: pointer").concat(important, ";\n font-family: inherit").concat(important, ";\n transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease").concat(important, ";\n box-shadow: inset 0 1px 0 rgba(255,255,255,0.14)").concat(important, ";\n }\n .text-interface-home-btn svg,\n .text-interface-close-btn svg,\n .text-interface-footer .ttp-info-btn svg { width: 18px").concat(important, "; height: 18px").concat(important, "; fill: none").concat(important, "; stroke: currentColor").concat(important, "; stroke-width: 1.9").concat(important, "; stroke-linecap: round").concat(important, "; stroke-linejoin: round").concat(important, "; }\n .text-interface-home-btn svg { fill: none").concat(important, "; stroke: currentColor").concat(important, "; width: 20px").concat(important, "; height: 20px").concat(important, "; stroke-width: 1.8").concat(important, "; }\n .input-container .text-interface-home-btn {\n width: 38px").concat(important, ";\n height: 38px").concat(important, ";\n color: rgba(224,218,255,0.84)").concat(important, ";\n background: rgba(255,255,255,0.12)").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n /* Messages container using new classes */\n #messagesContainer { \n flex: 1").concat(important, "; \n overflow-y: auto").concat(important, "; \n overflow-x: hidden").concat(important, "; \n padding: 20px").concat(important, "; \n background: ").concat(messagesAreaBg).concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n gap: 16px").concat(important, "; \n min-height: 0").concat(important, "; \n }\n .empty-state { \n flex: 1").concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n gap: 12px").concat(important, "; \n color: ").concat(emptyMuted).concat(important, "; \n text-align: center").concat(important, "; \n padding: 20px").concat(important, "; \n }\n .empty-state-icon { font-size: 52px").concat(important, "; opacity: 0.3").concat(important, "; }\n .empty-state-title { font-size: 22px").concat(important, "; font-weight: 700").concat(important, "; color: ").concat(emptyTitle).concat(important, "; }\n .empty-state-text { font-size: 15px").concat(important, "; max-width: 300px").concat(important, "; line-height: 1.45").concat(important, "; }\n .empty-state-icon {\n width: 48px").concat(important, "; height: 48px").concat(important, "; border-radius: 16px").concat(important, ";\n display: grid").concat(important, "; place-items: center").concat(important, "; color: rgba(230,225,255,0.9)").concat(important, ";\n background: rgba(255,255,255,0.1)").concat(important, ";\n border: 1px solid rgba(225,219,255,0.18)").concat(important, ";\n }\n .empty-state-icon svg { width: 24px").concat(important, "; height: 24px").concat(important, "; }\n\n .text-interface { \n display: none").concat(important, "; \n flex: 1").concat(important, "; \n flex-direction: column").concat(important, "; \n min-height: 0").concat(important, "; \n overflow: hidden").concat(important, "; \n direction: ").concat(textDirection).concat(important, ";\n }\n .text-interface.active { display: flex").concat(important, "; }\n \n .message { \n display: flex").concat(important, "; \n flex-direction: column").concat(important, ";\n gap: 4px").concat(important, ";\n padding: 4px 0").concat(important, "; \n width: 100%").concat(important, ";\n max-width: 100%").concat(important, "; \n align-items: stretch").concat(important, ";\n }\n .message-bubble { \n position: relative").concat(important, ";\n padding: 8px 62px 8px 11px").concat(important, ";\n padding-right: 62px").concat(important, ";\n border-radius: 12px").concat(important, ";\n max-width: min(72%, 420px)").concat(important, ";\n font-size: ").concat(messageFontSize).concat(important, ";\n line-height: 1.36").concat(important, ";\n word-wrap: break-word").concat(important, "; \n box-shadow: ").concat(useVoiceTheme ? 'inset 0 1px 0 rgba(255,255,255,0.36), inset 0 -1px 0 rgba(5, 3, 26, 0.18), 0 8px 20px rgba(4, 3, 22, 0.24)' : 'none').concat(important, ";\n }\n .message-bubble[dir=\"rtl\"] { text-align: right").concat(important, "; direction: rtl").concat(important, "; }\n .message-bubble[dir=\"ltr\"] { text-align: left").concat(important, "; direction: ltr").concat(important, "; }\n .message-bubble::before {\n content: ''").concat(important, ";\n position: absolute").concat(important, ";\n top: 1px").concat(important, ";\n right: 1px").concat(important, ";\n left: 1px").concat(important, ";\n height: 48%").concat(important, ";\n border-radius: 11px 11px 8px 8px").concat(important, ";\n background: linear-gradient(180deg, rgba(255,255,255,0.19), rgba(255,255,255,0))").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n .message.user .message-bubble { \n background: ").concat(userBubbleBg).concat(important, "; \n color: ").concat(userBubbleTextColor).concat(important, "; \n border-top-right-radius: 4px").concat(important, ";\n }\n .message.agent .message-bubble {\n background: ").concat(agentBubbleBg).concat(important, ";\n color: ").concat(agentBubbleTextColor).concat(important, ";\n border: ").concat(agentBubbleBorder).concat(important, ";\n border-top-left-radius: 4px").concat(important, ";\n }\n\n /* ---------------------------------------------------------------\n Rendered markdown inside the agent bubble.\n Everything is scoped to .message-bubble so host-page styles can't\n reach in and the user bubble (plain text) is untouched.\n Logical properties (padding-inline-start, border-inline-start) keep\n the layout correct in RTL locales such as Hebrew.\n ---------------------------------------------------------------- */\n .message-bubble.md, .message-bubble .md-seg { line-height: 1.55").concat(important, "; }\n .message-bubble .md-seg { display: block").concat(important, "; }\n\n .message-bubble p {\n margin: 0 0 10px").concat(important, ";\n padding: 0").concat(important, ";\n }\n /* Deliberately gated on .md / .md-seg rather than a bare\n \".message-bubble > *\": the VOICE live-transcript row reuses the\n .message-bubble class in this same shadow root, and its children\n (.live-badge, .ttp-cursor) are not ours to restyle. */\n .message-bubble.md > *:last-child,\n .message-bubble .md-seg:last-child > *:last-child { margin-bottom: 0").concat(important, "; }\n .message-bubble.md > *:first-child,\n .message-bubble .md-seg:first-child > *:first-child { margin-top: 0").concat(important, "; }\n\n .message-bubble .md-h {\n margin: 16px 0 7px").concat(important, ";\n padding: 0").concat(important, ";\n font-weight: 700").concat(important, ";\n line-height: 1.3").concat(important, ";\n color: inherit").concat(important, ";\n }\n .message-bubble .md-h1 { font-size: 1.18em").concat(important, "; }\n .message-bubble .md-h2 { font-size: 1.09em").concat(important, "; }\n .message-bubble .md-h3 { font-size: 1.02em").concat(important, "; }\n .message-bubble .md-h4,\n .message-bubble .md-h5,\n .message-bubble .md-h6 {\n font-size: 0.95em").concat(important, ";\n letter-spacing: 0.02em").concat(important, ";\n color: ").concat(mdMuted).concat(important, ";\n }\n\n /* Logical properties only \u2014 a physical padding-left/border-left after\n these would resolve to the same property and silently win in LTR. */\n .message-bubble ul, .message-bubble ol {\n margin: 8px 0 11px").concat(important, ";\n padding-inline-start: 1.45em").concat(important, ";\n list-style-position: outside").concat(important, ";\n }\n .message-bubble ul { list-style-type: disc").concat(important, "; }\n .message-bubble ol { list-style-type: decimal").concat(important, "; }\n .message-bubble ul ul { list-style-type: circle").concat(important, "; }\n .message-bubble li {\n margin: 0 0 5px").concat(important, ";\n padding: 0").concat(important, ";\n line-height: 1.5").concat(important, ";\n }\n .message-bubble li:last-child { margin-bottom: 0").concat(important, "; }\n .message-bubble li::marker { color: ").concat(mdAccent).concat(important, "; }\n .message-bubble li > ul, .message-bubble li > ol { margin: 5px 0 2px").concat(important, "; }\n .message-bubble li > p { margin: 0 0 5px").concat(important, "; }\n\n .message-bubble strong, .message-bubble b { font-weight: 700").concat(important, "; }\n .message-bubble em, .message-bubble i { font-style: italic").concat(important, "; }\n .message-bubble del { text-decoration: line-through").concat(important, "; opacity: 0.7").concat(important, "; }\n\n .message-bubble a {\n color: ").concat(mdLink).concat(important, ";\n text-decoration: underline").concat(important, ";\n text-underline-offset: 2px").concat(important, ";\n word-break: break-word").concat(important, ";\n }\n .message-bubble a:hover { opacity: 0.82").concat(important, "; }\n\n .message-bubble code {\n font-family: ").concat(mdMono).concat(important, ";\n font-size: 0.88em").concat(important, ";\n background: ").concat(mdSubtleBg).concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 5px").concat(important, ";\n padding: 1px 5px").concat(important, ";\n white-space: break-spaces").concat(important, ";\n word-break: break-word").concat(important, ";\n direction: ltr").concat(important, ";\n unicode-bidi: embed").concat(important, ";\n }\n .message-bubble pre {\n margin: 10px 0 11px").concat(important, ";\n padding: 10px 12px").concat(important, ";\n background: ").concat(mdSubtleBg).concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 10px").concat(important, ";\n overflow-x: auto").concat(important, ";\n direction: ltr").concat(important, ";\n text-align: left").concat(important, ";\n }\n .message-bubble pre code {\n background: none").concat(important, ";\n border: none").concat(important, ";\n border-radius: 0").concat(important, ";\n padding: 0").concat(important, ";\n font-size: 0.85em").concat(important, ";\n line-height: 1.5").concat(important, ";\n white-space: pre").concat(important, ";\n word-break: normal").concat(important, ";\n }\n\n .message-bubble blockquote {\n margin: 10px 0").concat(important, ";\n padding-block: 2px").concat(important, ";\n padding-inline-start: 12px").concat(important, ";\n padding-inline-end: 0").concat(important, ";\n border-inline-start: 3px solid ").concat(mdAccent).concat(important, ";\n color: ").concat(mdMuted).concat(important, ";\n }\n .message-bubble blockquote > *:last-child { margin-bottom: 0").concat(important, "; }\n\n .message-bubble hr {\n border: none").concat(important, ";\n border-top: 1px solid ").concat(mdRule).concat(important, ";\n height: 0").concat(important, ";\n margin: 13px 0").concat(important, ";\n }\n\n .message-bubble .md-table-wrap {\n margin: 10px 0 11px").concat(important, ";\n overflow-x: auto").concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 9px").concat(important, ";\n }\n .message-bubble table {\n border-collapse: collapse").concat(important, ";\n width: 100%").concat(important, ";\n font-size: 0.93em").concat(important, ";\n }\n .message-bubble th, .message-bubble td {\n padding: 7px 11px").concat(important, ";\n text-align: start").concat(important, ";\n border-bottom: 1px solid ").concat(mdBorder).concat(important, ";\n vertical-align: top").concat(important, ";\n }\n .message-bubble thead th {\n background: ").concat(mdSubtleBg).concat(important, ";\n font-weight: 700").concat(important, ";\n }\n .message-bubble tbody tr:nth-child(even) { background: ").concat(mdSubtlerBg).concat(important, "; }\n .message-bubble tbody tr:last-child td { border-bottom: none").concat(important, "; }\n\n .message-bubble .md-img {\n display: block").concat(important, ";\n max-width: 100%").concat(important, ";\n height: auto").concat(important, ";\n border-radius: 8px").concat(important, ";\n margin: 8px 0").concat(important, ";\n }\n\n .message.user { \n align-self: stretch").concat(important, ";\n align-items: stretch").concat(important, ";\n }\n .message.agent {\n align-self: stretch").concat(important, ";\n align-items: stretch").concat(important, ";\n }\n .message.user .message-bubble {\n margin-left: auto").concat(important, ";\n margin-right: 0").concat(important, ";\n }\n .message.agent .message-bubble {\n margin-left: 0").concat(important, ";\n margin-right: auto").concat(important, ";\n }\n .message-meta {\n position: absolute").concat(important, ";\n right: 11px").concat(important, ";\n bottom: 8px").concat(important, ";\n z-index: 2").concat(important, ";\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 5px").concat(important, ";\n margin: 0").concat(important, ";\n color: rgba(255,255,255,0.55)").concat(important, ";\n font-size: 9px").concat(important, ";\n line-height: 1").concat(important, ";\n direction: ltr").concat(important, ";\n white-space: nowrap").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n .message.user .message-meta {\n color: rgba(255,255,255,0.74)").concat(important, ";\n }\n .message-content { position: relative").concat(important, "; z-index: 1").concat(important, "; display: inline").concat(important, "; overflow-wrap: anywhere").concat(important, "; word-break: break-word").concat(important, "; direction: inherit").concat(important, "; }\n .message-meta-spacer { display: inline-block").concat(important, "; width: 0").concat(important, "; height: 1px").concat(important, "; }\n .message-delivery-checks { color: rgba(255,255,255,0.92)").concat(important, "; font-weight: 700").concat(important, "; letter-spacing: -2px").concat(important, "; }\n .message-expand-button {\n position: relative").concat(important, ";\n z-index: 3").concat(important, ";\n margin: 0 4px").concat(important, ";\n padding: 0").concat(important, ";\n border: 0").concat(important, ";\n background: transparent").concat(important, ";\n color: #c4b5fd").concat(important, ";\n font: inherit").concat(important, ";\n font-size: 0.78em").concat(important, ";\n font-weight: 600").concat(important, ";\n line-height: inherit").concat(important, ";\n text-decoration: underline").concat(important, ";\n text-underline-offset: 2px").concat(important, ";\n cursor: pointer").concat(important, ";\n }\n .message-expand-button:hover { color: #ede9fe").concat(important, "; }\n .message-avatar { \n width: ").concat(avatarSize).concat(important, "; \n height: ").concat(avatarSize).concat(important, "; \n min-width: ").concat(avatarSize).concat(important, "; \n border-radius: 50%").concat(important, "; \n display: flex").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n flex-shrink: 0").concat(important, "; \n color: inherit").concat(important, "; \n font-size: ").concat(useVoiceTheme ? '14' : '20', "px").concat(important, "; \n line-height: 1").concat(important, "; \n background: transparent").concat(important, "; \n border: none").concat(important, "; \n box-sizing: border-box").concat(important, "; \n }\n .message-avatar.user { background: ").concat(avatarUserBg).concat(important, "; color: ").concat(avatarUserColor).concat(important, "; }\n .message-avatar.agent { background: ").concat(avatarAgentBg).concat(important, "; }\n \n .message.system {\n background: ").concat(messages.systemBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n .message.error {\n background: ").concat(messages.errorBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n \n .input-container {\n display: flex").concat(important, ";\n gap: 8px").concat(important, ";\n padding: 12px 16px").concat(important, ";\n background: ").concat(inputContainerBg).concat(important, ";\n border-top: 1px solid ").concat(inputContainerBorderTop).concat(important, ";\n align-items: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n flex-direction: row").concat(important, ";\n direction: ltr").concat(important, ";\n }\n .text-interface-footer {\n position: relative").concat(important, ";\n flex-shrink: 0").concat(important, ";\n min-height: 28px").concat(important, ";\n padding: 4px 12px").concat(important, ";\n background: ").concat(topBarBg).concat(important, ";\n border-top: 1px solid ").concat(inputContainerBorderTop).concat(important, ";\n display: flex").concat(important, ";\n justify-content: center").concat(important, ";\n align-items: center").concat(important, ";\n gap: 8px").concat(important, ";\n direction: ltr").concat(important, ";\n }\n .text-interface-footer .ttp-info-btn {\n position: absolute").concat(important, ";\n right: 12px").concat(important, ";\n margin-left: auto").concat(important, ";\n }\n .text-interface-powered {\n color: rgba(255,255,255,0.46)").concat(important, ";\n font-size: 10px").concat(important, ";\n line-height: 1.2").concat(important, ";\n text-align: center").concat(important, ";\n white-space: nowrap").concat(important, ";\n }\n .text-interface-powered a {\n color: ").concat(sendButtonColor).concat(important, ";\n text-decoration: none").concat(important, ";\n }\n .text-interface.active ~ .ttp-footer {\n display: none").concat(important, ";\n }\n \n .input-wrapper {\n position: relative").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .message-input {\n width: 100%").concat(important, ";\n min-height: ").concat(TEXT_INPUT_MIN_HEIGHT_PX, "px").concat(important, ";\n max-height: ").concat(TEXT_INPUT_MAX_HEIGHT_PX, "px").concat(important, ";\n padding: ").concat(inputPadding, ";\n border: 1px solid ").concat(inputBorderColor, ";\n border-radius: ").concat(inputBorderRadius, "px;\n font-size: ").concat(inputFontSize, ";\n font-family: inherit").concat(important, ";\n line-height: 1.4").concat(important, ";\n resize: none").concat(important, ";\n overflow-y: auto").concat(important, ";\n background: ").concat(inputBackgroundColor, ";\n color: ").concat(inputTextColor, ";\n vertical-align: top").concat(important, ";\n margin: 0").concat(important, ";\n display: block").concat(important, ";\n white-space: pre-wrap").concat(important, ";\n word-wrap: break-word").concat(important, ";\n text-align: start").concat(important, ";\n direction: ").concat(textDirection).concat(important, ";\n unicode-bidi: plaintext").concat(important, ";\n -webkit-appearance: none").concat(important, ";\n appearance: none").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n \n .message-input:focus {\n outline: none").concat(important, ";\n border-color: ").concat(inputFocusColor, ";\n background: ").concat(inputFocusBg).concat(important, ";\n box-shadow: ").concat(inputFocusBoxShadow, ";\n }\n \n .message-input::placeholder {\n color: ").concat(placeholderColor).concat(important, ";\n text-align: start").concat(important, ";\n direction: ").concat(textDirection).concat(important, ";\n }\n \n .send-button {\n width: 38px").concat(important, ";\n height: 38px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n background: linear-gradient(135deg, ").concat(sendButtonColor, ", ").concat(sendButtonHoverColor, ")").concat(important, ";\n color: ").concat(sendButtonTextColor, ";\n font-size: ").concat(this.config.sendButtonFontSize || ((_this$config$panel15 = this.config.panel) === null || _this$config$panel15 === void 0 ? void 0 : _this$config$panel15.sendButtonFontSize) || '16px', ";\n font-weight: ").concat(this.config.sendButtonFontWeight || ((_this$config$panel16 = this.config.panel) === null || _this$config$panel16 === void 0 ? void 0 : _this$config$panel16.sendButtonFontWeight) || '500', ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n transition: all 0.2s ease").concat(important, ";\n box-shadow: 0 4px 12px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.32)").concat(important, ";\n }\n .send-icon {\n width: 18px").concat(important, ";\n height: 18px").concat(important, ";\n fill: none").concat(important, ";\n stroke: currentColor").concat(important, ";\n stroke-width: 2.1").concat(important, ";\n stroke-linecap: round").concat(important, ";\n stroke-linejoin: round").concat(important, ";\n }\n \n .send-button:hover:not(:disabled) {\n background: ").concat(sendButtonHoverColor, ";\n transform: scale(1.05)").concat(important, ";\n box-shadow: 0 6px 16px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.42)").concat(important, ";\n }\n \n .send-button-hint {\n width: 100%").concat(important, ";\n text-align: center").concat(important, ";\n margin-top: 4px").concat(important, ";\n }\n \n .send-button:disabled {\n opacity: 0.5").concat(important, ";\n cursor: not-allowed").concat(important, ";\n }\n \n .typing-indicator {\n display: inline-flex").concat(important, ";\n gap: 4px").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .typing-dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", ").concat(typingDotAlpha, ")").concat(important, ";\n animation: typingDot 1.4s ease-in-out infinite").concat(important, ";\n }\n \n .typing-dot:nth-child(2) { animation-delay: 0.2s").concat(important, "; }\n .typing-dot:nth-child(3) { animation-delay: 0.4s").concat(important, "; }\n \n @keyframes typingDot {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.7; }\n 30% { transform: translateY(-8px); opacity: 1; }\n }\n \n .error-message {\n padding: 12px").concat(important, ";\n background: ").concat(errorBubbleBg, ";\n border-radius: ").concat(messages.borderRadius, "px;\n color: ").concat(errorBubbleColor).concat(important, ";\n border: ").concat(errorBubbleBorder).concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n margin: 8px 0").concat(important, ";\n }\n \n ").concat(useVoiceTheme ? "\n #textInterface.active .input-container .send-button-hint {\n color: rgba(255,255,255,0.72)".concat(important, ";\n }\n ") : '', "\n \n @media (max-width: 768px) {\n #messagesContainer {\n padding: 12px").concat(important, ";\n gap: 12px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 85%").concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n padding: 12px 62px 12px 14px").concat(important, ";\n padding-right: 62px").concat(important, ";\n }\n \n .text-input-container {\n padding: 10px").concat(important, ";\n gap: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 10px 14px").concat(important, ";\n min-height: 44px").concat(important, ";\n }\n \n #text-chat-send {\n min-width: 56px").concat(important, ";\n min-height: 44px").concat(important, ";\n width: 56px").concat(important, ";\n height: 44px").concat(important, ";\n }\n \n .empty-state-icon {\n font-size: 44px").concat(important, ";\n }\n \n .empty-state-title {\n font-size: 20px").concat(important, ";\n }\n \n .empty-state-text {\n font-size: 14px").concat(important, ";\n }\n }\n \n @media (max-width: 480px) {\n #messagesContainer {\n padding: 10px").concat(important, ";\n gap: 10px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 90%").concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n padding: 10px 62px 10px 12px").concat(important, ";\n padding-right: 62px").concat(important, ";\n }\n \n .text-input-container {\n padding: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important;\n padding: 8px 12px").concat(important, ";\n }\n }\n ");
38048
+ return "\n .text-interface-top-bar {\n flex-shrink: 0".concat(important, ";\n padding: 12px 14px").concat(important, ";\n border-bottom: 1px solid ").concat(topBarBorder).concat(important, ";\n background: ").concat(topBarBg).concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: flex-start").concat(important, ";\n gap: 10px").concat(important, ";\n direction: ").concat(textDirection).concat(important, ";\n }\n .text-interface-agent-heading {\n display: flex").concat(important, ";\n flex-direction: row").concat(important, ";\n align-items: center").concat(important, ";\n gap: 9px").concat(important, ";\n min-width: 0").concat(important, ";\n flex: 1").concat(important, ";\n margin-right: 0").concat(important, ";\n }\n .text-interface-agent-avatar {\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex: 0 0 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: ").concat(useVoiceTheme ? avatarAgentBg : "linear-gradient(135deg, ".concat(sendButtonColor, ", ").concat(sendButtonHoverColor, ")")).concat(important, ";\n border: none").concat(important, ";\n box-shadow: none").concat(important, ";\n }\n .text-interface-agent-initial { color: #fff").concat(important, "; font-size: 14px").concat(important, "; font-weight: 500").concat(important, "; line-height: 1").concat(important, "; }\n .text-interface-agent-copy {\n display: flex").concat(important, ";\n flex-direction: column").concat(important, ";\n align-items: flex-start").concat(important, ";\n gap: 2px").concat(important, ";\n min-width: 0").concat(important, ";\n }\n .text-interface-agent-name {\n color: #f0eff8").concat(important, ";\n font-size: 14px").concat(important, ";\n font-weight: 500").concat(important, ";\n line-height: 1.15").concat(important, ";\n overflow: hidden").concat(important, ";\n text-overflow: ellipsis").concat(important, ";\n white-space: nowrap").concat(important, ";\n }\n .text-interface-agent-status {\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 6px").concat(important, ";\n color: rgba(255,255,255,0.7)").concat(important, ";\n font-size: 10px").concat(important, ";\n }\n .text-interface-agent-status > span {\n width: 7px").concat(important, ";\n height: 7px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: #4ade80").concat(important, ";\n box-shadow: 0 0 10px rgba(74,222,128,0.75)").concat(important, ";\n }\n .text-interface-top-actions { display: flex").concat(important, "; align-items: center").concat(important, "; gap: 4px").concat(important, "; margin-inline-start: auto").concat(important, "; }\n .text-interface-close-btn:hover,\n .text-interface-home-btn:hover,\n .text-interface-footer .ttp-info-btn:hover {\n background: rgba(255,255,255,0.22)").concat(important, ";\n color: #ffffff").concat(important, ";\n box-shadow: inset 0 1px 0 rgba(255,255,255,0.26), 0 4px 14px rgba(0,0,0,0.22)").concat(important, ";\n }\n .text-interface-close-btn,\n .text-interface-home-btn,\n .text-interface-footer .ttp-info-btn {\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n display: grid").concat(important, ";\n place-items: center").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n border: 1px solid rgba(255,255,255,0.14)").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(255,255,255,0.12)").concat(important, ";\n color: rgba(224,218,255,0.84)").concat(important, ";\n cursor: pointer").concat(important, ";\n font-family: inherit").concat(important, ";\n transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease").concat(important, ";\n box-shadow: inset 0 1px 0 rgba(255,255,255,0.14)").concat(important, ";\n }\n .text-interface-home-btn svg,\n .text-interface-close-btn svg,\n .text-interface-footer .ttp-info-btn svg { width: 18px").concat(important, "; height: 18px").concat(important, "; fill: none").concat(important, "; stroke: currentColor").concat(important, "; stroke-width: 1.9").concat(important, "; stroke-linecap: round").concat(important, "; stroke-linejoin: round").concat(important, "; }\n .text-interface-home-btn svg { fill: none").concat(important, "; stroke: currentColor").concat(important, "; width: 20px").concat(important, "; height: 20px").concat(important, "; stroke-width: 1.8").concat(important, "; }\n .input-container .text-interface-home-btn {\n width: 38px").concat(important, ";\n height: 38px").concat(important, ";\n color: rgba(224,218,255,0.84)").concat(important, ";\n background: rgba(255,255,255,0.12)").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n /* Messages container using new classes */\n #messagesContainer { \n flex: 1").concat(important, "; \n overflow-y: auto").concat(important, "; \n overflow-x: hidden").concat(important, "; \n padding: 20px").concat(important, "; \n background: ").concat(messagesAreaBg).concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n gap: 16px").concat(important, "; \n min-height: 0").concat(important, "; \n }\n .empty-state { \n flex: 1").concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n gap: 12px").concat(important, "; \n color: ").concat(emptyMuted).concat(important, "; \n text-align: center").concat(important, "; \n padding: 20px").concat(important, "; \n }\n .empty-state-icon { font-size: 52px").concat(important, "; opacity: 0.3").concat(important, "; }\n .empty-state-title { font-size: 22px").concat(important, "; font-weight: 700").concat(important, "; color: ").concat(emptyTitle).concat(important, "; }\n .empty-state-text { font-size: 15px").concat(important, "; max-width: 300px").concat(important, "; line-height: 1.45").concat(important, "; }\n .empty-state-icon {\n width: 48px").concat(important, "; height: 48px").concat(important, "; border-radius: 16px").concat(important, ";\n display: grid").concat(important, "; place-items: center").concat(important, "; color: rgba(230,225,255,0.9)").concat(important, ";\n background: rgba(255,255,255,0.1)").concat(important, ";\n border: 1px solid rgba(225,219,255,0.18)").concat(important, ";\n }\n .empty-state-icon svg { width: 24px").concat(important, "; height: 24px").concat(important, "; }\n\n .text-interface { \n display: none").concat(important, "; \n flex: 1").concat(important, "; \n flex-direction: column").concat(important, "; \n min-height: 0").concat(important, "; \n overflow: hidden").concat(important, "; \n direction: ").concat(textDirection).concat(important, ";\n }\n .text-interface.active { display: flex").concat(important, "; }\n \n .message { \n display: flex").concat(important, "; \n flex-direction: column").concat(important, ";\n gap: 4px").concat(important, ";\n padding: 4px 0").concat(important, "; \n width: 100%").concat(important, ";\n max-width: 100%").concat(important, "; \n align-items: stretch").concat(important, ";\n }\n .message-bubble { \n position: relative").concat(important, ";\n padding: 10px ").concat(MESSAGE_META_GUTTER_PX, "px 10px 14px").concat(important, ";\n border-radius: 14px").concat(important, ";\n max-width: min(72%, 440px)").concat(important, ";\n font-size: ").concat(messageFontSize).concat(important, ";\n line-height: 1.45").concat(important, ";\n word-wrap: break-word").concat(important, "; \n box-shadow: ").concat(useVoiceTheme ? 'inset 0 1px 0 rgba(255,255,255,0.36), inset 0 -1px 0 rgba(5, 3, 26, 0.18), 0 8px 20px rgba(4, 3, 22, 0.24)' : 'none').concat(important, ";\n }\n .message-bubble[dir=\"rtl\"] { text-align: right").concat(important, "; direction: rtl").concat(important, "; }\n .message-bubble[dir=\"ltr\"] { text-align: left").concat(important, "; direction: ltr").concat(important, "; }\n .message-bubble::before {\n content: ''").concat(important, ";\n position: absolute").concat(important, ";\n top: 1px").concat(important, ";\n right: 1px").concat(important, ";\n left: 1px").concat(important, ";\n height: 48%").concat(important, ";\n border-radius: 11px 11px 8px 8px").concat(important, ";\n background: linear-gradient(180deg, rgba(255,255,255,0.19), rgba(255,255,255,0))").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n .message.user .message-bubble { \n background: ").concat(userBubbleBg).concat(important, "; \n color: ").concat(userBubbleTextColor).concat(important, "; \n border-top-right-radius: 4px").concat(important, ";\n }\n .message.agent .message-bubble {\n background: ").concat(agentBubbleBg).concat(important, ";\n color: ").concat(agentBubbleTextColor).concat(important, ";\n border: ").concat(agentBubbleBorder).concat(important, ";\n border-top-left-radius: 4px").concat(important, ";\n }\n\n /* ---------------------------------------------------------------\n Rendered markdown inside the agent bubble.\n Everything is scoped to .message-bubble so host-page styles can't\n reach in and the user bubble (plain text) is untouched.\n Logical properties (padding-inline-start, border-inline-start) keep\n the layout correct in RTL locales such as Hebrew.\n ---------------------------------------------------------------- */\n .message-bubble.md, .message-bubble .md-seg { line-height: 1.55").concat(important, "; }\n .message-bubble .md-seg { display: block").concat(important, "; }\n\n .message-bubble p {\n margin: 0 0 10px").concat(important, ";\n padding: 0").concat(important, ";\n }\n /* Deliberately gated on .md / .md-seg rather than a bare\n \".message-bubble > *\": the VOICE live-transcript row reuses the\n .message-bubble class in this same shadow root, and its children\n (.live-badge, .ttp-cursor) are not ours to restyle. */\n .message-bubble.md > *:last-child,\n .message-bubble .md-seg:last-child > *:last-child { margin-bottom: 0").concat(important, "; }\n .message-bubble.md > *:first-child,\n .message-bubble .md-seg:first-child > *:first-child { margin-top: 0").concat(important, "; }\n\n .message-bubble .md-h {\n margin: 16px 0 7px").concat(important, ";\n padding: 0").concat(important, ";\n font-weight: 700").concat(important, ";\n line-height: 1.3").concat(important, ";\n color: inherit").concat(important, ";\n }\n .message-bubble .md-h1 { font-size: 1.18em").concat(important, "; }\n .message-bubble .md-h2 { font-size: 1.09em").concat(important, "; }\n .message-bubble .md-h3 { font-size: 1.02em").concat(important, "; }\n .message-bubble .md-h4,\n .message-bubble .md-h5,\n .message-bubble .md-h6 {\n font-size: 0.95em").concat(important, ";\n letter-spacing: 0.02em").concat(important, ";\n color: ").concat(mdMuted).concat(important, ";\n }\n\n /* Logical properties only \u2014 a physical padding-left/border-left after\n these would resolve to the same property and silently win in LTR. */\n .message-bubble ul, .message-bubble ol {\n margin: 8px 0 11px").concat(important, ";\n padding-inline-start: 1.45em").concat(important, ";\n list-style-position: outside").concat(important, ";\n }\n .message-bubble ul { list-style-type: disc").concat(important, "; }\n .message-bubble ol { list-style-type: decimal").concat(important, "; }\n .message-bubble ul ul { list-style-type: circle").concat(important, "; }\n .message-bubble li {\n margin: 0 0 5px").concat(important, ";\n padding: 0").concat(important, ";\n line-height: 1.5").concat(important, ";\n }\n .message-bubble li:last-child { margin-bottom: 0").concat(important, "; }\n .message-bubble li::marker { color: ").concat(mdAccent).concat(important, "; }\n .message-bubble li > ul, .message-bubble li > ol { margin: 5px 0 2px").concat(important, "; }\n .message-bubble li > p { margin: 0 0 5px").concat(important, "; }\n\n .message-bubble strong, .message-bubble b { font-weight: 700").concat(important, "; }\n .message-bubble em, .message-bubble i { font-style: italic").concat(important, "; }\n .message-bubble del { text-decoration: line-through").concat(important, "; opacity: 0.7").concat(important, "; }\n\n .message-bubble a {\n color: ").concat(mdLink).concat(important, ";\n text-decoration: underline").concat(important, ";\n text-underline-offset: 2px").concat(important, ";\n word-break: break-word").concat(important, ";\n }\n .message-bubble a:hover { opacity: 0.82").concat(important, "; }\n\n .message-bubble code {\n font-family: ").concat(mdMono).concat(important, ";\n font-size: 0.88em").concat(important, ";\n background: ").concat(mdSubtleBg).concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 5px").concat(important, ";\n padding: 1px 5px").concat(important, ";\n white-space: break-spaces").concat(important, ";\n word-break: break-word").concat(important, ";\n direction: ltr").concat(important, ";\n unicode-bidi: embed").concat(important, ";\n }\n .message-bubble pre {\n margin: 10px 0 11px").concat(important, ";\n padding: 10px 12px").concat(important, ";\n background: ").concat(mdSubtleBg).concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 10px").concat(important, ";\n overflow-x: auto").concat(important, ";\n direction: ltr").concat(important, ";\n text-align: left").concat(important, ";\n }\n .message-bubble pre code {\n background: none").concat(important, ";\n border: none").concat(important, ";\n border-radius: 0").concat(important, ";\n padding: 0").concat(important, ";\n font-size: 0.85em").concat(important, ";\n line-height: 1.5").concat(important, ";\n white-space: pre").concat(important, ";\n word-break: normal").concat(important, ";\n }\n\n .message-bubble blockquote {\n margin: 10px 0").concat(important, ";\n padding-block: 2px").concat(important, ";\n padding-inline-start: 12px").concat(important, ";\n padding-inline-end: 0").concat(important, ";\n border-inline-start: 3px solid ").concat(mdAccent).concat(important, ";\n color: ").concat(mdMuted).concat(important, ";\n }\n .message-bubble blockquote > *:last-child { margin-bottom: 0").concat(important, "; }\n\n .message-bubble hr {\n border: none").concat(important, ";\n border-top: 1px solid ").concat(mdRule).concat(important, ";\n height: 0").concat(important, ";\n margin: 13px 0").concat(important, ";\n }\n\n .message-bubble .md-table-wrap {\n margin: 10px 0 11px").concat(important, ";\n overflow-x: auto").concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 9px").concat(important, ";\n }\n .message-bubble table {\n border-collapse: collapse").concat(important, ";\n width: 100%").concat(important, ";\n font-size: 0.93em").concat(important, ";\n }\n .message-bubble th, .message-bubble td {\n padding: 7px 11px").concat(important, ";\n text-align: start").concat(important, ";\n border-bottom: 1px solid ").concat(mdBorder).concat(important, ";\n vertical-align: top").concat(important, ";\n }\n .message-bubble thead th {\n background: ").concat(mdSubtleBg).concat(important, ";\n font-weight: 700").concat(important, ";\n }\n .message-bubble tbody tr:nth-child(even) { background: ").concat(mdSubtlerBg).concat(important, "; }\n .message-bubble tbody tr:last-child td { border-bottom: none").concat(important, "; }\n\n .message-bubble .md-img {\n display: block").concat(important, ";\n max-width: 100%").concat(important, ";\n height: auto").concat(important, ";\n border-radius: 8px").concat(important, ";\n margin: 8px 0").concat(important, ";\n }\n\n .message.user { \n align-self: stretch").concat(important, ";\n align-items: stretch").concat(important, ";\n }\n .message.agent {\n align-self: stretch").concat(important, ";\n align-items: stretch").concat(important, ";\n }\n .message.user .message-bubble {\n margin-left: auto").concat(important, ";\n margin-right: 0").concat(important, ";\n }\n .message.agent .message-bubble {\n margin-left: 0").concat(important, ";\n margin-right: auto").concat(important, ";\n }\n .message-meta {\n position: absolute").concat(important, ";\n right: ").concat(MESSAGE_META_INSET_PX, "px").concat(important, ";\n bottom: 9px").concat(important, ";\n z-index: 2").concat(important, ";\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 5px").concat(important, ";\n margin: 0").concat(important, ";\n color: rgba(255,255,255,0.55)").concat(important, ";\n font-size: 10px").concat(important, ";\n line-height: 1").concat(important, ";\n direction: ltr").concat(important, ";\n white-space: nowrap").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n .message.user .message-meta {\n color: rgba(255,255,255,0.74)").concat(important, ";\n }\n .message-content { position: relative").concat(important, "; z-index: 1").concat(important, "; display: inline").concat(important, "; overflow-wrap: anywhere").concat(important, "; word-break: break-word").concat(important, "; direction: inherit").concat(important, "; }\n .message-meta-spacer { display: inline-block").concat(important, "; width: 0").concat(important, "; height: 1px").concat(important, "; }\n .message-delivery-checks { color: rgba(255,255,255,0.92)").concat(important, "; font-weight: 700").concat(important, "; letter-spacing: -2px").concat(important, "; }\n .message-expand-button {\n position: relative").concat(important, ";\n z-index: 3").concat(important, ";\n margin: 0 4px").concat(important, ";\n padding: 0").concat(important, ";\n border: 0").concat(important, ";\n background: transparent").concat(important, ";\n color: #c4b5fd").concat(important, ";\n font: inherit").concat(important, ";\n font-size: 0.78em").concat(important, ";\n font-weight: 600").concat(important, ";\n line-height: inherit").concat(important, ";\n text-decoration: underline").concat(important, ";\n text-underline-offset: 2px").concat(important, ";\n cursor: pointer").concat(important, ";\n }\n .message-expand-button:hover { color: #ede9fe").concat(important, "; }\n .message-avatar { \n width: ").concat(avatarSize).concat(important, "; \n height: ").concat(avatarSize).concat(important, "; \n min-width: ").concat(avatarSize).concat(important, "; \n border-radius: 50%").concat(important, "; \n display: flex").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n flex-shrink: 0").concat(important, "; \n color: inherit").concat(important, "; \n font-size: ").concat(useVoiceTheme ? '14' : '20', "px").concat(important, "; \n line-height: 1").concat(important, "; \n background: transparent").concat(important, "; \n border: none").concat(important, "; \n box-sizing: border-box").concat(important, "; \n }\n .message-avatar.user { background: ").concat(avatarUserBg).concat(important, "; color: ").concat(avatarUserColor).concat(important, "; }\n .message-avatar.agent { background: ").concat(avatarAgentBg).concat(important, "; }\n \n .message.system {\n background: ").concat(messages.systemBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n .message.error {\n background: ").concat(messages.errorBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n \n .input-container {\n display: flex").concat(important, ";\n gap: 8px").concat(important, ";\n padding: 12px 16px").concat(important, ";\n background: ").concat(inputContainerBg).concat(important, ";\n border-top: 1px solid ").concat(inputContainerBorderTop).concat(important, ";\n align-items: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n flex-direction: row").concat(important, ";\n direction: ltr").concat(important, ";\n }\n .text-interface-footer {\n position: relative").concat(important, ";\n flex-shrink: 0").concat(important, ";\n min-height: 28px").concat(important, ";\n padding: 4px 12px").concat(important, ";\n background: ").concat(topBarBg).concat(important, ";\n border-top: 1px solid ").concat(inputContainerBorderTop).concat(important, ";\n display: flex").concat(important, ";\n justify-content: center").concat(important, ";\n align-items: center").concat(important, ";\n gap: 8px").concat(important, ";\n direction: ltr").concat(important, ";\n }\n .text-interface-footer .ttp-info-btn {\n position: absolute").concat(important, ";\n right: 12px").concat(important, ";\n margin-left: auto").concat(important, ";\n }\n .text-interface-powered {\n color: rgba(255,255,255,0.46)").concat(important, ";\n font-size: 10px").concat(important, ";\n line-height: 1.2").concat(important, ";\n text-align: center").concat(important, ";\n white-space: nowrap").concat(important, ";\n }\n .text-interface-powered a {\n color: ").concat(sendButtonColor).concat(important, ";\n text-decoration: none").concat(important, ";\n }\n .text-interface.active ~ .ttp-footer {\n display: none").concat(important, ";\n }\n \n .input-wrapper {\n position: relative").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .message-input {\n width: 100%").concat(important, ";\n min-height: ").concat(TEXT_INPUT_MIN_HEIGHT_PX, "px").concat(important, ";\n max-height: ").concat(TEXT_INPUT_MAX_HEIGHT_PX, "px").concat(important, ";\n padding: ").concat(inputPadding, ";\n border: 1px solid ").concat(inputBorderColor, ";\n border-radius: ").concat(inputBorderRadius, "px;\n font-size: ").concat(inputFontSize, ";\n font-family: inherit").concat(important, ";\n line-height: 1.4").concat(important, ";\n resize: none").concat(important, ";\n overflow-y: auto").concat(important, ";\n background: ").concat(inputBackgroundColor, ";\n color: ").concat(inputTextColor, ";\n vertical-align: top").concat(important, ";\n margin: 0").concat(important, ";\n display: block").concat(important, ";\n white-space: pre-wrap").concat(important, ";\n word-wrap: break-word").concat(important, ";\n text-align: start").concat(important, ";\n direction: ").concat(textDirection).concat(important, ";\n unicode-bidi: plaintext").concat(important, ";\n -webkit-appearance: none").concat(important, ";\n appearance: none").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n \n .message-input:focus {\n outline: none").concat(important, ";\n border-color: ").concat(inputFocusColor, ";\n background: ").concat(inputFocusBg).concat(important, ";\n box-shadow: ").concat(inputFocusBoxShadow, ";\n }\n \n .message-input::placeholder {\n color: ").concat(placeholderColor).concat(important, ";\n text-align: start").concat(important, ";\n direction: ").concat(textDirection).concat(important, ";\n }\n \n .send-button {\n width: 38px").concat(important, ";\n height: 38px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n background: linear-gradient(135deg, ").concat(sendButtonColor, ", ").concat(sendButtonHoverColor, ")").concat(important, ";\n color: ").concat(sendButtonTextColor, ";\n font-size: ").concat(this.config.sendButtonFontSize || ((_this$config$panel15 = this.config.panel) === null || _this$config$panel15 === void 0 ? void 0 : _this$config$panel15.sendButtonFontSize) || '16px', ";\n font-weight: ").concat(this.config.sendButtonFontWeight || ((_this$config$panel16 = this.config.panel) === null || _this$config$panel16 === void 0 ? void 0 : _this$config$panel16.sendButtonFontWeight) || '500', ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n transition: all 0.2s ease").concat(important, ";\n box-shadow: 0 4px 12px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.32)").concat(important, ";\n }\n .send-icon {\n width: 18px").concat(important, ";\n height: 18px").concat(important, ";\n fill: none").concat(important, ";\n stroke: currentColor").concat(important, ";\n stroke-width: 2.1").concat(important, ";\n stroke-linecap: round").concat(important, ";\n stroke-linejoin: round").concat(important, ";\n }\n \n .send-button:hover:not(:disabled) {\n background: ").concat(sendButtonHoverColor, ";\n transform: scale(1.05)").concat(important, ";\n box-shadow: 0 6px 16px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.42)").concat(important, ";\n }\n \n .send-button-hint {\n width: 100%").concat(important, ";\n text-align: center").concat(important, ";\n margin-top: 4px").concat(important, ";\n }\n \n .send-button:disabled {\n opacity: 0.5").concat(important, ";\n cursor: not-allowed").concat(important, ";\n }\n \n .typing-indicator {\n display: inline-flex").concat(important, ";\n gap: 4px").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .typing-dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", ").concat(typingDotAlpha, ")").concat(important, ";\n animation: typingDot 1.4s ease-in-out infinite").concat(important, ";\n }\n \n .typing-dot:nth-child(2) { animation-delay: 0.2s").concat(important, "; }\n .typing-dot:nth-child(3) { animation-delay: 0.4s").concat(important, "; }\n \n @keyframes typingDot {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.7; }\n 30% { transform: translateY(-8px); opacity: 1; }\n }\n \n .error-message {\n padding: 12px").concat(important, ";\n background: ").concat(errorBubbleBg, ";\n border-radius: ").concat(messages.borderRadius, "px;\n color: ").concat(errorBubbleColor).concat(important, ";\n border: ").concat(errorBubbleBorder).concat(important, ";\n font-size: ").concat(messageFontSize).concat(important, ";\n margin: 8px 0").concat(important, ";\n }\n \n ").concat(useVoiceTheme ? "\n #textInterface.active .input-container .send-button-hint {\n color: rgba(255,255,255,0.72)".concat(important, ";\n }\n ") : '', "\n \n @media (max-width: 768px) {\n #messagesContainer {\n padding: 12px").concat(important, ";\n gap: 12px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 85%").concat(important, ";\n font-size: ").concat(messageFontSize).concat(important, ";\n padding: 11px ").concat(MESSAGE_META_GUTTER_PX, "px 11px 14px").concat(important, ";\n }\n \n .text-input-container {\n padding: 10px").concat(important, ";\n gap: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 10px 14px").concat(important, ";\n min-height: 44px").concat(important, ";\n }\n \n #text-chat-send {\n min-width: 56px").concat(important, ";\n min-height: 44px").concat(important, ";\n width: 56px").concat(important, ";\n height: 44px").concat(important, ";\n }\n \n .empty-state-icon {\n font-size: 44px").concat(important, ";\n }\n \n .empty-state-title {\n font-size: 20px").concat(important, ";\n }\n \n .empty-state-text {\n font-size: 14px").concat(important, ";\n }\n }\n \n @media (max-width: 480px) {\n #messagesContainer {\n padding: 10px").concat(important, ";\n gap: 10px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 90%").concat(important, ";\n font-size: ").concat(messageFontSize).concat(important, ";\n padding: 10px ").concat(MESSAGE_META_GUTTER_PX, "px 10px 13px").concat(important, ";\n }\n \n .text-input-container {\n padding: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important;\n padding: 8px 12px").concat(important, ";\n }\n }\n ");
37773
38049
  }
37774
38050
 
37775
38051
  /**
@@ -38418,10 +38694,11 @@ var TextInterface = /*#__PURE__*/function () {
38418
38694
  errorContainer.appendChild(message);
38419
38695
  messages.appendChild(errorContainer);
38420
38696
  } else {
38421
- // Show regular error
38697
+ var text = typeof error === 'string' ? error : error && error.message;
38698
+ if (!text) return;
38422
38699
  var errorEl = document.createElement('div');
38423
38700
  errorEl.className = 'error-message';
38424
- errorEl.textContent = typeof error === 'string' ? error : (error === null || error === void 0 ? void 0 : error.message) || error;
38701
+ errorEl.textContent = text;
38425
38702
  messages.appendChild(errorEl);
38426
38703
  }
38427
38704
  messages.scrollTop = messages.scrollHeight;