@elqnt/chat 3.1.0 → 3.5.0

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.
@@ -4,6 +4,18 @@
4
4
  import { useCallback, useEffect, useRef, useState } from "react";
5
5
 
6
6
  // transport/types.ts
7
+ async function resolveTransportToken(cfg) {
8
+ if (!cfg) return void 0;
9
+ if (cfg.getToken) {
10
+ try {
11
+ const t = await cfg.getToken();
12
+ return t ?? void 0;
13
+ } catch {
14
+ return void 0;
15
+ }
16
+ }
17
+ return cfg.token ?? void 0;
18
+ }
7
19
  function createLogger(debug = false) {
8
20
  return {
9
21
  debug: debug ? console.log.bind(console, "[chat]") : () => {
@@ -84,9 +96,12 @@ function createSSETransport(options = {}) {
84
96
  }
85
97
  const url = `${config.baseUrl}/${endpoint}`;
86
98
  logger.debug(`POST ${endpoint}`, body);
99
+ const token = await resolveTransportToken(config);
100
+ const headers = { "Content-Type": "application/json" };
101
+ if (token) headers["Authorization"] = `Bearer ${token}`;
87
102
  const response = await fetch(url, {
88
103
  method: "POST",
89
- headers: { "Content-Type": "application/json" },
104
+ headers,
90
105
  body: JSON.stringify(body)
91
106
  });
92
107
  if (!response.ok) {
@@ -112,6 +127,7 @@ function createSSETransport(options = {}) {
112
127
  function setupEventListeners(es) {
113
128
  es.addEventListener("message", handleMessage);
114
129
  const eventTypes = [
130
+ "error",
115
131
  "reconnected",
116
132
  "typing",
117
133
  "stopped_typing",
@@ -121,21 +137,53 @@ function createSSETransport(options = {}) {
121
137
  "human_agent_left",
122
138
  "chat_ended",
123
139
  "chat_updated",
140
+ "chat_removed",
124
141
  "load_chat_response",
125
142
  "new_chat_created",
126
143
  "show_csat_survey",
127
144
  "csat_response",
128
145
  "user_suggested_actions",
146
+ "user_suggested_action_selected",
129
147
  "agent_execution_started",
130
148
  "agent_execution_ended",
131
149
  "agent_context_update",
150
+ "load_agent_context_response",
132
151
  "plan_pending_approval",
152
+ "plan_approved",
153
+ "plan_rejected",
133
154
  "step_started",
134
155
  "step_completed",
135
156
  "step_failed",
136
157
  "plan_completed",
137
158
  "skills_changed",
138
- "summary_update"
159
+ "summary_update",
160
+ "message_status_update",
161
+ "delivered",
162
+ "read",
163
+ "sync_metadata_response",
164
+ "sync_user_session_response",
165
+ "client_action",
166
+ "client_action_callback",
167
+ "attachment_processing_started",
168
+ "attachment_processing_progress",
169
+ "attachment_processing_complete",
170
+ "attachment_processing_error",
171
+ "observer_joined",
172
+ "observer_left",
173
+ "block_user",
174
+ "message_edited",
175
+ "message_deleted",
176
+ "message_reaction",
177
+ "user_presence_changed",
178
+ "online_users",
179
+ "get_agents_response",
180
+ "room_created",
181
+ "room_updated",
182
+ "room_deleted",
183
+ "rooms_response",
184
+ "room_user_joined",
185
+ "room_user_left",
186
+ "user_invited"
139
187
  ];
140
188
  eventTypes.forEach((type) => {
141
189
  es.addEventListener(type, handleMessage);
@@ -180,20 +228,38 @@ function createSSETransport(options = {}) {
180
228
  reconnectTimeout = void 0;
181
229
  }
182
230
  state = retryCount > 0 ? "reconnecting" : "connecting";
231
+ const connectToken = await resolveTransportToken(cfg);
183
232
  return new Promise((resolve, reject) => {
184
233
  const connectionStart = Date.now();
185
- const url = `${cfg.baseUrl}/stream?orgId=${cfg.orgId}&userId=${cfg.userId}&clientType=${cfg.clientType}${cfg.chatKey ? `&chatId=${cfg.chatKey}` : ""}`;
234
+ const streamParams = new URLSearchParams({
235
+ orgId: cfg.orgId,
236
+ userId: cfg.userId,
237
+ clientType: cfg.clientType
238
+ });
239
+ if (cfg.chatKey) streamParams.set("chatId", cfg.chatKey);
240
+ if (connectToken) streamParams.set("token", connectToken);
241
+ const url = `${cfg.baseUrl}/stream?${streamParams.toString()}`;
186
242
  logger.debug("Connecting to:", url);
187
243
  const es = new EventSource(url);
188
244
  es.onopen = () => {
189
245
  const connectionTime = Date.now() - connectionStart;
190
246
  logger.info(`Connected in ${connectionTime}ms`);
247
+ const wasReconnect = retryCount > 0;
191
248
  state = "connected";
192
249
  error = void 0;
193
250
  retryCount = 0;
194
251
  metrics.connectedAt = Date.now();
195
252
  metrics.latency = connectionTime;
196
253
  setupEventListeners(es);
254
+ if (wasReconnect) {
255
+ emit({
256
+ type: "transport_reconnected",
257
+ orgId: cfg.orgId,
258
+ chatKey: cfg.chatKey || "",
259
+ userId: cfg.userId,
260
+ timestamp: Date.now()
261
+ });
262
+ }
197
263
  resolve();
198
264
  };
199
265
  es.onerror = () => {
@@ -425,9 +491,12 @@ function createFetchSSETransport(options = {}) {
425
491
  }
426
492
  const url = `${config.baseUrl}/${endpoint}`;
427
493
  logger.debug(`POST ${endpoint}`, body);
494
+ const token = await resolveTransportToken(config);
495
+ const headers = { "Content-Type": "application/json" };
496
+ if (token) headers["Authorization"] = `Bearer ${token}`;
428
497
  const response = await customFetch(url, {
429
498
  method: "POST",
430
- headers: { "Content-Type": "application/json" },
499
+ headers,
431
500
  body: JSON.stringify(body)
432
501
  });
433
502
  if (!response.ok) {
@@ -464,15 +533,25 @@ function createFetchSSETransport(options = {}) {
464
533
  return events;
465
534
  }
466
535
  async function startStream(cfg) {
467
- const url = `${cfg.baseUrl}/stream?orgId=${cfg.orgId}&userId=${cfg.userId}&clientType=${cfg.clientType}${cfg.chatKey ? `&chatId=${cfg.chatKey}` : ""}`;
536
+ const streamParams = new URLSearchParams({
537
+ orgId: cfg.orgId,
538
+ userId: cfg.userId,
539
+ clientType: cfg.clientType
540
+ });
541
+ if (cfg.chatKey) streamParams.set("chatId", cfg.chatKey);
542
+ const token = await resolveTransportToken(cfg);
543
+ if (token) streamParams.set("token", token);
544
+ const url = `${cfg.baseUrl}/stream?${streamParams.toString()}`;
468
545
  logger.debug("Connecting to:", url);
469
546
  abortController = new AbortController();
547
+ const streamHeaders = {
548
+ Accept: "text/event-stream",
549
+ "Cache-Control": "no-cache"
550
+ };
551
+ if (token) streamHeaders["Authorization"] = `Bearer ${token}`;
470
552
  const response = await customFetch(url, {
471
553
  method: "GET",
472
- headers: {
473
- Accept: "text/event-stream",
474
- "Cache-Control": "no-cache"
475
- },
554
+ headers: streamHeaders,
476
555
  signal: abortController.signal
477
556
  });
478
557
  if (!response.ok) {
@@ -570,6 +649,7 @@ function createFetchSSETransport(options = {}) {
570
649
  state = retryCount > 0 ? "reconnecting" : "connecting";
571
650
  const connectionStart = Date.now();
572
651
  try {
652
+ const wasReconnect = retryCount > 0;
573
653
  await startStream(cfg);
574
654
  const connectionTime = Date.now() - connectionStart;
575
655
  logger.info(`Connected in ${connectionTime}ms`);
@@ -578,6 +658,15 @@ function createFetchSSETransport(options = {}) {
578
658
  retryCount = 0;
579
659
  metrics.connectedAt = Date.now();
580
660
  metrics.latency = connectionTime;
661
+ if (wasReconnect) {
662
+ emit({
663
+ type: "transport_reconnected",
664
+ orgId: cfg.orgId,
665
+ chatKey: cfg.chatKey || "",
666
+ userId: cfg.userId,
667
+ timestamp: Date.now()
668
+ });
669
+ }
581
670
  } catch (err) {
582
671
  const connectError = {
583
672
  code: "CONNECTION_FAILED",
@@ -619,7 +708,8 @@ function createFetchSSETransport(options = {}) {
619
708
  orgId: event.orgId,
620
709
  chatKey: event.chatKey,
621
710
  userId: event.userId,
622
- message: event.message
711
+ message: event.message,
712
+ ...event.data ? { data: event.data } : {}
623
713
  });
624
714
  break;
625
715
  case "typing":
@@ -775,7 +865,9 @@ function useChat(options) {
775
865
  onConnectionChange,
776
866
  autoConnect = false,
777
867
  retryConfig,
778
- debug = false
868
+ debug = false,
869
+ token,
870
+ getToken
779
871
  } = options;
780
872
  const [connectionState, setConnectionState] = useState("disconnected");
781
873
  const [currentChat, setCurrentChat] = useState(null);
@@ -794,10 +886,14 @@ function useChat(options) {
794
886
  const onMessageRef = useRef(onMessage);
795
887
  const onErrorRef = useRef(onError);
796
888
  const typingTimeoutRef = useRef(null);
889
+ const getTokenRef = useRef(getToken);
890
+ const tokenRef = useRef(token);
797
891
  useEffect(() => {
798
892
  onMessageRef.current = onMessage;
799
893
  onErrorRef.current = onError;
800
- }, [onMessage, onError]);
894
+ getTokenRef.current = getToken;
895
+ tokenRef.current = token;
896
+ }, [onMessage, onError, getToken, token]);
801
897
  useEffect(() => {
802
898
  if (typeof transportOption === "object") {
803
899
  transportRef.current = transportOption;
@@ -880,7 +976,10 @@ function useChat(options) {
880
976
  userId,
881
977
  clientType,
882
978
  chatKey: chatKey || void 0,
883
- debug
979
+ debug,
980
+ // Always resolve via the ref so a long-lived/reconnecting stream picks
981
+ // up a freshly-minted token rather than a stale captured one.
982
+ getToken: () => getTokenRef.current ? getTokenRef.current() : tokenRef.current
884
983
  });
885
984
  setConnectionState("connected");
886
985
  setError(null);
@@ -939,7 +1038,7 @@ function useChat(options) {
939
1038
  [orgId, userId]
940
1039
  );
941
1040
  const sendMessage = useCallback(
942
- async (content, attachments) => {
1041
+ async (content, attachments, data) => {
943
1042
  const transport = transportRef.current;
944
1043
  if (!transport) {
945
1044
  throw new Error("Transport not initialized");
@@ -964,7 +1063,8 @@ function useChat(options) {
964
1063
  chatKey,
965
1064
  userId,
966
1065
  timestamp: Date.now(),
967
- message
1066
+ message,
1067
+ ...data ? { data } : {}
968
1068
  });
969
1069
  setMetrics((prev) => ({
970
1070
  ...prev,