@origonai/web-chat-sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/origon-chat-sdk.js +244 -260
- package/dist/origon-chat-sdk.js.map +1 -1
- package/package.json +1 -1
- package/src/chat.js +51 -99
package/dist/origon-chat-sdk.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { fetchEventSource as
|
|
2
|
-
function
|
|
3
|
-
const e = Date.now(),
|
|
4
|
-
crypto.getRandomValues(
|
|
5
|
-
const s = [...
|
|
1
|
+
import { fetchEventSource as K } from "@microsoft/fetch-event-source";
|
|
2
|
+
function j() {
|
|
3
|
+
const e = Date.now(), t = new Uint8Array(16);
|
|
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
|
+
const s = [...t].map((a) => a.toString(16).padStart(2, "0")).join("");
|
|
6
6
|
return `${s.slice(0, 8)}-${s.slice(8, 12)}-${s.slice(12, 16)}-${s.slice(
|
|
7
7
|
16,
|
|
8
8
|
20
|
|
9
9
|
)}-${s.slice(20)}`;
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function V() {
|
|
12
12
|
if (localStorage.getItem("chatDeviceId"))
|
|
13
13
|
return localStorage.getItem("chatDeviceId");
|
|
14
|
-
const e =
|
|
14
|
+
const e = j();
|
|
15
15
|
return localStorage.setItem("chatDeviceId", e), e;
|
|
16
16
|
}
|
|
17
|
-
async function
|
|
18
|
-
return new Promise((
|
|
17
|
+
async function Q(e) {
|
|
18
|
+
return new Promise((t) => setTimeout(t, e));
|
|
19
19
|
}
|
|
20
|
-
function
|
|
21
|
-
let
|
|
20
|
+
function Y(e) {
|
|
21
|
+
let t;
|
|
22
22
|
try {
|
|
23
23
|
const s = new URL(e);
|
|
24
|
-
|
|
24
|
+
t = `wss://${s.hostname}${s.pathname}/audio`;
|
|
25
25
|
} catch (s) {
|
|
26
26
|
console.error("getCallServerEndpoint: Invalid base URL: ", e);
|
|
27
27
|
}
|
|
28
|
-
return
|
|
28
|
+
return t;
|
|
29
29
|
}
|
|
30
|
-
const
|
|
30
|
+
const b = {
|
|
31
31
|
BOT: "assistant",
|
|
32
32
|
// this can be automated or LLM AI Agent response
|
|
33
33
|
USER: "user",
|
|
@@ -37,7 +37,7 @@ const k = {
|
|
|
37
37
|
SYSTEM: "system"
|
|
38
38
|
// this is system message, for ex "Agent joined" / "Agent left"
|
|
39
39
|
};
|
|
40
|
-
function
|
|
40
|
+
function L(e = {}) {
|
|
41
41
|
return {
|
|
42
42
|
credentials: void 0,
|
|
43
43
|
authenticated: !1,
|
|
@@ -46,190 +46,174 @@ function _(e = {}) {
|
|
|
46
46
|
requestId: void 0,
|
|
47
47
|
sseUrl: void 0,
|
|
48
48
|
abortController: void 0,
|
|
49
|
-
liveAgent: !1,
|
|
50
49
|
lastStreamId: void 0,
|
|
51
50
|
messages: [],
|
|
52
51
|
callbacks: e
|
|
53
52
|
};
|
|
54
53
|
}
|
|
55
|
-
let
|
|
56
|
-
function
|
|
57
|
-
|
|
54
|
+
let o = L();
|
|
55
|
+
function Se(e) {
|
|
56
|
+
o.callbacks = { ...o.callbacks, ...e };
|
|
58
57
|
}
|
|
59
|
-
function
|
|
60
|
-
console.log("Initializing chat...", e),
|
|
58
|
+
function Ce(e) {
|
|
59
|
+
console.log("Initializing chat...", e), o.credentials = e, e.token && (o.authenticated = !0);
|
|
61
60
|
}
|
|
62
|
-
function
|
|
63
|
-
return
|
|
61
|
+
function Z() {
|
|
62
|
+
return o.credentials;
|
|
64
63
|
}
|
|
65
|
-
function
|
|
66
|
-
var
|
|
67
|
-
e && e !==
|
|
64
|
+
function X(e) {
|
|
65
|
+
var t, s;
|
|
66
|
+
e && e !== o.sessionId && (o.sessionId = e, (s = (t = o.callbacks).onSessionUpdate) == null || s.call(t, e));
|
|
68
67
|
}
|
|
69
|
-
async function
|
|
70
|
-
var
|
|
68
|
+
async function Ie(e = {}) {
|
|
69
|
+
var t;
|
|
71
70
|
try {
|
|
72
|
-
console.log("startChat: ", e,
|
|
71
|
+
console.log("startChat: ", e, o);
|
|
73
72
|
let s = null;
|
|
74
|
-
|
|
73
|
+
o.authenticated ? s = o.configData : (s = await oe(o.credentials), o.authenticated = !0, o.configData = s);
|
|
75
74
|
let a = [];
|
|
76
75
|
if (e.sessionId) {
|
|
77
|
-
const
|
|
78
|
-
a = ((
|
|
76
|
+
const d = await se(e.sessionId);
|
|
77
|
+
a = ((t = d == null ? void 0 : d.sessionHistory) != null ? t : []).map((r) => ({
|
|
79
78
|
id: r.id,
|
|
80
79
|
text: r.text,
|
|
81
|
-
role: r.youtubeVideo ?
|
|
80
|
+
role: r.youtubeVideo ? b.BOT : r.role,
|
|
82
81
|
timestamp: r.timestamp,
|
|
83
82
|
video: r.youtubeVideo,
|
|
84
83
|
channel: r.channel,
|
|
85
84
|
done: !0
|
|
86
85
|
}));
|
|
87
86
|
}
|
|
88
|
-
const
|
|
89
|
-
externalId:
|
|
87
|
+
const l = new URLSearchParams({
|
|
88
|
+
externalId: $()
|
|
90
89
|
});
|
|
91
|
-
return
|
|
92
|
-
sessionId:
|
|
90
|
+
return o.sseUrl = `${o.credentials.endpoint}?${l.toString()}`, o.sessionId = e.sessionId, o.messages = a, console.log("Chat initiated successfully"), {
|
|
91
|
+
sessionId: o.sessionId,
|
|
93
92
|
messages: a,
|
|
94
93
|
configData: s
|
|
95
94
|
};
|
|
96
95
|
} catch (s) {
|
|
97
|
-
throw console.error(`Failed to start chat: ${s.message}`),
|
|
96
|
+
throw console.error(`Failed to start chat: ${s.message}`), J(), s;
|
|
98
97
|
}
|
|
99
98
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
99
|
+
function he() {
|
|
100
|
+
J();
|
|
102
101
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
const { callbacks: e, credentials:
|
|
106
|
-
|
|
102
|
+
function J() {
|
|
103
|
+
o.abortController && o.abortController.abort();
|
|
104
|
+
const { callbacks: e, credentials: t } = o;
|
|
105
|
+
o = L(e), o.credentials = t, console.log("Chat session cleaned up");
|
|
107
106
|
}
|
|
108
|
-
function
|
|
107
|
+
function $() {
|
|
109
108
|
var e;
|
|
110
|
-
return (e =
|
|
109
|
+
return (e = o.credentials) != null && e.externalId ? o.credentials.externalId : V();
|
|
111
110
|
}
|
|
112
|
-
function
|
|
111
|
+
function ke({ text: e, html: t }) {
|
|
113
112
|
return new Promise((s, a) => {
|
|
114
113
|
(async () => {
|
|
115
|
-
var
|
|
114
|
+
var l, d, r, h, i, k, M;
|
|
116
115
|
try {
|
|
117
|
-
const
|
|
118
|
-
role:
|
|
116
|
+
const I = {
|
|
117
|
+
role: b.USER,
|
|
119
118
|
text: e,
|
|
120
|
-
html:
|
|
119
|
+
html: t,
|
|
121
120
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
122
121
|
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const C = new URL(t.sseUrl);
|
|
134
|
-
t.sessionId && C.searchParams.set("sessionId", t.sessionId), t.requestId && C.searchParams.set("requestId", t.requestId), t.lastStreamId = void 0, t.abortController = new AbortController();
|
|
135
|
-
const I = {
|
|
122
|
+
o.messages = [...o.messages, I], (d = (l = o.callbacks).onMessageAdd) == null || d.call(l, I), await Q(200);
|
|
123
|
+
const w = {
|
|
124
|
+
role: b.BOT,
|
|
125
|
+
text: "",
|
|
126
|
+
loading: !0
|
|
127
|
+
};
|
|
128
|
+
o.messages = [...o.messages, w], (h = (r = o.callbacks).onMessageAdd) == null || h.call(r, w);
|
|
129
|
+
const g = new URL(o.sseUrl);
|
|
130
|
+
o.sessionId && g.searchParams.set("sessionId", o.sessionId), o.requestId && g.searchParams.set("requestId", o.requestId), o.lastStreamId = void 0, o.abortController = new AbortController();
|
|
131
|
+
const A = {
|
|
136
132
|
"Content-Type": "application/json"
|
|
137
133
|
};
|
|
138
|
-
(
|
|
134
|
+
(i = o.credentials) != null && i.token && (A.Authorization = `Bearer ${o.credentials.token}`), await K(g.toString(), {
|
|
139
135
|
method: "POST",
|
|
140
|
-
headers:
|
|
136
|
+
headers: A,
|
|
141
137
|
body: JSON.stringify({
|
|
142
138
|
message: e,
|
|
143
|
-
html:
|
|
139
|
+
html: t
|
|
144
140
|
}),
|
|
145
|
-
signal:
|
|
146
|
-
onopen: async (
|
|
147
|
-
if (!
|
|
148
|
-
throw console.error("Failed to send message bad response: ",
|
|
141
|
+
signal: o.abortController.signal,
|
|
142
|
+
onopen: async (u) => {
|
|
143
|
+
if (!u.ok)
|
|
144
|
+
throw console.error("Failed to send message bad response: ", u), new Error("Failed to send message");
|
|
149
145
|
},
|
|
150
|
-
onmessage: (
|
|
151
|
-
var
|
|
152
|
-
console.log("Event: ",
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
else if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const m = c.action === "ended" || c.action === "left";
|
|
170
|
-
m && (t.liveAgent = !1), (L = (N = t.callbacks).onMessagesUpdate) == null || L.call(N, [...t.messages]), (F = (q = t.callbacks).onLiveAgentMode) == null || F.call(q, !m);
|
|
171
|
-
}
|
|
172
|
-
(c.action === "ended" || c.action === "left") && (t.liveAgent = !1);
|
|
173
|
-
break;
|
|
174
|
-
default:
|
|
175
|
-
break;
|
|
146
|
+
onmessage: (u) => {
|
|
147
|
+
var y, O, x, P, U, D, N, q, F;
|
|
148
|
+
console.log("Event: ", u);
|
|
149
|
+
const c = JSON.parse(u.data);
|
|
150
|
+
if (c.status === "connected")
|
|
151
|
+
o.sessionId = c.sessionId, o.requestId = c.requestId;
|
|
152
|
+
else if (c.message !== void 0) {
|
|
153
|
+
if (c.streamId !== void 0) {
|
|
154
|
+
if (o.lastStreamId === void 0)
|
|
155
|
+
o.lastStreamId = c.streamId;
|
|
156
|
+
else if (c.streamId !== o.lastStreamId) {
|
|
157
|
+
o.lastStreamId = c.streamId;
|
|
158
|
+
const p = {
|
|
159
|
+
role: b.BOT,
|
|
160
|
+
text: "",
|
|
161
|
+
loading: !0
|
|
162
|
+
};
|
|
163
|
+
o.messages = [...o.messages, p], (O = (y = o.callbacks).onMessageAdd) == null || O.call(y, p);
|
|
164
|
+
}
|
|
176
165
|
}
|
|
177
|
-
|
|
178
|
-
let f = t.messages;
|
|
179
|
-
i.streamId !== void 0 && (t.lastStreamId === void 0 ? t.lastStreamId = i.streamId : i.streamId !== t.lastStreamId && (t.lastStreamId = i.streamId, f = [
|
|
166
|
+
const S = o.messages.length - 1, f = o.messages[S], T = {
|
|
180
167
|
...f,
|
|
181
|
-
{
|
|
182
|
-
role: k.BOT,
|
|
183
|
-
text: "",
|
|
184
|
-
loading: !0
|
|
185
|
-
}
|
|
186
|
-
])), t.messages = f.map((c, m) => {
|
|
187
|
-
var b;
|
|
188
|
-
return m === f.length - 1 ? {
|
|
189
|
-
...c,
|
|
190
|
-
loading: !1,
|
|
191
|
-
text: (c.text || "") + i.message,
|
|
192
|
-
done: (b = i.done) != null ? b : c.done
|
|
193
|
-
} : c;
|
|
194
|
-
}), (z = (J = t.callbacks).onMessagesUpdate) == null || z.call(J, [...t.messages]), i.done && s(t.sessionId), t.sessionId = (G = i.session_id) != null ? G : t.sessionId, t.requestId = (W = i.requestId) != null ? W : t.requestId;
|
|
195
|
-
} else if (i.error) {
|
|
196
|
-
const f = "Failed to connect to the system";
|
|
197
|
-
t.messages = t.messages.map((c, m) => m === t.messages.length - 1 ? {
|
|
198
|
-
...c,
|
|
199
168
|
loading: !1,
|
|
200
|
-
|
|
201
|
-
|
|
169
|
+
text: (f.text || "") + c.message,
|
|
170
|
+
done: (x = c.done) != null ? x : f.done
|
|
171
|
+
};
|
|
172
|
+
o.messages = o.messages.map(
|
|
173
|
+
(p, R) => R === S ? T : p
|
|
174
|
+
), (U = (P = o.callbacks).onMessageUpdate) == null || U.call(P, S, T), c.done && s(o.sessionId), o.sessionId = (D = c.session_id) != null ? D : o.sessionId, o.requestId = (N = c.requestId) != null ? N : o.requestId;
|
|
175
|
+
} else if (c.error) {
|
|
176
|
+
const S = "Failed to connect to the system", f = o.messages.length - 1, p = {
|
|
177
|
+
...o.messages[f],
|
|
178
|
+
loading: !1,
|
|
179
|
+
errorText: S
|
|
180
|
+
};
|
|
181
|
+
o.messages = o.messages.map(
|
|
182
|
+
(R, _) => _ === f ? p : R
|
|
183
|
+
), (F = (q = o.callbacks).onMessageUpdate) == null || F.call(q, f, p), a(new Error(S));
|
|
202
184
|
}
|
|
203
185
|
},
|
|
204
|
-
onerror: (
|
|
205
|
-
throw
|
|
186
|
+
onerror: (u) => {
|
|
187
|
+
throw u;
|
|
206
188
|
},
|
|
207
189
|
openWhenHidden: !0
|
|
208
190
|
});
|
|
209
|
-
} catch (
|
|
210
|
-
console.error("Failed to send message: ",
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
...I,
|
|
191
|
+
} catch (I) {
|
|
192
|
+
console.error("Failed to send message: ", I);
|
|
193
|
+
const w = "Failed to connect to the system", g = o.messages.length - 1, u = {
|
|
194
|
+
...o.messages[g],
|
|
214
195
|
loading: !1,
|
|
215
|
-
errorText:
|
|
196
|
+
errorText: w,
|
|
216
197
|
done: !0
|
|
217
|
-
}
|
|
198
|
+
};
|
|
199
|
+
o.messages = o.messages.map(
|
|
200
|
+
(c, y) => y === g ? u : c
|
|
201
|
+
), (M = (k = o.callbacks).onMessageUpdate) == null || M.call(k, g, u), a(I);
|
|
218
202
|
}
|
|
219
203
|
})();
|
|
220
204
|
});
|
|
221
205
|
}
|
|
222
|
-
const
|
|
223
|
-
let
|
|
206
|
+
const ee = "Something went wrong initializing the chat", ne = "Chat SDK not initialized";
|
|
207
|
+
let z = {
|
|
224
208
|
endpoint: null
|
|
225
209
|
};
|
|
226
|
-
function
|
|
227
|
-
|
|
210
|
+
function te(e) {
|
|
211
|
+
z = {
|
|
228
212
|
endpoint: e.endpoint
|
|
229
213
|
};
|
|
230
214
|
}
|
|
231
|
-
async function
|
|
232
|
-
const { endpoint:
|
|
215
|
+
async function oe(e) {
|
|
216
|
+
const { endpoint: t } = e, s = `${t}/config`, a = await fetch(s, {
|
|
233
217
|
method: "GET",
|
|
234
218
|
headers: {
|
|
235
219
|
"Content-Type": "application/json"
|
|
@@ -237,41 +221,41 @@ async function le(e) {
|
|
|
237
221
|
});
|
|
238
222
|
if (!a.ok) {
|
|
239
223
|
const r = await a.json();
|
|
240
|
-
throw new Error((r == null ? void 0 : r.error) ||
|
|
224
|
+
throw new Error((r == null ? void 0 : r.error) || ee);
|
|
241
225
|
}
|
|
242
|
-
const
|
|
243
|
-
return
|
|
226
|
+
const d = (await a.json()).data;
|
|
227
|
+
return te({ endpoint: t }), d;
|
|
244
228
|
}
|
|
245
|
-
async function
|
|
229
|
+
async function we() {
|
|
246
230
|
const e = new URLSearchParams({
|
|
247
|
-
externalId:
|
|
248
|
-
}),
|
|
249
|
-
if (!
|
|
231
|
+
externalId: $()
|
|
232
|
+
}), t = await G(`/sessions?${e.toString()}`, "GET");
|
|
233
|
+
if (!t.ok)
|
|
250
234
|
throw new Error("Unable to load history, please try again later");
|
|
251
|
-
return
|
|
235
|
+
return t.json();
|
|
252
236
|
}
|
|
253
|
-
async function
|
|
254
|
-
const
|
|
237
|
+
async function se(e) {
|
|
238
|
+
const t = new URLSearchParams({
|
|
255
239
|
sessionId: e
|
|
256
|
-
}), s = await
|
|
240
|
+
}), s = await G(`/session?${t.toString()}`, "GET");
|
|
257
241
|
if (!s.ok)
|
|
258
242
|
throw new Error("Unable to load messages, please try again later");
|
|
259
243
|
return s.json();
|
|
260
244
|
}
|
|
261
|
-
async function
|
|
262
|
-
const { endpoint: a } =
|
|
245
|
+
async function G(e, t = "GET", s = null) {
|
|
246
|
+
const { endpoint: a } = z;
|
|
263
247
|
if (!a)
|
|
264
|
-
throw new Error(
|
|
265
|
-
const
|
|
266
|
-
return fetch(
|
|
248
|
+
throw new Error(ne);
|
|
249
|
+
const l = `${a}${e}`;
|
|
250
|
+
return fetch(l, {
|
|
267
251
|
headers: {
|
|
268
252
|
"Content-Type": "application/json"
|
|
269
253
|
},
|
|
270
|
-
method:
|
|
254
|
+
method: t,
|
|
271
255
|
body: s ? JSON.stringify(s) : null
|
|
272
256
|
});
|
|
273
257
|
}
|
|
274
|
-
function
|
|
258
|
+
function W(e = {}) {
|
|
275
259
|
return {
|
|
276
260
|
sessionId: void 0,
|
|
277
261
|
socket: null,
|
|
@@ -289,30 +273,30 @@ function Q(e = {}) {
|
|
|
289
273
|
pendingRemoteIceCandidates: []
|
|
290
274
|
};
|
|
291
275
|
}
|
|
292
|
-
let n =
|
|
293
|
-
const
|
|
276
|
+
let n = W();
|
|
277
|
+
const ae = {
|
|
294
278
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }, { urls: "stun:stun1.l.google.com:19302" }]
|
|
295
279
|
};
|
|
296
|
-
function
|
|
280
|
+
function ye(e) {
|
|
297
281
|
n.callbacks = { ...n.callbacks, ...e };
|
|
298
282
|
}
|
|
299
|
-
function
|
|
300
|
-
n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((
|
|
283
|
+
function B() {
|
|
284
|
+
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), v();
|
|
301
285
|
const e = n.callbacks;
|
|
302
|
-
n =
|
|
286
|
+
n = W(e), console.log("Call session cleaned up");
|
|
303
287
|
}
|
|
304
|
-
function
|
|
305
|
-
var
|
|
306
|
-
n.callStatus = e, (s = (
|
|
288
|
+
function m(e) {
|
|
289
|
+
var t, s;
|
|
290
|
+
n.callStatus = e, (s = (t = n.callbacks).onCallStatus) == null || s.call(t, e);
|
|
307
291
|
}
|
|
308
292
|
function E(e) {
|
|
309
|
-
var
|
|
310
|
-
(s = (
|
|
293
|
+
var t, s;
|
|
294
|
+
(s = (t = n.callbacks).onCallError) == null || s.call(t, e);
|
|
311
295
|
}
|
|
312
296
|
function v() {
|
|
313
297
|
n.pingInterval && (clearInterval(n.pingInterval), n.pingInterval = null);
|
|
314
298
|
}
|
|
315
|
-
function
|
|
299
|
+
function re() {
|
|
316
300
|
v(), n.pingInterval = setInterval(() => {
|
|
317
301
|
if (n.socket && n.socket.readyState === WebSocket.OPEN) {
|
|
318
302
|
n.pingCount++;
|
|
@@ -321,15 +305,15 @@ function ge() {
|
|
|
321
305
|
timestamp: Date.now(),
|
|
322
306
|
count: n.pingCount
|
|
323
307
|
};
|
|
324
|
-
|
|
308
|
+
C(e), console.log(`Sending keep-alive ping #${n.pingCount}`);
|
|
325
309
|
} else
|
|
326
310
|
console.log("Socket not open, stopping ping interval"), v();
|
|
327
311
|
}, 1e4);
|
|
328
312
|
}
|
|
329
|
-
function
|
|
313
|
+
function ce() {
|
|
330
314
|
n.lastPongTime = Date.now(), console.log(`Received pong #${n.pingCount}`);
|
|
331
315
|
}
|
|
332
|
-
function
|
|
316
|
+
function C(e) {
|
|
333
317
|
if (!n.socket) {
|
|
334
318
|
console.error("Failed to send event: no socket instance");
|
|
335
319
|
return;
|
|
@@ -340,7 +324,7 @@ function S(e) {
|
|
|
340
324
|
}
|
|
341
325
|
n.socket.send(JSON.stringify(e));
|
|
342
326
|
}
|
|
343
|
-
async function
|
|
327
|
+
async function ie() {
|
|
344
328
|
try {
|
|
345
329
|
n.localStream = await navigator.mediaDevices.getUserMedia({
|
|
346
330
|
audio: !0,
|
|
@@ -350,40 +334,40 @@ async function pe() {
|
|
|
350
334
|
throw console.error(`Failed to get audio media: ${e.message}`), e;
|
|
351
335
|
}
|
|
352
336
|
}
|
|
353
|
-
function
|
|
354
|
-
n.peerConnection = new RTCPeerConnection(
|
|
337
|
+
function le() {
|
|
338
|
+
n.peerConnection = new RTCPeerConnection(ae), n.peerConnection.onicecandidate = (e) => {
|
|
355
339
|
if (e.candidate) {
|
|
356
|
-
const
|
|
357
|
-
n.peerConnection && n.peerConnection.remoteDescription ? (
|
|
340
|
+
const t = JSON.stringify(e.candidate);
|
|
341
|
+
n.peerConnection && n.peerConnection.remoteDescription ? (C({
|
|
358
342
|
type: "ice",
|
|
359
343
|
data: {
|
|
360
|
-
candidate:
|
|
344
|
+
candidate: t
|
|
361
345
|
}
|
|
362
|
-
}), console.log("Sent ICE candidate immediately")) : (n.localIceCandidates.push(
|
|
346
|
+
}), console.log("Sent ICE candidate immediately")) : (n.localIceCandidates.push(t), console.log("Queued local ICE candidate"));
|
|
363
347
|
}
|
|
364
348
|
}, n.peerConnection.ontrack = (e) => {
|
|
365
|
-
console.log("Received remote audio stream"), 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(() => console.log("🔊 remote audio playing")).catch((
|
|
349
|
+
console.log("Received remote audio stream"), 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(() => console.log("🔊 remote audio playing")).catch((t) => console.error("❌ playback error:", t));
|
|
366
350
|
}, n.peerConnection.onconnectionstatechange = () => {
|
|
367
351
|
const e = n.peerConnection.connectionState;
|
|
368
|
-
console.log(`Connection state: ${e}`), e === "connected" ?
|
|
352
|
+
console.log(`Connection state: ${e}`), e === "connected" ? m("connected") : (e === "disconnected" || e === "closed") && (m("disconnected"), H());
|
|
369
353
|
}, n.peerConnection.oniceconnectionstatechange = () => {
|
|
370
354
|
console.log(`ICE connection state: ${n.peerConnection.iceConnectionState}`);
|
|
371
355
|
};
|
|
372
356
|
}
|
|
373
|
-
function
|
|
374
|
-
return new Promise((
|
|
357
|
+
function de(e) {
|
|
358
|
+
return new Promise((t, s) => {
|
|
375
359
|
if (n.socket && (n.socket.readyState === WebSocket.CONNECTING || n.socket.readyState === WebSocket.OPEN)) {
|
|
376
|
-
console.log("Socket in connecting/open state, returning."),
|
|
360
|
+
console.log("Socket in connecting/open state, returning."), t(n.socket.readyState === WebSocket.OPEN);
|
|
377
361
|
return;
|
|
378
362
|
}
|
|
379
363
|
console.log("Initializing socket connection..");
|
|
380
|
-
const a =
|
|
364
|
+
const a = Z();
|
|
381
365
|
if (!a || !a.endpoint) {
|
|
382
366
|
s(new Error("SDK not initialized. Please initialize SDK first."));
|
|
383
367
|
return;
|
|
384
368
|
}
|
|
385
|
-
const
|
|
386
|
-
if (!
|
|
369
|
+
const l = Y(a.endpoint);
|
|
370
|
+
if (!l) {
|
|
387
371
|
s(
|
|
388
372
|
new Error(
|
|
389
373
|
"Invalid endpoint while initializing SDK. Please check the endpoint and try again."
|
|
@@ -391,58 +375,58 @@ function Se(e) {
|
|
|
391
375
|
);
|
|
392
376
|
return;
|
|
393
377
|
}
|
|
394
|
-
const
|
|
395
|
-
externalId:
|
|
378
|
+
const d = $(), r = new URLSearchParams({
|
|
379
|
+
externalId: d
|
|
396
380
|
});
|
|
397
381
|
e.sessionId && r.set("sessionId", e.sessionId), a.token && r.set("token", a.token);
|
|
398
|
-
const h = `${
|
|
399
|
-
n.socket = new WebSocket(h), n.socket.onopen = (
|
|
400
|
-
console.log("Socket connection established: ",
|
|
401
|
-
}, n.socket.onmessage = (
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
}, n.socket.onerror = (
|
|
405
|
-
console.error("Socket error: ",
|
|
406
|
-
}, n.socket.onclose = (
|
|
407
|
-
console.log("Socket connection closed: ",
|
|
382
|
+
const h = `${l}?${r.toString()}`;
|
|
383
|
+
n.socket = new WebSocket(h), n.socket.onopen = (i) => {
|
|
384
|
+
console.log("Socket connection established: ", i), re(), t(!0);
|
|
385
|
+
}, n.socket.onmessage = (i) => {
|
|
386
|
+
const k = JSON.parse(i.data);
|
|
387
|
+
ue(k);
|
|
388
|
+
}, n.socket.onerror = (i) => {
|
|
389
|
+
console.error("Socket error: ", i), m("error"), E(i.message || "Unable to connect voice"), s(i);
|
|
390
|
+
}, n.socket.onclose = (i) => {
|
|
391
|
+
console.log("Socket connection closed: ", i), v();
|
|
408
392
|
};
|
|
409
393
|
});
|
|
410
394
|
}
|
|
411
|
-
function
|
|
395
|
+
function ue(e) {
|
|
412
396
|
switch (console.log("Handling socket server event: ", e), e.type) {
|
|
413
397
|
case "pong":
|
|
414
|
-
|
|
398
|
+
ce();
|
|
415
399
|
break;
|
|
416
400
|
case "answer":
|
|
417
|
-
|
|
401
|
+
ge(e.data);
|
|
418
402
|
break;
|
|
419
403
|
case "ice":
|
|
420
|
-
|
|
404
|
+
fe(e.data);
|
|
421
405
|
break;
|
|
422
406
|
case "renegotiationOffer":
|
|
423
|
-
|
|
407
|
+
pe(e.data);
|
|
424
408
|
break;
|
|
425
409
|
case "end":
|
|
426
|
-
|
|
410
|
+
H();
|
|
427
411
|
break;
|
|
428
412
|
case "error":
|
|
429
|
-
|
|
413
|
+
m("error"), E(e.error || "Unable to connect voice");
|
|
430
414
|
break;
|
|
431
415
|
default:
|
|
432
416
|
console.log("Unknown call event type: ", e.type);
|
|
433
417
|
break;
|
|
434
418
|
}
|
|
435
419
|
}
|
|
436
|
-
async function
|
|
420
|
+
async function ge(e) {
|
|
437
421
|
try {
|
|
438
|
-
if (console.log("Received answer"), n.sessionId = e.sessionId,
|
|
439
|
-
const
|
|
422
|
+
if (console.log("Received answer"), n.sessionId = e.sessionId, X(e.sessionId), n.peerConnection) {
|
|
423
|
+
const t = new RTCSessionDescription({
|
|
440
424
|
type: "answer",
|
|
441
425
|
sdp: e.sdp
|
|
442
426
|
});
|
|
443
|
-
console.log("Setting remote description answer: ",
|
|
427
|
+
console.log("Setting remote description answer: ", t), await n.peerConnection.setRemoteDescription(t), console.log("Remote description set");
|
|
444
428
|
for (const s of n.localIceCandidates)
|
|
445
|
-
|
|
429
|
+
C({
|
|
446
430
|
type: "ice",
|
|
447
431
|
data: {
|
|
448
432
|
candidate: s
|
|
@@ -458,70 +442,70 @@ async function Ie(e) {
|
|
|
458
442
|
}
|
|
459
443
|
n.pendingRemoteIceCandidates = [];
|
|
460
444
|
}
|
|
461
|
-
} catch (
|
|
462
|
-
console.error(`Failed to handle answer: ${
|
|
445
|
+
} catch (t) {
|
|
446
|
+
console.error(`Failed to handle answer: ${t.message}`);
|
|
463
447
|
}
|
|
464
448
|
}
|
|
465
|
-
async function
|
|
449
|
+
async function fe(e) {
|
|
466
450
|
try {
|
|
467
451
|
if (n.peerConnection) {
|
|
468
452
|
if (!n.peerConnection.remoteDescription) {
|
|
469
453
|
n.pendingRemoteIceCandidates.push(e.candidate), console.log("Queued remote ICE candidate - remote description not set");
|
|
470
454
|
return;
|
|
471
455
|
}
|
|
472
|
-
const
|
|
473
|
-
await n.peerConnection.addIceCandidate(
|
|
456
|
+
const t = new RTCIceCandidate(JSON.parse(e.candidate));
|
|
457
|
+
await n.peerConnection.addIceCandidate(t), console.log("Added ICE candidate");
|
|
474
458
|
}
|
|
475
|
-
} catch (
|
|
476
|
-
console.error(`Failed to add ICE candidate: ${
|
|
459
|
+
} catch (t) {
|
|
460
|
+
console.error(`Failed to add ICE candidate: ${t.message}`);
|
|
477
461
|
}
|
|
478
462
|
}
|
|
479
|
-
async function
|
|
463
|
+
async function pe(e) {
|
|
480
464
|
try {
|
|
481
465
|
if (console.log("Received renegotiation offer"), n.peerConnection) {
|
|
482
|
-
const
|
|
466
|
+
const t = new RTCSessionDescription({
|
|
483
467
|
type: "offer",
|
|
484
468
|
sdp: e.sdp
|
|
485
469
|
});
|
|
486
|
-
console.log("Setting remote description offer: ",
|
|
470
|
+
console.log("Setting remote description offer: ", t), await n.peerConnection.setRemoteDescription(t), console.log("Remote description set");
|
|
487
471
|
const s = await n.peerConnection.createAnswer();
|
|
488
|
-
await n.peerConnection.setLocalDescription(s),
|
|
472
|
+
await n.peerConnection.setLocalDescription(s), C({
|
|
489
473
|
type: "renegotiationAnswer",
|
|
490
474
|
data: {
|
|
491
475
|
sdp: s.sdp
|
|
492
476
|
}
|
|
493
477
|
});
|
|
494
478
|
}
|
|
495
|
-
} catch (
|
|
496
|
-
console.error(`Failed to handle renegotiation offer: ${
|
|
479
|
+
} catch (t) {
|
|
480
|
+
console.error(`Failed to handle renegotiation offer: ${t.message}`);
|
|
497
481
|
}
|
|
498
482
|
}
|
|
499
|
-
async function
|
|
483
|
+
async function be(e = {}) {
|
|
500
484
|
try {
|
|
501
485
|
if (n.callStatus === "connecting" || n.callStatus === "connected") {
|
|
502
486
|
console.log(`Call already in ${n.callStatus} state`);
|
|
503
487
|
return;
|
|
504
488
|
}
|
|
505
|
-
console.log("Starting audio call..."),
|
|
489
|
+
console.log("Starting audio call..."), m("connecting"), E(null), n.sessionId = e.sessionId, await ie(), le(), n.localStream.getTracks().forEach((s) => {
|
|
506
490
|
n.peerConnection.addTrack(s, n.localStream), console.log(`Added ${s.kind} track`);
|
|
507
|
-
}), await
|
|
508
|
-
const
|
|
509
|
-
await n.peerConnection.setLocalDescription(
|
|
491
|
+
}), await de(e);
|
|
492
|
+
const t = await n.peerConnection.createOffer();
|
|
493
|
+
await n.peerConnection.setLocalDescription(t), C({
|
|
510
494
|
type: "offer",
|
|
511
495
|
data: {
|
|
512
|
-
sdp:
|
|
496
|
+
sdp: t.sdp
|
|
513
497
|
}
|
|
514
498
|
}), console.log("Call initiated successfully");
|
|
515
|
-
} catch (
|
|
516
|
-
console.log("error: ",
|
|
499
|
+
} catch (t) {
|
|
500
|
+
console.log("error: ", t), console.error(`Failed to start call: ${t.message}`), m("error"), E(t.message || "Unable to connect voice"), B();
|
|
517
501
|
}
|
|
518
502
|
}
|
|
519
|
-
function
|
|
520
|
-
|
|
503
|
+
function H() {
|
|
504
|
+
C({
|
|
521
505
|
type: "end"
|
|
522
|
-
}), n.socket && (n.socket.close(), n.socket = null),
|
|
506
|
+
}), n.socket && (n.socket.close(), n.socket = null), m("disconnected"), n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((e) => e.stop()), n.localStream = null), B();
|
|
523
507
|
}
|
|
524
|
-
function
|
|
508
|
+
function Ee() {
|
|
525
509
|
if (n.localStream) {
|
|
526
510
|
const e = n.localStream.getAudioTracks()[0];
|
|
527
511
|
if (e)
|
|
@@ -529,56 +513,56 @@ function Oe() {
|
|
|
529
513
|
}
|
|
530
514
|
return !1;
|
|
531
515
|
}
|
|
532
|
-
function
|
|
516
|
+
function ve() {
|
|
533
517
|
return n.localStream;
|
|
534
518
|
}
|
|
535
|
-
function
|
|
536
|
-
return new Promise((e,
|
|
519
|
+
function Ae() {
|
|
520
|
+
return new Promise((e, t) => {
|
|
537
521
|
if (!n.peerConnection) {
|
|
538
|
-
|
|
522
|
+
t(new Error("no peer connection"));
|
|
539
523
|
return;
|
|
540
524
|
}
|
|
541
525
|
n.peerConnection.getStats().then((s) => {
|
|
542
526
|
s.forEach((a) => {
|
|
543
527
|
a.type == "inbound-rtp" && e(a.totalAudioEnergy);
|
|
544
|
-
}),
|
|
528
|
+
}), t(new Error("no inbound-rtp stats found"));
|
|
545
529
|
}).catch((s) => {
|
|
546
|
-
|
|
530
|
+
t(s);
|
|
547
531
|
});
|
|
548
532
|
});
|
|
549
533
|
}
|
|
550
|
-
function
|
|
551
|
-
return new Promise((e,
|
|
534
|
+
function Te() {
|
|
535
|
+
return new Promise((e, t) => {
|
|
552
536
|
if (!n.peerConnection) {
|
|
553
|
-
|
|
537
|
+
t(new Error("no peer connection"));
|
|
554
538
|
return;
|
|
555
539
|
}
|
|
556
540
|
n.peerConnection.getStats().then((s) => {
|
|
557
541
|
s.forEach((a) => {
|
|
558
542
|
a.type == "outbound-rtp" && e(a.totalAudioEnergy);
|
|
559
|
-
}),
|
|
543
|
+
}), t(new Error("no outbound-rtp stats found"));
|
|
560
544
|
}).catch((s) => {
|
|
561
|
-
|
|
545
|
+
t(s);
|
|
562
546
|
});
|
|
563
547
|
});
|
|
564
548
|
}
|
|
565
549
|
export {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
550
|
+
b as MESSAGE_ROLES,
|
|
551
|
+
oe as authenticate,
|
|
552
|
+
te as configure,
|
|
553
|
+
he as disconnect,
|
|
554
|
+
H as disconnectCall,
|
|
555
|
+
we as getHistory,
|
|
556
|
+
Ae as getInboundAudioEnergy,
|
|
557
|
+
ve as getLocalStream,
|
|
558
|
+
se as getMessages,
|
|
559
|
+
Te as getOutboundAudioEnergy,
|
|
560
|
+
Ce as initialize,
|
|
561
|
+
ke as sendMessage,
|
|
562
|
+
ye as setCallCallbacks,
|
|
563
|
+
Se as setCallbacks,
|
|
564
|
+
be as startCall,
|
|
565
|
+
Ie as startChat,
|
|
566
|
+
Ee as toggleMute
|
|
583
567
|
};
|
|
584
568
|
//# sourceMappingURL=origon-chat-sdk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.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 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('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 * 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, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(messages: Array) => void} [onMessagesUpdate] - Called when messages array should be updated\n * @property {(isTyping: boolean) => void} [onTyping] - Called when typing status changes\n * @property {(isLiveAgent: boolean) => void} [onLiveAgentMode] - Called when live agent mode status changes\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {boolean} liveAgent\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\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 liveAgent: false,\n lastStreamId: undefined,\n messages: [],\n callbacks\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 externalId: getExternalId()\n })\n currentSession.sseUrl = `${currentSession.credentials.endpoint}?${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\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 * 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 }) {\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 currentSession.messages = [...currentSession.messages, userMessage]\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n\n await sleep(200)\n\n // Add loading message for bot if not in live agent mode\n if (!currentSession.liveAgent) {\n const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, loadingMessage]\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n } else {\n resolve(currentSession.sessionId)\n }\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({\n message: text,\n html\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: (event) => {\n console.log('Event: ', event)\n const data = JSON.parse(event.data)\n\n if (data.status === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n if (currentSession.liveAgent) {\n resolve(currentSession.sessionId)\n }\n } else if (data.agent) {\n currentSession.liveAgent = true\n const { type, data: payload } = data.agent\n\n switch (type) {\n case 'typing':\n currentSession.callbacks.onTyping?.(true)\n currentSession.callbacks.onLiveAgentMode?.(true)\n break\n case 'typingOff':\n currentSession.callbacks.onTyping?.(false)\n currentSession.callbacks.onLiveAgentMode?.(true)\n break\n case 'message':\n if (payload.role !== MESSAGE_ROLES.USER) {\n // Remove loading messages and add new message\n currentSession.messages = currentSession.messages.filter((msg) => !msg.loading)\n currentSession.messages = [...currentSession.messages, payload]\n\n const isEnded = payload.action === 'ended' || payload.action === 'left'\n if (isEnded) {\n currentSession.liveAgent = false\n }\n\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n currentSession.callbacks.onLiveAgentMode?.(!isEnded)\n }\n if (payload.action === 'ended' || payload.action === 'left') {\n currentSession.liveAgent = false\n }\n break\n default:\n break\n }\n } else if (data.message !== undefined) {\n let messages = currentSession.messages\n\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n messages = [\n ...messages,\n {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n ]\n }\n }\n\n // Update the last message with new content\n currentSession.messages = messages.map((msg, index) => {\n if (index === messages.length - 1) {\n return {\n ...msg,\n loading: false,\n text: (msg.text || '') + data.message,\n done: data.done ?? msg.done\n }\n }\n return msg\n })\n\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\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 } else if (data.error) {\n const errorMessage = 'Failed to connect to the system'\n currentSession.messages = currentSession.messages.map((msg, index) => {\n if (index === currentSession.messages.length - 1) {\n return {\n ...msg,\n loading: false,\n errorText: errorMessage\n }\n }\n return msg\n })\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n reject(new Error(errorMessage))\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 currentSession.messages = currentSession.messages.map((msg, index) => {\n if (index === currentSession.messages.length - 1) {\n return {\n ...msg,\n loading: false,\n errorText: errorMessage,\n done: true\n }\n }\n return msg\n })\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\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 { getExternalId } from './chat.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n// Module-level configuration\nlet _config = {\n endpoint: null\n}\n\n/**\n * Configure the API service with endpoint\n * @param {{ endpoint: string }} credentials\n */\nexport function configure(credentials) {\n _config = {\n endpoint: credentials.endpoint\n }\n}\n\n/**\n * Get current configuration\n * @returns {{ endpoint: string | null }}\n */\nexport function getConfig() {\n return { ..._config }\n}\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 // Store endpoint for subsequent requests\n configure({ endpoint })\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 { endpoint } = _config\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n return fetch(url, {\n headers: {\n 'Content-Type': 'application/json'\n },\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","getCallServerEndpoint","baseUrl","socketEndpoint","url","e","MESSAGE_ROLES","createSession","callbacks","currentSession","setCallbacks","initialize","credentials","getCredentials","updateSessionId","sessionId","startChat","payload","configData","authenticate","messages","messagesRes","getMessages","_a","msg","searchParams","getExternalId","error","cleanup","disconnect","sendMessage","text","html","reject","userMessage","loadingMessage","headers","fetchEventSource","response","event","data","type","isEnded","index","_o","_p","errorMessage","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","_config","configure","endpoint","errorPayload","getHistory","queryParams","fetchRequest","pathname","method","body","rtcConfig","setCallCallbacks","track","stopPingInterval","setCallStatus","status","_b","setCallError","startPingInterval","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","connectSocket","fulfill","externalId","socketUrl","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/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 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('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 * 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, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.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 */\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 */\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 }\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 externalId: getExternalId()\n })\n currentSession.sseUrl = `${currentSession.credentials.endpoint}?${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\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 * 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 }) {\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 currentSession.messages = [...currentSession.messages, userMessage]\n currentSession.callbacks.onMessageAdd?.(userMessage)\n\n await sleep(200)\n\n const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, loadingMessage]\n currentSession.callbacks.onMessageAdd?.(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 }),\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: (event) => {\n console.log('Event: ', event)\n const data = JSON.parse(event.data)\n\n if (data.status === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\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 currentSession.messages = [...currentSession.messages, newBotMessage]\n currentSession.callbacks.onMessageAdd?.(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 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 } else if (data.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: 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 }\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: 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 { getExternalId } from './chat.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n// Module-level configuration\nlet _config = {\n endpoint: null\n}\n\n/**\n * Configure the API service with endpoint\n * @param {{ endpoint: string }} credentials\n */\nexport function configure(credentials) {\n _config = {\n endpoint: credentials.endpoint\n }\n}\n\n/**\n * Get current configuration\n * @returns {{ endpoint: string | null }}\n */\nexport function getConfig() {\n return { ..._config }\n}\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 // Store endpoint for subsequent requests\n configure({ endpoint })\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 { endpoint } = _config\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n return fetch(url, {\n headers: {\n 'Content-Type': 'application/json'\n },\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","getCallServerEndpoint","baseUrl","socketEndpoint","url","e","MESSAGE_ROLES","createSession","callbacks","currentSession","setCallbacks","initialize","credentials","getCredentials","updateSessionId","sessionId","startChat","payload","configData","authenticate","messages","messagesRes","getMessages","_a","msg","searchParams","getExternalId","error","cleanup","disconnect","sendMessage","text","html","reject","userMessage","loadingMessage","headers","fetchEventSource","response","event","data","newBotMessage","lastIndex","lastMsg","updatedMsg","_c","index","_f","_g","errorMessage","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","_config","configure","endpoint","errorPayload","getHistory","queryParams","fetchRequest","pathname","method","body","rtcConfig","setCallCallbacks","track","stopPingInterval","setCallStatus","status","_b","setCallError","startPingInterval","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","connectSocket","fulfill","externalId","socketUrl","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
|
@@ -11,9 +11,8 @@ import { MESSAGE_ROLES } from './constants.js'
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @typedef {Object} ChatCallbacks
|
|
14
|
-
* @property {(
|
|
15
|
-
* @property {(
|
|
16
|
-
* @property {(isLiveAgent: boolean) => void} [onLiveAgentMode] - Called when live agent mode status changes
|
|
14
|
+
* @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added
|
|
15
|
+
* @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated
|
|
17
16
|
* @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated
|
|
18
17
|
*/
|
|
19
18
|
|
|
@@ -22,7 +21,6 @@ import { MESSAGE_ROLES } from './constants.js'
|
|
|
22
21
|
* @property {string} sessionId
|
|
23
22
|
* @property {string} sseUrl
|
|
24
23
|
* @property {string} [requestId]
|
|
25
|
-
* @property {boolean} liveAgent
|
|
26
24
|
* @property {AbortController} [abortController]
|
|
27
25
|
* @property {string} [lastStreamId]
|
|
28
26
|
* @property {Array} messages
|
|
@@ -43,7 +41,6 @@ function createSession(callbacks = {}) {
|
|
|
43
41
|
requestId: undefined,
|
|
44
42
|
sseUrl: undefined,
|
|
45
43
|
abortController: undefined,
|
|
46
|
-
liveAgent: false,
|
|
47
44
|
lastStreamId: undefined,
|
|
48
45
|
messages: [],
|
|
49
46
|
callbacks
|
|
@@ -196,22 +193,17 @@ export function sendMessage({ text, html }) {
|
|
|
196
193
|
timestamp: new Date().toISOString()
|
|
197
194
|
}
|
|
198
195
|
currentSession.messages = [...currentSession.messages, userMessage]
|
|
199
|
-
currentSession.callbacks.
|
|
196
|
+
currentSession.callbacks.onMessageAdd?.(userMessage)
|
|
200
197
|
|
|
201
198
|
await sleep(200)
|
|
202
199
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
text: '',
|
|
208
|
-
loading: true
|
|
209
|
-
}
|
|
210
|
-
currentSession.messages = [...currentSession.messages, loadingMessage]
|
|
211
|
-
currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
|
|
212
|
-
} else {
|
|
213
|
-
resolve(currentSession.sessionId)
|
|
200
|
+
const loadingMessage = {
|
|
201
|
+
role: MESSAGE_ROLES.BOT,
|
|
202
|
+
text: '',
|
|
203
|
+
loading: true
|
|
214
204
|
}
|
|
205
|
+
currentSession.messages = [...currentSession.messages, loadingMessage]
|
|
206
|
+
currentSession.callbacks.onMessageAdd?.(loadingMessage)
|
|
215
207
|
|
|
216
208
|
const url = new URL(currentSession.sseUrl)
|
|
217
209
|
if (currentSession.sessionId) {
|
|
@@ -254,77 +246,37 @@ export function sendMessage({ text, html }) {
|
|
|
254
246
|
if (data.status === 'connected') {
|
|
255
247
|
currentSession.sessionId = data.sessionId
|
|
256
248
|
currentSession.requestId = data.requestId
|
|
257
|
-
if (currentSession.liveAgent) {
|
|
258
|
-
resolve(currentSession.sessionId)
|
|
259
|
-
}
|
|
260
|
-
} else if (data.agent) {
|
|
261
|
-
currentSession.liveAgent = true
|
|
262
|
-
const { type, data: payload } = data.agent
|
|
263
|
-
|
|
264
|
-
switch (type) {
|
|
265
|
-
case 'typing':
|
|
266
|
-
currentSession.callbacks.onTyping?.(true)
|
|
267
|
-
currentSession.callbacks.onLiveAgentMode?.(true)
|
|
268
|
-
break
|
|
269
|
-
case 'typingOff':
|
|
270
|
-
currentSession.callbacks.onTyping?.(false)
|
|
271
|
-
currentSession.callbacks.onLiveAgentMode?.(true)
|
|
272
|
-
break
|
|
273
|
-
case 'message':
|
|
274
|
-
if (payload.role !== MESSAGE_ROLES.USER) {
|
|
275
|
-
// Remove loading messages and add new message
|
|
276
|
-
currentSession.messages = currentSession.messages.filter((msg) => !msg.loading)
|
|
277
|
-
currentSession.messages = [...currentSession.messages, payload]
|
|
278
|
-
|
|
279
|
-
const isEnded = payload.action === 'ended' || payload.action === 'left'
|
|
280
|
-
if (isEnded) {
|
|
281
|
-
currentSession.liveAgent = false
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
|
|
285
|
-
currentSession.callbacks.onLiveAgentMode?.(!isEnded)
|
|
286
|
-
}
|
|
287
|
-
if (payload.action === 'ended' || payload.action === 'left') {
|
|
288
|
-
currentSession.liveAgent = false
|
|
289
|
-
}
|
|
290
|
-
break
|
|
291
|
-
default:
|
|
292
|
-
break
|
|
293
|
-
}
|
|
294
249
|
} else if (data.message !== undefined) {
|
|
295
|
-
let messages = currentSession.messages
|
|
296
|
-
|
|
297
250
|
// If streamId changes, start a new assistant message
|
|
298
251
|
if (data.streamId !== undefined) {
|
|
299
252
|
if (currentSession.lastStreamId === undefined) {
|
|
300
253
|
currentSession.lastStreamId = data.streamId
|
|
301
254
|
} else if (data.streamId !== currentSession.lastStreamId) {
|
|
302
255
|
currentSession.lastStreamId = data.streamId
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
]
|
|
256
|
+
const newBotMessage = {
|
|
257
|
+
role: MESSAGE_ROLES.BOT,
|
|
258
|
+
text: '',
|
|
259
|
+
loading: true
|
|
260
|
+
}
|
|
261
|
+
currentSession.messages = [...currentSession.messages, newBotMessage]
|
|
262
|
+
currentSession.callbacks.onMessageAdd?.(newBotMessage)
|
|
311
263
|
}
|
|
312
264
|
}
|
|
313
265
|
|
|
314
266
|
// Update the last message with new content
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
267
|
+
const lastIndex = currentSession.messages.length - 1
|
|
268
|
+
const lastMsg = currentSession.messages[lastIndex]
|
|
269
|
+
const updatedMsg = {
|
|
270
|
+
...lastMsg,
|
|
271
|
+
loading: false,
|
|
272
|
+
text: (lastMsg.text || '') + data.message,
|
|
273
|
+
done: data.done ?? lastMsg.done
|
|
274
|
+
}
|
|
275
|
+
currentSession.messages = currentSession.messages.map((msg, index) =>
|
|
276
|
+
index === lastIndex ? updatedMsg : msg
|
|
277
|
+
)
|
|
326
278
|
|
|
327
|
-
currentSession.callbacks.
|
|
279
|
+
currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
|
|
328
280
|
|
|
329
281
|
if (data.done) {
|
|
330
282
|
resolve(currentSession.sessionId)
|
|
@@ -335,17 +287,17 @@ export function sendMessage({ text, html }) {
|
|
|
335
287
|
currentSession.requestId = data.requestId ?? currentSession.requestId
|
|
336
288
|
} else if (data.error) {
|
|
337
289
|
const errorMessage = 'Failed to connect to the system'
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
currentSession.callbacks.
|
|
290
|
+
const lastIndex = currentSession.messages.length - 1
|
|
291
|
+
const lastMsg = currentSession.messages[lastIndex]
|
|
292
|
+
const updatedMsg = {
|
|
293
|
+
...lastMsg,
|
|
294
|
+
loading: false,
|
|
295
|
+
errorText: errorMessage
|
|
296
|
+
}
|
|
297
|
+
currentSession.messages = currentSession.messages.map((msg, index) =>
|
|
298
|
+
index === lastIndex ? updatedMsg : msg
|
|
299
|
+
)
|
|
300
|
+
currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
|
|
349
301
|
reject(new Error(errorMessage))
|
|
350
302
|
}
|
|
351
303
|
},
|
|
@@ -357,18 +309,18 @@ export function sendMessage({ text, html }) {
|
|
|
357
309
|
} catch (error) {
|
|
358
310
|
console.error('Failed to send message: ', error)
|
|
359
311
|
const errorMessage = 'Failed to connect to the system'
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
currentSession.callbacks.
|
|
312
|
+
const lastIndex = currentSession.messages.length - 1
|
|
313
|
+
const lastMsg = currentSession.messages[lastIndex]
|
|
314
|
+
const updatedMsg = {
|
|
315
|
+
...lastMsg,
|
|
316
|
+
loading: false,
|
|
317
|
+
errorText: errorMessage,
|
|
318
|
+
done: true
|
|
319
|
+
}
|
|
320
|
+
currentSession.messages = currentSession.messages.map((msg, index) =>
|
|
321
|
+
index === lastIndex ? updatedMsg : msg
|
|
322
|
+
)
|
|
323
|
+
currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
|
|
372
324
|
reject(error)
|
|
373
325
|
}
|
|
374
326
|
})()
|