@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.js CHANGED
@@ -121,7 +121,6 @@ function waitForDomSettle(options = {}) {
121
121
  if (!resolved) {
122
122
  resolved = true;
123
123
  cleanup();
124
- console.log("[DOM Sync] Forced resolution by max timeout");
125
124
  resolve();
126
125
  }
127
126
  }, Math.max(timeoutMs, minWaitMs));
@@ -237,6 +236,60 @@ function resolveSocketIoTransports(serverUrl, order) {
237
236
 
238
237
  // src/auto-extract.ts
239
238
  var import_react2 = require("react");
239
+
240
+ // src/utils/dev-logging.ts
241
+ function isSdkDebugEnabled(devMode) {
242
+ if (devMode) return true;
243
+ if (typeof window !== "undefined" && Boolean(window.MODELNEX_DEBUG)) {
244
+ return true;
245
+ }
246
+ return process.env.NODE_ENV === "development";
247
+ }
248
+ function emitSdkDebugLog(message, payload, options) {
249
+ if (!isSdkDebugEnabled(options?.devMode)) return;
250
+ if (payload && Object.keys(payload).length > 0) {
251
+ console.log(message, payload);
252
+ } else {
253
+ console.log(message);
254
+ }
255
+ if (options?.dispatchEvent && typeof window !== "undefined") {
256
+ window.dispatchEvent(new CustomEvent("modelnex-debug", { detail: { msg: message, data: payload } }));
257
+ }
258
+ }
259
+ function sanitizeActionList(actions) {
260
+ if (!Array.isArray(actions) || actions.length === 0) return void 0;
261
+ return actions.map(({ actionId }) => ({ actionId }));
262
+ }
263
+ function sanitizeAgentDebug(debug) {
264
+ if (!debug) return void 0;
265
+ const actions = sanitizeActionList(debug.actions);
266
+ const traces = Array.isArray(debug.traces) && debug.traces.length > 0 ? debug.traces.map((trace) => ({
267
+ step: trace.step,
268
+ actions: sanitizeActionList(trace.actions) ?? [],
269
+ results: Array.isArray(trace.results) && trace.results.length > 0 ? trace.results.map(({ actionId, success }) => ({ actionId, success })) : void 0
270
+ })) : void 0;
271
+ if ((!actions || actions.length === 0) && (!traces || traces.length === 0)) {
272
+ return void 0;
273
+ }
274
+ return {
275
+ ...actions ? { actions } : {},
276
+ ...traces ? { traces } : {}
277
+ };
278
+ }
279
+ function sanitizeChatMessages(messages, devMode) {
280
+ const includeDebug = isSdkDebugEnabled(devMode);
281
+ return messages.map((message) => {
282
+ if (message.role !== "assistant") {
283
+ return message;
284
+ }
285
+ return {
286
+ ...message,
287
+ debug: includeDebug ? sanitizeAgentDebug(message.debug) : void 0
288
+ };
289
+ });
290
+ }
291
+
292
+ // src/auto-extract.ts
240
293
  function simpleHash(str) {
241
294
  let hash = 0;
242
295
  for (let i = 0; i < str.length; i++) {
@@ -500,7 +553,7 @@ function extractInteractiveElements() {
500
553
  }
501
554
  return elements;
502
555
  }
503
- function useAutoExtract() {
556
+ function useAutoExtract(devMode) {
504
557
  const [elements, setElements] = (0, import_react2.useState)([]);
505
558
  const timerRef = (0, import_react2.useRef)(null);
506
559
  const lastSnapshotRef = (0, import_react2.useRef)("");
@@ -518,9 +571,11 @@ function useAutoExtract() {
518
571
  })));
519
572
  if (snapshot === lastSnapshotRef.current) return;
520
573
  lastSnapshotRef.current = snapshot;
521
- console.log(`[ModelNex AutoExtract] Found ${extracted.length} interactive elements`, extracted.map((e) => ({ fp: e.fingerprint, role: e.role, text: e.text.slice(0, 30) })));
574
+ emitSdkDebugLog("[ModelNex AutoExtract] Scan complete", {
575
+ elementCount: extracted.length
576
+ }, { devMode });
522
577
  setElements(extracted);
523
- }, []);
578
+ }, [devMode]);
524
579
  (0, import_react2.useEffect)(() => {
525
580
  const initialTimer = setTimeout(scan, 300);
526
581
  const observer = new MutationObserver((mutations) => {
@@ -835,7 +890,8 @@ function useModelNexSocket({
835
890
  setStagingFields,
836
891
  setExecutedFields,
837
892
  onSocketId,
838
- websiteId
893
+ websiteId,
894
+ devMode
839
895
  }) {
840
896
  const socketRef = (0, import_react3.useRef)(null);
841
897
  const actionsRef = (0, import_react3.useRef)(actions);
@@ -868,12 +924,14 @@ function useModelNexSocket({
868
924
  allTaggedElements: tagsRef.current ? Array.from(tagsRef.current.values()) : void 0
869
925
  });
870
926
  socket.on("connect", () => {
871
- console.log("ModelNex SDK: Connected to agent server", socket.id);
927
+ emitSdkDebugLog("[ModelNex SDK] Connected to agent server", {
928
+ socketId: socket.id ?? null
929
+ }, { devMode });
872
930
  onSocketId?.(socket.id || null);
873
931
  socket.emit("client:sync", buildSyncPayload());
874
932
  });
875
933
  socket.on("disconnect", () => {
876
- console.log("ModelNex SDK: Disconnected from agent server");
934
+ emitSdkDebugLog("[ModelNex SDK] Disconnected from agent server", void 0, { devMode });
877
935
  onSocketId?.(null);
878
936
  });
879
937
  socket.on("agent:request_context", () => {
@@ -899,12 +957,11 @@ function useModelNexSocket({
899
957
  reasoning
900
958
  }) => {
901
959
  const log = (msg, data) => {
902
- console.log(msg, data ?? "");
903
- window.dispatchEvent(new CustomEvent("modelnex-debug", { detail: { msg, data } }));
960
+ emitSdkDebugLog(msg, data, { devMode, dispatchEvent: true });
904
961
  };
905
962
  if (reasoning || params?.fingerprint) {
906
963
  window.dispatchEvent(new CustomEvent("modelnex-action-start", {
907
- detail: { actionId, fingerprint: params?.fingerprint ?? null, reasoning: reasoning ?? "" }
964
+ detail: { actionId, fingerprint: params?.fingerprint ?? null }
908
965
  }));
909
966
  }
910
967
  const sendResult = (success, result, error) => {
@@ -915,17 +972,18 @@ function useModelNexSocket({
915
972
  const currentActions = actionsRef.current;
916
973
  log("[SDK] agent:execute received", {
917
974
  actionId,
918
- params,
919
- registeredActions: Array.from(currentActions.keys())
975
+ executionId: executionId ?? null,
976
+ fieldId: fieldId ?? null,
977
+ hasParams: params != null
920
978
  });
921
979
  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"];
922
980
  const action = currentActions.get(actionId);
923
981
  if (action) {
924
982
  try {
925
983
  const validatedParams = action.schema.parse(params);
926
- log("[SDK] Executing", { actionId, validatedParams });
984
+ log("[SDK] Executing action", { actionId });
927
985
  const execResult = await action.execute(validatedParams);
928
- log("[SDK] Execute completed", { actionId });
986
+ log("[SDK] Action completed", { actionId });
929
987
  sendResult(true, execResult);
930
988
  if (NAV_ACTION_IDS.includes(actionId)) {
931
989
  requestAnimationFrame(() => {
@@ -949,25 +1007,29 @@ function useModelNexSocket({
949
1007
  }
950
1008
  } catch (err) {
951
1009
  const errMsg = err instanceof Error ? err.message : String(err);
952
- console.error(`[ModelNex SDK] Execution failed for ${actionId}`, err);
1010
+ console.error(`[ModelNex SDK] Execution failed for ${actionId}: ${errMsg}`);
1011
+ if (isSdkDebugEnabled(devMode)) {
1012
+ window.dispatchEvent(
1013
+ new CustomEvent("modelnex-debug", {
1014
+ detail: { msg: "[SDK] Execution failed", data: { actionId, error: errMsg } }
1015
+ })
1016
+ );
1017
+ }
1018
+ sendResult(false, void 0, errMsg);
1019
+ }
1020
+ } else {
1021
+ const errMsg = `Action not found: ${actionId}`;
1022
+ console.warn(`[ModelNex SDK] ${errMsg}`);
1023
+ if (isSdkDebugEnabled(devMode)) {
953
1024
  window.dispatchEvent(
954
1025
  new CustomEvent("modelnex-debug", {
955
- detail: { msg: "[SDK] Execution failed", data: { actionId, error: errMsg } }
1026
+ detail: {
1027
+ msg: "[SDK] Action not found",
1028
+ data: { actionId }
1029
+ }
956
1030
  })
957
1031
  );
958
- sendResult(false, void 0, errMsg);
959
1032
  }
960
- } else {
961
- const errMsg = `Action not found: ${actionId}`;
962
- console.warn("[ModelNex SDK]", errMsg, "Available:", Array.from(currentActions.keys()));
963
- window.dispatchEvent(
964
- new CustomEvent("modelnex-debug", {
965
- detail: {
966
- msg: "[SDK] Action not found",
967
- data: { actionId, available: Array.from(actions.keys()) }
968
- }
969
- })
970
- );
971
1033
  sendResult(false, void 0, errMsg);
972
1034
  }
973
1035
  }
@@ -2696,6 +2758,54 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
2696
2758
  // src/constants.ts
2697
2759
  var DEFAULT_MODELNEX_SERVER_URL = "https://api.modelnex.com";
2698
2760
 
2761
+ // src/utils/dev-mode.ts
2762
+ var DEV_MODE_KEY_GLOBAL_NAMES = ["__MODELNEX_DEV_MODE_KEY__", "MODELNEX_DEV_MODE_KEY"];
2763
+ var devModeValidationCache = /* @__PURE__ */ new Map();
2764
+ function normalizeDevModeKey(value) {
2765
+ if (typeof value !== "string") return void 0;
2766
+ const normalized = value.trim();
2767
+ return normalized || void 0;
2768
+ }
2769
+ function resolveInjectedDevModeKey() {
2770
+ if (typeof window === "undefined") return void 0;
2771
+ const browserWindow = window;
2772
+ for (const globalName of DEV_MODE_KEY_GLOBAL_NAMES) {
2773
+ const normalizedGlobalKey = normalizeDevModeKey(browserWindow[globalName]);
2774
+ if (normalizedGlobalKey) return normalizedGlobalKey;
2775
+ }
2776
+ const scriptInjectedKey = typeof document !== "undefined" ? normalizeDevModeKey(
2777
+ document.querySelector("script[data-modelnex-dev-mode-key]")?.dataset.modelnexDevModeKey
2778
+ ) : void 0;
2779
+ if (scriptInjectedKey) return scriptInjectedKey;
2780
+ return void 0;
2781
+ }
2782
+ async function validateInjectedDevModeKey(serverUrl, websiteId, devModeKey) {
2783
+ const normalizedWebsiteId = websiteId.trim();
2784
+ const normalizedDevModeKey = devModeKey.trim();
2785
+ if (!normalizedWebsiteId || !normalizedDevModeKey) return false;
2786
+ const cacheKey = `${serverUrl}::${normalizedWebsiteId}::${normalizedDevModeKey}`;
2787
+ const cached = devModeValidationCache.get(cacheKey);
2788
+ if (cached) {
2789
+ return cached;
2790
+ }
2791
+ const validationPromise = fetch(
2792
+ `${serverUrl.replace(/\/$/, "")}/api/websites/${encodeURIComponent(normalizedWebsiteId)}/dev-mode/verify`,
2793
+ {
2794
+ method: "POST",
2795
+ headers: {
2796
+ "Content-Type": "application/json"
2797
+ },
2798
+ body: JSON.stringify({ key: normalizedDevModeKey })
2799
+ }
2800
+ ).then(async (response) => {
2801
+ if (!response.ok) return false;
2802
+ const payload = await response.json().catch(() => null);
2803
+ return payload?.enabled === true;
2804
+ }).catch(() => false);
2805
+ devModeValidationCache.set(cacheKey, validationPromise);
2806
+ return validationPromise;
2807
+ }
2808
+
2699
2809
  // src/hooks/useRunCommand.ts
2700
2810
  var import_react9 = require("react");
2701
2811
  function searchTaggedElementsForQuery(store, query, limit = 8) {
@@ -2738,7 +2848,10 @@ function useRunCommand(serverUrlOverride) {
2738
2848
  if (!res.ok) {
2739
2849
  throw new Error(data?.error ?? `Request failed: ${res.status}`);
2740
2850
  }
2741
- return data;
2851
+ return {
2852
+ ...data,
2853
+ debug: sanitizeAgentDebug(data?.debug)
2854
+ };
2742
2855
  },
2743
2856
  [baseUrl, tagStore]
2744
2857
  );
@@ -3646,7 +3759,9 @@ function useTourPlayback({
3646
3759
  const socket = tourSocketPool.acquire(serverUrl);
3647
3760
  socketRef.current = socket;
3648
3761
  const handleConnect = () => {
3649
- console.log("[TourClient] Connected to tour agent server:", socket.id);
3762
+ emitSdkDebugLog("[TourClient] Connected to tour agent server", {
3763
+ socketId: socket.id ?? null
3764
+ }, { devMode: devModeRef.current });
3650
3765
  const profile = userProfileRef.current;
3651
3766
  const currentWebsiteId = websiteIdRef.current;
3652
3767
  if (currentWebsiteId && profile?.userId) {
@@ -3663,7 +3778,9 @@ function useTourPlayback({
3663
3778
  setServerState(payload);
3664
3779
  };
3665
3780
  const handleCommandCancel = (payload) => {
3666
- console.log("[TourClient] Received command_cancel:", payload);
3781
+ emitSdkDebugLog("[TourClient] Received command cancel", {
3782
+ commandBatchId: payload.commandBatchId ?? null
3783
+ }, { devMode: devModeRef.current });
3667
3784
  if (payload.commandBatchId && activeCommandBatchIdRef.current === payload.commandBatchId) {
3668
3785
  activeCommandBatchIdRef.current = null;
3669
3786
  activeExecutionTokenRef.current++;
@@ -3678,14 +3795,22 @@ function useTourPlayback({
3678
3795
  const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
3679
3796
  if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
3680
3797
  const activeType = experienceTypeRef.current;
3681
- console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
3798
+ emitSdkDebugLog("[TourClient] Ignoring command batch for inactive playback hook", {
3799
+ experienceType: activeType,
3800
+ stepIndex: payload.stepIndex
3801
+ }, { devMode: devModeRef.current });
3682
3802
  return;
3683
3803
  }
3684
3804
  const emitIfOpen = (ev, data) => {
3685
3805
  if (socketRef.current !== socket) return;
3686
3806
  emitSocketEvent(socket, ev, data);
3687
3807
  };
3688
- console.log("[TourClient] Received command batch:", payload.stepIndex, payload.commands);
3808
+ const commandBatchId = payload.commandBatchId ?? null;
3809
+ emitSdkDebugLog("[TourClient] Received command batch", {
3810
+ stepIndex: payload.stepIndex,
3811
+ commandCount: Array.isArray(payload.commands) ? payload.commands.length : 0,
3812
+ commandBatchId
3813
+ }, { devMode: devModeRef.current });
3689
3814
  runCleanup(pendingManualWaitCleanupRef.current);
3690
3815
  pendingManualWaitCleanupRef.current = null;
3691
3816
  if (voiceInputResolveRef.current) {
@@ -3695,7 +3820,6 @@ function useTourPlayback({
3695
3820
  }
3696
3821
  setPlaybackState("executing");
3697
3822
  commandInFlightRef.current = true;
3698
- const commandBatchId = payload.commandBatchId ?? null;
3699
3823
  turnIdRef.current = payload.turnId ?? turnIdRef.current;
3700
3824
  const clearCommandBatchId = () => {
3701
3825
  if (activeCommandBatchIdRef.current === commandBatchId) {
@@ -3741,7 +3865,7 @@ function useTourPlayback({
3741
3865
  }
3742
3866
  }
3743
3867
  if (!payload.commands || !Array.isArray(payload.commands)) {
3744
- console.warn("[TourClient] Payload commands is not an array:", payload);
3868
+ console.warn("[TourClient] Command batch payload was invalid.");
3745
3869
  commandInFlightRef.current = false;
3746
3870
  emitIfOpen("tour:action_result", {
3747
3871
  success: false,
@@ -3790,7 +3914,9 @@ function useTourPlayback({
3790
3914
  };
3791
3915
  const executeOne = async (action) => {
3792
3916
  assertNotInterrupted();
3793
- console.log("[TourClient] Executing action:", action?.type, action?.params ? JSON.stringify(action.params).slice(0, 120) : "");
3917
+ emitSdkDebugLog("[TourClient] Executing action", {
3918
+ actionType: action?.type ?? "unknown"
3919
+ }, { devMode: devModeRef.current });
3794
3920
  const currentStep = tourRef.current?.steps?.[stepIndexRef.current] ?? null;
3795
3921
  const executeTimeline = async (params = {}) => {
3796
3922
  const segments = Array.isArray(params.segments) ? params.segments : [];
@@ -3977,7 +4103,7 @@ function useTourPlayback({
3977
4103
  handleTourEnd();
3978
4104
  return { result: "ended" };
3979
4105
  }
3980
- console.warn("[TourClient] Unknown action type:", action?.type, "- skipping");
4106
+ console.warn(`[TourClient] Unknown action type: ${String(action?.type ?? "unknown")}. Skipping.`);
3981
4107
  return { result: "unknown_action_skipped" };
3982
4108
  };
3983
4109
  try {
@@ -4033,14 +4159,15 @@ function useTourPlayback({
4033
4159
  clearCommandBatchId();
4034
4160
  return;
4035
4161
  }
4036
- console.error("[TourClient] Command batch execution failed:", err);
4162
+ const errMsg = err instanceof Error ? err.message : String(err);
4163
+ console.error(`[TourClient] Command batch execution failed: ${errMsg}`);
4037
4164
  if (reviewModeRef.current && activeTourId && activePreviewRunId) {
4038
4165
  void logPreviewEvent(serverUrl, toursApiBaseRef.current, activeTourId, activePreviewRunId, websiteId, {
4039
4166
  stepOrder: stepIndexRef.current,
4040
4167
  eventType: "command_batch_failed",
4041
4168
  payload: {
4042
4169
  commandBatchId,
4043
- error: String(err),
4170
+ error: errMsg,
4044
4171
  partialResults: results
4045
4172
  },
4046
4173
  status: "active",
@@ -4050,7 +4177,7 @@ function useTourPlayback({
4050
4177
  emitIfOpen("tour:action_result", {
4051
4178
  success: false,
4052
4179
  reason: "execution_error",
4053
- error: String(err),
4180
+ error: errMsg,
4054
4181
  results,
4055
4182
  commandBatchId,
4056
4183
  runId: runIdRef.current,
@@ -4076,7 +4203,10 @@ function useTourPlayback({
4076
4203
  let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
4077
4204
  if (inputLikeWait && preferredWaitTarget && manualWaitTarget && manualWaitTarget !== preferredWaitTarget && !isEditableWaitTarget(manualWaitTarget) && isEditableWaitTarget(preferredWaitTarget)) {
4078
4205
  manualWaitTarget = preferredWaitTarget;
4079
- console.log("[TourClient] wait_for_input: preferring current editable target over hinted step target", manualWaitTarget);
4206
+ emitSdkDebugLog("[TourClient] wait_for_input preferred highlighted editable target", {
4207
+ stepIndex: stepIndexRef.current,
4208
+ event: waitEvent
4209
+ }, { devMode: devModeRef.current });
4080
4210
  }
4081
4211
  if (manualWaitTarget) {
4082
4212
  const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep);
@@ -4090,7 +4220,10 @@ function useTourPlayback({
4090
4220
  manualWaitPromise = manualWait.promise;
4091
4221
  manualWaitKind = manualWait.kind;
4092
4222
  pendingManualWaitCleanupRef.current = manualWait.cleanup;
4093
- console.log("[TourClient] wait_for_input: using current editable target as fallback wait target", preferredWaitTarget);
4223
+ emitSdkDebugLog("[TourClient] wait_for_input using fallback editable target", {
4224
+ stepIndex: stepIndexRef.current,
4225
+ event: waitEvent
4226
+ }, { devMode: devModeRef.current });
4094
4227
  }
4095
4228
  if (!manualWaitPromise && inputLikeWait) {
4096
4229
  const firstInput = document.querySelector(
@@ -4101,7 +4234,10 @@ function useTourPlayback({
4101
4234
  manualWaitPromise = manualWait.promise;
4102
4235
  manualWaitKind = manualWait.kind;
4103
4236
  pendingManualWaitCleanupRef.current = manualWait.cleanup;
4104
- console.log("[TourClient] wait_for_input: no target found, falling back to first visible editable element", firstInput);
4237
+ emitSdkDebugLog("[TourClient] wait_for_input falling back to first editable element", {
4238
+ stepIndex: stepIndexRef.current,
4239
+ event: waitEvent
4240
+ }, { devMode: devModeRef.current });
4105
4241
  }
4106
4242
  }
4107
4243
  setPlaybackState(manualWaitKind ? "waiting_input" : "waiting_voice");
@@ -4183,12 +4319,17 @@ function useTourPlayback({
4183
4319
  const tour = tourData.tourContext ?? tourRef.current;
4184
4320
  const expType = experienceTypeRef.current;
4185
4321
  if (tour?.type && tour.type !== expType) {
4186
- console.log(`[TourClient] Ignoring ${tour.type} start (this hook is for ${expType})`);
4322
+ emitSdkDebugLog("[TourClient] Ignoring tour start for mismatched experience type", {
4323
+ incomingType: tour.type,
4324
+ hookType: expType
4325
+ }, { devMode: devModeRef.current });
4187
4326
  return;
4188
4327
  }
4189
4328
  const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
4190
4329
  if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
4191
- console.log(`[TourClient] Ignoring ${expType} start because another hook already owns playback`);
4330
+ emitSdkDebugLog("[TourClient] Ignoring tour start because playback ownership is already claimed", {
4331
+ experienceType: expType
4332
+ }, { devMode: devModeRef.current });
4192
4333
  return;
4193
4334
  }
4194
4335
  claimedPlaybackOwnerKeyRef.current = ownerKey;
@@ -4213,6 +4354,8 @@ function useTourPlayback({
4213
4354
  },
4214
4355
  currentStepOrder: 0
4215
4356
  });
4357
+ } else if (tour?.id && userProfile?.userId) {
4358
+ void recordTourEvent(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, "started", websiteId);
4216
4359
  }
4217
4360
  try {
4218
4361
  const { generateMinifiedAOM: generateMinifiedAOM2 } = await Promise.resolve().then(() => (init_aom(), aom_exports));
@@ -4225,7 +4368,8 @@ function useTourPlayback({
4225
4368
  });
4226
4369
  }
4227
4370
  } catch (e) {
4228
- console.warn("[TourClient] Initial DOM sync failed:", e);
4371
+ const errMsg = e instanceof Error ? e.message : String(e);
4372
+ console.warn(`[TourClient] Initial DOM sync failed: ${errMsg}`);
4229
4373
  }
4230
4374
  };
4231
4375
  const handleTourUpdate = (payload) => {
@@ -4263,12 +4407,11 @@ function useTourPlayback({
4263
4407
  return;
4264
4408
  }
4265
4409
  if (isDev) {
4266
- console.log(`%c[ModelNex Tour] ${entry.type}`, "color: #3b82f6; font-weight: bold", entry);
4267
- if (typeof window !== "undefined") {
4268
- window.dispatchEvent(new CustomEvent("modelnex-debug", {
4269
- detail: { msg: `[Tour Timeline] ${entry.type}`, data: entry }
4270
- }));
4271
- }
4410
+ emitSdkDebugLog(`[Tour Timeline] ${entry.type}`, {
4411
+ stepIndex: entry?.data?.stepIndex ?? null,
4412
+ runId: entry?.data?.runId ?? null,
4413
+ turnId: entry?.data?.turnId ?? null
4414
+ }, { devMode: devModeRef.current, dispatchEvent: true });
4272
4415
  }
4273
4416
  };
4274
4417
  socket.on("connect", handleConnect);
@@ -4279,7 +4422,7 @@ function useTourPlayback({
4279
4422
  socket.on("tour:update", handleTourUpdate);
4280
4423
  socket.on("tour:end", handleTourEndEvent);
4281
4424
  socket.on("tour:debug_log", handleDebugLog);
4282
- console.log("[ModelNex SDK] Tour playback initialized. Debug logs enabled:", devModeRef.current || process.env.NODE_ENV === "development");
4425
+ emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
4283
4426
  return () => {
4284
4427
  socket.off("connect", handleConnect);
4285
4428
  socket.off("tour:server_state", handleServerState);
@@ -4390,6 +4533,8 @@ function useTourPlayback({
4390
4533
  status: "stopped",
4391
4534
  currentStepOrder: stepIndexRef.current
4392
4535
  });
4536
+ } else if (tourRef.current?.id && userProfile?.userId) {
4537
+ void recordTourEvent(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, "cancelled", websiteId);
4393
4538
  }
4394
4539
  if (reviewModeRef.current) {
4395
4540
  if (tourRef.current?.id) {
@@ -4462,7 +4607,9 @@ function useTourPlayback({
4462
4607
  isPlaybackActive: isActiveRef.current,
4463
4608
  startRequested: startRequestedRef.current
4464
4609
  })) {
4465
- console.log("[TourClient] Ignoring duplicate start request while playback is already active or starting:", tour.id);
4610
+ emitSdkDebugLog("[TourClient] Ignoring duplicate start request while playback is already active or starting", {
4611
+ tourId: tour.id
4612
+ }, { devMode: devModeRef.current });
4466
4613
  return;
4467
4614
  }
4468
4615
  setPendingTour(null);
@@ -4478,7 +4625,9 @@ function useTourPlayback({
4478
4625
  }
4479
4626
  const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
4480
4627
  if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
4481
- console.log("[TourClient] Ignoring duplicate start request because another hook already owns this experience:", tour.id);
4628
+ emitSdkDebugLog("[TourClient] Ignoring duplicate start request because another hook already owns this experience", {
4629
+ tourId: tour.id
4630
+ }, { devMode: devModeRef.current });
4482
4631
  return;
4483
4632
  }
4484
4633
  claimedPlaybackOwnerKeyRef.current = ownerKey;
@@ -4501,7 +4650,8 @@ function useTourPlayback({
4501
4650
  setPreviewRunId(previewRun.id);
4502
4651
  previewRunIdRef.current = previewRun.id;
4503
4652
  } catch (err) {
4504
- console.warn("[TourClient] Failed to create preview run:", err);
4653
+ const errMsg = err instanceof Error ? err.message : String(err);
4654
+ console.warn(`[TourClient] Failed to create preview run: ${errMsg}`);
4505
4655
  setReviewStatusMessage("Preview review logging is unavailable.");
4506
4656
  setPreviewRunId(null);
4507
4657
  previewRunIdRef.current = null;
@@ -4550,7 +4700,7 @@ function useTourPlayback({
4550
4700
  if (!tour) {
4551
4701
  clearActiveDraftPreview(experienceType);
4552
4702
  persistSuppressedDraftPreview(draftPreview);
4553
- console.warn("[ModelNex] Tour fetch failed:", tourId, experienceType, "(Check ModelNex server is running and CORS allows this origin)");
4703
+ console.warn(`[ModelNex] Tour fetch failed for ${experienceType}:${tourId}. Check the ModelNex server and CORS configuration.`);
4554
4704
  return;
4555
4705
  }
4556
4706
  if (cancelled) {
@@ -4573,7 +4723,8 @@ function useTourPlayback({
4573
4723
  await runTour(tour, previewOptions);
4574
4724
  }
4575
4725
  } catch (err) {
4576
- console.warn("[ModelNex] Tour test failed:", err);
4726
+ const errMsg = err instanceof Error ? err.message : String(err);
4727
+ console.warn(`[ModelNex] Tour test failed: ${errMsg}`);
4577
4728
  }
4578
4729
  })();
4579
4730
  return () => {
@@ -4683,7 +4834,8 @@ function useTourPlayback({
4683
4834
  setPlaybackState("executing");
4684
4835
  }
4685
4836
  } catch (err) {
4686
- console.warn("[TourClient] Failed to submit review feedback:", err);
4837
+ const errMsg = err instanceof Error ? err.message : String(err);
4838
+ console.warn(`[TourClient] Failed to submit review feedback: ${errMsg}`);
4687
4839
  setReviewStatusMessage("Unable to save correction.");
4688
4840
  } finally {
4689
4841
  setReviewSubmitting(false);
@@ -4724,23 +4876,29 @@ function useTourPlayback({
4724
4876
  }, [voice]);
4725
4877
  const handleVoiceInput = (0, import_react12.useCallback)((transcript) => {
4726
4878
  const text = transcript.trim();
4727
- console.log("[TourAgent] handleVoiceInput called with text:", text);
4879
+ emitSdkDebugLog("[TourAgent] Voice input received", {
4880
+ textLength: text.length
4881
+ }, { devMode: devModeRef.current });
4728
4882
  if (!text) return;
4729
4883
  if (interruptExecution(text)) {
4730
4884
  return;
4731
4885
  }
4732
4886
  if (voiceInputResolveRef.current) {
4733
- console.log("[TourAgent] Resolving loop waiting_voice with text:", text);
4887
+ emitSdkDebugLog("[TourAgent] Resolving waiting voice input", void 0, { devMode: devModeRef.current });
4734
4888
  voiceInputResolveRef.current(text);
4735
4889
  } else if (isSocketWritable(socketRef.current)) {
4736
- console.log("[TourAgent] Forwarding ambient voice to server:", text);
4890
+ emitSdkDebugLog("[TourAgent] Forwarding ambient voice input to server", {
4891
+ textLength: text.length
4892
+ }, { devMode: devModeRef.current });
4737
4893
  emitSocketEvent(socketRef.current, "tour:user_input", {
4738
4894
  transcript: text,
4739
4895
  runId: runIdRef.current,
4740
4896
  turnId: turnIdRef.current
4741
4897
  });
4742
4898
  } else {
4743
- console.log("[TourAgent] buffering voice input because socket is not ready:", text);
4899
+ emitSdkDebugLog("[TourAgent] Buffering voice input until socket is ready", {
4900
+ textLength: text.length
4901
+ }, { devMode: devModeRef.current });
4744
4902
  pendingInputBufRef.current = text;
4745
4903
  }
4746
4904
  }, [interruptExecution]);
@@ -5895,7 +6053,6 @@ function useVoice(serverUrl) {
5895
6053
  setIsListening(false);
5896
6054
  }, [stopLiveSttTransport]);
5897
6055
  const startListening = (0, import_react14.useCallback)((onResult, onInterruption, onError, options = {}) => {
5898
- console.log("[Voice] startListening called, options:", options);
5899
6056
  stopListening();
5900
6057
  listeningSessionIdRef.current = createVoiceDebugId("stt");
5901
6058
  listeningStartedAtRef.current = performance.now();
@@ -6109,7 +6266,9 @@ function useVoice(serverUrl) {
6109
6266
  recorder.start(LIVE_STT_TIMESLICE_MS);
6110
6267
  }
6111
6268
  setIsListening(true);
6112
- console.log("[Voice] Live STT pipeline active (Deepgram streaming + WebRTC loopback)");
6269
+ emitVoiceDebug("stt_live_pipeline_active", {
6270
+ listeningSessionId: listeningSessionIdRef.current
6271
+ });
6113
6272
  } catch (err) {
6114
6273
  const normalizedError = normalizeSttError(err);
6115
6274
  console.warn("[Voice] Failed to start live STT recorder:", normalizedError);
@@ -8931,7 +9090,7 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8931
9090
  },
8932
9091
  children: [
8933
9092
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
8934
- "Agent traces (",
9093
+ "Execution summary (",
8935
9094
  traces.length,
8936
9095
  " step",
8937
9096
  traces.length !== 1 ? "s" : "",
@@ -8947,21 +9106,9 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8947
9106
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: command })
8948
9107
  ] }),
8949
9108
  !hasTraceContent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px", padding: "8px", background: "#fef3c7", borderRadius: "4px", borderLeft: "3px solid #f59e0b" }, children: [
8950
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#92400e", marginBottom: "4px" }, children: "No trace data" }),
8951
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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." }),
8952
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "8px", background: "#fff", borderRadius: "4px", fontSize: "10px", overflow: "auto", maxHeight: "120px", color: "#27272a" }, children: JSON.stringify(debug, null, 2) })
9109
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#92400e", marginBottom: "4px" }, children: "No safe debug summary" }),
9110
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#71717a" }, children: "Verbose prompts, reasoning, and raw tool payloads are intentionally hidden by the SDK." })
8953
9111
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
8954
- debug.llmInput && traces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px" }, children: [
8955
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "Input \u2192 agent" }),
8956
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
8957
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "System" }),
8958
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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)" })
8959
- ] }),
8960
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
8961
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "User" }),
8962
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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)" })
8963
- ] })
8964
- ] }),
8965
9112
  traces.map((t) => {
8966
9113
  const isStepExpanded = expandedSteps.has(t.step);
8967
9114
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px", paddingBottom: "12px", borderBottom: "1px solid #e4e4e7" }, children: [
@@ -8994,31 +9141,6 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
8994
9141
  }
8995
9142
  ),
8996
9143
  isStepExpanded && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
8997
- t.reasoning && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
8998
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Reasoning" }),
8999
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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 })
9000
- ] }),
9001
- t.llmInput && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
9002
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
9003
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Input \u2192 agent" }),
9004
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "4px" }, children: [
9005
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "System" }),
9006
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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)" })
9007
- ] }),
9008
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
9009
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "User" }),
9010
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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)" })
9011
- ] })
9012
- ] }),
9013
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
9014
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Output \u2190 agent" }),
9015
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "60px", overflow: "auto" }, children: t.llmOutput ?? "(empty)" })
9016
- ] })
9017
- ] }),
9018
- !t.llmInput && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
9019
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "LLM output" }),
9020
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxHeight: "60px", overflow: "auto" }, children: t.llmOutput ?? "(empty)" })
9021
- ] }),
9022
9144
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
9023
9145
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Actions" }),
9024
9146
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "6px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: JSON.stringify(t.actions, null, 2) })
@@ -9030,10 +9152,6 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
9030
9152
  ] })
9031
9153
  ] }, t.step);
9032
9154
  }),
9033
- traces.length === 0 && debug.llmOutput && debug.llmOutput.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px" }, children: [
9034
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "LLM output" }),
9035
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: debug.llmOutput.join("\n\n") })
9036
- ] }),
9037
9155
  traces.length === 0 && (debug.actions?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px" }, children: [
9038
9156
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "Executed actions" }),
9039
9157
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: JSON.stringify(debug.actions, null, 2) })
@@ -9229,7 +9347,6 @@ function ModelNexChatBubble({
9229
9347
  tagStore.setTagsBatch(data.tags, true);
9230
9348
  lastAutoTaggedUrlRef.current = currentUrl;
9231
9349
  localStorage.setItem(storageKey, "true");
9232
- console.log(`[ModelNex] Auto-tagged ${data.tags.length} elements for ${currentUrl}`);
9233
9350
  }
9234
9351
  } catch (err) {
9235
9352
  console.warn("[ModelNex] Auto-tag error:", err);
@@ -9393,12 +9510,9 @@ function ModelNexChatBubble({
9393
9510
  };
9394
9511
  const listeningExperience = resolveTourListeningExperience(preferredExperience, listeningState);
9395
9512
  preferredListeningExperienceRef.current = listeningExperience;
9396
- console.log("[ChatBubble] startTourListening called. listeningState:", listeningState, "preferredExperience:", preferredExperience, "listeningExperience:", listeningExperience);
9397
9513
  if (!canStartTourListening(listeningState)) {
9398
- console.log("[ChatBubble] startTourListening bailed out early.");
9399
9514
  return;
9400
9515
  }
9401
- console.log("[ChatBubble] Proceeding to startTourListening...");
9402
9516
  sttActiveRef.current = true;
9403
9517
  updateTourSttError(null);
9404
9518
  resetFloatingLiveTranscriptSuppression();
@@ -9604,7 +9718,7 @@ function ModelNexChatBubble({
9604
9718
  content: summary || fallbackContent,
9605
9719
  summary: summary ?? void 0,
9606
9720
  nextSteps: nextSteps ?? void 0,
9607
- debug: data?.debug ?? void 0
9721
+ debug: devMode ? data?.debug ?? void 0 : void 0
9608
9722
  }
9609
9723
  ]);
9610
9724
  } catch (err) {
@@ -10647,7 +10761,7 @@ function ModelNexChatBubble({
10647
10761
  )
10648
10762
  }
10649
10763
  ),
10650
- msg.role === "assistant" && msg.debug && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentTraces, { debug: msg.debug, command: messages[i - 1]?.content ?? "" })
10764
+ msg.role === "assistant" && devMode && msg.debug && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentTraces, { debug: msg.debug, command: messages[i - 1]?.content ?? "" })
10651
10765
  ] }, i)),
10652
10766
  loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
10653
10767
  "div",
@@ -11625,7 +11739,6 @@ var ModelNexProvider = ({
11625
11739
  websiteId,
11626
11740
  userProfile,
11627
11741
  toursApiBase,
11628
- devMode,
11629
11742
  serverUrl: serverUrlProp
11630
11743
  }) => {
11631
11744
  const serverUrl = serverUrlProp ?? DEFAULT_MODELNEX_SERVER_URL;
@@ -11648,6 +11761,25 @@ var ModelNexProvider = ({
11648
11761
  const [voiceMuted, setVoiceMuted] = (0, import_react21.useState)(false);
11649
11762
  const [socketId, setSocketId] = (0, import_react21.useState)(null);
11650
11763
  const [actions, setActions] = (0, import_react21.useState)(/* @__PURE__ */ new Map());
11764
+ const [validatedBrowserDevMode, setValidatedBrowserDevMode] = (0, import_react21.useState)(false);
11765
+ const resolvedDevModeKey = (0, import_react21.useMemo)(() => resolveInjectedDevModeKey(), []);
11766
+ (0, import_react21.useEffect)(() => {
11767
+ let cancelled = false;
11768
+ if (!websiteId || !resolvedDevModeKey) {
11769
+ setValidatedBrowserDevMode(false);
11770
+ return () => {
11771
+ cancelled = true;
11772
+ };
11773
+ }
11774
+ void validateInjectedDevModeKey(serverUrl, websiteId, resolvedDevModeKey).then((enabled) => {
11775
+ if (cancelled) return;
11776
+ setValidatedBrowserDevMode(enabled);
11777
+ });
11778
+ return () => {
11779
+ cancelled = true;
11780
+ };
11781
+ }, [resolvedDevModeKey, serverUrl, websiteId]);
11782
+ const effectiveDevMode = validatedBrowserDevMode;
11651
11783
  const registerAction = (0, import_react21.useCallback)((action) => {
11652
11784
  setActions((prev) => {
11653
11785
  const next = new Map(prev);
@@ -11662,7 +11794,7 @@ var ModelNexProvider = ({
11662
11794
  return next;
11663
11795
  });
11664
11796
  }, []);
11665
- const extractedElements = useAutoExtract();
11797
+ const extractedElements = useAutoExtract(effectiveDevMode);
11666
11798
  const tagStore = useTagStore({ serverUrl, websiteId });
11667
11799
  useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId, toursApiBase, userProfile);
11668
11800
  const CHAT_STORAGE_KEY = "modelnex-chat-messages";
@@ -11671,21 +11803,32 @@ var ModelNexProvider = ({
11671
11803
  try {
11672
11804
  const stored = sessionStorage.getItem(CHAT_STORAGE_KEY);
11673
11805
  if (stored) {
11674
- setChatMessagesRaw(JSON.parse(stored));
11806
+ setChatMessagesRaw(sanitizeChatMessages(JSON.parse(stored), effectiveDevMode));
11675
11807
  }
11676
11808
  } catch {
11677
11809
  }
11678
- }, []);
11810
+ }, [effectiveDevMode]);
11811
+ (0, import_react21.useEffect)(() => {
11812
+ setChatMessagesRaw((prev) => {
11813
+ const next = sanitizeChatMessages(prev, effectiveDevMode);
11814
+ try {
11815
+ sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(next));
11816
+ } catch {
11817
+ }
11818
+ return next;
11819
+ });
11820
+ }, [effectiveDevMode]);
11679
11821
  const setChatMessages = (0, import_react21.useCallback)((action) => {
11680
11822
  setChatMessagesRaw((prev) => {
11681
- const next = typeof action === "function" ? action(prev) : action;
11823
+ const resolved = typeof action === "function" ? action(prev) : action;
11824
+ const next = sanitizeChatMessages(resolved, effectiveDevMode);
11682
11825
  try {
11683
11826
  sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(next));
11684
11827
  } catch {
11685
11828
  }
11686
11829
  return next;
11687
11830
  });
11688
- }, []);
11831
+ }, [effectiveDevMode]);
11689
11832
  useModelNexSocket({
11690
11833
  serverUrl,
11691
11834
  actions,
@@ -11697,7 +11840,8 @@ var ModelNexProvider = ({
11697
11840
  setStagingFields,
11698
11841
  setExecutedFields,
11699
11842
  onSocketId: setSocketId,
11700
- websiteId
11843
+ websiteId,
11844
+ devMode: effectiveDevMode
11701
11845
  });
11702
11846
  useFieldHighlight(stagingFields, executedFields, setExecutedFields);
11703
11847
  (0, import_react21.useEffect)(() => {
@@ -11732,9 +11876,9 @@ var ModelNexProvider = ({
11732
11876
  voiceMuted,
11733
11877
  setVoiceMuted,
11734
11878
  socketId,
11735
- devMode
11879
+ devMode: effectiveDevMode
11736
11880
  }),
11737
- [serverUrl, commandUrl, registerAction, unregisterAction, activeAgentActions, stagingFields, highlightActions, studioMode, recordingMode, extractedElements, tagStore, chatMessages, websiteId, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, toursApiBase, voiceMuted, socketId, devMode]
11881
+ [serverUrl, commandUrl, registerAction, unregisterAction, activeAgentActions, stagingFields, highlightActions, studioMode, recordingMode, extractedElements, tagStore, chatMessages, websiteId, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, toursApiBase, voiceMuted, socketId, effectiveDevMode]
11738
11882
  );
11739
11883
  return import_react21.default.createElement(
11740
11884
  ModelNexContext.Provider,