@origonai/web-chat-sdk 1.0.6 → 1.0.8
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/dist/origon-chat-sdk.js +138 -135
- package/dist/origon-chat-sdk.js.map +1 -1
- package/package.json +1 -1
- package/src/chat.js +5 -5
- package/src/http.js +1 -0
package/dist/origon-chat-sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fetchEventSource as
|
|
2
|
-
function
|
|
1
|
+
import { fetchEventSource as ue } from "@microsoft/fetch-event-source";
|
|
2
|
+
function Z() {
|
|
3
3
|
const e = Date.now(), n = new Uint8Array(16);
|
|
4
4
|
crypto.getRandomValues(n), n[0] = e >> 40 & 255, n[1] = e >> 32 & 255, n[2] = e >> 24 & 255, n[3] = e >> 16 & 255, n[4] = e >> 8 & 255, n[5] = e & 255, n[6] = n[6] & 15 | 112, n[8] = n[8] & 63 | 128;
|
|
5
5
|
const o = [...n].map((r) => r.toString(16).padStart(2, "0")).join("");
|
|
@@ -8,16 +8,16 @@ function X() {
|
|
|
8
8
|
20
|
|
9
9
|
)}-${o.slice(20)}`;
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function ge() {
|
|
12
12
|
if (localStorage.getItem("chatDeviceId"))
|
|
13
13
|
return localStorage.getItem("chatDeviceId");
|
|
14
|
-
const e =
|
|
14
|
+
const e = Z();
|
|
15
15
|
return localStorage.setItem("chatDeviceId", e), e;
|
|
16
16
|
}
|
|
17
|
-
async function
|
|
17
|
+
async function fe(e) {
|
|
18
18
|
return new Promise((n) => setTimeout(n, e));
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function pe(e) {
|
|
21
21
|
let n;
|
|
22
22
|
try {
|
|
23
23
|
const o = new URL(e);
|
|
@@ -26,7 +26,7 @@ function fe(e) {
|
|
|
26
26
|
}
|
|
27
27
|
return n;
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function me(e) {
|
|
30
30
|
let n;
|
|
31
31
|
try {
|
|
32
32
|
const o = new URL(e);
|
|
@@ -35,7 +35,7 @@ function pe(e) {
|
|
|
35
35
|
}
|
|
36
36
|
return n;
|
|
37
37
|
}
|
|
38
|
-
function
|
|
38
|
+
function Se(e) {
|
|
39
39
|
let n;
|
|
40
40
|
try {
|
|
41
41
|
const o = new URL(e);
|
|
@@ -53,13 +53,13 @@ const y = {
|
|
|
53
53
|
// this is human supervisor (ex. Samespace Dock agent, or Resolve human agent)
|
|
54
54
|
SYSTEM: "system"
|
|
55
55
|
// this is system message, for ex "Agent joined" / "Agent left"
|
|
56
|
-
},
|
|
56
|
+
}, j = 1e4, ke = 5e3, A = {
|
|
57
57
|
MESSAGE: "message",
|
|
58
58
|
TYPING: "typing",
|
|
59
59
|
TYPING_STOP: "typingOff",
|
|
60
60
|
END: "end"
|
|
61
61
|
};
|
|
62
|
-
function
|
|
62
|
+
function Ie() {
|
|
63
63
|
return {
|
|
64
64
|
socket: null,
|
|
65
65
|
previouslyConnected: !1,
|
|
@@ -69,25 +69,25 @@ function ke() {
|
|
|
69
69
|
socketConnectionTimeout: null
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
let a =
|
|
72
|
+
let a = Ie();
|
|
73
73
|
function M() {
|
|
74
74
|
a.pingInterval && (clearInterval(a.pingInterval), a.pingInterval = null);
|
|
75
75
|
}
|
|
76
|
-
function
|
|
76
|
+
function he() {
|
|
77
77
|
M(), a.pingInterval = setInterval(() => {
|
|
78
78
|
a.socket && a.socket.readyState === WebSocket.OPEN ? x({ type: "ping" }) : M();
|
|
79
|
-
},
|
|
79
|
+
}, j);
|
|
80
80
|
}
|
|
81
|
-
function
|
|
81
|
+
function ee() {
|
|
82
82
|
M(), a.socketDisconnectedTimeout && (clearTimeout(a.socketDisconnectedTimeout), a.socketDisconnectedTimeout = null), a.socketConnectionTimeout && (clearTimeout(a.socketConnectionTimeout), a.socketConnectionTimeout = null);
|
|
83
83
|
}
|
|
84
|
-
function
|
|
84
|
+
function ne() {
|
|
85
85
|
a.socketDisconnected = !1, O("socket");
|
|
86
86
|
}
|
|
87
|
-
function
|
|
87
|
+
function te() {
|
|
88
88
|
a.socketDisconnected = !0, O("sse");
|
|
89
89
|
}
|
|
90
|
-
function
|
|
90
|
+
function we(e) {
|
|
91
91
|
return new Promise((n, o) => {
|
|
92
92
|
if (a.socket && (a.socket.readyState === WebSocket.CONNECTING || a.socket.readyState === WebSocket.OPEN)) {
|
|
93
93
|
n(a.socket.readyState === WebSocket.OPEN);
|
|
@@ -98,7 +98,7 @@ function he(e) {
|
|
|
98
98
|
o(new Error("SDK not initialized. Please initialize SDK first."));
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
-
const l =
|
|
101
|
+
const l = me(r.endpoint);
|
|
102
102
|
if (!l) {
|
|
103
103
|
o(
|
|
104
104
|
new Error(
|
|
@@ -113,47 +113,47 @@ function he(e) {
|
|
|
113
113
|
e.sessionId && c.set("sessionId", e.sessionId), e.requestId && c.set("requestId", e.requestId), r.token && c.set("token", r.token);
|
|
114
114
|
const u = `${l}?${c.toString()}`;
|
|
115
115
|
a.socket = new WebSocket(u), a.socket.onopen = () => {
|
|
116
|
-
a.previouslyConnected = !0,
|
|
116
|
+
a.previouslyConnected = !0, ne(), x({ type: "ping" }), clearTimeout(a.socketConnectionTimeout), he(), n(!0);
|
|
117
117
|
}, a.socket.onmessage = (f) => {
|
|
118
118
|
const g = JSON.parse(f.data);
|
|
119
|
-
|
|
119
|
+
Ce(g);
|
|
120
120
|
}, a.socket.onerror = (f) => {
|
|
121
121
|
O("sse"), o(f);
|
|
122
122
|
}, a.socket.onclose = (f) => {
|
|
123
|
-
f.target === a.socket && (f.code === 1006 && (a.previouslyConnected ?
|
|
123
|
+
f.target === a.socket && (f.code === 1006 && (a.previouslyConnected ? te() : h({
|
|
124
124
|
errorText: "Unable to establish connection",
|
|
125
125
|
done: !0,
|
|
126
126
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
127
|
-
}), clearTimeout(a.socketConnectionTimeout)), a.socket = null,
|
|
127
|
+
}), clearTimeout(a.socketConnectionTimeout)), a.socket = null, ee());
|
|
128
128
|
}, a.previouslyConnected || (a.socketConnectionTimeout = setTimeout(() => {
|
|
129
129
|
h({
|
|
130
130
|
errorText: "Unable to establish connection",
|
|
131
131
|
done: !0,
|
|
132
132
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
133
133
|
}), o(new Error("Socket connection timed out"));
|
|
134
|
-
},
|
|
134
|
+
}, ke));
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
137
|
function x(e) {
|
|
138
|
-
a.socketDisconnected || !a.socket || a.socket.send(JSON.stringify({ ...e, eventId: e.eventId ||
|
|
138
|
+
a.socketDisconnected || !a.socket || a.socket.send(JSON.stringify({ ...e, eventId: e.eventId || Z() }));
|
|
139
139
|
}
|
|
140
|
-
function
|
|
140
|
+
function Ce(e) {
|
|
141
141
|
switch (e.type) {
|
|
142
142
|
case "pong": {
|
|
143
|
-
a.socketDisconnected &&
|
|
144
|
-
|
|
145
|
-
},
|
|
143
|
+
a.socketDisconnected && ne(), a.socketDisconnectedTimeout && clearTimeout(a.socketDisconnectedTimeout), a.socketDisconnectedTimeout = setTimeout(() => {
|
|
144
|
+
te();
|
|
145
|
+
}, j + 1e3);
|
|
146
146
|
break;
|
|
147
147
|
}
|
|
148
|
-
case
|
|
149
|
-
|
|
148
|
+
case A.TYPING: {
|
|
149
|
+
X(!0);
|
|
150
150
|
break;
|
|
151
151
|
}
|
|
152
|
-
case
|
|
153
|
-
|
|
152
|
+
case A.TYPING_STOP: {
|
|
153
|
+
X(!1);
|
|
154
154
|
break;
|
|
155
155
|
}
|
|
156
|
-
case
|
|
156
|
+
case A.MESSAGE: {
|
|
157
157
|
const { eventId: n, data: o } = e;
|
|
158
158
|
n || h({
|
|
159
159
|
...o,
|
|
@@ -162,19 +162,19 @@ function we(e) {
|
|
|
162
162
|
});
|
|
163
163
|
break;
|
|
164
164
|
}
|
|
165
|
-
case
|
|
166
|
-
|
|
165
|
+
case A.END: {
|
|
166
|
+
oe();
|
|
167
167
|
break;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
function
|
|
172
|
-
a.socket && a.socket.close(1e3), a.previouslyConnected = !1,
|
|
171
|
+
function oe() {
|
|
172
|
+
a.socket && a.socket.close(1e3), a.previouslyConnected = !1, ee(), a.socket = null, O("sse");
|
|
173
173
|
}
|
|
174
|
-
function
|
|
174
|
+
function Ee() {
|
|
175
175
|
return a.socket !== null && a.socket.readyState === WebSocket.OPEN && !a.socketDisconnected;
|
|
176
176
|
}
|
|
177
|
-
function
|
|
177
|
+
function se(e = {}) {
|
|
178
178
|
return {
|
|
179
179
|
credentials: void 0,
|
|
180
180
|
authenticated: !1,
|
|
@@ -190,33 +190,33 @@ function oe(e = {}) {
|
|
|
190
190
|
control: "agent"
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
|
-
let s =
|
|
194
|
-
function
|
|
193
|
+
let s = se();
|
|
194
|
+
function Fe(e) {
|
|
195
195
|
s.callbacks = { ...s.callbacks, ...e };
|
|
196
196
|
}
|
|
197
|
-
function
|
|
197
|
+
function Ge(e) {
|
|
198
198
|
s.credentials = e, e.token && (s.authenticated = !0);
|
|
199
199
|
}
|
|
200
200
|
function b() {
|
|
201
201
|
return s.credentials;
|
|
202
202
|
}
|
|
203
|
-
function
|
|
203
|
+
function ye(e) {
|
|
204
204
|
var n, o;
|
|
205
205
|
e && e !== s.sessionId && (s.sessionId = e, (o = (n = s.callbacks).onSessionUpdate) == null || o.call(n, e));
|
|
206
206
|
}
|
|
207
|
-
async function
|
|
207
|
+
async function Je(e = {}) {
|
|
208
208
|
try {
|
|
209
209
|
let n = null;
|
|
210
|
-
s.authenticated ? n = s.configData : (n = await
|
|
210
|
+
s.authenticated ? n = s.configData : (n = await ve(s.credentials), s.authenticated = !0, s.configData = n);
|
|
211
211
|
let o = [], r = "agent";
|
|
212
212
|
if (e.sessionId) {
|
|
213
|
-
const d = await
|
|
213
|
+
const d = await $e(e.sessionId);
|
|
214
214
|
o = d.messages, r = d.control || "agent";
|
|
215
215
|
}
|
|
216
216
|
const l = new URLSearchParams();
|
|
217
|
-
return s.credentials.token || l.set("externalId", P()), s.sseUrl = `${
|
|
217
|
+
return s.credentials.token || l.set("externalId", P()), s.sseUrl = `${pe(
|
|
218
218
|
s.credentials.endpoint
|
|
219
|
-
)}?${l.toString()}`, s.sessionId = e.sessionId, s.messages = o, s.control = r, r === "human" &&
|
|
219
|
+
)}?${l.toString()}`, s.sessionId = e.sessionId, s.messages = o, s.control = r, r === "human" && be({ text: "", html: "" }).catch(() => {
|
|
220
220
|
}), {
|
|
221
221
|
sessionId: s.sessionId,
|
|
222
222
|
messages: o,
|
|
@@ -224,26 +224,26 @@ async function Ge(e = {}) {
|
|
|
224
224
|
configData: n
|
|
225
225
|
};
|
|
226
226
|
} catch (n) {
|
|
227
|
-
throw
|
|
227
|
+
throw re(), n;
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
|
-
function
|
|
231
|
-
|
|
230
|
+
function We() {
|
|
231
|
+
re();
|
|
232
232
|
}
|
|
233
|
-
function
|
|
234
|
-
s.abortController && s.abortController.abort(),
|
|
233
|
+
function re() {
|
|
234
|
+
s.abortController && s.abortController.abort(), oe();
|
|
235
235
|
const { callbacks: e, credentials: n } = s;
|
|
236
|
-
s =
|
|
236
|
+
s = se(e), s.credentials = n;
|
|
237
237
|
}
|
|
238
238
|
function P() {
|
|
239
239
|
var e;
|
|
240
|
-
return (e = s.credentials) != null && e.externalId ? s.credentials.externalId :
|
|
240
|
+
return (e = s.credentials) != null && e.externalId ? s.credentials.externalId : ge();
|
|
241
241
|
}
|
|
242
242
|
function h(e) {
|
|
243
243
|
var n, o;
|
|
244
244
|
s.messages = [...s.messages, e], (o = (n = s.callbacks).onMessageAdd) == null || o.call(n, e);
|
|
245
245
|
}
|
|
246
|
-
function
|
|
246
|
+
function X(e) {
|
|
247
247
|
var n, o;
|
|
248
248
|
(o = (n = s.callbacks).onTyping) == null || o.call(n, e);
|
|
249
249
|
}
|
|
@@ -251,12 +251,12 @@ function O(e) {
|
|
|
251
251
|
var n, o;
|
|
252
252
|
s.transport = e, (o = (n = s.callbacks).onTransportUpdate) == null || o.call(n, e);
|
|
253
253
|
}
|
|
254
|
-
function
|
|
254
|
+
function be({ text: e, html: n, context: o, attachments: r }) {
|
|
255
255
|
return new Promise((l, d) => {
|
|
256
256
|
(async () => {
|
|
257
257
|
var c, u, f;
|
|
258
258
|
try {
|
|
259
|
-
const g = !e && !n;
|
|
259
|
+
const g = !e && !n && !(r != null && r.length);
|
|
260
260
|
if (!g) {
|
|
261
261
|
const p = {
|
|
262
262
|
role: y.USER,
|
|
@@ -265,9 +265,9 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
265
265
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
266
266
|
attachments: r
|
|
267
267
|
};
|
|
268
|
-
h(p), await
|
|
268
|
+
h(p), await fe(200);
|
|
269
269
|
}
|
|
270
|
-
if (s.transport === "socket" &&
|
|
270
|
+
if (s.transport === "socket" && Ee()) {
|
|
271
271
|
x({
|
|
272
272
|
type: "message",
|
|
273
273
|
data: {
|
|
@@ -290,7 +290,7 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
290
290
|
const C = {
|
|
291
291
|
"Content-Type": "application/json"
|
|
292
292
|
};
|
|
293
|
-
(c = s.credentials) != null && c.token && (C.Authorization = `Bearer ${s.credentials.token}`), await
|
|
293
|
+
(c = s.credentials) != null && c.token && (C.Authorization = `Bearer ${s.credentials.token}`), await ue(I.toString(), {
|
|
294
294
|
method: "POST",
|
|
295
295
|
headers: C,
|
|
296
296
|
body: g ? void 0 : JSON.stringify({
|
|
@@ -306,12 +306,12 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
306
306
|
throw new Error("Failed to send message");
|
|
307
307
|
},
|
|
308
308
|
onmessage: (p) => {
|
|
309
|
-
var $,
|
|
309
|
+
var $, D, q, L, z, F, G, J, W, _, K, H, V, B, Y, Q;
|
|
310
310
|
const i = JSON.parse(p.data);
|
|
311
311
|
if (p.event === "connected")
|
|
312
|
-
s.sessionId = i.sessionId, s.requestId = i.requestId, i.control && (s.control = i.control, (
|
|
312
|
+
s.sessionId = i.sessionId, s.requestId = i.requestId, i.control && (s.control = i.control, (D = ($ = s.callbacks).onControlUpdate) == null || D.call($, i.control));
|
|
313
313
|
else if (p.event === "upgrade_to_websocket")
|
|
314
|
-
|
|
314
|
+
we({
|
|
315
315
|
sessionId: s.sessionId,
|
|
316
316
|
requestId: i.requestId
|
|
317
317
|
});
|
|
@@ -324,7 +324,7 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
324
324
|
errorText: m
|
|
325
325
|
};
|
|
326
326
|
s.messages = s.messages.map(
|
|
327
|
-
(v,
|
|
327
|
+
(v, de) => de === k ? S : v
|
|
328
328
|
), (F = (z = s.callbacks).onMessageUpdate) == null || F.call(z, k, S), d(new Error(m));
|
|
329
329
|
} else if (p.event === "done") {
|
|
330
330
|
const m = s.messages.length - 1, E = {
|
|
@@ -335,15 +335,16 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
335
335
|
s.messages = s.messages.map(
|
|
336
336
|
(S, v) => v === m ? E : S
|
|
337
337
|
), (J = (G = s.callbacks).onMessageUpdate) == null || J.call(G, m, E), l(s.sessionId);
|
|
338
|
-
} else if (i.message !== void 0) {
|
|
338
|
+
} else if (i.message !== void 0 || ((W = i.attachments) == null ? void 0 : W.length) > 0) {
|
|
339
339
|
if (i.role === y.SUPERVISOR) {
|
|
340
340
|
const S = {
|
|
341
341
|
role: y.SUPERVISOR,
|
|
342
342
|
text: i.message,
|
|
343
|
+
attachments: i.attachments,
|
|
343
344
|
sources: i.sources,
|
|
344
345
|
done: !0
|
|
345
346
|
};
|
|
346
|
-
h(S), l(s.sessionId), s.sessionId = (
|
|
347
|
+
h(S), l(s.sessionId), s.sessionId = (_ = i.sessionId) != null ? _ : s.sessionId, s.requestId = (K = i.requestId) != null ? K : s.requestId;
|
|
347
348
|
return;
|
|
348
349
|
}
|
|
349
350
|
if (i.streamId !== void 0) {
|
|
@@ -364,11 +365,12 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
364
365
|
loading: !1,
|
|
365
366
|
text: (k.text || "") + i.message,
|
|
366
367
|
sources: i.sources,
|
|
367
|
-
|
|
368
|
+
attachments: i.attachments,
|
|
369
|
+
done: (H = i.done) != null ? H : k.done
|
|
368
370
|
};
|
|
369
371
|
s.messages = s.messages.map(
|
|
370
372
|
(S, v) => v === m ? E : S
|
|
371
|
-
), (
|
|
373
|
+
), (B = (V = s.callbacks).onMessageUpdate) == null || B.call(V, m, E), s.sessionId = (Y = i.sessionId) != null ? Y : s.sessionId, s.requestId = (Q = i.requestId) != null ? Q : s.requestId;
|
|
372
374
|
}
|
|
373
375
|
},
|
|
374
376
|
onerror: (p) => {
|
|
@@ -384,14 +386,14 @@ function ye({ text: e, html: n, context: o, attachments: r }) {
|
|
|
384
386
|
done: !0
|
|
385
387
|
};
|
|
386
388
|
s.messages = s.messages.map(
|
|
387
|
-
($,
|
|
389
|
+
($, D) => D === C ? i : $
|
|
388
390
|
), (f = (u = s.callbacks).onMessageUpdate) == null || f.call(u, C, i), d(g);
|
|
389
391
|
}
|
|
390
392
|
})();
|
|
391
393
|
});
|
|
392
394
|
}
|
|
393
|
-
const
|
|
394
|
-
async function
|
|
395
|
+
const Te = "Something went wrong initializing the chat", N = "Chat SDK not initialized";
|
|
396
|
+
async function ve(e) {
|
|
395
397
|
const { endpoint: n } = e, o = `${n}/config`, r = await fetch(o, {
|
|
396
398
|
method: "GET",
|
|
397
399
|
headers: {
|
|
@@ -400,23 +402,23 @@ async function Te(e) {
|
|
|
400
402
|
});
|
|
401
403
|
if (!r.ok) {
|
|
402
404
|
const c = await r.json();
|
|
403
|
-
throw new Error((c == null ? void 0 : c.error) ||
|
|
405
|
+
throw new Error((c == null ? void 0 : c.error) || Te);
|
|
404
406
|
}
|
|
405
407
|
return (await r.json()).data;
|
|
406
408
|
}
|
|
407
|
-
async function
|
|
409
|
+
async function _e() {
|
|
408
410
|
const e = new URLSearchParams({
|
|
409
411
|
externalId: P()
|
|
410
|
-
}), n = await
|
|
412
|
+
}), n = await ae(`/sessions?${e.toString()}`, "GET");
|
|
411
413
|
if (!n.ok)
|
|
412
414
|
throw new Error("Unable to load history, please try again later");
|
|
413
415
|
return n.json();
|
|
414
416
|
}
|
|
415
|
-
async function
|
|
417
|
+
async function $e(e) {
|
|
416
418
|
var c;
|
|
417
419
|
const n = new URLSearchParams({
|
|
418
420
|
sessionId: e
|
|
419
|
-
}), o = await
|
|
421
|
+
}), o = await ae(`/session?${n.toString()}`, "GET");
|
|
420
422
|
if (!o.ok)
|
|
421
423
|
throw new Error("Unable to load messages, please try again later");
|
|
422
424
|
const r = await o.json(), l = r == null ? void 0 : r.control, d = ((c = r == null ? void 0 : r.history) != null ? c : []).map((u) => ({
|
|
@@ -425,12 +427,13 @@ async function ve(e) {
|
|
|
425
427
|
role: u.youtubeVideo ? y.ASSISTANT : u.role,
|
|
426
428
|
timestamp: u.timestamp,
|
|
427
429
|
video: u.youtubeVideo,
|
|
430
|
+
attachments: u.attachments,
|
|
428
431
|
channel: u.channel,
|
|
429
432
|
done: !0
|
|
430
433
|
}));
|
|
431
434
|
return { control: l, messages: d };
|
|
432
435
|
}
|
|
433
|
-
function
|
|
436
|
+
function Ke(e, n, o) {
|
|
434
437
|
const r = b(), { endpoint: l, token: d } = r || {};
|
|
435
438
|
if (!l) {
|
|
436
439
|
const g = new Error(N);
|
|
@@ -460,7 +463,7 @@ function _e(e, n, o) {
|
|
|
460
463
|
o && o(new Error("Upload aborted"), null);
|
|
461
464
|
}), c.send(u), c;
|
|
462
465
|
}
|
|
463
|
-
async function
|
|
466
|
+
async function He(e) {
|
|
464
467
|
const n = b(), { endpoint: o, token: r } = n || {};
|
|
465
468
|
if (!o)
|
|
466
469
|
throw new Error(N);
|
|
@@ -474,13 +477,13 @@ async function Ke(e) {
|
|
|
474
477
|
throw new Error(`Delete failed with status ${c.status}`);
|
|
475
478
|
return { success: !0 };
|
|
476
479
|
}
|
|
477
|
-
function
|
|
480
|
+
function Ve(e) {
|
|
478
481
|
const n = b(), { endpoint: o } = n || {};
|
|
479
482
|
if (!o)
|
|
480
483
|
throw new Error(N);
|
|
481
484
|
return `${o}/upload/${e}`;
|
|
482
485
|
}
|
|
483
|
-
async function
|
|
486
|
+
async function ae(e, n = "GET", o = null) {
|
|
484
487
|
const r = b(), { endpoint: l, token: d } = r || {};
|
|
485
488
|
if (!l)
|
|
486
489
|
throw new Error(N);
|
|
@@ -493,7 +496,7 @@ async function re(e, n = "GET", o = null) {
|
|
|
493
496
|
body: o ? JSON.stringify(o) : null
|
|
494
497
|
});
|
|
495
498
|
}
|
|
496
|
-
function
|
|
499
|
+
function ce(e = {}) {
|
|
497
500
|
return {
|
|
498
501
|
sessionId: void 0,
|
|
499
502
|
socket: null,
|
|
@@ -511,17 +514,17 @@ function ae(e = {}) {
|
|
|
511
514
|
pendingRemoteIceCandidates: []
|
|
512
515
|
};
|
|
513
516
|
}
|
|
514
|
-
let t =
|
|
515
|
-
const
|
|
517
|
+
let t = ce();
|
|
518
|
+
const De = {
|
|
516
519
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }, { urls: "stun:stun1.l.google.com:19302" }]
|
|
517
520
|
};
|
|
518
|
-
function
|
|
521
|
+
function Be(e) {
|
|
519
522
|
t.callbacks = { ...t.callbacks, ...e };
|
|
520
523
|
}
|
|
521
|
-
function
|
|
524
|
+
function ie() {
|
|
522
525
|
t.peerConnection && (t.peerConnection.close(), t.peerConnection = null), t.localStream && (t.localStream.getTracks().forEach((n) => n.stop()), t.localStream = null), t.remoteStream && (t.remoteStream = null), t.remoteAudio && (t.remoteAudio.srcObject = null, t.remoteAudio.parentNode && t.remoteAudio.parentNode.removeChild(t.remoteAudio), t.remoteAudio = null), t.socket && (t.socket.close(), t.socket = null), U();
|
|
523
526
|
const e = t.callbacks;
|
|
524
|
-
t =
|
|
527
|
+
t = ce(e);
|
|
525
528
|
}
|
|
526
529
|
function w(e) {
|
|
527
530
|
var n, o;
|
|
@@ -548,13 +551,13 @@ function Ae() {
|
|
|
548
551
|
U();
|
|
549
552
|
}, 1e4);
|
|
550
553
|
}
|
|
551
|
-
function
|
|
554
|
+
function Re() {
|
|
552
555
|
t.lastPongTime = Date.now();
|
|
553
556
|
}
|
|
554
557
|
function T(e) {
|
|
555
558
|
t.socket && t.socket.readyState === WebSocket.OPEN && t.socket.send(JSON.stringify(e));
|
|
556
559
|
}
|
|
557
|
-
async function
|
|
560
|
+
async function Ue() {
|
|
558
561
|
try {
|
|
559
562
|
t.localStream = await navigator.mediaDevices.getUserMedia({
|
|
560
563
|
audio: !0,
|
|
@@ -564,8 +567,8 @@ async function Re() {
|
|
|
564
567
|
throw e;
|
|
565
568
|
}
|
|
566
569
|
}
|
|
567
|
-
function
|
|
568
|
-
t.peerConnection = new RTCPeerConnection(
|
|
570
|
+
function Pe() {
|
|
571
|
+
t.peerConnection = new RTCPeerConnection(De), t.peerConnection.onicecandidate = (e) => {
|
|
569
572
|
if (e.candidate) {
|
|
570
573
|
const n = JSON.stringify(e.candidate);
|
|
571
574
|
t.peerConnection && t.peerConnection.remoteDescription ? T({
|
|
@@ -581,11 +584,11 @@ function Ue() {
|
|
|
581
584
|
});
|
|
582
585
|
}, t.peerConnection.onconnectionstatechange = () => {
|
|
583
586
|
const e = t.peerConnection.connectionState;
|
|
584
|
-
e === "connected" ? w("connected") : (e === "disconnected" || e === "closed") && (w("disconnected"),
|
|
587
|
+
e === "connected" ? w("connected") : (e === "disconnected" || e === "closed") && (w("disconnected"), le());
|
|
585
588
|
}, t.peerConnection.oniceconnectionstatechange = () => {
|
|
586
589
|
};
|
|
587
590
|
}
|
|
588
|
-
function
|
|
591
|
+
function Oe(e) {
|
|
589
592
|
return new Promise((n, o) => {
|
|
590
593
|
if (t.socket && (t.socket.readyState === WebSocket.CONNECTING || t.socket.readyState === WebSocket.OPEN)) {
|
|
591
594
|
n(t.socket.readyState === WebSocket.OPEN);
|
|
@@ -596,7 +599,7 @@ function Pe(e) {
|
|
|
596
599
|
o(new Error("SDK not initialized. Please initialize SDK first."));
|
|
597
600
|
return;
|
|
598
601
|
}
|
|
599
|
-
const l =
|
|
602
|
+
const l = Se(r.endpoint);
|
|
600
603
|
if (!l) {
|
|
601
604
|
o(
|
|
602
605
|
new Error(
|
|
@@ -614,7 +617,7 @@ function Pe(e) {
|
|
|
614
617
|
Ae(), n(!0);
|
|
615
618
|
}, t.socket.onmessage = (f) => {
|
|
616
619
|
const g = JSON.parse(f.data);
|
|
617
|
-
|
|
620
|
+
Ne(g);
|
|
618
621
|
}, t.socket.onerror = (f) => {
|
|
619
622
|
w("error"), R(f.message || "Unable to connect voice"), o(f);
|
|
620
623
|
}, t.socket.onclose = (f) => {
|
|
@@ -622,25 +625,25 @@ function Pe(e) {
|
|
|
622
625
|
};
|
|
623
626
|
});
|
|
624
627
|
}
|
|
625
|
-
function
|
|
628
|
+
function Ne(e) {
|
|
626
629
|
switch (e.type) {
|
|
627
630
|
case "pong":
|
|
628
|
-
|
|
631
|
+
Re();
|
|
629
632
|
break;
|
|
630
633
|
case "answer":
|
|
631
|
-
|
|
634
|
+
xe(e.data);
|
|
632
635
|
break;
|
|
633
636
|
case "connected":
|
|
634
|
-
|
|
637
|
+
Me(e.data);
|
|
635
638
|
break;
|
|
636
639
|
case "ice":
|
|
637
|
-
|
|
640
|
+
qe(e.data);
|
|
638
641
|
break;
|
|
639
642
|
case "renegotiationOffer":
|
|
640
|
-
|
|
643
|
+
Le(e.data);
|
|
641
644
|
break;
|
|
642
645
|
case "end":
|
|
643
|
-
|
|
646
|
+
le();
|
|
644
647
|
break;
|
|
645
648
|
case "error":
|
|
646
649
|
w("error"), R(e.error || "Unable to connect voice");
|
|
@@ -649,10 +652,10 @@ function Oe(e) {
|
|
|
649
652
|
break;
|
|
650
653
|
}
|
|
651
654
|
}
|
|
652
|
-
function
|
|
653
|
-
t.sessionId = e.sessionId,
|
|
655
|
+
function Me(e) {
|
|
656
|
+
t.sessionId = e.sessionId, ye(e.sessionId);
|
|
654
657
|
}
|
|
655
|
-
async function
|
|
658
|
+
async function xe(e) {
|
|
656
659
|
try {
|
|
657
660
|
if (t.peerConnection) {
|
|
658
661
|
const n = new RTCSessionDescription({
|
|
@@ -679,7 +682,7 @@ async function Me(e) {
|
|
|
679
682
|
} catch (n) {
|
|
680
683
|
}
|
|
681
684
|
}
|
|
682
|
-
async function
|
|
685
|
+
async function qe(e) {
|
|
683
686
|
try {
|
|
684
687
|
if (t.peerConnection) {
|
|
685
688
|
if (!t.peerConnection.remoteDescription) {
|
|
@@ -692,7 +695,7 @@ async function xe(e) {
|
|
|
692
695
|
} catch (n) {
|
|
693
696
|
}
|
|
694
697
|
}
|
|
695
|
-
async function
|
|
698
|
+
async function Le(e) {
|
|
696
699
|
try {
|
|
697
700
|
if (t.peerConnection) {
|
|
698
701
|
const n = new RTCSessionDescription({
|
|
@@ -711,13 +714,13 @@ async function qe(e) {
|
|
|
711
714
|
} catch (n) {
|
|
712
715
|
}
|
|
713
716
|
}
|
|
714
|
-
async function
|
|
717
|
+
async function Ye(e = {}) {
|
|
715
718
|
try {
|
|
716
719
|
if (t.callStatus === "connecting" || t.callStatus === "connected")
|
|
717
720
|
return;
|
|
718
|
-
w("connecting"), R(null), t.sessionId = e.sessionId, await
|
|
721
|
+
w("connecting"), R(null), t.sessionId = e.sessionId, await Ue(), Pe(), t.localStream.getTracks().forEach((o) => {
|
|
719
722
|
t.peerConnection.addTrack(o, t.localStream);
|
|
720
|
-
}), await
|
|
723
|
+
}), await Oe(e);
|
|
721
724
|
const n = await t.peerConnection.createOffer();
|
|
722
725
|
await t.peerConnection.setLocalDescription(n), T({
|
|
723
726
|
type: "offer",
|
|
@@ -726,15 +729,15 @@ async function Be(e = {}) {
|
|
|
726
729
|
}
|
|
727
730
|
});
|
|
728
731
|
} catch (n) {
|
|
729
|
-
w("error"), R(n.message || "Unable to connect voice"),
|
|
732
|
+
w("error"), R(n.message || "Unable to connect voice"), ie();
|
|
730
733
|
}
|
|
731
734
|
}
|
|
732
|
-
function
|
|
735
|
+
function le() {
|
|
733
736
|
T({
|
|
734
737
|
type: "end"
|
|
735
|
-
}), t.socket && (t.socket.close(), t.socket = null), w("disconnected"), t.peerConnection && (t.peerConnection.close(), t.peerConnection = null), t.localStream && (t.localStream.getTracks().forEach((e) => e.stop()), t.localStream = null),
|
|
738
|
+
}), t.socket && (t.socket.close(), t.socket = null), w("disconnected"), t.peerConnection && (t.peerConnection.close(), t.peerConnection = null), t.localStream && (t.localStream.getTracks().forEach((e) => e.stop()), t.localStream = null), ie();
|
|
736
739
|
}
|
|
737
|
-
function
|
|
740
|
+
function Qe() {
|
|
738
741
|
if (t.localStream) {
|
|
739
742
|
const e = t.localStream.getAudioTracks()[0];
|
|
740
743
|
if (e)
|
|
@@ -742,10 +745,10 @@ function Ye() {
|
|
|
742
745
|
}
|
|
743
746
|
return !1;
|
|
744
747
|
}
|
|
745
|
-
function
|
|
748
|
+
function Xe() {
|
|
746
749
|
return t.localStream;
|
|
747
750
|
}
|
|
748
|
-
function
|
|
751
|
+
function Ze() {
|
|
749
752
|
return new Promise((e, n) => {
|
|
750
753
|
if (!t.peerConnection) {
|
|
751
754
|
n(new Error("no peer connection"));
|
|
@@ -760,7 +763,7 @@ function Xe() {
|
|
|
760
763
|
});
|
|
761
764
|
});
|
|
762
765
|
}
|
|
763
|
-
function
|
|
766
|
+
function je() {
|
|
764
767
|
return new Promise((e, n) => {
|
|
765
768
|
if (!t.peerConnection) {
|
|
766
769
|
n(new Error("no peer connection"));
|
|
@@ -777,23 +780,23 @@ function Ze() {
|
|
|
777
780
|
}
|
|
778
781
|
export {
|
|
779
782
|
y as MESSAGE_ROLES,
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
783
|
+
ve as authenticate,
|
|
784
|
+
He as deleteAttachment,
|
|
785
|
+
We as disconnect,
|
|
786
|
+
le as disconnectCall,
|
|
787
|
+
Ve as getAttachment,
|
|
788
|
+
_e as getHistory,
|
|
789
|
+
Ze as getInboundAudioEnergy,
|
|
790
|
+
Xe as getLocalStream,
|
|
791
|
+
je as getOutboundAudioEnergy,
|
|
792
|
+
$e as getSession,
|
|
793
|
+
Ge as initialize,
|
|
794
|
+
be as sendMessage,
|
|
795
|
+
Be as setCallCallbacks,
|
|
796
|
+
Fe as setCallbacks,
|
|
797
|
+
Ye as startCall,
|
|
798
|
+
Je as startChat,
|
|
799
|
+
Qe as toggleMute,
|
|
800
|
+
Ke as uploadAttachment
|
|
798
801
|
};
|
|
799
802
|
//# sourceMappingURL=origon-chat-sdk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.js","../src/socket.js","../src/chat.js","../src/http.js","../src/call.js"],"sourcesContent":["/**\n * Utility functions for the Chat SDK\n */\n\nexport function uuidv7() {\n const timestamp = Date.now()\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n\n // Set timestamp (48 bits)\n bytes[0] = (timestamp >> 40) & 0xff\n bytes[1] = (timestamp >> 32) & 0xff\n bytes[2] = (timestamp >> 24) & 0xff\n bytes[3] = (timestamp >> 16) & 0xff\n bytes[4] = (timestamp >> 8) & 0xff\n bytes[5] = timestamp & 0xff\n\n // Set version 7 (0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70\n\n // Set variant (10xx)\n bytes[8] = (bytes[8] & 0x3f) | 0x80\n\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20\n )}-${hex.slice(20)}`\n}\n\nexport function getDeviceId() {\n if (localStorage.getItem('chatDeviceId')) {\n return localStorage.getItem('chatDeviceId')\n }\n\n const deviceId = uuidv7()\n localStorage.setItem('chatDeviceId', deviceId)\n return deviceId\n}\n\nexport function parseJwt(token) {\n try {\n const base64Url = token.split('.')[1]\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join('')\n )\n\n return JSON.parse(jsonPayload)\n } catch {\n return null\n }\n}\n\nexport async function sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function getSseEndpoint(baseUrl) {\n let sseEndpoint\n try {\n const url = new URL(baseUrl)\n sseEndpoint = `https://${url.hostname}${url.pathname}/sse`\n } catch {\n console.error('SSE Invalid base URL: ', baseUrl)\n }\n return sseEndpoint\n}\n\nexport function getSocketEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/wss`\n } catch {\n console.error('Socket Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n\nexport function getCallServerEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/audio`\n } catch {\n console.error('getCallServerEndpoint: Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n","/**\n * Constants for the Chat SDK\n */\n\nexport const MESSAGE_ROLES = {\n ASSISTANT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n SUPERVISOR: 'supervisor', // this is human supervisor (ex. Samespace Dock agent, or Resolve human agent)\n SYSTEM: 'system' // this is system message, for ex \"Agent joined\" / \"Agent left\"\n}\n","/**\n * Socket Service for Chat SDK\n * Handles WebSocket connection for real-time chat functionality\n */\n\nimport { getSocketEndpoint, uuidv7 } from './utils.js'\nimport {\n getCredentials,\n getExternalId,\n addMessage,\n toggleTypingStatus,\n setTransport\n} from './chat.js'\n\nconst PING_INTERVAL = 10000\nconst SOCKET_TIMEOUT = 5000\n\n/**\n * @typedef {Object} SocketSession\n * @property {WebSocket} [socket]\n * @property {boolean} previouslyConnected\n * @property {boolean} socketDisconnected\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {NodeJS.Timeout} [socketDisconnectedTimeout]\n * @property {NodeJS.Timeout} [socketConnectionTimeout]\n */\n\n/**\n * Socket Events\n */\nexport const SocketEvents = {\n MESSAGE: 'message',\n TYPING: 'typing',\n TYPING_STOP: 'typingOff',\n END: 'end'\n}\n\n/**\n * Create a new socket session\n * @returns {SocketSession}\n */\nfunction createSession() {\n return {\n socket: null,\n previouslyConnected: false,\n socketDisconnected: false,\n pingInterval: null,\n socketDisconnectedTimeout: null,\n socketConnectionTimeout: null\n }\n}\n\n/** @type {SocketSession} */\nlet currentSession = createSession()\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n send({ type: 'ping' })\n console.log('Sending keep-alive ping')\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, PING_INTERVAL)\n}\n\n/**\n * Clear all timeouts\n */\nfunction clearAllTimeouts() {\n stopPingInterval()\n if (currentSession.socketDisconnectedTimeout) {\n clearTimeout(currentSession.socketDisconnectedTimeout)\n currentSession.socketDisconnectedTimeout = null\n }\n if (currentSession.socketConnectionTimeout) {\n clearTimeout(currentSession.socketConnectionTimeout)\n currentSession.socketConnectionTimeout = null\n }\n}\n\n/**\n * Handle socket connected state\n */\nfunction handleSocketConnected() {\n console.log('handleSocketConnected')\n currentSession.socketDisconnected = false\n setTransport('socket')\n}\n\n/**\n * Handle socket disconnected state\n */\nfunction handleSocketDisconnected() {\n console.log('handleSocketDisconnected')\n currentSession.socketDisconnected = true\n setTransport('sse')\n}\n\n/**\n * Connect to socket\n * @param {{ token: string, sessionId?: string }} payload\n * @returns {Promise<boolean>}\n */\nexport function connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n const socketEndpoint = getSocketEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (payload.requestId) {\n queryParams.set('requestId', payload.requestId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = () => {\n console.log('-------- socket connected --------')\n currentSession.previouslyConnected = true\n handleSocketConnected()\n send({ type: 'ping' })\n clearTimeout(currentSession.socketConnectionTimeout)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const message = JSON.parse(event.data)\n handleSocketEvent(message)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error:', error)\n setTransport('sse')\n reject(error)\n }\n\n currentSession.socket.onclose = (ws) => {\n console.log('-------- socket disconnected --------: ', ws.code, ws.reason)\n\n if (ws.target === currentSession.socket) {\n if (ws.code === 1006) {\n // abnormal closure\n if (currentSession.previouslyConnected) {\n handleSocketDisconnected()\n } else {\n addMessage({\n errorText: 'Unable to establish connection',\n done: true,\n timestamp: new Date().toISOString()\n })\n }\n clearTimeout(currentSession.socketConnectionTimeout)\n }\n\n currentSession.socket = null\n clearAllTimeouts()\n }\n }\n\n if (!currentSession.previouslyConnected) {\n currentSession.socketConnectionTimeout = setTimeout(() => {\n console.error('Socket connection timed out')\n addMessage({\n errorText: 'Unable to establish connection',\n done: true,\n timestamp: new Date().toISOString()\n })\n reject(new Error('Socket connection timed out'))\n }, SOCKET_TIMEOUT)\n }\n })\n}\n\n/**\n * Send data through socket\n * @param {Object} data\n */\nexport function send(data) {\n console.log('sending socket event: ', data.type)\n if (currentSession.socketDisconnected || !currentSession.socket) {\n return\n }\n currentSession.socket.send(JSON.stringify({ ...data, eventId: data.eventId || uuidv7() }))\n}\n\n/**\n * Send data through socket and wait for acknowledgment\n * @param {Object} data\n * @returns {Promise<Object>}\n */\nexport function sendWithAck(data) {\n return new Promise((resolve, reject) => {\n if (!currentSession.socket) {\n console.error('sendWithAck: socket instance not found or not connected')\n reject(new Error('Socket instance not found or not connected'))\n return\n }\n\n const autoRejectTimeout = setTimeout(() => {\n reject(new Error('Timeout'))\n }, 5000)\n\n const eventId = data.eventId || uuidv7()\n\n const onMessage = (event) => {\n const eventData = JSON.parse(event.data)\n if (eventData.eventId === eventId) {\n clearTimeout(autoRejectTimeout)\n currentSession.socket.removeEventListener('message', onMessage)\n if (eventData.data) {\n resolve(eventData.data)\n } else {\n reject(new Error(eventData.error?.message ?? 'Unknown error'))\n }\n }\n }\n currentSession.socket.addEventListener('message', onMessage)\n currentSession.socket.send(JSON.stringify({ ...data, eventId }))\n })\n}\n\n/**\n * Handle socket event\n * @param {Object} event\n */\nfunction handleSocketEvent(event) {\n console.log('received socket event: ', event.type)\n\n switch (event.type) {\n case 'pong': {\n if (currentSession.socketDisconnected) {\n handleSocketConnected()\n }\n if (currentSession.socketDisconnectedTimeout) {\n clearTimeout(currentSession.socketDisconnectedTimeout)\n }\n currentSession.socketDisconnectedTimeout = setTimeout(() => {\n console.log('---- socket ping timeout ----')\n handleSocketDisconnected()\n }, PING_INTERVAL + 1000)\n break\n }\n case SocketEvents.TYPING: {\n toggleTypingStatus(true)\n break\n }\n case SocketEvents.TYPING_STOP: {\n toggleTypingStatus(false)\n break\n }\n case SocketEvents.MESSAGE: {\n const { eventId, data } = event\n if (!eventId) {\n addMessage({\n ...data,\n done: true,\n timestamp: new Date().toISOString()\n })\n }\n break\n }\n case SocketEvents.END: {\n disconnect()\n break\n }\n default:\n break\n }\n}\n\n/**\n * Reconnect to socket\n */\nexport function reconnect() {\n if (currentSession.socket) {\n send({ type: 'ping' })\n }\n}\n\n/**\n * Disconnect socket\n */\nexport function disconnect() {\n console.log('Disconnecting socket')\n if (currentSession.socket) {\n currentSession.socket.close(1000)\n }\n currentSession.previouslyConnected = false\n clearAllTimeouts()\n currentSession.socket = null\n setTransport('sse')\n}\n\n/**\n * Check if socket is connected\n * @returns {boolean}\n */\nexport function isConnected() {\n return (\n currentSession.socket !== null &&\n currentSession.socket.readyState === WebSocket.OPEN &&\n !currentSession.socketDisconnected\n )\n}\n\n/**\n * Check if socket is disconnected\n * @returns {boolean}\n */\nexport function isDisconnected() {\n return currentSession.socketDisconnected\n}\n","/**\n * Chat Service for Chat SDK\n * Handles real-time chat functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source'\nimport { getSession, authenticate } from './http.js'\nimport { getDeviceId, getSseEndpoint, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\nimport {\n connectSocket,\n send as socketSend,\n disconnect as disconnectSocket,\n isConnected as isSocketConnected\n} from './socket.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added\n * @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n * @property {(transport: TransportType) => void} [onTransportUpdate] - Called when transport type changes\n * @property {(control: 'agent' | 'human') => void} [onControlUpdate] - Called when control changes between agent and human\n */\n\n/**\n * @typedef {'sse' | 'socket'} TransportType\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\n * @property {TransportType} transport\n */\n\n/**\n * Create a new chat session\n * @param {ChatCallbacks} [callbacks={}]\n * @returns {ChatSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n credentials: undefined,\n authenticated: false,\n configData: undefined,\n sessionId: undefined,\n requestId: undefined,\n sseUrl: undefined,\n abortController: undefined,\n lastStreamId: undefined,\n messages: [],\n callbacks,\n transport: 'sse',\n control: 'agent'\n }\n}\n\n/** @type {ChatSession} */\nlet currentSession = createSession()\n\n/**\n * Set callbacks for the current session\n * @param {ChatCallbacks} callbacks\n */\nexport function setCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Initialize the chat session\n * @param {Object} credentials - Credentials for the chat\n */\nexport function initialize(credentials) {\n console.log('Initializing chat...', credentials)\n currentSession.credentials = credentials\n if (credentials.token) {\n currentSession.authenticated = true\n }\n}\n\n/**\n * Get current chat session credentials\n * @returns {{ endpoint: string, apiKey: string } | undefined}\n */\nexport function getCredentials() {\n return currentSession.credentials\n}\n\n/**\n * Update the session ID and notify via callback\n * @param {string} sessionId - The new session ID\n */\nexport function updateSessionId(sessionId) {\n if (sessionId && sessionId !== currentSession.sessionId) {\n currentSession.sessionId = sessionId\n currentSession.callbacks.onSessionUpdate?.(sessionId)\n }\n}\n\n/**\n * Initiate a new chat session or resume an existing one\n * @param {Object} credentials - Credentials for the chat\n * @param {Object} payload - Payload for the chat. It contains sessionId (optional)\n * @param {string} [payload.sessionId] - Optional session ID to resume\n * @returns {Promise<{ sessionId: string, messages: Array }>}\n */\nexport async function startChat(payload = {}) {\n try {\n console.log('startChat: ', payload, currentSession)\n\n let configData = null\n if (!currentSession.authenticated) {\n configData = await authenticate(currentSession.credentials)\n currentSession.authenticated = true\n currentSession.configData = configData\n } else {\n configData = currentSession.configData\n }\n\n let messages = []\n let control = 'agent'\n\n if (payload.sessionId) {\n const session = await getSession(payload.sessionId)\n messages = session.messages\n control = session.control || 'agent'\n }\n\n const searchParams = new URLSearchParams()\n if (!currentSession.credentials.token) {\n searchParams.set('externalId', getExternalId()) // externalId is needed only for public urls, not for internal chat (where token is provided)\n }\n currentSession.sseUrl = `${getSseEndpoint(\n currentSession.credentials.endpoint\n )}?${searchParams.toString()}`\n currentSession.sessionId = payload.sessionId\n currentSession.messages = messages\n currentSession.control = control\n\n if (control === 'human') {\n // Connect to SSE for incoming messages when control is human\n sendMessage({ text: '', html: '' }).catch(() => {})\n }\n\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\n control,\n configData\n }\n } catch (error) {\n console.error(`Failed to start chat: ${error.message}`)\n cleanup()\n throw error\n }\n}\n\n/**\n * Disconnect from the current chat session\n */\nexport function disconnect() {\n cleanup()\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.abortController) {\n currentSession.abortController.abort()\n }\n disconnectSocket()\n\n const { callbacks, credentials } = currentSession\n currentSession = createSession(callbacks)\n currentSession.credentials = credentials\n\n console.log('Chat session cleaned up')\n}\n\nexport function getExternalId() {\n if (currentSession.credentials?.externalId) {\n return currentSession.credentials.externalId\n }\n return getDeviceId()\n}\n\n/**\n * Add a message to the chat\n * @param {Object} message - The message object to add\n */\nexport function addMessage(message) {\n currentSession.messages = [...currentSession.messages, message]\n currentSession.callbacks.onMessageAdd?.(message)\n}\n\nexport function toggleTypingStatus(isTyping) {\n currentSession.callbacks.onTyping?.(isTyping)\n}\n\n/**\n * Set the transport type\n * @param {TransportType} transport\n */\nexport function setTransport(transport) {\n console.log('Setting transport to:', transport)\n currentSession.transport = transport\n currentSession.callbacks.onTransportUpdate?.(transport)\n}\n\n/**\n * Get current transport type\n * @returns {TransportType}\n */\nexport function getTransport() {\n return currentSession.transport\n}\n\n/**\n * Send a message in the current chat session\n * @param {{ text: string, html?: string }} message\n * @returns {Promise<string>}\n */\nexport function sendMessage({ text, html, context, attachments }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n const isEmpty = !text && !html\n\n // Add user message only if there's content\n if (!isEmpty) {\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString(),\n attachments\n }\n addMessage(userMessage)\n await sleep(200)\n }\n\n // If transport is socket and socket is connected, use socket\n if (currentSession.transport === 'socket' && isSocketConnected()) {\n socketSend({\n type: 'message',\n data: {\n text,\n html\n }\n })\n resolve(currentSession.sessionId)\n return\n }\n\n if (currentSession.control === 'agent') {\n const loadingMessage = {\n role: MESSAGE_ROLES.ASSISTANT,\n text: '',\n loading: true\n }\n addMessage(loadingMessage)\n }\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: isEmpty\n ? undefined\n : JSON.stringify({\n message: text,\n html,\n context,\n attachments\n }),\n signal: currentSession.abortController.signal,\n openWhenHidden: true,\n onopen: async (response) => {\n if (!response.ok) {\n console.error('Failed to send message bad response: ', response)\n throw new Error('Failed to send message')\n }\n },\n onmessage: (response) => {\n // console.log('Event: ', response)\n const data = JSON.parse(response.data)\n\n if (response.event === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n console.log('Connected: ', data)\n if (data.control) {\n currentSession.control = data.control\n currentSession.callbacks.onControlUpdate?.(data.control)\n }\n } else if (response.event === 'upgrade_to_websocket') {\n console.log('Upgrade to websocket: ', data)\n connectSocket({\n sessionId: currentSession.sessionId,\n requestId: data.requestId\n })\n } else if (response.event === 'update') {\n if (data.control) {\n currentSession.control = data.control\n currentSession.callbacks.onControlUpdate?.(data.control)\n }\n } else if (data.error) {\n const errorMessage =\n data.error && typeof data.error === 'string'\n ? data.error\n : 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: errorMessage\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(new Error(errorMessage))\n } else if (response.event === 'done') {\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n resolve(currentSession.sessionId)\n } else if (data.message !== undefined) {\n // If role is supervisor, treat it as a new message\n if (data.role === MESSAGE_ROLES.SUPERVISOR) {\n const supervisorMessage = {\n role: MESSAGE_ROLES.SUPERVISOR,\n text: data.message,\n sources: data.sources,\n done: true\n }\n addMessage(supervisorMessage)\n resolve(currentSession.sessionId)\n\n // Store session info for reuse\n currentSession.sessionId = data.sessionId ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n return\n }\n\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n const newBotMessage = {\n role: MESSAGE_ROLES.ASSISTANT,\n text: '',\n loading: true\n }\n addMessage(newBotMessage)\n }\n }\n\n // Update the last message with new content\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n text: (lastMsg.text || '') + data.message,\n sources: data.sources,\n done: data.done ?? lastMsg.done\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n\n if (data.done) {\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.sessionId ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n }\n },\n onerror: (error) => {\n throw error // Rethrow to stop retries\n },\n openWhenHidden: true\n })\n } catch (error) {\n console.error('Failed to send message: ', error)\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: lastMsg.done ? undefined : error.message || errorMessage,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(error)\n }\n })()\n })\n}\n","/**\n * API Service for Chat SDK\n * Handles all HTTP requests without depending on external state\n */\n\nimport { getCredentials, getExternalId } from './chat.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n/**\n * Authenticate with the chat service\n * @param {{ endpoint: string }} credentials\n * @returns {Promise<object>} Authentication response data\n */\nexport async function authenticate(payload) {\n const { endpoint } = payload\n const url = `${endpoint}/config`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n const errorPayload = await response.json()\n throw new Error(errorPayload?.error || AUTHENTICATION_ERROR)\n }\n\n const res = await response.json()\n const data = res.data\n\n return data\n}\n\n/**\n * Get chat history for the current device\n * @returns {Promise<{ sessions: Array }>}\n */\nexport async function getHistory() {\n const queryParams = new URLSearchParams({\n externalId: getExternalId()\n })\n const response = await fetchRequest(`/sessions?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load history, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Get session data (control and history) for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ control: string, messages: Array }>}\n */\nexport async function getSession(sessionId) {\n const queryParams = new URLSearchParams({\n sessionId\n })\n const response = await fetchRequest(`/session?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load messages, please try again later')\n }\n\n const data = await response.json()\n const control = data?.control\n const messages = (data?.history ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.ASSISTANT // for youtube video messages, role is \"system\" from backend, we need to make it \"assistant\"\n : msg.role,\n timestamp: msg.timestamp,\n video: msg.youtubeVideo,\n channel: msg.channel,\n done: true\n }))\n\n return { control, messages }\n}\n\n/**\n * Upload attachment with progress tracking\n * @param {File} file\n * @param {Function} onProgress - callback (percentComplete, loaded, total)\n * @param {Function} onComplete - callback (error, response)\n * @returns {XMLHttpRequest} xhr object to allow abort\n */\nexport function uploadAttachment(file, onProgress, onComplete) {\n const credentials = getCredentials()\n const { endpoint, token } = credentials || {}\n\n if (!endpoint) {\n const error = new Error(INITIALIZATION_ERROR)\n if (onComplete) {\n onComplete(error, null)\n }\n return null\n }\n\n const xhr = new XMLHttpRequest()\n const formData = new FormData()\n formData.append('file', file)\n\n const uploadUrl = `${endpoint}/upload`\n\n xhr.open('POST', uploadUrl)\n if (token) {\n xhr.setRequestHeader('Authorization', `Bearer ${token}`)\n }\n\n // Upload progress callback\n if (onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n const percentComplete = (event.loaded / event.total) * 100\n onProgress(percentComplete, event.loaded, event.total)\n }\n })\n }\n\n // Upload complete callback\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const response = JSON.parse(xhr.responseText)\n if (onComplete) {\n onComplete(null, response)\n }\n } catch (error) {\n if (onComplete) {\n onComplete(new Error('Failed to parse response'), null)\n }\n }\n } else {\n if (onComplete) {\n onComplete(new Error(`Upload failed with status ${xhr.status}`), null)\n }\n }\n })\n\n xhr.addEventListener('error', () => {\n if (onComplete) {\n onComplete(new Error('Network error during upload'), null)\n }\n })\n\n xhr.addEventListener('abort', () => {\n if (onComplete) {\n onComplete(new Error('Upload aborted'), null)\n }\n })\n\n xhr.send(formData)\n\n return xhr\n}\n\n/**\n * Delete attachment by mediaId\n * @param {string} mediaId\n * @returns {Promise<{ success: boolean }>}\n */\nexport async function deleteAttachment(mediaId) {\n const credentials = getCredentials()\n const { endpoint, token } = credentials || {}\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const deleteUrl = `${endpoint}/delete/${mediaId}`\n\n const headers = {}\n if (token) {\n headers.Authorization = `Bearer ${token}`\n }\n\n const response = await fetch(deleteUrl, {\n method: 'DELETE',\n headers\n })\n\n if (!response.ok) {\n throw new Error(`Delete failed with status ${response.status}`)\n }\n\n return { success: true }\n}\n\n/**\n * Get attachment URL by mediaId\n * @param {string} mediaId\n * @returns {string} Full URL to the attachment\n */\nexport function getAttachment(mediaId) {\n const credentials = getCredentials()\n const { endpoint } = credentials || {}\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n return `${endpoint}/upload/${mediaId}`\n}\n\n/**\n * Internal fetch request helper\n * @param {string} pathname\n * @param {string} method\n * @param {object|null} body\n * @returns {Promise<Response>}\n */\nasync function fetchRequest(pathname, method = 'GET', body = null) {\n const credentials = getCredentials()\n\n const { endpoint, token } = credentials || {}\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (token) {\n headers.Authorization = `Bearer ${token}`\n }\n\n return fetch(url, {\n headers,\n method,\n body: body ? JSON.stringify(body) : null\n })\n}\n","/**\n * Socket Service for Call SDK\n * Handles WebRTC call functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { getCallServerEndpoint } from './utils.js'\nimport { getCredentials, getExternalId, updateSessionId } from './chat.js'\n\n/**\n * @typedef {Object} CallCallbacks\n * @property {(status: string) => void} [onCallStatus] - Called when call status changes\n * @property {(error: string | null) => void} [onCallError] - Called when call error occurs\n */\n\n/**\n * @typedef {Object} CallSession\n * @property {string} [sessionId]\n * @property {WebSocket} [socket]\n * @property {RTCPeerConnection} [peerConnection]\n * @property {MediaStream} [localStream]\n * @property {MediaStream} [remoteStream]\n * @property {HTMLAudioElement} [remoteAudio]\n * @property {boolean} isMuted\n * @property {string} callStatus\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {number} pingCount\n * @property {number | null} lastPongTime\n * @property {CallCallbacks} callbacks\n * @property {string[]} localIceCandidates - Queued local ICE candidates to send after remote description is set\n * @property {string[]} pendingRemoteIceCandidates - Queued remote ICE candidates to add after remote description is set\n */\n\n/**\n * Create a new call session\n * @param {CallCallbacks} [callbacks={}]\n * @returns {CallSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n sessionId: undefined,\n socket: null,\n peerConnection: null,\n localStream: null,\n remoteStream: null,\n remoteAudio: null,\n isMuted: false,\n callStatus: 'disconnected',\n pingInterval: null,\n pingCount: 0,\n lastPongTime: null,\n callbacks,\n localIceCandidates: [],\n pendingRemoteIceCandidates: []\n }\n}\n\n/** @type {CallSession} */\nlet currentSession = createSession()\n\nconst rtcConfig = {\n iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }]\n}\n\n/**\n * Set callbacks for the current session\n * @param {CallCallbacks} callbacks\n */\nexport function setCallCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n\n if (currentSession.remoteStream) {\n currentSession.remoteStream = null\n }\n\n if (currentSession.remoteAudio) {\n currentSession.remoteAudio.srcObject = null\n if (currentSession.remoteAudio.parentNode) {\n currentSession.remoteAudio.parentNode.removeChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio = null\n }\n\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n\n stopPingInterval()\n\n const callbacks = currentSession.callbacks\n currentSession = createSession(callbacks)\n\n console.log('Call session cleaned up')\n}\n\n/**\n * Update call status and notify callback\n * @param {string} status\n */\nfunction setCallStatus(status) {\n currentSession.callStatus = status\n currentSession.callbacks.onCallStatus?.(status)\n}\n\n/**\n * Update call error and notify callback\n * @param {string | null} error\n */\nfunction setCallError(error) {\n currentSession.callbacks.onCallError?.(error)\n}\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n currentSession.pingCount++\n const pingMessage = {\n type: 'ping',\n timestamp: Date.now(),\n count: currentSession.pingCount\n }\n sendEvent(pingMessage)\n console.log(`Sending keep-alive ping #${currentSession.pingCount}`)\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, 10000)\n}\n\n/**\n * Handle pong response\n */\nfunction handlePong() {\n currentSession.lastPongTime = Date.now()\n console.log(`Received pong #${currentSession.pingCount}`)\n}\n\n/**\n * Send event through socket\n * @param {Object} payload\n */\nfunction sendEvent(payload) {\n if (!currentSession.socket) {\n console.error('Failed to send event: no socket instance')\n return\n }\n if (currentSession.socket.readyState !== WebSocket.OPEN) {\n console.error('Failed to send event: socket state not open ', payload)\n return\n }\n\n currentSession.socket.send(JSON.stringify(payload))\n}\n\n/**\n * Get user media\n */\nasync function getUserMedia() {\n try {\n currentSession.localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n })\n console.log('Got audio media')\n } catch (error) {\n console.error(`Failed to get audio media: ${error.message}`)\n throw error\n }\n}\n\n/**\n * Create peer connection\n */\nfunction createPeerConnection() {\n currentSession.peerConnection = new RTCPeerConnection(rtcConfig)\n\n currentSession.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateJson = JSON.stringify(event.candidate)\n // Queue local ICE candidates until remote description is set\n if (currentSession.peerConnection && currentSession.peerConnection.remoteDescription) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent ICE candidate immediately')\n } else {\n currentSession.localIceCandidates.push(candidateJson)\n console.log('Queued local ICE candidate')\n }\n }\n }\n\n currentSession.peerConnection.ontrack = (event) => {\n console.log('Received remote audio stream')\n currentSession.remoteStream = event.streams[0]\n\n if (!currentSession.remoteAudio) {\n currentSession.remoteAudio = document.createElement('audio')\n currentSession.remoteAudio.autoplay = true\n currentSession.remoteAudio.controls = false\n document.body.appendChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio.srcObject = currentSession.remoteStream\n // explicitly kick off playback and catch any policy/gesture errors\n currentSession.remoteAudio\n .play()\n .then(() => console.log('🔊 remote audio playing'))\n .catch((err) => console.error('❌ playback error:', err))\n }\n\n currentSession.peerConnection.onconnectionstatechange = () => {\n const newState = currentSession.peerConnection.connectionState\n console.log(`Connection state: ${newState}`)\n\n if (newState === 'connected') {\n setCallStatus('connected')\n } else if (newState === 'disconnected' || newState === 'closed') {\n setCallStatus('disconnected')\n disconnectCall()\n }\n }\n\n currentSession.peerConnection.oniceconnectionstatechange = () => {\n console.log(`ICE connection state: ${currentSession.peerConnection.iceConnectionState}`)\n }\n}\n\n/**\n * Connect socket\n * @param {{ sessionId?: string }} payload\n */\nfunction connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n // Extract hostname from endpoint\n const socketEndpoint = getCallServerEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = (event) => {\n console.log('Socket connection established: ', event)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const data = JSON.parse(event.data)\n handleCallServerEvent(data)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error: ', error)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n reject(error)\n }\n\n currentSession.socket.onclose = (event) => {\n console.log('Socket connection closed: ', event)\n stopPingInterval()\n }\n })\n}\n\n/**\n * Handle call server event\n * @param {Object} action\n */\nfunction handleCallServerEvent(action) {\n console.log('Handling socket server event: ', action)\n\n switch (action.type) {\n case 'pong':\n handlePong()\n break\n\n case 'answer':\n handleAnswer(action.data)\n break\n\n case 'connected':\n handleConnected(action.data)\n break\n\n case 'ice':\n handleIceCandidate(action.data)\n break\n\n case 'renegotiationOffer':\n handleRenegotiationOffer(action.data)\n break\n case 'end':\n disconnectCall()\n break\n case 'error':\n setCallStatus('error')\n setCallError(action.error || 'Unable to connect voice')\n break\n\n default:\n console.log('Unknown call event type: ', action.type)\n break\n }\n}\n\n/**\n * Handle connected event\n * @param {Object} data\n */\nfunction handleConnected(data) {\n console.log('Received connected event')\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\n}\n\n/**\n * Handle answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n if (currentSession.peerConnection) {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: data.sdp\n })\n console.log('Setting remote description answer: ', answer)\n await currentSession.peerConnection.setRemoteDescription(answer)\n console.log('Remote description set')\n\n // Send all queued local ICE candidates\n for (const candidateJson of currentSession.localIceCandidates) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent queued local ICE candidate')\n }\n currentSession.localIceCandidates = []\n\n // Process any pending remote ICE candidates\n for (const candidateJson of currentSession.pendingRemoteIceCandidates) {\n try {\n const candidate = new RTCIceCandidate(JSON.parse(candidateJson))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added pending remote ICE candidate')\n } catch (err) {\n console.error(`Failed to add pending ICE candidate: ${err.message}`)\n }\n }\n currentSession.pendingRemoteIceCandidates = []\n }\n } catch (error) {\n console.error(`Failed to handle answer: ${error.message}`)\n }\n}\n\n/**\n * Handle ICE candidate\n * @param {Object} data\n */\nasync function handleIceCandidate(data) {\n try {\n if (currentSession.peerConnection) {\n // Check if remote description is set\n if (!currentSession.peerConnection.remoteDescription) {\n // Queue the candidate until remote description is set\n currentSession.pendingRemoteIceCandidates.push(data.candidate)\n console.log('Queued remote ICE candidate - remote description not set')\n return\n }\n const candidate = new RTCIceCandidate(JSON.parse(data.candidate))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added ICE candidate')\n }\n } catch (error) {\n console.error(`Failed to add ICE candidate: ${error.message}`)\n }\n}\n\n/**\n * Handle renegotiation offer\n * @param {Object} data\n */\nasync function handleRenegotiationOffer(data) {\n try {\n console.log('Received renegotiation offer')\n\n if (currentSession.peerConnection) {\n const offer = new RTCSessionDescription({\n type: 'offer',\n sdp: data.sdp\n })\n console.log('Setting remote description offer: ', offer)\n await currentSession.peerConnection.setRemoteDescription(offer)\n console.log('Remote description set')\n\n const answer = await currentSession.peerConnection.createAnswer()\n await currentSession.peerConnection.setLocalDescription(answer)\n\n sendEvent({\n type: 'renegotiationAnswer',\n data: {\n sdp: answer.sdp\n }\n })\n }\n } catch (error) {\n console.error(`Failed to handle renegotiation offer: ${error.message}`)\n }\n}\n\n/**\n * Start a call\n * @param {{ sessionId?: string }} payload\n */\nexport async function startCall(payload = {}) {\n try {\n if (currentSession.callStatus === 'connecting' || currentSession.callStatus === 'connected') {\n console.log(`Call already in ${currentSession.callStatus} state`)\n return\n }\n\n console.log('Starting audio call...')\n setCallStatus('connecting')\n setCallError(null)\n\n currentSession.sessionId = payload.sessionId\n\n await getUserMedia()\n\n createPeerConnection()\n\n currentSession.localStream.getTracks().forEach((track) => {\n currentSession.peerConnection.addTrack(track, currentSession.localStream)\n console.log(`Added ${track.kind} track`)\n })\n await connectSocket(payload)\n const offer = await currentSession.peerConnection.createOffer()\n await currentSession.peerConnection.setLocalDescription(offer)\n\n sendEvent({\n type: 'offer',\n data: {\n sdp: offer.sdp\n }\n })\n\n console.log('Call initiated successfully')\n } catch (error) {\n console.log('error: ', error)\n console.error(`Failed to start call: ${error.message}`)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n cleanup()\n }\n}\n\n/**\n * Disconnect call\n */\nexport function disconnectCall() {\n sendEvent({\n type: 'end'\n })\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n setCallStatus('disconnected')\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n cleanup()\n}\n\n/**\n * Toggle mute\n * @returns {boolean}\n */\nexport function toggleMute() {\n if (currentSession.localStream) {\n const audioTrack = currentSession.localStream.getAudioTracks()[0]\n if (audioTrack) {\n audioTrack.enabled = !audioTrack.enabled\n currentSession.isMuted = !audioTrack.enabled\n console.log(`Audio ${currentSession.isMuted ? 'muted' : 'unmuted'}`)\n return currentSession.isMuted\n }\n }\n return false\n}\n\n/**\n * Get local stream\n * @returns {MediaStream | null}\n */\nexport function getLocalStream() {\n return currentSession.localStream\n}\n\n/**\n * Get inbound audio energy\n * @returns {Promise<number>}\n */\nexport function getInboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'inbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no inbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n\n/**\n * Get outbound audio energy (not implemented in original, but may be needed)\n * @returns {Promise<number>}\n */\nexport function getOutboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'outbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no outbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n"],"names":["uuidv7","timestamp","bytes","hex","b","getDeviceId","deviceId","sleep","ms","resolve","getSseEndpoint","baseUrl","sseEndpoint","url","e","getSocketEndpoint","socketEndpoint","getCallServerEndpoint","MESSAGE_ROLES","PING_INTERVAL","SOCKET_TIMEOUT","SocketEvents","createSession","currentSession","stopPingInterval","startPingInterval","send","clearAllTimeouts","handleSocketConnected","setTransport","handleSocketDisconnected","connectSocket","payload","fulfill","reject","credentials","getCredentials","externalId","getExternalId","queryParams","socketUrl","event","message","handleSocketEvent","error","ws","addMessage","data","toggleTypingStatus","eventId","disconnect","isConnected","callbacks","setCallbacks","initialize","updateSessionId","sessionId","startChat","configData","authenticate","messages","control","session","getSession","searchParams","sendMessage","cleanup","disconnectSocket","isTyping","transport","text","html","context","attachments","isEmpty","userMessage","isSocketConnected","socketSend","loadingMessage","headers","fetchEventSource","response","errorMessage","lastIndex","updatedMsg","msg","index","supervisorMessage","_i","_j","newBotMessage","lastMsg","_k","_n","_o","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","endpoint","errorPayload","getHistory","fetchRequest","_a","uploadAttachment","file","onProgress","onComplete","token","xhr","formData","uploadUrl","percentComplete","deleteAttachment","mediaId","deleteUrl","getAttachment","pathname","method","body","rtcConfig","setCallCallbacks","track","setCallStatus","status","_b","setCallError","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","handleCallServerEvent","action","handleAnswer","handleConnected","handleIceCandidate","handleRenegotiationOffer","answer","candidate","offer","startCall","toggleMute","audioTrack","getLocalStream","getInboundAudioEnergy","stats","report","getOutboundAudioEnergy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.js","../src/socket.js","../src/chat.js","../src/http.js","../src/call.js"],"sourcesContent":["/**\n * Utility functions for the Chat SDK\n */\n\nexport function uuidv7() {\n const timestamp = Date.now()\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n\n // Set timestamp (48 bits)\n bytes[0] = (timestamp >> 40) & 0xff\n bytes[1] = (timestamp >> 32) & 0xff\n bytes[2] = (timestamp >> 24) & 0xff\n bytes[3] = (timestamp >> 16) & 0xff\n bytes[4] = (timestamp >> 8) & 0xff\n bytes[5] = timestamp & 0xff\n\n // Set version 7 (0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70\n\n // Set variant (10xx)\n bytes[8] = (bytes[8] & 0x3f) | 0x80\n\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20\n )}-${hex.slice(20)}`\n}\n\nexport function getDeviceId() {\n if (localStorage.getItem('chatDeviceId')) {\n return localStorage.getItem('chatDeviceId')\n }\n\n const deviceId = uuidv7()\n localStorage.setItem('chatDeviceId', deviceId)\n return deviceId\n}\n\nexport function parseJwt(token) {\n try {\n const base64Url = token.split('.')[1]\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join('')\n )\n\n return JSON.parse(jsonPayload)\n } catch {\n return null\n }\n}\n\nexport async function sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function getSseEndpoint(baseUrl) {\n let sseEndpoint\n try {\n const url = new URL(baseUrl)\n sseEndpoint = `https://${url.hostname}${url.pathname}/sse`\n } catch {\n console.error('SSE Invalid base URL: ', baseUrl)\n }\n return sseEndpoint\n}\n\nexport function getSocketEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/wss`\n } catch {\n console.error('Socket Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n\nexport function getCallServerEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/audio`\n } catch {\n console.error('getCallServerEndpoint: Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n","/**\n * Constants for the Chat SDK\n */\n\nexport const MESSAGE_ROLES = {\n ASSISTANT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n SUPERVISOR: 'supervisor', // this is human supervisor (ex. Samespace Dock agent, or Resolve human agent)\n SYSTEM: 'system' // this is system message, for ex \"Agent joined\" / \"Agent left\"\n}\n","/**\n * Socket Service for Chat SDK\n * Handles WebSocket connection for real-time chat functionality\n */\n\nimport { getSocketEndpoint, uuidv7 } from './utils.js'\nimport {\n getCredentials,\n getExternalId,\n addMessage,\n toggleTypingStatus,\n setTransport\n} from './chat.js'\n\nconst PING_INTERVAL = 10000\nconst SOCKET_TIMEOUT = 5000\n\n/**\n * @typedef {Object} SocketSession\n * @property {WebSocket} [socket]\n * @property {boolean} previouslyConnected\n * @property {boolean} socketDisconnected\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {NodeJS.Timeout} [socketDisconnectedTimeout]\n * @property {NodeJS.Timeout} [socketConnectionTimeout]\n */\n\n/**\n * Socket Events\n */\nexport const SocketEvents = {\n MESSAGE: 'message',\n TYPING: 'typing',\n TYPING_STOP: 'typingOff',\n END: 'end'\n}\n\n/**\n * Create a new socket session\n * @returns {SocketSession}\n */\nfunction createSession() {\n return {\n socket: null,\n previouslyConnected: false,\n socketDisconnected: false,\n pingInterval: null,\n socketDisconnectedTimeout: null,\n socketConnectionTimeout: null\n }\n}\n\n/** @type {SocketSession} */\nlet currentSession = createSession()\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n send({ type: 'ping' })\n console.log('Sending keep-alive ping')\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, PING_INTERVAL)\n}\n\n/**\n * Clear all timeouts\n */\nfunction clearAllTimeouts() {\n stopPingInterval()\n if (currentSession.socketDisconnectedTimeout) {\n clearTimeout(currentSession.socketDisconnectedTimeout)\n currentSession.socketDisconnectedTimeout = null\n }\n if (currentSession.socketConnectionTimeout) {\n clearTimeout(currentSession.socketConnectionTimeout)\n currentSession.socketConnectionTimeout = null\n }\n}\n\n/**\n * Handle socket connected state\n */\nfunction handleSocketConnected() {\n console.log('handleSocketConnected')\n currentSession.socketDisconnected = false\n setTransport('socket')\n}\n\n/**\n * Handle socket disconnected state\n */\nfunction handleSocketDisconnected() {\n console.log('handleSocketDisconnected')\n currentSession.socketDisconnected = true\n setTransport('sse')\n}\n\n/**\n * Connect to socket\n * @param {{ token: string, sessionId?: string }} payload\n * @returns {Promise<boolean>}\n */\nexport function connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n const socketEndpoint = getSocketEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (payload.requestId) {\n queryParams.set('requestId', payload.requestId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = () => {\n console.log('-------- socket connected --------')\n currentSession.previouslyConnected = true\n handleSocketConnected()\n send({ type: 'ping' })\n clearTimeout(currentSession.socketConnectionTimeout)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const message = JSON.parse(event.data)\n handleSocketEvent(message)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error:', error)\n setTransport('sse')\n reject(error)\n }\n\n currentSession.socket.onclose = (ws) => {\n console.log('-------- socket disconnected --------: ', ws.code, ws.reason)\n\n if (ws.target === currentSession.socket) {\n if (ws.code === 1006) {\n // abnormal closure\n if (currentSession.previouslyConnected) {\n handleSocketDisconnected()\n } else {\n addMessage({\n errorText: 'Unable to establish connection',\n done: true,\n timestamp: new Date().toISOString()\n })\n }\n clearTimeout(currentSession.socketConnectionTimeout)\n }\n\n currentSession.socket = null\n clearAllTimeouts()\n }\n }\n\n if (!currentSession.previouslyConnected) {\n currentSession.socketConnectionTimeout = setTimeout(() => {\n console.error('Socket connection timed out')\n addMessage({\n errorText: 'Unable to establish connection',\n done: true,\n timestamp: new Date().toISOString()\n })\n reject(new Error('Socket connection timed out'))\n }, SOCKET_TIMEOUT)\n }\n })\n}\n\n/**\n * Send data through socket\n * @param {Object} data\n */\nexport function send(data) {\n console.log('sending socket event: ', data.type)\n if (currentSession.socketDisconnected || !currentSession.socket) {\n return\n }\n currentSession.socket.send(JSON.stringify({ ...data, eventId: data.eventId || uuidv7() }))\n}\n\n/**\n * Send data through socket and wait for acknowledgment\n * @param {Object} data\n * @returns {Promise<Object>}\n */\nexport function sendWithAck(data) {\n return new Promise((resolve, reject) => {\n if (!currentSession.socket) {\n console.error('sendWithAck: socket instance not found or not connected')\n reject(new Error('Socket instance not found or not connected'))\n return\n }\n\n const autoRejectTimeout = setTimeout(() => {\n reject(new Error('Timeout'))\n }, 5000)\n\n const eventId = data.eventId || uuidv7()\n\n const onMessage = (event) => {\n const eventData = JSON.parse(event.data)\n if (eventData.eventId === eventId) {\n clearTimeout(autoRejectTimeout)\n currentSession.socket.removeEventListener('message', onMessage)\n if (eventData.data) {\n resolve(eventData.data)\n } else {\n reject(new Error(eventData.error?.message ?? 'Unknown error'))\n }\n }\n }\n currentSession.socket.addEventListener('message', onMessage)\n currentSession.socket.send(JSON.stringify({ ...data, eventId }))\n })\n}\n\n/**\n * Handle socket event\n * @param {Object} event\n */\nfunction handleSocketEvent(event) {\n console.log('received socket event: ', event.type)\n\n switch (event.type) {\n case 'pong': {\n if (currentSession.socketDisconnected) {\n handleSocketConnected()\n }\n if (currentSession.socketDisconnectedTimeout) {\n clearTimeout(currentSession.socketDisconnectedTimeout)\n }\n currentSession.socketDisconnectedTimeout = setTimeout(() => {\n console.log('---- socket ping timeout ----')\n handleSocketDisconnected()\n }, PING_INTERVAL + 1000)\n break\n }\n case SocketEvents.TYPING: {\n toggleTypingStatus(true)\n break\n }\n case SocketEvents.TYPING_STOP: {\n toggleTypingStatus(false)\n break\n }\n case SocketEvents.MESSAGE: {\n const { eventId, data } = event\n if (!eventId) {\n addMessage({\n ...data,\n done: true,\n timestamp: new Date().toISOString()\n })\n }\n break\n }\n case SocketEvents.END: {\n disconnect()\n break\n }\n default:\n break\n }\n}\n\n/**\n * Reconnect to socket\n */\nexport function reconnect() {\n if (currentSession.socket) {\n send({ type: 'ping' })\n }\n}\n\n/**\n * Disconnect socket\n */\nexport function disconnect() {\n console.log('Disconnecting socket')\n if (currentSession.socket) {\n currentSession.socket.close(1000)\n }\n currentSession.previouslyConnected = false\n clearAllTimeouts()\n currentSession.socket = null\n setTransport('sse')\n}\n\n/**\n * Check if socket is connected\n * @returns {boolean}\n */\nexport function isConnected() {\n return (\n currentSession.socket !== null &&\n currentSession.socket.readyState === WebSocket.OPEN &&\n !currentSession.socketDisconnected\n )\n}\n\n/**\n * Check if socket is disconnected\n * @returns {boolean}\n */\nexport function isDisconnected() {\n return currentSession.socketDisconnected\n}\n","/**\n * Chat Service for Chat SDK\n * Handles real-time chat functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source'\nimport { getSession, authenticate } from './http.js'\nimport { getDeviceId, getSseEndpoint, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\nimport {\n connectSocket,\n send as socketSend,\n disconnect as disconnectSocket,\n isConnected as isSocketConnected\n} from './socket.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added\n * @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n * @property {(transport: TransportType) => void} [onTransportUpdate] - Called when transport type changes\n * @property {(control: 'agent' | 'human') => void} [onControlUpdate] - Called when control changes between agent and human\n */\n\n/**\n * @typedef {'sse' | 'socket'} TransportType\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\n * @property {TransportType} transport\n */\n\n/**\n * Create a new chat session\n * @param {ChatCallbacks} [callbacks={}]\n * @returns {ChatSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n credentials: undefined,\n authenticated: false,\n configData: undefined,\n sessionId: undefined,\n requestId: undefined,\n sseUrl: undefined,\n abortController: undefined,\n lastStreamId: undefined,\n messages: [],\n callbacks,\n transport: 'sse',\n control: 'agent'\n }\n}\n\n/** @type {ChatSession} */\nlet currentSession = createSession()\n\n/**\n * Set callbacks for the current session\n * @param {ChatCallbacks} callbacks\n */\nexport function setCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Initialize the chat session\n * @param {Object} credentials - Credentials for the chat\n */\nexport function initialize(credentials) {\n console.log('Initializing chat...', credentials)\n currentSession.credentials = credentials\n if (credentials.token) {\n currentSession.authenticated = true\n }\n}\n\n/**\n * Get current chat session credentials\n * @returns {{ endpoint: string, apiKey: string } | undefined}\n */\nexport function getCredentials() {\n return currentSession.credentials\n}\n\n/**\n * Update the session ID and notify via callback\n * @param {string} sessionId - The new session ID\n */\nexport function updateSessionId(sessionId) {\n if (sessionId && sessionId !== currentSession.sessionId) {\n currentSession.sessionId = sessionId\n currentSession.callbacks.onSessionUpdate?.(sessionId)\n }\n}\n\n/**\n * Initiate a new chat session or resume an existing one\n * @param {Object} credentials - Credentials for the chat\n * @param {Object} payload - Payload for the chat. It contains sessionId (optional)\n * @param {string} [payload.sessionId] - Optional session ID to resume\n * @returns {Promise<{ sessionId: string, messages: Array }>}\n */\nexport async function startChat(payload = {}) {\n try {\n console.log('startChat: ', payload, currentSession)\n\n let configData = null\n if (!currentSession.authenticated) {\n configData = await authenticate(currentSession.credentials)\n currentSession.authenticated = true\n currentSession.configData = configData\n } else {\n configData = currentSession.configData\n }\n\n let messages = []\n let control = 'agent'\n\n if (payload.sessionId) {\n const session = await getSession(payload.sessionId)\n messages = session.messages\n control = session.control || 'agent'\n }\n\n const searchParams = new URLSearchParams()\n if (!currentSession.credentials.token) {\n searchParams.set('externalId', getExternalId()) // externalId is needed only for public urls, not for internal chat (where token is provided)\n }\n currentSession.sseUrl = `${getSseEndpoint(\n currentSession.credentials.endpoint\n )}?${searchParams.toString()}`\n currentSession.sessionId = payload.sessionId\n currentSession.messages = messages\n currentSession.control = control\n\n if (control === 'human') {\n // Connect to SSE for incoming messages when control is human\n sendMessage({ text: '', html: '' }).catch(() => {})\n }\n\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\n control,\n configData\n }\n } catch (error) {\n console.error(`Failed to start chat: ${error.message}`)\n cleanup()\n throw error\n }\n}\n\n/**\n * Disconnect from the current chat session\n */\nexport function disconnect() {\n cleanup()\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.abortController) {\n currentSession.abortController.abort()\n }\n disconnectSocket()\n\n const { callbacks, credentials } = currentSession\n currentSession = createSession(callbacks)\n currentSession.credentials = credentials\n\n console.log('Chat session cleaned up')\n}\n\nexport function getExternalId() {\n if (currentSession.credentials?.externalId) {\n return currentSession.credentials.externalId\n }\n return getDeviceId()\n}\n\n/**\n * Add a message to the chat\n * @param {Object} message - The message object to add\n */\nexport function addMessage(message) {\n currentSession.messages = [...currentSession.messages, message]\n currentSession.callbacks.onMessageAdd?.(message)\n}\n\nexport function toggleTypingStatus(isTyping) {\n currentSession.callbacks.onTyping?.(isTyping)\n}\n\n/**\n * Set the transport type\n * @param {TransportType} transport\n */\nexport function setTransport(transport) {\n console.log('Setting transport to:', transport)\n currentSession.transport = transport\n currentSession.callbacks.onTransportUpdate?.(transport)\n}\n\n/**\n * Get current transport type\n * @returns {TransportType}\n */\nexport function getTransport() {\n return currentSession.transport\n}\n\n/**\n * Send a message in the current chat session\n * @param {{ text: string, html?: string }} message\n * @returns {Promise<string>}\n */\nexport function sendMessage({ text, html, context, attachments }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n const isEmpty = !text && !html && !attachments?.length\n\n // Add user message only if there's content\n if (!isEmpty) {\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString(),\n attachments\n }\n addMessage(userMessage)\n await sleep(200)\n }\n\n // If transport is socket and socket is connected, use socket\n if (currentSession.transport === 'socket' && isSocketConnected()) {\n socketSend({\n type: 'message',\n data: {\n text,\n html\n }\n })\n resolve(currentSession.sessionId)\n return\n }\n\n if (currentSession.control === 'agent') {\n const loadingMessage = {\n role: MESSAGE_ROLES.ASSISTANT,\n text: '',\n loading: true\n }\n addMessage(loadingMessage)\n }\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: isEmpty\n ? undefined\n : JSON.stringify({\n message: text,\n html,\n context,\n attachments\n }),\n signal: currentSession.abortController.signal,\n openWhenHidden: true,\n onopen: async (response) => {\n if (!response.ok) {\n console.error('Failed to send message bad response: ', response)\n throw new Error('Failed to send message')\n }\n },\n onmessage: (response) => {\n // console.log('Event: ', response)\n const data = JSON.parse(response.data)\n\n if (response.event === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n console.log('Connected: ', data)\n if (data.control) {\n currentSession.control = data.control\n currentSession.callbacks.onControlUpdate?.(data.control)\n }\n } else if (response.event === 'upgrade_to_websocket') {\n console.log('Upgrade to websocket: ', data)\n connectSocket({\n sessionId: currentSession.sessionId,\n requestId: data.requestId\n })\n } else if (response.event === 'update') {\n if (data.control) {\n currentSession.control = data.control\n currentSession.callbacks.onControlUpdate?.(data.control)\n }\n } else if (data.error) {\n const errorMessage =\n data.error && typeof data.error === 'string'\n ? data.error\n : 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: errorMessage\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(new Error(errorMessage))\n } else if (response.event === 'done') {\n console.log('Done: ', data)\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n resolve(currentSession.sessionId)\n } else if (data.message !== undefined || data.attachments?.length > 0) {\n // If role is supervisor, treat it as a new message\n if (data.role === MESSAGE_ROLES.SUPERVISOR) {\n const supervisorMessage = {\n role: MESSAGE_ROLES.SUPERVISOR,\n text: data.message,\n attachments: data.attachments,\n sources: data.sources,\n done: true\n }\n addMessage(supervisorMessage)\n resolve(currentSession.sessionId)\n\n // Store session info for reuse\n currentSession.sessionId = data.sessionId ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n return\n }\n\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n const newBotMessage = {\n role: MESSAGE_ROLES.ASSISTANT,\n text: '',\n loading: true\n }\n addMessage(newBotMessage)\n }\n }\n\n // Update the last message with new content\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n text: (lastMsg.text || '') + data.message,\n sources: data.sources,\n attachments: data.attachments,\n done: data.done ?? lastMsg.done\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n\n // Store session info for reuse\n currentSession.sessionId = data.sessionId ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n }\n },\n onerror: (error) => {\n throw error // Rethrow to stop retries\n },\n openWhenHidden: true\n })\n } catch (error) {\n console.error('Failed to send message: ', error)\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: lastMsg.done ? undefined : error.message || errorMessage,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(error)\n }\n })()\n })\n}\n","/**\n * API Service for Chat SDK\n * Handles all HTTP requests without depending on external state\n */\n\nimport { getCredentials, getExternalId } from './chat.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n/**\n * Authenticate with the chat service\n * @param {{ endpoint: string }} credentials\n * @returns {Promise<object>} Authentication response data\n */\nexport async function authenticate(payload) {\n const { endpoint } = payload\n const url = `${endpoint}/config`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n const errorPayload = await response.json()\n throw new Error(errorPayload?.error || AUTHENTICATION_ERROR)\n }\n\n const res = await response.json()\n const data = res.data\n\n return data\n}\n\n/**\n * Get chat history for the current device\n * @returns {Promise<{ sessions: Array }>}\n */\nexport async function getHistory() {\n const queryParams = new URLSearchParams({\n externalId: getExternalId()\n })\n const response = await fetchRequest(`/sessions?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load history, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Get session data (control and history) for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ control: string, messages: Array }>}\n */\nexport async function getSession(sessionId) {\n const queryParams = new URLSearchParams({\n sessionId\n })\n const response = await fetchRequest(`/session?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load messages, please try again later')\n }\n\n const data = await response.json()\n const control = data?.control\n const messages = (data?.history ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.ASSISTANT // for youtube video messages, role is \"system\" from backend, we need to make it \"assistant\"\n : msg.role,\n timestamp: msg.timestamp,\n video: msg.youtubeVideo,\n attachments: msg.attachments,\n channel: msg.channel,\n done: true\n }))\n\n return { control, messages }\n}\n\n/**\n * Upload attachment with progress tracking\n * @param {File} file\n * @param {Function} onProgress - callback (percentComplete, loaded, total)\n * @param {Function} onComplete - callback (error, response)\n * @returns {XMLHttpRequest} xhr object to allow abort\n */\nexport function uploadAttachment(file, onProgress, onComplete) {\n const credentials = getCredentials()\n const { endpoint, token } = credentials || {}\n\n if (!endpoint) {\n const error = new Error(INITIALIZATION_ERROR)\n if (onComplete) {\n onComplete(error, null)\n }\n return null\n }\n\n const xhr = new XMLHttpRequest()\n const formData = new FormData()\n formData.append('file', file)\n\n const uploadUrl = `${endpoint}/upload`\n\n xhr.open('POST', uploadUrl)\n if (token) {\n xhr.setRequestHeader('Authorization', `Bearer ${token}`)\n }\n\n // Upload progress callback\n if (onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n const percentComplete = (event.loaded / event.total) * 100\n onProgress(percentComplete, event.loaded, event.total)\n }\n })\n }\n\n // Upload complete callback\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const response = JSON.parse(xhr.responseText)\n if (onComplete) {\n onComplete(null, response)\n }\n } catch (error) {\n if (onComplete) {\n onComplete(new Error('Failed to parse response'), null)\n }\n }\n } else {\n if (onComplete) {\n onComplete(new Error(`Upload failed with status ${xhr.status}`), null)\n }\n }\n })\n\n xhr.addEventListener('error', () => {\n if (onComplete) {\n onComplete(new Error('Network error during upload'), null)\n }\n })\n\n xhr.addEventListener('abort', () => {\n if (onComplete) {\n onComplete(new Error('Upload aborted'), null)\n }\n })\n\n xhr.send(formData)\n\n return xhr\n}\n\n/**\n * Delete attachment by mediaId\n * @param {string} mediaId\n * @returns {Promise<{ success: boolean }>}\n */\nexport async function deleteAttachment(mediaId) {\n const credentials = getCredentials()\n const { endpoint, token } = credentials || {}\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const deleteUrl = `${endpoint}/delete/${mediaId}`\n\n const headers = {}\n if (token) {\n headers.Authorization = `Bearer ${token}`\n }\n\n const response = await fetch(deleteUrl, {\n method: 'DELETE',\n headers\n })\n\n if (!response.ok) {\n throw new Error(`Delete failed with status ${response.status}`)\n }\n\n return { success: true }\n}\n\n/**\n * Get attachment URL by mediaId\n * @param {string} mediaId\n * @returns {string} Full URL to the attachment\n */\nexport function getAttachment(mediaId) {\n const credentials = getCredentials()\n const { endpoint } = credentials || {}\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n return `${endpoint}/upload/${mediaId}`\n}\n\n/**\n * Internal fetch request helper\n * @param {string} pathname\n * @param {string} method\n * @param {object|null} body\n * @returns {Promise<Response>}\n */\nasync function fetchRequest(pathname, method = 'GET', body = null) {\n const credentials = getCredentials()\n\n const { endpoint, token } = credentials || {}\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (token) {\n headers.Authorization = `Bearer ${token}`\n }\n\n return fetch(url, {\n headers,\n method,\n body: body ? JSON.stringify(body) : null\n })\n}\n","/**\n * Socket Service for Call SDK\n * Handles WebRTC call functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { getCallServerEndpoint } from './utils.js'\nimport { getCredentials, getExternalId, updateSessionId } from './chat.js'\n\n/**\n * @typedef {Object} CallCallbacks\n * @property {(status: string) => void} [onCallStatus] - Called when call status changes\n * @property {(error: string | null) => void} [onCallError] - Called when call error occurs\n */\n\n/**\n * @typedef {Object} CallSession\n * @property {string} [sessionId]\n * @property {WebSocket} [socket]\n * @property {RTCPeerConnection} [peerConnection]\n * @property {MediaStream} [localStream]\n * @property {MediaStream} [remoteStream]\n * @property {HTMLAudioElement} [remoteAudio]\n * @property {boolean} isMuted\n * @property {string} callStatus\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {number} pingCount\n * @property {number | null} lastPongTime\n * @property {CallCallbacks} callbacks\n * @property {string[]} localIceCandidates - Queued local ICE candidates to send after remote description is set\n * @property {string[]} pendingRemoteIceCandidates - Queued remote ICE candidates to add after remote description is set\n */\n\n/**\n * Create a new call session\n * @param {CallCallbacks} [callbacks={}]\n * @returns {CallSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n sessionId: undefined,\n socket: null,\n peerConnection: null,\n localStream: null,\n remoteStream: null,\n remoteAudio: null,\n isMuted: false,\n callStatus: 'disconnected',\n pingInterval: null,\n pingCount: 0,\n lastPongTime: null,\n callbacks,\n localIceCandidates: [],\n pendingRemoteIceCandidates: []\n }\n}\n\n/** @type {CallSession} */\nlet currentSession = createSession()\n\nconst rtcConfig = {\n iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }]\n}\n\n/**\n * Set callbacks for the current session\n * @param {CallCallbacks} callbacks\n */\nexport function setCallCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n\n if (currentSession.remoteStream) {\n currentSession.remoteStream = null\n }\n\n if (currentSession.remoteAudio) {\n currentSession.remoteAudio.srcObject = null\n if (currentSession.remoteAudio.parentNode) {\n currentSession.remoteAudio.parentNode.removeChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio = null\n }\n\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n\n stopPingInterval()\n\n const callbacks = currentSession.callbacks\n currentSession = createSession(callbacks)\n\n console.log('Call session cleaned up')\n}\n\n/**\n * Update call status and notify callback\n * @param {string} status\n */\nfunction setCallStatus(status) {\n currentSession.callStatus = status\n currentSession.callbacks.onCallStatus?.(status)\n}\n\n/**\n * Update call error and notify callback\n * @param {string | null} error\n */\nfunction setCallError(error) {\n currentSession.callbacks.onCallError?.(error)\n}\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n currentSession.pingCount++\n const pingMessage = {\n type: 'ping',\n timestamp: Date.now(),\n count: currentSession.pingCount\n }\n sendEvent(pingMessage)\n console.log(`Sending keep-alive ping #${currentSession.pingCount}`)\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, 10000)\n}\n\n/**\n * Handle pong response\n */\nfunction handlePong() {\n currentSession.lastPongTime = Date.now()\n console.log(`Received pong #${currentSession.pingCount}`)\n}\n\n/**\n * Send event through socket\n * @param {Object} payload\n */\nfunction sendEvent(payload) {\n if (!currentSession.socket) {\n console.error('Failed to send event: no socket instance')\n return\n }\n if (currentSession.socket.readyState !== WebSocket.OPEN) {\n console.error('Failed to send event: socket state not open ', payload)\n return\n }\n\n currentSession.socket.send(JSON.stringify(payload))\n}\n\n/**\n * Get user media\n */\nasync function getUserMedia() {\n try {\n currentSession.localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n })\n console.log('Got audio media')\n } catch (error) {\n console.error(`Failed to get audio media: ${error.message}`)\n throw error\n }\n}\n\n/**\n * Create peer connection\n */\nfunction createPeerConnection() {\n currentSession.peerConnection = new RTCPeerConnection(rtcConfig)\n\n currentSession.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateJson = JSON.stringify(event.candidate)\n // Queue local ICE candidates until remote description is set\n if (currentSession.peerConnection && currentSession.peerConnection.remoteDescription) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent ICE candidate immediately')\n } else {\n currentSession.localIceCandidates.push(candidateJson)\n console.log('Queued local ICE candidate')\n }\n }\n }\n\n currentSession.peerConnection.ontrack = (event) => {\n console.log('Received remote audio stream')\n currentSession.remoteStream = event.streams[0]\n\n if (!currentSession.remoteAudio) {\n currentSession.remoteAudio = document.createElement('audio')\n currentSession.remoteAudio.autoplay = true\n currentSession.remoteAudio.controls = false\n document.body.appendChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio.srcObject = currentSession.remoteStream\n // explicitly kick off playback and catch any policy/gesture errors\n currentSession.remoteAudio\n .play()\n .then(() => console.log('🔊 remote audio playing'))\n .catch((err) => console.error('❌ playback error:', err))\n }\n\n currentSession.peerConnection.onconnectionstatechange = () => {\n const newState = currentSession.peerConnection.connectionState\n console.log(`Connection state: ${newState}`)\n\n if (newState === 'connected') {\n setCallStatus('connected')\n } else if (newState === 'disconnected' || newState === 'closed') {\n setCallStatus('disconnected')\n disconnectCall()\n }\n }\n\n currentSession.peerConnection.oniceconnectionstatechange = () => {\n console.log(`ICE connection state: ${currentSession.peerConnection.iceConnectionState}`)\n }\n}\n\n/**\n * Connect socket\n * @param {{ sessionId?: string }} payload\n */\nfunction connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n // Extract hostname from endpoint\n const socketEndpoint = getCallServerEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = (event) => {\n console.log('Socket connection established: ', event)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const data = JSON.parse(event.data)\n handleCallServerEvent(data)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error: ', error)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n reject(error)\n }\n\n currentSession.socket.onclose = (event) => {\n console.log('Socket connection closed: ', event)\n stopPingInterval()\n }\n })\n}\n\n/**\n * Handle call server event\n * @param {Object} action\n */\nfunction handleCallServerEvent(action) {\n console.log('Handling socket server event: ', action)\n\n switch (action.type) {\n case 'pong':\n handlePong()\n break\n\n case 'answer':\n handleAnswer(action.data)\n break\n\n case 'connected':\n handleConnected(action.data)\n break\n\n case 'ice':\n handleIceCandidate(action.data)\n break\n\n case 'renegotiationOffer':\n handleRenegotiationOffer(action.data)\n break\n case 'end':\n disconnectCall()\n break\n case 'error':\n setCallStatus('error')\n setCallError(action.error || 'Unable to connect voice')\n break\n\n default:\n console.log('Unknown call event type: ', action.type)\n break\n }\n}\n\n/**\n * Handle connected event\n * @param {Object} data\n */\nfunction handleConnected(data) {\n console.log('Received connected event')\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\n}\n\n/**\n * Handle answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n if (currentSession.peerConnection) {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: data.sdp\n })\n console.log('Setting remote description answer: ', answer)\n await currentSession.peerConnection.setRemoteDescription(answer)\n console.log('Remote description set')\n\n // Send all queued local ICE candidates\n for (const candidateJson of currentSession.localIceCandidates) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent queued local ICE candidate')\n }\n currentSession.localIceCandidates = []\n\n // Process any pending remote ICE candidates\n for (const candidateJson of currentSession.pendingRemoteIceCandidates) {\n try {\n const candidate = new RTCIceCandidate(JSON.parse(candidateJson))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added pending remote ICE candidate')\n } catch (err) {\n console.error(`Failed to add pending ICE candidate: ${err.message}`)\n }\n }\n currentSession.pendingRemoteIceCandidates = []\n }\n } catch (error) {\n console.error(`Failed to handle answer: ${error.message}`)\n }\n}\n\n/**\n * Handle ICE candidate\n * @param {Object} data\n */\nasync function handleIceCandidate(data) {\n try {\n if (currentSession.peerConnection) {\n // Check if remote description is set\n if (!currentSession.peerConnection.remoteDescription) {\n // Queue the candidate until remote description is set\n currentSession.pendingRemoteIceCandidates.push(data.candidate)\n console.log('Queued remote ICE candidate - remote description not set')\n return\n }\n const candidate = new RTCIceCandidate(JSON.parse(data.candidate))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added ICE candidate')\n }\n } catch (error) {\n console.error(`Failed to add ICE candidate: ${error.message}`)\n }\n}\n\n/**\n * Handle renegotiation offer\n * @param {Object} data\n */\nasync function handleRenegotiationOffer(data) {\n try {\n console.log('Received renegotiation offer')\n\n if (currentSession.peerConnection) {\n const offer = new RTCSessionDescription({\n type: 'offer',\n sdp: data.sdp\n })\n console.log('Setting remote description offer: ', offer)\n await currentSession.peerConnection.setRemoteDescription(offer)\n console.log('Remote description set')\n\n const answer = await currentSession.peerConnection.createAnswer()\n await currentSession.peerConnection.setLocalDescription(answer)\n\n sendEvent({\n type: 'renegotiationAnswer',\n data: {\n sdp: answer.sdp\n }\n })\n }\n } catch (error) {\n console.error(`Failed to handle renegotiation offer: ${error.message}`)\n }\n}\n\n/**\n * Start a call\n * @param {{ sessionId?: string }} payload\n */\nexport async function startCall(payload = {}) {\n try {\n if (currentSession.callStatus === 'connecting' || currentSession.callStatus === 'connected') {\n console.log(`Call already in ${currentSession.callStatus} state`)\n return\n }\n\n console.log('Starting audio call...')\n setCallStatus('connecting')\n setCallError(null)\n\n currentSession.sessionId = payload.sessionId\n\n await getUserMedia()\n\n createPeerConnection()\n\n currentSession.localStream.getTracks().forEach((track) => {\n currentSession.peerConnection.addTrack(track, currentSession.localStream)\n console.log(`Added ${track.kind} track`)\n })\n await connectSocket(payload)\n const offer = await currentSession.peerConnection.createOffer()\n await currentSession.peerConnection.setLocalDescription(offer)\n\n sendEvent({\n type: 'offer',\n data: {\n sdp: offer.sdp\n }\n })\n\n console.log('Call initiated successfully')\n } catch (error) {\n console.log('error: ', error)\n console.error(`Failed to start call: ${error.message}`)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n cleanup()\n }\n}\n\n/**\n * Disconnect call\n */\nexport function disconnectCall() {\n sendEvent({\n type: 'end'\n })\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n setCallStatus('disconnected')\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n cleanup()\n}\n\n/**\n * Toggle mute\n * @returns {boolean}\n */\nexport function toggleMute() {\n if (currentSession.localStream) {\n const audioTrack = currentSession.localStream.getAudioTracks()[0]\n if (audioTrack) {\n audioTrack.enabled = !audioTrack.enabled\n currentSession.isMuted = !audioTrack.enabled\n console.log(`Audio ${currentSession.isMuted ? 'muted' : 'unmuted'}`)\n return currentSession.isMuted\n }\n }\n return false\n}\n\n/**\n * Get local stream\n * @returns {MediaStream | null}\n */\nexport function getLocalStream() {\n return currentSession.localStream\n}\n\n/**\n * Get inbound audio energy\n * @returns {Promise<number>}\n */\nexport function getInboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'inbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no inbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n\n/**\n * Get outbound audio energy (not implemented in original, but may be needed)\n * @returns {Promise<number>}\n */\nexport function getOutboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'outbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no outbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n"],"names":["uuidv7","timestamp","bytes","hex","b","getDeviceId","deviceId","sleep","ms","resolve","getSseEndpoint","baseUrl","sseEndpoint","url","e","getSocketEndpoint","socketEndpoint","getCallServerEndpoint","MESSAGE_ROLES","PING_INTERVAL","SOCKET_TIMEOUT","SocketEvents","createSession","currentSession","stopPingInterval","startPingInterval","send","clearAllTimeouts","handleSocketConnected","setTransport","handleSocketDisconnected","connectSocket","payload","fulfill","reject","credentials","getCredentials","externalId","getExternalId","queryParams","socketUrl","event","message","handleSocketEvent","error","ws","addMessage","data","toggleTypingStatus","eventId","disconnect","isConnected","callbacks","setCallbacks","initialize","updateSessionId","sessionId","startChat","configData","authenticate","messages","control","session","getSession","searchParams","sendMessage","cleanup","disconnectSocket","isTyping","transport","text","html","context","attachments","isEmpty","userMessage","isSocketConnected","socketSend","loadingMessage","headers","fetchEventSource","response","errorMessage","lastIndex","updatedMsg","msg","index","_i","supervisorMessage","_j","_k","newBotMessage","lastMsg","_l","_o","_p","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","endpoint","errorPayload","getHistory","fetchRequest","_a","uploadAttachment","file","onProgress","onComplete","token","xhr","formData","uploadUrl","percentComplete","deleteAttachment","mediaId","deleteUrl","getAttachment","pathname","method","body","rtcConfig","setCallCallbacks","track","setCallStatus","status","_b","setCallError","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","handleCallServerEvent","action","handleAnswer","handleConnected","handleIceCandidate","handleRenegotiationOffer","answer","candidate","offer","startCall","toggleMute","audioTrack","getLocalStream","getInboundAudioEnergy","stats","report","getOutboundAudioEnergy"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -234,7 +234,7 @@ export function sendMessage({ text, html, context, attachments }) {
|
|
|
234
234
|
return new Promise((resolve, reject) => {
|
|
235
235
|
;(async () => {
|
|
236
236
|
try {
|
|
237
|
-
const isEmpty = !text && !html
|
|
237
|
+
const isEmpty = !text && !html && !attachments?.length
|
|
238
238
|
|
|
239
239
|
// Add user message only if there's content
|
|
240
240
|
if (!isEmpty) {
|
|
@@ -351,6 +351,7 @@ export function sendMessage({ text, html, context, attachments }) {
|
|
|
351
351
|
currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
|
|
352
352
|
reject(new Error(errorMessage))
|
|
353
353
|
} else if (response.event === 'done') {
|
|
354
|
+
console.log('Done: ', data)
|
|
354
355
|
const lastIndex = currentSession.messages.length - 1
|
|
355
356
|
const lastMsg = currentSession.messages[lastIndex]
|
|
356
357
|
const updatedMsg = {
|
|
@@ -363,12 +364,13 @@ export function sendMessage({ text, html, context, attachments }) {
|
|
|
363
364
|
)
|
|
364
365
|
currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
|
|
365
366
|
resolve(currentSession.sessionId)
|
|
366
|
-
} else if (data.message !== undefined) {
|
|
367
|
+
} else if (data.message !== undefined || data.attachments?.length > 0) {
|
|
367
368
|
// If role is supervisor, treat it as a new message
|
|
368
369
|
if (data.role === MESSAGE_ROLES.SUPERVISOR) {
|
|
369
370
|
const supervisorMessage = {
|
|
370
371
|
role: MESSAGE_ROLES.SUPERVISOR,
|
|
371
372
|
text: data.message,
|
|
373
|
+
attachments: data.attachments,
|
|
372
374
|
sources: data.sources,
|
|
373
375
|
done: true
|
|
374
376
|
}
|
|
@@ -404,6 +406,7 @@ export function sendMessage({ text, html, context, attachments }) {
|
|
|
404
406
|
loading: false,
|
|
405
407
|
text: (lastMsg.text || '') + data.message,
|
|
406
408
|
sources: data.sources,
|
|
409
|
+
attachments: data.attachments,
|
|
407
410
|
done: data.done ?? lastMsg.done
|
|
408
411
|
}
|
|
409
412
|
currentSession.messages = currentSession.messages.map((msg, index) =>
|
|
@@ -412,9 +415,6 @@ export function sendMessage({ text, html, context, attachments }) {
|
|
|
412
415
|
|
|
413
416
|
currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
|
|
414
417
|
|
|
415
|
-
if (data.done) {
|
|
416
|
-
}
|
|
417
|
-
|
|
418
418
|
// Store session info for reuse
|
|
419
419
|
currentSession.sessionId = data.sessionId ?? currentSession.sessionId
|
|
420
420
|
currentSession.requestId = data.requestId ?? currentSession.requestId
|