@exotel-npm-dev/exotel-ai-assist 1.0.0 → 1.0.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.
@@ -398,13 +398,14 @@ class BroadcastChannelTransport {
398
398
  if (this.socket) {
399
399
  this.socket.close(1000, "reconnecting");
400
400
  }
401
- // authToken is passed as a subprotocol so it is sent in the
402
- // Sec-WebSocket-Protocol header — the only way to include auth
403
- // credentials in a browser WebSocket handshake.
404
401
  this.socket = new WebSocket(url);
405
402
  this.socket.onopen = () => {
406
403
  var _a;
407
404
  this.socketConnected = true;
405
+ this.socket.send(JSON.stringify({
406
+ type: "auth",
407
+ access_token: authToken
408
+ }));
408
409
  const msg = {
409
410
  type: "CONNECTED"
410
411
  };
@@ -429,11 +430,12 @@ class BroadcastChannelTransport {
429
430
  (_a = this.handler) === null || _a === void 0 ? void 0 : _a.call(this, msg);
430
431
  this.channel.postMessage(msg);
431
432
  };
432
- this.socket.onclose = () => {
433
+ this.socket.onclose = ev => {
433
434
  var _a;
434
435
  this.socketConnected = false;
435
436
  const msg = {
436
- type: "DISCONNECTED"
437
+ type: "DISCONNECTED",
438
+ code: ev.code
437
439
  };
438
440
  (_a = this.handler) === null || _a === void 0 ? void 0 : _a.call(this, msg);
439
441
  this.channel.postMessage(msg);
@@ -1537,9 +1539,9 @@ class Utils {
1537
1539
  const INTERNAL_KEYS = new Set(["authToken", "accountId", "wssBaseUrl", "reconnectInterval", "maxReconnectAttempts"]);
1538
1540
  // Mandatory server params remapped to the names the API expects.
1539
1541
  // accountId is embedded in the URL path — never sent as a query param.
1540
- const mandatory = [["access_token", String(params.authToken)]];
1542
+ const pairs = [];
1541
1543
  if (params.call_sid !== undefined && params.call_sid !== null) {
1542
- mandatory.push(["call_sid", String(params.call_sid)]);
1544
+ pairs.push(["call_sid", String(params.call_sid)]);
1543
1545
  }
1544
1546
  // Extra caller-supplied params, excluding internals and already-mapped keys.
1545
1547
  const extra = Object.entries(params).filter(([k, v]) => !INTERNAL_KEYS.has(k) && k !== "call_sid" && v !== undefined && v !== null).map(([k, v]) => [k, String(v)]);
@@ -1549,7 +1551,7 @@ class Utils {
1549
1551
  // array as a plain object, turning index 0 into a key and the inner
1550
1552
  // array's toString() (comma-joined) into the value.
1551
1553
  const query = new URLSearchParams();
1552
- [...mandatory, ...extra].forEach(([k, v]) => query.append(k, v));
1554
+ [...pairs, ...extra].forEach(([k, v]) => query.append(k, v));
1553
1555
  return query.toString();
1554
1556
  }
1555
1557
  /**
@@ -1645,7 +1647,11 @@ class ExotelAIAssistController extends EventEmitter {
1645
1647
  case "DISCONNECTED":
1646
1648
  this._setStatus("disconnected");
1647
1649
  this.emit("onCallEnd");
1648
- this._scheduleReconnect();
1650
+ // Close codes 4001 (auth_failed) and 4002 (auth_timeout) are permanent
1651
+ // auth failures — reconnecting with the same token would fail again.
1652
+ if (msg.code !== 4001 && msg.code !== 4002) {
1653
+ this._scheduleReconnect();
1654
+ }
1649
1655
  break;
1650
1656
  case "ERROR":
1651
1657
  this._setStatus("error");
@@ -1657,14 +1663,26 @@ class ExotelAIAssistController extends EventEmitter {
1657
1663
  }
1658
1664
  }
1659
1665
  _handleServerPayload(raw) {
1666
+ var _a;
1660
1667
  let parsed;
1661
1668
  try {
1662
1669
  parsed = JSON.parse(raw);
1663
- } catch (_a) {
1670
+ } catch (_b) {
1664
1671
  this.emit("error", new Error(`Failed to parse server message: ${raw}`));
1665
1672
  return;
1666
1673
  }
1667
1674
  this.emit("raw", parsed);
1675
+ // Auth error messages arrive before the server closes the connection.
1676
+ // Treat both as fatal — the server will follow up with a 4001/4002 close.
1677
+ const msgType = parsed.type;
1678
+ if (msgType === "auth_failed" || msgType === "auth_timeout") {
1679
+ const detail = (_a = parsed.error) !== null && _a !== void 0 ? _a : msgType;
1680
+ const err = new Error(detail);
1681
+ err.code = msgType.toUpperCase();
1682
+ this._setStatus("error");
1683
+ this.emit("error", err);
1684
+ return;
1685
+ }
1668
1686
  if (parsed.config) {
1669
1687
  this.emit("botConfig", parsed.config);
1670
1688
  }
package/dist/index.js CHANGED
@@ -19816,13 +19816,14 @@ class BroadcastChannelTransport {
19816
19816
  if (this.socket) {
19817
19817
  this.socket.close(1000, "reconnecting");
19818
19818
  }
19819
- // authToken is passed as a subprotocol so it is sent in the
19820
- // Sec-WebSocket-Protocol header — the only way to include auth
19821
- // credentials in a browser WebSocket handshake.
19822
19819
  this.socket = new WebSocket(url);
19823
19820
  this.socket.onopen = () => {
19824
19821
  var _a;
19825
19822
  this.socketConnected = true;
19823
+ this.socket.send(JSON.stringify({
19824
+ type: "auth",
19825
+ access_token: authToken
19826
+ }));
19826
19827
  const msg = {
19827
19828
  type: "CONNECTED"
19828
19829
  };
@@ -19847,11 +19848,12 @@ class BroadcastChannelTransport {
19847
19848
  (_a = this.handler) === null || _a === void 0 ? void 0 : _a.call(this, msg);
19848
19849
  this.channel.postMessage(msg);
19849
19850
  };
19850
- this.socket.onclose = () => {
19851
+ this.socket.onclose = ev => {
19851
19852
  var _a;
19852
19853
  this.socketConnected = false;
19853
19854
  const msg = {
19854
- type: "DISCONNECTED"
19855
+ type: "DISCONNECTED",
19856
+ code: ev.code
19855
19857
  };
19856
19858
  (_a = this.handler) === null || _a === void 0 ? void 0 : _a.call(this, msg);
19857
19859
  this.channel.postMessage(msg);
@@ -20955,9 +20957,9 @@ class Utils {
20955
20957
  const INTERNAL_KEYS = new Set(["authToken", "accountId", "wssBaseUrl", "reconnectInterval", "maxReconnectAttempts"]);
20956
20958
  // Mandatory server params remapped to the names the API expects.
20957
20959
  // accountId is embedded in the URL path — never sent as a query param.
20958
- const mandatory = [["access_token", String(params.authToken)]];
20960
+ const pairs = [];
20959
20961
  if (params.call_sid !== undefined && params.call_sid !== null) {
20960
- mandatory.push(["call_sid", String(params.call_sid)]);
20962
+ pairs.push(["call_sid", String(params.call_sid)]);
20961
20963
  }
20962
20964
  // Extra caller-supplied params, excluding internals and already-mapped keys.
20963
20965
  const extra = Object.entries(params).filter(([k, v]) => !INTERNAL_KEYS.has(k) && k !== "call_sid" && v !== undefined && v !== null).map(([k, v]) => [k, String(v)]);
@@ -20967,7 +20969,7 @@ class Utils {
20967
20969
  // array as a plain object, turning index 0 into a key and the inner
20968
20970
  // array's toString() (comma-joined) into the value.
20969
20971
  const query = new URLSearchParams();
20970
- [...mandatory, ...extra].forEach(([k, v]) => query.append(k, v));
20972
+ [...pairs, ...extra].forEach(([k, v]) => query.append(k, v));
20971
20973
  return query.toString();
20972
20974
  }
20973
20975
  /**
@@ -21063,7 +21065,11 @@ class ExotelAIAssistController extends EventEmitter {
21063
21065
  case "DISCONNECTED":
21064
21066
  this._setStatus("disconnected");
21065
21067
  this.emit("onCallEnd");
21066
- this._scheduleReconnect();
21068
+ // Close codes 4001 (auth_failed) and 4002 (auth_timeout) are permanent
21069
+ // auth failures — reconnecting with the same token would fail again.
21070
+ if (msg.code !== 4001 && msg.code !== 4002) {
21071
+ this._scheduleReconnect();
21072
+ }
21067
21073
  break;
21068
21074
  case "ERROR":
21069
21075
  this._setStatus("error");
@@ -21075,14 +21081,26 @@ class ExotelAIAssistController extends EventEmitter {
21075
21081
  }
21076
21082
  }
21077
21083
  _handleServerPayload(raw) {
21084
+ var _a;
21078
21085
  let parsed;
21079
21086
  try {
21080
21087
  parsed = JSON.parse(raw);
21081
- } catch (_a) {
21088
+ } catch (_b) {
21082
21089
  this.emit("error", new Error(`Failed to parse server message: ${raw}`));
21083
21090
  return;
21084
21091
  }
21085
21092
  this.emit("raw", parsed);
21093
+ // Auth error messages arrive before the server closes the connection.
21094
+ // Treat both as fatal — the server will follow up with a 4001/4002 close.
21095
+ const msgType = parsed.type;
21096
+ if (msgType === "auth_failed" || msgType === "auth_timeout") {
21097
+ const detail = (_a = parsed.error) !== null && _a !== void 0 ? _a : msgType;
21098
+ const err = new Error(detail);
21099
+ err.code = msgType.toUpperCase();
21100
+ this._setStatus("error");
21101
+ this.emit("error", err);
21102
+ return;
21103
+ }
21086
21104
  if (parsed.config) {
21087
21105
  this.emit("botConfig", parsed.config);
21088
21106
  }
@@ -21257,7 +21275,7 @@ function styleInject(css, ref) {
21257
21275
  }
21258
21276
  }
21259
21277
 
21260
- var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover{background:#f9fafb;color:#374151}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-muted{color:#6b7280}";
21278
+ var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover{background:#f9fafb;color:#374151}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-muted{color:#6b7280}";
21261
21279
  styleInject(css_248z);
21262
21280
 
21263
21281
  function Sparkle() {
@@ -5102,13 +5102,14 @@ class BroadcastChannelTransport {
5102
5102
  if (this.socket) {
5103
5103
  this.socket.close(1000, "reconnecting");
5104
5104
  }
5105
- // authToken is passed as a subprotocol so it is sent in the
5106
- // Sec-WebSocket-Protocol header — the only way to include auth
5107
- // credentials in a browser WebSocket handshake.
5108
5105
  this.socket = new WebSocket(url);
5109
5106
  this.socket.onopen = () => {
5110
5107
  var _a;
5111
5108
  this.socketConnected = true;
5109
+ this.socket.send(JSON.stringify({
5110
+ type: "auth",
5111
+ access_token: authToken
5112
+ }));
5112
5113
  const msg = {
5113
5114
  type: "CONNECTED"
5114
5115
  };
@@ -5133,11 +5134,12 @@ class BroadcastChannelTransport {
5133
5134
  (_a = this.handler) === null || _a === void 0 ? void 0 : _a.call(this, msg);
5134
5135
  this.channel.postMessage(msg);
5135
5136
  };
5136
- this.socket.onclose = () => {
5137
+ this.socket.onclose = ev => {
5137
5138
  var _a;
5138
5139
  this.socketConnected = false;
5139
5140
  const msg = {
5140
- type: "DISCONNECTED"
5141
+ type: "DISCONNECTED",
5142
+ code: ev.code
5141
5143
  };
5142
5144
  (_a = this.handler) === null || _a === void 0 ? void 0 : _a.call(this, msg);
5143
5145
  this.channel.postMessage(msg);
@@ -6241,9 +6243,9 @@ class Utils {
6241
6243
  const INTERNAL_KEYS = new Set(["authToken", "accountId", "wssBaseUrl", "reconnectInterval", "maxReconnectAttempts"]);
6242
6244
  // Mandatory server params remapped to the names the API expects.
6243
6245
  // accountId is embedded in the URL path — never sent as a query param.
6244
- const mandatory = [["access_token", String(params.authToken)]];
6246
+ const pairs = [];
6245
6247
  if (params.call_sid !== undefined && params.call_sid !== null) {
6246
- mandatory.push(["call_sid", String(params.call_sid)]);
6248
+ pairs.push(["call_sid", String(params.call_sid)]);
6247
6249
  }
6248
6250
  // Extra caller-supplied params, excluding internals and already-mapped keys.
6249
6251
  const extra = Object.entries(params).filter(([k, v]) => !INTERNAL_KEYS.has(k) && k !== "call_sid" && v !== undefined && v !== null).map(([k, v]) => [k, String(v)]);
@@ -6253,7 +6255,7 @@ class Utils {
6253
6255
  // array as a plain object, turning index 0 into a key and the inner
6254
6256
  // array's toString() (comma-joined) into the value.
6255
6257
  const query = new URLSearchParams();
6256
- [...mandatory, ...extra].forEach(([k, v]) => query.append(k, v));
6258
+ [...pairs, ...extra].forEach(([k, v]) => query.append(k, v));
6257
6259
  return query.toString();
6258
6260
  }
6259
6261
  /**
@@ -6349,7 +6351,11 @@ class ExotelAIAssistController extends EventEmitter {
6349
6351
  case "DISCONNECTED":
6350
6352
  this._setStatus("disconnected");
6351
6353
  this.emit("onCallEnd");
6352
- this._scheduleReconnect();
6354
+ // Close codes 4001 (auth_failed) and 4002 (auth_timeout) are permanent
6355
+ // auth failures — reconnecting with the same token would fail again.
6356
+ if (msg.code !== 4001 && msg.code !== 4002) {
6357
+ this._scheduleReconnect();
6358
+ }
6353
6359
  break;
6354
6360
  case "ERROR":
6355
6361
  this._setStatus("error");
@@ -6361,14 +6367,26 @@ class ExotelAIAssistController extends EventEmitter {
6361
6367
  }
6362
6368
  }
6363
6369
  _handleServerPayload(raw) {
6370
+ var _a;
6364
6371
  let parsed;
6365
6372
  try {
6366
6373
  parsed = JSON.parse(raw);
6367
- } catch (_a) {
6374
+ } catch (_b) {
6368
6375
  this.emit("error", new Error(`Failed to parse server message: ${raw}`));
6369
6376
  return;
6370
6377
  }
6371
6378
  this.emit("raw", parsed);
6379
+ // Auth error messages arrive before the server closes the connection.
6380
+ // Treat both as fatal — the server will follow up with a 4001/4002 close.
6381
+ const msgType = parsed.type;
6382
+ if (msgType === "auth_failed" || msgType === "auth_timeout") {
6383
+ const detail = (_a = parsed.error) !== null && _a !== void 0 ? _a : msgType;
6384
+ const err = new Error(detail);
6385
+ err.code = msgType.toUpperCase();
6386
+ this._setStatus("error");
6387
+ this.emit("error", err);
6388
+ return;
6389
+ }
6372
6390
  if (parsed.config) {
6373
6391
  this.emit("botConfig", parsed.config);
6374
6392
  }
@@ -6543,7 +6561,7 @@ function styleInject(css, ref) {
6543
6561
  }
6544
6562
  }
6545
6563
 
6546
- var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover{background:#f9fafb;color:#374151}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-muted{color:#6b7280}";
6564
+ var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover{background:#f9fafb;color:#374151}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-muted{color:#6b7280}";
6547
6565
  styleInject(css_248z);
6548
6566
 
6549
6567
  function Sparkle() {
@@ -82,6 +82,7 @@ export type WorkerInboundMessage = {
82
82
  type: "CONNECTED";
83
83
  } | {
84
84
  type: "DISCONNECTED";
85
+ code?: number;
85
86
  } | {
86
87
  type: "ERROR";
87
88
  message: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exotel-npm-dev/exotel-ai-assist",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "AI-powered real-time suggestions, transcript, and sentiment for agent calls",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",