@modelnex/sdk 0.5.28 → 0.5.30

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.mjs CHANGED
@@ -27,6 +27,60 @@ function resolveSocketIoTransports(serverUrl, order) {
27
27
 
28
28
  // src/auto-extract.ts
29
29
  import { useState, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2 } from "react";
30
+
31
+ // src/utils/dev-logging.ts
32
+ function isSdkDebugEnabled(devMode) {
33
+ if (devMode) return true;
34
+ if (typeof window !== "undefined" && Boolean(window.MODELNEX_DEBUG)) {
35
+ return true;
36
+ }
37
+ return process.env.NODE_ENV === "development";
38
+ }
39
+ function emitSdkDebugLog(message, payload, options) {
40
+ if (!isSdkDebugEnabled(options?.devMode)) return;
41
+ if (payload && Object.keys(payload).length > 0) {
42
+ console.log(message, payload);
43
+ } else {
44
+ console.log(message);
45
+ }
46
+ if (options?.dispatchEvent && typeof window !== "undefined") {
47
+ window.dispatchEvent(new CustomEvent("modelnex-debug", { detail: { msg: message, data: payload } }));
48
+ }
49
+ }
50
+ function sanitizeActionList(actions) {
51
+ if (!Array.isArray(actions) || actions.length === 0) return void 0;
52
+ return actions.map(({ actionId }) => ({ actionId }));
53
+ }
54
+ function sanitizeAgentDebug(debug) {
55
+ if (!debug) return void 0;
56
+ const actions = sanitizeActionList(debug.actions);
57
+ const traces = Array.isArray(debug.traces) && debug.traces.length > 0 ? debug.traces.map((trace) => ({
58
+ step: trace.step,
59
+ actions: sanitizeActionList(trace.actions) ?? [],
60
+ results: Array.isArray(trace.results) && trace.results.length > 0 ? trace.results.map(({ actionId, success }) => ({ actionId, success })) : void 0
61
+ })) : void 0;
62
+ if ((!actions || actions.length === 0) && (!traces || traces.length === 0)) {
63
+ return void 0;
64
+ }
65
+ return {
66
+ ...actions ? { actions } : {},
67
+ ...traces ? { traces } : {}
68
+ };
69
+ }
70
+ function sanitizeChatMessages(messages, devMode) {
71
+ const includeDebug = isSdkDebugEnabled(devMode);
72
+ return messages.map((message) => {
73
+ if (message.role !== "assistant") {
74
+ return message;
75
+ }
76
+ return {
77
+ ...message,
78
+ debug: includeDebug ? sanitizeAgentDebug(message.debug) : void 0
79
+ };
80
+ });
81
+ }
82
+
83
+ // src/auto-extract.ts
30
84
  function simpleHash(str) {
31
85
  let hash = 0;
32
86
  for (let i = 0; i < str.length; i++) {
@@ -290,7 +344,7 @@ function extractInteractiveElements() {
290
344
  }
291
345
  return elements;
292
346
  }
293
- function useAutoExtract() {
347
+ function useAutoExtract(devMode) {
294
348
  const [elements, setElements] = useState([]);
295
349
  const timerRef = useRef2(null);
296
350
  const lastSnapshotRef = useRef2("");
@@ -308,9 +362,11 @@ function useAutoExtract() {
308
362
  })));
309
363
  if (snapshot === lastSnapshotRef.current) return;
310
364
  lastSnapshotRef.current = snapshot;
311
- console.log(`[ModelNex AutoExtract] Found ${extracted.length} interactive elements`, extracted.map((e) => ({ fp: e.fingerprint, role: e.role, text: e.text.slice(0, 30) })));
365
+ emitSdkDebugLog("[ModelNex AutoExtract] Scan complete", {
366
+ elementCount: extracted.length
367
+ }, { devMode });
312
368
  setElements(extracted);
313
- }, []);
369
+ }, [devMode]);
314
370
  useEffect2(() => {
315
371
  const initialTimer = setTimeout(scan, 300);
316
372
  const observer = new MutationObserver((mutations) => {
@@ -625,7 +681,8 @@ function useModelNexSocket({
625
681
  setStagingFields,
626
682
  setExecutedFields,
627
683
  onSocketId,
628
- websiteId
684
+ websiteId,
685
+ devMode
629
686
  }) {
630
687
  const socketRef = useRef3(null);
631
688
  const actionsRef = useRef3(actions);
@@ -658,12 +715,14 @@ function useModelNexSocket({
658
715
  allTaggedElements: tagsRef.current ? Array.from(tagsRef.current.values()) : void 0
659
716
  });
660
717
  socket.on("connect", () => {
661
- console.log("ModelNex SDK: Connected to agent server", socket.id);
718
+ emitSdkDebugLog("[ModelNex SDK] Connected to agent server", {
719
+ socketId: socket.id ?? null
720
+ }, { devMode });
662
721
  onSocketId?.(socket.id || null);
663
722
  socket.emit("client:sync", buildSyncPayload());
664
723
  });
665
724
  socket.on("disconnect", () => {
666
- console.log("ModelNex SDK: Disconnected from agent server");
725
+ emitSdkDebugLog("[ModelNex SDK] Disconnected from agent server", void 0, { devMode });
667
726
  onSocketId?.(null);
668
727
  });
669
728
  socket.on("agent:request_context", () => {
@@ -689,12 +748,11 @@ function useModelNexSocket({
689
748
  reasoning
690
749
  }) => {
691
750
  const log = (msg, data) => {
692
- console.log(msg, data ?? "");
693
- window.dispatchEvent(new CustomEvent("modelnex-debug", { detail: { msg, data } }));
751
+ emitSdkDebugLog(msg, data, { devMode, dispatchEvent: true });
694
752
  };
695
753
  if (reasoning || params?.fingerprint) {
696
754
  window.dispatchEvent(new CustomEvent("modelnex-action-start", {
697
- detail: { actionId, fingerprint: params?.fingerprint ?? null, reasoning: reasoning ?? "" }
755
+ detail: { actionId, fingerprint: params?.fingerprint ?? null }
698
756
  }));
699
757
  }
700
758
  const sendResult = (success, result, error) => {
@@ -705,17 +763,18 @@ function useModelNexSocket({
705
763
  const currentActions = actionsRef.current;
706
764
  log("[SDK] agent:execute received", {
707
765
  actionId,
708
- params,
709
- registeredActions: Array.from(currentActions.keys())
766
+ executionId: executionId ?? null,
767
+ fieldId: fieldId ?? null,
768
+ hasParams: params != null
710
769
  });
711
770
  const NAV_ACTION_IDS = ["navigate_to_documents", "navigate_to_templates", "navigate_to_inbox", "navigate_to_settings", "navigate_to_template", "navigate_to_document", "navigate_to_folder", "navigate_editor_step"];
712
771
  const action = currentActions.get(actionId);
713
772
  if (action) {
714
773
  try {
715
774
  const validatedParams = action.schema.parse(params);
716
- log("[SDK] Executing", { actionId, validatedParams });
775
+ log("[SDK] Executing action", { actionId });
717
776
  const execResult = await action.execute(validatedParams);
718
- log("[SDK] Execute completed", { actionId });
777
+ log("[SDK] Action completed", { actionId });
719
778
  sendResult(true, execResult);
720
779
  if (NAV_ACTION_IDS.includes(actionId)) {
721
780
  requestAnimationFrame(() => {
@@ -739,25 +798,29 @@ function useModelNexSocket({
739
798
  }
740
799
  } catch (err) {
741
800
  const errMsg = err instanceof Error ? err.message : String(err);
742
- console.error(`[ModelNex SDK] Execution failed for ${actionId}`, err);
801
+ console.error(`[ModelNex SDK] Execution failed for ${actionId}: ${errMsg}`);
802
+ if (isSdkDebugEnabled(devMode)) {
803
+ window.dispatchEvent(
804
+ new CustomEvent("modelnex-debug", {
805
+ detail: { msg: "[SDK] Execution failed", data: { actionId, error: errMsg } }
806
+ })
807
+ );
808
+ }
809
+ sendResult(false, void 0, errMsg);
810
+ }
811
+ } else {
812
+ const errMsg = `Action not found: ${actionId}`;
813
+ console.warn(`[ModelNex SDK] ${errMsg}`);
814
+ if (isSdkDebugEnabled(devMode)) {
743
815
  window.dispatchEvent(
744
816
  new CustomEvent("modelnex-debug", {
745
- detail: { msg: "[SDK] Execution failed", data: { actionId, error: errMsg } }
817
+ detail: {
818
+ msg: "[SDK] Action not found",
819
+ data: { actionId }
820
+ }
746
821
  })
747
822
  );
748
- sendResult(false, void 0, errMsg);
749
823
  }
750
- } else {
751
- const errMsg = `Action not found: ${actionId}`;
752
- console.warn("[ModelNex SDK]", errMsg, "Available:", Array.from(currentActions.keys()));
753
- window.dispatchEvent(
754
- new CustomEvent("modelnex-debug", {
755
- detail: {
756
- msg: "[SDK] Action not found",
757
- data: { actionId, available: Array.from(actions.keys()) }
758
- }
759
- })
760
- );
761
824
  sendResult(false, void 0, errMsg);
762
825
  }
763
826
  }
@@ -2486,6 +2549,54 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
2486
2549
  // src/constants.ts
2487
2550
  var DEFAULT_MODELNEX_SERVER_URL = "https://api.modelnex.com";
2488
2551
 
2552
+ // src/utils/dev-mode.ts
2553
+ var DEV_MODE_KEY_GLOBAL_NAMES = ["__MODELNEX_DEV_MODE_KEY__", "MODELNEX_DEV_MODE_KEY"];
2554
+ var devModeValidationCache = /* @__PURE__ */ new Map();
2555
+ function normalizeDevModeKey(value) {
2556
+ if (typeof value !== "string") return void 0;
2557
+ const normalized = value.trim();
2558
+ return normalized || void 0;
2559
+ }
2560
+ function resolveInjectedDevModeKey() {
2561
+ if (typeof window === "undefined") return void 0;
2562
+ const browserWindow = window;
2563
+ for (const globalName of DEV_MODE_KEY_GLOBAL_NAMES) {
2564
+ const normalizedGlobalKey = normalizeDevModeKey(browserWindow[globalName]);
2565
+ if (normalizedGlobalKey) return normalizedGlobalKey;
2566
+ }
2567
+ const scriptInjectedKey = typeof document !== "undefined" ? normalizeDevModeKey(
2568
+ document.querySelector("script[data-modelnex-dev-mode-key]")?.dataset.modelnexDevModeKey
2569
+ ) : void 0;
2570
+ if (scriptInjectedKey) return scriptInjectedKey;
2571
+ return void 0;
2572
+ }
2573
+ async function validateInjectedDevModeKey(serverUrl, websiteId, devModeKey) {
2574
+ const normalizedWebsiteId = websiteId.trim();
2575
+ const normalizedDevModeKey = devModeKey.trim();
2576
+ if (!normalizedWebsiteId || !normalizedDevModeKey) return false;
2577
+ const cacheKey = `${serverUrl}::${normalizedWebsiteId}::${normalizedDevModeKey}`;
2578
+ const cached = devModeValidationCache.get(cacheKey);
2579
+ if (cached) {
2580
+ return cached;
2581
+ }
2582
+ const validationPromise = fetch(
2583
+ `${serverUrl.replace(/\/$/, "")}/api/websites/${encodeURIComponent(normalizedWebsiteId)}/dev-mode/verify`,
2584
+ {
2585
+ method: "POST",
2586
+ headers: {
2587
+ "Content-Type": "application/json"
2588
+ },
2589
+ body: JSON.stringify({ key: normalizedDevModeKey })
2590
+ }
2591
+ ).then(async (response) => {
2592
+ if (!response.ok) return false;
2593
+ const payload = await response.json().catch(() => null);
2594
+ return payload?.enabled === true;
2595
+ }).catch(() => false);
2596
+ devModeValidationCache.set(cacheKey, validationPromise);
2597
+ return validationPromise;
2598
+ }
2599
+
2489
2600
  // src/hooks/useRunCommand.ts
2490
2601
  import { useCallback as useCallback5, useContext as useContext2 } from "react";
2491
2602
  function searchTaggedElementsForQuery(store, query, limit = 8) {
@@ -2528,7 +2639,10 @@ function useRunCommand(serverUrlOverride) {
2528
2639
  if (!res.ok) {
2529
2640
  throw new Error(data?.error ?? `Request failed: ${res.status}`);
2530
2641
  }
2531
- return data;
2642
+ return {
2643
+ ...data,
2644
+ debug: sanitizeAgentDebug(data?.debug)
2645
+ };
2532
2646
  },
2533
2647
  [baseUrl, tagStore]
2534
2648
  );
@@ -3436,7 +3550,9 @@ function useTourPlayback({
3436
3550
  const socket = tourSocketPool.acquire(serverUrl);
3437
3551
  socketRef.current = socket;
3438
3552
  const handleConnect = () => {
3439
- console.log("[TourClient] Connected to tour agent server:", socket.id);
3553
+ emitSdkDebugLog("[TourClient] Connected to tour agent server", {
3554
+ socketId: socket.id ?? null
3555
+ }, { devMode: devModeRef.current });
3440
3556
  const profile = userProfileRef.current;
3441
3557
  const currentWebsiteId = websiteIdRef.current;
3442
3558
  if (currentWebsiteId && profile?.userId) {
@@ -3453,7 +3569,9 @@ function useTourPlayback({
3453
3569
  setServerState(payload);
3454
3570
  };
3455
3571
  const handleCommandCancel = (payload) => {
3456
- console.log("[TourClient] Received command_cancel:", payload);
3572
+ emitSdkDebugLog("[TourClient] Received command cancel", {
3573
+ commandBatchId: payload.commandBatchId ?? null
3574
+ }, { devMode: devModeRef.current });
3457
3575
  if (payload.commandBatchId && activeCommandBatchIdRef.current === payload.commandBatchId) {
3458
3576
  activeCommandBatchIdRef.current = null;
3459
3577
  activeExecutionTokenRef.current++;
@@ -3468,14 +3586,22 @@ function useTourPlayback({
3468
3586
  const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
3469
3587
  if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3470
3588
  const activeType = experienceTypeRef.current;
3471
- console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
3589
+ emitSdkDebugLog("[TourClient] Ignoring command batch for inactive playback hook", {
3590
+ experienceType: activeType,
3591
+ stepIndex: payload.stepIndex
3592
+ }, { devMode: devModeRef.current });
3472
3593
  return;
3473
3594
  }
3474
3595
  const emitIfOpen = (ev, data) => {
3475
3596
  if (socketRef.current !== socket) return;
3476
3597
  emitSocketEvent(socket, ev, data);
3477
3598
  };
3478
- console.log("[TourClient] Received command batch:", payload.stepIndex, payload.commands);
3599
+ const commandBatchId = payload.commandBatchId ?? null;
3600
+ emitSdkDebugLog("[TourClient] Received command batch", {
3601
+ stepIndex: payload.stepIndex,
3602
+ commandCount: Array.isArray(payload.commands) ? payload.commands.length : 0,
3603
+ commandBatchId
3604
+ }, { devMode: devModeRef.current });
3479
3605
  runCleanup(pendingManualWaitCleanupRef.current);
3480
3606
  pendingManualWaitCleanupRef.current = null;
3481
3607
  if (voiceInputResolveRef.current) {
@@ -3485,7 +3611,6 @@ function useTourPlayback({
3485
3611
  }
3486
3612
  setPlaybackState("executing");
3487
3613
  commandInFlightRef.current = true;
3488
- const commandBatchId = payload.commandBatchId ?? null;
3489
3614
  turnIdRef.current = payload.turnId ?? turnIdRef.current;
3490
3615
  const clearCommandBatchId = () => {
3491
3616
  if (activeCommandBatchIdRef.current === commandBatchId) {
@@ -3531,7 +3656,7 @@ function useTourPlayback({
3531
3656
  }
3532
3657
  }
3533
3658
  if (!payload.commands || !Array.isArray(payload.commands)) {
3534
- console.warn("[TourClient] Payload commands is not an array:", payload);
3659
+ console.warn("[TourClient] Command batch payload was invalid.");
3535
3660
  commandInFlightRef.current = false;
3536
3661
  emitIfOpen("tour:action_result", {
3537
3662
  success: false,
@@ -3580,7 +3705,9 @@ function useTourPlayback({
3580
3705
  };
3581
3706
  const executeOne = async (action) => {
3582
3707
  assertNotInterrupted();
3583
- console.log("[TourClient] Executing action:", action?.type, action?.params ? JSON.stringify(action.params).slice(0, 120) : "");
3708
+ emitSdkDebugLog("[TourClient] Executing action", {
3709
+ actionType: action?.type ?? "unknown"
3710
+ }, { devMode: devModeRef.current });
3584
3711
  const currentStep = tourRef.current?.steps?.[stepIndexRef.current] ?? null;
3585
3712
  const executeTimeline = async (params = {}) => {
3586
3713
  const segments = Array.isArray(params.segments) ? params.segments : [];
@@ -3684,7 +3811,7 @@ function useTourPlayback({
3684
3811
  }
3685
3812
  removeHighlight();
3686
3813
  await performInteractiveClick(targetEl);
3687
- const { waitForDomSettle: waitForDomSettleClick } = await import("./dom-sync-L5KIP45X.mjs");
3814
+ const { waitForDomSettle: waitForDomSettleClick } = await import("./dom-sync-GABDEODR.mjs");
3688
3815
  await waitForDomSettleClick({ timeoutMs: 3e3, debounceMs: 300 });
3689
3816
  return { result: "clicked" };
3690
3817
  }
@@ -3721,7 +3848,7 @@ function useTourPlayback({
3721
3848
  throw new Error("navigate_to_url missing url");
3722
3849
  }
3723
3850
  await navigateToTourUrl(nextUrl);
3724
- const { waitForDomSettle: waitForDomSettleNav } = await import("./dom-sync-L5KIP45X.mjs");
3851
+ const { waitForDomSettle: waitForDomSettleNav } = await import("./dom-sync-GABDEODR.mjs");
3725
3852
  await waitForDomSettleNav({ timeoutMs: 3e3, debounceMs: 300 });
3726
3853
  return { result: nextUrl };
3727
3854
  }
@@ -3745,7 +3872,7 @@ function useTourPlayback({
3745
3872
  if (action.params?.wait !== false) {
3746
3873
  await res.json();
3747
3874
  }
3748
- const { waitForDomSettle } = await import("./dom-sync-L5KIP45X.mjs");
3875
+ const { waitForDomSettle } = await import("./dom-sync-GABDEODR.mjs");
3749
3876
  await waitForDomSettle({ timeoutMs: 3e3, debounceMs: 300 });
3750
3877
  await syncAOM();
3751
3878
  return { result: action.params?.command ?? "executed" };
@@ -3767,7 +3894,7 @@ function useTourPlayback({
3767
3894
  handleTourEnd();
3768
3895
  return { result: "ended" };
3769
3896
  }
3770
- console.warn("[TourClient] Unknown action type:", action?.type, "- skipping");
3897
+ console.warn(`[TourClient] Unknown action type: ${String(action?.type ?? "unknown")}. Skipping.`);
3771
3898
  return { result: "unknown_action_skipped" };
3772
3899
  };
3773
3900
  try {
@@ -3823,14 +3950,15 @@ function useTourPlayback({
3823
3950
  clearCommandBatchId();
3824
3951
  return;
3825
3952
  }
3826
- console.error("[TourClient] Command batch execution failed:", err);
3953
+ const errMsg = err instanceof Error ? err.message : String(err);
3954
+ console.error(`[TourClient] Command batch execution failed: ${errMsg}`);
3827
3955
  if (reviewModeRef.current && activeTourId && activePreviewRunId) {
3828
3956
  void logPreviewEvent(serverUrl, toursApiBaseRef.current, activeTourId, activePreviewRunId, websiteId, {
3829
3957
  stepOrder: stepIndexRef.current,
3830
3958
  eventType: "command_batch_failed",
3831
3959
  payload: {
3832
3960
  commandBatchId,
3833
- error: String(err),
3961
+ error: errMsg,
3834
3962
  partialResults: results
3835
3963
  },
3836
3964
  status: "active",
@@ -3840,7 +3968,7 @@ function useTourPlayback({
3840
3968
  emitIfOpen("tour:action_result", {
3841
3969
  success: false,
3842
3970
  reason: "execution_error",
3843
- error: String(err),
3971
+ error: errMsg,
3844
3972
  results,
3845
3973
  commandBatchId,
3846
3974
  runId: runIdRef.current,
@@ -3866,7 +3994,10 @@ function useTourPlayback({
3866
3994
  let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
3867
3995
  if (inputLikeWait && preferredWaitTarget && manualWaitTarget && manualWaitTarget !== preferredWaitTarget && !isEditableWaitTarget(manualWaitTarget) && isEditableWaitTarget(preferredWaitTarget)) {
3868
3996
  manualWaitTarget = preferredWaitTarget;
3869
- console.log("[TourClient] wait_for_input: preferring current editable target over hinted step target", manualWaitTarget);
3997
+ emitSdkDebugLog("[TourClient] wait_for_input preferred highlighted editable target", {
3998
+ stepIndex: stepIndexRef.current,
3999
+ event: waitEvent
4000
+ }, { devMode: devModeRef.current });
3870
4001
  }
3871
4002
  if (manualWaitTarget) {
3872
4003
  const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep);
@@ -3880,7 +4011,10 @@ function useTourPlayback({
3880
4011
  manualWaitPromise = manualWait.promise;
3881
4012
  manualWaitKind = manualWait.kind;
3882
4013
  pendingManualWaitCleanupRef.current = manualWait.cleanup;
3883
- console.log("[TourClient] wait_for_input: using current editable target as fallback wait target", preferredWaitTarget);
4014
+ emitSdkDebugLog("[TourClient] wait_for_input using fallback editable target", {
4015
+ stepIndex: stepIndexRef.current,
4016
+ event: waitEvent
4017
+ }, { devMode: devModeRef.current });
3884
4018
  }
3885
4019
  if (!manualWaitPromise && inputLikeWait) {
3886
4020
  const firstInput = document.querySelector(
@@ -3891,7 +4025,10 @@ function useTourPlayback({
3891
4025
  manualWaitPromise = manualWait.promise;
3892
4026
  manualWaitKind = manualWait.kind;
3893
4027
  pendingManualWaitCleanupRef.current = manualWait.cleanup;
3894
- console.log("[TourClient] wait_for_input: no target found, falling back to first visible editable element", firstInput);
4028
+ emitSdkDebugLog("[TourClient] wait_for_input falling back to first editable element", {
4029
+ stepIndex: stepIndexRef.current,
4030
+ event: waitEvent
4031
+ }, { devMode: devModeRef.current });
3895
4032
  }
3896
4033
  }
3897
4034
  setPlaybackState(manualWaitKind ? "waiting_input" : "waiting_voice");
@@ -3936,7 +4073,7 @@ function useTourPlayback({
3936
4073
  if (!transcript) {
3937
4074
  return;
3938
4075
  }
3939
- const { waitForDomSettle } = await import("./dom-sync-L5KIP45X.mjs");
4076
+ const { waitForDomSettle } = await import("./dom-sync-GABDEODR.mjs");
3940
4077
  await waitForDomSettle({ timeoutMs: 1500, debounceMs: 200 });
3941
4078
  await syncAOM();
3942
4079
  emitIfOpen("tour:user_input", {
@@ -3973,12 +4110,17 @@ function useTourPlayback({
3973
4110
  const tour = tourData.tourContext ?? tourRef.current;
3974
4111
  const expType = experienceTypeRef.current;
3975
4112
  if (tour?.type && tour.type !== expType) {
3976
- console.log(`[TourClient] Ignoring ${tour.type} start (this hook is for ${expType})`);
4113
+ emitSdkDebugLog("[TourClient] Ignoring tour start for mismatched experience type", {
4114
+ incomingType: tour.type,
4115
+ hookType: expType
4116
+ }, { devMode: devModeRef.current });
3977
4117
  return;
3978
4118
  }
3979
4119
  const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
3980
4120
  if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3981
- console.log(`[TourClient] Ignoring ${expType} start because another hook already owns playback`);
4121
+ emitSdkDebugLog("[TourClient] Ignoring tour start because playback ownership is already claimed", {
4122
+ experienceType: expType
4123
+ }, { devMode: devModeRef.current });
3982
4124
  return;
3983
4125
  }
3984
4126
  claimedPlaybackOwnerKeyRef.current = ownerKey;
@@ -4003,6 +4145,8 @@ function useTourPlayback({
4003
4145
  },
4004
4146
  currentStepOrder: 0
4005
4147
  });
4148
+ } else if (tour?.id && userProfile?.userId) {
4149
+ void recordTourEvent(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, "started", websiteId);
4006
4150
  }
4007
4151
  try {
4008
4152
  const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-HDYNCIOY.mjs");
@@ -4015,7 +4159,8 @@ function useTourPlayback({
4015
4159
  });
4016
4160
  }
4017
4161
  } catch (e) {
4018
- console.warn("[TourClient] Initial DOM sync failed:", e);
4162
+ const errMsg = e instanceof Error ? e.message : String(e);
4163
+ console.warn(`[TourClient] Initial DOM sync failed: ${errMsg}`);
4019
4164
  }
4020
4165
  };
4021
4166
  const handleTourUpdate = (payload) => {
@@ -4053,12 +4198,11 @@ function useTourPlayback({
4053
4198
  return;
4054
4199
  }
4055
4200
  if (isDev) {
4056
- console.log(`%c[ModelNex Tour] ${entry.type}`, "color: #3b82f6; font-weight: bold", entry);
4057
- if (typeof window !== "undefined") {
4058
- window.dispatchEvent(new CustomEvent("modelnex-debug", {
4059
- detail: { msg: `[Tour Timeline] ${entry.type}`, data: entry }
4060
- }));
4061
- }
4201
+ emitSdkDebugLog(`[Tour Timeline] ${entry.type}`, {
4202
+ stepIndex: entry?.data?.stepIndex ?? null,
4203
+ runId: entry?.data?.runId ?? null,
4204
+ turnId: entry?.data?.turnId ?? null
4205
+ }, { devMode: devModeRef.current, dispatchEvent: true });
4062
4206
  }
4063
4207
  };
4064
4208
  socket.on("connect", handleConnect);
@@ -4069,7 +4213,7 @@ function useTourPlayback({
4069
4213
  socket.on("tour:update", handleTourUpdate);
4070
4214
  socket.on("tour:end", handleTourEndEvent);
4071
4215
  socket.on("tour:debug_log", handleDebugLog);
4072
- console.log("[ModelNex SDK] Tour playback initialized. Debug logs enabled:", devModeRef.current || process.env.NODE_ENV === "development");
4216
+ emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
4073
4217
  return () => {
4074
4218
  socket.off("connect", handleConnect);
4075
4219
  socket.off("tour:server_state", handleServerState);
@@ -4180,6 +4324,8 @@ function useTourPlayback({
4180
4324
  status: "stopped",
4181
4325
  currentStepOrder: stepIndexRef.current
4182
4326
  });
4327
+ } else if (tourRef.current?.id && userProfile?.userId) {
4328
+ void recordTourEvent(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, "cancelled", websiteId);
4183
4329
  }
4184
4330
  if (reviewModeRef.current) {
4185
4331
  if (tourRef.current?.id) {
@@ -4252,7 +4398,9 @@ function useTourPlayback({
4252
4398
  isPlaybackActive: isActiveRef.current,
4253
4399
  startRequested: startRequestedRef.current
4254
4400
  })) {
4255
- console.log("[TourClient] Ignoring duplicate start request while playback is already active or starting:", tour.id);
4401
+ emitSdkDebugLog("[TourClient] Ignoring duplicate start request while playback is already active or starting", {
4402
+ tourId: tour.id
4403
+ }, { devMode: devModeRef.current });
4256
4404
  return;
4257
4405
  }
4258
4406
  setPendingTour(null);
@@ -4268,7 +4416,9 @@ function useTourPlayback({
4268
4416
  }
4269
4417
  const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
4270
4418
  if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
4271
- console.log("[TourClient] Ignoring duplicate start request because another hook already owns this experience:", tour.id);
4419
+ emitSdkDebugLog("[TourClient] Ignoring duplicate start request because another hook already owns this experience", {
4420
+ tourId: tour.id
4421
+ }, { devMode: devModeRef.current });
4272
4422
  return;
4273
4423
  }
4274
4424
  claimedPlaybackOwnerKeyRef.current = ownerKey;
@@ -4291,7 +4441,8 @@ function useTourPlayback({
4291
4441
  setPreviewRunId(previewRun.id);
4292
4442
  previewRunIdRef.current = previewRun.id;
4293
4443
  } catch (err) {
4294
- console.warn("[TourClient] Failed to create preview run:", err);
4444
+ const errMsg = err instanceof Error ? err.message : String(err);
4445
+ console.warn(`[TourClient] Failed to create preview run: ${errMsg}`);
4295
4446
  setReviewStatusMessage("Preview review logging is unavailable.");
4296
4447
  setPreviewRunId(null);
4297
4448
  previewRunIdRef.current = null;
@@ -4340,7 +4491,7 @@ function useTourPlayback({
4340
4491
  if (!tour) {
4341
4492
  clearActiveDraftPreview(experienceType);
4342
4493
  persistSuppressedDraftPreview(draftPreview);
4343
- console.warn("[ModelNex] Tour fetch failed:", tourId, experienceType, "(Check ModelNex server is running and CORS allows this origin)");
4494
+ console.warn(`[ModelNex] Tour fetch failed for ${experienceType}:${tourId}. Check the ModelNex server and CORS configuration.`);
4344
4495
  return;
4345
4496
  }
4346
4497
  if (cancelled) {
@@ -4363,7 +4514,8 @@ function useTourPlayback({
4363
4514
  await runTour(tour, previewOptions);
4364
4515
  }
4365
4516
  } catch (err) {
4366
- console.warn("[ModelNex] Tour test failed:", err);
4517
+ const errMsg = err instanceof Error ? err.message : String(err);
4518
+ console.warn(`[ModelNex] Tour test failed: ${errMsg}`);
4367
4519
  }
4368
4520
  })();
4369
4521
  return () => {
@@ -4473,7 +4625,8 @@ function useTourPlayback({
4473
4625
  setPlaybackState("executing");
4474
4626
  }
4475
4627
  } catch (err) {
4476
- console.warn("[TourClient] Failed to submit review feedback:", err);
4628
+ const errMsg = err instanceof Error ? err.message : String(err);
4629
+ console.warn(`[TourClient] Failed to submit review feedback: ${errMsg}`);
4477
4630
  setReviewStatusMessage("Unable to save correction.");
4478
4631
  } finally {
4479
4632
  setReviewSubmitting(false);
@@ -4514,23 +4667,29 @@ function useTourPlayback({
4514
4667
  }, [voice]);
4515
4668
  const handleVoiceInput = useCallback7((transcript) => {
4516
4669
  const text = transcript.trim();
4517
- console.log("[TourAgent] handleVoiceInput called with text:", text);
4670
+ emitSdkDebugLog("[TourAgent] Voice input received", {
4671
+ textLength: text.length
4672
+ }, { devMode: devModeRef.current });
4518
4673
  if (!text) return;
4519
4674
  if (interruptExecution(text)) {
4520
4675
  return;
4521
4676
  }
4522
4677
  if (voiceInputResolveRef.current) {
4523
- console.log("[TourAgent] Resolving loop waiting_voice with text:", text);
4678
+ emitSdkDebugLog("[TourAgent] Resolving waiting voice input", void 0, { devMode: devModeRef.current });
4524
4679
  voiceInputResolveRef.current(text);
4525
4680
  } else if (isSocketWritable(socketRef.current)) {
4526
- console.log("[TourAgent] Forwarding ambient voice to server:", text);
4681
+ emitSdkDebugLog("[TourAgent] Forwarding ambient voice input to server", {
4682
+ textLength: text.length
4683
+ }, { devMode: devModeRef.current });
4527
4684
  emitSocketEvent(socketRef.current, "tour:user_input", {
4528
4685
  transcript: text,
4529
4686
  runId: runIdRef.current,
4530
4687
  turnId: turnIdRef.current
4531
4688
  });
4532
4689
  } else {
4533
- console.log("[TourAgent] buffering voice input because socket is not ready:", text);
4690
+ emitSdkDebugLog("[TourAgent] Buffering voice input until socket is ready", {
4691
+ textLength: text.length
4692
+ }, { devMode: devModeRef.current });
4534
4693
  pendingInputBufRef.current = text;
4535
4694
  }
4536
4695
  }, [interruptExecution]);
@@ -5685,7 +5844,6 @@ function useVoice(serverUrl) {
5685
5844
  setIsListening(false);
5686
5845
  }, [stopLiveSttTransport]);
5687
5846
  const startListening = useCallback9((onResult, onInterruption, onError, options = {}) => {
5688
- console.log("[Voice] startListening called, options:", options);
5689
5847
  stopListening();
5690
5848
  listeningSessionIdRef.current = createVoiceDebugId("stt");
5691
5849
  listeningStartedAtRef.current = performance.now();
@@ -5899,7 +6057,9 @@ function useVoice(serverUrl) {
5899
6057
  recorder.start(LIVE_STT_TIMESLICE_MS);
5900
6058
  }
5901
6059
  setIsListening(true);
5902
- console.log("[Voice] Live STT pipeline active (Deepgram streaming + WebRTC loopback)");
6060
+ emitVoiceDebug("stt_live_pipeline_active", {
6061
+ listeningSessionId: listeningSessionIdRef.current
6062
+ });
5903
6063
  } catch (err) {
5904
6064
  const normalizedError = normalizeSttError(err);
5905
6065
  console.warn("[Voice] Failed to start live STT recorder:", normalizedError);
@@ -8720,7 +8880,7 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8720
8880
  },
8721
8881
  children: [
8722
8882
  /* @__PURE__ */ jsxs3("span", { children: [
8723
- "Agent traces (",
8883
+ "Execution summary (",
8724
8884
  traces.length,
8725
8885
  " step",
8726
8886
  traces.length !== 1 ? "s" : "",
@@ -8736,21 +8896,9 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8736
8896
  /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: command })
8737
8897
  ] }),
8738
8898
  !hasTraceContent ? /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px", padding: "8px", background: "#fef3c7", borderRadius: "4px", borderLeft: "3px solid #f59e0b" }, children: [
8739
- /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, color: "#92400e", marginBottom: "4px" }, children: "No trace data" }),
8740
- /* @__PURE__ */ jsx4("div", { style: { fontSize: "10px", color: "#71717a", marginBottom: "6px" }, children: "The server may not have OPENROUTER_API_KEY set, or the request failed before the agent ran." }),
8741
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "8px", background: "#fff", borderRadius: "4px", fontSize: "10px", overflow: "auto", maxHeight: "120px", color: "#27272a" }, children: JSON.stringify(debug, null, 2) })
8899
+ /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, color: "#92400e", marginBottom: "4px" }, children: "No safe debug summary" }),
8900
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "10px", color: "#71717a" }, children: "Verbose prompts, reasoning, and raw tool payloads are intentionally hidden by the SDK." })
8742
8901
  ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
8743
- debug.llmInput && traces.length === 0 && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px" }, children: [
8744
- /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "Input \u2192 agent" }),
8745
- /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "6px" }, children: [
8746
- /* @__PURE__ */ jsx4("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "System" }),
8747
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "80px", overflow: "auto" }, children: debug.llmInput.systemPrompt ?? "(empty)" })
8748
- ] }),
8749
- /* @__PURE__ */ jsxs3("div", { children: [
8750
- /* @__PURE__ */ jsx4("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "User" }),
8751
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "80px", overflow: "auto" }, children: debug.llmInput.userMessage ?? "(empty)" })
8752
- ] })
8753
- ] }),
8754
8902
  traces.map((t) => {
8755
8903
  const isStepExpanded = expandedSteps.has(t.step);
8756
8904
  return /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px", paddingBottom: "12px", borderBottom: "1px solid #e4e4e7" }, children: [
@@ -8783,31 +8931,6 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8783
8931
  }
8784
8932
  ),
8785
8933
  isStepExpanded && /* @__PURE__ */ jsxs3(Fragment2, { children: [
8786
- t.reasoning && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "6px" }, children: [
8787
- /* @__PURE__ */ jsx4("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Reasoning" }),
8788
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "6px", background: "#fef3c7", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "100px", overflow: "auto", borderLeft: "3px solid #f59e0b", color: "#451a03" }, children: t.reasoning })
8789
- ] }),
8790
- t.llmInput && /* @__PURE__ */ jsxs3(Fragment2, { children: [
8791
- /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "6px" }, children: [
8792
- /* @__PURE__ */ jsx4("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Input \u2192 agent" }),
8793
- /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "4px" }, children: [
8794
- /* @__PURE__ */ jsx4("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "System" }),
8795
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "80px", overflow: "auto" }, children: t.llmInput.systemPrompt ?? "(empty)" })
8796
- ] }),
8797
- /* @__PURE__ */ jsxs3("div", { children: [
8798
- /* @__PURE__ */ jsx4("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "User" }),
8799
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "60px", overflow: "auto" }, children: t.llmInput.userMessage ?? "(empty)" })
8800
- ] })
8801
- ] }),
8802
- /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "6px" }, children: [
8803
- /* @__PURE__ */ jsx4("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Output \u2190 agent" }),
8804
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "60px", overflow: "auto" }, children: t.llmOutput ?? "(empty)" })
8805
- ] })
8806
- ] }),
8807
- !t.llmInput && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "6px" }, children: [
8808
- /* @__PURE__ */ jsx4("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "LLM output" }),
8809
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "60px", overflow: "auto" }, children: t.llmOutput ?? "(empty)" })
8810
- ] }),
8811
8934
  /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "6px" }, children: [
8812
8935
  /* @__PURE__ */ jsx4("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Actions" }),
8813
8936
  /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: JSON.stringify(t.actions, null, 2) })
@@ -8819,10 +8942,6 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8819
8942
  ] })
8820
8943
  ] }, t.step);
8821
8944
  }),
8822
- traces.length === 0 && debug.llmOutput && debug.llmOutput.length > 0 && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px" }, children: [
8823
- /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "LLM output" }),
8824
- /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: debug.llmOutput.join("\n\n") })
8825
- ] }),
8826
8945
  traces.length === 0 && (debug.actions?.length ?? 0) > 0 && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px" }, children: [
8827
8946
  /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "Executed actions" }),
8828
8947
  /* @__PURE__ */ jsx4("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: JSON.stringify(debug.actions, null, 2) })
@@ -9018,7 +9137,6 @@ function ModelNexChatBubble({
9018
9137
  tagStore.setTagsBatch(data.tags, true);
9019
9138
  lastAutoTaggedUrlRef.current = currentUrl;
9020
9139
  localStorage.setItem(storageKey, "true");
9021
- console.log(`[ModelNex] Auto-tagged ${data.tags.length} elements for ${currentUrl}`);
9022
9140
  }
9023
9141
  } catch (err) {
9024
9142
  console.warn("[ModelNex] Auto-tag error:", err);
@@ -9182,12 +9300,9 @@ function ModelNexChatBubble({
9182
9300
  };
9183
9301
  const listeningExperience = resolveTourListeningExperience(preferredExperience, listeningState);
9184
9302
  preferredListeningExperienceRef.current = listeningExperience;
9185
- console.log("[ChatBubble] startTourListening called. listeningState:", listeningState, "preferredExperience:", preferredExperience, "listeningExperience:", listeningExperience);
9186
9303
  if (!canStartTourListening(listeningState)) {
9187
- console.log("[ChatBubble] startTourListening bailed out early.");
9188
9304
  return;
9189
9305
  }
9190
- console.log("[ChatBubble] Proceeding to startTourListening...");
9191
9306
  sttActiveRef.current = true;
9192
9307
  updateTourSttError(null);
9193
9308
  resetFloatingLiveTranscriptSuppression();
@@ -9393,7 +9508,7 @@ function ModelNexChatBubble({
9393
9508
  content: summary || fallbackContent,
9394
9509
  summary: summary ?? void 0,
9395
9510
  nextSteps: nextSteps ?? void 0,
9396
- debug: data?.debug ?? void 0
9511
+ debug: devMode ? data?.debug ?? void 0 : void 0
9397
9512
  }
9398
9513
  ]);
9399
9514
  } catch (err) {
@@ -10436,7 +10551,7 @@ function ModelNexChatBubble({
10436
10551
  )
10437
10552
  }
10438
10553
  ),
10439
- msg.role === "assistant" && msg.debug && /* @__PURE__ */ jsx4(AgentTraces, { debug: msg.debug, command: messages[i - 1]?.content ?? "" })
10554
+ msg.role === "assistant" && devMode && msg.debug && /* @__PURE__ */ jsx4(AgentTraces, { debug: msg.debug, command: messages[i - 1]?.content ?? "" })
10440
10555
  ] }, i)),
10441
10556
  loading && /* @__PURE__ */ jsx4("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ jsxs3(
10442
10557
  "div",
@@ -11414,7 +11529,6 @@ var ModelNexProvider = ({
11414
11529
  websiteId,
11415
11530
  userProfile,
11416
11531
  toursApiBase,
11417
- devMode,
11418
11532
  serverUrl: serverUrlProp
11419
11533
  }) => {
11420
11534
  const serverUrl = serverUrlProp ?? DEFAULT_MODELNEX_SERVER_URL;
@@ -11437,6 +11551,25 @@ var ModelNexProvider = ({
11437
11551
  const [voiceMuted, setVoiceMuted] = useState15(false);
11438
11552
  const [socketId, setSocketId] = useState15(null);
11439
11553
  const [actions, setActions] = useState15(/* @__PURE__ */ new Map());
11554
+ const [validatedBrowserDevMode, setValidatedBrowserDevMode] = useState15(false);
11555
+ const resolvedDevModeKey = useMemo5(() => resolveInjectedDevModeKey(), []);
11556
+ useEffect19(() => {
11557
+ let cancelled = false;
11558
+ if (!websiteId || !resolvedDevModeKey) {
11559
+ setValidatedBrowserDevMode(false);
11560
+ return () => {
11561
+ cancelled = true;
11562
+ };
11563
+ }
11564
+ void validateInjectedDevModeKey(serverUrl, websiteId, resolvedDevModeKey).then((enabled) => {
11565
+ if (cancelled) return;
11566
+ setValidatedBrowserDevMode(enabled);
11567
+ });
11568
+ return () => {
11569
+ cancelled = true;
11570
+ };
11571
+ }, [resolvedDevModeKey, serverUrl, websiteId]);
11572
+ const effectiveDevMode = validatedBrowserDevMode;
11440
11573
  const registerAction = useCallback14((action) => {
11441
11574
  setActions((prev) => {
11442
11575
  const next = new Map(prev);
@@ -11451,7 +11584,7 @@ var ModelNexProvider = ({
11451
11584
  return next;
11452
11585
  });
11453
11586
  }, []);
11454
- const extractedElements = useAutoExtract();
11587
+ const extractedElements = useAutoExtract(effectiveDevMode);
11455
11588
  const tagStore = useTagStore({ serverUrl, websiteId });
11456
11589
  useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId, toursApiBase, userProfile);
11457
11590
  const CHAT_STORAGE_KEY = "modelnex-chat-messages";
@@ -11460,21 +11593,32 @@ var ModelNexProvider = ({
11460
11593
  try {
11461
11594
  const stored = sessionStorage.getItem(CHAT_STORAGE_KEY);
11462
11595
  if (stored) {
11463
- setChatMessagesRaw(JSON.parse(stored));
11596
+ setChatMessagesRaw(sanitizeChatMessages(JSON.parse(stored), effectiveDevMode));
11464
11597
  }
11465
11598
  } catch {
11466
11599
  }
11467
- }, []);
11600
+ }, [effectiveDevMode]);
11601
+ useEffect19(() => {
11602
+ setChatMessagesRaw((prev) => {
11603
+ const next = sanitizeChatMessages(prev, effectiveDevMode);
11604
+ try {
11605
+ sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(next));
11606
+ } catch {
11607
+ }
11608
+ return next;
11609
+ });
11610
+ }, [effectiveDevMode]);
11468
11611
  const setChatMessages = useCallback14((action) => {
11469
11612
  setChatMessagesRaw((prev) => {
11470
- const next = typeof action === "function" ? action(prev) : action;
11613
+ const resolved = typeof action === "function" ? action(prev) : action;
11614
+ const next = sanitizeChatMessages(resolved, effectiveDevMode);
11471
11615
  try {
11472
11616
  sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(next));
11473
11617
  } catch {
11474
11618
  }
11475
11619
  return next;
11476
11620
  });
11477
- }, []);
11621
+ }, [effectiveDevMode]);
11478
11622
  useModelNexSocket({
11479
11623
  serverUrl,
11480
11624
  actions,
@@ -11486,7 +11630,8 @@ var ModelNexProvider = ({
11486
11630
  setStagingFields,
11487
11631
  setExecutedFields,
11488
11632
  onSocketId: setSocketId,
11489
- websiteId
11633
+ websiteId,
11634
+ devMode: effectiveDevMode
11490
11635
  });
11491
11636
  useFieldHighlight(stagingFields, executedFields, setExecutedFields);
11492
11637
  useEffect19(() => {
@@ -11521,9 +11666,9 @@ var ModelNexProvider = ({
11521
11666
  voiceMuted,
11522
11667
  setVoiceMuted,
11523
11668
  socketId,
11524
- devMode
11669
+ devMode: effectiveDevMode
11525
11670
  }),
11526
- [serverUrl, commandUrl, registerAction, unregisterAction, activeAgentActions, stagingFields, highlightActions, studioMode, recordingMode, extractedElements, tagStore, chatMessages, websiteId, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, toursApiBase, voiceMuted, socketId, devMode]
11671
+ [serverUrl, commandUrl, registerAction, unregisterAction, activeAgentActions, stagingFields, highlightActions, studioMode, recordingMode, extractedElements, tagStore, chatMessages, websiteId, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, toursApiBase, voiceMuted, socketId, effectiveDevMode]
11527
11672
  );
11528
11673
  return React8.createElement(
11529
11674
  ModelNexContext.Provider,