@skippr/live-agent-sdk 0.36.0 → 0.38.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.
@@ -137,12 +137,24 @@ function useAuth({ appKey }) {
137
137
  setIsSubmitting(false);
138
138
  }
139
139
  }, [appKey]);
140
- const logout = useCallback(() => {
140
+ const logout = useCallback(async () => {
141
+ const token = authToken;
141
142
  if (appKey)
142
143
  clearStoredToken(appKey);
143
144
  setAuthToken(null);
144
145
  setError("");
145
- }, [appKey]);
146
+ if (!token)
147
+ return;
148
+ try {
149
+ await fetch(`${API_URL}/v1/auth/logout`, {
150
+ method: "POST",
151
+ credentials: "omit",
152
+ headers: { Authorization: `Bearer ${token}` }
153
+ });
154
+ } catch (e) {
155
+ console.warn("[Skippr] Failed to revoke session on logout:", e);
156
+ }
157
+ }, [appKey, authToken]);
146
158
  return {
147
159
  isAuthenticated,
148
160
  isValidating,
@@ -160,23 +172,17 @@ import { useCallback as useCallback2, useEffect as useEffect2, useState as useSt
160
172
  var API_URL2 = "https://specialist.skippr.ai/api";
161
173
  function useAvailableModules({
162
174
  enabled,
163
- bearerToken
175
+ bearerToken,
176
+ authedFetch
164
177
  }) {
165
178
  const [modules, setModules] = useState2([]);
166
179
  const [isLoading, setIsLoading] = useState2(false);
167
180
  const [error, setError] = useState2(null);
168
181
  const fetchModules = useCallback2(async () => {
169
- if (!bearerToken) {
170
- setError("No auth token available");
171
- return;
172
- }
173
182
  setIsLoading(true);
174
183
  setError(null);
175
184
  try {
176
- const resp = await fetch(`${API_URL2}/v1/modules/available`, {
177
- credentials: "omit",
178
- headers: { Authorization: `Bearer ${bearerToken}` }
179
- });
185
+ const resp = await authedFetch(`${API_URL2}/v1/modules/available`);
180
186
  if (!resp.ok) {
181
187
  const body = await resp.json().catch(() => null);
182
188
  throw new Error(body?.detail || `Failed to load modules: ${resp.status}`);
@@ -188,7 +194,7 @@ function useAvailableModules({
188
194
  } finally {
189
195
  setIsLoading(false);
190
196
  }
191
- }, [bearerToken]);
197
+ }, [authedFetch]);
192
198
  useEffect2(() => {
193
199
  if (!enabled || !bearerToken)
194
200
  return;
@@ -205,12 +211,9 @@ function useAvailableModules({
205
211
  // src/hooks/useSession.ts
206
212
  import { useCallback as useCallback3, useEffect as useEffect3, useRef, useState as useState3 } from "react";
207
213
  var API_URL3 = "https://specialist.skippr.ai/api";
208
- async function fetchSessionMessages(sessionId, bearerToken) {
214
+ async function fetchSessionMessages(sessionId, authedFetch) {
209
215
  try {
210
- const resp = await fetch(`${API_URL3}/v1/sessions/${sessionId}/messages`, {
211
- credentials: "omit",
212
- headers: { Authorization: `Bearer ${bearerToken}` }
213
- });
216
+ const resp = await authedFetch(`${API_URL3}/v1/sessions/${sessionId}/messages`);
214
217
  if (!resp.ok)
215
218
  return [];
216
219
  const { messages } = await resp.json();
@@ -225,6 +228,7 @@ async function fetchSessionMessages(sessionId, bearerToken) {
225
228
  return [];
226
229
  }
227
230
  }
231
+ var REFRESH_BUFFER_MS = 180000;
228
232
  async function requestScreenShare() {
229
233
  try {
230
234
  return await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: "browser" } });
@@ -250,13 +254,14 @@ async function exchangeForBearerToken(appKey, userToken) {
250
254
  if (!resp.ok) {
251
255
  throw new Error(`Token exchange failed: ${resp.status}`);
252
256
  }
253
- const { token } = await resp.json();
254
- return token;
257
+ const { token, expiresAt } = await resp.json();
258
+ return { token, expiresAt: new Date(expiresAt).getTime() };
255
259
  }
256
260
  function useSession({
257
261
  captureMode = "screenshare",
258
262
  authToken,
259
263
  appKey,
264
+ getUserToken,
260
265
  userToken,
261
266
  onStart,
262
267
  onStartError,
@@ -274,25 +279,106 @@ function useSession({
274
279
  const [isPaused, setIsPaused] = useState3(false);
275
280
  const [isPausing, setIsPausing] = useState3(false);
276
281
  const [historyMessages, setHistoryMessages] = useState3([]);
282
+ const [authFailed, setAuthFailed] = useState3(false);
283
+ const tokenRef = useRef(authToken ? { token: authToken, expiresAt: null } : null);
284
+ const getUserTokenRef = useRef(getUserToken);
285
+ getUserTokenRef.current = getUserToken ?? (userToken ? () => Promise.resolve(userToken) : undefined);
286
+ const failedBearerRef = useRef(null);
287
+ const inFlightExchangeRef = useRef(null);
288
+ const canExchange = useCallback3(() => !!appKey && !!getUserTokenRef.current, [appKey]);
289
+ const reExchange = useCallback3(async () => {
290
+ if (inFlightExchangeRef.current)
291
+ return inFlightExchangeRef.current;
292
+ const provider = getUserTokenRef.current;
293
+ if (!appKey || !provider)
294
+ throw new Error("No token provider available");
295
+ const exchange = (async () => {
296
+ const userToken2 = await provider();
297
+ const { token, expiresAt } = await exchangeForBearerToken(appKey, userToken2);
298
+ tokenRef.current = { token, expiresAt };
299
+ setBearerToken(token);
300
+ return token;
301
+ })();
302
+ inFlightExchangeRef.current = exchange;
303
+ try {
304
+ return await exchange;
305
+ } finally {
306
+ inFlightExchangeRef.current = null;
307
+ }
308
+ }, [appKey]);
309
+ const getValidBearer = useCallback3(async () => {
310
+ const current = tokenRef.current;
311
+ if (current) {
312
+ if (current.expiresAt === null)
313
+ return current.token;
314
+ if (Date.now() < current.expiresAt - REFRESH_BUFFER_MS)
315
+ return current.token;
316
+ }
317
+ if (canExchange())
318
+ return reExchange();
319
+ return current?.token ?? null;
320
+ }, [canExchange, reExchange]);
321
+ const authedFetch = useCallback3(async (input, init = {}) => {
322
+ const isSecretMode = canExchange();
323
+ const onTerminalAuthFailure = () => {
324
+ if (isSecretMode)
325
+ setAuthFailed(true);
326
+ else
327
+ window.dispatchEvent(new Event("skippr:logout"));
328
+ };
329
+ const baseHeaders = init.headers ?? {};
330
+ const run = (token) => fetch(input, {
331
+ ...init,
332
+ credentials: "omit",
333
+ headers: token ? { ...baseHeaders, Authorization: `Bearer ${token}` } : baseHeaders
334
+ });
335
+ let bearer;
336
+ try {
337
+ bearer = await getValidBearer();
338
+ } catch (e) {
339
+ onTerminalAuthFailure();
340
+ throw e;
341
+ }
342
+ let resp = await run(bearer);
343
+ const canRetry = resp.status === 401 && isSecretMode && bearer !== failedBearerRef.current;
344
+ if (canRetry) {
345
+ let refreshed;
346
+ try {
347
+ refreshed = await reExchange();
348
+ } catch (e) {
349
+ onTerminalAuthFailure();
350
+ throw e;
351
+ }
352
+ resp = await run(refreshed);
353
+ failedBearerRef.current = resp.status === 401 ? refreshed : null;
354
+ } else if (resp.status !== 401) {
355
+ failedBearerRef.current = null;
356
+ }
357
+ if (resp.status === 401)
358
+ onTerminalAuthFailure();
359
+ else if (resp.ok)
360
+ setAuthFailed(false);
361
+ return resp;
362
+ }, [canExchange, getValidBearer, reExchange]);
277
363
  useEffect3(() => {
278
364
  let stale = false;
279
365
  if (authToken) {
366
+ tokenRef.current = { token: authToken, expiresAt: null };
280
367
  setBearerToken(authToken);
281
368
  return;
282
369
  }
283
- if (appKey && userToken) {
284
- exchangeForBearerToken(appKey, userToken).then((token) => {
285
- if (!stale)
286
- setBearerToken(token);
287
- }).catch((e) => {
288
- if (!stale)
289
- setError(e instanceof Error ? e.message : "Token exchange failed");
370
+ if (canExchange()) {
371
+ reExchange().catch((e) => {
372
+ if (stale)
373
+ return;
374
+ setError(e instanceof Error ? e.message : "Token exchange failed");
375
+ setAuthFailed(true);
290
376
  });
291
377
  }
292
378
  return () => {
293
379
  stale = true;
294
380
  };
295
- }, [authToken, appKey, userToken]);
381
+ }, [authToken, canExchange, reExchange]);
296
382
  const pauseOnUnloadRef = useRef(null);
297
383
  pauseOnUnloadRef.current = connection !== null && !isPaused && sessionId && bearerToken ? { sessionId, bearerToken } : null;
298
384
  useEffect3(() => {
@@ -328,14 +414,12 @@ function useSession({
328
414
  screenStream = await requestScreenShare();
329
415
  }
330
416
  const requestAgentControls = agentControls?.highlight === true ? { highlight: true } : undefined;
331
- const headers = { Authorization: `Bearer ${bearerToken}` };
332
417
  try {
333
418
  let resolvedSessionId = existingSessionId;
334
419
  if (!resolvedSessionId) {
335
- const createResp = await fetch(`${API_URL3}/v1/sessions`, {
420
+ const createResp = await authedFetch(`${API_URL3}/v1/sessions`, {
336
421
  method: "POST",
337
- credentials: "omit",
338
- headers: { "Content-Type": "application/json", ...headers },
422
+ headers: { "Content-Type": "application/json" },
339
423
  body: JSON.stringify({
340
424
  agentId,
341
425
  captureMode,
@@ -350,10 +434,9 @@ function useSession({
350
434
  const { session: session2 } = await createResp.json();
351
435
  resolvedSessionId = session2.id;
352
436
  }
353
- const startResp = await fetch(`${API_URL3}/v1/sessions/${resolvedSessionId}/start`, {
437
+ const startResp = await authedFetch(`${API_URL3}/v1/sessions/${resolvedSessionId}/start`, {
354
438
  method: "POST",
355
- credentials: "omit",
356
- headers: { "Content-Type": "application/json", ...headers },
439
+ headers: { "Content-Type": "application/json" },
357
440
  body: JSON.stringify({ captureMode, agentControls: requestAgentControls })
358
441
  });
359
442
  if (!startResp.ok) {
@@ -362,7 +445,7 @@ function useSession({
362
445
  throw new Error(body?.detail || `Failed to start session: ${startResp.status}`);
363
446
  }
364
447
  const { session, connection: conn } = await startResp.json();
365
- const history2 = existingSessionId ? await fetchSessionMessages(session.id, bearerToken) : [];
448
+ const history2 = existingSessionId ? await fetchSessionMessages(session.id, authedFetch) : [];
366
449
  setSessionId(session.id);
367
450
  setHistoryMessages(history2);
368
451
  setConnection({
@@ -379,16 +462,14 @@ function useSession({
379
462
  } finally {
380
463
  setIsStarting(false);
381
464
  }
382
- }, [captureMode, bearerToken, onStart, onStartError]);
465
+ }, [captureMode, bearerToken, authedFetch, onStart, onStartError]);
383
466
  const pauseSession = useCallback3(async () => {
384
467
  if (!sessionId || !bearerToken)
385
468
  return;
386
469
  setIsPausing(true);
387
470
  try {
388
- const resp = await fetch(`${API_URL3}/v1/sessions/${sessionId}/pause`, {
389
- method: "POST",
390
- credentials: "omit",
391
- headers: { Authorization: `Bearer ${bearerToken}` }
471
+ const resp = await authedFetch(`${API_URL3}/v1/sessions/${sessionId}/pause`, {
472
+ method: "POST"
392
473
  });
393
474
  if (!resp.ok) {
394
475
  const body = await resp.json().catch(() => null);
@@ -399,7 +480,7 @@ function useSession({
399
480
  setIsPausing(false);
400
481
  return;
401
482
  }
402
- const history2 = await fetchSessionMessages(sessionId, bearerToken);
483
+ const history2 = await fetchSessionMessages(sessionId, authedFetch);
403
484
  stopStream(pendingScreenStream);
404
485
  setPendingScreenStream(null);
405
486
  setShouldConnect(false);
@@ -407,16 +488,14 @@ function useSession({
407
488
  setHistoryMessages(history2);
408
489
  setIsPaused(true);
409
490
  setIsPausing(false);
410
- }, [sessionId, bearerToken, pendingScreenStream]);
491
+ }, [sessionId, bearerToken, authedFetch, pendingScreenStream]);
411
492
  const disconnect = useCallback3(async () => {
412
493
  setIsDisconnecting(true);
413
494
  try {
414
495
  if (sessionId && bearerToken) {
415
496
  try {
416
- await fetch(`${API_URL3}/v1/sessions/${sessionId}/pause`, {
417
- method: "POST",
418
- credentials: "omit",
419
- headers: { Authorization: `Bearer ${bearerToken}` }
497
+ await authedFetch(`${API_URL3}/v1/sessions/${sessionId}/pause`, {
498
+ method: "POST"
420
499
  });
421
500
  } catch {}
422
501
  }
@@ -432,7 +511,7 @@ function useSession({
432
511
  } finally {
433
512
  setIsDisconnecting(false);
434
513
  }
435
- }, [sessionId, bearerToken, pendingScreenStream, onDisconnect]);
514
+ }, [sessionId, bearerToken, authedFetch, pendingScreenStream, onDisconnect]);
436
515
  return {
437
516
  connection,
438
517
  shouldConnect,
@@ -447,7 +526,9 @@ function useSession({
447
526
  isPausing,
448
527
  historyMessages,
449
528
  pendingScreenStream,
450
- bearerToken
529
+ bearerToken,
530
+ authedFetch,
531
+ authFailed
451
532
  };
452
533
  }
453
534
 
@@ -468,7 +549,7 @@ function getResumableSessionId(currentSession, mode) {
468
549
  const { id, status } = currentSession;
469
550
  if (status === "paused" || status === "active")
470
551
  return id;
471
- if (status === "expired" && mode === "always_on")
552
+ if (mode === "always_on" && (status === "expired" || status === "completed"))
472
553
  return id;
473
554
  return;
474
555
  }
@@ -481,6 +562,20 @@ function getResumableSession(module) {
481
562
  const id = getResumableSessionId(module.currentSession, module.mode);
482
563
  return id ? { id, agentId: module.id } : null;
483
564
  }
565
+ function getModuleSessionState(currentSession, mode) {
566
+ if (!currentSession)
567
+ return null;
568
+ if (canResumeSession(currentSession, mode))
569
+ return "resume";
570
+ if (mode === "standard") {
571
+ const { status } = currentSession;
572
+ if (status === "completed")
573
+ return "completed";
574
+ if (status === "expired")
575
+ return "expired";
576
+ }
577
+ return null;
578
+ }
484
579
 
485
580
  // src/components/AutoStartMedia.tsx
486
581
  import { useConnectionState, useLocalParticipant } from "@livekit/components-react/hooks";
@@ -1450,17 +1545,24 @@ var createLucideIcon = (iconName, iconNode) => {
1450
1545
  return Component;
1451
1546
  };
1452
1547
 
1453
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle-check.js
1548
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle-alert.js
1454
1549
  var __iconNode = [
1550
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
1551
+ ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
1552
+ ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
1553
+ ];
1554
+ var CircleAlert = createLucideIcon("circle-alert", __iconNode);
1555
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle-check.js
1556
+ var __iconNode2 = [
1455
1557
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
1456
1558
  ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
1457
1559
  ];
1458
- var CircleCheck = createLucideIcon("circle-check", __iconNode);
1560
+ var CircleCheck = createLucideIcon("circle-check", __iconNode2);
1459
1561
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/loader-circle.js
1460
- var __iconNode2 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
1461
- var LoaderCircle = createLucideIcon("loader-circle", __iconNode2);
1562
+ var __iconNode3 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
1563
+ var LoaderCircle = createLucideIcon("loader-circle", __iconNode3);
1462
1564
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/sparkles.js
1463
- var __iconNode3 = [
1565
+ var __iconNode4 = [
1464
1566
  [
1465
1567
  "path",
1466
1568
  {
@@ -1472,9 +1574,9 @@ var __iconNode3 = [
1472
1574
  ["path", { d: "M22 4h-4", key: "gwowj6" }],
1473
1575
  ["circle", { cx: "4", cy: "20", r: "2", key: "6kqj1y" }]
1474
1576
  ];
1475
- var Sparkles = createLucideIcon("sparkles", __iconNode3);
1577
+ var Sparkles = createLucideIcon("sparkles", __iconNode4);
1476
1578
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/bot.js
1477
- var __iconNode4 = [
1579
+ var __iconNode5 = [
1478
1580
  ["path", { d: "M12 8V4H8", key: "hb8ula" }],
1479
1581
  ["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
1480
1582
  ["path", { d: "M2 14h2", key: "vft8re" }],
@@ -1482,23 +1584,29 @@ var __iconNode4 = [
1482
1584
  ["path", { d: "M15 13v2", key: "1xurst" }],
1483
1585
  ["path", { d: "M9 13v2", key: "rq6x2g" }]
1484
1586
  ];
1485
- var Bot = createLucideIcon("bot", __iconNode4);
1587
+ var Bot = createLucideIcon("bot", __iconNode5);
1486
1588
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/calendar.js
1487
- var __iconNode5 = [
1589
+ var __iconNode6 = [
1488
1590
  ["path", { d: "M8 2v4", key: "1cmpym" }],
1489
1591
  ["path", { d: "M16 2v4", key: "4m81vk" }],
1490
1592
  ["rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", key: "1hopcy" }],
1491
1593
  ["path", { d: "M3 10h18", key: "8toen8" }]
1492
1594
  ];
1493
- var Calendar = createLucideIcon("calendar", __iconNode5);
1595
+ var Calendar = createLucideIcon("calendar", __iconNode6);
1494
1596
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/chevron-down.js
1495
- var __iconNode6 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
1496
- var ChevronDown = createLucideIcon("chevron-down", __iconNode6);
1597
+ var __iconNode7 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
1598
+ var ChevronDown = createLucideIcon("chevron-down", __iconNode7);
1497
1599
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
1498
- var __iconNode7 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
1499
- var Circle = createLucideIcon("circle", __iconNode7);
1600
+ var __iconNode8 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
1601
+ var Circle = createLucideIcon("circle", __iconNode8);
1602
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/clock.js
1603
+ var __iconNode9 = [
1604
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
1605
+ ["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
1606
+ ];
1607
+ var Clock = createLucideIcon("clock", __iconNode9);
1500
1608
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/graduation-cap.js
1501
- var __iconNode8 = [
1609
+ var __iconNode10 = [
1502
1610
  [
1503
1611
  "path",
1504
1612
  {
@@ -1509,9 +1617,9 @@ var __iconNode8 = [
1509
1617
  ["path", { d: "M22 10v6", key: "1lu8f3" }],
1510
1618
  ["path", { d: "M6 12.5V16a6 3 0 0 0 12 0v-3.5", key: "1r8lef" }]
1511
1619
  ];
1512
- var GraduationCap = createLucideIcon("graduation-cap", __iconNode8);
1620
+ var GraduationCap = createLucideIcon("graduation-cap", __iconNode10);
1513
1621
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/headset.js
1514
- var __iconNode9 = [
1622
+ var __iconNode11 = [
1515
1623
  [
1516
1624
  "path",
1517
1625
  {
@@ -1521,15 +1629,15 @@ var __iconNode9 = [
1521
1629
  ],
1522
1630
  ["path", { d: "M21 16v2a4 4 0 0 1-4 4h-5", key: "1x7m43" }]
1523
1631
  ];
1524
- var Headset = createLucideIcon("headset", __iconNode9);
1632
+ var Headset = createLucideIcon("headset", __iconNode11);
1525
1633
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mail.js
1526
- var __iconNode10 = [
1634
+ var __iconNode12 = [
1527
1635
  ["path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7", key: "132q7q" }],
1528
1636
  ["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }]
1529
1637
  ];
1530
- var Mail = createLucideIcon("mail", __iconNode10);
1638
+ var Mail = createLucideIcon("mail", __iconNode12);
1531
1639
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
1532
- var __iconNode11 = [
1640
+ var __iconNode13 = [
1533
1641
  [
1534
1642
  "path",
1535
1643
  {
@@ -1538,9 +1646,9 @@ var __iconNode11 = [
1538
1646
  }
1539
1647
  ]
1540
1648
  ];
1541
- var MessageCircle = createLucideIcon("message-circle", __iconNode11);
1649
+ var MessageCircle = createLucideIcon("message-circle", __iconNode13);
1542
1650
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-square.js
1543
- var __iconNode12 = [
1651
+ var __iconNode14 = [
1544
1652
  [
1545
1653
  "path",
1546
1654
  {
@@ -1549,9 +1657,9 @@ var __iconNode12 = [
1549
1657
  }
1550
1658
  ]
1551
1659
  ];
1552
- var MessageSquare = createLucideIcon("message-square", __iconNode12);
1660
+ var MessageSquare = createLucideIcon("message-square", __iconNode14);
1553
1661
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
1554
- var __iconNode13 = [
1662
+ var __iconNode15 = [
1555
1663
  ["path", { d: "M12 19v3", key: "npa21l" }],
1556
1664
  ["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
1557
1665
  ["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
@@ -1559,40 +1667,40 @@ var __iconNode13 = [
1559
1667
  ["path", { d: "m2 2 20 20", key: "1ooewy" }],
1560
1668
  ["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
1561
1669
  ];
1562
- var MicOff = createLucideIcon("mic-off", __iconNode13);
1670
+ var MicOff = createLucideIcon("mic-off", __iconNode15);
1563
1671
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
1564
- var __iconNode14 = [
1672
+ var __iconNode16 = [
1565
1673
  ["path", { d: "M12 19v3", key: "npa21l" }],
1566
1674
  ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
1567
1675
  ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
1568
1676
  ];
1569
- var Mic = createLucideIcon("mic", __iconNode14);
1677
+ var Mic = createLucideIcon("mic", __iconNode16);
1570
1678
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/minimize-2.js
1571
- var __iconNode15 = [
1679
+ var __iconNode17 = [
1572
1680
  ["path", { d: "m14 10 7-7", key: "oa77jy" }],
1573
1681
  ["path", { d: "M20 10h-6V4", key: "mjg0md" }],
1574
1682
  ["path", { d: "m3 21 7-7", key: "tjx5ai" }],
1575
1683
  ["path", { d: "M4 14h6v6", key: "rmj7iw" }]
1576
1684
  ];
1577
- var Minimize2 = createLucideIcon("minimize-2", __iconNode15);
1685
+ var Minimize2 = createLucideIcon("minimize-2", __iconNode17);
1578
1686
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
1579
- var __iconNode16 = [
1687
+ var __iconNode18 = [
1580
1688
  ["path", { d: "M12 17v4", key: "1riwvh" }],
1581
1689
  ["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
1582
1690
  ["path", { d: "m2 2 20 20", key: "1ooewy" }],
1583
1691
  ["path", { d: "M8 21h8", key: "1ev6f3" }],
1584
1692
  ["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
1585
1693
  ];
1586
- var MonitorOff = createLucideIcon("monitor-off", __iconNode16);
1694
+ var MonitorOff = createLucideIcon("monitor-off", __iconNode18);
1587
1695
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
1588
- var __iconNode17 = [
1696
+ var __iconNode19 = [
1589
1697
  ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
1590
1698
  ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
1591
1699
  ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
1592
1700
  ];
1593
- var Monitor = createLucideIcon("monitor", __iconNode17);
1701
+ var Monitor = createLucideIcon("monitor", __iconNode19);
1594
1702
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mouse-pointer-2.js
1595
- var __iconNode18 = [
1703
+ var __iconNode20 = [
1596
1704
  [
1597
1705
  "path",
1598
1706
  {
@@ -1601,15 +1709,15 @@ var __iconNode18 = [
1601
1709
  }
1602
1710
  ]
1603
1711
  ];
1604
- var MousePointer2 = createLucideIcon("mouse-pointer-2", __iconNode18);
1712
+ var MousePointer2 = createLucideIcon("mouse-pointer-2", __iconNode20);
1605
1713
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/pause.js
1606
- var __iconNode19 = [
1714
+ var __iconNode21 = [
1607
1715
  ["rect", { x: "14", y: "3", width: "5", height: "18", rx: "1", key: "kaeet6" }],
1608
1716
  ["rect", { x: "5", y: "3", width: "5", height: "18", rx: "1", key: "1wsw3u" }]
1609
1717
  ];
1610
- var Pause = createLucideIcon("pause", __iconNode19);
1718
+ var Pause = createLucideIcon("pause", __iconNode21);
1611
1719
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
1612
- var __iconNode20 = [
1720
+ var __iconNode22 = [
1613
1721
  [
1614
1722
  "path",
1615
1723
  {
@@ -1626,9 +1734,9 @@ var __iconNode20 = [
1626
1734
  }
1627
1735
  ]
1628
1736
  ];
1629
- var PhoneOff = createLucideIcon("phone-off", __iconNode20);
1737
+ var PhoneOff = createLucideIcon("phone-off", __iconNode22);
1630
1738
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/play.js
1631
- var __iconNode21 = [
1739
+ var __iconNode23 = [
1632
1740
  [
1633
1741
  "path",
1634
1742
  {
@@ -1637,9 +1745,9 @@ var __iconNode21 = [
1637
1745
  }
1638
1746
  ]
1639
1747
  ];
1640
- var Play = createLucideIcon("play", __iconNode21);
1748
+ var Play = createLucideIcon("play", __iconNode23);
1641
1749
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/rocket.js
1642
- var __iconNode22 = [
1750
+ var __iconNode24 = [
1643
1751
  ["path", { d: "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5", key: "qeys4" }],
1644
1752
  [
1645
1753
  "path",
@@ -1657,9 +1765,9 @@ var __iconNode22 = [
1657
1765
  ],
1658
1766
  ["path", { d: "M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05", key: "92ym6u" }]
1659
1767
  ];
1660
- var Rocket = createLucideIcon("rocket", __iconNode22);
1768
+ var Rocket = createLucideIcon("rocket", __iconNode24);
1661
1769
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send.js
1662
- var __iconNode23 = [
1770
+ var __iconNode25 = [
1663
1771
  [
1664
1772
  "path",
1665
1773
  {
@@ -1669,15 +1777,15 @@ var __iconNode23 = [
1669
1777
  ],
1670
1778
  ["path", { d: "m21.854 2.147-10.94 10.939", key: "12cjpa" }]
1671
1779
  ];
1672
- var Send = createLucideIcon("send", __iconNode23);
1780
+ var Send = createLucideIcon("send", __iconNode25);
1673
1781
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/user-plus.js
1674
- var __iconNode24 = [
1782
+ var __iconNode26 = [
1675
1783
  ["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2", key: "1yyitq" }],
1676
1784
  ["circle", { cx: "9", cy: "7", r: "4", key: "nufk8" }],
1677
1785
  ["line", { x1: "19", x2: "19", y1: "8", y2: "14", key: "1bvyxn" }],
1678
1786
  ["line", { x1: "22", x2: "16", y1: "11", y2: "11", key: "1shjgl" }]
1679
1787
  ];
1680
- var UserPlus = createLucideIcon("user-plus", __iconNode24);
1788
+ var UserPlus = createLucideIcon("user-plus", __iconNode26);
1681
1789
  // src/components/HighlightOverlay.tsx
1682
1790
  import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState4 } from "react";
1683
1791
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1926,6 +2034,8 @@ function useLiveAgent() {
1926
2034
  throw new Error("useLiveAgent must be used within a <LiveAgent> provider");
1927
2035
  }
1928
2036
  const {
2037
+ usesHostAuth,
2038
+ authFailed,
1929
2039
  connection,
1930
2040
  shouldConnect,
1931
2041
  historyMessages,
@@ -3340,14 +3450,15 @@ function ModuleSelector() {
3340
3450
  const isFeatured = index2 === 0;
3341
3451
  const isWide = index2 === availableModules.length - 1 && availableModules.length % 2 === 1;
3342
3452
  const Icon2 = getAgentIcon(module.type);
3343
- const canResume = canResumeSession(module.currentSession, module.mode);
3453
+ const sessionState = getModuleSessionState(module.currentSession, module.mode);
3454
+ const isTerminal = sessionState === "completed" || sessionState === "expired";
3344
3455
  const base = "skippr:group skippr:flex skippr:cursor-pointer skippr:gap-3 skippr:rounded-xl skippr:text-left skippr:transition-colors skippr:disabled:cursor-not-allowed skippr:disabled:opacity-50";
3345
3456
  const layout = isWide ? "skippr:items-center skippr:p-3.5 skippr:pb-5" : "skippr:flex-col skippr:items-start skippr:p-3.5";
3346
3457
  const variant = isFeatured ? "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90" : "skippr:bg-background skippr:ring-1 skippr:ring-foreground/10 skippr:hover:bg-muted/50";
3347
3458
  const span = isWide ? "skippr:col-span-2" : "";
3348
3459
  return /* @__PURE__ */ jsxs13("button", {
3349
3460
  type: "button",
3350
- disabled: isBusy,
3461
+ disabled: isBusy || isTerminal,
3351
3462
  onClick: () => selectModule(module.id),
3352
3463
  className: `${base} ${layout} ${variant} ${span}`,
3353
3464
  children: [
@@ -3367,7 +3478,7 @@ function ModuleSelector() {
3367
3478
  className: "skippr:line-clamp-1 skippr:text-sm skippr:font-semibold",
3368
3479
  children: module.name
3369
3480
  }),
3370
- canResume && /* @__PURE__ */ jsxs13("span", {
3481
+ sessionState === "resume" && /* @__PURE__ */ jsxs13("span", {
3371
3482
  className: `skippr:inline-flex skippr:shrink-0 skippr:items-center skippr:gap-0.5 skippr:rounded-full skippr:px-1.5 skippr:py-0.5 skippr:text-[9px] skippr:font-medium skippr:uppercase skippr:tracking-wide ${isFeatured ? "skippr:bg-primary-foreground/20 skippr:text-primary-foreground" : "skippr:bg-bubble/15 skippr:text-bubble"}`,
3372
3483
  children: [
3373
3484
  /* @__PURE__ */ jsx14(Play, {
@@ -3375,6 +3486,24 @@ function ModuleSelector() {
3375
3486
  }),
3376
3487
  "Resume"
3377
3488
  ]
3489
+ }),
3490
+ sessionState === "completed" && /* @__PURE__ */ jsxs13("span", {
3491
+ className: "skippr:inline-flex skippr:shrink-0 skippr:items-center skippr:gap-0.5 skippr:rounded-full skippr:bg-muted skippr:px-1.5 skippr:py-0.5 skippr:text-[9px] skippr:font-medium skippr:uppercase skippr:tracking-wide skippr:text-muted-foreground",
3492
+ children: [
3493
+ /* @__PURE__ */ jsx14(CircleCheck, {
3494
+ className: "skippr:size-2.5"
3495
+ }),
3496
+ "Completed"
3497
+ ]
3498
+ }),
3499
+ sessionState === "expired" && /* @__PURE__ */ jsxs13("span", {
3500
+ className: "skippr:inline-flex skippr:shrink-0 skippr:items-center skippr:gap-0.5 skippr:rounded-full skippr:bg-muted skippr:px-1.5 skippr:py-0.5 skippr:text-[9px] skippr:font-medium skippr:uppercase skippr:tracking-wide skippr:text-muted-foreground",
3501
+ children: [
3502
+ /* @__PURE__ */ jsx14(Clock, {
3503
+ className: "skippr:size-2.5"
3504
+ }),
3505
+ "Expired"
3506
+ ]
3378
3507
  })
3379
3508
  ]
3380
3509
  }),
@@ -3549,6 +3678,9 @@ function Sidebar({
3549
3678
  agentMode,
3550
3679
  agentControls
3551
3680
  } = useLiveAgent();
3681
+ const internalCtx = useContextValue(LiveAgentContext);
3682
+ const usesHostAuth = internalCtx?.usesHostAuth ?? false;
3683
+ const authFailed = internalCtx?.authFailed ?? false;
3552
3684
  const showAgenda = agentMode !== "always_on";
3553
3685
  const isFloating = variant === "floating";
3554
3686
  const isSidebar = variant === "sidebar";
@@ -3571,17 +3703,7 @@ function Sidebar({
3571
3703
  style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
3572
3704
  children: [
3573
3705
  !hideHeader && /* @__PURE__ */ jsx18(ChatHeader, {}),
3574
- !isAuthenticated && isValidating ? /* @__PURE__ */ jsx18("div", {
3575
- className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
3576
- children: /* @__PURE__ */ jsx18(LoadingDots, {
3577
- label: "Loading..."
3578
- })
3579
- }) : !isAuthenticated ? /* @__PURE__ */ jsx18(LoginFlow, {
3580
- requestOtp,
3581
- verifyOtp,
3582
- error: authError,
3583
- isSubmitting: isAuthSubmitting
3584
- }) : /* @__PURE__ */ jsx18(AuthenticatedContent, {
3706
+ authFailed ? /* @__PURE__ */ jsx18(AuthError, {}) : isAuthenticated ? /* @__PURE__ */ jsx18(AuthenticatedContent, {
3585
3707
  isConnected,
3586
3708
  isPaused,
3587
3709
  isPausing,
@@ -3603,6 +3725,16 @@ function Sidebar({
3603
3725
  agentId,
3604
3726
  agentControls,
3605
3727
  showAgenda
3728
+ }) : usesHostAuth || isValidating ? /* @__PURE__ */ jsx18("div", {
3729
+ className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
3730
+ children: /* @__PURE__ */ jsx18(LoadingDots, {
3731
+ label: "Loading..."
3732
+ })
3733
+ }) : /* @__PURE__ */ jsx18(LoginFlow, {
3734
+ requestOtp,
3735
+ verifyOtp,
3736
+ error: authError,
3737
+ isSubmitting: isAuthSubmitting
3606
3738
  })
3607
3739
  ]
3608
3740
  });
@@ -3721,6 +3853,24 @@ function AuthenticatedContent({
3721
3853
  ]
3722
3854
  });
3723
3855
  }
3856
+ function AuthError() {
3857
+ return /* @__PURE__ */ jsxs16("div", {
3858
+ className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:px-6 skippr:py-4 skippr:text-center",
3859
+ children: [
3860
+ /* @__PURE__ */ jsx18(CircleAlert, {
3861
+ className: "skippr:mb-3 skippr:size-7 skippr:text-destructive"
3862
+ }),
3863
+ /* @__PURE__ */ jsx18("p", {
3864
+ className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
3865
+ children: "We couldn't verify your session"
3866
+ }),
3867
+ /* @__PURE__ */ jsx18("p", {
3868
+ className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
3869
+ children: "Please contact administrator."
3870
+ })
3871
+ ]
3872
+ });
3873
+ }
3724
3874
  function ConnectedBanner() {
3725
3875
  const remaining = useSessionRemaining();
3726
3876
  return /* @__PURE__ */ jsx18(SessionWarningBanner, {
@@ -3782,6 +3932,7 @@ function LiveAgent(props) {
3782
3932
  agentId: hostAgentId,
3783
3933
  authToken: authTokenProp,
3784
3934
  appKey,
3935
+ getUserToken,
3785
3936
  userToken,
3786
3937
  position = "right",
3787
3938
  variant = "floating",
@@ -3804,7 +3955,8 @@ function LiveAgent(props) {
3804
3955
  }
3805
3956
  }
3806
3957
  const auth = useAuth({ appKey });
3807
- const effectiveAuthToken = authTokenProp || auth.authToken || undefined;
3958
+ const usesHostAuth = !!getUserToken || !!userToken || !!authTokenProp;
3959
+ const effectiveAuthToken = usesHostAuth ? authTokenProp : auth.authToken ?? undefined;
3808
3960
  const hasModuleSelector = !hostAgentId;
3809
3961
  const [activeModule, setActiveModule] = useState11(null);
3810
3962
  const agentId = hasModuleSelector ? activeModule?.id ?? null : hostAgentId;
@@ -3842,11 +3994,14 @@ function LiveAgent(props) {
3842
3994
  isPaused,
3843
3995
  historyMessages,
3844
3996
  pendingScreenStream,
3845
- bearerToken
3997
+ bearerToken,
3998
+ authedFetch,
3999
+ authFailed
3846
4000
  } = useSession({
3847
4001
  captureMode,
3848
4002
  authToken: effectiveAuthToken,
3849
4003
  appKey,
4004
+ getUserToken,
3850
4005
  userToken,
3851
4006
  onStart: minimizeOnSessionStart,
3852
4007
  onStartError: expandOnSessionStartError,
@@ -3870,7 +4025,8 @@ function LiveAgent(props) {
3870
4025
  refetch: refetchModules
3871
4026
  } = useAvailableModules({
3872
4027
  enabled: hasModuleSelector && isPanelOpen,
3873
- bearerToken
4028
+ bearerToken,
4029
+ authedFetch
3874
4030
  });
3875
4031
  const activeModuleForAgent = useMemo5(() => {
3876
4032
  if (activeModule)
@@ -3931,7 +4087,7 @@ function LiveAgent(props) {
3931
4087
  setIsPanelOpen(false);
3932
4088
  }, [minimizable]);
3933
4089
  const isConnected = connection !== null;
3934
- const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
4090
+ const isAuthenticated = bearerToken !== null || auth.isAuthenticated;
3935
4091
  const ctx = useMemo5(() => ({
3936
4092
  connection,
3937
4093
  shouldConnect,
@@ -3962,6 +4118,8 @@ function LiveAgent(props) {
3962
4118
  expandPanel,
3963
4119
  minimizePanel,
3964
4120
  isAuthenticated,
4121
+ usesHostAuth,
4122
+ authFailed,
3965
4123
  isValidating: auth.isValidating,
3966
4124
  authError: auth.error,
3967
4125
  requestOtp: auth.requestOtp,
@@ -4011,6 +4169,8 @@ function LiveAgent(props) {
4011
4169
  expandPanel,
4012
4170
  minimizePanel,
4013
4171
  isAuthenticated,
4172
+ usesHostAuth,
4173
+ authFailed,
4014
4174
  auth.isValidating,
4015
4175
  auth.error,
4016
4176
  auth.requestOtp,