@origonai/web-chat-sdk 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/origon-chat-sdk.js +190 -207
- package/dist/origon-chat-sdk.js.map +1 -1
- package/package.json +1 -1
- package/src/chat.js +4 -14
- package/src/constants.js +9 -3
- package/src/http.js +15 -1
package/README.md
CHANGED
|
@@ -282,10 +282,14 @@ Enum for message roles in the chat.
|
|
|
282
282
|
```javascript
|
|
283
283
|
import { MESSAGE_ROLES } from '@origonai/web-chat-sdk'
|
|
284
284
|
|
|
285
|
-
MESSAGE_ROLES.
|
|
286
|
-
MESSAGE_ROLES.USER // 'user' -
|
|
287
|
-
MESSAGE_ROLES.
|
|
288
|
-
MESSAGE_ROLES.SYSTEM // 'system' - System notifications
|
|
285
|
+
MESSAGE_ROLES.ASSISTANT // 'assistant' - AI/bot or LLM AI Agent responses
|
|
286
|
+
MESSAGE_ROLES.USER // 'user' - Widget user messages
|
|
287
|
+
MESSAGE_ROLES.SUPERVISOR // 'supervisor' - Human supervisor messages
|
|
288
|
+
MESSAGE_ROLES.SYSTEM // 'system' - System notifications (e.g., "Agent joined" / "Agent left")
|
|
289
|
+
|
|
290
|
+
// Deprecated (will be removed in v1.0):
|
|
291
|
+
MESSAGE_ROLES.BOT // use ASSISTANT instead
|
|
292
|
+
MESSAGE_ROLES.AGENT // use SUPERVISOR instead
|
|
289
293
|
```
|
|
290
294
|
|
|
291
295
|
---
|
|
@@ -299,7 +303,7 @@ interface Message {
|
|
|
299
303
|
id?: string
|
|
300
304
|
text: string
|
|
301
305
|
html?: string
|
|
302
|
-
role: 'assistant' | 'user' | '
|
|
306
|
+
role: 'assistant' | 'user' | 'supervisor' | 'system'
|
|
303
307
|
timestamp?: string
|
|
304
308
|
loading?: boolean
|
|
305
309
|
done?: boolean
|
package/dist/origon-chat-sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchEventSource as ne } from "@microsoft/fetch-event-source";
|
|
2
|
-
function
|
|
2
|
+
function W() {
|
|
3
3
|
const e = Date.now(), t = new Uint8Array(16);
|
|
4
4
|
crypto.getRandomValues(t), t[0] = e >> 40 & 255, t[1] = e >> 32 & 255, t[2] = e >> 24 & 255, t[3] = e >> 16 & 255, t[4] = e >> 8 & 255, t[5] = e & 255, t[6] = t[6] & 15 | 112, t[8] = t[8] & 63 | 128;
|
|
5
5
|
const o = [...t].map((a) => a.toString(16).padStart(2, "0")).join("");
|
|
@@ -11,7 +11,7 @@ function J() {
|
|
|
11
11
|
function te() {
|
|
12
12
|
if (localStorage.getItem("chatDeviceId"))
|
|
13
13
|
return localStorage.getItem("chatDeviceId");
|
|
14
|
-
const e =
|
|
14
|
+
const e = W();
|
|
15
15
|
return localStorage.setItem("chatDeviceId", e), e;
|
|
16
16
|
}
|
|
17
17
|
async function oe(e) {
|
|
@@ -23,17 +23,15 @@ function se(e) {
|
|
|
23
23
|
const o = new URL(e);
|
|
24
24
|
t = `https://${o.hostname}${o.pathname}/sse`;
|
|
25
25
|
} catch (o) {
|
|
26
|
-
console.error("SSE Invalid base URL: ", e);
|
|
27
26
|
}
|
|
28
27
|
return t;
|
|
29
28
|
}
|
|
30
|
-
function
|
|
29
|
+
function re(e) {
|
|
31
30
|
let t;
|
|
32
31
|
try {
|
|
33
32
|
const o = new URL(e);
|
|
34
33
|
t = `wss://${o.hostname}${o.pathname}/wss`;
|
|
35
34
|
} catch (o) {
|
|
36
|
-
console.error("Socket Invalid base URL: ", e);
|
|
37
35
|
}
|
|
38
36
|
return t;
|
|
39
37
|
}
|
|
@@ -43,26 +41,30 @@ function ae(e) {
|
|
|
43
41
|
const o = new URL(e);
|
|
44
42
|
t = `wss://${o.hostname}${o.pathname}/audio`;
|
|
45
43
|
} catch (o) {
|
|
46
|
-
console.error("getCallServerEndpoint: Invalid base URL: ", e);
|
|
47
44
|
}
|
|
48
45
|
return t;
|
|
49
46
|
}
|
|
50
47
|
const T = {
|
|
51
|
-
|
|
48
|
+
ASSISTANT: "assistant",
|
|
52
49
|
// this can be automated or LLM AI Agent response
|
|
53
50
|
USER: "user",
|
|
54
51
|
// this is widget user
|
|
55
|
-
|
|
56
|
-
// this is human
|
|
57
|
-
SYSTEM: "system"
|
|
52
|
+
SUPERVISOR: "supervisor",
|
|
53
|
+
// this is human supervisor (ex. Samespace Dock agent, or Resolve human agent)
|
|
54
|
+
SYSTEM: "system",
|
|
58
55
|
// this is system message, for ex "Agent joined" / "Agent left"
|
|
59
|
-
|
|
56
|
+
// Deprecated aliases for backwards compatibility (will be removed in v1.0)
|
|
57
|
+
/** @deprecated Use ASSISTANT instead */
|
|
58
|
+
BOT: "assistant",
|
|
59
|
+
/** @deprecated Use SUPERVISOR instead */
|
|
60
|
+
AGENT: "supervisor"
|
|
61
|
+
}, J = 1e4, ce = 5e3, b = {
|
|
60
62
|
MESSAGE: "message",
|
|
61
63
|
TYPING: "typing",
|
|
62
64
|
TYPING_STOP: "typingOff",
|
|
63
65
|
END: "end"
|
|
64
66
|
};
|
|
65
|
-
function
|
|
67
|
+
function ie() {
|
|
66
68
|
return {
|
|
67
69
|
socket: null,
|
|
68
70
|
previouslyConnected: !1,
|
|
@@ -72,37 +74,36 @@ function re() {
|
|
|
72
74
|
socketConnectionTimeout: null
|
|
73
75
|
};
|
|
74
76
|
}
|
|
75
|
-
let
|
|
76
|
-
function
|
|
77
|
-
|
|
77
|
+
let r = ie();
|
|
78
|
+
function N() {
|
|
79
|
+
r.pingInterval && (clearInterval(r.pingInterval), r.pingInterval = null);
|
|
78
80
|
}
|
|
79
81
|
function le() {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
},
|
|
82
|
+
N(), r.pingInterval = setInterval(() => {
|
|
83
|
+
r.socket && r.socket.readyState === WebSocket.OPEN ? O({ type: "ping" }) : N();
|
|
84
|
+
}, J);
|
|
83
85
|
}
|
|
84
86
|
function _() {
|
|
85
|
-
|
|
87
|
+
N(), r.socketDisconnectedTimeout && (clearTimeout(r.socketDisconnectedTimeout), r.socketDisconnectedTimeout = null), r.socketConnectionTimeout && (clearTimeout(r.socketConnectionTimeout), r.socketConnectionTimeout = null);
|
|
86
88
|
}
|
|
87
89
|
function K() {
|
|
88
|
-
|
|
90
|
+
r.socketDisconnected = !1, $("socket");
|
|
89
91
|
}
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
+
function H() {
|
|
93
|
+
r.socketDisconnected = !0, $("sse");
|
|
92
94
|
}
|
|
93
95
|
function de(e) {
|
|
94
96
|
return new Promise((t, o) => {
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
+
if (r.socket && (r.socket.readyState === WebSocket.CONNECTING || r.socket.readyState === WebSocket.OPEN)) {
|
|
98
|
+
t(r.socket.readyState === WebSocket.OPEN);
|
|
97
99
|
return;
|
|
98
100
|
}
|
|
99
|
-
console.log("Initializing socket connection..");
|
|
100
101
|
const a = U();
|
|
101
102
|
if (!a || !a.endpoint) {
|
|
102
103
|
o(new Error("SDK not initialized. Please initialize SDK first."));
|
|
103
104
|
return;
|
|
104
105
|
}
|
|
105
|
-
const d =
|
|
106
|
+
const d = re(a.endpoint);
|
|
106
107
|
if (!d) {
|
|
107
108
|
o(
|
|
108
109
|
new Error(
|
|
@@ -111,42 +112,42 @@ function de(e) {
|
|
|
111
112
|
);
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
114
|
-
const u =
|
|
115
|
+
const u = D(), c = new URLSearchParams({
|
|
115
116
|
externalId: u
|
|
116
117
|
});
|
|
117
|
-
e.sessionId &&
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
},
|
|
122
|
-
const
|
|
123
|
-
ue(
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
},
|
|
127
|
-
|
|
118
|
+
e.sessionId && c.set("sessionId", e.sessionId), e.requestId && c.set("requestId", e.requestId), a.token && c.set("token", a.token);
|
|
119
|
+
const p = `${d}?${c.toString()}`;
|
|
120
|
+
r.socket = new WebSocket(p), r.socket.onopen = () => {
|
|
121
|
+
r.previouslyConnected = !0, K(), O({ type: "ping" }), clearTimeout(r.socketConnectionTimeout), le(), t(!0);
|
|
122
|
+
}, r.socket.onmessage = (l) => {
|
|
123
|
+
const f = JSON.parse(l.data);
|
|
124
|
+
ue(f);
|
|
125
|
+
}, r.socket.onerror = (l) => {
|
|
126
|
+
$("sse"), o(l);
|
|
127
|
+
}, r.socket.onclose = (l) => {
|
|
128
|
+
l.target === r.socket && (l.code === 1006 && (r.previouslyConnected ? H() : h({
|
|
128
129
|
errorText: "Unable to establish connection",
|
|
129
130
|
done: !0,
|
|
130
131
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
131
|
-
}), clearTimeout(
|
|
132
|
-
},
|
|
133
|
-
|
|
132
|
+
}), clearTimeout(r.socketConnectionTimeout)), r.socket = null, _());
|
|
133
|
+
}, r.previouslyConnected || (r.socketConnectionTimeout = setTimeout(() => {
|
|
134
|
+
h({
|
|
134
135
|
errorText: "Unable to establish connection",
|
|
135
136
|
done: !0,
|
|
136
137
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
137
138
|
}), o(new Error("Socket connection timed out"));
|
|
138
|
-
},
|
|
139
|
+
}, ce));
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
142
|
+
function O(e) {
|
|
143
|
+
r.socketDisconnected || !r.socket || r.socket.send(JSON.stringify({ ...e, eventId: e.eventId || W() }));
|
|
143
144
|
}
|
|
144
145
|
function ue(e) {
|
|
145
|
-
switch (
|
|
146
|
+
switch (e.type) {
|
|
146
147
|
case "pong": {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
},
|
|
148
|
+
r.socketDisconnected && K(), r.socketDisconnectedTimeout && clearTimeout(r.socketDisconnectedTimeout), r.socketDisconnectedTimeout = setTimeout(() => {
|
|
149
|
+
H();
|
|
150
|
+
}, J + 1e3);
|
|
150
151
|
break;
|
|
151
152
|
}
|
|
152
153
|
case b.TYPING: {
|
|
@@ -167,16 +168,16 @@ function ue(e) {
|
|
|
167
168
|
break;
|
|
168
169
|
}
|
|
169
170
|
case b.END: {
|
|
170
|
-
|
|
171
|
+
V();
|
|
171
172
|
break;
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
|
-
function
|
|
176
|
-
|
|
176
|
+
function V() {
|
|
177
|
+
r.socket && r.socket.close(1e3), r.previouslyConnected = !1, _(), r.socket = null, $("sse");
|
|
177
178
|
}
|
|
178
179
|
function ge() {
|
|
179
|
-
return
|
|
180
|
+
return r.socket !== null && r.socket.readyState === WebSocket.OPEN && !r.socketDisconnected;
|
|
180
181
|
}
|
|
181
182
|
function Y(e = {}) {
|
|
182
183
|
return {
|
|
@@ -194,59 +195,46 @@ function Y(e = {}) {
|
|
|
194
195
|
};
|
|
195
196
|
}
|
|
196
197
|
let s = Y();
|
|
197
|
-
function
|
|
198
|
+
function $e(e) {
|
|
198
199
|
s.callbacks = { ...s.callbacks, ...e };
|
|
199
200
|
}
|
|
200
|
-
function
|
|
201
|
-
|
|
201
|
+
function Pe(e) {
|
|
202
|
+
s.credentials = e, e.token && (s.authenticated = !0);
|
|
202
203
|
}
|
|
203
204
|
function U() {
|
|
204
205
|
return s.credentials;
|
|
205
206
|
}
|
|
206
|
-
function
|
|
207
|
+
function pe(e) {
|
|
207
208
|
var t, o;
|
|
208
209
|
e && e !== s.sessionId && (s.sessionId = e, (o = (t = s.callbacks).onSessionUpdate) == null || o.call(t, e));
|
|
209
210
|
}
|
|
210
|
-
async function
|
|
211
|
-
var t;
|
|
211
|
+
async function Re(e = {}) {
|
|
212
212
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
a = ((t = u == null ? void 0 : u.sessionHistory) != null ? t : []).map((i) => ({
|
|
220
|
-
id: i.id,
|
|
221
|
-
text: i.text,
|
|
222
|
-
role: i.youtubeVideo ? T.BOT : i.role,
|
|
223
|
-
timestamp: i.timestamp,
|
|
224
|
-
video: i.youtubeVideo,
|
|
225
|
-
channel: i.channel,
|
|
226
|
-
done: !0
|
|
227
|
-
}));
|
|
228
|
-
}
|
|
229
|
-
const d = new URLSearchParams();
|
|
230
|
-
return s.credentials.token || d.set("externalId", $()), s.sseUrl = `${se(
|
|
213
|
+
let t = null;
|
|
214
|
+
s.authenticated ? t = s.configData : (t = await Se(s.credentials), s.authenticated = !0, s.configData = t);
|
|
215
|
+
let o = [];
|
|
216
|
+
e.sessionId && (o = await ke(e.sessionId));
|
|
217
|
+
const a = new URLSearchParams();
|
|
218
|
+
return s.credentials.token || a.set("externalId", D()), s.sseUrl = `${se(
|
|
231
219
|
s.credentials.endpoint
|
|
232
|
-
)}?${
|
|
220
|
+
)}?${a.toString()}`, s.sessionId = e.sessionId, s.messages = o, {
|
|
233
221
|
sessionId: s.sessionId,
|
|
234
|
-
messages:
|
|
235
|
-
configData:
|
|
222
|
+
messages: o,
|
|
223
|
+
configData: t
|
|
236
224
|
};
|
|
237
|
-
} catch (
|
|
238
|
-
throw
|
|
225
|
+
} catch (t) {
|
|
226
|
+
throw B(), t;
|
|
239
227
|
}
|
|
240
228
|
}
|
|
241
|
-
function
|
|
242
|
-
|
|
229
|
+
function Ne() {
|
|
230
|
+
B();
|
|
243
231
|
}
|
|
244
|
-
function
|
|
245
|
-
s.abortController && s.abortController.abort(),
|
|
232
|
+
function B() {
|
|
233
|
+
s.abortController && s.abortController.abort(), V();
|
|
246
234
|
const { callbacks: e, credentials: t } = s;
|
|
247
|
-
s = Y(e), s.credentials = t
|
|
235
|
+
s = Y(e), s.credentials = t;
|
|
248
236
|
}
|
|
249
|
-
function
|
|
237
|
+
function D() {
|
|
250
238
|
var e;
|
|
251
239
|
return (e = s.credentials) != null && e.externalId ? s.credentials.externalId : te();
|
|
252
240
|
}
|
|
@@ -258,23 +246,23 @@ function F(e) {
|
|
|
258
246
|
var t, o;
|
|
259
247
|
(o = (t = s.callbacks).onTyping) == null || o.call(t, e);
|
|
260
248
|
}
|
|
261
|
-
function
|
|
249
|
+
function $(e) {
|
|
262
250
|
var t, o;
|
|
263
|
-
|
|
251
|
+
s.transport = e, (o = (t = s.callbacks).onTransportUpdate) == null || o.call(t, e);
|
|
264
252
|
}
|
|
265
|
-
function
|
|
253
|
+
function Oe({ text: e, html: t, context: o }) {
|
|
266
254
|
return new Promise((a, d) => {
|
|
267
255
|
(async () => {
|
|
268
|
-
var u,
|
|
256
|
+
var u, c, p;
|
|
269
257
|
try {
|
|
270
|
-
const
|
|
258
|
+
const l = {
|
|
271
259
|
role: T.USER,
|
|
272
260
|
text: e,
|
|
273
261
|
html: t,
|
|
274
262
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
275
263
|
};
|
|
276
|
-
if (h(
|
|
277
|
-
|
|
264
|
+
if (h(l), await oe(200), s.transport === "socket" && ge()) {
|
|
265
|
+
O({
|
|
278
266
|
type: "message",
|
|
279
267
|
data: {
|
|
280
268
|
text: e,
|
|
@@ -283,12 +271,12 @@ function Ne({ text: e, html: t, context: o }) {
|
|
|
283
271
|
}), a(s.sessionId);
|
|
284
272
|
return;
|
|
285
273
|
}
|
|
286
|
-
const
|
|
287
|
-
role: T.
|
|
274
|
+
const f = {
|
|
275
|
+
role: T.ASSISTANT,
|
|
288
276
|
text: "",
|
|
289
277
|
loading: !0
|
|
290
278
|
};
|
|
291
|
-
h(
|
|
279
|
+
h(f);
|
|
292
280
|
const m = new URL(s.sseUrl);
|
|
293
281
|
s.sessionId && m.searchParams.set("sessionId", s.sessionId), s.requestId && m.searchParams.set("requestId", s.requestId), s.lastStreamId = void 0, s.abortController = new AbortController();
|
|
294
282
|
const y = {
|
|
@@ -303,53 +291,54 @@ function Ne({ text: e, html: t, context: o }) {
|
|
|
303
291
|
context: o
|
|
304
292
|
}),
|
|
305
293
|
signal: s.abortController.signal,
|
|
294
|
+
openWhenHidden: !0,
|
|
306
295
|
onopen: async (g) => {
|
|
307
296
|
if (!g.ok)
|
|
308
|
-
throw
|
|
297
|
+
throw new Error("Failed to send message");
|
|
309
298
|
},
|
|
310
299
|
onmessage: (g) => {
|
|
311
300
|
var E, M, x, q, L, G, z;
|
|
312
|
-
const
|
|
301
|
+
const i = JSON.parse(g.data);
|
|
313
302
|
if (g.event === "connected")
|
|
314
|
-
s.sessionId =
|
|
303
|
+
s.sessionId = i.sessionId, s.requestId = i.requestId;
|
|
315
304
|
else if (g.event === "upgrade_to_websocket")
|
|
316
|
-
|
|
305
|
+
de({
|
|
317
306
|
sessionId: s.sessionId,
|
|
318
|
-
requestId:
|
|
307
|
+
requestId: i.requestId
|
|
319
308
|
});
|
|
320
|
-
else if (
|
|
321
|
-
const I =
|
|
322
|
-
...s.messages[
|
|
309
|
+
else if (i.error) {
|
|
310
|
+
const I = i.error && typeof i.error == "string" ? i.error : "Failed to connect to the system", S = s.messages.length - 1, C = {
|
|
311
|
+
...s.messages[S],
|
|
323
312
|
loading: !1,
|
|
324
313
|
errorText: I
|
|
325
314
|
};
|
|
326
315
|
s.messages = s.messages.map(
|
|
327
|
-
(
|
|
328
|
-
), (M = (E = s.callbacks).onMessageUpdate) == null || M.call(E,
|
|
329
|
-
} else if (
|
|
330
|
-
if (
|
|
316
|
+
(R, ee) => ee === S ? C : R
|
|
317
|
+
), (M = (E = s.callbacks).onMessageUpdate) == null || M.call(E, S, C), d(new Error(I));
|
|
318
|
+
} else if (i.message !== void 0) {
|
|
319
|
+
if (i.streamId !== void 0) {
|
|
331
320
|
if (s.lastStreamId === void 0)
|
|
332
|
-
s.lastStreamId =
|
|
333
|
-
else if (
|
|
334
|
-
s.lastStreamId =
|
|
321
|
+
s.lastStreamId = i.streamId;
|
|
322
|
+
else if (i.streamId !== s.lastStreamId) {
|
|
323
|
+
s.lastStreamId = i.streamId;
|
|
335
324
|
const C = {
|
|
336
|
-
role: T.
|
|
325
|
+
role: T.ASSISTANT,
|
|
337
326
|
text: "",
|
|
338
327
|
loading: !0
|
|
339
328
|
};
|
|
340
329
|
h(C);
|
|
341
330
|
}
|
|
342
331
|
}
|
|
343
|
-
const I = s.messages.length - 1,
|
|
344
|
-
...
|
|
332
|
+
const I = s.messages.length - 1, S = s.messages[I], P = {
|
|
333
|
+
...S,
|
|
345
334
|
loading: !1,
|
|
346
|
-
text: (
|
|
347
|
-
sources:
|
|
348
|
-
done: (x =
|
|
335
|
+
text: (S.text || "") + i.message,
|
|
336
|
+
sources: i.sources,
|
|
337
|
+
done: (x = i.done) != null ? x : S.done
|
|
349
338
|
};
|
|
350
339
|
s.messages = s.messages.map(
|
|
351
|
-
(C,
|
|
352
|
-
), (L = (q = s.callbacks).onMessageUpdate) == null || L.call(q, I,
|
|
340
|
+
(C, R) => R === I ? P : C
|
|
341
|
+
), (L = (q = s.callbacks).onMessageUpdate) == null || L.call(q, I, P), i.done && a(s.sessionId), s.sessionId = (G = i.session_id) != null ? G : s.sessionId, s.requestId = (z = i.requestId) != null ? z : s.requestId;
|
|
353
342
|
}
|
|
354
343
|
},
|
|
355
344
|
onerror: (g) => {
|
|
@@ -357,23 +346,22 @@ function Ne({ text: e, html: t, context: o }) {
|
|
|
357
346
|
},
|
|
358
347
|
openWhenHidden: !0
|
|
359
348
|
});
|
|
360
|
-
} catch (
|
|
361
|
-
|
|
362
|
-
const p = "Failed to connect to the system", m = s.messages.length - 1, y = s.messages[m], g = {
|
|
349
|
+
} catch (l) {
|
|
350
|
+
const f = "Failed to connect to the system", m = s.messages.length - 1, y = s.messages[m], g = {
|
|
363
351
|
...y,
|
|
364
352
|
loading: !1,
|
|
365
|
-
errorText: y.done ? void 0 :
|
|
353
|
+
errorText: y.done ? void 0 : l.message || f,
|
|
366
354
|
done: !0
|
|
367
355
|
};
|
|
368
356
|
s.messages = s.messages.map(
|
|
369
|
-
(
|
|
370
|
-
), (
|
|
357
|
+
(i, E) => E === m ? g : i
|
|
358
|
+
), (p = (c = s.callbacks).onMessageUpdate) == null || p.call(c, m, g), d(l);
|
|
371
359
|
}
|
|
372
360
|
})();
|
|
373
361
|
});
|
|
374
362
|
}
|
|
375
|
-
const
|
|
376
|
-
async function
|
|
363
|
+
const fe = "Something went wrong initializing the chat", me = "Chat SDK not initialized";
|
|
364
|
+
async function Se(e) {
|
|
377
365
|
const { endpoint: t } = e, o = `${t}/config`, a = await fetch(o, {
|
|
378
366
|
method: "GET",
|
|
379
367
|
headers: {
|
|
@@ -381,36 +369,46 @@ async function ke(e) {
|
|
|
381
369
|
}
|
|
382
370
|
});
|
|
383
371
|
if (!a.ok) {
|
|
384
|
-
const
|
|
385
|
-
throw new Error((
|
|
372
|
+
const c = await a.json();
|
|
373
|
+
throw new Error((c == null ? void 0 : c.error) || fe);
|
|
386
374
|
}
|
|
387
375
|
return (await a.json()).data;
|
|
388
376
|
}
|
|
389
377
|
async function Ue() {
|
|
390
378
|
const e = new URLSearchParams({
|
|
391
|
-
externalId:
|
|
379
|
+
externalId: D()
|
|
392
380
|
}), t = await Q(`/sessions?${e.toString()}`, "GET");
|
|
393
381
|
if (!t.ok)
|
|
394
382
|
throw new Error("Unable to load history, please try again later");
|
|
395
383
|
return t.json();
|
|
396
384
|
}
|
|
397
|
-
async function
|
|
385
|
+
async function ke(e) {
|
|
386
|
+
var u;
|
|
398
387
|
const t = new URLSearchParams({
|
|
399
388
|
sessionId: e
|
|
400
389
|
}), o = await Q(`/session?${t.toString()}`, "GET");
|
|
401
390
|
if (!o.ok)
|
|
402
391
|
throw new Error("Unable to load messages, please try again later");
|
|
403
|
-
|
|
392
|
+
const a = await o.json();
|
|
393
|
+
return ((u = a == null ? void 0 : a.sessionHistory) != null ? u : []).map((c) => ({
|
|
394
|
+
id: c.id,
|
|
395
|
+
text: c.text,
|
|
396
|
+
role: c.youtubeVideo ? T.ASSISTANT : c.role,
|
|
397
|
+
timestamp: c.timestamp,
|
|
398
|
+
video: c.youtubeVideo,
|
|
399
|
+
channel: c.channel,
|
|
400
|
+
done: !0
|
|
401
|
+
}));
|
|
404
402
|
}
|
|
405
403
|
async function Q(e, t = "GET", o = null) {
|
|
406
404
|
const a = U(), { endpoint: d, token: u } = a || {};
|
|
407
405
|
if (!d)
|
|
408
406
|
throw new Error(me);
|
|
409
|
-
const
|
|
407
|
+
const c = `${d}${e}`, p = {
|
|
410
408
|
"Content-Type": "application/json"
|
|
411
409
|
};
|
|
412
|
-
return u && (
|
|
413
|
-
headers:
|
|
410
|
+
return u && (p.Authorization = `Bearer ${u}`), fetch(c, {
|
|
411
|
+
headers: p,
|
|
414
412
|
method: t,
|
|
415
413
|
body: o ? JSON.stringify(o) : null
|
|
416
414
|
});
|
|
@@ -441,11 +439,11 @@ function Me(e) {
|
|
|
441
439
|
n.callbacks = { ...n.callbacks, ...e };
|
|
442
440
|
}
|
|
443
441
|
function j() {
|
|
444
|
-
n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((t) => t.stop()), n.localStream = null), n.remoteStream && (n.remoteStream = null), n.remoteAudio && (n.remoteAudio.srcObject = null, n.remoteAudio.parentNode && n.remoteAudio.parentNode.removeChild(n.remoteAudio), n.remoteAudio = null), n.socket && (n.socket.close(), n.socket = null),
|
|
442
|
+
n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((t) => t.stop()), n.localStream = null), n.remoteStream && (n.remoteStream = null), n.remoteAudio && (n.remoteAudio.srcObject = null, n.remoteAudio.parentNode && n.remoteAudio.parentNode.removeChild(n.remoteAudio), n.remoteAudio = null), n.socket && (n.socket.close(), n.socket = null), A();
|
|
445
443
|
const e = n.callbacks;
|
|
446
|
-
n = Z(e)
|
|
444
|
+
n = Z(e);
|
|
447
445
|
}
|
|
448
|
-
function
|
|
446
|
+
function k(e) {
|
|
449
447
|
var t, o;
|
|
450
448
|
n.callStatus = e, (o = (t = n.callbacks).onCallStatus) == null || o.call(t, e);
|
|
451
449
|
}
|
|
@@ -453,11 +451,11 @@ function v(e) {
|
|
|
453
451
|
var t, o;
|
|
454
452
|
(o = (t = n.callbacks).onCallError) == null || o.call(t, e);
|
|
455
453
|
}
|
|
456
|
-
function
|
|
454
|
+
function A() {
|
|
457
455
|
n.pingInterval && (clearInterval(n.pingInterval), n.pingInterval = null);
|
|
458
456
|
}
|
|
459
457
|
function Ce() {
|
|
460
|
-
|
|
458
|
+
A(), n.pingInterval = setInterval(() => {
|
|
461
459
|
if (n.socket && n.socket.readyState === WebSocket.OPEN) {
|
|
462
460
|
n.pingCount++;
|
|
463
461
|
const e = {
|
|
@@ -465,62 +463,54 @@ function Ce() {
|
|
|
465
463
|
timestamp: Date.now(),
|
|
466
464
|
count: n.pingCount
|
|
467
465
|
};
|
|
468
|
-
w(e)
|
|
466
|
+
w(e);
|
|
469
467
|
} else
|
|
470
|
-
|
|
468
|
+
A();
|
|
471
469
|
}, 1e4);
|
|
472
470
|
}
|
|
473
471
|
function he() {
|
|
474
|
-
n.lastPongTime = Date.now()
|
|
472
|
+
n.lastPongTime = Date.now();
|
|
475
473
|
}
|
|
476
474
|
function w(e) {
|
|
477
|
-
|
|
478
|
-
console.error("Failed to send event: no socket instance");
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (n.socket.readyState !== WebSocket.OPEN) {
|
|
482
|
-
console.error("Failed to send event: socket state not open ", e);
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
n.socket.send(JSON.stringify(e));
|
|
475
|
+
n.socket && n.socket.readyState === WebSocket.OPEN && n.socket.send(JSON.stringify(e));
|
|
486
476
|
}
|
|
487
477
|
async function we() {
|
|
488
478
|
try {
|
|
489
479
|
n.localStream = await navigator.mediaDevices.getUserMedia({
|
|
490
480
|
audio: !0,
|
|
491
481
|
video: !1
|
|
492
|
-
})
|
|
482
|
+
});
|
|
493
483
|
} catch (e) {
|
|
494
|
-
throw
|
|
484
|
+
throw e;
|
|
495
485
|
}
|
|
496
486
|
}
|
|
497
487
|
function ye() {
|
|
498
488
|
n.peerConnection = new RTCPeerConnection(Ie), n.peerConnection.onicecandidate = (e) => {
|
|
499
489
|
if (e.candidate) {
|
|
500
490
|
const t = JSON.stringify(e.candidate);
|
|
501
|
-
n.peerConnection && n.peerConnection.remoteDescription ?
|
|
491
|
+
n.peerConnection && n.peerConnection.remoteDescription ? w({
|
|
502
492
|
type: "ice",
|
|
503
493
|
data: {
|
|
504
494
|
candidate: t
|
|
505
495
|
}
|
|
506
|
-
})
|
|
496
|
+
}) : n.localIceCandidates.push(t);
|
|
507
497
|
}
|
|
508
498
|
}, n.peerConnection.ontrack = (e) => {
|
|
509
|
-
|
|
499
|
+
n.remoteStream = e.streams[0], n.remoteAudio || (n.remoteAudio = document.createElement("audio"), n.remoteAudio.autoplay = !0, n.remoteAudio.controls = !1, document.body.appendChild(n.remoteAudio)), n.remoteAudio.srcObject = n.remoteStream, n.remoteAudio.play().then(() => {
|
|
500
|
+
}).catch((t) => {
|
|
501
|
+
});
|
|
510
502
|
}, n.peerConnection.onconnectionstatechange = () => {
|
|
511
503
|
const e = n.peerConnection.connectionState;
|
|
512
|
-
|
|
504
|
+
e === "connected" ? k("connected") : (e === "disconnected" || e === "closed") && (k("disconnected"), X());
|
|
513
505
|
}, n.peerConnection.oniceconnectionstatechange = () => {
|
|
514
|
-
console.log(`ICE connection state: ${n.peerConnection.iceConnectionState}`);
|
|
515
506
|
};
|
|
516
507
|
}
|
|
517
508
|
function Ee(e) {
|
|
518
509
|
return new Promise((t, o) => {
|
|
519
510
|
if (n.socket && (n.socket.readyState === WebSocket.CONNECTING || n.socket.readyState === WebSocket.OPEN)) {
|
|
520
|
-
|
|
511
|
+
t(n.socket.readyState === WebSocket.OPEN);
|
|
521
512
|
return;
|
|
522
513
|
}
|
|
523
|
-
console.log("Initializing socket connection..");
|
|
524
514
|
const a = U();
|
|
525
515
|
if (!a || !a.endpoint) {
|
|
526
516
|
o(new Error("SDK not initialized. Please initialize SDK first."));
|
|
@@ -535,25 +525,25 @@ function Ee(e) {
|
|
|
535
525
|
);
|
|
536
526
|
return;
|
|
537
527
|
}
|
|
538
|
-
const u =
|
|
528
|
+
const u = D(), c = new URLSearchParams({
|
|
539
529
|
externalId: u
|
|
540
530
|
});
|
|
541
|
-
e.sessionId &&
|
|
542
|
-
const
|
|
543
|
-
n.socket = new WebSocket(
|
|
544
|
-
|
|
545
|
-
}, n.socket.onmessage = (
|
|
546
|
-
const
|
|
547
|
-
be(
|
|
548
|
-
}, n.socket.onerror = (
|
|
549
|
-
|
|
550
|
-
}, n.socket.onclose = (
|
|
551
|
-
|
|
531
|
+
e.sessionId && c.set("sessionId", e.sessionId), a.token && c.set("token", a.token);
|
|
532
|
+
const p = `${d}?${c.toString()}`;
|
|
533
|
+
n.socket = new WebSocket(p), n.socket.onopen = (l) => {
|
|
534
|
+
Ce(), t(!0);
|
|
535
|
+
}, n.socket.onmessage = (l) => {
|
|
536
|
+
const f = JSON.parse(l.data);
|
|
537
|
+
be(f);
|
|
538
|
+
}, n.socket.onerror = (l) => {
|
|
539
|
+
k("error"), v(l.message || "Unable to connect voice"), o(l);
|
|
540
|
+
}, n.socket.onclose = (l) => {
|
|
541
|
+
A();
|
|
552
542
|
};
|
|
553
543
|
});
|
|
554
544
|
}
|
|
555
545
|
function be(e) {
|
|
556
|
-
switch (
|
|
546
|
+
switch (e.type) {
|
|
557
547
|
case "pong":
|
|
558
548
|
he();
|
|
559
549
|
break;
|
|
@@ -564,70 +554,66 @@ function be(e) {
|
|
|
564
554
|
ve(e.data);
|
|
565
555
|
break;
|
|
566
556
|
case "renegotiationOffer":
|
|
567
|
-
|
|
557
|
+
Ae(e.data);
|
|
568
558
|
break;
|
|
569
559
|
case "end":
|
|
570
560
|
X();
|
|
571
561
|
break;
|
|
572
562
|
case "error":
|
|
573
|
-
|
|
563
|
+
k("error"), v(e.error || "Unable to connect voice");
|
|
574
564
|
break;
|
|
575
565
|
default:
|
|
576
|
-
console.log("Unknown call event type: ", e.type);
|
|
577
566
|
break;
|
|
578
567
|
}
|
|
579
568
|
}
|
|
580
569
|
async function Te(e) {
|
|
581
570
|
try {
|
|
582
|
-
if (
|
|
571
|
+
if (n.sessionId = e.sessionId, pe(e.sessionId), n.peerConnection) {
|
|
583
572
|
const t = new RTCSessionDescription({
|
|
584
573
|
type: "answer",
|
|
585
574
|
sdp: e.sdp
|
|
586
575
|
});
|
|
587
|
-
|
|
576
|
+
await n.peerConnection.setRemoteDescription(t);
|
|
588
577
|
for (const o of n.localIceCandidates)
|
|
589
578
|
w({
|
|
590
579
|
type: "ice",
|
|
591
580
|
data: {
|
|
592
581
|
candidate: o
|
|
593
582
|
}
|
|
594
|
-
})
|
|
583
|
+
});
|
|
595
584
|
n.localIceCandidates = [];
|
|
596
585
|
for (const o of n.pendingRemoteIceCandidates)
|
|
597
586
|
try {
|
|
598
587
|
const a = new RTCIceCandidate(JSON.parse(o));
|
|
599
|
-
await n.peerConnection.addIceCandidate(a)
|
|
588
|
+
await n.peerConnection.addIceCandidate(a);
|
|
600
589
|
} catch (a) {
|
|
601
|
-
console.error(`Failed to add pending ICE candidate: ${a.message}`);
|
|
602
590
|
}
|
|
603
591
|
n.pendingRemoteIceCandidates = [];
|
|
604
592
|
}
|
|
605
593
|
} catch (t) {
|
|
606
|
-
console.error(`Failed to handle answer: ${t.message}`);
|
|
607
594
|
}
|
|
608
595
|
}
|
|
609
596
|
async function ve(e) {
|
|
610
597
|
try {
|
|
611
598
|
if (n.peerConnection) {
|
|
612
599
|
if (!n.peerConnection.remoteDescription) {
|
|
613
|
-
n.pendingRemoteIceCandidates.push(e.candidate)
|
|
600
|
+
n.pendingRemoteIceCandidates.push(e.candidate);
|
|
614
601
|
return;
|
|
615
602
|
}
|
|
616
603
|
const t = new RTCIceCandidate(JSON.parse(e.candidate));
|
|
617
|
-
await n.peerConnection.addIceCandidate(t)
|
|
604
|
+
await n.peerConnection.addIceCandidate(t);
|
|
618
605
|
}
|
|
619
606
|
} catch (t) {
|
|
620
|
-
console.error(`Failed to add ICE candidate: ${t.message}`);
|
|
621
607
|
}
|
|
622
608
|
}
|
|
623
|
-
async function
|
|
609
|
+
async function Ae(e) {
|
|
624
610
|
try {
|
|
625
|
-
if (
|
|
611
|
+
if (n.peerConnection) {
|
|
626
612
|
const t = new RTCSessionDescription({
|
|
627
613
|
type: "offer",
|
|
628
614
|
sdp: e.sdp
|
|
629
615
|
});
|
|
630
|
-
|
|
616
|
+
await n.peerConnection.setRemoteDescription(t);
|
|
631
617
|
const o = await n.peerConnection.createAnswer();
|
|
632
618
|
await n.peerConnection.setLocalDescription(o), w({
|
|
633
619
|
type: "renegotiationAnswer",
|
|
@@ -637,17 +623,14 @@ async function De(e) {
|
|
|
637
623
|
});
|
|
638
624
|
}
|
|
639
625
|
} catch (t) {
|
|
640
|
-
console.error(`Failed to handle renegotiation offer: ${t.message}`);
|
|
641
626
|
}
|
|
642
627
|
}
|
|
643
628
|
async function xe(e = {}) {
|
|
644
629
|
try {
|
|
645
|
-
if (n.callStatus === "connecting" || n.callStatus === "connected")
|
|
646
|
-
console.log(`Call already in ${n.callStatus} state`);
|
|
630
|
+
if (n.callStatus === "connecting" || n.callStatus === "connected")
|
|
647
631
|
return;
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
n.peerConnection.addTrack(o, n.localStream), console.log(`Added ${o.kind} track`);
|
|
632
|
+
k("connecting"), v(null), n.sessionId = e.sessionId, await we(), ye(), n.localStream.getTracks().forEach((o) => {
|
|
633
|
+
n.peerConnection.addTrack(o, n.localStream);
|
|
651
634
|
}), await Ee(e);
|
|
652
635
|
const t = await n.peerConnection.createOffer();
|
|
653
636
|
await n.peerConnection.setLocalDescription(t), w({
|
|
@@ -655,21 +638,21 @@ async function xe(e = {}) {
|
|
|
655
638
|
data: {
|
|
656
639
|
sdp: t.sdp
|
|
657
640
|
}
|
|
658
|
-
})
|
|
641
|
+
});
|
|
659
642
|
} catch (t) {
|
|
660
|
-
|
|
643
|
+
k("error"), v(t.message || "Unable to connect voice"), j();
|
|
661
644
|
}
|
|
662
645
|
}
|
|
663
646
|
function X() {
|
|
664
647
|
w({
|
|
665
648
|
type: "end"
|
|
666
|
-
}), n.socket && (n.socket.close(), n.socket = null),
|
|
649
|
+
}), n.socket && (n.socket.close(), n.socket = null), k("disconnected"), n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((e) => e.stop()), n.localStream = null), j();
|
|
667
650
|
}
|
|
668
651
|
function qe() {
|
|
669
652
|
if (n.localStream) {
|
|
670
653
|
const e = n.localStream.getAudioTracks()[0];
|
|
671
654
|
if (e)
|
|
672
|
-
return e.enabled = !e.enabled, n.isMuted = !e.enabled,
|
|
655
|
+
return e.enabled = !e.enabled, n.isMuted = !e.enabled, n.isMuted;
|
|
673
656
|
}
|
|
674
657
|
return !1;
|
|
675
658
|
}
|
|
@@ -708,20 +691,20 @@ function ze() {
|
|
|
708
691
|
}
|
|
709
692
|
export {
|
|
710
693
|
T as MESSAGE_ROLES,
|
|
711
|
-
|
|
712
|
-
|
|
694
|
+
Se as authenticate,
|
|
695
|
+
Ne as disconnect,
|
|
713
696
|
X as disconnectCall,
|
|
714
697
|
Ue as getHistory,
|
|
715
698
|
Ge as getInboundAudioEnergy,
|
|
716
699
|
Le as getLocalStream,
|
|
717
|
-
|
|
700
|
+
ke as getMessages,
|
|
718
701
|
ze as getOutboundAudioEnergy,
|
|
719
|
-
|
|
720
|
-
|
|
702
|
+
Pe as initialize,
|
|
703
|
+
Oe as sendMessage,
|
|
721
704
|
Me as setCallCallbacks,
|
|
722
|
-
|
|
705
|
+
$e as setCallbacks,
|
|
723
706
|
xe as startCall,
|
|
724
|
-
|
|
707
|
+
Re as startChat,
|
|
725
708
|
qe as toggleMute
|
|
726
709
|
};
|
|
727
710
|
//# 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 BOT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n AGENT: 'agent', // this is human agent (dock side)\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 { getMessages, 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 */\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 }\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\n if (payload.sessionId) {\n const messagesRes = await getMessages(payload.sessionId)\n messages = (messagesRes?.sessionHistory ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.BOT // 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\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\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\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 }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n // Add user message\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString()\n }\n addMessage(userMessage)\n await sleep(200)\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 const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n addMessage(loadingMessage)\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: JSON.stringify({\n message: text,\n html,\n context\n }),\n signal: currentSession.abortController.signal,\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 } 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 (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 (data.message !== undefined) {\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.BOT,\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 resolve(currentSession.sessionId)\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.session_id ?? 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'\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 messages for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ sessionHistory: Array }>}\n */\nexport async function getMessages(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 return response.json()\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 '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 answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\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","messagesRes","getMessages","_a","msg","searchParams","cleanup","disconnectSocket","isTyping","transport","sendMessage","text","html","context","userMessage","isSocketConnected","socketSend","loadingMessage","headers","fetchEventSource","response","errorMessage","lastIndex","updatedMsg","index","newBotMessage","lastMsg","_c","_f","_g","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","endpoint","errorPayload","getHistory","fetchRequest","pathname","method","body","token","rtcConfig","setCallCallbacks","track","setCallStatus","status","_b","setCallError","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","handleCallServerEvent","action","handleAnswer","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 // Deprecated aliases for backwards compatibility (will be removed in v1.0)\n /** @deprecated Use ASSISTANT instead */\n BOT: 'assistant',\n /** @deprecated Use SUPERVISOR instead */\n AGENT: 'supervisor'\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 { getMessages, 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 */\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 }\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\n if (payload.sessionId) {\n messages = await getMessages(payload.sessionId)\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\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\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 }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n // Add user message\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString()\n }\n addMessage(userMessage)\n await sleep(200)\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 const loadingMessage = {\n role: MESSAGE_ROLES.ASSISTANT,\n text: '',\n loading: true\n }\n addMessage(loadingMessage)\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: JSON.stringify({\n message: text,\n html,\n context\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 } 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 (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 (data.message !== undefined) {\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 resolve(currentSession.sessionId)\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.session_id ?? 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 messages for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ sessionHistory: Array }>}\n */\nexport async function getMessages(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 messages = (data?.sessionHistory ?? []).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 messages\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 '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 answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\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","getMessages","searchParams","cleanup","disconnectSocket","isTyping","transport","sendMessage","text","html","context","userMessage","isSocketConnected","socketSend","loadingMessage","headers","fetchEventSource","response","errorMessage","lastIndex","updatedMsg","msg","index","newBotMessage","lastMsg","_c","_f","_g","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","endpoint","errorPayload","getHistory","fetchRequest","_a","pathname","method","body","token","rtcConfig","setCallCallbacks","track","setCallStatus","status","_b","setCallError","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","handleCallServerEvent","action","handleAnswer","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
|
@@ -125,18 +125,7 @@ export async function startChat(payload = {}) {
|
|
|
125
125
|
let messages = []
|
|
126
126
|
|
|
127
127
|
if (payload.sessionId) {
|
|
128
|
-
|
|
129
|
-
messages = (messagesRes?.sessionHistory ?? []).map((msg) => ({
|
|
130
|
-
id: msg.id,
|
|
131
|
-
text: msg.text,
|
|
132
|
-
role: msg.youtubeVideo
|
|
133
|
-
? MESSAGE_ROLES.BOT // for youtube video messages, role is "system" from backend, we need to make it "assistant"
|
|
134
|
-
: msg.role,
|
|
135
|
-
timestamp: msg.timestamp,
|
|
136
|
-
video: msg.youtubeVideo,
|
|
137
|
-
channel: msg.channel,
|
|
138
|
-
done: true
|
|
139
|
-
}))
|
|
128
|
+
messages = await getMessages(payload.sessionId)
|
|
140
129
|
}
|
|
141
130
|
|
|
142
131
|
const searchParams = new URLSearchParams()
|
|
@@ -257,7 +246,7 @@ export function sendMessage({ text, html, context }) {
|
|
|
257
246
|
}
|
|
258
247
|
|
|
259
248
|
const loadingMessage = {
|
|
260
|
-
role: MESSAGE_ROLES.
|
|
249
|
+
role: MESSAGE_ROLES.ASSISTANT,
|
|
261
250
|
text: '',
|
|
262
251
|
loading: true
|
|
263
252
|
}
|
|
@@ -292,6 +281,7 @@ export function sendMessage({ text, html, context }) {
|
|
|
292
281
|
context
|
|
293
282
|
}),
|
|
294
283
|
signal: currentSession.abortController.signal,
|
|
284
|
+
openWhenHidden: true,
|
|
295
285
|
onopen: async (response) => {
|
|
296
286
|
if (!response.ok) {
|
|
297
287
|
console.error('Failed to send message bad response: ', response)
|
|
@@ -336,7 +326,7 @@ export function sendMessage({ text, html, context }) {
|
|
|
336
326
|
} else if (data.streamId !== currentSession.lastStreamId) {
|
|
337
327
|
currentSession.lastStreamId = data.streamId
|
|
338
328
|
const newBotMessage = {
|
|
339
|
-
role: MESSAGE_ROLES.
|
|
329
|
+
role: MESSAGE_ROLES.ASSISTANT,
|
|
340
330
|
text: '',
|
|
341
331
|
loading: true
|
|
342
332
|
}
|
package/src/constants.js
CHANGED
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export const MESSAGE_ROLES = {
|
|
6
|
-
|
|
6
|
+
ASSISTANT: 'assistant', // this can be automated or LLM AI Agent response
|
|
7
7
|
USER: 'user', // this is widget user
|
|
8
|
-
|
|
9
|
-
SYSTEM: 'system' // this is system message, for ex "Agent joined" / "Agent left"
|
|
8
|
+
SUPERVISOR: 'supervisor', // this is human supervisor (ex. Samespace Dock agent, or Resolve human agent)
|
|
9
|
+
SYSTEM: 'system', // this is system message, for ex "Agent joined" / "Agent left"
|
|
10
|
+
|
|
11
|
+
// Deprecated aliases for backwards compatibility (will be removed in v1.0)
|
|
12
|
+
/** @deprecated Use ASSISTANT instead */
|
|
13
|
+
BOT: 'assistant',
|
|
14
|
+
/** @deprecated Use SUPERVISOR instead */
|
|
15
|
+
AGENT: 'supervisor'
|
|
10
16
|
}
|
package/src/http.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getCredentials, getExternalId } from './chat.js'
|
|
7
|
+
import { MESSAGE_ROLES } from './constants.js'
|
|
7
8
|
|
|
8
9
|
const AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'
|
|
9
10
|
const INITIALIZATION_ERROR = 'Chat SDK not initialized'
|
|
@@ -67,7 +68,20 @@ export async function getMessages(sessionId) {
|
|
|
67
68
|
throw new Error('Unable to load messages, please try again later')
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
const data = await response.json()
|
|
72
|
+
const messages = (data?.sessionHistory ?? []).map((msg) => ({
|
|
73
|
+
id: msg.id,
|
|
74
|
+
text: msg.text,
|
|
75
|
+
role: msg.youtubeVideo
|
|
76
|
+
? MESSAGE_ROLES.ASSISTANT // for youtube video messages, role is "system" from backend, we need to make it "assistant"
|
|
77
|
+
: msg.role,
|
|
78
|
+
timestamp: msg.timestamp,
|
|
79
|
+
video: msg.youtubeVideo,
|
|
80
|
+
channel: msg.channel,
|
|
81
|
+
done: true
|
|
82
|
+
}))
|
|
83
|
+
|
|
84
|
+
return messages
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
/**
|