@exotel-npm-dev/exotel-ai-assist 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ export declare class ExotelAIAssistController extends EventEmitter<ControllerEve
6
6
  private status;
7
7
  private destroyed;
8
8
  private connectionEstablished;
9
+ private _acknowledged;
9
10
  private reconnectAttempt;
10
11
  private reconnectTimer;
11
12
  private static readonly MAX_RECONNECT_ATTEMPTS;
@@ -1595,6 +1595,7 @@ class ExotelAIAssistController extends EventEmitter {
1595
1595
  // connectionEstablished === false → connection never opened → retry
1596
1596
  // connectionEstablished === true → server ended a live connection → destroy
1597
1597
  this.connectionEstablished = false;
1598
+ this._acknowledged = false;
1598
1599
  this.reconnectAttempt = 0;
1599
1600
  this.reconnectTimer = null;
1600
1601
  this.params = Object.assign({}, params);
@@ -1603,6 +1604,7 @@ class ExotelAIAssistController extends EventEmitter {
1603
1604
  if (this.destroyed) return;
1604
1605
  this._clearReconnectTimer();
1605
1606
  this.connectionEstablished = false;
1607
+ this._acknowledged = false;
1606
1608
  this.reconnectAttempt = 0;
1607
1609
  this._setStatus("connecting");
1608
1610
  this._ensureTransport();
@@ -1622,6 +1624,7 @@ class ExotelAIAssistController extends EventEmitter {
1622
1624
  if (hashChanged) {
1623
1625
  this._clearReconnectTimer();
1624
1626
  this.connectionEstablished = false;
1627
+ this._acknowledged = false;
1625
1628
  this.reconnectAttempt = 0;
1626
1629
  // Tear down the current transport and start fresh with the new params.
1627
1630
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
@@ -1702,6 +1705,7 @@ class ExotelAIAssistController extends EventEmitter {
1702
1705
  var _a;
1703
1706
  if (this.destroyed) return;
1704
1707
  this.connectionEstablished = false;
1708
+ this._acknowledged = false;
1705
1709
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
1706
1710
  }, delay);
1707
1711
  }
@@ -1732,6 +1736,10 @@ class ExotelAIAssistController extends EventEmitter {
1732
1736
  this.emit("error", err);
1733
1737
  return;
1734
1738
  }
1739
+ if (!this._acknowledged && msgType === "ack") {
1740
+ this._acknowledged = true;
1741
+ this.emit("acknowledged");
1742
+ }
1735
1743
  if (parsed.config) {
1736
1744
  this.emit("botConfig", parsed.config);
1737
1745
  }
@@ -1743,12 +1751,12 @@ class ExotelAIAssistController extends EventEmitter {
1743
1751
  if (!event) continue;
1744
1752
  if (event.transcript && event.transcript.length > 0) {
1745
1753
  const lines = event.transcript.map(msg => {
1746
- var _a, _b;
1754
+ var _a, _b, _c, _d, _e;
1747
1755
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
1748
1756
  const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
1749
1757
  return {
1750
1758
  id: String(msg.sequence),
1751
- text: msg.transcript_segments.map(s => s.text).join(" "),
1759
+ text: (_e = (_d = (_c = msg.transcript_segments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : "",
1752
1760
  startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
1753
1761
  endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
1754
1762
  isFinal: msg.transcript_segments.every(s => s.is_final)
@@ -1,6 +1,8 @@
1
1
  import { ExotelAIAssistParams, Suggestion, TranscriptLine, Sentiment, BotConfig, ConnectionStatus } from "../types";
2
2
  export interface UseExotelAIAssistReturn {
3
3
  status: ConnectionStatus;
4
+ /** True once the server has acknowledged the connection (first valid payload after auth). */
5
+ isAcknowledged: boolean;
4
6
  /** AI suggestions, oldest first, capped at 50. */
5
7
  suggestions: Suggestion[];
6
8
  /** Live transcript lines, ordered by start time. */
package/dist/index.js CHANGED
@@ -21013,6 +21013,7 @@ class ExotelAIAssistController extends EventEmitter {
21013
21013
  // connectionEstablished === false → connection never opened → retry
21014
21014
  // connectionEstablished === true → server ended a live connection → destroy
21015
21015
  this.connectionEstablished = false;
21016
+ this._acknowledged = false;
21016
21017
  this.reconnectAttempt = 0;
21017
21018
  this.reconnectTimer = null;
21018
21019
  this.params = Object.assign({}, params);
@@ -21021,6 +21022,7 @@ class ExotelAIAssistController extends EventEmitter {
21021
21022
  if (this.destroyed) return;
21022
21023
  this._clearReconnectTimer();
21023
21024
  this.connectionEstablished = false;
21025
+ this._acknowledged = false;
21024
21026
  this.reconnectAttempt = 0;
21025
21027
  this._setStatus("connecting");
21026
21028
  this._ensureTransport();
@@ -21040,6 +21042,7 @@ class ExotelAIAssistController extends EventEmitter {
21040
21042
  if (hashChanged) {
21041
21043
  this._clearReconnectTimer();
21042
21044
  this.connectionEstablished = false;
21045
+ this._acknowledged = false;
21043
21046
  this.reconnectAttempt = 0;
21044
21047
  // Tear down the current transport and start fresh with the new params.
21045
21048
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
@@ -21120,6 +21123,7 @@ class ExotelAIAssistController extends EventEmitter {
21120
21123
  var _a;
21121
21124
  if (this.destroyed) return;
21122
21125
  this.connectionEstablished = false;
21126
+ this._acknowledged = false;
21123
21127
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
21124
21128
  }, delay);
21125
21129
  }
@@ -21150,6 +21154,10 @@ class ExotelAIAssistController extends EventEmitter {
21150
21154
  this.emit("error", err);
21151
21155
  return;
21152
21156
  }
21157
+ if (!this._acknowledged && msgType === "ack") {
21158
+ this._acknowledged = true;
21159
+ this.emit("acknowledged");
21160
+ }
21153
21161
  if (parsed.config) {
21154
21162
  this.emit("botConfig", parsed.config);
21155
21163
  }
@@ -21161,12 +21169,12 @@ class ExotelAIAssistController extends EventEmitter {
21161
21169
  if (!event) continue;
21162
21170
  if (event.transcript && event.transcript.length > 0) {
21163
21171
  const lines = event.transcript.map(msg => {
21164
- var _a, _b;
21172
+ var _a, _b, _c, _d, _e;
21165
21173
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
21166
21174
  const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
21167
21175
  return {
21168
21176
  id: String(msg.sequence),
21169
- text: msg.transcript_segments.map(s => s.text).join(" "),
21177
+ text: (_e = (_d = (_c = msg.transcript_segments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : "",
21170
21178
  startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
21171
21179
  endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
21172
21180
  isFinal: msg.transcript_segments.every(s => s.is_final)
@@ -21203,15 +21211,19 @@ ExotelAIAssistController.RECONNECT_BASE_MS = 3000;
21203
21211
  function useExotelAIAssist(params) {
21204
21212
  const controllerRef = reactExports.useRef(null);
21205
21213
  const [status, setStatus] = reactExports.useState("idle");
21214
+ const [isAcknowledged, setIsAcknowledged] = reactExports.useState(false);
21206
21215
  const [suggestions, setSuggestions] = reactExports.useState([]);
21207
21216
  const [transcripts, setTranscripts] = reactExports.useState([]);
21208
21217
  const [sentiment, setSentiment] = reactExports.useState(null);
21209
21218
  const [botConfig, setBotConfig] = reactExports.useState(null);
21210
21219
  const [lastError, setLastError] = reactExports.useState(null);
21211
- const paramHash = reactExports.useMemo(() => Utils.hash(params), [params]);
21220
+ const paramHash = Utils.hash(params);
21221
+ console.log("params", params);
21222
+ console.log("paramHash", paramHash);
21212
21223
  reactExports.useEffect(() => {
21213
21224
  // Clear all stale state from the previous session before connecting.
21214
21225
  setStatus("idle");
21226
+ setIsAcknowledged(false);
21215
21227
  setSuggestions([]);
21216
21228
  setTranscripts([]);
21217
21229
  setSentiment(null);
@@ -21220,6 +21232,7 @@ function useExotelAIAssist(params) {
21220
21232
  const ctrl = new ExotelAIAssistController(params);
21221
21233
  controllerRef.current = ctrl;
21222
21234
  ctrl.on("statusChange", setStatus);
21235
+ ctrl.on("acknowledged", () => setIsAcknowledged(true));
21223
21236
  ctrl.on("botConfig", setBotConfig);
21224
21237
  ctrl.on("suggestion", s => setSuggestions(prev => [...prev, s].slice(-50)));
21225
21238
  ctrl.on("sentiment", setSentiment);
@@ -21250,6 +21263,7 @@ function useExotelAIAssist(params) {
21250
21263
  }, []);
21251
21264
  return {
21252
21265
  status,
21266
+ isAcknowledged,
21253
21267
  suggestions,
21254
21268
  transcripts,
21255
21269
  sentiment,
@@ -6299,6 +6299,7 @@ class ExotelAIAssistController extends EventEmitter {
6299
6299
  // connectionEstablished === false → connection never opened → retry
6300
6300
  // connectionEstablished === true → server ended a live connection → destroy
6301
6301
  this.connectionEstablished = false;
6302
+ this._acknowledged = false;
6302
6303
  this.reconnectAttempt = 0;
6303
6304
  this.reconnectTimer = null;
6304
6305
  this.params = Object.assign({}, params);
@@ -6307,6 +6308,7 @@ class ExotelAIAssistController extends EventEmitter {
6307
6308
  if (this.destroyed) return;
6308
6309
  this._clearReconnectTimer();
6309
6310
  this.connectionEstablished = false;
6311
+ this._acknowledged = false;
6310
6312
  this.reconnectAttempt = 0;
6311
6313
  this._setStatus("connecting");
6312
6314
  this._ensureTransport();
@@ -6326,6 +6328,7 @@ class ExotelAIAssistController extends EventEmitter {
6326
6328
  if (hashChanged) {
6327
6329
  this._clearReconnectTimer();
6328
6330
  this.connectionEstablished = false;
6331
+ this._acknowledged = false;
6329
6332
  this.reconnectAttempt = 0;
6330
6333
  // Tear down the current transport and start fresh with the new params.
6331
6334
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
@@ -6406,6 +6409,7 @@ class ExotelAIAssistController extends EventEmitter {
6406
6409
  var _a;
6407
6410
  if (this.destroyed) return;
6408
6411
  this.connectionEstablished = false;
6412
+ this._acknowledged = false;
6409
6413
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
6410
6414
  }, delay);
6411
6415
  }
@@ -6436,6 +6440,10 @@ class ExotelAIAssistController extends EventEmitter {
6436
6440
  this.emit("error", err);
6437
6441
  return;
6438
6442
  }
6443
+ if (!this._acknowledged && msgType === "ack") {
6444
+ this._acknowledged = true;
6445
+ this.emit("acknowledged");
6446
+ }
6439
6447
  if (parsed.config) {
6440
6448
  this.emit("botConfig", parsed.config);
6441
6449
  }
@@ -6447,12 +6455,12 @@ class ExotelAIAssistController extends EventEmitter {
6447
6455
  if (!event) continue;
6448
6456
  if (event.transcript && event.transcript.length > 0) {
6449
6457
  const lines = event.transcript.map(msg => {
6450
- var _a, _b;
6458
+ var _a, _b, _c, _d, _e;
6451
6459
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
6452
6460
  const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
6453
6461
  return {
6454
6462
  id: String(msg.sequence),
6455
- text: msg.transcript_segments.map(s => s.text).join(" "),
6463
+ text: (_e = (_d = (_c = msg.transcript_segments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : "",
6456
6464
  startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
6457
6465
  endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
6458
6466
  isFinal: msg.transcript_segments.every(s => s.is_final)
@@ -6489,15 +6497,19 @@ ExotelAIAssistController.RECONNECT_BASE_MS = 3000;
6489
6497
  function useExotelAIAssist(params) {
6490
6498
  const controllerRef = React.useRef(null);
6491
6499
  const [status, setStatus] = React.useState("idle");
6500
+ const [isAcknowledged, setIsAcknowledged] = React.useState(false);
6492
6501
  const [suggestions, setSuggestions] = React.useState([]);
6493
6502
  const [transcripts, setTranscripts] = React.useState([]);
6494
6503
  const [sentiment, setSentiment] = React.useState(null);
6495
6504
  const [botConfig, setBotConfig] = React.useState(null);
6496
6505
  const [lastError, setLastError] = React.useState(null);
6497
- const paramHash = React.useMemo(() => Utils.hash(params), [params]);
6506
+ const paramHash = Utils.hash(params);
6507
+ console.log("params", params);
6508
+ console.log("paramHash", paramHash);
6498
6509
  React.useEffect(() => {
6499
6510
  // Clear all stale state from the previous session before connecting.
6500
6511
  setStatus("idle");
6512
+ setIsAcknowledged(false);
6501
6513
  setSuggestions([]);
6502
6514
  setTranscripts([]);
6503
6515
  setSentiment(null);
@@ -6506,6 +6518,7 @@ function useExotelAIAssist(params) {
6506
6518
  const ctrl = new ExotelAIAssistController(params);
6507
6519
  controllerRef.current = ctrl;
6508
6520
  ctrl.on("statusChange", setStatus);
6521
+ ctrl.on("acknowledged", () => setIsAcknowledged(true));
6509
6522
  ctrl.on("botConfig", setBotConfig);
6510
6523
  ctrl.on("suggestion", s => setSuggestions(prev => [...prev, s].slice(-50)));
6511
6524
  ctrl.on("sentiment", setSentiment);
@@ -6536,6 +6549,7 @@ function useExotelAIAssist(params) {
6536
6549
  }, []);
6537
6550
  return {
6538
6551
  status,
6552
+ isAcknowledged,
6539
6553
  suggestions,
6540
6554
  transcripts,
6541
6555
  sentiment,
@@ -72,6 +72,8 @@ export interface ControllerEvents {
72
72
  onCallStart: () => void;
73
73
  onCallEnd: () => void;
74
74
  statusChange: (status: ConnectionStatus) => void;
75
+ /** Fired once when the server sends its first valid payload after auth. */
76
+ acknowledged: () => void;
75
77
  error: (err: Error) => void;
76
78
  raw: (data: unknown) => void;
77
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exotel-npm-dev/exotel-ai-assist",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
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",