@modelnex/sdk 0.5.43 → 0.5.45

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.
@@ -2,7 +2,7 @@ import {
2
2
  clearAOMMap,
3
3
  generateMinifiedAOM,
4
4
  getElementByUid
5
- } from "./chunk-H4LUY7LI.mjs";
5
+ } from "./chunk-SXGINP3O.mjs";
6
6
  export {
7
7
  clearAOMMap,
8
8
  generateMinifiedAOM,
@@ -0,0 +1,683 @@
1
+ // src/utils/editable-controls.ts
2
+ var EDITABLE_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox"]);
3
+ var EDITABLE_CONTROL_SELECTOR = [
4
+ 'input:not([type="hidden"])',
5
+ "textarea",
6
+ "select",
7
+ '[contenteditable="true"]',
8
+ '[role="textbox"]',
9
+ '[role="combobox"]'
10
+ ].join(", ");
11
+ function getTagName(element) {
12
+ return String(element?.tagName || "").toLowerCase();
13
+ }
14
+ function getRole(element) {
15
+ const role = element && typeof element.getAttribute === "function" ? element.getAttribute("role") : null;
16
+ return String(role || "").toLowerCase();
17
+ }
18
+ function getLabelControl(element) {
19
+ const labelCtor = typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null;
20
+ if (labelCtor && element instanceof labelCtor && element.control instanceof HTMLElement) {
21
+ return element.control;
22
+ }
23
+ return null;
24
+ }
25
+ function isValueBearingElement(element) {
26
+ if (!element) return false;
27
+ const inputCtor = typeof HTMLInputElement !== "undefined" ? HTMLInputElement : null;
28
+ if (inputCtor && element instanceof inputCtor) return true;
29
+ const textareaCtor = typeof HTMLTextAreaElement !== "undefined" ? HTMLTextAreaElement : null;
30
+ if (textareaCtor && element instanceof textareaCtor) return true;
31
+ const selectCtor = typeof HTMLSelectElement !== "undefined" ? HTMLSelectElement : null;
32
+ if (selectCtor && element instanceof selectCtor) return true;
33
+ return ["input", "textarea", "select"].includes(getTagName(element));
34
+ }
35
+ function findEditableControlInShadowRoot(root) {
36
+ if (!root) return null;
37
+ if (root.shadowRoot) {
38
+ const found = root.shadowRoot.querySelector(EDITABLE_CONTROL_SELECTOR);
39
+ if (found) {
40
+ return resolveEditableControlElement(found);
41
+ }
42
+ }
43
+ for (const child of Array.from(root.children)) {
44
+ if (child instanceof HTMLElement) {
45
+ const found = findEditableControlInShadowRoot(child);
46
+ if (found) return found;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ function resolveEditableControlElement(element) {
52
+ const labelControl = getLabelControl(element);
53
+ if (labelControl) {
54
+ return resolveEditableControlElement(labelControl);
55
+ }
56
+ if (isValueBearingElement(element) || element.isContentEditable) {
57
+ return element;
58
+ }
59
+ const role = getRole(element);
60
+ if (EDITABLE_ROLES.has(role)) {
61
+ const nested2 = element.querySelector(EDITABLE_CONTROL_SELECTOR);
62
+ if (nested2 && nested2 !== element) {
63
+ return resolveEditableControlElement(nested2);
64
+ }
65
+ const shadowNested2 = findEditableControlInShadowRoot(element);
66
+ return shadowNested2 ?? element;
67
+ }
68
+ const nested = element.querySelector(EDITABLE_CONTROL_SELECTOR);
69
+ if (nested) {
70
+ return resolveEditableControlElement(nested);
71
+ }
72
+ const shadowNested = findEditableControlInShadowRoot(element);
73
+ return shadowNested ?? element;
74
+ }
75
+ function readEditableControlValue(element, options = {}) {
76
+ const target = resolveEditableControlElement(element);
77
+ const maskPasswords = options.maskPasswords !== false;
78
+ if (isValueBearingElement(target)) {
79
+ const type = String(target.type || "").toLowerCase();
80
+ if (maskPasswords && type === "password") {
81
+ return "***";
82
+ }
83
+ return String(target.value || "").trim().slice(0, 100);
84
+ }
85
+ const genericValue = target.value;
86
+ if (typeof genericValue === "string") {
87
+ return genericValue.trim().slice(0, 100);
88
+ }
89
+ if (typeof genericValue === "number" && Number.isFinite(genericValue)) {
90
+ return String(genericValue).trim().slice(0, 100);
91
+ }
92
+ if (target.isContentEditable) {
93
+ return String(target.textContent || "").trim().slice(0, 100);
94
+ }
95
+ const ariaValueText = target.getAttribute("aria-valuetext");
96
+ if (ariaValueText?.trim()) {
97
+ return ariaValueText.trim().slice(0, 100);
98
+ }
99
+ const ariaValueNow = target.getAttribute("aria-valuenow");
100
+ if (ariaValueNow?.trim()) {
101
+ return ariaValueNow.trim().slice(0, 100);
102
+ }
103
+ return String(target.textContent || "").trim().slice(0, 100);
104
+ }
105
+ function readEditableControlPlaceholder(element) {
106
+ const target = resolveEditableControlElement(element);
107
+ if (typeof target.placeholder === "string") {
108
+ return String(target.placeholder || "").trim().slice(0, 100);
109
+ }
110
+ return String(target.getAttribute("placeholder") || "").trim().slice(0, 100);
111
+ }
112
+ function readEditableControlName(element) {
113
+ const target = resolveEditableControlElement(element);
114
+ const explicitName = target.name;
115
+ if (typeof explicitName === "string" && explicitName.trim()) {
116
+ return explicitName.trim().slice(0, 100);
117
+ }
118
+ return String(
119
+ target.getAttribute("name") || target.getAttribute("id") || target.getAttribute("aria-label") || ""
120
+ ).trim().slice(0, 100);
121
+ }
122
+ function readEditableControlType(element) {
123
+ const target = resolveEditableControlElement(element);
124
+ const role = getRole(target);
125
+ if (role) return role;
126
+ const explicitType = target.type;
127
+ if (typeof explicitType === "string" && explicitType.trim()) {
128
+ return explicitType.trim().toLowerCase().slice(0, 50);
129
+ }
130
+ const attributeType = target.getAttribute("type");
131
+ if (attributeType?.trim()) {
132
+ return attributeType.trim().toLowerCase().slice(0, 50);
133
+ }
134
+ return getTagName(target);
135
+ }
136
+ function isEditableControlDisabled(element) {
137
+ const target = resolveEditableControlElement(element);
138
+ const ariaDisabled = String(target.getAttribute("aria-disabled") || "").toLowerCase();
139
+ if (ariaDisabled === "true") return true;
140
+ return Boolean(target.disabled);
141
+ }
142
+ function collectEditableControls(scope) {
143
+ const seen = /* @__PURE__ */ new Set();
144
+ const controls = [];
145
+ for (const rawElement of Array.from(scope.querySelectorAll(EDITABLE_CONTROL_SELECTOR))) {
146
+ const resolved = resolveEditableControlElement(rawElement);
147
+ if (seen.has(resolved)) continue;
148
+ if (isValueBearingElement(resolved) && String(resolved.type || "").toLowerCase() === "hidden") {
149
+ continue;
150
+ }
151
+ seen.add(resolved);
152
+ controls.push(resolved);
153
+ }
154
+ return controls;
155
+ }
156
+
157
+ // src/auto-extract.ts
158
+ import { useState, useEffect, useRef, useCallback } from "react";
159
+
160
+ // src/utils/dev-logging.ts
161
+ function isSdkDebugEnabled(devMode) {
162
+ if (devMode) return true;
163
+ if (typeof window !== "undefined" && Boolean(window.MODELNEX_DEBUG)) {
164
+ return true;
165
+ }
166
+ return process.env.NODE_ENV === "development";
167
+ }
168
+ function emitSdkDebugLog(message, payload, options) {
169
+ if (!isSdkDebugEnabled(options?.devMode)) return;
170
+ if (payload && Object.keys(payload).length > 0) {
171
+ console.log(message, payload);
172
+ } else {
173
+ console.log(message);
174
+ }
175
+ if (options?.dispatchEvent && typeof window !== "undefined") {
176
+ window.dispatchEvent(new CustomEvent("modelnex-debug", { detail: { msg: message, data: payload } }));
177
+ }
178
+ }
179
+ function sanitizeActionList(actions) {
180
+ if (!Array.isArray(actions) || actions.length === 0) return void 0;
181
+ return actions.map(({ actionId }) => ({ actionId }));
182
+ }
183
+ function sanitizeAgentDebug(debug) {
184
+ if (!debug) return void 0;
185
+ const actions = sanitizeActionList(debug.actions);
186
+ const traces = Array.isArray(debug.traces) && debug.traces.length > 0 ? debug.traces.map((trace) => ({
187
+ step: trace.step,
188
+ actions: sanitizeActionList(trace.actions) ?? [],
189
+ results: Array.isArray(trace.results) && trace.results.length > 0 ? trace.results.map(({ actionId, success }) => ({ actionId, success })) : void 0
190
+ })) : void 0;
191
+ if ((!actions || actions.length === 0) && (!traces || traces.length === 0)) {
192
+ return void 0;
193
+ }
194
+ return {
195
+ ...actions ? { actions } : {},
196
+ ...traces ? { traces } : {}
197
+ };
198
+ }
199
+ function sanitizeChatMessages(messages, devMode) {
200
+ const includeDebug = isSdkDebugEnabled(devMode);
201
+ return messages.map((message) => {
202
+ if (message.role !== "assistant") {
203
+ return message;
204
+ }
205
+ return {
206
+ ...message,
207
+ debug: includeDebug ? sanitizeAgentDebug(message.debug) : void 0
208
+ };
209
+ });
210
+ }
211
+
212
+ // src/auto-extract.ts
213
+ function simpleHash(str) {
214
+ let hash = 0;
215
+ for (let i = 0; i < str.length; i++) {
216
+ const char = str.charCodeAt(i);
217
+ hash = (hash << 5) - hash + char;
218
+ hash |= 0;
219
+ }
220
+ return (hash >>> 0).toString(16).padStart(8, "0");
221
+ }
222
+ function isStableDomId(id) {
223
+ if (!id) return false;
224
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
225
+ const REACT_USE_ID_RE = /^(?:radix-)?:[rR][^\s]*$/;
226
+ if (REACT_USE_ID_RE.test(id)) return false;
227
+ if (/^\d+$/.test(id)) return false;
228
+ if (UUID_RE.test(id)) return false;
229
+ return true;
230
+ }
231
+ function getNearestHeading(el) {
232
+ const shellContainer = el.closest('nav, aside, header, [role="navigation"], [role="complementary"], [role="banner"]');
233
+ const boundary = shellContainer || document.body;
234
+ let current = el;
235
+ while (current && current !== boundary) {
236
+ let sibling = current.previousElementSibling;
237
+ while (sibling) {
238
+ if (/^H[1-6]$/.test(sibling.tagName)) {
239
+ return sibling.textContent?.trim().slice(0, 100) || null;
240
+ }
241
+ const innerHeading = sibling.querySelector("h1, h2, h3, h4, h5, h6");
242
+ if (innerHeading) {
243
+ return innerHeading.textContent?.trim().slice(0, 100) || null;
244
+ }
245
+ sibling = sibling.previousElementSibling;
246
+ }
247
+ const parentHeading = current.parentElement?.querySelector("h1, h2, h3, h4, h5, h6");
248
+ if (parentHeading && !current.contains(parentHeading)) {
249
+ return parentHeading.textContent?.trim().slice(0, 100) || null;
250
+ }
251
+ current = current.parentElement;
252
+ }
253
+ return null;
254
+ }
255
+ function getParentContainer(el) {
256
+ let current = el.parentElement;
257
+ while (current && current !== document.body) {
258
+ const role = current.getAttribute("role");
259
+ const tag = current.tagName.toLowerCase();
260
+ const state = current.getAttribute("data-state");
261
+ if (role === "dialog" || role === "alertdialog" || tag === "dialog") {
262
+ const title = current.querySelector('h2, h3, [class*="title"]');
263
+ const titleText = title?.textContent?.trim().slice(0, 60) || "";
264
+ return titleText ? `dialog:${titleText}` : "dialog";
265
+ }
266
+ if (role === "menu" || role === "listbox") {
267
+ return role;
268
+ }
269
+ if (current.hasAttribute("popover") || state === "open" && current.getAttribute("data-radix-popper-content-wrapper") !== null) {
270
+ return "popover";
271
+ }
272
+ if (current.getAttribute("data-radix-menu-content") !== null || role === "menubar") {
273
+ return "dropdown-menu";
274
+ }
275
+ if (role === "tabpanel") {
276
+ const label = current.getAttribute("aria-label") || current.getAttribute("aria-labelledby");
277
+ return label ? `tabpanel:${label.slice(0, 40)}` : "tabpanel";
278
+ }
279
+ if (tag === "nav" || role === "navigation") {
280
+ const label = current.getAttribute("aria-label");
281
+ return label ? `navigation:${label.slice(0, 40)}` : "navigation";
282
+ }
283
+ if (tag === "aside") {
284
+ return "sidebar";
285
+ }
286
+ if (tag === "header") {
287
+ return "header";
288
+ }
289
+ current = current.parentElement;
290
+ }
291
+ return null;
292
+ }
293
+ function getRowContext(el) {
294
+ let current = el.parentElement;
295
+ while (current && current !== document.body) {
296
+ const tag = current.tagName.toLowerCase();
297
+ if (tag === "tr" || tag === "li" || current.getAttribute("role") === "row" || current.getAttribute("role") === "listitem") {
298
+ const rowText = current.textContent?.trim().replace(/\s+/g, " ").slice(0, 120) || "";
299
+ return rowText;
300
+ }
301
+ current = current.parentElement;
302
+ }
303
+ return "";
304
+ }
305
+ function getNearestAncestorId(el) {
306
+ let current = el.parentElement;
307
+ while (current && current !== document.body) {
308
+ const aid = current.id;
309
+ if (isStableDomId(aid)) {
310
+ return aid;
311
+ }
312
+ const ancestorTestId = current.getAttribute("data-testid");
313
+ if (ancestorTestId) return `[data-testid="${ancestorTestId}"]`;
314
+ current = current.parentElement;
315
+ }
316
+ return null;
317
+ }
318
+ function generateFingerprint(el) {
319
+ const tag = el.tagName.toLowerCase();
320
+ const testId = el.getAttribute("data-testid");
321
+ const id = el.id;
322
+ const name = el.getAttribute("name");
323
+ const ariaLabel = el.getAttribute("aria-label");
324
+ const type = el.type || "";
325
+ const text = el.textContent?.trim().slice(0, 80) || "";
326
+ if (testId) return `tid:${testId}`;
327
+ if (isStableDomId(id)) return `id:${id}`;
328
+ if (name) return `name:${tag}:${name}`;
329
+ if (ariaLabel) {
330
+ const rowCtx2 = getRowContext(el);
331
+ if (rowCtx2) {
332
+ return `aria:${simpleHash(ariaLabel + ":" + rowCtx2)}:${ariaLabel.slice(0, 40)}`;
333
+ }
334
+ return `aria:${simpleHash(ariaLabel)}:${ariaLabel.slice(0, 40)}`;
335
+ }
336
+ if (tag === "a") {
337
+ const href = el.getAttribute("href");
338
+ if (href && href !== "#" && !href.startsWith("javascript:")) {
339
+ return `href:${simpleHash(href + ":" + text)}:${text.slice(0, 30) || href.slice(0, 30)}`;
340
+ }
341
+ }
342
+ const ancestorId = getNearestAncestorId(el);
343
+ if (ancestorId) {
344
+ return `anc:${simpleHash(ancestorId + ":" + tag + ":" + text)}:${text.slice(0, 30) || tag}`;
345
+ }
346
+ const heading = getNearestHeading(el) || "";
347
+ const rowCtx = getRowContext(el);
348
+ const raw = `${tag}:${type}:${text}:${heading}:${rowCtx}`;
349
+ return `hash:${simpleHash(raw)}:${text.slice(0, 30) || tag}`;
350
+ }
351
+ function classifyRole(el) {
352
+ const tag = el.tagName.toLowerCase();
353
+ const role = el.getAttribute("role");
354
+ if (tag === "button" || role === "button") return "button";
355
+ if (tag === "a") return "link";
356
+ if (tag === "input") return "input";
357
+ if (tag === "textarea") return "textarea";
358
+ if (tag === "select") return "select";
359
+ if (tag === "form") return "form";
360
+ if (tag === "label") return "label";
361
+ if (role === "link") return "link";
362
+ if (role === "menuitem") return "menuitem";
363
+ if (role === "menuitemcheckbox") return "menuitem";
364
+ if (role === "menuitemradio") return "menuitem";
365
+ if (role === "tab") return "tab";
366
+ if (role === "checkbox") return "checkbox";
367
+ if (role === "radio") return "radio";
368
+ if (role === "switch") return "switch";
369
+ if (role === "slider") return "slider";
370
+ if (role === "combobox") return "combobox";
371
+ if (role === "option") return "option";
372
+ if (role === "treeitem") return "treeitem";
373
+ if (role === "gridcell") return "gridcell";
374
+ if (el.hasAttribute("tabindex") && el.getAttribute("tabindex") !== "-1") return "interactive";
375
+ if (el.hasAttribute("cmdk-item")) return "menuitem";
376
+ return null;
377
+ }
378
+ var INTERACTIVE_SELECTOR = [
379
+ "button",
380
+ "a",
381
+ // All anchor tags (SPA links may not have href)
382
+ 'input:not([type="hidden"])',
383
+ "textarea",
384
+ "select",
385
+ "label[for]",
386
+ '[role="button"]',
387
+ '[role="link"]',
388
+ '[role="menuitem"]',
389
+ '[role="menuitemcheckbox"]',
390
+ '[role="menuitemradio"]',
391
+ '[role="tab"]',
392
+ '[role="checkbox"]',
393
+ '[role="radio"]',
394
+ '[role="switch"]',
395
+ '[role="slider"]',
396
+ '[role="combobox"]',
397
+ '[role="option"]',
398
+ '[role="treeitem"]',
399
+ '[role="gridcell"]',
400
+ '[tabindex]:not([tabindex="-1"])',
401
+ "[data-discover]",
402
+ // React Router links
403
+ "[cmdk-item]",
404
+ // cmdk menu items
405
+ "form"
406
+ ].join(", ");
407
+ function isVisible(el) {
408
+ if (el.offsetParent === null) {
409
+ const computed = window.getComputedStyle(el);
410
+ if (computed.position !== "fixed" && computed.position !== "sticky") {
411
+ if (computed.display === "none" || computed.visibility === "hidden") {
412
+ return false;
413
+ }
414
+ const rect2 = el.getBoundingClientRect();
415
+ if (rect2.width === 0 && rect2.height === 0) return false;
416
+ }
417
+ }
418
+ const style = window.getComputedStyle(el);
419
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
420
+ return false;
421
+ }
422
+ const rect = el.getBoundingClientRect();
423
+ return rect.width > 0 && rect.height > 0;
424
+ }
425
+ function extractInteractiveElements() {
426
+ const elements = [];
427
+ const seen = /* @__PURE__ */ new Set();
428
+ try {
429
+ const nodes = document.querySelectorAll(INTERACTIVE_SELECTOR);
430
+ nodes.forEach((node) => {
431
+ const el = node;
432
+ if (!isVisible(el)) return;
433
+ if (el.closest(".modelnex-chat-panel, .modelnex-studio-overlay, [data-modelnex-internal]")) return;
434
+ const role = classifyRole(el);
435
+ if (!role) return;
436
+ let fingerprint = generateFingerprint(el);
437
+ if (seen.has(fingerprint)) {
438
+ let idx = 2;
439
+ while (seen.has(`${fingerprint}#${idx}`)) idx++;
440
+ fingerprint = `${fingerprint}#${idx}`;
441
+ }
442
+ seen.add(fingerprint);
443
+ const rect = el.getBoundingClientRect();
444
+ const text = el.textContent?.trim().slice(0, 200) || "";
445
+ elements.push({
446
+ fingerprint,
447
+ tagName: el.tagName.toLowerCase(),
448
+ text,
449
+ role,
450
+ rect: {
451
+ top: rect.top,
452
+ left: rect.left,
453
+ width: rect.width,
454
+ height: rect.height
455
+ },
456
+ attributes: {
457
+ id: el.id || void 0,
458
+ name: el.getAttribute("name") || void 0,
459
+ type: el.type || void 0,
460
+ ariaLabel: el.getAttribute("aria-label") || void 0,
461
+ dataTestId: el.getAttribute("data-testid") || void 0,
462
+ href: el.href || void 0,
463
+ placeholder: el.placeholder || void 0
464
+ },
465
+ nearestHeading: getNearestHeading(el),
466
+ parentContainer: getParentContainer(el),
467
+ disabled: el.disabled || el.getAttribute("aria-disabled") === "true",
468
+ element: el
469
+ });
470
+ });
471
+ } catch (err) {
472
+ console.warn("[ModelNex] Auto-extraction error:", err);
473
+ }
474
+ return elements;
475
+ }
476
+ function useAutoExtract(devMode) {
477
+ const [elements, setElements] = useState([]);
478
+ const timerRef = useRef(null);
479
+ const lastSnapshotRef = useRef("");
480
+ const scan = useCallback(() => {
481
+ if (typeof document === "undefined") return;
482
+ const extracted = extractInteractiveElements();
483
+ const snapshot = JSON.stringify(extracted.map((e) => ({
484
+ fingerprint: e.fingerprint,
485
+ role: e.role,
486
+ text: e.text,
487
+ rect: e.rect,
488
+ disabled: e.disabled,
489
+ nearestHeading: e.nearestHeading,
490
+ parentContainer: e.parentContainer
491
+ })));
492
+ if (snapshot === lastSnapshotRef.current) return;
493
+ lastSnapshotRef.current = snapshot;
494
+ emitSdkDebugLog("[ModelNex AutoExtract] Scan complete", {
495
+ elementCount: extracted.length
496
+ }, { devMode });
497
+ setElements(extracted);
498
+ }, [devMode]);
499
+ useEffect(() => {
500
+ const initialTimer = setTimeout(scan, 300);
501
+ const observer = new MutationObserver((mutations) => {
502
+ const hasRelevantMutation = mutations.some((mutation) => {
503
+ const target = mutation.target;
504
+ if (target?.closest?.(".modelnex-chat-panel, .modelnex-studio-overlay, [data-modelnex-internal]")) {
505
+ return false;
506
+ }
507
+ return true;
508
+ });
509
+ if (!hasRelevantMutation) return;
510
+ if (timerRef.current) clearTimeout(timerRef.current);
511
+ timerRef.current = setTimeout(scan, 500);
512
+ });
513
+ observer.observe(document.body, {
514
+ childList: true,
515
+ subtree: true,
516
+ attributes: true,
517
+ attributeFilter: ["disabled", "aria-disabled", "hidden", "style", "class"]
518
+ });
519
+ const handlePositionChange = () => {
520
+ if (timerRef.current) clearTimeout(timerRef.current);
521
+ timerRef.current = setTimeout(scan, 200);
522
+ };
523
+ window.addEventListener("scroll", handlePositionChange, { passive: true });
524
+ window.addEventListener("resize", handlePositionChange, { passive: true });
525
+ return () => {
526
+ clearTimeout(initialTimer);
527
+ if (timerRef.current) clearTimeout(timerRef.current);
528
+ observer.disconnect();
529
+ window.removeEventListener("scroll", handlePositionChange);
530
+ window.removeEventListener("resize", handlePositionChange);
531
+ };
532
+ }, [scan]);
533
+ return elements;
534
+ }
535
+
536
+ // src/utils/aom.ts
537
+ var uidMap = /* @__PURE__ */ new Map();
538
+ var elementUidMap = /* @__PURE__ */ new WeakMap();
539
+ var locatorUidMap = /* @__PURE__ */ new Map();
540
+ var nextUid = 1;
541
+ function normalizeUidText(value) {
542
+ return String(value || "").replace(/\s+/g, " ").trim().slice(0, 120);
543
+ }
544
+ function buildStableUidLocator(el) {
545
+ const tag = el.tagName.toLowerCase();
546
+ const role = normalizeUidText(el.getAttribute("role"));
547
+ const testId = normalizeUidText(el.getAttribute("data-testid"));
548
+ const fingerprint = normalizeUidText(generateFingerprint(el));
549
+ const controlName = normalizeUidText(readEditableControlName(el));
550
+ const ariaLabel = normalizeUidText(el.getAttribute("aria-label"));
551
+ const placeholder = normalizeUidText(readEditableControlPlaceholder(el));
552
+ const inputType = normalizeUidText(el.type || el.getAttribute("type"));
553
+ const href = el instanceof HTMLAnchorElement ? normalizeUidText(el.getAttribute("href")) : "";
554
+ return [
555
+ tag,
556
+ role,
557
+ testId,
558
+ fingerprint,
559
+ controlName,
560
+ ariaLabel,
561
+ placeholder,
562
+ inputType,
563
+ href
564
+ ].join("::");
565
+ }
566
+ function allocateUid() {
567
+ return `node:${nextUid++}`;
568
+ }
569
+ function resolveStableUid(el, activeUids) {
570
+ const existingElementUid = elementUidMap.get(el);
571
+ if (existingElementUid && !activeUids.has(existingElementUid)) {
572
+ return existingElementUid;
573
+ }
574
+ const locatorKey = buildStableUidLocator(el);
575
+ const existingLocatorUid = locatorUidMap.get(locatorKey);
576
+ if (existingLocatorUid && !activeUids.has(existingLocatorUid)) {
577
+ elementUidMap.set(el, existingLocatorUid);
578
+ return existingLocatorUid;
579
+ }
580
+ const uid = allocateUid();
581
+ elementUidMap.set(el, uid);
582
+ if (locatorKey) {
583
+ locatorUidMap.set(locatorKey, uid);
584
+ }
585
+ return uid;
586
+ }
587
+ function generateMinifiedAOM() {
588
+ const interactiveSet = /* @__PURE__ */ new Set();
589
+ const activeUids = /* @__PURE__ */ new Set();
590
+ const nextUidMap = /* @__PURE__ */ new Map();
591
+ const interactiveCandidates = [
592
+ ...Array.from(document.querySelectorAll(
593
+ 'button, a, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], [role="textbox"], [role="combobox"], [contenteditable="true"]'
594
+ )),
595
+ ...collectEditableControls(document)
596
+ ];
597
+ const nodes = [];
598
+ interactiveCandidates.forEach((candidate) => {
599
+ const el = resolveEditableControlElement(candidate);
600
+ if (interactiveSet.has(el)) {
601
+ return;
602
+ }
603
+ interactiveSet.add(el);
604
+ if (!el.offsetParent && (el.offsetWidth === 0 || el.offsetHeight === 0)) {
605
+ return;
606
+ }
607
+ if (el.closest("#modelnex-studio-root") || el.closest("#modelnex-active-agent-root")) {
608
+ return;
609
+ }
610
+ const uid = resolveStableUid(el, activeUids);
611
+ activeUids.add(uid);
612
+ nextUidMap.set(uid, el);
613
+ let text = (el.textContent || "").replace(/\s+/g, " ").trim();
614
+ const ariaLabel = el.getAttribute("aria-label");
615
+ const placeholder = el.getAttribute("placeholder");
616
+ const value = readEditableControlValue(el, { maskPasswords: false }) || void 0;
617
+ let role = el.tagName.toLowerCase();
618
+ if (el.hasAttribute("role")) {
619
+ role = el.getAttribute("role");
620
+ } else if (role === "a") {
621
+ role = "link";
622
+ } else if (el.tagName.toLowerCase() === "input") {
623
+ const inputType = el.type;
624
+ role = inputType ? `input[${inputType}]` : "input";
625
+ }
626
+ const node = { uid, role };
627
+ const displayLabel = ariaLabel || text || placeholder;
628
+ if (displayLabel) {
629
+ node.text = displayLabel.substring(0, 100);
630
+ }
631
+ const controlName = readEditableControlName(el);
632
+ if (controlName) {
633
+ node.name = controlName;
634
+ }
635
+ if (value) {
636
+ node.value = value.substring(0, 100);
637
+ }
638
+ if (el instanceof HTMLAnchorElement && el.href) {
639
+ try {
640
+ const url = new URL(el.href);
641
+ node.href = url.pathname + url.search + url.hash;
642
+ } catch {
643
+ node.href = el.getAttribute("href");
644
+ }
645
+ }
646
+ nodes.push(node);
647
+ });
648
+ uidMap.clear();
649
+ for (const [uid, el] of nextUidMap.entries()) {
650
+ uidMap.set(uid, el);
651
+ }
652
+ return { nodes };
653
+ }
654
+ function getElementByUid(uid) {
655
+ return uidMap.get(uid) || null;
656
+ }
657
+ function clearAOMMap() {
658
+ uidMap.clear();
659
+ locatorUidMap.clear();
660
+ nextUid = 1;
661
+ }
662
+
663
+ export {
664
+ isSdkDebugEnabled,
665
+ emitSdkDebugLog,
666
+ sanitizeAgentDebug,
667
+ sanitizeChatMessages,
668
+ generateFingerprint,
669
+ extractInteractiveElements,
670
+ useAutoExtract,
671
+ isValueBearingElement,
672
+ findEditableControlInShadowRoot,
673
+ resolveEditableControlElement,
674
+ readEditableControlValue,
675
+ readEditableControlPlaceholder,
676
+ readEditableControlName,
677
+ readEditableControlType,
678
+ isEditableControlDisabled,
679
+ collectEditableControls,
680
+ generateMinifiedAOM,
681
+ getElementByUid,
682
+ clearAOMMap
683
+ };