@modelnex/sdk 0.5.27 → 0.5.29
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/{dom-sync-L5KIP45X.mjs → dom-sync-GABDEODR.mjs} +0 -1
- package/dist/index.d.mts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +701 -257
- package/dist/index.mjs +705 -260
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
919
|
-
|
|
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
|
|
984
|
+
log("[SDK] Executing action", { actionId });
|
|
927
985
|
const execResult = await action.execute(validatedParams);
|
|
928
|
-
log("[SDK]
|
|
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}
|
|
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: {
|
|
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
|
}
|
|
@@ -1827,8 +1889,153 @@ async function captureScreenshot(selector) {
|
|
|
1827
1889
|
return canvas.toDataURL("image/png");
|
|
1828
1890
|
}
|
|
1829
1891
|
|
|
1892
|
+
// src/utils/experience-tool-bridge.ts
|
|
1893
|
+
var activeBridge = null;
|
|
1894
|
+
function registerExperienceToolBridge(bridge) {
|
|
1895
|
+
activeBridge = bridge;
|
|
1896
|
+
return () => {
|
|
1897
|
+
if (activeBridge === bridge) {
|
|
1898
|
+
activeBridge = null;
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
function getExperienceToolBridge() {
|
|
1903
|
+
return activeBridge;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
// src/utils/tourDiscovery.ts
|
|
1907
|
+
async function fetchTours(serverUrl, toursApiBase, websiteId, userType, userId, experienceType = "tour") {
|
|
1908
|
+
try {
|
|
1909
|
+
const params = new URLSearchParams({
|
|
1910
|
+
websiteId,
|
|
1911
|
+
userType
|
|
1912
|
+
});
|
|
1913
|
+
if (userId) params.set("userId", userId);
|
|
1914
|
+
if (experienceType !== "tour") params.set("type", experienceType);
|
|
1915
|
+
const res = await fetch(getTourApiUrl(serverUrl, toursApiBase, `/tours?${params.toString()}`));
|
|
1916
|
+
if (!res.ok) return [];
|
|
1917
|
+
const data = await res.json();
|
|
1918
|
+
return data.tours ?? [];
|
|
1919
|
+
} catch {
|
|
1920
|
+
return [];
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
async function fetchTourById(serverUrl, toursApiBase, tourId, experienceType = "tour", websiteId) {
|
|
1924
|
+
try {
|
|
1925
|
+
const url = new URL(withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}`), websiteId));
|
|
1926
|
+
if (experienceType !== "tour") {
|
|
1927
|
+
url.searchParams.set("type", experienceType);
|
|
1928
|
+
}
|
|
1929
|
+
const res = await fetch(url.toString());
|
|
1930
|
+
if (!res.ok) {
|
|
1931
|
+
return null;
|
|
1932
|
+
}
|
|
1933
|
+
const data = await res.json();
|
|
1934
|
+
return data.tour ?? null;
|
|
1935
|
+
} catch {
|
|
1936
|
+
return null;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
async function markTourComplete(serverUrl, toursApiBase, tourId, userId, experienceType = "tour", websiteId) {
|
|
1940
|
+
try {
|
|
1941
|
+
const url = new URL(withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}/complete`), websiteId));
|
|
1942
|
+
if (experienceType !== "tour") {
|
|
1943
|
+
url.searchParams.set("type", experienceType);
|
|
1944
|
+
}
|
|
1945
|
+
await fetch(url.toString(), {
|
|
1946
|
+
method: "POST",
|
|
1947
|
+
headers: { "Content-Type": "application/json" },
|
|
1948
|
+
body: JSON.stringify({ userId })
|
|
1949
|
+
});
|
|
1950
|
+
} catch {
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
async function markTourDismissed(serverUrl, toursApiBase, tourId, userId, experienceType = "tour", websiteId) {
|
|
1954
|
+
try {
|
|
1955
|
+
const url = new URL(withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}/dismiss`), websiteId));
|
|
1956
|
+
if (experienceType !== "tour") {
|
|
1957
|
+
url.searchParams.set("type", experienceType);
|
|
1958
|
+
}
|
|
1959
|
+
await fetch(url.toString(), {
|
|
1960
|
+
method: "POST",
|
|
1961
|
+
headers: { "Content-Type": "application/json" },
|
|
1962
|
+
body: JSON.stringify({ userId })
|
|
1963
|
+
});
|
|
1964
|
+
} catch {
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
async function recordTourEvent(serverUrl, toursApiBase, tourId, userId, eventType, websiteId) {
|
|
1968
|
+
try {
|
|
1969
|
+
const url = withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}/events`), websiteId);
|
|
1970
|
+
await fetch(url, {
|
|
1971
|
+
method: "POST",
|
|
1972
|
+
headers: { "Content-Type": "application/json" },
|
|
1973
|
+
body: JSON.stringify({ userId, eventType })
|
|
1974
|
+
});
|
|
1975
|
+
} catch {
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
function getToursBaseUrl(serverUrl, toursApiBase) {
|
|
1979
|
+
if (toursApiBase?.startsWith("/")) {
|
|
1980
|
+
return `${typeof window !== "undefined" ? window.location.origin : ""}${toursApiBase.replace(/\/$/, "")}`;
|
|
1981
|
+
}
|
|
1982
|
+
return serverUrl.replace(/\/$/, "");
|
|
1983
|
+
}
|
|
1984
|
+
function getTourApiUrl(serverUrl, toursApiBase, path) {
|
|
1985
|
+
const base = getToursBaseUrl(serverUrl, toursApiBase);
|
|
1986
|
+
return toursApiBase ? `${base}${path}` : `${base}/api${path}`;
|
|
1987
|
+
}
|
|
1988
|
+
function withWebsiteId(url, websiteId) {
|
|
1989
|
+
if (!websiteId) {
|
|
1990
|
+
return url;
|
|
1991
|
+
}
|
|
1992
|
+
const baseOrigin = typeof window !== "undefined" ? window.location.origin : "http://localhost";
|
|
1993
|
+
const resolved = new URL(url, baseOrigin);
|
|
1994
|
+
resolved.searchParams.set("websiteId", websiteId);
|
|
1995
|
+
return resolved.toString();
|
|
1996
|
+
}
|
|
1997
|
+
function normalizeTrigger(trigger) {
|
|
1998
|
+
if (trigger === "feature_launch" || trigger === "feature_unlocked" || trigger === "feature_unlock" || trigger === "new_feature") {
|
|
1999
|
+
return "feature_launch";
|
|
2000
|
+
}
|
|
2001
|
+
if (trigger === "return_visit") {
|
|
2002
|
+
return "return_visit";
|
|
2003
|
+
}
|
|
2004
|
+
return trigger ?? "first_visit";
|
|
2005
|
+
}
|
|
2006
|
+
function getStartPolicy(tour) {
|
|
2007
|
+
return tour.startPolicy ?? (normalizeTrigger(tour.trigger) === "manual" ? "immediate_start" : "prompt_only");
|
|
2008
|
+
}
|
|
2009
|
+
function getNotificationType(tour) {
|
|
2010
|
+
return tour.notificationType ?? "bubble_card";
|
|
2011
|
+
}
|
|
2012
|
+
function getUserProfileFeatures(userProfile) {
|
|
2013
|
+
const directFeatures = Array.isArray(userProfile.features) ? userProfile.features : [];
|
|
2014
|
+
const nestedFeatures = Array.isArray(userProfile.tourFacts?.features) ? userProfile.tourFacts.features : [];
|
|
2015
|
+
return [...new Set([...directFeatures, ...nestedFeatures].filter((value) => Boolean(value)))];
|
|
2016
|
+
}
|
|
2017
|
+
function isTourEligible(tour, userProfile) {
|
|
2018
|
+
switch (normalizeTrigger(tour.trigger)) {
|
|
2019
|
+
case "first_visit":
|
|
2020
|
+
return !!userProfile.isNewUser;
|
|
2021
|
+
case "return_visit":
|
|
2022
|
+
return userProfile.isNewUser === false;
|
|
2023
|
+
case "feature_launch": {
|
|
2024
|
+
const featureKey = tour.featureKey?.trim();
|
|
2025
|
+
if (!featureKey) return false;
|
|
2026
|
+
return getUserProfileFeatures(userProfile).includes(featureKey);
|
|
2027
|
+
}
|
|
2028
|
+
case "manual":
|
|
2029
|
+
return false;
|
|
2030
|
+
default:
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
|
|
1830
2035
|
// src/hooks/useBuiltinActions.ts
|
|
1831
2036
|
var resolutionCache = /* @__PURE__ */ new Map();
|
|
2037
|
+
var DEFAULT_WORKFLOW_SEARCH_LIMIT = 5;
|
|
2038
|
+
var WORKFLOW_TYPES = ["onboarding", "tour"];
|
|
1832
2039
|
function safeQueryAll2(selector) {
|
|
1833
2040
|
try {
|
|
1834
2041
|
return Array.from(document.querySelectorAll(selector)).filter(
|
|
@@ -2003,6 +2210,137 @@ function lastResortScan(fingerprint, options, elements) {
|
|
|
2003
2210
|
function cacheResolution(originalFp, selector, resolvedFp) {
|
|
2004
2211
|
resolutionCache.set(originalFp, { selector, resolvedFingerprint: resolvedFp });
|
|
2005
2212
|
}
|
|
2213
|
+
function normalizeWorkflowQuery(value) {
|
|
2214
|
+
return value.trim().toLowerCase();
|
|
2215
|
+
}
|
|
2216
|
+
function getWorkflowSearchText(tour) {
|
|
2217
|
+
const firstSteps = (tour.steps || []).slice(0, 6).flatMap((step) => [step.goal, step.narration, step.ask]).filter(Boolean).join(" ");
|
|
2218
|
+
return [
|
|
2219
|
+
tour.name,
|
|
2220
|
+
tour.type,
|
|
2221
|
+
tour.trigger,
|
|
2222
|
+
tour.startPolicy,
|
|
2223
|
+
tour.featureKey,
|
|
2224
|
+
tour.goal?.primaryAction,
|
|
2225
|
+
tour.goal?.successMetric,
|
|
2226
|
+
...tour.targetUserTypes,
|
|
2227
|
+
firstSteps
|
|
2228
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
2229
|
+
}
|
|
2230
|
+
function scoreWorkflowMatch(tour, query) {
|
|
2231
|
+
const normalizedQuery = normalizeWorkflowQuery(query);
|
|
2232
|
+
if (!normalizedQuery) return 1;
|
|
2233
|
+
const name = tour.name.toLowerCase();
|
|
2234
|
+
const featureKey = (tour.featureKey || "").toLowerCase();
|
|
2235
|
+
const haystack = getWorkflowSearchText(tour);
|
|
2236
|
+
if (name === normalizedQuery) return 140;
|
|
2237
|
+
if (featureKey && featureKey === normalizedQuery) return 125;
|
|
2238
|
+
if (name.includes(normalizedQuery)) return 115;
|
|
2239
|
+
if (featureKey && featureKey.includes(normalizedQuery)) return 100;
|
|
2240
|
+
if (haystack.includes(normalizedQuery)) return 85;
|
|
2241
|
+
const words = normalizedQuery.split(/\s+/).filter((word) => word.length > 1);
|
|
2242
|
+
if (words.length === 0) return 0;
|
|
2243
|
+
const matched = words.filter((word) => haystack.includes(word));
|
|
2244
|
+
if (matched.length === 0) return 0;
|
|
2245
|
+
const ratio = matched.length / words.length;
|
|
2246
|
+
return Math.round(ratio * 70);
|
|
2247
|
+
}
|
|
2248
|
+
function summarizeWorkflowStep(step, index) {
|
|
2249
|
+
const detail = step.goal || step.ask || step.narration || step.rawNarration || "No description";
|
|
2250
|
+
return `${index + 1}. [${step.type}] ${detail}`;
|
|
2251
|
+
}
|
|
2252
|
+
function formatWorkflowSummary(tour, options) {
|
|
2253
|
+
const stepLimit = options?.stepLimit ?? 4;
|
|
2254
|
+
const parts = [
|
|
2255
|
+
`**${tour.name}**`,
|
|
2256
|
+
`ID: ${tour.id}`,
|
|
2257
|
+
`Type: ${tour.type || "tour"}`,
|
|
2258
|
+
`Trigger: ${tour.trigger}`,
|
|
2259
|
+
`Start policy: ${tour.startPolicy || "prompt_only"}`,
|
|
2260
|
+
tour.featureKey ? `Feature key: ${tour.featureKey}` : null,
|
|
2261
|
+
tour.targetUserTypes?.length ? `Target users: ${tour.targetUserTypes.join(", ")}` : null,
|
|
2262
|
+
`Steps: ${tour.steps?.length ?? 0}`,
|
|
2263
|
+
tour.goal?.primaryAction ? `Primary action: ${tour.goal.primaryAction}` : null
|
|
2264
|
+
].filter(Boolean);
|
|
2265
|
+
if (options?.includeSteps) {
|
|
2266
|
+
const stepPreview = (tour.steps || []).slice(0, stepLimit).map((step, index) => summarizeWorkflowStep(step, index)).join("\n");
|
|
2267
|
+
if (stepPreview) {
|
|
2268
|
+
parts.push(`Steps preview:
|
|
2269
|
+
${stepPreview}`);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
return parts.join("\n");
|
|
2273
|
+
}
|
|
2274
|
+
function getRequestedWorkflowTypes(experienceType) {
|
|
2275
|
+
if (!experienceType || experienceType === "all") {
|
|
2276
|
+
return WORKFLOW_TYPES;
|
|
2277
|
+
}
|
|
2278
|
+
return [experienceType];
|
|
2279
|
+
}
|
|
2280
|
+
async function fetchAvailableWorkflows(getters, experienceType) {
|
|
2281
|
+
const serverUrl = getters.serverUrl();
|
|
2282
|
+
const websiteId = getters.websiteId();
|
|
2283
|
+
if (!serverUrl) {
|
|
2284
|
+
throw new Error("Server URL is not configured.");
|
|
2285
|
+
}
|
|
2286
|
+
if (!websiteId) {
|
|
2287
|
+
throw new Error("websiteId is required to search workflows.");
|
|
2288
|
+
}
|
|
2289
|
+
const userProfile = getters.userProfile();
|
|
2290
|
+
const userType = userProfile?.type || "";
|
|
2291
|
+
const userId = userProfile?.userId;
|
|
2292
|
+
const toursApiBase = getters.toursApiBase();
|
|
2293
|
+
const requestedTypes = getRequestedWorkflowTypes(experienceType);
|
|
2294
|
+
const lists = await Promise.all(
|
|
2295
|
+
requestedTypes.map(
|
|
2296
|
+
(type) => fetchTours(serverUrl, toursApiBase, websiteId, userType, userId, type)
|
|
2297
|
+
)
|
|
2298
|
+
);
|
|
2299
|
+
return lists.flatMap(
|
|
2300
|
+
(list, index) => list.map((tour) => ({
|
|
2301
|
+
...tour,
|
|
2302
|
+
type: tour.type || requestedTypes[index]
|
|
2303
|
+
}))
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
async function resolveWorkflowFromInput(getters, params) {
|
|
2307
|
+
const serverUrl = getters.serverUrl();
|
|
2308
|
+
const websiteId = getters.websiteId();
|
|
2309
|
+
const toursApiBase = getters.toursApiBase();
|
|
2310
|
+
if (!serverUrl) {
|
|
2311
|
+
throw new Error("Server URL is not configured.");
|
|
2312
|
+
}
|
|
2313
|
+
if (params.workflowId) {
|
|
2314
|
+
const requestedTypes = getRequestedWorkflowTypes(params.experienceType);
|
|
2315
|
+
for (const type of requestedTypes) {
|
|
2316
|
+
const workflow = await fetchTourById(serverUrl, toursApiBase, params.workflowId, type, websiteId);
|
|
2317
|
+
if (workflow) {
|
|
2318
|
+
return { workflow: { ...workflow, type: workflow.type || type } };
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
const query = params.name?.trim();
|
|
2323
|
+
if (!query) {
|
|
2324
|
+
return { workflow: null, reason: "Provide either workflowId or name." };
|
|
2325
|
+
}
|
|
2326
|
+
const workflows = await fetchAvailableWorkflows(getters, params.experienceType);
|
|
2327
|
+
const ranked = workflows.map((workflow) => ({ workflow, score: scoreWorkflowMatch(workflow, query) })).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score);
|
|
2328
|
+
if (ranked.length === 0) {
|
|
2329
|
+
return {
|
|
2330
|
+
workflow: null,
|
|
2331
|
+
reason: `No workflow matched "${query}". Try search_workflows first for available options.`
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
if (ranked.length > 1 && ranked[0].score === ranked[1].score) {
|
|
2335
|
+
return {
|
|
2336
|
+
workflow: null,
|
|
2337
|
+
reason: `Multiple workflows matched "${query}". Try view_workflow with an exact workflowId.
|
|
2338
|
+
|
|
2339
|
+
` + ranked.slice(0, 3).map(({ workflow }) => formatWorkflowSummary(workflow)).join("\n\n---\n\n")
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
return { workflow: ranked[0].workflow };
|
|
2343
|
+
}
|
|
2006
2344
|
var screenshotSchema = import_zod2.z.object({
|
|
2007
2345
|
selector: import_zod2.z.string().optional().describe("Optional CSS selector to capture a specific element. Omit to capture the full viewport.")
|
|
2008
2346
|
});
|
|
@@ -2283,15 +2621,91 @@ function createSearchTaggedElementsAction(getTagStore) {
|
|
|
2283
2621
|
}
|
|
2284
2622
|
};
|
|
2285
2623
|
}
|
|
2624
|
+
var workflowExperienceTypeSchema = import_zod2.z.enum(["onboarding", "tour", "all"]);
|
|
2625
|
+
var searchWorkflowsSchema = import_zod2.z.object({
|
|
2626
|
+
query: import_zod2.z.string().optional().describe("Optional workflow name, feature, or task phrase to search for. Omit to list available workflows."),
|
|
2627
|
+
experienceType: workflowExperienceTypeSchema.optional().describe("Which workflow type to search: onboarding, tour, or all. Default: onboarding."),
|
|
2628
|
+
limit: import_zod2.z.number().optional().describe(`Maximum number of workflows to return (default: ${DEFAULT_WORKFLOW_SEARCH_LIMIT}).`)
|
|
2629
|
+
});
|
|
2630
|
+
function createSearchWorkflowsAction(getters) {
|
|
2631
|
+
return {
|
|
2632
|
+
id: "search_workflows",
|
|
2633
|
+
description: "Search available workflows and tours that can be launched for the current website/user. Use this when the user asks for onboarding, a walkthrough, a tutorial, a guided setup, or a named workflow/tour.",
|
|
2634
|
+
schema: searchWorkflowsSchema,
|
|
2635
|
+
execute: async (params) => {
|
|
2636
|
+
const workflows = await fetchAvailableWorkflows(getters, params.experienceType ?? "onboarding");
|
|
2637
|
+
if (workflows.length === 0) {
|
|
2638
|
+
return "No workflows are currently available for this website/user.";
|
|
2639
|
+
}
|
|
2640
|
+
const query = params.query?.trim();
|
|
2641
|
+
const limit = params.limit ?? DEFAULT_WORKFLOW_SEARCH_LIMIT;
|
|
2642
|
+
const ranked = workflows.map((workflow) => ({
|
|
2643
|
+
workflow,
|
|
2644
|
+
score: query ? scoreWorkflowMatch(workflow, query) : 1
|
|
2645
|
+
})).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
2646
|
+
if (ranked.length === 0) {
|
|
2647
|
+
return `No workflows matched "${query}".`;
|
|
2648
|
+
}
|
|
2649
|
+
return ranked.map(({ workflow }) => formatWorkflowSummary(workflow)).join("\n\n---\n\n");
|
|
2650
|
+
}
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
var resolveWorkflowSchema = import_zod2.z.object({
|
|
2654
|
+
workflowId: import_zod2.z.string().optional().describe("Exact workflow id when known."),
|
|
2655
|
+
name: import_zod2.z.string().optional().describe("Workflow name or a close name match when the id is unknown."),
|
|
2656
|
+
experienceType: workflowExperienceTypeSchema.optional().describe("Which workflow type to search: onboarding, tour, or all. Default: onboarding.")
|
|
2657
|
+
});
|
|
2658
|
+
function createViewWorkflowAction(getters) {
|
|
2659
|
+
return {
|
|
2660
|
+
id: "view_workflow",
|
|
2661
|
+
description: "View a specific workflow or tour in more detail, including its id, trigger, start policy, and the first few steps. Use after search_workflows when you need to inspect a candidate before starting it.",
|
|
2662
|
+
schema: resolveWorkflowSchema,
|
|
2663
|
+
execute: async (params) => {
|
|
2664
|
+
const { workflow, reason } = await resolveWorkflowFromInput(getters, params);
|
|
2665
|
+
if (!workflow) {
|
|
2666
|
+
return reason || "Workflow not found.";
|
|
2667
|
+
}
|
|
2668
|
+
return formatWorkflowSummary(workflow, { includeSteps: true, stepLimit: 5 });
|
|
2669
|
+
}
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
var startWorkflowSchema = resolveWorkflowSchema.extend({
|
|
2673
|
+
reviewMode: import_zod2.z.boolean().optional().describe("Optional. Start in review mode instead of normal playback. Default: false.")
|
|
2674
|
+
});
|
|
2675
|
+
function createStartWorkflowAction(getters) {
|
|
2676
|
+
return {
|
|
2677
|
+
id: "start_workflow",
|
|
2678
|
+
description: "Start a specific workflow or tour in the chat experience. Use this after search_workflows or view_workflow when the user wants to begin a named guided workflow.",
|
|
2679
|
+
schema: startWorkflowSchema,
|
|
2680
|
+
execute: async (params) => {
|
|
2681
|
+
const { workflow, reason } = await resolveWorkflowFromInput(getters, params);
|
|
2682
|
+
if (!workflow) {
|
|
2683
|
+
return reason || "Workflow not found.";
|
|
2684
|
+
}
|
|
2685
|
+
const bridge = getExperienceToolBridge();
|
|
2686
|
+
if (!bridge) {
|
|
2687
|
+
return "Workflow launch is unavailable because no chat playback controller is mounted.";
|
|
2688
|
+
}
|
|
2689
|
+
bridge.startExperience(workflow, workflow.type, params.reviewMode ? {
|
|
2690
|
+
reviewMode: true,
|
|
2691
|
+
reviewMetadata: { trigger: "agent_tool" }
|
|
2692
|
+
} : void 0);
|
|
2693
|
+
return `Started workflow "${workflow.name}" (${workflow.id}).`;
|
|
2694
|
+
}
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2286
2697
|
var BUILTIN_ACTION_IDS = {
|
|
2287
2698
|
screenshot: "take_screenshot",
|
|
2288
2699
|
click: "click_element",
|
|
2289
2700
|
fill: "fill_input",
|
|
2290
2701
|
wait: "request_user_action",
|
|
2291
2702
|
searchDocs: "search_docs",
|
|
2292
|
-
searchTaggedElements: "search_tagged_elements"
|
|
2703
|
+
searchTaggedElements: "search_tagged_elements",
|
|
2704
|
+
searchWorkflows: "search_workflows",
|
|
2705
|
+
viewWorkflow: "view_workflow",
|
|
2706
|
+
startWorkflow: "start_workflow"
|
|
2293
2707
|
};
|
|
2294
|
-
function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId) {
|
|
2708
|
+
function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId, toursApiBase, userProfile) {
|
|
2295
2709
|
const registeredRef = (0, import_react8.useRef)(false);
|
|
2296
2710
|
const tagStoreRef = (0, import_react8.useRef)(tagStore);
|
|
2297
2711
|
tagStoreRef.current = tagStore;
|
|
@@ -2299,18 +2713,33 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
|
|
|
2299
2713
|
serverUrlRef.current = serverUrl;
|
|
2300
2714
|
const websiteIdRef = (0, import_react8.useRef)(websiteId);
|
|
2301
2715
|
websiteIdRef.current = websiteId;
|
|
2716
|
+
const toursApiBaseRef = (0, import_react8.useRef)(toursApiBase);
|
|
2717
|
+
toursApiBaseRef.current = toursApiBase;
|
|
2718
|
+
const userProfileRef = (0, import_react8.useRef)(userProfile);
|
|
2719
|
+
userProfileRef.current = userProfile;
|
|
2302
2720
|
(0, import_react8.useEffect)(() => {
|
|
2303
2721
|
if (registeredRef.current) return;
|
|
2304
2722
|
registeredRef.current = true;
|
|
2305
2723
|
const getTagStore = () => tagStoreRef.current;
|
|
2306
2724
|
const getServerUrl = () => serverUrlRef.current;
|
|
2307
2725
|
const getWebsiteId = () => websiteIdRef.current;
|
|
2726
|
+
const getToursApiBase = () => toursApiBaseRef.current;
|
|
2727
|
+
const getUserProfile = () => userProfileRef.current;
|
|
2728
|
+
const workflowToolGetters = {
|
|
2729
|
+
serverUrl: getServerUrl,
|
|
2730
|
+
websiteId: getWebsiteId,
|
|
2731
|
+
toursApiBase: getToursApiBase,
|
|
2732
|
+
userProfile: getUserProfile
|
|
2733
|
+
};
|
|
2308
2734
|
registerAction(BUILTIN_SCREENSHOT_ACTION);
|
|
2309
2735
|
registerAction(createClickAction(getTagStore));
|
|
2310
2736
|
registerAction(createFillAction(getTagStore));
|
|
2311
2737
|
registerAction(createWaitAction(getTagStore));
|
|
2312
2738
|
registerAction(createSearchDocsAction(getServerUrl, getWebsiteId));
|
|
2313
2739
|
registerAction(createSearchTaggedElementsAction(getTagStore));
|
|
2740
|
+
registerAction(createSearchWorkflowsAction(workflowToolGetters));
|
|
2741
|
+
registerAction(createViewWorkflowAction(workflowToolGetters));
|
|
2742
|
+
registerAction(createStartWorkflowAction(workflowToolGetters));
|
|
2314
2743
|
return () => {
|
|
2315
2744
|
unregisterAction(BUILTIN_SCREENSHOT_ACTION.id);
|
|
2316
2745
|
unregisterAction(BUILTIN_ACTION_IDS.click);
|
|
@@ -2318,6 +2747,9 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
|
|
|
2318
2747
|
unregisterAction(BUILTIN_ACTION_IDS.wait);
|
|
2319
2748
|
unregisterAction(BUILTIN_ACTION_IDS.searchDocs);
|
|
2320
2749
|
unregisterAction(BUILTIN_ACTION_IDS.searchTaggedElements);
|
|
2750
|
+
unregisterAction(BUILTIN_ACTION_IDS.searchWorkflows);
|
|
2751
|
+
unregisterAction(BUILTIN_ACTION_IDS.viewWorkflow);
|
|
2752
|
+
unregisterAction(BUILTIN_ACTION_IDS.startWorkflow);
|
|
2321
2753
|
registeredRef.current = false;
|
|
2322
2754
|
};
|
|
2323
2755
|
}, [registerAction, unregisterAction]);
|
|
@@ -2326,6 +2758,56 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
|
|
|
2326
2758
|
// src/constants.ts
|
|
2327
2759
|
var DEFAULT_MODELNEX_SERVER_URL = "https://api.modelnex.com";
|
|
2328
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(explicitDevModeKey) {
|
|
2770
|
+
const normalizedExplicitKey = normalizeDevModeKey(explicitDevModeKey);
|
|
2771
|
+
if (normalizedExplicitKey) return normalizedExplicitKey;
|
|
2772
|
+
if (typeof window === "undefined") return void 0;
|
|
2773
|
+
const browserWindow = window;
|
|
2774
|
+
for (const globalName of DEV_MODE_KEY_GLOBAL_NAMES) {
|
|
2775
|
+
const normalizedGlobalKey = normalizeDevModeKey(browserWindow[globalName]);
|
|
2776
|
+
if (normalizedGlobalKey) return normalizedGlobalKey;
|
|
2777
|
+
}
|
|
2778
|
+
const scriptInjectedKey = typeof document !== "undefined" ? normalizeDevModeKey(
|
|
2779
|
+
document.querySelector("script[data-modelnex-dev-mode-key]")?.dataset.modelnexDevModeKey
|
|
2780
|
+
) : void 0;
|
|
2781
|
+
if (scriptInjectedKey) return scriptInjectedKey;
|
|
2782
|
+
return void 0;
|
|
2783
|
+
}
|
|
2784
|
+
async function validateInjectedDevModeKey(serverUrl, websiteId, devModeKey) {
|
|
2785
|
+
const normalizedWebsiteId = websiteId.trim();
|
|
2786
|
+
const normalizedDevModeKey = devModeKey.trim();
|
|
2787
|
+
if (!normalizedWebsiteId || !normalizedDevModeKey) return false;
|
|
2788
|
+
const cacheKey = `${serverUrl}::${normalizedWebsiteId}::${normalizedDevModeKey}`;
|
|
2789
|
+
const cached = devModeValidationCache.get(cacheKey);
|
|
2790
|
+
if (cached) {
|
|
2791
|
+
return cached;
|
|
2792
|
+
}
|
|
2793
|
+
const validationPromise = fetch(
|
|
2794
|
+
`${serverUrl.replace(/\/$/, "")}/api/websites/${encodeURIComponent(normalizedWebsiteId)}/dev-mode/verify`,
|
|
2795
|
+
{
|
|
2796
|
+
method: "POST",
|
|
2797
|
+
headers: {
|
|
2798
|
+
"Content-Type": "application/json"
|
|
2799
|
+
},
|
|
2800
|
+
body: JSON.stringify({ key: normalizedDevModeKey })
|
|
2801
|
+
}
|
|
2802
|
+
).then(async (response) => {
|
|
2803
|
+
if (!response.ok) return false;
|
|
2804
|
+
const payload = await response.json().catch(() => null);
|
|
2805
|
+
return payload?.enabled === true;
|
|
2806
|
+
}).catch(() => false);
|
|
2807
|
+
devModeValidationCache.set(cacheKey, validationPromise);
|
|
2808
|
+
return validationPromise;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2329
2811
|
// src/hooks/useRunCommand.ts
|
|
2330
2812
|
var import_react9 = require("react");
|
|
2331
2813
|
function searchTaggedElementsForQuery(store, query, limit = 8) {
|
|
@@ -2368,7 +2850,10 @@ function useRunCommand(serverUrlOverride) {
|
|
|
2368
2850
|
if (!res.ok) {
|
|
2369
2851
|
throw new Error(data?.error ?? `Request failed: ${res.status}`);
|
|
2370
2852
|
}
|
|
2371
|
-
return
|
|
2853
|
+
return {
|
|
2854
|
+
...data,
|
|
2855
|
+
debug: sanitizeAgentDebug(data?.debug)
|
|
2856
|
+
};
|
|
2372
2857
|
},
|
|
2373
2858
|
[baseUrl, tagStore]
|
|
2374
2859
|
);
|
|
@@ -2572,135 +3057,6 @@ function readPreviewSessionSuppression() {
|
|
|
2572
3057
|
}
|
|
2573
3058
|
}
|
|
2574
3059
|
|
|
2575
|
-
// src/utils/tourDiscovery.ts
|
|
2576
|
-
async function fetchTours(serverUrl, toursApiBase, websiteId, userType, userId, experienceType = "tour") {
|
|
2577
|
-
try {
|
|
2578
|
-
const params = new URLSearchParams({
|
|
2579
|
-
websiteId,
|
|
2580
|
-
userType
|
|
2581
|
-
});
|
|
2582
|
-
if (userId) params.set("userId", userId);
|
|
2583
|
-
if (experienceType !== "tour") params.set("type", experienceType);
|
|
2584
|
-
const res = await fetch(getTourApiUrl(serverUrl, toursApiBase, `/tours?${params.toString()}`));
|
|
2585
|
-
if (!res.ok) return [];
|
|
2586
|
-
const data = await res.json();
|
|
2587
|
-
return data.tours ?? [];
|
|
2588
|
-
} catch {
|
|
2589
|
-
return [];
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
async function fetchTourById(serverUrl, toursApiBase, tourId, experienceType = "tour", websiteId) {
|
|
2593
|
-
try {
|
|
2594
|
-
const url = new URL(withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}`), websiteId));
|
|
2595
|
-
if (experienceType !== "tour") {
|
|
2596
|
-
url.searchParams.set("type", experienceType);
|
|
2597
|
-
}
|
|
2598
|
-
const res = await fetch(url.toString());
|
|
2599
|
-
if (!res.ok) {
|
|
2600
|
-
return null;
|
|
2601
|
-
}
|
|
2602
|
-
const data = await res.json();
|
|
2603
|
-
return data.tour ?? null;
|
|
2604
|
-
} catch {
|
|
2605
|
-
return null;
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
async function markTourComplete(serverUrl, toursApiBase, tourId, userId, experienceType = "tour", websiteId) {
|
|
2609
|
-
try {
|
|
2610
|
-
const url = new URL(withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}/complete`), websiteId));
|
|
2611
|
-
if (experienceType !== "tour") {
|
|
2612
|
-
url.searchParams.set("type", experienceType);
|
|
2613
|
-
}
|
|
2614
|
-
await fetch(url.toString(), {
|
|
2615
|
-
method: "POST",
|
|
2616
|
-
headers: { "Content-Type": "application/json" },
|
|
2617
|
-
body: JSON.stringify({ userId })
|
|
2618
|
-
});
|
|
2619
|
-
} catch {
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
async function markTourDismissed(serverUrl, toursApiBase, tourId, userId, experienceType = "tour", websiteId) {
|
|
2623
|
-
try {
|
|
2624
|
-
const url = new URL(withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}/dismiss`), websiteId));
|
|
2625
|
-
if (experienceType !== "tour") {
|
|
2626
|
-
url.searchParams.set("type", experienceType);
|
|
2627
|
-
}
|
|
2628
|
-
await fetch(url.toString(), {
|
|
2629
|
-
method: "POST",
|
|
2630
|
-
headers: { "Content-Type": "application/json" },
|
|
2631
|
-
body: JSON.stringify({ userId })
|
|
2632
|
-
});
|
|
2633
|
-
} catch {
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
async function recordTourEvent(serverUrl, toursApiBase, tourId, userId, eventType, websiteId) {
|
|
2637
|
-
try {
|
|
2638
|
-
const url = withWebsiteId(getTourApiUrl(serverUrl, toursApiBase, `/tours/${tourId}/events`), websiteId);
|
|
2639
|
-
await fetch(url, {
|
|
2640
|
-
method: "POST",
|
|
2641
|
-
headers: { "Content-Type": "application/json" },
|
|
2642
|
-
body: JSON.stringify({ userId, eventType })
|
|
2643
|
-
});
|
|
2644
|
-
} catch {
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
function getToursBaseUrl(serverUrl, toursApiBase) {
|
|
2648
|
-
if (toursApiBase?.startsWith("/")) {
|
|
2649
|
-
return `${typeof window !== "undefined" ? window.location.origin : ""}${toursApiBase.replace(/\/$/, "")}`;
|
|
2650
|
-
}
|
|
2651
|
-
return serverUrl.replace(/\/$/, "");
|
|
2652
|
-
}
|
|
2653
|
-
function getTourApiUrl(serverUrl, toursApiBase, path) {
|
|
2654
|
-
const base = getToursBaseUrl(serverUrl, toursApiBase);
|
|
2655
|
-
return toursApiBase ? `${base}${path}` : `${base}/api${path}`;
|
|
2656
|
-
}
|
|
2657
|
-
function withWebsiteId(url, websiteId) {
|
|
2658
|
-
if (!websiteId) {
|
|
2659
|
-
return url;
|
|
2660
|
-
}
|
|
2661
|
-
const baseOrigin = typeof window !== "undefined" ? window.location.origin : "http://localhost";
|
|
2662
|
-
const resolved = new URL(url, baseOrigin);
|
|
2663
|
-
resolved.searchParams.set("websiteId", websiteId);
|
|
2664
|
-
return resolved.toString();
|
|
2665
|
-
}
|
|
2666
|
-
function normalizeTrigger(trigger) {
|
|
2667
|
-
if (trigger === "feature_launch" || trigger === "feature_unlocked" || trigger === "feature_unlock" || trigger === "new_feature") {
|
|
2668
|
-
return "feature_launch";
|
|
2669
|
-
}
|
|
2670
|
-
if (trigger === "return_visit") {
|
|
2671
|
-
return "return_visit";
|
|
2672
|
-
}
|
|
2673
|
-
return trigger ?? "first_visit";
|
|
2674
|
-
}
|
|
2675
|
-
function getStartPolicy(tour) {
|
|
2676
|
-
return tour.startPolicy ?? (normalizeTrigger(tour.trigger) === "manual" ? "immediate_start" : "prompt_only");
|
|
2677
|
-
}
|
|
2678
|
-
function getNotificationType(tour) {
|
|
2679
|
-
return tour.notificationType ?? "bubble_card";
|
|
2680
|
-
}
|
|
2681
|
-
function getUserProfileFeatures(userProfile) {
|
|
2682
|
-
const directFeatures = Array.isArray(userProfile.features) ? userProfile.features : [];
|
|
2683
|
-
const nestedFeatures = Array.isArray(userProfile.tourFacts?.features) ? userProfile.tourFacts.features : [];
|
|
2684
|
-
return [...new Set([...directFeatures, ...nestedFeatures].filter((value) => Boolean(value)))];
|
|
2685
|
-
}
|
|
2686
|
-
function isTourEligible(tour, userProfile) {
|
|
2687
|
-
switch (normalizeTrigger(tour.trigger)) {
|
|
2688
|
-
case "first_visit":
|
|
2689
|
-
return !!userProfile.isNewUser;
|
|
2690
|
-
case "return_visit":
|
|
2691
|
-
return userProfile.isNewUser === false;
|
|
2692
|
-
case "feature_launch": {
|
|
2693
|
-
const featureKey = tour.featureKey?.trim();
|
|
2694
|
-
if (!featureKey) return false;
|
|
2695
|
-
return getUserProfileFeatures(userProfile).includes(featureKey);
|
|
2696
|
-
}
|
|
2697
|
-
case "manual":
|
|
2698
|
-
return false;
|
|
2699
|
-
default:
|
|
2700
|
-
return false;
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
|
|
2704
3060
|
// src/hooks/useTourPlayback.ts
|
|
2705
3061
|
var import_react12 = require("react");
|
|
2706
3062
|
|
|
@@ -3405,7 +3761,9 @@ function useTourPlayback({
|
|
|
3405
3761
|
const socket = tourSocketPool.acquire(serverUrl);
|
|
3406
3762
|
socketRef.current = socket;
|
|
3407
3763
|
const handleConnect = () => {
|
|
3408
|
-
|
|
3764
|
+
emitSdkDebugLog("[TourClient] Connected to tour agent server", {
|
|
3765
|
+
socketId: socket.id ?? null
|
|
3766
|
+
}, { devMode: devModeRef.current });
|
|
3409
3767
|
const profile = userProfileRef.current;
|
|
3410
3768
|
const currentWebsiteId = websiteIdRef.current;
|
|
3411
3769
|
if (currentWebsiteId && profile?.userId) {
|
|
@@ -3422,7 +3780,9 @@ function useTourPlayback({
|
|
|
3422
3780
|
setServerState(payload);
|
|
3423
3781
|
};
|
|
3424
3782
|
const handleCommandCancel = (payload) => {
|
|
3425
|
-
|
|
3783
|
+
emitSdkDebugLog("[TourClient] Received command cancel", {
|
|
3784
|
+
commandBatchId: payload.commandBatchId ?? null
|
|
3785
|
+
}, { devMode: devModeRef.current });
|
|
3426
3786
|
if (payload.commandBatchId && activeCommandBatchIdRef.current === payload.commandBatchId) {
|
|
3427
3787
|
activeCommandBatchIdRef.current = null;
|
|
3428
3788
|
activeExecutionTokenRef.current++;
|
|
@@ -3437,14 +3797,22 @@ function useTourPlayback({
|
|
|
3437
3797
|
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
3438
3798
|
if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3439
3799
|
const activeType = experienceTypeRef.current;
|
|
3440
|
-
|
|
3800
|
+
emitSdkDebugLog("[TourClient] Ignoring command batch for inactive playback hook", {
|
|
3801
|
+
experienceType: activeType,
|
|
3802
|
+
stepIndex: payload.stepIndex
|
|
3803
|
+
}, { devMode: devModeRef.current });
|
|
3441
3804
|
return;
|
|
3442
3805
|
}
|
|
3443
3806
|
const emitIfOpen = (ev, data) => {
|
|
3444
3807
|
if (socketRef.current !== socket) return;
|
|
3445
3808
|
emitSocketEvent(socket, ev, data);
|
|
3446
3809
|
};
|
|
3447
|
-
|
|
3810
|
+
const commandBatchId = payload.commandBatchId ?? null;
|
|
3811
|
+
emitSdkDebugLog("[TourClient] Received command batch", {
|
|
3812
|
+
stepIndex: payload.stepIndex,
|
|
3813
|
+
commandCount: Array.isArray(payload.commands) ? payload.commands.length : 0,
|
|
3814
|
+
commandBatchId
|
|
3815
|
+
}, { devMode: devModeRef.current });
|
|
3448
3816
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
3449
3817
|
pendingManualWaitCleanupRef.current = null;
|
|
3450
3818
|
if (voiceInputResolveRef.current) {
|
|
@@ -3454,7 +3822,6 @@ function useTourPlayback({
|
|
|
3454
3822
|
}
|
|
3455
3823
|
setPlaybackState("executing");
|
|
3456
3824
|
commandInFlightRef.current = true;
|
|
3457
|
-
const commandBatchId = payload.commandBatchId ?? null;
|
|
3458
3825
|
turnIdRef.current = payload.turnId ?? turnIdRef.current;
|
|
3459
3826
|
const clearCommandBatchId = () => {
|
|
3460
3827
|
if (activeCommandBatchIdRef.current === commandBatchId) {
|
|
@@ -3500,7 +3867,7 @@ function useTourPlayback({
|
|
|
3500
3867
|
}
|
|
3501
3868
|
}
|
|
3502
3869
|
if (!payload.commands || !Array.isArray(payload.commands)) {
|
|
3503
|
-
console.warn("[TourClient]
|
|
3870
|
+
console.warn("[TourClient] Command batch payload was invalid.");
|
|
3504
3871
|
commandInFlightRef.current = false;
|
|
3505
3872
|
emitIfOpen("tour:action_result", {
|
|
3506
3873
|
success: false,
|
|
@@ -3549,7 +3916,9 @@ function useTourPlayback({
|
|
|
3549
3916
|
};
|
|
3550
3917
|
const executeOne = async (action) => {
|
|
3551
3918
|
assertNotInterrupted();
|
|
3552
|
-
|
|
3919
|
+
emitSdkDebugLog("[TourClient] Executing action", {
|
|
3920
|
+
actionType: action?.type ?? "unknown"
|
|
3921
|
+
}, { devMode: devModeRef.current });
|
|
3553
3922
|
const currentStep = tourRef.current?.steps?.[stepIndexRef.current] ?? null;
|
|
3554
3923
|
const executeTimeline = async (params = {}) => {
|
|
3555
3924
|
const segments = Array.isArray(params.segments) ? params.segments : [];
|
|
@@ -3736,7 +4105,7 @@ function useTourPlayback({
|
|
|
3736
4105
|
handleTourEnd();
|
|
3737
4106
|
return { result: "ended" };
|
|
3738
4107
|
}
|
|
3739
|
-
console.warn(
|
|
4108
|
+
console.warn(`[TourClient] Unknown action type: ${String(action?.type ?? "unknown")}. Skipping.`);
|
|
3740
4109
|
return { result: "unknown_action_skipped" };
|
|
3741
4110
|
};
|
|
3742
4111
|
try {
|
|
@@ -3792,14 +4161,15 @@ function useTourPlayback({
|
|
|
3792
4161
|
clearCommandBatchId();
|
|
3793
4162
|
return;
|
|
3794
4163
|
}
|
|
3795
|
-
|
|
4164
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4165
|
+
console.error(`[TourClient] Command batch execution failed: ${errMsg}`);
|
|
3796
4166
|
if (reviewModeRef.current && activeTourId && activePreviewRunId) {
|
|
3797
4167
|
void logPreviewEvent(serverUrl, toursApiBaseRef.current, activeTourId, activePreviewRunId, websiteId, {
|
|
3798
4168
|
stepOrder: stepIndexRef.current,
|
|
3799
4169
|
eventType: "command_batch_failed",
|
|
3800
4170
|
payload: {
|
|
3801
4171
|
commandBatchId,
|
|
3802
|
-
error:
|
|
4172
|
+
error: errMsg,
|
|
3803
4173
|
partialResults: results
|
|
3804
4174
|
},
|
|
3805
4175
|
status: "active",
|
|
@@ -3809,7 +4179,7 @@ function useTourPlayback({
|
|
|
3809
4179
|
emitIfOpen("tour:action_result", {
|
|
3810
4180
|
success: false,
|
|
3811
4181
|
reason: "execution_error",
|
|
3812
|
-
error:
|
|
4182
|
+
error: errMsg,
|
|
3813
4183
|
results,
|
|
3814
4184
|
commandBatchId,
|
|
3815
4185
|
runId: runIdRef.current,
|
|
@@ -3835,7 +4205,10 @@ function useTourPlayback({
|
|
|
3835
4205
|
let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
|
|
3836
4206
|
if (inputLikeWait && preferredWaitTarget && manualWaitTarget && manualWaitTarget !== preferredWaitTarget && !isEditableWaitTarget(manualWaitTarget) && isEditableWaitTarget(preferredWaitTarget)) {
|
|
3837
4207
|
manualWaitTarget = preferredWaitTarget;
|
|
3838
|
-
|
|
4208
|
+
emitSdkDebugLog("[TourClient] wait_for_input preferred highlighted editable target", {
|
|
4209
|
+
stepIndex: stepIndexRef.current,
|
|
4210
|
+
event: waitEvent
|
|
4211
|
+
}, { devMode: devModeRef.current });
|
|
3839
4212
|
}
|
|
3840
4213
|
if (manualWaitTarget) {
|
|
3841
4214
|
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep);
|
|
@@ -3849,7 +4222,10 @@ function useTourPlayback({
|
|
|
3849
4222
|
manualWaitPromise = manualWait.promise;
|
|
3850
4223
|
manualWaitKind = manualWait.kind;
|
|
3851
4224
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
3852
|
-
|
|
4225
|
+
emitSdkDebugLog("[TourClient] wait_for_input using fallback editable target", {
|
|
4226
|
+
stepIndex: stepIndexRef.current,
|
|
4227
|
+
event: waitEvent
|
|
4228
|
+
}, { devMode: devModeRef.current });
|
|
3853
4229
|
}
|
|
3854
4230
|
if (!manualWaitPromise && inputLikeWait) {
|
|
3855
4231
|
const firstInput = document.querySelector(
|
|
@@ -3860,7 +4236,10 @@ function useTourPlayback({
|
|
|
3860
4236
|
manualWaitPromise = manualWait.promise;
|
|
3861
4237
|
manualWaitKind = manualWait.kind;
|
|
3862
4238
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
3863
|
-
|
|
4239
|
+
emitSdkDebugLog("[TourClient] wait_for_input falling back to first editable element", {
|
|
4240
|
+
stepIndex: stepIndexRef.current,
|
|
4241
|
+
event: waitEvent
|
|
4242
|
+
}, { devMode: devModeRef.current });
|
|
3864
4243
|
}
|
|
3865
4244
|
}
|
|
3866
4245
|
setPlaybackState(manualWaitKind ? "waiting_input" : "waiting_voice");
|
|
@@ -3942,12 +4321,17 @@ function useTourPlayback({
|
|
|
3942
4321
|
const tour = tourData.tourContext ?? tourRef.current;
|
|
3943
4322
|
const expType = experienceTypeRef.current;
|
|
3944
4323
|
if (tour?.type && tour.type !== expType) {
|
|
3945
|
-
|
|
4324
|
+
emitSdkDebugLog("[TourClient] Ignoring tour start for mismatched experience type", {
|
|
4325
|
+
incomingType: tour.type,
|
|
4326
|
+
hookType: expType
|
|
4327
|
+
}, { devMode: devModeRef.current });
|
|
3946
4328
|
return;
|
|
3947
4329
|
}
|
|
3948
4330
|
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
|
|
3949
4331
|
if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3950
|
-
|
|
4332
|
+
emitSdkDebugLog("[TourClient] Ignoring tour start because playback ownership is already claimed", {
|
|
4333
|
+
experienceType: expType
|
|
4334
|
+
}, { devMode: devModeRef.current });
|
|
3951
4335
|
return;
|
|
3952
4336
|
}
|
|
3953
4337
|
claimedPlaybackOwnerKeyRef.current = ownerKey;
|
|
@@ -3972,6 +4356,8 @@ function useTourPlayback({
|
|
|
3972
4356
|
},
|
|
3973
4357
|
currentStepOrder: 0
|
|
3974
4358
|
});
|
|
4359
|
+
} else if (tour?.id && userProfile?.userId) {
|
|
4360
|
+
void recordTourEvent(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, "started", websiteId);
|
|
3975
4361
|
}
|
|
3976
4362
|
try {
|
|
3977
4363
|
const { generateMinifiedAOM: generateMinifiedAOM2 } = await Promise.resolve().then(() => (init_aom(), aom_exports));
|
|
@@ -3984,7 +4370,8 @@ function useTourPlayback({
|
|
|
3984
4370
|
});
|
|
3985
4371
|
}
|
|
3986
4372
|
} catch (e) {
|
|
3987
|
-
|
|
4373
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
4374
|
+
console.warn(`[TourClient] Initial DOM sync failed: ${errMsg}`);
|
|
3988
4375
|
}
|
|
3989
4376
|
};
|
|
3990
4377
|
const handleTourUpdate = (payload) => {
|
|
@@ -4022,12 +4409,11 @@ function useTourPlayback({
|
|
|
4022
4409
|
return;
|
|
4023
4410
|
}
|
|
4024
4411
|
if (isDev) {
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
}
|
|
4412
|
+
emitSdkDebugLog(`[Tour Timeline] ${entry.type}`, {
|
|
4413
|
+
stepIndex: entry?.data?.stepIndex ?? null,
|
|
4414
|
+
runId: entry?.data?.runId ?? null,
|
|
4415
|
+
turnId: entry?.data?.turnId ?? null
|
|
4416
|
+
}, { devMode: devModeRef.current, dispatchEvent: true });
|
|
4031
4417
|
}
|
|
4032
4418
|
};
|
|
4033
4419
|
socket.on("connect", handleConnect);
|
|
@@ -4038,7 +4424,7 @@ function useTourPlayback({
|
|
|
4038
4424
|
socket.on("tour:update", handleTourUpdate);
|
|
4039
4425
|
socket.on("tour:end", handleTourEndEvent);
|
|
4040
4426
|
socket.on("tour:debug_log", handleDebugLog);
|
|
4041
|
-
|
|
4427
|
+
emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
|
|
4042
4428
|
return () => {
|
|
4043
4429
|
socket.off("connect", handleConnect);
|
|
4044
4430
|
socket.off("tour:server_state", handleServerState);
|
|
@@ -4149,6 +4535,8 @@ function useTourPlayback({
|
|
|
4149
4535
|
status: "stopped",
|
|
4150
4536
|
currentStepOrder: stepIndexRef.current
|
|
4151
4537
|
});
|
|
4538
|
+
} else if (tourRef.current?.id && userProfile?.userId) {
|
|
4539
|
+
void recordTourEvent(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, "cancelled", websiteId);
|
|
4152
4540
|
}
|
|
4153
4541
|
if (reviewModeRef.current) {
|
|
4154
4542
|
if (tourRef.current?.id) {
|
|
@@ -4221,7 +4609,9 @@ function useTourPlayback({
|
|
|
4221
4609
|
isPlaybackActive: isActiveRef.current,
|
|
4222
4610
|
startRequested: startRequestedRef.current
|
|
4223
4611
|
})) {
|
|
4224
|
-
|
|
4612
|
+
emitSdkDebugLog("[TourClient] Ignoring duplicate start request while playback is already active or starting", {
|
|
4613
|
+
tourId: tour.id
|
|
4614
|
+
}, { devMode: devModeRef.current });
|
|
4225
4615
|
return;
|
|
4226
4616
|
}
|
|
4227
4617
|
setPendingTour(null);
|
|
@@ -4237,7 +4627,9 @@ function useTourPlayback({
|
|
|
4237
4627
|
}
|
|
4238
4628
|
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
|
|
4239
4629
|
if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
4240
|
-
|
|
4630
|
+
emitSdkDebugLog("[TourClient] Ignoring duplicate start request because another hook already owns this experience", {
|
|
4631
|
+
tourId: tour.id
|
|
4632
|
+
}, { devMode: devModeRef.current });
|
|
4241
4633
|
return;
|
|
4242
4634
|
}
|
|
4243
4635
|
claimedPlaybackOwnerKeyRef.current = ownerKey;
|
|
@@ -4260,7 +4652,8 @@ function useTourPlayback({
|
|
|
4260
4652
|
setPreviewRunId(previewRun.id);
|
|
4261
4653
|
previewRunIdRef.current = previewRun.id;
|
|
4262
4654
|
} catch (err) {
|
|
4263
|
-
|
|
4655
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4656
|
+
console.warn(`[TourClient] Failed to create preview run: ${errMsg}`);
|
|
4264
4657
|
setReviewStatusMessage("Preview review logging is unavailable.");
|
|
4265
4658
|
setPreviewRunId(null);
|
|
4266
4659
|
previewRunIdRef.current = null;
|
|
@@ -4309,7 +4702,7 @@ function useTourPlayback({
|
|
|
4309
4702
|
if (!tour) {
|
|
4310
4703
|
clearActiveDraftPreview(experienceType);
|
|
4311
4704
|
persistSuppressedDraftPreview(draftPreview);
|
|
4312
|
-
console.warn(
|
|
4705
|
+
console.warn(`[ModelNex] Tour fetch failed for ${experienceType}:${tourId}. Check the ModelNex server and CORS configuration.`);
|
|
4313
4706
|
return;
|
|
4314
4707
|
}
|
|
4315
4708
|
if (cancelled) {
|
|
@@ -4332,7 +4725,8 @@ function useTourPlayback({
|
|
|
4332
4725
|
await runTour(tour, previewOptions);
|
|
4333
4726
|
}
|
|
4334
4727
|
} catch (err) {
|
|
4335
|
-
|
|
4728
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4729
|
+
console.warn(`[ModelNex] Tour test failed: ${errMsg}`);
|
|
4336
4730
|
}
|
|
4337
4731
|
})();
|
|
4338
4732
|
return () => {
|
|
@@ -4442,7 +4836,8 @@ function useTourPlayback({
|
|
|
4442
4836
|
setPlaybackState("executing");
|
|
4443
4837
|
}
|
|
4444
4838
|
} catch (err) {
|
|
4445
|
-
|
|
4839
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4840
|
+
console.warn(`[TourClient] Failed to submit review feedback: ${errMsg}`);
|
|
4446
4841
|
setReviewStatusMessage("Unable to save correction.");
|
|
4447
4842
|
} finally {
|
|
4448
4843
|
setReviewSubmitting(false);
|
|
@@ -4483,23 +4878,29 @@ function useTourPlayback({
|
|
|
4483
4878
|
}, [voice]);
|
|
4484
4879
|
const handleVoiceInput = (0, import_react12.useCallback)((transcript) => {
|
|
4485
4880
|
const text = transcript.trim();
|
|
4486
|
-
|
|
4881
|
+
emitSdkDebugLog("[TourAgent] Voice input received", {
|
|
4882
|
+
textLength: text.length
|
|
4883
|
+
}, { devMode: devModeRef.current });
|
|
4487
4884
|
if (!text) return;
|
|
4488
4885
|
if (interruptExecution(text)) {
|
|
4489
4886
|
return;
|
|
4490
4887
|
}
|
|
4491
4888
|
if (voiceInputResolveRef.current) {
|
|
4492
|
-
|
|
4889
|
+
emitSdkDebugLog("[TourAgent] Resolving waiting voice input", void 0, { devMode: devModeRef.current });
|
|
4493
4890
|
voiceInputResolveRef.current(text);
|
|
4494
4891
|
} else if (isSocketWritable(socketRef.current)) {
|
|
4495
|
-
|
|
4892
|
+
emitSdkDebugLog("[TourAgent] Forwarding ambient voice input to server", {
|
|
4893
|
+
textLength: text.length
|
|
4894
|
+
}, { devMode: devModeRef.current });
|
|
4496
4895
|
emitSocketEvent(socketRef.current, "tour:user_input", {
|
|
4497
4896
|
transcript: text,
|
|
4498
4897
|
runId: runIdRef.current,
|
|
4499
4898
|
turnId: turnIdRef.current
|
|
4500
4899
|
});
|
|
4501
4900
|
} else {
|
|
4502
|
-
|
|
4901
|
+
emitSdkDebugLog("[TourAgent] Buffering voice input until socket is ready", {
|
|
4902
|
+
textLength: text.length
|
|
4903
|
+
}, { devMode: devModeRef.current });
|
|
4503
4904
|
pendingInputBufRef.current = text;
|
|
4504
4905
|
}
|
|
4505
4906
|
}, [interruptExecution]);
|
|
@@ -5654,7 +6055,6 @@ function useVoice(serverUrl) {
|
|
|
5654
6055
|
setIsListening(false);
|
|
5655
6056
|
}, [stopLiveSttTransport]);
|
|
5656
6057
|
const startListening = (0, import_react14.useCallback)((onResult, onInterruption, onError, options = {}) => {
|
|
5657
|
-
console.log("[Voice] startListening called, options:", options);
|
|
5658
6058
|
stopListening();
|
|
5659
6059
|
listeningSessionIdRef.current = createVoiceDebugId("stt");
|
|
5660
6060
|
listeningStartedAtRef.current = performance.now();
|
|
@@ -5868,7 +6268,9 @@ function useVoice(serverUrl) {
|
|
|
5868
6268
|
recorder.start(LIVE_STT_TIMESLICE_MS);
|
|
5869
6269
|
}
|
|
5870
6270
|
setIsListening(true);
|
|
5871
|
-
|
|
6271
|
+
emitVoiceDebug("stt_live_pipeline_active", {
|
|
6272
|
+
listeningSessionId: listeningSessionIdRef.current
|
|
6273
|
+
});
|
|
5872
6274
|
} catch (err) {
|
|
5873
6275
|
const normalizedError = normalizeSttError(err);
|
|
5874
6276
|
console.warn("[Voice] Failed to start live STT recorder:", normalizedError);
|
|
@@ -8616,6 +9018,7 @@ function Tooltip({ children, title }) {
|
|
|
8616
9018
|
);
|
|
8617
9019
|
}
|
|
8618
9020
|
var BUBBLE_EXPANDED_STORAGE_KEY = "modelnex-chat-bubble-expanded";
|
|
9021
|
+
var BUBBLE_DOCKED_STORAGE_KEY = "modelnex-chat-bubble-docked";
|
|
8619
9022
|
var TOUR_MINIMIZED_STORAGE_KEY = "modelnex-tour-bubble-minimized";
|
|
8620
9023
|
var BotIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "var(--modelnex-bubble-icon-size, 20px)", height: "var(--modelnex-bubble-icon-size, 20px)", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
8621
9024
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }),
|
|
@@ -8629,6 +9032,18 @@ var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { wid
|
|
|
8629
9032
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
8630
9033
|
] });
|
|
8631
9034
|
var MinimizeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M8 18h8" }) });
|
|
9035
|
+
var DockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
9036
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 3v10" }),
|
|
9037
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m8 9 4 4 4-4" }),
|
|
9038
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4 17h16" }),
|
|
9039
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M6 21h12" })
|
|
9040
|
+
] });
|
|
9041
|
+
var UndockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
9042
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 21V11" }),
|
|
9043
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m8 15 4-4 4 4" }),
|
|
9044
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4 7h16" }),
|
|
9045
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M6 3h12" })
|
|
9046
|
+
] });
|
|
8632
9047
|
var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
8633
9048
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M3 6h18" }),
|
|
8634
9049
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
@@ -8677,7 +9092,7 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
|
|
|
8677
9092
|
},
|
|
8678
9093
|
children: [
|
|
8679
9094
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
|
|
8680
|
-
"
|
|
9095
|
+
"Execution summary (",
|
|
8681
9096
|
traces.length,
|
|
8682
9097
|
" step",
|
|
8683
9098
|
traces.length !== 1 ? "s" : "",
|
|
@@ -8693,21 +9108,9 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
|
|
|
8693
9108
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, padding: "8px", background: "#f4f4f5", borderRadius: "4px", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: command })
|
|
8694
9109
|
] }),
|
|
8695
9110
|
!hasTraceContent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px", padding: "8px", background: "#fef3c7", borderRadius: "4px", borderLeft: "3px solid #f59e0b" }, children: [
|
|
8696
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#92400e", marginBottom: "4px" }, children: "No
|
|
8697
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#71717a"
|
|
8698
|
-
/* @__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) })
|
|
9111
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#92400e", marginBottom: "4px" }, children: "No safe debug summary" }),
|
|
9112
|
+
/* @__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." })
|
|
8699
9113
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
8700
|
-
debug.llmInput && traces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px" }, children: [
|
|
8701
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "Input \u2192 agent" }),
|
|
8702
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
|
|
8703
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "System" }),
|
|
8704
|
-
/* @__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)" })
|
|
8705
|
-
] }),
|
|
8706
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
8707
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "User" }),
|
|
8708
|
-
/* @__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)" })
|
|
8709
|
-
] })
|
|
8710
|
-
] }),
|
|
8711
9114
|
traces.map((t) => {
|
|
8712
9115
|
const isStepExpanded = expandedSteps.has(t.step);
|
|
8713
9116
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px", paddingBottom: "12px", borderBottom: "1px solid #e4e4e7" }, children: [
|
|
@@ -8740,31 +9143,6 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
|
|
|
8740
9143
|
}
|
|
8741
9144
|
),
|
|
8742
9145
|
isStepExpanded && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
8743
|
-
t.reasoning && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
|
|
8744
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Reasoning" }),
|
|
8745
|
-
/* @__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 })
|
|
8746
|
-
] }),
|
|
8747
|
-
t.llmInput && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
8748
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
|
|
8749
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Input \u2192 agent" }),
|
|
8750
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "4px" }, children: [
|
|
8751
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "System" }),
|
|
8752
|
-
/* @__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)" })
|
|
8753
|
-
] }),
|
|
8754
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
8755
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "10px", color: "#a1a1aa", marginBottom: "2px" }, children: "User" }),
|
|
8756
|
-
/* @__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)" })
|
|
8757
|
-
] })
|
|
8758
|
-
] }),
|
|
8759
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
|
|
8760
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Output \u2190 agent" }),
|
|
8761
|
-
/* @__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)" })
|
|
8762
|
-
] })
|
|
8763
|
-
] }),
|
|
8764
|
-
!t.llmInput && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
|
|
8765
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "LLM output" }),
|
|
8766
|
-
/* @__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)" })
|
|
8767
|
-
] }),
|
|
8768
9146
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "6px" }, children: [
|
|
8769
9147
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#71717a", marginBottom: "2px" }, children: "Actions" }),
|
|
8770
9148
|
/* @__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) })
|
|
@@ -8776,10 +9154,6 @@ function AgentTraces({ debug, command, defaultExpanded = true }) {
|
|
|
8776
9154
|
] })
|
|
8777
9155
|
] }, t.step);
|
|
8778
9156
|
}),
|
|
8779
|
-
traces.length === 0 && debug.llmOutput && debug.llmOutput.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px" }, children: [
|
|
8780
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "LLM output" }),
|
|
8781
|
-
/* @__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") })
|
|
8782
|
-
] }),
|
|
8783
9157
|
traces.length === 0 && (debug.actions?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: "12px" }, children: [
|
|
8784
9158
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: "#3f3f46", marginBottom: "4px" }, children: "Executed actions" }),
|
|
8785
9159
|
/* @__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) })
|
|
@@ -8849,6 +9223,7 @@ function ModelNexChatBubble({
|
|
|
8849
9223
|
const ctx = (0, import_react18.useContext)(ModelNexContext);
|
|
8850
9224
|
const [hydrated, setHydrated] = (0, import_react18.useState)(false);
|
|
8851
9225
|
const [expanded, setExpanded] = (0, import_react18.useState)(false);
|
|
9226
|
+
const [docked, setDocked] = (0, import_react18.useState)(false);
|
|
8852
9227
|
const [input, setInput] = (0, import_react18.useState)("");
|
|
8853
9228
|
const messages = ctx?.chatMessages ?? [];
|
|
8854
9229
|
const setMessages = ctx?.setChatMessages ?? (() => {
|
|
@@ -8903,6 +9278,11 @@ function ModelNexChatBubble({
|
|
|
8903
9278
|
const activePlayback = playbackController.playback;
|
|
8904
9279
|
const activeExperienceType = playbackController.activeExperienceType;
|
|
8905
9280
|
const startingExperienceType = playbackController.startingExperienceType;
|
|
9281
|
+
(0, import_react18.useEffect)(() => {
|
|
9282
|
+
return registerExperienceToolBridge({
|
|
9283
|
+
startExperience: playbackController.startExperience
|
|
9284
|
+
});
|
|
9285
|
+
}, [playbackController.startExperience]);
|
|
8906
9286
|
const createPlaybackView = (0, import_react18.useCallback)((experienceType) => {
|
|
8907
9287
|
const isActiveExperience = activePlayback.isActive && activeExperienceType === experienceType;
|
|
8908
9288
|
const pendingTour = playbackController.pendingPrompt?.experienceType === experienceType ? playbackController.pendingPrompt.tour : null;
|
|
@@ -8969,7 +9349,6 @@ function ModelNexChatBubble({
|
|
|
8969
9349
|
tagStore.setTagsBatch(data.tags, true);
|
|
8970
9350
|
lastAutoTaggedUrlRef.current = currentUrl;
|
|
8971
9351
|
localStorage.setItem(storageKey, "true");
|
|
8972
|
-
console.log(`[ModelNex] Auto-tagged ${data.tags.length} elements for ${currentUrl}`);
|
|
8973
9352
|
}
|
|
8974
9353
|
} catch (err) {
|
|
8975
9354
|
console.warn("[ModelNex] Auto-tag error:", err);
|
|
@@ -9000,8 +9379,10 @@ function ModelNexChatBubble({
|
|
|
9000
9379
|
setHydrated(true);
|
|
9001
9380
|
try {
|
|
9002
9381
|
setExpanded(sessionStorage.getItem(BUBBLE_EXPANDED_STORAGE_KEY) === "true");
|
|
9382
|
+
setDocked(sessionStorage.getItem(BUBBLE_DOCKED_STORAGE_KEY) === "true");
|
|
9003
9383
|
} catch {
|
|
9004
9384
|
setExpanded(false);
|
|
9385
|
+
setDocked(false);
|
|
9005
9386
|
}
|
|
9006
9387
|
}, []);
|
|
9007
9388
|
const sttActiveRef = (0, import_react18.useRef)(false);
|
|
@@ -9061,6 +9442,13 @@ function ModelNexChatBubble({
|
|
|
9061
9442
|
} catch {
|
|
9062
9443
|
}
|
|
9063
9444
|
}, [tourPlayback.isActive, onboardingPlayback.isActive]);
|
|
9445
|
+
const setDockedState = (0, import_react18.useCallback)((next) => {
|
|
9446
|
+
setDocked(next);
|
|
9447
|
+
try {
|
|
9448
|
+
sessionStorage.setItem(BUBBLE_DOCKED_STORAGE_KEY, String(next));
|
|
9449
|
+
} catch {
|
|
9450
|
+
}
|
|
9451
|
+
}, []);
|
|
9064
9452
|
(0, import_react18.useEffect)(() => {
|
|
9065
9453
|
if (shouldAutoExpandForPendingPrompt({
|
|
9066
9454
|
pendingPrompt,
|
|
@@ -9124,12 +9512,9 @@ function ModelNexChatBubble({
|
|
|
9124
9512
|
};
|
|
9125
9513
|
const listeningExperience = resolveTourListeningExperience(preferredExperience, listeningState);
|
|
9126
9514
|
preferredListeningExperienceRef.current = listeningExperience;
|
|
9127
|
-
console.log("[ChatBubble] startTourListening called. listeningState:", listeningState, "preferredExperience:", preferredExperience, "listeningExperience:", listeningExperience);
|
|
9128
9515
|
if (!canStartTourListening(listeningState)) {
|
|
9129
|
-
console.log("[ChatBubble] startTourListening bailed out early.");
|
|
9130
9516
|
return;
|
|
9131
9517
|
}
|
|
9132
|
-
console.log("[ChatBubble] Proceeding to startTourListening...");
|
|
9133
9518
|
sttActiveRef.current = true;
|
|
9134
9519
|
updateTourSttError(null);
|
|
9135
9520
|
resetFloatingLiveTranscriptSuppression();
|
|
@@ -9335,7 +9720,7 @@ function ModelNexChatBubble({
|
|
|
9335
9720
|
content: summary || fallbackContent,
|
|
9336
9721
|
summary: summary ?? void 0,
|
|
9337
9722
|
nextSteps: nextSteps ?? void 0,
|
|
9338
|
-
debug: data?.debug ?? void 0
|
|
9723
|
+
debug: devMode ? data?.debug ?? void 0 : void 0
|
|
9339
9724
|
}
|
|
9340
9725
|
]);
|
|
9341
9726
|
} catch (err) {
|
|
@@ -9422,11 +9807,13 @@ function ModelNexChatBubble({
|
|
|
9422
9807
|
fontFamily: "var(--modelnex-font)",
|
|
9423
9808
|
...themeStyles
|
|
9424
9809
|
};
|
|
9810
|
+
const desktopPanelHeight = docked ? "min(calc(100vh - 48px), calc(var(--modelnex-panel-max-height, 600px) + 180px))" : "var(--modelnex-panel-max-height, 600px)";
|
|
9811
|
+
const desktopPanelMaxHeight = docked ? "calc(100vh - 48px)" : "calc(100vh - 120px)";
|
|
9425
9812
|
const panelStyle = {
|
|
9426
9813
|
width: isMobile ? "100%" : "var(--modelnex-panel-width, 380px)",
|
|
9427
9814
|
maxWidth: "calc(100vw - 32px)",
|
|
9428
|
-
height: isMobile ? "100%" :
|
|
9429
|
-
maxHeight: "
|
|
9815
|
+
height: isMobile ? "100%" : desktopPanelHeight,
|
|
9816
|
+
maxHeight: isMobile ? "100%" : desktopPanelMaxHeight,
|
|
9430
9817
|
borderRadius: isMobile ? "0" : "var(--modelnex-radius-panel, 20px)",
|
|
9431
9818
|
border: isMobile ? "none" : "1px solid var(--modelnex-border, #e4e4e7)",
|
|
9432
9819
|
background: "var(--modelnex-bg, #ffffff)",
|
|
@@ -9578,6 +9965,31 @@ function ModelNexChatBubble({
|
|
|
9578
9965
|
] })
|
|
9579
9966
|
] }) }),
|
|
9580
9967
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
|
|
9968
|
+
!isMobile && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tooltip, { title: docked ? "Undock" : "Dock", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
9969
|
+
"button",
|
|
9970
|
+
{
|
|
9971
|
+
onClick: () => setDockedState(!docked),
|
|
9972
|
+
style: {
|
|
9973
|
+
padding: "8px 10px",
|
|
9974
|
+
borderRadius: "10px",
|
|
9975
|
+
border: "none",
|
|
9976
|
+
background: docked ? "rgba(79,70,229,0.08)" : "transparent",
|
|
9977
|
+
cursor: "pointer",
|
|
9978
|
+
color: docked ? "var(--modelnex-accent, #4f46e5)" : "#71717a",
|
|
9979
|
+
transition: "all 0.2s",
|
|
9980
|
+
display: "flex",
|
|
9981
|
+
alignItems: "center",
|
|
9982
|
+
gap: "6px",
|
|
9983
|
+
fontSize: "12px",
|
|
9984
|
+
fontWeight: 700
|
|
9985
|
+
},
|
|
9986
|
+
"aria-label": docked ? "Undock chat bubble" : "Dock chat bubble",
|
|
9987
|
+
children: [
|
|
9988
|
+
docked ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UndockIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DockIcon, {}),
|
|
9989
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: docked ? "Undock" : "Dock" })
|
|
9990
|
+
]
|
|
9991
|
+
}
|
|
9992
|
+
) }),
|
|
9581
9993
|
(tourPlayback.isActive || onboardingPlayback.isActive || voice.isSpeaking) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tooltip, { title: voice.isMuted ? "Unmute" : "Mute", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
9582
9994
|
"button",
|
|
9583
9995
|
{
|
|
@@ -10351,7 +10763,7 @@ function ModelNexChatBubble({
|
|
|
10351
10763
|
)
|
|
10352
10764
|
}
|
|
10353
10765
|
),
|
|
10354
|
-
msg.role === "assistant" && msg.debug && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentTraces, { debug: msg.debug, command: messages[i - 1]?.content ?? "" })
|
|
10766
|
+
msg.role === "assistant" && devMode && msg.debug && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentTraces, { debug: msg.debug, command: messages[i - 1]?.content ?? "" })
|
|
10355
10767
|
] }, i)),
|
|
10356
10768
|
loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
10357
10769
|
"div",
|
|
@@ -11330,6 +11742,7 @@ var ModelNexProvider = ({
|
|
|
11330
11742
|
userProfile,
|
|
11331
11743
|
toursApiBase,
|
|
11332
11744
|
devMode,
|
|
11745
|
+
devModeKey,
|
|
11333
11746
|
serverUrl: serverUrlProp
|
|
11334
11747
|
}) => {
|
|
11335
11748
|
const serverUrl = serverUrlProp ?? DEFAULT_MODELNEX_SERVER_URL;
|
|
@@ -11352,6 +11765,25 @@ var ModelNexProvider = ({
|
|
|
11352
11765
|
const [voiceMuted, setVoiceMuted] = (0, import_react21.useState)(false);
|
|
11353
11766
|
const [socketId, setSocketId] = (0, import_react21.useState)(null);
|
|
11354
11767
|
const [actions, setActions] = (0, import_react21.useState)(/* @__PURE__ */ new Map());
|
|
11768
|
+
const [validatedBrowserDevMode, setValidatedBrowserDevMode] = (0, import_react21.useState)(false);
|
|
11769
|
+
const resolvedDevModeKey = (0, import_react21.useMemo)(() => resolveInjectedDevModeKey(devModeKey), [devModeKey]);
|
|
11770
|
+
(0, import_react21.useEffect)(() => {
|
|
11771
|
+
let cancelled = false;
|
|
11772
|
+
if (!websiteId || !resolvedDevModeKey) {
|
|
11773
|
+
setValidatedBrowserDevMode(false);
|
|
11774
|
+
return () => {
|
|
11775
|
+
cancelled = true;
|
|
11776
|
+
};
|
|
11777
|
+
}
|
|
11778
|
+
void validateInjectedDevModeKey(serverUrl, websiteId, resolvedDevModeKey).then((enabled) => {
|
|
11779
|
+
if (cancelled) return;
|
|
11780
|
+
setValidatedBrowserDevMode(enabled);
|
|
11781
|
+
});
|
|
11782
|
+
return () => {
|
|
11783
|
+
cancelled = true;
|
|
11784
|
+
};
|
|
11785
|
+
}, [resolvedDevModeKey, serverUrl, websiteId]);
|
|
11786
|
+
const effectiveDevMode = Boolean(devMode) || validatedBrowserDevMode;
|
|
11355
11787
|
const registerAction = (0, import_react21.useCallback)((action) => {
|
|
11356
11788
|
setActions((prev) => {
|
|
11357
11789
|
const next = new Map(prev);
|
|
@@ -11366,30 +11798,41 @@ var ModelNexProvider = ({
|
|
|
11366
11798
|
return next;
|
|
11367
11799
|
});
|
|
11368
11800
|
}, []);
|
|
11369
|
-
const extractedElements = useAutoExtract();
|
|
11801
|
+
const extractedElements = useAutoExtract(effectiveDevMode);
|
|
11370
11802
|
const tagStore = useTagStore({ serverUrl, websiteId });
|
|
11371
|
-
useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId);
|
|
11803
|
+
useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId, toursApiBase, userProfile);
|
|
11372
11804
|
const CHAT_STORAGE_KEY = "modelnex-chat-messages";
|
|
11373
11805
|
const [chatMessages, setChatMessagesRaw] = (0, import_react21.useState)([]);
|
|
11374
11806
|
(0, import_react21.useEffect)(() => {
|
|
11375
11807
|
try {
|
|
11376
11808
|
const stored = sessionStorage.getItem(CHAT_STORAGE_KEY);
|
|
11377
11809
|
if (stored) {
|
|
11378
|
-
setChatMessagesRaw(JSON.parse(stored));
|
|
11810
|
+
setChatMessagesRaw(sanitizeChatMessages(JSON.parse(stored), effectiveDevMode));
|
|
11379
11811
|
}
|
|
11380
11812
|
} catch {
|
|
11381
11813
|
}
|
|
11382
|
-
}, []);
|
|
11814
|
+
}, [effectiveDevMode]);
|
|
11815
|
+
(0, import_react21.useEffect)(() => {
|
|
11816
|
+
setChatMessagesRaw((prev) => {
|
|
11817
|
+
const next = sanitizeChatMessages(prev, effectiveDevMode);
|
|
11818
|
+
try {
|
|
11819
|
+
sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(next));
|
|
11820
|
+
} catch {
|
|
11821
|
+
}
|
|
11822
|
+
return next;
|
|
11823
|
+
});
|
|
11824
|
+
}, [effectiveDevMode]);
|
|
11383
11825
|
const setChatMessages = (0, import_react21.useCallback)((action) => {
|
|
11384
11826
|
setChatMessagesRaw((prev) => {
|
|
11385
|
-
const
|
|
11827
|
+
const resolved = typeof action === "function" ? action(prev) : action;
|
|
11828
|
+
const next = sanitizeChatMessages(resolved, effectiveDevMode);
|
|
11386
11829
|
try {
|
|
11387
11830
|
sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(next));
|
|
11388
11831
|
} catch {
|
|
11389
11832
|
}
|
|
11390
11833
|
return next;
|
|
11391
11834
|
});
|
|
11392
|
-
}, []);
|
|
11835
|
+
}, [effectiveDevMode]);
|
|
11393
11836
|
useModelNexSocket({
|
|
11394
11837
|
serverUrl,
|
|
11395
11838
|
actions,
|
|
@@ -11401,7 +11844,8 @@ var ModelNexProvider = ({
|
|
|
11401
11844
|
setStagingFields,
|
|
11402
11845
|
setExecutedFields,
|
|
11403
11846
|
onSocketId: setSocketId,
|
|
11404
|
-
websiteId
|
|
11847
|
+
websiteId,
|
|
11848
|
+
devMode: effectiveDevMode
|
|
11405
11849
|
});
|
|
11406
11850
|
useFieldHighlight(stagingFields, executedFields, setExecutedFields);
|
|
11407
11851
|
(0, import_react21.useEffect)(() => {
|
|
@@ -11436,9 +11880,9 @@ var ModelNexProvider = ({
|
|
|
11436
11880
|
voiceMuted,
|
|
11437
11881
|
setVoiceMuted,
|
|
11438
11882
|
socketId,
|
|
11439
|
-
devMode
|
|
11883
|
+
devMode: effectiveDevMode
|
|
11440
11884
|
}),
|
|
11441
|
-
[serverUrl, commandUrl, registerAction, unregisterAction, activeAgentActions, stagingFields, highlightActions, studioMode, recordingMode, extractedElements, tagStore, chatMessages, websiteId, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, toursApiBase, voiceMuted, socketId,
|
|
11885
|
+
[serverUrl, commandUrl, registerAction, unregisterAction, activeAgentActions, stagingFields, highlightActions, studioMode, recordingMode, extractedElements, tagStore, chatMessages, websiteId, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, toursApiBase, voiceMuted, socketId, effectiveDevMode]
|
|
11442
11886
|
);
|
|
11443
11887
|
return import_react21.default.createElement(
|
|
11444
11888
|
ModelNexContext.Provider,
|