@modelnex/sdk 0.5.17 → 0.5.19

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.
package/dist/index.js CHANGED
@@ -2718,6 +2718,51 @@ function createTourSocketPool({
2718
2718
  }
2719
2719
  var tourSocketPool = createTourSocketPool();
2720
2720
 
2721
+ // src/utils/tour-playback-guards.ts
2722
+ var playbackOwners = /* @__PURE__ */ new Map();
2723
+ function shouldExecuteTourCommandBatch(isPlaybackActive) {
2724
+ return isPlaybackActive;
2725
+ }
2726
+ function shouldAcceptTourStart(state) {
2727
+ return !state.isPlaybackActive && !state.startRequested;
2728
+ }
2729
+ function shouldRunTourAutoDiscovery(state) {
2730
+ return state.enableAutoDiscovery && !state.disabled && !state.isPlaybackActive && !state.startRequested && !state.hasPendingTour;
2731
+ }
2732
+ function shouldLogTourDebugEntry(state) {
2733
+ if (!state.isPlaybackActive) {
2734
+ if (state.entryType !== "tour_start") {
2735
+ return false;
2736
+ }
2737
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2738
+ return false;
2739
+ }
2740
+ }
2741
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2742
+ return false;
2743
+ }
2744
+ return true;
2745
+ }
2746
+ function createTourPlaybackOwnerKey(serverUrl, websiteId, experienceType) {
2747
+ return `${serverUrl}::${websiteId || "__default__"}::${experienceType}`;
2748
+ }
2749
+ function claimTourPlaybackOwnership(key, ownerId) {
2750
+ const currentOwner = playbackOwners.get(key);
2751
+ if (currentOwner && currentOwner !== ownerId) {
2752
+ return false;
2753
+ }
2754
+ playbackOwners.set(key, ownerId);
2755
+ return true;
2756
+ }
2757
+ function hasTourPlaybackOwnership(key, ownerId) {
2758
+ return playbackOwners.get(key) === ownerId;
2759
+ }
2760
+ function releaseTourPlaybackOwnership(key, ownerId) {
2761
+ if (playbackOwners.get(key) === ownerId) {
2762
+ playbackOwners.delete(key);
2763
+ }
2764
+ }
2765
+
2721
2766
  // src/hooks/useTourPlayback.ts
2722
2767
  function resolveElement(step) {
2723
2768
  const el = step.element;
@@ -3251,6 +3296,9 @@ function useTourPlayback({
3251
3296
  const showCaptionsRef = (0, import_react12.useRef)(showCaptions);
3252
3297
  const runIdRef = (0, import_react12.useRef)(null);
3253
3298
  const turnIdRef = (0, import_react12.useRef)(null);
3299
+ const startRequestedRef = (0, import_react12.useRef)(false);
3300
+ const playbackOwnerIdRef = (0, import_react12.useRef)(`playback-owner-${Math.random().toString(36).slice(2, 10)}`);
3301
+ const claimedPlaybackOwnerKeyRef = (0, import_react12.useRef)(null);
3254
3302
  const socketRef = (0, import_react12.useRef)(null);
3255
3303
  const socketIdRef = (0, import_react12.useRef)(socketId);
3256
3304
  const commandUrlRef = (0, import_react12.useRef)(commandUrl);
@@ -3268,6 +3316,12 @@ function useTourPlayback({
3268
3316
  toursApiBaseRef.current = toursApiBase;
3269
3317
  pendingTourRef.current = pendingTour;
3270
3318
  showCaptionsRef.current = showCaptions;
3319
+ const releasePlaybackOwnership = (0, import_react12.useCallback)(() => {
3320
+ const claimedKey = claimedPlaybackOwnerKeyRef.current;
3321
+ if (!claimedKey) return;
3322
+ releaseTourPlaybackOwnership(claimedKey, playbackOwnerIdRef.current);
3323
+ claimedPlaybackOwnerKeyRef.current = null;
3324
+ }, []);
3271
3325
  (0, import_react12.useEffect)(() => {
3272
3326
  if (disabled) return;
3273
3327
  if (typeof window === "undefined") return;
@@ -3303,6 +3357,12 @@ function useTourPlayback({
3303
3357
  }
3304
3358
  };
3305
3359
  const handleCommand = async (payload) => {
3360
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
3361
+ if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3362
+ const activeType = experienceTypeRef.current;
3363
+ console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
3364
+ return;
3365
+ }
3306
3366
  const emitIfOpen = (ev, data) => {
3307
3367
  if (socketRef.current !== socket) return;
3308
3368
  emitSocketEvent(socket, ev, data);
@@ -3808,7 +3868,14 @@ function useTourPlayback({
3808
3868
  console.log(`[TourClient] Ignoring ${tour.type} start (this hook is for ${expType})`);
3809
3869
  return;
3810
3870
  }
3871
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
3872
+ if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3873
+ console.log(`[TourClient] Ignoring ${expType} start because another hook already owns playback`);
3874
+ return;
3875
+ }
3876
+ claimedPlaybackOwnerKeyRef.current = ownerKey;
3811
3877
  skipRequestedRef.current = false;
3878
+ startRequestedRef.current = false;
3812
3879
  const total = tourData.totalSteps ?? tour?.steps?.length ?? 0;
3813
3880
  isActiveRef.current = true;
3814
3881
  setIsActive(true);
@@ -3867,6 +3934,16 @@ function useTourPlayback({
3867
3934
  };
3868
3935
  const handleDebugLog = (entry) => {
3869
3936
  const isDev = devModeRef.current || process.env.NODE_ENV === "development" || typeof window !== "undefined" && window.MODELNEX_DEBUG;
3937
+ const entryTourType = entry?.data?.tourContext?.type ?? tourRef.current?.type ?? null;
3938
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
3939
+ if (!shouldLogTourDebugEntry({
3940
+ isPlaybackActive: isActiveRef.current,
3941
+ entryType: entry?.type,
3942
+ entryTourType,
3943
+ experienceType: experienceTypeRef.current
3944
+ }) || isActiveRef.current && !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3945
+ return;
3946
+ }
3870
3947
  if (isDev) {
3871
3948
  console.log(`%c[ModelNex Tour] ${entry.type}`, "color: #3b82f6; font-weight: bold", entry);
3872
3949
  if (typeof window !== "undefined") {
@@ -3899,9 +3976,10 @@ function useTourPlayback({
3899
3976
  setServerState(null);
3900
3977
  runIdRef.current = null;
3901
3978
  turnIdRef.current = null;
3979
+ releasePlaybackOwnership();
3902
3980
  tourSocketPool.release(serverUrl, toClose);
3903
3981
  };
3904
- }, [serverUrl, disabled]);
3982
+ }, [serverUrl, disabled, releasePlaybackOwnership]);
3905
3983
  (0, import_react12.useEffect)(() => {
3906
3984
  if (disabled) return;
3907
3985
  const s = socketRef.current;
@@ -3972,6 +4050,8 @@ function useTourPlayback({
3972
4050
  const stopTour = (0, import_react12.useCallback)(() => {
3973
4051
  skipRequestedRef.current = true;
3974
4052
  isActiveRef.current = false;
4053
+ startRequestedRef.current = false;
4054
+ releasePlaybackOwnership();
3975
4055
  activeExecutionTokenRef.current += 1;
3976
4056
  commandInFlightRef.current = false;
3977
4057
  activeCommandBatchIdRef.current = null;
@@ -4007,12 +4087,14 @@ function useTourPlayback({
4007
4087
  setIsReviewMode(false);
4008
4088
  pendingInputBufRef.current = null;
4009
4089
  onTourEnd?.();
4010
- }, [voice, onTourEnd, serverUrl, websiteId]);
4090
+ }, [voice, onTourEnd, serverUrl, websiteId, releasePlaybackOwnership]);
4011
4091
  const handleTourEnd = (0, import_react12.useCallback)(() => {
4012
4092
  const endingTourId = tourRef.current?.id;
4013
4093
  const endingPreviewRunId = previewRunIdRef.current;
4014
4094
  const endingStepOrder = stepIndexRef.current;
4015
4095
  isActiveRef.current = false;
4096
+ startRequestedRef.current = false;
4097
+ releasePlaybackOwnership();
4016
4098
  setPlaybackState("complete");
4017
4099
  removeHighlight();
4018
4100
  removeCaption();
@@ -4044,8 +4126,15 @@ function useTourPlayback({
4044
4126
  clearActiveDraftPreview(experienceType);
4045
4127
  }
4046
4128
  onTourEnd?.();
4047
- }, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId]);
4129
+ }, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId, releasePlaybackOwnership]);
4048
4130
  const runTour = (0, import_react12.useCallback)(async (tour, options) => {
4131
+ if (!shouldAcceptTourStart({
4132
+ isPlaybackActive: isActiveRef.current,
4133
+ startRequested: startRequestedRef.current
4134
+ })) {
4135
+ console.log("[TourClient] Ignoring duplicate start request while playback is already active or starting:", tour.id);
4136
+ return;
4137
+ }
4049
4138
  setPendingTour(null);
4050
4139
  pendingTourRef.current = null;
4051
4140
  let retries = 0;
@@ -4057,6 +4146,13 @@ function useTourPlayback({
4057
4146
  console.warn("[TourClient] Cannot run tour, socket not connected.");
4058
4147
  return;
4059
4148
  }
4149
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
4150
+ if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
4151
+ console.log("[TourClient] Ignoring duplicate start request because another hook already owns this experience:", tour.id);
4152
+ return;
4153
+ }
4154
+ claimedPlaybackOwnerKeyRef.current = ownerKey;
4155
+ startRequestedRef.current = true;
4060
4156
  const shouldReview = Boolean(options?.reviewMode);
4061
4157
  resetCaptionSuppression();
4062
4158
  setReviewStatusMessage(null);
@@ -4088,8 +4184,13 @@ function useTourPlayback({
4088
4184
  });
4089
4185
  }, [serverUrl, websiteId]);
4090
4186
  (0, import_react12.useEffect)(() => {
4091
- if (!enableAutoDiscovery) return;
4092
- if (disabled) return;
4187
+ if (!shouldRunTourAutoDiscovery({
4188
+ enableAutoDiscovery,
4189
+ disabled,
4190
+ isPlaybackActive: isActiveRef.current,
4191
+ startRequested: startRequestedRef.current,
4192
+ hasPendingTour: Boolean(pendingTourRef.current)
4193
+ })) return;
4093
4194
  if (typeof window === "undefined") return;
4094
4195
  const params = new URLSearchParams(window.location.search);
4095
4196
  const queryParam = experienceType === "onboarding" ? "modelnex_test_workflow" : "modelnex_test_tour";
@@ -4137,8 +4238,13 @@ function useTourPlayback({
4137
4238
  };
4138
4239
  }, [serverUrl, toursApiBase, disabled, websiteId, experienceType, enableAutoDiscovery]);
4139
4240
  (0, import_react12.useEffect)(() => {
4140
- if (!enableAutoDiscovery) return;
4141
- if (disabled) return;
4241
+ if (!shouldRunTourAutoDiscovery({
4242
+ enableAutoDiscovery,
4243
+ disabled,
4244
+ isPlaybackActive: isActiveRef.current,
4245
+ startRequested: startRequestedRef.current,
4246
+ hasPendingTour: Boolean(pendingTourRef.current)
4247
+ })) return;
4142
4248
  if (!websiteId || !userProfile) return;
4143
4249
  if (typeof window !== "undefined") {
4144
4250
  const params = new URLSearchParams(window.location.search);
@@ -4177,7 +4283,7 @@ function useTourPlayback({
4177
4283
  cancelled = true;
4178
4284
  clearTimeout(timer);
4179
4285
  };
4180
- }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile, enableAutoDiscovery]);
4286
+ }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, enableAutoDiscovery]);
4181
4287
  (0, import_react12.useEffect)(() => {
4182
4288
  if (!disabled || !isActiveRef.current) return;
4183
4289
  stopTour();
package/dist/index.mjs CHANGED
@@ -2508,6 +2508,51 @@ function createTourSocketPool({
2508
2508
  }
2509
2509
  var tourSocketPool = createTourSocketPool();
2510
2510
 
2511
+ // src/utils/tour-playback-guards.ts
2512
+ var playbackOwners = /* @__PURE__ */ new Map();
2513
+ function shouldExecuteTourCommandBatch(isPlaybackActive) {
2514
+ return isPlaybackActive;
2515
+ }
2516
+ function shouldAcceptTourStart(state) {
2517
+ return !state.isPlaybackActive && !state.startRequested;
2518
+ }
2519
+ function shouldRunTourAutoDiscovery(state) {
2520
+ return state.enableAutoDiscovery && !state.disabled && !state.isPlaybackActive && !state.startRequested && !state.hasPendingTour;
2521
+ }
2522
+ function shouldLogTourDebugEntry(state) {
2523
+ if (!state.isPlaybackActive) {
2524
+ if (state.entryType !== "tour_start") {
2525
+ return false;
2526
+ }
2527
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2528
+ return false;
2529
+ }
2530
+ }
2531
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2532
+ return false;
2533
+ }
2534
+ return true;
2535
+ }
2536
+ function createTourPlaybackOwnerKey(serverUrl, websiteId, experienceType) {
2537
+ return `${serverUrl}::${websiteId || "__default__"}::${experienceType}`;
2538
+ }
2539
+ function claimTourPlaybackOwnership(key, ownerId) {
2540
+ const currentOwner = playbackOwners.get(key);
2541
+ if (currentOwner && currentOwner !== ownerId) {
2542
+ return false;
2543
+ }
2544
+ playbackOwners.set(key, ownerId);
2545
+ return true;
2546
+ }
2547
+ function hasTourPlaybackOwnership(key, ownerId) {
2548
+ return playbackOwners.get(key) === ownerId;
2549
+ }
2550
+ function releaseTourPlaybackOwnership(key, ownerId) {
2551
+ if (playbackOwners.get(key) === ownerId) {
2552
+ playbackOwners.delete(key);
2553
+ }
2554
+ }
2555
+
2511
2556
  // src/hooks/useTourPlayback.ts
2512
2557
  function resolveElement(step) {
2513
2558
  const el = step.element;
@@ -3041,6 +3086,9 @@ function useTourPlayback({
3041
3086
  const showCaptionsRef = useRef8(showCaptions);
3042
3087
  const runIdRef = useRef8(null);
3043
3088
  const turnIdRef = useRef8(null);
3089
+ const startRequestedRef = useRef8(false);
3090
+ const playbackOwnerIdRef = useRef8(`playback-owner-${Math.random().toString(36).slice(2, 10)}`);
3091
+ const claimedPlaybackOwnerKeyRef = useRef8(null);
3044
3092
  const socketRef = useRef8(null);
3045
3093
  const socketIdRef = useRef8(socketId);
3046
3094
  const commandUrlRef = useRef8(commandUrl);
@@ -3058,6 +3106,12 @@ function useTourPlayback({
3058
3106
  toursApiBaseRef.current = toursApiBase;
3059
3107
  pendingTourRef.current = pendingTour;
3060
3108
  showCaptionsRef.current = showCaptions;
3109
+ const releasePlaybackOwnership = useCallback7(() => {
3110
+ const claimedKey = claimedPlaybackOwnerKeyRef.current;
3111
+ if (!claimedKey) return;
3112
+ releaseTourPlaybackOwnership(claimedKey, playbackOwnerIdRef.current);
3113
+ claimedPlaybackOwnerKeyRef.current = null;
3114
+ }, []);
3061
3115
  useEffect11(() => {
3062
3116
  if (disabled) return;
3063
3117
  if (typeof window === "undefined") return;
@@ -3093,6 +3147,12 @@ function useTourPlayback({
3093
3147
  }
3094
3148
  };
3095
3149
  const handleCommand = async (payload) => {
3150
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
3151
+ if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3152
+ const activeType = experienceTypeRef.current;
3153
+ console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
3154
+ return;
3155
+ }
3096
3156
  const emitIfOpen = (ev, data) => {
3097
3157
  if (socketRef.current !== socket) return;
3098
3158
  emitSocketEvent(socket, ev, data);
@@ -3598,7 +3658,14 @@ function useTourPlayback({
3598
3658
  console.log(`[TourClient] Ignoring ${tour.type} start (this hook is for ${expType})`);
3599
3659
  return;
3600
3660
  }
3661
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
3662
+ if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3663
+ console.log(`[TourClient] Ignoring ${expType} start because another hook already owns playback`);
3664
+ return;
3665
+ }
3666
+ claimedPlaybackOwnerKeyRef.current = ownerKey;
3601
3667
  skipRequestedRef.current = false;
3668
+ startRequestedRef.current = false;
3602
3669
  const total = tourData.totalSteps ?? tour?.steps?.length ?? 0;
3603
3670
  isActiveRef.current = true;
3604
3671
  setIsActive(true);
@@ -3657,6 +3724,16 @@ function useTourPlayback({
3657
3724
  };
3658
3725
  const handleDebugLog = (entry) => {
3659
3726
  const isDev = devModeRef.current || process.env.NODE_ENV === "development" || typeof window !== "undefined" && window.MODELNEX_DEBUG;
3727
+ const entryTourType = entry?.data?.tourContext?.type ?? tourRef.current?.type ?? null;
3728
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
3729
+ if (!shouldLogTourDebugEntry({
3730
+ isPlaybackActive: isActiveRef.current,
3731
+ entryType: entry?.type,
3732
+ entryTourType,
3733
+ experienceType: experienceTypeRef.current
3734
+ }) || isActiveRef.current && !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3735
+ return;
3736
+ }
3660
3737
  if (isDev) {
3661
3738
  console.log(`%c[ModelNex Tour] ${entry.type}`, "color: #3b82f6; font-weight: bold", entry);
3662
3739
  if (typeof window !== "undefined") {
@@ -3689,9 +3766,10 @@ function useTourPlayback({
3689
3766
  setServerState(null);
3690
3767
  runIdRef.current = null;
3691
3768
  turnIdRef.current = null;
3769
+ releasePlaybackOwnership();
3692
3770
  tourSocketPool.release(serverUrl, toClose);
3693
3771
  };
3694
- }, [serverUrl, disabled]);
3772
+ }, [serverUrl, disabled, releasePlaybackOwnership]);
3695
3773
  useEffect11(() => {
3696
3774
  if (disabled) return;
3697
3775
  const s = socketRef.current;
@@ -3762,6 +3840,8 @@ function useTourPlayback({
3762
3840
  const stopTour = useCallback7(() => {
3763
3841
  skipRequestedRef.current = true;
3764
3842
  isActiveRef.current = false;
3843
+ startRequestedRef.current = false;
3844
+ releasePlaybackOwnership();
3765
3845
  activeExecutionTokenRef.current += 1;
3766
3846
  commandInFlightRef.current = false;
3767
3847
  activeCommandBatchIdRef.current = null;
@@ -3797,12 +3877,14 @@ function useTourPlayback({
3797
3877
  setIsReviewMode(false);
3798
3878
  pendingInputBufRef.current = null;
3799
3879
  onTourEnd?.();
3800
- }, [voice, onTourEnd, serverUrl, websiteId]);
3880
+ }, [voice, onTourEnd, serverUrl, websiteId, releasePlaybackOwnership]);
3801
3881
  const handleTourEnd = useCallback7(() => {
3802
3882
  const endingTourId = tourRef.current?.id;
3803
3883
  const endingPreviewRunId = previewRunIdRef.current;
3804
3884
  const endingStepOrder = stepIndexRef.current;
3805
3885
  isActiveRef.current = false;
3886
+ startRequestedRef.current = false;
3887
+ releasePlaybackOwnership();
3806
3888
  setPlaybackState("complete");
3807
3889
  removeHighlight();
3808
3890
  removeCaption();
@@ -3834,8 +3916,15 @@ function useTourPlayback({
3834
3916
  clearActiveDraftPreview(experienceType);
3835
3917
  }
3836
3918
  onTourEnd?.();
3837
- }, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId]);
3919
+ }, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId, releasePlaybackOwnership]);
3838
3920
  const runTour = useCallback7(async (tour, options) => {
3921
+ if (!shouldAcceptTourStart({
3922
+ isPlaybackActive: isActiveRef.current,
3923
+ startRequested: startRequestedRef.current
3924
+ })) {
3925
+ console.log("[TourClient] Ignoring duplicate start request while playback is already active or starting:", tour.id);
3926
+ return;
3927
+ }
3839
3928
  setPendingTour(null);
3840
3929
  pendingTourRef.current = null;
3841
3930
  let retries = 0;
@@ -3847,6 +3936,13 @@ function useTourPlayback({
3847
3936
  console.warn("[TourClient] Cannot run tour, socket not connected.");
3848
3937
  return;
3849
3938
  }
3939
+ const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
3940
+ if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3941
+ console.log("[TourClient] Ignoring duplicate start request because another hook already owns this experience:", tour.id);
3942
+ return;
3943
+ }
3944
+ claimedPlaybackOwnerKeyRef.current = ownerKey;
3945
+ startRequestedRef.current = true;
3850
3946
  const shouldReview = Boolean(options?.reviewMode);
3851
3947
  resetCaptionSuppression();
3852
3948
  setReviewStatusMessage(null);
@@ -3878,8 +3974,13 @@ function useTourPlayback({
3878
3974
  });
3879
3975
  }, [serverUrl, websiteId]);
3880
3976
  useEffect11(() => {
3881
- if (!enableAutoDiscovery) return;
3882
- if (disabled) return;
3977
+ if (!shouldRunTourAutoDiscovery({
3978
+ enableAutoDiscovery,
3979
+ disabled,
3980
+ isPlaybackActive: isActiveRef.current,
3981
+ startRequested: startRequestedRef.current,
3982
+ hasPendingTour: Boolean(pendingTourRef.current)
3983
+ })) return;
3883
3984
  if (typeof window === "undefined") return;
3884
3985
  const params = new URLSearchParams(window.location.search);
3885
3986
  const queryParam = experienceType === "onboarding" ? "modelnex_test_workflow" : "modelnex_test_tour";
@@ -3927,8 +4028,13 @@ function useTourPlayback({
3927
4028
  };
3928
4029
  }, [serverUrl, toursApiBase, disabled, websiteId, experienceType, enableAutoDiscovery]);
3929
4030
  useEffect11(() => {
3930
- if (!enableAutoDiscovery) return;
3931
- if (disabled) return;
4031
+ if (!shouldRunTourAutoDiscovery({
4032
+ enableAutoDiscovery,
4033
+ disabled,
4034
+ isPlaybackActive: isActiveRef.current,
4035
+ startRequested: startRequestedRef.current,
4036
+ hasPendingTour: Boolean(pendingTourRef.current)
4037
+ })) return;
3932
4038
  if (!websiteId || !userProfile) return;
3933
4039
  if (typeof window !== "undefined") {
3934
4040
  const params = new URLSearchParams(window.location.search);
@@ -3967,7 +4073,7 @@ function useTourPlayback({
3967
4073
  cancelled = true;
3968
4074
  clearTimeout(timer);
3969
4075
  };
3970
- }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile, enableAutoDiscovery]);
4076
+ }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, enableAutoDiscovery]);
3971
4077
  useEffect11(() => {
3972
4078
  if (!disabled || !isActiveRef.current) return;
3973
4079
  stopTour();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelnex/sdk",
3
- "version": "0.5.17",
3
+ "version": "0.5.19",
4
4
  "description": "React SDK for natural language control of web apps via AI agents",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",