@skippr/live-agent-sdk 0.7.0 → 0.9.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.
@@ -1,6 +1,6 @@
1
1
  // src/components/LiveAgent.tsx
2
2
  import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
3
- import { useCallback as useCallback6, useMemo as useMemo2, useState as useState4 } from "react";
3
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState5 } from "react";
4
4
 
5
5
  // src/context/LiveAgentContext.tsx
6
6
  import { createContext } from "react";
@@ -9,7 +9,7 @@ var LiveAgentContext = createContext(null);
9
9
  // src/hooks/useSession.ts
10
10
  import { useCallback, useState } from "react";
11
11
  var API_URL = "https://skipprapi-production.up.railway.app";
12
- function useSession({ organizationId, agentId }) {
12
+ function useSession({ organizationId, agentId, authToken }) {
13
13
  const [connection, setConnection] = useState(null);
14
14
  const [shouldConnect, setShouldConnect] = useState(false);
15
15
  const [isStarting, setIsStarting] = useState(false);
@@ -19,9 +19,10 @@ function useSession({ organizationId, agentId }) {
19
19
  setIsStarting(true);
20
20
  setError("");
21
21
  try {
22
+ const authHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};
22
23
  const createResp = await fetch(`${API_URL}/v1/sessions`, {
23
24
  method: "POST",
24
- headers: { "Content-Type": "application/json" },
25
+ headers: { "Content-Type": "application/json", ...authHeaders },
25
26
  body: JSON.stringify({ organizationId, agentId })
26
27
  });
27
28
  if (!createResp.ok) {
@@ -29,7 +30,8 @@ function useSession({ organizationId, agentId }) {
29
30
  }
30
31
  const { session } = await createResp.json();
31
32
  const startResp = await fetch(`${API_URL}/v1/sessions/${session.id}/start`, {
32
- method: "POST"
33
+ method: "POST",
34
+ headers: authHeaders
33
35
  });
34
36
  if (!startResp.ok) {
35
37
  throw new Error(`Failed to start session: ${startResp.status}`);
@@ -46,13 +48,14 @@ function useSession({ organizationId, agentId }) {
46
48
  } finally {
47
49
  setIsStarting(false);
48
50
  }
49
- }, [organizationId, agentId]);
51
+ }, [organizationId, agentId, authToken]);
50
52
  const disconnect = useCallback(async () => {
51
53
  if (sessionId) {
54
+ const authHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};
52
55
  try {
53
56
  await fetch(`${API_URL}/v1/sessions/${sessionId}/complete`, {
54
57
  method: "POST",
55
- headers: { "Content-Type": "application/json" },
58
+ headers: { "Content-Type": "application/json", ...authHeaders },
56
59
  body: JSON.stringify({})
57
60
  });
58
61
  } catch {}
@@ -61,14 +64,94 @@ function useSession({ organizationId, agentId }) {
61
64
  setShouldConnect(false);
62
65
  setConnection(null);
63
66
  setSessionId(null);
64
- }, [sessionId]);
67
+ }, [sessionId, authToken]);
65
68
  return { connection, shouldConnect, isStarting, error, startSession, disconnect };
66
69
  }
67
70
 
68
71
  // src/components/Sidebar.tsx
69
72
  import { useConnectionState } from "@livekit/components-react";
70
73
  import { ConnectionState } from "livekit-client";
71
- import { useEffect as useEffect3 } from "react";
74
+ import { useEffect as useEffect4 } from "react";
75
+
76
+ // src/hooks/useCombinedMessages.ts
77
+ import { useVoiceAssistant } from "@livekit/components-react";
78
+ import { useMemo as useMemo3 } from "react";
79
+
80
+ // src/hooks/useChatMessages.ts
81
+ import { useChat, useLocalParticipant } from "@livekit/components-react";
82
+ import { useMemo } from "react";
83
+
84
+ // src/lib/filterSystemMessages.ts
85
+ var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
86
+ function filterSystemMessages(messages) {
87
+ return messages.filter((m) => !SYSTEM_MESSAGE_PATTERN.test(m.content.trim()));
88
+ }
89
+
90
+ // src/hooks/useChatMessages.ts
91
+ function useChatMessages() {
92
+ const { chatMessages: rawMessages, send, isSending } = useChat();
93
+ const { localParticipant } = useLocalParticipant();
94
+ const localIdentity = localParticipant.identity;
95
+ const chatMessages = useMemo(() => {
96
+ const sortedMessages = rawMessages.map((msg) => ({
97
+ id: msg.id,
98
+ role: msg.from?.identity === localIdentity ? "user" : "assistant",
99
+ content: msg.message,
100
+ source: "chat",
101
+ timestamp: msg.timestamp
102
+ })).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
103
+ return filterSystemMessages(sortedMessages);
104
+ }, [rawMessages, localIdentity]);
105
+ return { chatMessages, sendChatMessage: send, isSendingChat: isSending };
106
+ }
107
+
108
+ // src/hooks/useStreamingTranscript.ts
109
+ import { useLocalParticipant as useLocalParticipant2, useTranscriptions } from "@livekit/components-react";
110
+ import { useMemo as useMemo2 } from "react";
111
+ function useStreamingTranscript() {
112
+ const transcriptions = useTranscriptions();
113
+ const { localParticipant } = useLocalParticipant2();
114
+ const localIdentity = localParticipant.identity;
115
+ const transcriptMessages = useMemo2(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
116
+ id: stream.streamInfo.id,
117
+ role: stream.participantInfo.identity === localIdentity ? "user" : "assistant",
118
+ content: stream.text,
119
+ source: "voice-transcript",
120
+ timestamp: stream.streamInfo.timestamp
121
+ }))), [transcriptions, localIdentity]);
122
+ return { transcriptMessages };
123
+ }
124
+
125
+ // src/hooks/useCombinedMessages.ts
126
+ function mergeChatsIntoTranscripts(transcripts, chats) {
127
+ const merged = [];
128
+ let chatIndex = 0;
129
+ for (const transcript of transcripts) {
130
+ while (chatIndex < chats.length && (chats[chatIndex].timestamp ?? 0) <= (transcript.timestamp ?? 0)) {
131
+ merged.push(chats[chatIndex]);
132
+ chatIndex++;
133
+ }
134
+ merged.push(transcript);
135
+ }
136
+ while (chatIndex < chats.length) {
137
+ merged.push(chats[chatIndex]);
138
+ chatIndex++;
139
+ }
140
+ return merged;
141
+ }
142
+ function useCombinedMessages() {
143
+ const { transcriptMessages } = useStreamingTranscript();
144
+ const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
145
+ const { state: agentState } = useVoiceAssistant();
146
+ const allMessages = useMemo3(() => {
147
+ if (chatMessages.length === 0)
148
+ return transcriptMessages;
149
+ if (transcriptMessages.length === 0)
150
+ return chatMessages;
151
+ return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
152
+ }, [transcriptMessages, chatMessages]);
153
+ return { allMessages, agentState, sendChatMessage, isSendingChat };
154
+ }
72
155
 
73
156
  // src/hooks/useLiveAgent.ts
74
157
  import { use } from "react";
@@ -158,43 +241,6 @@ function useQuestionUpdates() {
158
241
  return { questions };
159
242
  }
160
243
 
161
- // src/hooks/useTranscriptMessages.ts
162
- import {
163
- useLocalParticipant,
164
- useTrackTranscription,
165
- useVoiceAssistant
166
- } from "@livekit/components-react";
167
- import { Track } from "livekit-client";
168
- import { useMemo } from "react";
169
- function segmentsToMessages(segments, role) {
170
- return segments.filter((s) => s.final && s.text.trim().length > 0).map((s) => ({
171
- id: s.id,
172
- role,
173
- content: s.text
174
- }));
175
- }
176
- function useTranscriptMessages() {
177
- const { agentTranscriptions, state } = useVoiceAssistant();
178
- const { localParticipant } = useLocalParticipant();
179
- const localMicTrack = useMemo(() => localParticipant.getTrackPublication(Track.Source.Microphone) ? {
180
- participant: localParticipant,
181
- source: Track.Source.Microphone,
182
- publication: localParticipant.getTrackPublication(Track.Source.Microphone)
183
- } : undefined, [localParticipant]);
184
- const { segments: userSegments } = useTrackTranscription(localMicTrack);
185
- const messages = useMemo(() => {
186
- const agent = segmentsToMessages(agentTranscriptions, "assistant");
187
- const user = segmentsToMessages(userSegments, "user");
188
- const timeMap = new Map;
189
- for (const s of agentTranscriptions)
190
- timeMap.set(s.id, s.firstReceivedTime);
191
- for (const s of userSegments)
192
- timeMap.set(s.id, s.firstReceivedTime);
193
- return [...agent, ...user].sort((a, b) => (timeMap.get(a.id) ?? 0) - (timeMap.get(b.id) ?? 0));
194
- }, [agentTranscriptions, userSegments]);
195
- return { messages, agentState: state };
196
- }
197
-
198
244
  // src/lib/constants.ts
199
245
  var SIDEBAR_WIDTH = 600;
200
246
 
@@ -270,7 +316,7 @@ function ChatHeader({ onClose }) {
270
316
  }
271
317
 
272
318
  // src/components/MeetingControls.tsx
273
- import { useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
319
+ import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
274
320
  import { ScreenSharePresets } from "livekit-client";
275
321
  import { Mic, MicOff, Monitor, MonitorOff, PhoneOff } from "lucide-react";
276
322
  import { useCallback as useCallback4, useEffect as useEffect2, useState as useState3 } from "react";
@@ -285,7 +331,7 @@ function formatTime(seconds) {
285
331
  // src/components/MeetingControls.tsx
286
332
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
287
333
  function MeetingControls({ onHangUp }) {
288
- const { localParticipant } = useLocalParticipant2();
334
+ const { localParticipant } = useLocalParticipant3();
289
335
  const isMuted = !localParticipant.isMicrophoneEnabled;
290
336
  const isScreenSharing = localParticipant.isScreenShareEnabled;
291
337
  const [elapsed, setElapsed] = useState3(0);
@@ -362,15 +408,55 @@ function MeetingControls({ onHangUp }) {
362
408
  }
363
409
 
364
410
  // src/components/MessageList.tsx
365
- import { useCallback as useCallback5 } from "react";
411
+ import { useEffect as useEffect3, useRef } from "react";
412
+
413
+ // src/components/ChatInput.tsx
414
+ import { SendHorizontal } from "lucide-react";
415
+ import { useState as useState4 } from "react";
416
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
417
+ function ChatInput({ sendChatMessage, isSendingChat }) {
418
+ const [inputText, setInputText] = useState4("");
419
+ const canSend = inputText.trim().length > 0 && !isSendingChat;
420
+ function handleSubmit(e) {
421
+ e.preventDefault();
422
+ const text = inputText.trim();
423
+ if (!text || isSendingChat)
424
+ return;
425
+ setInputText("");
426
+ sendChatMessage(text).catch(() => setInputText(text));
427
+ }
428
+ return /* @__PURE__ */ jsxs3("form", {
429
+ onSubmit: handleSubmit,
430
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
431
+ children: [
432
+ /* @__PURE__ */ jsx4("input", {
433
+ type: "text",
434
+ value: inputText,
435
+ onChange: (e) => setInputText(e.target.value),
436
+ placeholder: "Type a message...",
437
+ className: cn("skippr:flex-1 skippr:rounded-lg skippr:border skippr:border-border skippr:bg-background", "skippr:px-3 skippr:py-2 skippr:text-sm skippr:text-foreground", "skippr:placeholder:text-muted-foreground skippr:outline-none", "skippr:focus:ring-1 skippr:focus:ring-ring"),
438
+ disabled: isSendingChat
439
+ }),
440
+ /* @__PURE__ */ jsx4("button", {
441
+ type: "submit",
442
+ disabled: !canSend,
443
+ "aria-label": "Send message",
444
+ className: cn("skippr:flex skippr:size-9 skippr:shrink-0 skippr:items-center skippr:justify-center", "skippr:rounded-lg skippr:bg-primary skippr:text-primary-foreground", "skippr:transition-opacity", !canSend && "skippr:opacity-50 skippr:cursor-not-allowed"),
445
+ children: /* @__PURE__ */ jsx4(SendHorizontal, {
446
+ className: "skippr:size-4"
447
+ })
448
+ })
449
+ ]
450
+ });
451
+ }
366
452
 
367
453
  // src/components/ChatMessage.tsx
368
- import { jsx as jsx4 } from "react/jsx-runtime";
454
+ import { jsx as jsx5 } from "react/jsx-runtime";
369
455
  function ChatMessage({ message }) {
370
456
  const isUser = message.role === "user";
371
- return /* @__PURE__ */ jsx4("div", {
457
+ return /* @__PURE__ */ jsx5("div", {
372
458
  className: cn("skippr:flex skippr:w-full skippr:px-4 skippr:py-1", isUser ? "skippr:justify-end" : "skippr:justify-start"),
373
- children: /* @__PURE__ */ jsx4("div", {
459
+ children: /* @__PURE__ */ jsx5("div", {
374
460
  className: cn("skippr:max-w-[85%] skippr:whitespace-pre-wrap skippr:rounded-2xl skippr:px-4 skippr:py-2.5 skippr:text-sm skippr:leading-relaxed", isUser ? "skippr:rounded-br-sm skippr:bg-primary skippr:text-primary-foreground" : "skippr:rounded-bl-sm skippr:bg-muted skippr:text-foreground"),
375
461
  children: message.content
376
462
  })
@@ -378,20 +464,20 @@ function ChatMessage({ message }) {
378
464
  }
379
465
 
380
466
  // src/components/TypingIndicator.tsx
381
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
467
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
382
468
  function TypingIndicator() {
383
- return /* @__PURE__ */ jsx5("div", {
469
+ return /* @__PURE__ */ jsx6("div", {
384
470
  className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
385
- children: /* @__PURE__ */ jsxs3("div", {
471
+ children: /* @__PURE__ */ jsxs4("div", {
386
472
  className: "skippr:flex skippr:items-center skippr:gap-1 skippr:rounded-2xl skippr:rounded-bl-sm skippr:bg-muted skippr:px-4 skippr:py-2.5",
387
473
  children: [
388
- /* @__PURE__ */ jsx5("span", {
474
+ /* @__PURE__ */ jsx6("span", {
389
475
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
390
476
  }),
391
- /* @__PURE__ */ jsx5("span", {
477
+ /* @__PURE__ */ jsx6("span", {
392
478
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
393
479
  }),
394
- /* @__PURE__ */ jsx5("span", {
480
+ /* @__PURE__ */ jsx6("span", {
395
481
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
396
482
  })
397
483
  ]
@@ -400,56 +486,71 @@ function TypingIndicator() {
400
486
  }
401
487
 
402
488
  // src/components/MessageList.tsx
403
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
404
- function MessageList({ messages, isStreaming }) {
405
- const scrollRef = useCallback5((node) => {
406
- node?.scrollIntoView({ behavior: "smooth" });
407
- }, []);
489
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
490
+ function MessageList({
491
+ messages,
492
+ isStreaming,
493
+ sendChatMessage,
494
+ isSendingChat
495
+ }) {
496
+ const scrollRef = useRef(null);
408
497
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
498
+ useEffect3(() => {
499
+ scrollRef.current?.scrollIntoView({ behavior: "smooth" });
500
+ }, [messages.length, lastMessage?.content]);
409
501
  const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
410
- return /* @__PURE__ */ jsx6("div", {
411
- className: "skippr:flex-1 skippr:overflow-y-auto",
412
- children: /* @__PURE__ */ jsxs4("div", {
413
- className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
414
- children: [
415
- messages.map((message) => /* @__PURE__ */ jsx6(ChatMessage, {
416
- message
417
- }, message.id)),
418
- showTyping && /* @__PURE__ */ jsx6(TypingIndicator, {}),
419
- /* @__PURE__ */ jsx6("div", {
420
- ref: scrollRef
421
- }, `scroll-${messages.length}`)
422
- ]
423
- })
502
+ return /* @__PURE__ */ jsxs5("div", {
503
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
504
+ children: [
505
+ /* @__PURE__ */ jsx7("div", {
506
+ className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
507
+ children: /* @__PURE__ */ jsxs5("div", {
508
+ className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
509
+ children: [
510
+ messages.map((message) => /* @__PURE__ */ jsx7(ChatMessage, {
511
+ message
512
+ }, message.id)),
513
+ showTyping && /* @__PURE__ */ jsx7(TypingIndicator, {}),
514
+ /* @__PURE__ */ jsx7("div", {
515
+ ref: scrollRef
516
+ })
517
+ ]
518
+ })
519
+ }),
520
+ /* @__PURE__ */ jsx7(ChatInput, {
521
+ sendChatMessage,
522
+ isSendingChat
523
+ })
524
+ ]
424
525
  });
425
526
  }
426
527
 
427
528
  // src/components/QuickActions.tsx
428
529
  import { Loader2, MessageCircleQuestion } from "lucide-react";
429
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
530
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
430
531
  function QuickActions({ onStartSession, isStarting, error }) {
431
- return /* @__PURE__ */ jsxs5("div", {
532
+ return /* @__PURE__ */ jsxs6("div", {
432
533
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:gap-6 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
433
534
  children: [
434
- /* @__PURE__ */ jsx7("p", {
535
+ /* @__PURE__ */ jsx8("p", {
435
536
  className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
436
537
  children: "How can I help you today?"
437
538
  }),
438
- /* @__PURE__ */ jsxs5(Button, {
539
+ /* @__PURE__ */ jsxs6(Button, {
439
540
  variant: "outline",
440
541
  className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
441
542
  onClick: onStartSession,
442
543
  disabled: isStarting,
443
544
  children: [
444
- isStarting ? /* @__PURE__ */ jsx7(Loader2, {
545
+ isStarting ? /* @__PURE__ */ jsx8(Loader2, {
445
546
  className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
446
- }) : /* @__PURE__ */ jsx7(MessageCircleQuestion, {
547
+ }) : /* @__PURE__ */ jsx8(MessageCircleQuestion, {
447
548
  className: "skippr:size-4 skippr:text-primary"
448
549
  }),
449
550
  isStarting ? "Starting..." : "Start Session"
450
551
  ]
451
552
  }),
452
- error && /* @__PURE__ */ jsx7("p", {
553
+ error && /* @__PURE__ */ jsx8("p", {
453
554
  className: "skippr:text-xs skippr:text-destructive",
454
555
  children: error
455
556
  })
@@ -459,52 +560,52 @@ function QuickActions({ onStartSession, isStarting, error }) {
459
560
 
460
561
  // src/components/SessionAgenda.tsx
461
562
  import { Check, Circle, Loader2 as Loader22 } from "lucide-react";
462
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
563
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
463
564
  function SessionAgenda({ phases, questions = [] }) {
464
565
  if (phases.length === 0) {
465
- return /* @__PURE__ */ jsxs6("div", {
566
+ return /* @__PURE__ */ jsxs7("div", {
466
567
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
467
568
  children: [
468
- /* @__PURE__ */ jsx8("h3", {
569
+ /* @__PURE__ */ jsx9("h3", {
469
570
  className: "skippr:text-sm skippr:font-semibold",
470
571
  children: "Agenda"
471
572
  }),
472
- /* @__PURE__ */ jsx8("p", {
573
+ /* @__PURE__ */ jsx9("p", {
473
574
  className: "skippr:text-xs skippr:text-muted-foreground",
474
575
  children: "Waiting for session..."
475
576
  })
476
577
  ]
477
578
  });
478
579
  }
479
- return /* @__PURE__ */ jsxs6("div", {
580
+ return /* @__PURE__ */ jsxs7("div", {
480
581
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
481
582
  children: [
482
- /* @__PURE__ */ jsx8("h3", {
583
+ /* @__PURE__ */ jsx9("h3", {
483
584
  className: "skippr:text-sm skippr:font-semibold",
484
585
  children: "Agenda"
485
586
  }),
486
- /* @__PURE__ */ jsx8("ul", {
587
+ /* @__PURE__ */ jsx9("ul", {
487
588
  className: "skippr:flex skippr:flex-col skippr:gap-2",
488
589
  children: phases.map((phase) => {
489
590
  const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
490
591
  const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
491
592
  const totalCount = phaseQuestions.length;
492
- return /* @__PURE__ */ jsxs6("li", {
593
+ return /* @__PURE__ */ jsxs7("li", {
493
594
  className: "skippr:flex skippr:flex-col skippr:gap-0.5",
494
595
  children: [
495
- /* @__PURE__ */ jsxs6("div", {
596
+ /* @__PURE__ */ jsxs7("div", {
496
597
  className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
497
598
  children: [
498
- /* @__PURE__ */ jsx8(PhaseIcon, {
599
+ /* @__PURE__ */ jsx9(PhaseIcon, {
499
600
  status: phase.status
500
601
  }),
501
- /* @__PURE__ */ jsx8("span", {
602
+ /* @__PURE__ */ jsx9("span", {
502
603
  className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
503
604
  children: phase.name
504
605
  })
505
606
  ]
506
607
  }),
507
- totalCount > 0 && /* @__PURE__ */ jsxs6("span", {
608
+ totalCount > 0 && /* @__PURE__ */ jsxs7("span", {
508
609
  className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
509
610
  children: [
510
611
  answeredCount,
@@ -522,56 +623,56 @@ function SessionAgenda({ phases, questions = [] }) {
522
623
  }
523
624
  function PhaseIcon({ status }) {
524
625
  if (status === "completed") {
525
- return /* @__PURE__ */ jsx8("div", {
626
+ return /* @__PURE__ */ jsx9("div", {
526
627
  className: "skippr:flex skippr:size-4 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary",
527
- children: /* @__PURE__ */ jsx8(Check, {
628
+ children: /* @__PURE__ */ jsx9(Check, {
528
629
  className: "skippr:size-2.5 skippr:text-primary-foreground",
529
630
  strokeWidth: 3
530
631
  })
531
632
  });
532
633
  }
533
634
  if (status === "active") {
534
- return /* @__PURE__ */ jsx8(Loader22, {
635
+ return /* @__PURE__ */ jsx9(Loader22, {
535
636
  className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
536
637
  });
537
638
  }
538
- return /* @__PURE__ */ jsx8(Circle, {
639
+ return /* @__PURE__ */ jsx9(Circle, {
539
640
  className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
540
641
  });
541
642
  }
542
643
 
543
644
  // src/components/Sidebar.tsx
544
- import { jsx as jsx9, jsxs as jsxs7, Fragment } from "react/jsx-runtime";
645
+ import { jsx as jsx10, jsxs as jsxs8, Fragment } from "react/jsx-runtime";
545
646
  function Sidebar() {
546
647
  const { isConnected, isStarting, error, startSession, disconnect, isPanelOpen, closePanel } = useLiveAgent();
547
- useEffect3(() => {
648
+ useEffect4(() => {
548
649
  document.body.style.transition = "margin-right 300ms ease-in-out";
549
650
  document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
550
651
  }, [isPanelOpen]);
551
- useEffect3(() => {
652
+ useEffect4(() => {
552
653
  return () => {
553
654
  document.body.style.marginRight = "";
554
655
  document.body.style.transition = "";
555
656
  };
556
657
  }, []);
557
- return /* @__PURE__ */ jsx9("div", {
658
+ return /* @__PURE__ */ jsx10("div", {
558
659
  className: cn("skippr:fixed skippr:top-0 skippr:right-0 skippr:h-full skippr:z-[9999]", "skippr:bg-background skippr:border-l skippr:border-border", "skippr:flex skippr:flex-col", "skippr:transition-all skippr:duration-300 skippr:ease-in-out skippr:overflow-hidden", !isPanelOpen && "skippr:w-0 skippr:border-l-0"),
559
660
  style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
560
- children: isConnected ? /* @__PURE__ */ jsxs7(Fragment, {
661
+ children: isConnected ? /* @__PURE__ */ jsxs8(Fragment, {
561
662
  children: [
562
- /* @__PURE__ */ jsx9(ChatHeader, {
663
+ /* @__PURE__ */ jsx10(ChatHeader, {
563
664
  onClose: closePanel
564
665
  }),
565
- /* @__PURE__ */ jsx9(ConnectedContent, {
666
+ /* @__PURE__ */ jsx10(ConnectedContent, {
566
667
  onDisconnect: disconnect
567
668
  })
568
669
  ]
569
- }) : /* @__PURE__ */ jsxs7(Fragment, {
670
+ }) : /* @__PURE__ */ jsxs8(Fragment, {
570
671
  children: [
571
- /* @__PURE__ */ jsx9(ChatHeader, {
672
+ /* @__PURE__ */ jsx10(ChatHeader, {
572
673
  onClose: closePanel
573
674
  }),
574
- /* @__PURE__ */ jsx9(QuickActions, {
675
+ /* @__PURE__ */ jsx10(QuickActions, {
575
676
  onStartSession: startSession,
576
677
  isStarting,
577
678
  error
@@ -583,39 +684,41 @@ function Sidebar() {
583
684
  function ConnectedContent({ onDisconnect }) {
584
685
  const connectionState = useConnectionState();
585
686
  const isConnected = connectionState === ConnectionState.Connected;
586
- const { messages, agentState } = useTranscriptMessages();
687
+ const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
587
688
  const { phases } = usePhaseUpdates();
588
689
  const { questions } = useQuestionUpdates();
589
690
  if (!isConnected) {
590
- return /* @__PURE__ */ jsx9("div", {
691
+ return /* @__PURE__ */ jsx10("div", {
591
692
  className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
592
- children: /* @__PURE__ */ jsx9("p", {
693
+ children: /* @__PURE__ */ jsx10("p", {
593
694
  className: "skippr:text-sm skippr:text-muted-foreground",
594
695
  children: "Connecting..."
595
696
  })
596
697
  });
597
698
  }
598
699
  const isAgentSpeaking = agentState === "speaking";
599
- return /* @__PURE__ */ jsxs7(Fragment, {
700
+ return /* @__PURE__ */ jsxs8(Fragment, {
600
701
  children: [
601
- /* @__PURE__ */ jsx9(MeetingControls, {
702
+ /* @__PURE__ */ jsx10(MeetingControls, {
602
703
  onHangUp: onDisconnect
603
704
  }),
604
- /* @__PURE__ */ jsxs7("div", {
705
+ /* @__PURE__ */ jsxs8("div", {
605
706
  className: "skippr:flex skippr:min-h-0 skippr:flex-1",
606
707
  children: [
607
- /* @__PURE__ */ jsx9("div", {
708
+ /* @__PURE__ */ jsx10("div", {
608
709
  className: "skippr:w-[260px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
609
- children: /* @__PURE__ */ jsx9(SessionAgenda, {
710
+ children: /* @__PURE__ */ jsx10(SessionAgenda, {
610
711
  phases,
611
712
  questions
612
713
  })
613
714
  }),
614
- /* @__PURE__ */ jsx9("div", {
715
+ /* @__PURE__ */ jsx10("div", {
615
716
  className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
616
- children: /* @__PURE__ */ jsx9(MessageList, {
617
- messages,
618
- isStreaming: isAgentSpeaking
717
+ children: /* @__PURE__ */ jsx10(MessageList, {
718
+ messages: allMessages,
719
+ isStreaming: isAgentSpeaking,
720
+ sendChatMessage,
721
+ isSendingChat
619
722
  })
620
723
  })
621
724
  ]
@@ -626,43 +729,45 @@ function ConnectedContent({ onDisconnect }) {
626
729
 
627
730
  // src/components/SidebarTrigger.tsx
628
731
  import { MessageCircle, X as X2 } from "lucide-react";
629
- import { jsx as jsx10 } from "react/jsx-runtime";
732
+ import { jsx as jsx11 } from "react/jsx-runtime";
630
733
  var TRIGGER_GAP = 16;
631
734
  var TRIGGER_DEFAULT_RIGHT = 24;
632
735
  function SidebarTrigger() {
633
736
  const { isPanelOpen, togglePanel } = useLiveAgent();
634
- return /* @__PURE__ */ jsx10(Button, {
737
+ return /* @__PURE__ */ jsx11(Button, {
635
738
  size: "icon-lg",
636
739
  onClick: togglePanel,
637
740
  className: "skippr:fixed skippr:bottom-6 skippr:z-[9998] skippr:size-14 skippr:rounded-full skippr:shadow-lg skippr:transition-all skippr:duration-300",
638
741
  style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
639
742
  title: isPanelOpen ? "Close chat" : "Chat with us",
640
- children: isPanelOpen ? /* @__PURE__ */ jsx10(X2, {
743
+ children: isPanelOpen ? /* @__PURE__ */ jsx11(X2, {
641
744
  className: "skippr:size-6"
642
- }) : /* @__PURE__ */ jsx10(MessageCircle, {
745
+ }) : /* @__PURE__ */ jsx11(MessageCircle, {
643
746
  className: "skippr:size-6"
644
747
  })
645
748
  });
646
749
  }
647
750
 
648
751
  // src/components/LiveAgent.tsx
649
- import { jsx as jsx11, jsxs as jsxs8, Fragment as Fragment2 } from "react/jsx-runtime";
752
+ import { jsx as jsx12, jsxs as jsxs9, Fragment as Fragment2 } from "react/jsx-runtime";
650
753
  function LiveAgent({
651
754
  organizationId,
652
755
  agentId,
756
+ authToken,
653
757
  defaultOpen = false,
654
758
  children
655
759
  }) {
656
760
  const { connection, shouldConnect, isStarting, error, startSession, disconnect } = useSession({
657
761
  organizationId,
658
- agentId
762
+ agentId,
763
+ authToken
659
764
  });
660
- const [isPanelOpen, setIsPanelOpen] = useState4(defaultOpen);
661
- const openPanel = useCallback6(() => setIsPanelOpen(true), []);
662
- const closePanel = useCallback6(() => setIsPanelOpen(false), []);
663
- const togglePanel = useCallback6(() => setIsPanelOpen((prev) => !prev), []);
765
+ const [isPanelOpen, setIsPanelOpen] = useState5(defaultOpen);
766
+ const openPanel = useCallback5(() => setIsPanelOpen(true), []);
767
+ const closePanel = useCallback5(() => setIsPanelOpen(false), []);
768
+ const togglePanel = useCallback5(() => setIsPanelOpen((prev) => !prev), []);
664
769
  const isConnected = connection !== null;
665
- const ctx = useMemo2(() => ({
770
+ const ctx = useMemo4(() => ({
666
771
  connection,
667
772
  shouldConnect,
668
773
  isConnected,
@@ -687,17 +792,17 @@ function LiveAgent({
687
792
  closePanel,
688
793
  togglePanel
689
794
  ]);
690
- const widgetContent = /* @__PURE__ */ jsxs8(Fragment2, {
795
+ const widgetContent = /* @__PURE__ */ jsxs9(Fragment2, {
691
796
  children: [
692
- connection && /* @__PURE__ */ jsx11(RoomAudioRenderer, {}),
693
- /* @__PURE__ */ jsx11(SidebarTrigger, {}),
694
- /* @__PURE__ */ jsx11(Sidebar, {}),
797
+ connection && /* @__PURE__ */ jsx12(RoomAudioRenderer, {}),
798
+ /* @__PURE__ */ jsx12(SidebarTrigger, {}),
799
+ /* @__PURE__ */ jsx12(Sidebar, {}),
695
800
  children
696
801
  ]
697
802
  });
698
- return /* @__PURE__ */ jsx11(LiveAgentContext.Provider, {
803
+ return /* @__PURE__ */ jsx12(LiveAgentContext.Provider, {
699
804
  value: ctx,
700
- children: connection ? /* @__PURE__ */ jsx11(LiveKitRoom, {
805
+ children: connection ? /* @__PURE__ */ jsx12(LiveKitRoom, {
701
806
  serverUrl: connection.livekitUrl,
702
807
  token: connection.token,
703
808
  connect: shouldConnect,