@exotel-npm-dev/exotel-ai-assist 1.2.5 → 1.2.6

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.
package/README.md CHANGED
@@ -123,11 +123,12 @@ function MyCustomUI({ call_sid }: { call_sid: string }) {
123
123
 
124
124
  if (streamState === "throttled") return <p>AI Assist is at capacity — available on your next call.</p>;
125
125
  if (streamState === "disconnected") return <p>AI Assist disconnected.</p>;
126
+ if (streamState === "connection_timeout") return <p>Connection timed out.</p>;
126
127
 
127
128
  return (
128
129
  <div>
129
130
  {suggestions.map((s) => (
130
- <p key={s.id}>{s.text}</p>
131
+ <p key={s.id}>{s.value}</p>
131
132
  ))}
132
133
  </div>
133
134
  );
@@ -153,7 +154,7 @@ function SuggestionsPanel() {
153
154
  return (
154
155
  <ul>
155
156
  {suggestions.map((s) => (
156
- <li key={s.id}>{s.text}</li>
157
+ <li key={s.id}>{s.value}</li>
157
158
  ))}
158
159
  </ul>
159
160
  );
@@ -188,7 +189,7 @@ ctrl.on("suggestion", (s) => console.log("Suggestion:", s));
188
189
  ctrl.on("transcript", (t) => console.log("Transcript:", t));
189
190
  ctrl.on("streamState", (state) => {
190
191
  console.log("Stream state:", state);
191
- // state: "connected" | "throttled" | "disconnected"
192
+ // state: "connected" | "throttled" | "pending" | "disconnected" | "connection_timeout"
192
193
  });
193
194
  ctrl.on("error", (err) => console.error("Error:", err));
194
195
 
@@ -238,14 +239,14 @@ Extends `EventEmitter`.
238
239
 
239
240
  #### Methods
240
241
 
241
- | Method | Description |
242
- | ------------------ | ---------------------------------------------- |
243
- | `connect()` | Open the WebSocket |
244
- | `disconnect()` | Close cleanly |
245
- | `setParams(patch)` | Merge params; reconnects if `call_sid` changes |
246
- | `destroy()` | Dispose controller and remove all listeners |
247
- | `getStatus()` | Returns current `ConnectionStatus` |
248
- | `getStreamState()` | Returns current `StreamState \| null` |
242
+ | Method | Description |
243
+ | --------------------------------------- | ---------------------------------------------- |
244
+ | `connect()` | Open the WebSocket |
245
+ | `disconnect()` | Close cleanly |
246
+ | `setParams(patch)` | Merge params; reconnects if `call_sid` changes |
247
+ | `destroy()` | Dispose controller and remove all listeners |
248
+ | `getStatus()` | Returns current `ConnectionStatus` |
249
+ | `getStreamState()` | Returns current `StreamState \| null` |
249
250
 
250
251
  #### Events
251
252
 
@@ -255,7 +256,7 @@ Extends `EventEmitter`.
255
256
  | `suggestion` | `Suggestion` | New AI suggestion (capped at last 50) |
256
257
  | `transcript` | `TranscriptLine[]` | Live transcript update |
257
258
  | `sentiment` | `Sentiment` | Sentiment label update |
258
- | `streamState` | `StreamState` | Server stream state change (`connected`, `throttled`, `disconnected`) |
259
+ | `streamState` | `StreamState` | Server stream state change (`connected`, `throttled`, `pending`, `disconnected`, `connection_timeout`) |
259
260
  | `onCallStart` | — | Connection opened |
260
261
  | `onCallEnd` | — | Connection closed |
261
262
  | `statusChange` | `ConnectionStatus` | WebSocket-level status transition (internal) |
@@ -266,36 +267,36 @@ Extends `EventEmitter`.
266
267
 
267
268
  ### `useExotelAIAssist(params)` — Hook return values
268
269
 
269
- | Field | Type | Description |
270
- | -------------- | ----------------------- | ---------------------------------------------------------------------------- |
271
- | `isReady` | `boolean` | `true` after connected + server ack. Multi-tab safe. `false` on disconnect |
272
- | `streamState` | `StreamState \| null` | Server stream state: `"connected"`, `"throttled"`, or `"disconnected"`. `null` until the first `stream_status` message |
273
- | `suggestions` | `Suggestion[]` | AI suggestions, oldest first, capped at 50 |
274
- | `transcripts` | `TranscriptLine[]` | Live transcript lines, ordered by start time |
275
- | `sentiment` | `Sentiment \| null` | Latest sentiment reading |
276
- | `lastError` | `Error \| null` | Most recent error |
277
- | `connect()` | `() => void` | Manually open the connection |
278
- | `disconnect()` | `() => void` | Manually close the connection |
279
- | `setParams()` | `(patch) => void` | Merge params; reconnects if connection params change |
270
+ | Field | Type | Description |
271
+ | -------------------------- | ----------------------- | ---------------------------------------------------------------------------- |
272
+ | `isReady` | `boolean` | `true` after connected + server ack. Multi-tab safe. `false` on disconnect |
273
+ | `streamState` | `StreamState \| null` | Server stream state: `"connected"`, `"throttled"`, `"pending"`, `"disconnected"`, or `"connection_timeout"`. `null` until the first `stream_status` message |
274
+ | `suggestions` | `Suggestion[]` | AI suggestions, oldest first, capped at 50 |
275
+ | `transcripts` | `TranscriptLine[]` | Live transcript lines, ordered by start time |
276
+ | `sentiment` | `Sentiment \| null` | Latest sentiment reading |
277
+ | `lastError` | `Error \| null` | Most recent error |
278
+ | `connect()` | `() => void` | Manually open the connection |
279
+ | `disconnect()` | `() => void` | Manually close the connection |
280
+ | `setParams()` | `(patch) => void` | Merge params; reconnects if connection params change |
280
281
 
281
282
  ---
282
283
 
283
284
  ### TypeScript Types
284
285
 
285
286
  ```ts
286
- type StreamState = "connected" | "throttled" | "disconnected";
287
+ type StreamState = "connected" | "throttled" | "pending" | "disconnected" | "connection_timeout";
287
288
 
288
289
  type ConnectionStatus = "idle" | "connecting" | "connected" | "disconnected" | "error";
289
290
 
290
291
  interface Suggestion {
291
292
  id: string;
292
- text: string;
293
+ value: string;
293
294
  timestamp: number;
294
295
  }
295
296
 
296
297
  interface TranscriptLine {
297
298
  id: string;
298
- text: string;
299
+ value: string;
299
300
  startTime: number;
300
301
  endTime: number;
301
302
  isFinal: boolean;
@@ -330,15 +331,17 @@ wss://<wssBaseUrl>?[customParam1=value1&customParam2=value2&...]
330
331
  "type": "stream_status",
331
332
  "config": { /* bot config, populated on connected, null otherwise */ },
332
333
  "events": [ /* accumulated events since last message, usually [] */ ],
333
- "stream_state": "connected" | "throttled" | "disconnected"
334
+ "stream_state": "connected" | "throttled" | "pending" | "disconnected" | "connection_timeout"
334
335
  }
335
336
  ```
336
337
 
337
- | `stream_state` | Meaning |
338
- | --------------- | --------------------------------------------------------- |
339
- | `connected` | Stream is active — suggestions, transcript, and sentiment flow normally |
340
- | `throttled` | Capacity full — AI Assist cannot join this call; available on the next call |
341
- | `disconnected` | Stream ended or dropped |
338
+ | `stream_state` | Meaning |
339
+ | -------------------- | --------------------------------------------------------- |
340
+ | `connected` | Stream is active — suggestions, transcript, and sentiment flow normally |
341
+ | `throttled` | Capacity full — AI Assist cannot join this call; available on the next call |
342
+ | `pending` | Stream is pending — waiting for a definitive state |
343
+ | `disconnected` | Stream ended or dropped |
344
+ | `connection_timeout` | Connection timeout occurred |
342
345
 
343
346
  ### Reconnection
344
347
 
@@ -1774,14 +1774,14 @@ class ExotelAIAssistController extends EventEmitter {
1774
1774
  for (const event of events) {
1775
1775
  if (!event) continue;
1776
1776
  if (event.event_type === "transcript" && event.value) {
1777
- const transcripts = Array.isArray(event === null || event === void 0 ? void 0 : event.value) ? event.value : (_c = event === null || event === void 0 ? void 0 : event.transcript) !== null && _c !== void 0 ? _c : [];
1777
+ const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
1778
1778
  const lines = transcripts.map(msg => {
1779
- var _a, _b, _c, _d, _e;
1779
+ var _a, _b, _c;
1780
1780
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
1781
1781
  const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
1782
1782
  return {
1783
1783
  id: String(msg.sequence),
1784
- value: (_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 : "",
1784
+ value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
1785
1785
  startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
1786
1786
  endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
1787
1787
  isFinal: msg.transcript_segments.every(s => s.is_final)
@@ -1792,7 +1792,7 @@ class ExotelAIAssistController extends EventEmitter {
1792
1792
  if (event.event_type === "suggestion" && event.value) {
1793
1793
  const suggestion = {
1794
1794
  id: Utils.getUniqueId(),
1795
- value: event.value,
1795
+ value: event.value.text,
1796
1796
  timestamp: now
1797
1797
  };
1798
1798
  this.emit("suggestion", suggestion);
package/dist/index.js CHANGED
@@ -21192,14 +21192,14 @@ class ExotelAIAssistController extends EventEmitter {
21192
21192
  for (const event of events) {
21193
21193
  if (!event) continue;
21194
21194
  if (event.event_type === "transcript" && event.value) {
21195
- const transcripts = Array.isArray(event === null || event === void 0 ? void 0 : event.value) ? event.value : (_c = event === null || event === void 0 ? void 0 : event.transcript) !== null && _c !== void 0 ? _c : [];
21195
+ const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
21196
21196
  const lines = transcripts.map(msg => {
21197
- var _a, _b, _c, _d, _e;
21197
+ var _a, _b, _c;
21198
21198
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
21199
21199
  const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
21200
21200
  return {
21201
21201
  id: String(msg.sequence),
21202
- value: (_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 : "",
21202
+ value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
21203
21203
  startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
21204
21204
  endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
21205
21205
  isFinal: msg.transcript_segments.every(s => s.is_final)
@@ -21210,7 +21210,7 @@ class ExotelAIAssistController extends EventEmitter {
21210
21210
  if (event.event_type === "suggestion" && event.value) {
21211
21211
  const suggestion = {
21212
21212
  id: Utils.getUniqueId(),
21213
- value: event.value,
21213
+ value: event.value.text,
21214
21214
  timestamp: now
21215
21215
  };
21216
21216
  this.emit("suggestion", suggestion);
@@ -21902,7 +21902,7 @@ function ExotelAIAssist(_a) {
21902
21902
  if (streamState === "connection_timeout") {
21903
21903
  return jsxRuntimeExports.jsx(EmptyState, {
21904
21904
  title: "AI Assist is currently inactive",
21905
- subtitle: "we were unable to establish a connection."
21905
+ subtitle: "We were unable to establish a connection."
21906
21906
  });
21907
21907
  }
21908
21908
  return null;
@@ -6478,14 +6478,14 @@ class ExotelAIAssistController extends EventEmitter {
6478
6478
  for (const event of events) {
6479
6479
  if (!event) continue;
6480
6480
  if (event.event_type === "transcript" && event.value) {
6481
- const transcripts = Array.isArray(event === null || event === void 0 ? void 0 : event.value) ? event.value : (_c = event === null || event === void 0 ? void 0 : event.transcript) !== null && _c !== void 0 ? _c : [];
6481
+ const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
6482
6482
  const lines = transcripts.map(msg => {
6483
- var _a, _b, _c, _d, _e;
6483
+ var _a, _b, _c;
6484
6484
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
6485
6485
  const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
6486
6486
  return {
6487
6487
  id: String(msg.sequence),
6488
- value: (_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 : "",
6488
+ value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
6489
6489
  startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
6490
6490
  endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
6491
6491
  isFinal: msg.transcript_segments.every(s => s.is_final)
@@ -6496,7 +6496,7 @@ class ExotelAIAssistController extends EventEmitter {
6496
6496
  if (event.event_type === "suggestion" && event.value) {
6497
6497
  const suggestion = {
6498
6498
  id: Utils.getUniqueId(),
6499
- value: event.value,
6499
+ value: event.value.text,
6500
6500
  timestamp: now
6501
6501
  };
6502
6502
  this.emit("suggestion", suggestion);
@@ -7188,7 +7188,7 @@ function ExotelAIAssist(_a) {
7188
7188
  if (streamState === "connection_timeout") {
7189
7189
  return jsxRuntime.jsx(EmptyState, {
7190
7190
  title: "AI Assist is currently inactive",
7191
- subtitle: "we were unable to establish a connection."
7191
+ subtitle: "We were unable to establish a connection."
7192
7192
  });
7193
7193
  }
7194
7194
  return null;
@@ -50,17 +50,34 @@ interface TranscriptSegment {
50
50
  is_final: boolean;
51
51
  end_timestamp: string;
52
52
  start_timestamp: string;
53
- value: string;
53
+ speaker: string;
54
+ text: string;
54
55
  }
55
56
  interface TranscriptMessage {
56
57
  sequence: number;
57
58
  transcript_segments: TranscriptSegment[];
58
59
  }
59
- export interface WssEvent {
60
- event_type: "suggestion" | "sentiment" | "transcript";
60
+ export interface SuggestionValue {
61
+ sequence: number;
62
+ text: string;
63
+ }
64
+ interface WssEventBase {
61
65
  transcript: TranscriptMessage[];
66
+ text: string;
67
+ }
68
+ interface WssTranscriptEvent extends WssEventBase {
69
+ event_type: "transcript";
70
+ value: TranscriptMessage[];
71
+ }
72
+ interface WssSentimentEvent extends WssEventBase {
73
+ event_type: "sentiment";
62
74
  value: string;
63
75
  }
76
+ interface WssSuggestionEvent extends WssEventBase {
77
+ event_type: "suggestion";
78
+ value: SuggestionValue;
79
+ }
80
+ export type WssEvent = WssTranscriptEvent | WssSentimentEvent | WssSuggestionEvent;
64
81
  export interface InitialHandshakeResponse {
65
82
  type: string;
66
83
  config: BotConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exotel-npm-dev/exotel-ai-assist",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
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",
@@ -42,7 +42,7 @@ export function ExotelAIAssist({ className, onReady, ...params }: ExotelAIAssist
42
42
  );
43
43
  }
44
44
  if (streamState === "connection_timeout") {
45
- return <EmptyState title="AI Assist is currently inactive" subtitle="we were unable to establish a connection." />;
45
+ return <EmptyState title="AI Assist is currently inactive" subtitle="We were unable to establish a connection." />;
46
46
  }
47
47
  return null;
48
48
  }
@@ -225,16 +225,16 @@ export class ExotelAIAssistController extends EventEmitter<ControllerEvents> {
225
225
  if (!event) continue;
226
226
 
227
227
  if (event.event_type === "transcript" && event.value) {
228
- const transcripts = Array.isArray(event?.value) ? event.value : (event?.transcript ?? []);
228
+ const transcripts = Array.isArray(event.value) ? event.value : (event.transcript ?? []);
229
229
  const lines: TranscriptLine[] = transcripts.map((msg) => {
230
230
  const first = msg.transcript_segments?.[0];
231
231
  const last = msg.transcript_segments?.[msg.transcript_segments.length - 1];
232
232
  return {
233
233
  id: String(msg.sequence),
234
- value: msg.transcript_segments?.[0]?.text ?? "",
234
+ value: first?.text ?? "",
235
235
  startTime: first?.start_timestamp ? Date.parse(first.start_timestamp) : now,
236
236
  endTime: last?.end_timestamp ? Date.parse(last.end_timestamp) : now,
237
- isFinal: msg.transcript_segments.every((s: any) => s.is_final),
237
+ isFinal: msg.transcript_segments.every((s) => s.is_final),
238
238
  };
239
239
  });
240
240
  this.emit("transcript", lines);
@@ -243,7 +243,7 @@ export class ExotelAIAssistController extends EventEmitter<ControllerEvents> {
243
243
  if (event.event_type === "suggestion" && event.value) {
244
244
  const suggestion: Suggestion = {
245
245
  id: Utils.getUniqueId(),
246
- value: event.value,
246
+ value: event.value.text,
247
247
  timestamp: now,
248
248
  };
249
249
  this.emit("suggestion", suggestion);
@@ -61,7 +61,8 @@ interface TranscriptSegment {
61
61
  is_final: boolean;
62
62
  end_timestamp: string;
63
63
  start_timestamp: string;
64
- value: string;
64
+ speaker: string;
65
+ text: string;
65
66
  }
66
67
 
67
68
  interface TranscriptMessage {
@@ -69,12 +70,33 @@ interface TranscriptMessage {
69
70
  transcript_segments: TranscriptSegment[];
70
71
  }
71
72
 
72
- export interface WssEvent {
73
- event_type: "suggestion" | "sentiment" | "transcript";
73
+ export interface SuggestionValue {
74
+ sequence: number;
75
+ text: string;
76
+ }
77
+
78
+ interface WssEventBase {
74
79
  transcript: TranscriptMessage[];
80
+ text: string;
81
+ }
82
+
83
+ interface WssTranscriptEvent extends WssEventBase {
84
+ event_type: "transcript";
85
+ value: TranscriptMessage[];
86
+ }
87
+
88
+ interface WssSentimentEvent extends WssEventBase {
89
+ event_type: "sentiment";
75
90
  value: string;
76
91
  }
77
92
 
93
+ interface WssSuggestionEvent extends WssEventBase {
94
+ event_type: "suggestion";
95
+ value: SuggestionValue;
96
+ }
97
+
98
+ export type WssEvent = WssTranscriptEvent | WssSentimentEvent | WssSuggestionEvent;
99
+
78
100
  export interface InitialHandshakeResponse {
79
101
  type: string;
80
102
  config: BotConfig;