@skippr/live-agent-sdk 0.7.0 → 0.8.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";
@@ -68,7 +68,87 @@ function useSession({ organizationId, agentId }) {
68
68
  // src/components/Sidebar.tsx
69
69
  import { useConnectionState } from "@livekit/components-react";
70
70
  import { ConnectionState } from "livekit-client";
71
- import { useEffect as useEffect3 } from "react";
71
+ import { useEffect as useEffect4 } from "react";
72
+
73
+ // src/hooks/useCombinedMessages.ts
74
+ import { useVoiceAssistant } from "@livekit/components-react";
75
+ import { useMemo as useMemo3 } from "react";
76
+
77
+ // src/hooks/useChatMessages.ts
78
+ import { useChat, useLocalParticipant } from "@livekit/components-react";
79
+ import { useMemo } from "react";
80
+
81
+ // src/lib/filterSystemMessages.ts
82
+ var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
83
+ function filterSystemMessages(messages) {
84
+ return messages.filter((m) => !SYSTEM_MESSAGE_PATTERN.test(m.content.trim()));
85
+ }
86
+
87
+ // src/hooks/useChatMessages.ts
88
+ function useChatMessages() {
89
+ const { chatMessages: rawMessages, send, isSending } = useChat();
90
+ const { localParticipant } = useLocalParticipant();
91
+ const localIdentity = localParticipant.identity;
92
+ const chatMessages = useMemo(() => {
93
+ const sortedMessages = rawMessages.map((msg) => ({
94
+ id: msg.id,
95
+ role: msg.from?.identity === localIdentity ? "user" : "assistant",
96
+ content: msg.message,
97
+ source: "chat",
98
+ timestamp: msg.timestamp
99
+ })).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
100
+ return filterSystemMessages(sortedMessages);
101
+ }, [rawMessages, localIdentity]);
102
+ return { chatMessages, sendChatMessage: send, isSendingChat: isSending };
103
+ }
104
+
105
+ // src/hooks/useStreamingTranscript.ts
106
+ import { useLocalParticipant as useLocalParticipant2, useTranscriptions } from "@livekit/components-react";
107
+ import { useMemo as useMemo2 } from "react";
108
+ function useStreamingTranscript() {
109
+ const transcriptions = useTranscriptions();
110
+ const { localParticipant } = useLocalParticipant2();
111
+ const localIdentity = localParticipant.identity;
112
+ const transcriptMessages = useMemo2(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
113
+ id: stream.streamInfo.id,
114
+ role: stream.participantInfo.identity === localIdentity ? "user" : "assistant",
115
+ content: stream.text,
116
+ source: "voice-transcript",
117
+ timestamp: stream.streamInfo.timestamp
118
+ }))), [transcriptions, localIdentity]);
119
+ return { transcriptMessages };
120
+ }
121
+
122
+ // src/hooks/useCombinedMessages.ts
123
+ function mergeChatsIntoTranscripts(transcripts, chats) {
124
+ const merged = [];
125
+ let chatIndex = 0;
126
+ for (const transcript of transcripts) {
127
+ while (chatIndex < chats.length && (chats[chatIndex].timestamp ?? 0) <= (transcript.timestamp ?? 0)) {
128
+ merged.push(chats[chatIndex]);
129
+ chatIndex++;
130
+ }
131
+ merged.push(transcript);
132
+ }
133
+ while (chatIndex < chats.length) {
134
+ merged.push(chats[chatIndex]);
135
+ chatIndex++;
136
+ }
137
+ return merged;
138
+ }
139
+ function useCombinedMessages() {
140
+ const { transcriptMessages } = useStreamingTranscript();
141
+ const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
142
+ const { state: agentState } = useVoiceAssistant();
143
+ const allMessages = useMemo3(() => {
144
+ if (chatMessages.length === 0)
145
+ return transcriptMessages;
146
+ if (transcriptMessages.length === 0)
147
+ return chatMessages;
148
+ return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
149
+ }, [transcriptMessages, chatMessages]);
150
+ return { allMessages, agentState, sendChatMessage, isSendingChat };
151
+ }
72
152
 
73
153
  // src/hooks/useLiveAgent.ts
74
154
  import { use } from "react";
@@ -158,43 +238,6 @@ function useQuestionUpdates() {
158
238
  return { questions };
159
239
  }
160
240
 
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
241
  // src/lib/constants.ts
199
242
  var SIDEBAR_WIDTH = 600;
200
243
 
@@ -270,7 +313,7 @@ function ChatHeader({ onClose }) {
270
313
  }
271
314
 
272
315
  // src/components/MeetingControls.tsx
273
- import { useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
316
+ import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
274
317
  import { ScreenSharePresets } from "livekit-client";
275
318
  import { Mic, MicOff, Monitor, MonitorOff, PhoneOff } from "lucide-react";
276
319
  import { useCallback as useCallback4, useEffect as useEffect2, useState as useState3 } from "react";
@@ -285,7 +328,7 @@ function formatTime(seconds) {
285
328
  // src/components/MeetingControls.tsx
286
329
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
287
330
  function MeetingControls({ onHangUp }) {
288
- const { localParticipant } = useLocalParticipant2();
331
+ const { localParticipant } = useLocalParticipant3();
289
332
  const isMuted = !localParticipant.isMicrophoneEnabled;
290
333
  const isScreenSharing = localParticipant.isScreenShareEnabled;
291
334
  const [elapsed, setElapsed] = useState3(0);
@@ -362,15 +405,55 @@ function MeetingControls({ onHangUp }) {
362
405
  }
363
406
 
364
407
  // src/components/MessageList.tsx
365
- import { useCallback as useCallback5 } from "react";
408
+ import { useEffect as useEffect3, useRef } from "react";
409
+
410
+ // src/components/ChatInput.tsx
411
+ import { SendHorizontal } from "lucide-react";
412
+ import { useState as useState4 } from "react";
413
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
414
+ function ChatInput({ sendChatMessage, isSendingChat }) {
415
+ const [inputText, setInputText] = useState4("");
416
+ const canSend = inputText.trim().length > 0 && !isSendingChat;
417
+ function handleSubmit(e) {
418
+ e.preventDefault();
419
+ const text = inputText.trim();
420
+ if (!text || isSendingChat)
421
+ return;
422
+ setInputText("");
423
+ sendChatMessage(text).catch(() => setInputText(text));
424
+ }
425
+ return /* @__PURE__ */ jsxs3("form", {
426
+ onSubmit: handleSubmit,
427
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
428
+ children: [
429
+ /* @__PURE__ */ jsx4("input", {
430
+ type: "text",
431
+ value: inputText,
432
+ onChange: (e) => setInputText(e.target.value),
433
+ placeholder: "Type a message...",
434
+ 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"),
435
+ disabled: isSendingChat
436
+ }),
437
+ /* @__PURE__ */ jsx4("button", {
438
+ type: "submit",
439
+ disabled: !canSend,
440
+ "aria-label": "Send message",
441
+ 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"),
442
+ children: /* @__PURE__ */ jsx4(SendHorizontal, {
443
+ className: "skippr:size-4"
444
+ })
445
+ })
446
+ ]
447
+ });
448
+ }
366
449
 
367
450
  // src/components/ChatMessage.tsx
368
- import { jsx as jsx4 } from "react/jsx-runtime";
451
+ import { jsx as jsx5 } from "react/jsx-runtime";
369
452
  function ChatMessage({ message }) {
370
453
  const isUser = message.role === "user";
371
- return /* @__PURE__ */ jsx4("div", {
454
+ return /* @__PURE__ */ jsx5("div", {
372
455
  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", {
456
+ children: /* @__PURE__ */ jsx5("div", {
374
457
  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
458
  children: message.content
376
459
  })
@@ -378,20 +461,20 @@ function ChatMessage({ message }) {
378
461
  }
379
462
 
380
463
  // src/components/TypingIndicator.tsx
381
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
464
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
382
465
  function TypingIndicator() {
383
- return /* @__PURE__ */ jsx5("div", {
466
+ return /* @__PURE__ */ jsx6("div", {
384
467
  className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
385
- children: /* @__PURE__ */ jsxs3("div", {
468
+ children: /* @__PURE__ */ jsxs4("div", {
386
469
  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
470
  children: [
388
- /* @__PURE__ */ jsx5("span", {
471
+ /* @__PURE__ */ jsx6("span", {
389
472
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
390
473
  }),
391
- /* @__PURE__ */ jsx5("span", {
474
+ /* @__PURE__ */ jsx6("span", {
392
475
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
393
476
  }),
394
- /* @__PURE__ */ jsx5("span", {
477
+ /* @__PURE__ */ jsx6("span", {
395
478
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
396
479
  })
397
480
  ]
@@ -400,56 +483,71 @@ function TypingIndicator() {
400
483
  }
401
484
 
402
485
  // 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
- }, []);
486
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
487
+ function MessageList({
488
+ messages,
489
+ isStreaming,
490
+ sendChatMessage,
491
+ isSendingChat
492
+ }) {
493
+ const scrollRef = useRef(null);
408
494
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
495
+ useEffect3(() => {
496
+ scrollRef.current?.scrollIntoView({ behavior: "smooth" });
497
+ }, [messages.length, lastMessage?.content]);
409
498
  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
- })
499
+ return /* @__PURE__ */ jsxs5("div", {
500
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
501
+ children: [
502
+ /* @__PURE__ */ jsx7("div", {
503
+ className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
504
+ children: /* @__PURE__ */ jsxs5("div", {
505
+ className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
506
+ children: [
507
+ messages.map((message) => /* @__PURE__ */ jsx7(ChatMessage, {
508
+ message
509
+ }, message.id)),
510
+ showTyping && /* @__PURE__ */ jsx7(TypingIndicator, {}),
511
+ /* @__PURE__ */ jsx7("div", {
512
+ ref: scrollRef
513
+ })
514
+ ]
515
+ })
516
+ }),
517
+ /* @__PURE__ */ jsx7(ChatInput, {
518
+ sendChatMessage,
519
+ isSendingChat
520
+ })
521
+ ]
424
522
  });
425
523
  }
426
524
 
427
525
  // src/components/QuickActions.tsx
428
526
  import { Loader2, MessageCircleQuestion } from "lucide-react";
429
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
527
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
430
528
  function QuickActions({ onStartSession, isStarting, error }) {
431
- return /* @__PURE__ */ jsxs5("div", {
529
+ return /* @__PURE__ */ jsxs6("div", {
432
530
  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
531
  children: [
434
- /* @__PURE__ */ jsx7("p", {
532
+ /* @__PURE__ */ jsx8("p", {
435
533
  className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
436
534
  children: "How can I help you today?"
437
535
  }),
438
- /* @__PURE__ */ jsxs5(Button, {
536
+ /* @__PURE__ */ jsxs6(Button, {
439
537
  variant: "outline",
440
538
  className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
441
539
  onClick: onStartSession,
442
540
  disabled: isStarting,
443
541
  children: [
444
- isStarting ? /* @__PURE__ */ jsx7(Loader2, {
542
+ isStarting ? /* @__PURE__ */ jsx8(Loader2, {
445
543
  className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
446
- }) : /* @__PURE__ */ jsx7(MessageCircleQuestion, {
544
+ }) : /* @__PURE__ */ jsx8(MessageCircleQuestion, {
447
545
  className: "skippr:size-4 skippr:text-primary"
448
546
  }),
449
547
  isStarting ? "Starting..." : "Start Session"
450
548
  ]
451
549
  }),
452
- error && /* @__PURE__ */ jsx7("p", {
550
+ error && /* @__PURE__ */ jsx8("p", {
453
551
  className: "skippr:text-xs skippr:text-destructive",
454
552
  children: error
455
553
  })
@@ -459,52 +557,52 @@ function QuickActions({ onStartSession, isStarting, error }) {
459
557
 
460
558
  // src/components/SessionAgenda.tsx
461
559
  import { Check, Circle, Loader2 as Loader22 } from "lucide-react";
462
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
560
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
463
561
  function SessionAgenda({ phases, questions = [] }) {
464
562
  if (phases.length === 0) {
465
- return /* @__PURE__ */ jsxs6("div", {
563
+ return /* @__PURE__ */ jsxs7("div", {
466
564
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
467
565
  children: [
468
- /* @__PURE__ */ jsx8("h3", {
566
+ /* @__PURE__ */ jsx9("h3", {
469
567
  className: "skippr:text-sm skippr:font-semibold",
470
568
  children: "Agenda"
471
569
  }),
472
- /* @__PURE__ */ jsx8("p", {
570
+ /* @__PURE__ */ jsx9("p", {
473
571
  className: "skippr:text-xs skippr:text-muted-foreground",
474
572
  children: "Waiting for session..."
475
573
  })
476
574
  ]
477
575
  });
478
576
  }
479
- return /* @__PURE__ */ jsxs6("div", {
577
+ return /* @__PURE__ */ jsxs7("div", {
480
578
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
481
579
  children: [
482
- /* @__PURE__ */ jsx8("h3", {
580
+ /* @__PURE__ */ jsx9("h3", {
483
581
  className: "skippr:text-sm skippr:font-semibold",
484
582
  children: "Agenda"
485
583
  }),
486
- /* @__PURE__ */ jsx8("ul", {
584
+ /* @__PURE__ */ jsx9("ul", {
487
585
  className: "skippr:flex skippr:flex-col skippr:gap-2",
488
586
  children: phases.map((phase) => {
489
587
  const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
490
588
  const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
491
589
  const totalCount = phaseQuestions.length;
492
- return /* @__PURE__ */ jsxs6("li", {
590
+ return /* @__PURE__ */ jsxs7("li", {
493
591
  className: "skippr:flex skippr:flex-col skippr:gap-0.5",
494
592
  children: [
495
- /* @__PURE__ */ jsxs6("div", {
593
+ /* @__PURE__ */ jsxs7("div", {
496
594
  className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
497
595
  children: [
498
- /* @__PURE__ */ jsx8(PhaseIcon, {
596
+ /* @__PURE__ */ jsx9(PhaseIcon, {
499
597
  status: phase.status
500
598
  }),
501
- /* @__PURE__ */ jsx8("span", {
599
+ /* @__PURE__ */ jsx9("span", {
502
600
  className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
503
601
  children: phase.name
504
602
  })
505
603
  ]
506
604
  }),
507
- totalCount > 0 && /* @__PURE__ */ jsxs6("span", {
605
+ totalCount > 0 && /* @__PURE__ */ jsxs7("span", {
508
606
  className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
509
607
  children: [
510
608
  answeredCount,
@@ -522,56 +620,56 @@ function SessionAgenda({ phases, questions = [] }) {
522
620
  }
523
621
  function PhaseIcon({ status }) {
524
622
  if (status === "completed") {
525
- return /* @__PURE__ */ jsx8("div", {
623
+ return /* @__PURE__ */ jsx9("div", {
526
624
  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, {
625
+ children: /* @__PURE__ */ jsx9(Check, {
528
626
  className: "skippr:size-2.5 skippr:text-primary-foreground",
529
627
  strokeWidth: 3
530
628
  })
531
629
  });
532
630
  }
533
631
  if (status === "active") {
534
- return /* @__PURE__ */ jsx8(Loader22, {
632
+ return /* @__PURE__ */ jsx9(Loader22, {
535
633
  className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
536
634
  });
537
635
  }
538
- return /* @__PURE__ */ jsx8(Circle, {
636
+ return /* @__PURE__ */ jsx9(Circle, {
539
637
  className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
540
638
  });
541
639
  }
542
640
 
543
641
  // src/components/Sidebar.tsx
544
- import { jsx as jsx9, jsxs as jsxs7, Fragment } from "react/jsx-runtime";
642
+ import { jsx as jsx10, jsxs as jsxs8, Fragment } from "react/jsx-runtime";
545
643
  function Sidebar() {
546
644
  const { isConnected, isStarting, error, startSession, disconnect, isPanelOpen, closePanel } = useLiveAgent();
547
- useEffect3(() => {
645
+ useEffect4(() => {
548
646
  document.body.style.transition = "margin-right 300ms ease-in-out";
549
647
  document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
550
648
  }, [isPanelOpen]);
551
- useEffect3(() => {
649
+ useEffect4(() => {
552
650
  return () => {
553
651
  document.body.style.marginRight = "";
554
652
  document.body.style.transition = "";
555
653
  };
556
654
  }, []);
557
- return /* @__PURE__ */ jsx9("div", {
655
+ return /* @__PURE__ */ jsx10("div", {
558
656
  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
657
  style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
560
- children: isConnected ? /* @__PURE__ */ jsxs7(Fragment, {
658
+ children: isConnected ? /* @__PURE__ */ jsxs8(Fragment, {
561
659
  children: [
562
- /* @__PURE__ */ jsx9(ChatHeader, {
660
+ /* @__PURE__ */ jsx10(ChatHeader, {
563
661
  onClose: closePanel
564
662
  }),
565
- /* @__PURE__ */ jsx9(ConnectedContent, {
663
+ /* @__PURE__ */ jsx10(ConnectedContent, {
566
664
  onDisconnect: disconnect
567
665
  })
568
666
  ]
569
- }) : /* @__PURE__ */ jsxs7(Fragment, {
667
+ }) : /* @__PURE__ */ jsxs8(Fragment, {
570
668
  children: [
571
- /* @__PURE__ */ jsx9(ChatHeader, {
669
+ /* @__PURE__ */ jsx10(ChatHeader, {
572
670
  onClose: closePanel
573
671
  }),
574
- /* @__PURE__ */ jsx9(QuickActions, {
672
+ /* @__PURE__ */ jsx10(QuickActions, {
575
673
  onStartSession: startSession,
576
674
  isStarting,
577
675
  error
@@ -583,39 +681,41 @@ function Sidebar() {
583
681
  function ConnectedContent({ onDisconnect }) {
584
682
  const connectionState = useConnectionState();
585
683
  const isConnected = connectionState === ConnectionState.Connected;
586
- const { messages, agentState } = useTranscriptMessages();
684
+ const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
587
685
  const { phases } = usePhaseUpdates();
588
686
  const { questions } = useQuestionUpdates();
589
687
  if (!isConnected) {
590
- return /* @__PURE__ */ jsx9("div", {
688
+ return /* @__PURE__ */ jsx10("div", {
591
689
  className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
592
- children: /* @__PURE__ */ jsx9("p", {
690
+ children: /* @__PURE__ */ jsx10("p", {
593
691
  className: "skippr:text-sm skippr:text-muted-foreground",
594
692
  children: "Connecting..."
595
693
  })
596
694
  });
597
695
  }
598
696
  const isAgentSpeaking = agentState === "speaking";
599
- return /* @__PURE__ */ jsxs7(Fragment, {
697
+ return /* @__PURE__ */ jsxs8(Fragment, {
600
698
  children: [
601
- /* @__PURE__ */ jsx9(MeetingControls, {
699
+ /* @__PURE__ */ jsx10(MeetingControls, {
602
700
  onHangUp: onDisconnect
603
701
  }),
604
- /* @__PURE__ */ jsxs7("div", {
702
+ /* @__PURE__ */ jsxs8("div", {
605
703
  className: "skippr:flex skippr:min-h-0 skippr:flex-1",
606
704
  children: [
607
- /* @__PURE__ */ jsx9("div", {
705
+ /* @__PURE__ */ jsx10("div", {
608
706
  className: "skippr:w-[260px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
609
- children: /* @__PURE__ */ jsx9(SessionAgenda, {
707
+ children: /* @__PURE__ */ jsx10(SessionAgenda, {
610
708
  phases,
611
709
  questions
612
710
  })
613
711
  }),
614
- /* @__PURE__ */ jsx9("div", {
712
+ /* @__PURE__ */ jsx10("div", {
615
713
  className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
616
- children: /* @__PURE__ */ jsx9(MessageList, {
617
- messages,
618
- isStreaming: isAgentSpeaking
714
+ children: /* @__PURE__ */ jsx10(MessageList, {
715
+ messages: allMessages,
716
+ isStreaming: isAgentSpeaking,
717
+ sendChatMessage,
718
+ isSendingChat
619
719
  })
620
720
  })
621
721
  ]
@@ -626,27 +726,27 @@ function ConnectedContent({ onDisconnect }) {
626
726
 
627
727
  // src/components/SidebarTrigger.tsx
628
728
  import { MessageCircle, X as X2 } from "lucide-react";
629
- import { jsx as jsx10 } from "react/jsx-runtime";
729
+ import { jsx as jsx11 } from "react/jsx-runtime";
630
730
  var TRIGGER_GAP = 16;
631
731
  var TRIGGER_DEFAULT_RIGHT = 24;
632
732
  function SidebarTrigger() {
633
733
  const { isPanelOpen, togglePanel } = useLiveAgent();
634
- return /* @__PURE__ */ jsx10(Button, {
734
+ return /* @__PURE__ */ jsx11(Button, {
635
735
  size: "icon-lg",
636
736
  onClick: togglePanel,
637
737
  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
738
  style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
639
739
  title: isPanelOpen ? "Close chat" : "Chat with us",
640
- children: isPanelOpen ? /* @__PURE__ */ jsx10(X2, {
740
+ children: isPanelOpen ? /* @__PURE__ */ jsx11(X2, {
641
741
  className: "skippr:size-6"
642
- }) : /* @__PURE__ */ jsx10(MessageCircle, {
742
+ }) : /* @__PURE__ */ jsx11(MessageCircle, {
643
743
  className: "skippr:size-6"
644
744
  })
645
745
  });
646
746
  }
647
747
 
648
748
  // src/components/LiveAgent.tsx
649
- import { jsx as jsx11, jsxs as jsxs8, Fragment as Fragment2 } from "react/jsx-runtime";
749
+ import { jsx as jsx12, jsxs as jsxs9, Fragment as Fragment2 } from "react/jsx-runtime";
650
750
  function LiveAgent({
651
751
  organizationId,
652
752
  agentId,
@@ -657,12 +757,12 @@ function LiveAgent({
657
757
  organizationId,
658
758
  agentId
659
759
  });
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), []);
760
+ const [isPanelOpen, setIsPanelOpen] = useState5(defaultOpen);
761
+ const openPanel = useCallback5(() => setIsPanelOpen(true), []);
762
+ const closePanel = useCallback5(() => setIsPanelOpen(false), []);
763
+ const togglePanel = useCallback5(() => setIsPanelOpen((prev) => !prev), []);
664
764
  const isConnected = connection !== null;
665
- const ctx = useMemo2(() => ({
765
+ const ctx = useMemo4(() => ({
666
766
  connection,
667
767
  shouldConnect,
668
768
  isConnected,
@@ -687,17 +787,17 @@ function LiveAgent({
687
787
  closePanel,
688
788
  togglePanel
689
789
  ]);
690
- const widgetContent = /* @__PURE__ */ jsxs8(Fragment2, {
790
+ const widgetContent = /* @__PURE__ */ jsxs9(Fragment2, {
691
791
  children: [
692
- connection && /* @__PURE__ */ jsx11(RoomAudioRenderer, {}),
693
- /* @__PURE__ */ jsx11(SidebarTrigger, {}),
694
- /* @__PURE__ */ jsx11(Sidebar, {}),
792
+ connection && /* @__PURE__ */ jsx12(RoomAudioRenderer, {}),
793
+ /* @__PURE__ */ jsx12(SidebarTrigger, {}),
794
+ /* @__PURE__ */ jsx12(Sidebar, {}),
695
795
  children
696
796
  ]
697
797
  });
698
- return /* @__PURE__ */ jsx11(LiveAgentContext.Provider, {
798
+ return /* @__PURE__ */ jsx12(LiveAgentContext.Provider, {
699
799
  value: ctx,
700
- children: connection ? /* @__PURE__ */ jsx11(LiveKitRoom, {
800
+ children: connection ? /* @__PURE__ */ jsx12(LiveKitRoom, {
701
801
  serverUrl: connection.livekitUrl,
702
802
  token: connection.token,
703
803
  connect: shouldConnect,