@skippr/live-agent-sdk 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -145,17 +145,21 @@ function useAuth({ appKey }) {
145
145
  }
146
146
 
147
147
  // src/hooks/useSession.ts
148
- import { useCallback as useCallback2, useState as useState2 } from "react";
148
+ import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
149
149
  var API_URL2 = "https://skipprapi-production.up.railway.app";
150
- function resolveAuthHeaders(authToken, appKey, userToken) {
151
- const headers = {};
152
- if (authToken)
153
- headers.Authorization = `Bearer ${authToken}`;
154
- if (appKey)
155
- headers["X-App-Key"] = appKey;
156
- if (userToken)
157
- headers["X-User-Token"] = userToken;
158
- return headers;
150
+ async function exchangeForBearerToken(appKey, userToken) {
151
+ const resp = await fetch(`${API_URL2}/v1/auth/token-exchange`, {
152
+ method: "POST",
153
+ headers: {
154
+ "X-App-Key": appKey,
155
+ "X-User-Token": userToken
156
+ }
157
+ });
158
+ if (!resp.ok) {
159
+ throw new Error(`Token exchange failed: ${resp.status}`);
160
+ }
161
+ const { token } = await resp.json();
162
+ return token;
159
163
  }
160
164
  function useSession({ agentId, authToken, appKey, userToken }) {
161
165
  const [connection, setConnection] = useState2(null);
@@ -163,32 +167,53 @@ function useSession({ agentId, authToken, appKey, userToken }) {
163
167
  const [isStarting, setIsStarting] = useState2(false);
164
168
  const [error, setError] = useState2("");
165
169
  const [sessionId, setSessionId] = useState2(null);
166
- const [sessionToken, setSessionToken] = useState2(null);
170
+ const [bearerToken, setBearerToken] = useState2(authToken ?? null);
171
+ useEffect2(() => {
172
+ let stale = false;
173
+ if (authToken) {
174
+ setBearerToken(authToken);
175
+ return;
176
+ }
177
+ if (appKey && userToken) {
178
+ exchangeForBearerToken(appKey, userToken).then((token) => {
179
+ if (!stale)
180
+ setBearerToken(token);
181
+ }).catch((e) => {
182
+ if (!stale)
183
+ setError(e instanceof Error ? e.message : "Token exchange failed");
184
+ });
185
+ }
186
+ return () => {
187
+ stale = true;
188
+ };
189
+ }, [authToken, appKey, userToken]);
167
190
  const startSession = useCallback2(async () => {
191
+ if (!bearerToken) {
192
+ setError("No auth token available");
193
+ return;
194
+ }
195
+ const headers = { Authorization: `Bearer ${bearerToken}` };
168
196
  setIsStarting(true);
169
197
  setError("");
170
198
  try {
171
- const authHeaders = resolveAuthHeaders(authToken, appKey, userToken);
172
199
  const createResp = await fetch(`${API_URL2}/v1/sessions`, {
173
200
  method: "POST",
174
- headers: { "Content-Type": "application/json", ...authHeaders },
201
+ headers: { "Content-Type": "application/json", ...headers },
175
202
  body: JSON.stringify({ agentId })
176
203
  });
177
204
  if (!createResp.ok) {
178
205
  throw new Error(`Failed to create session: ${createResp.status}`);
179
206
  }
180
207
  const { session } = await createResp.json();
181
- const sessionHeaders = { "X-Session-Token": session.sessionToken };
182
208
  const startResp = await fetch(`${API_URL2}/v1/sessions/${session.id}/start`, {
183
209
  method: "POST",
184
- headers: sessionHeaders
210
+ headers
185
211
  });
186
212
  if (!startResp.ok) {
187
213
  throw new Error(`Failed to start session: ${startResp.status}`);
188
214
  }
189
215
  const { connection: conn } = await startResp.json();
190
216
  setSessionId(session.id);
191
- setSessionToken(session.sessionToken);
192
217
  setConnection({
193
218
  livekitUrl: conn.livekitUrl,
194
219
  token: conn.token
@@ -199,14 +224,13 @@ function useSession({ agentId, authToken, appKey, userToken }) {
199
224
  } finally {
200
225
  setIsStarting(false);
201
226
  }
202
- }, [agentId, authToken, appKey, userToken]);
227
+ }, [agentId, bearerToken]);
203
228
  const disconnect = useCallback2(async () => {
204
- if (sessionId && sessionToken) {
205
- const sessionHeaders = { "X-Session-Token": sessionToken };
229
+ if (sessionId && bearerToken) {
206
230
  try {
207
231
  await fetch(`${API_URL2}/v1/sessions/${sessionId}/complete`, {
208
232
  method: "POST",
209
- headers: { "Content-Type": "application/json", ...sessionHeaders },
233
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
210
234
  body: JSON.stringify({})
211
235
  });
212
236
  } catch {}
@@ -215,15 +239,14 @@ function useSession({ agentId, authToken, appKey, userToken }) {
215
239
  setShouldConnect(false);
216
240
  setConnection(null);
217
241
  setSessionId(null);
218
- setSessionToken(null);
219
- }, [sessionId, sessionToken]);
242
+ }, [sessionId, bearerToken]);
220
243
  return { connection, shouldConnect, isStarting, error, startSession, disconnect };
221
244
  }
222
245
 
223
246
  // src/components/Sidebar.tsx
224
247
  import { useConnectionState } from "@livekit/components-react";
225
248
  import { ConnectionState } from "livekit-client";
226
- import { useEffect as useEffect6 } from "react";
249
+ import { useEffect as useEffect7 } from "react";
227
250
 
228
251
  // src/hooks/useCombinedMessages.ts
229
252
  import { useVoiceAssistant } from "@livekit/components-react";
@@ -321,11 +344,11 @@ import { useCallback as useCallback3 } from "react";
321
344
 
322
345
  // src/hooks/useAgentState.ts
323
346
  import { useRemoteParticipants } from "@livekit/components-react";
324
- import { useEffect as useEffect2, useState as useState3 } from "react";
347
+ import { useEffect as useEffect3, useState as useState3 } from "react";
325
348
  function useAgentState(attributeKey, parse, initial) {
326
349
  const [value, setValue] = useState3(initial);
327
350
  const remoteParticipants = useRemoteParticipants();
328
- useEffect2(() => {
351
+ useEffect3(() => {
329
352
  const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
330
353
  if (agentParticipant) {
331
354
  const attr = agentParticipant.attributes?.[attributeKey];
@@ -666,7 +689,7 @@ function ChatHeader({ onClose }) {
666
689
  }
667
690
 
668
691
  // src/components/LoginFlow.tsx
669
- import { useCallback as useCallback5, useEffect as useEffect3, useRef, useState as useState4 } from "react";
692
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef, useState as useState4 } from "react";
670
693
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
671
694
  var OTP_LENGTH = 6;
672
695
  var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
@@ -765,14 +788,14 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
765
788
  const [resendCooldown, setResendCooldown] = useState4(0);
766
789
  const inputRefs = useRef([]);
767
790
  const submittedRef = useRef(false);
768
- useEffect3(() => {
791
+ useEffect4(() => {
769
792
  inputRefs.current[0]?.focus();
770
793
  }, []);
771
- useEffect3(() => {
794
+ useEffect4(() => {
772
795
  if (error)
773
796
  submittedRef.current = false;
774
797
  }, [error]);
775
- useEffect3(() => {
798
+ useEffect4(() => {
776
799
  if (resendCooldown <= 0)
777
800
  return;
778
801
  const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
@@ -915,7 +938,7 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
915
938
  // src/components/MeetingControls.tsx
916
939
  import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
917
940
  import { ScreenSharePresets } from "livekit-client";
918
- import { useCallback as useCallback6, useEffect as useEffect4, useRef as useRef2, useState as useState5 } from "react";
941
+ import { useCallback as useCallback6, useEffect as useEffect5, useRef as useRef2, useState as useState5 } from "react";
919
942
 
920
943
  // src/lib/format.ts
921
944
  function formatTime(seconds) {
@@ -950,7 +973,7 @@ function MeetingControls({ onHangUp }) {
950
973
  const isScreenSharing = localParticipant.isScreenShareEnabled;
951
974
  const endTimeRef = useRef2(null);
952
975
  const [remaining, setRemaining] = useState5(null);
953
- useEffect4(() => {
976
+ useEffect5(() => {
954
977
  if (maxCallDuration === null || endTimeRef.current !== null)
955
978
  return;
956
979
  endTimeRef.current = Date.now() + maxCallDuration * 1000;
@@ -980,7 +1003,7 @@ function MeetingControls({ onHangUp }) {
980
1003
  console.error("Failed to toggle screen share:", e);
981
1004
  }
982
1005
  }, [localParticipant, isScreenSharing]);
983
- useEffect4(() => {
1006
+ useEffect5(() => {
984
1007
  toggleMute().then(() => toggleScreenShare());
985
1008
  }, []);
986
1009
  return /* @__PURE__ */ jsxs3("div", {
@@ -1038,7 +1061,7 @@ function MeetingControls({ onHangUp }) {
1038
1061
  }
1039
1062
 
1040
1063
  // src/components/MessageList.tsx
1041
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
1064
+ import { useEffect as useEffect6, useRef as useRef3 } from "react";
1042
1065
 
1043
1066
  // src/components/ChatInput.tsx
1044
1067
  import { useState as useState6 } from "react";
@@ -1124,7 +1147,7 @@ function MessageList({
1124
1147
  }) {
1125
1148
  const scrollRef = useRef3(null);
1126
1149
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
1127
- useEffect5(() => {
1150
+ useEffect6(() => {
1128
1151
  scrollRef.current?.scrollIntoView({ behavior: "smooth" });
1129
1152
  }, [messages.length, lastMessage?.content]);
1130
1153
  const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
@@ -1286,11 +1309,11 @@ function Sidebar() {
1286
1309
  verifyOtp,
1287
1310
  isAuthSubmitting
1288
1311
  } = useLiveAgent();
1289
- useEffect6(() => {
1312
+ useEffect7(() => {
1290
1313
  document.body.style.transition = "margin-right 300ms ease-in-out";
1291
1314
  document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
1292
1315
  }, [isPanelOpen]);
1293
- useEffect6(() => {
1316
+ useEffect7(() => {
1294
1317
  return () => {
1295
1318
  document.body.style.marginRight = "";
1296
1319
  document.body.style.transition = "";