@skippr/live-agent-sdk 0.124.0 → 0.125.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.
@@ -5508,7 +5508,7 @@ function usePlanUpdates() {
5508
5508
  // src/hooks/useSession.ts
5509
5509
  import { useCallback as useCallback15, useEffect as useEffect20, useRef as useRef15, useState as useState17 } from "react";
5510
5510
  var API_URL4 = "https://app.skippr.ai/api";
5511
- var SDK_VERSION = "0.124.0";
5511
+ var SDK_VERSION = "0.125.0";
5512
5512
  async function fetchSessionMessages(sessionId, authedFetch) {
5513
5513
  try {
5514
5514
  const resp = await authedFetch(`${API_URL4}/v1/sessions/${sessionId}/messages`);
@@ -6054,6 +6054,21 @@ function useSplashOverlayOptOut(authedFetch, recordedForUser) {
6054
6054
  return { optedOut: recordedForUser || recordedLocally, optOut };
6055
6055
  }
6056
6056
 
6057
+ // src/lib/hostStart.ts
6058
+ var start = null;
6059
+ var requested = false;
6060
+ var live = false;
6061
+ function setHostSessionLive(isLive) {
6062
+ live = isLive;
6063
+ }
6064
+ function provideStartSession(fn) {
6065
+ start = fn;
6066
+ if (!fn || !requested)
6067
+ return;
6068
+ requested = false;
6069
+ fn();
6070
+ }
6071
+
6057
6072
  // src/lib/launcher.ts
6058
6073
  var DEFAULT_LAUNCHER_BOTTOM_PX = 48;
6059
6074
  var LAUNCHER_HEIGHT_PX = 50;
@@ -6586,8 +6601,8 @@ function AgentCursor({
6586
6601
  }
6587
6602
  setPhase("travel");
6588
6603
  const from = prevCursorRef.current ?? widgetEntryPoint(ctx?.position ?? "right", ctx?.launcherBottomPx ?? 0);
6589
- const start = ringTopLeft(rect);
6590
- startGlide(from, start, glideDuration(from, start), () => {
6604
+ const start2 = ringTopLeft(rect);
6605
+ startGlide(from, start2, glideDuration(from, start2), () => {
6591
6606
  after(DRAW_START_DELAY_MS, () => {
6592
6607
  const path = pathRef.current;
6593
6608
  if (!path) {
@@ -6596,9 +6611,9 @@ function AgentCursor({
6596
6611
  }
6597
6612
  setPhase("drawing");
6598
6613
  traceRef.current = traceRing(path, DRAW_MS, (eased) => {
6599
- const live = liveRectOf(element, rect);
6614
+ const live2 = liveRectOf(element, rect);
6600
6615
  const point = path.getPointAtLength(path.getTotalLength() * eased);
6601
- moveCursorTo({ x: live.left - RING_PAD + point.x, y: live.top - RING_PAD + point.y });
6616
+ moveCursorTo({ x: live2.left - RING_PAD + point.x, y: live2.top - RING_PAD + point.y });
6602
6617
  }, () => rest(liveRectOf(element, rect), SETTLE_TO_CENTER_MS, ACT_CLICK_DELAY_MS));
6603
6618
  });
6604
6619
  });
@@ -8001,7 +8016,11 @@ async function unpublishAndStopTrack(localParticipant, videoTrack) {
8001
8016
  } catch {}
8002
8017
  videoTrack.stop();
8003
8018
  }
8004
- function DomCapture({ pushOrTapMicMode = false, startMuted = false }) {
8019
+ function DomCapture({
8020
+ pushOrTapMicMode = false,
8021
+ startMuted = false,
8022
+ suppressVideoPublish = false
8023
+ }) {
8005
8024
  const { localParticipant } = useLocalParticipant5();
8006
8025
  const connectionState = useConnectionState2();
8007
8026
  const { isHeld, clearPausedCaptureRef } = useSessionHoldContext();
@@ -8045,18 +8064,20 @@ function DomCapture({ pushOrTapMicMode = false, startMuted = false }) {
8045
8064
  if (!pushOrTapMicMode && !startMuted && !localParticipant.isMicrophoneEnabled) {
8046
8065
  localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
8047
8066
  }
8048
- localParticipant.publishTrack(videoTrack, {
8049
- source: Track3.Source.ScreenShare,
8050
- videoEncoding: {
8051
- maxBitrate: CAPTURE_BITRATE,
8052
- maxFramerate: CAPTURE_FPS
8053
- },
8054
- simulcast: false
8055
- }).catch((error) => {
8056
- console.error("Failed to publish DOM capture track:", error);
8057
- for (const track of canvasStream.getTracks())
8058
- track.stop();
8059
- });
8067
+ if (!suppressVideoPublish) {
8068
+ localParticipant.publishTrack(videoTrack, {
8069
+ source: Track3.Source.ScreenShare,
8070
+ videoEncoding: {
8071
+ maxBitrate: CAPTURE_BITRATE,
8072
+ maxFramerate: CAPTURE_FPS
8073
+ },
8074
+ simulcast: false
8075
+ }).catch((error) => {
8076
+ console.error("Failed to publish DOM capture track:", error);
8077
+ for (const track of canvasStream.getTracks())
8078
+ track.stop();
8079
+ });
8080
+ }
8060
8081
  const commitPageLayerIfNewest = (sequence, page) => {
8061
8082
  if (cancelled || pausedRef.current || sequence <= lastCommittedSequence)
8062
8083
  return false;
@@ -8068,13 +8089,18 @@ function DomCapture({ pushOrTapMicMode = false, startMuted = false }) {
8068
8089
  };
8069
8090
  const rasterizeAndCommitWaitingOutTimeout = async (options) => {
8070
8091
  const sequence = ++rasterizeSequence;
8092
+ const startedAt = performance.now();
8071
8093
  const captureInProgress = rasterizeViewportToPageLayer(canvas.width, canvas.height, options);
8072
8094
  const captureOrTimeout = await Promise.race([
8073
8095
  captureInProgress,
8074
8096
  new Promise((resolve) => setTimeout(() => resolve(RASTERIZE_TIMED_OUT), RASTERIZE_TIMEOUT_MS))
8075
8097
  ]);
8076
8098
  if (captureOrTimeout === RASTERIZE_TIMED_OUT) {
8077
- captureInProgress.then((lateCapture) => commitPageLayerIfNewest(sequence, lateCapture)).catch(() => {
8099
+ console.warn(`[Skippr] Rasterize exceeded ${RASTERIZE_TIMEOUT_MS}ms; falling back to fast capture`);
8100
+ captureInProgress.then((lateCapture) => {
8101
+ console.warn(`[Skippr] Late rasterize finished in ${Math.round(performance.now() - startedAt)}ms`);
8102
+ return commitPageLayerIfNewest(sequence, lateCapture);
8103
+ }).catch(() => {
8078
8104
  return;
8079
8105
  });
8080
8106
  return { timedOut: true };
@@ -8084,6 +8110,8 @@ function DomCapture({ pushOrTapMicMode = false, startMuted = false }) {
8084
8110
  };
8085
8111
  let followUpRefreshQueued = false;
8086
8112
  const refreshPageAndComposite = async ({ queueWhenBusy = false } = {}) => {
8113
+ if (suppressVideoPublish)
8114
+ return;
8087
8115
  if (cancelled || snapshotInFlight || pausedRef.current) {
8088
8116
  if (snapshotInFlight && !cancelled && queueWhenBusy)
8089
8117
  followUpRefreshQueued = true;
@@ -8245,11 +8273,19 @@ function DomCapture({ pushOrTapMicMode = false, startMuted = false }) {
8245
8273
  cleanupCursorSubscription();
8246
8274
  cleanupDomEventListeners();
8247
8275
  cleanupSnapshotRequest();
8248
- unpublishAndStopTrack(localParticipant, videoTrack);
8276
+ if (!suppressVideoPublish)
8277
+ unpublishAndStopTrack(localParticipant, videoTrack);
8249
8278
  for (const track of canvasStream.getTracks())
8250
8279
  track.stop();
8251
8280
  };
8252
- }, [connectionState, localParticipant, pushOrTapMicMode, startMuted, clearPausedCaptureRef]);
8281
+ }, [
8282
+ connectionState,
8283
+ localParticipant,
8284
+ pushOrTapMicMode,
8285
+ startMuted,
8286
+ suppressVideoPublish,
8287
+ clearPausedCaptureRef
8288
+ ]);
8253
8289
  useEffect25(() => {
8254
8290
  if (isHeld) {
8255
8291
  freezeCaptureRef.current?.();
@@ -8263,6 +8299,14 @@ function DomCapture({ pushOrTapMicMode = false, startMuted = false }) {
8263
8299
  // src/components/HighlightOverlay.tsx
8264
8300
  import { useDataChannel, useLocalParticipant as useLocalParticipant6 } from "@livekit/components-react/hooks";
8265
8301
  import { useCallback as useCallback18, useEffect as useEffect26, useRef as useRef20 } from "react";
8302
+
8303
+ // src/lib/agentSender.ts
8304
+ import { ParticipantKind } from "livekit-client";
8305
+ function isFromAgent(from) {
8306
+ return from?.kind === ParticipantKind.AGENT;
8307
+ }
8308
+
8309
+ // src/components/HighlightOverlay.tsx
8266
8310
  var HIGHLIGHT_AUTO_CLEAR_MS = 30000;
8267
8311
  var PAGE_CHECK_MS = 250;
8268
8312
  var ACTED_REASON = "acted";
@@ -8315,6 +8359,8 @@ function HighlightOverlay() {
8315
8359
  }).catch((error) => console.error("Failed to report highlight clear:", error));
8316
8360
  }, [clear, localParticipant]);
8317
8361
  const onHighlightMessage = useCallback18((msg) => {
8362
+ if (!isFromAgent(msg.from))
8363
+ return;
8318
8364
  const parsed = parseHighlightMessage(msg.payload);
8319
8365
  if (!parsed)
8320
8366
  return;
@@ -9314,6 +9360,8 @@ function PageActionHandler() {
9314
9360
  }
9315
9361
  }, [runStep]);
9316
9362
  const onMessage = useCallback19((msg) => {
9363
+ if (!isFromAgent(msg.from))
9364
+ return;
9317
9365
  const action = parsePageAction(msg.payload);
9318
9366
  if (!action)
9319
9367
  return;
@@ -9833,7 +9881,7 @@ function SessionControlBar() {
9833
9881
  const pushOrTapMicMode = isRequestResponseMicMode(micMode);
9834
9882
  const isPushToTalk = micMode === MIC_MODE.PushToTalk;
9835
9883
  const agentActive = state === "speaking" || state === "thinking";
9836
- const { isCapturingSpeech, toggle: toggleTapToTalk, start, commit } = useVoiceTurnContext();
9884
+ const { isCapturingSpeech, toggle: toggleTapToTalk, start: start2, commit } = useVoiceTurnContext();
9837
9885
  const liveUserTranscript = useLiveUserTranscript(isCapturingSpeech);
9838
9886
  const { reportActivity } = useIdleAutoPause({
9839
9887
  enabled: pushOrTapMicMode,
@@ -9846,9 +9894,9 @@ function SessionControlBar() {
9846
9894
  });
9847
9895
  const [pushToTalkHintSeen, setPushToTalkHintSeen] = useState25(false);
9848
9896
  const startPushToTalk = useCallback22(() => {
9849
- start();
9897
+ start2();
9850
9898
  reportActivity();
9851
- }, [start, reportActivity]);
9899
+ }, [start2, reportActivity]);
9852
9900
  const commitPushToTalk = useCallback22(() => {
9853
9901
  commit();
9854
9902
  reportActivity();
@@ -11701,7 +11749,7 @@ function useSplash(surface, onDismiss) {
11701
11749
  dismissSplash(surface);
11702
11750
  onDismiss?.();
11703
11751
  }, [commitOptOut, releaseScreen, dismissSplash, surface, onDismiss]);
11704
- const start = useCallback29(() => {
11752
+ const start2 = useCallback29(() => {
11705
11753
  commitOptOut();
11706
11754
  startPrimarySession();
11707
11755
  }, [commitOptOut, startPrimarySession]);
@@ -11747,7 +11795,7 @@ function useSplash(surface, onDismiss) {
11747
11795
  toggleStartMuted,
11748
11796
  enableScreen,
11749
11797
  startTip,
11750
- start,
11798
+ start: start2,
11751
11799
  dismiss,
11752
11800
  optOutChecked,
11753
11801
  toggleOptOut
@@ -11772,7 +11820,7 @@ function useSplash(surface, onDismiss) {
11772
11820
  enableMic,
11773
11821
  toggleStartMuted,
11774
11822
  enableScreen,
11775
- start,
11823
+ start: start2,
11776
11824
  dismiss,
11777
11825
  optOutChecked,
11778
11826
  toggleOptOut
@@ -12055,7 +12103,7 @@ function SplashAlwaysOn({
12055
12103
  toggleStartMuted,
12056
12104
  enableScreen,
12057
12105
  startTip,
12058
- start,
12106
+ start: start2,
12059
12107
  dismiss,
12060
12108
  optOutChecked,
12061
12109
  toggleOptOut,
@@ -12145,7 +12193,7 @@ function SplashAlwaysOn({
12145
12193
  }, tip.goalId)) : /* @__PURE__ */ jsx44(TipRow, {
12146
12194
  title: copy.tipsEmptyTitle,
12147
12195
  subtext: copy.tipsEmptyDescription,
12148
- onStart: start
12196
+ onStart: start2
12149
12197
  })
12150
12198
  })
12151
12199
  ]
@@ -12167,7 +12215,7 @@ function SplashAlwaysOn({
12167
12215
  /* @__PURE__ */ jsxs35("button", {
12168
12216
  ref: ctaRef,
12169
12217
  type: "button",
12170
- onClick: start,
12218
+ onClick: start2,
12171
12219
  className: `${FOCUS_RING3} skippr:flex skippr:w-full skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:gap-2 skippr:rounded-full skippr:bg-primary skippr:px-5 skippr:py-[13px] skippr:text-[16px] skippr:font-semibold skippr:text-primary-foreground skippr:transition-all skippr:hover:bg-primary/90`,
12172
12220
  children: [
12173
12221
  headerIconUrl ? /* @__PURE__ */ jsx44("img", {
@@ -12256,7 +12304,7 @@ function SplashPrimary({
12256
12304
  enableMic,
12257
12305
  toggleStartMuted,
12258
12306
  enableScreen,
12259
- start,
12307
+ start: start2,
12260
12308
  dismiss,
12261
12309
  optOutChecked,
12262
12310
  toggleOptOut,
@@ -12434,7 +12482,7 @@ function SplashPrimary({
12434
12482
  /* @__PURE__ */ jsxs36("button", {
12435
12483
  ref: ctaRef,
12436
12484
  type: "button",
12437
- onClick: start,
12485
+ onClick: start2,
12438
12486
  className: `${FOCUS_RING3} skippr:flex skippr:w-full skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:gap-2 skippr:rounded-full skippr:bg-primary skippr:px-5 skippr:py-[13px] skippr:text-[16px] skippr:font-semibold skippr:text-primary-foreground skippr:transition-all skippr:hover:bg-primary/90`,
12439
12487
  children: [
12440
12488
  headerIconUrl ? /* @__PURE__ */ jsx45("img", {
@@ -13597,8 +13645,8 @@ function VoiceBarFrame({
13597
13645
  const { localParticipant } = useLocalParticipant13();
13598
13646
  const isUserSpeaking = useIsSpeaking(localParticipant);
13599
13647
  const { state } = useAgentVoiceState();
13600
- const { isCapturingSpeech, toggle: toggleTapToTalk, start, commit } = useVoiceTurnContext();
13601
- const holdPushToTalk = usePointerHold(start, commit);
13648
+ const { isCapturingSpeech, toggle: toggleTapToTalk, start: start2, commit } = useVoiceTurnContext();
13649
+ const holdPushToTalk = usePointerHold(start2, commit);
13602
13650
  const { isHeld, toggle: togglePause } = useSessionHoldContext();
13603
13651
  const plan = usePlanUpdates();
13604
13652
  const linkCard = useLinkCard();
@@ -13643,7 +13691,7 @@ function VoiceBarFrame({
13643
13691
  }, [chatLineOpen, chatLineMounted]);
13644
13692
  useKeyboardPushToTalk({
13645
13693
  enabled: isConnected && isPushToTalk,
13646
- onStart: start,
13694
+ onStart: start2,
13647
13695
  onCommit: commit,
13648
13696
  code: "Space",
13649
13697
  requireCtrl: true
@@ -14668,6 +14716,7 @@ function LiveAgent(props) {
14668
14716
  startSessionLabel = "Talk to Skippr",
14669
14717
  autoFocusChat = true,
14670
14718
  animateAgentCursor = false,
14719
+ suppressVideoPublish = false,
14671
14720
  children
14672
14721
  } = props;
14673
14722
  const captureMode = props.captureMode ?? DEFAULT_CAPTURE_MODE;
@@ -15063,6 +15112,15 @@ function LiveAgent(props) {
15063
15112
  selectModule,
15064
15113
  startAdoptionSession
15065
15114
  });
15115
+ useEffect51(() => {
15116
+ if (bearerToken === null || !hasResolvedModules || !defaultModule)
15117
+ return;
15118
+ provideStartSession(startDefaultSession);
15119
+ return () => provideStartSession(null);
15120
+ }, [bearerToken, hasResolvedModules, defaultModule, startDefaultSession]);
15121
+ useEffect51(() => {
15122
+ setHostSessionLive(isSessionLive);
15123
+ }, [isSessionLive]);
15066
15124
  useEffect51(() => {
15067
15125
  if (!autoStartAfterLogin)
15068
15126
  return;
@@ -15380,7 +15438,8 @@ function LiveAgent(props) {
15380
15438
  }),
15381
15439
  connection && captureMode === CAPTURE_MODE.Auto && /* @__PURE__ */ jsx56(DomCapture, {
15382
15440
  pushOrTapMicMode: isRequestResponseMicMode(agentControls?.micMode),
15383
- startMuted
15441
+ startMuted,
15442
+ suppressVideoPublish
15384
15443
  }),
15385
15444
  /* @__PURE__ */ jsxs44(ShadowHost, {
15386
15445
  accentColor: appearance?.accentColor ?? null,