@qontinui/ui-bridge 0.1.1 → 0.2.0

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.
Files changed (43) hide show
  1. package/dist/ai/index.d.mts +893 -0
  2. package/dist/ai/index.d.ts +893 -0
  3. package/dist/ai/index.js +3897 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/ai/index.mjs +3839 -0
  6. package/dist/ai/index.mjs.map +1 -0
  7. package/dist/control/index.d.mts +5 -4
  8. package/dist/control/index.d.ts +5 -4
  9. package/dist/core/index.d.mts +6 -4
  10. package/dist/core/index.d.ts +6 -4
  11. package/dist/core/index.js +581 -4
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/core/index.mjs +581 -4
  14. package/dist/core/index.mjs.map +1 -1
  15. package/dist/debug/index.d.mts +3 -3
  16. package/dist/debug/index.d.ts +3 -3
  17. package/dist/index.d.mts +7 -5
  18. package/dist/index.d.ts +7 -5
  19. package/dist/index.js +4112 -12
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +4056 -13
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/{metrics-QCnK0EFw.d.ts → metrics-C9XRi_mL.d.ts} +2 -2
  24. package/dist/{metrics-BCG7z7Aq.d.mts → metrics-NC3csD0R.d.mts} +2 -2
  25. package/dist/react/index.d.mts +6 -5
  26. package/dist/react/index.d.ts +6 -5
  27. package/dist/react/index.js +587 -10
  28. package/dist/react/index.js.map +1 -1
  29. package/dist/react/index.mjs +587 -10
  30. package/dist/react/index.mjs.map +1 -1
  31. package/dist/{registry-CT6BVVKr.d.mts → registry-CIEDjbQ9.d.ts} +22 -1
  32. package/dist/{registry-D4mQ01B3.d.ts → registry-SsSDq46X.d.mts} +22 -1
  33. package/dist/render-log/index.d.mts +1 -1
  34. package/dist/render-log/index.d.ts +1 -1
  35. package/dist/types-BvCfFuEV.d.ts +534 -0
  36. package/dist/types-CFT3Dnx4.d.mts +534 -0
  37. package/dist/{types-BpvpStn3.d.mts → types-CPMbN_Iw.d.mts} +8 -0
  38. package/dist/{types-BpvpStn3.d.ts → types-CPMbN_Iw.d.ts} +8 -0
  39. package/dist/{types-DdJD9yw5.d.mts → types-Dr6tH-bm.d.mts} +1 -1
  40. package/dist/{types-BDkXy5si.d.ts → types-oCTrRxSw.d.ts} +1 -1
  41. package/dist/{websocket-client-DupH0X7B.d.ts → websocket-client-CX4QJesI.d.ts} +1 -1
  42. package/dist/{websocket-client-B2LC9CYc.d.mts → websocket-client-C_Na0OSp.d.mts} +1 -1
  43. package/package.json +6 -1
package/dist/index.js CHANGED
@@ -222,6 +222,583 @@ function elementMatchesIdentifier(element, identifier) {
222
222
  return identifier.uiId && element.getAttribute("data-ui-id") === identifier.uiId || identifier.testId && element.getAttribute("data-testid") === identifier.testId || identifier.awasId && element.getAttribute("data-awas-element") === identifier.awasId || identifier.htmlId && element.id === identifier.htmlId || false;
223
223
  }
224
224
 
225
+ // src/ai/fuzzy-matcher.ts
226
+ var DEFAULT_FUZZY_CONFIG = {
227
+ threshold: 0.7,
228
+ levenshteinWeight: 0.3,
229
+ jaroWinklerWeight: 0.4,
230
+ ngramWeight: 0.3,
231
+ ngramSize: 2,
232
+ caseSensitive: false,
233
+ ignoreWhitespace: true
234
+ };
235
+ function levenshteinDistance(s1, s2) {
236
+ const len1 = s1.length;
237
+ const len2 = s2.length;
238
+ const matrix = Array(len1 + 1).fill(null).map(() => Array(len2 + 1).fill(0));
239
+ for (let i = 0; i <= len1; i++) matrix[i][0] = i;
240
+ for (let j = 0; j <= len2; j++) matrix[0][j] = j;
241
+ for (let i = 1; i <= len1; i++) {
242
+ for (let j = 1; j <= len2; j++) {
243
+ const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
244
+ matrix[i][j] = Math.min(
245
+ matrix[i - 1][j] + 1,
246
+ // deletion
247
+ matrix[i][j - 1] + 1,
248
+ // insertion
249
+ matrix[i - 1][j - 1] + cost
250
+ // substitution
251
+ );
252
+ }
253
+ }
254
+ return matrix[len1][len2];
255
+ }
256
+ function levenshteinSimilarity(s1, s2) {
257
+ if (s1.length === 0 && s2.length === 0) return 1;
258
+ if (s1.length === 0 || s2.length === 0) return 0;
259
+ const distance = levenshteinDistance(s1, s2);
260
+ const maxLength = Math.max(s1.length, s2.length);
261
+ return 1 - distance / maxLength;
262
+ }
263
+ function jaroSimilarity(s1, s2) {
264
+ if (s1.length === 0 && s2.length === 0) return 1;
265
+ if (s1.length === 0 || s2.length === 0) return 0;
266
+ const matchDistance = Math.floor(Math.max(s1.length, s2.length) / 2) - 1;
267
+ const s1Matches = new Array(s1.length).fill(false);
268
+ const s2Matches = new Array(s2.length).fill(false);
269
+ let matches = 0;
270
+ let transpositions = 0;
271
+ for (let i = 0; i < s1.length; i++) {
272
+ const start = Math.max(0, i - matchDistance);
273
+ const end = Math.min(i + matchDistance + 1, s2.length);
274
+ for (let j = start; j < end; j++) {
275
+ if (s2Matches[j] || s1[i] !== s2[j]) continue;
276
+ s1Matches[i] = true;
277
+ s2Matches[j] = true;
278
+ matches++;
279
+ break;
280
+ }
281
+ }
282
+ if (matches === 0) return 0;
283
+ let k = 0;
284
+ for (let i = 0; i < s1.length; i++) {
285
+ if (!s1Matches[i]) continue;
286
+ while (!s2Matches[k]) k++;
287
+ if (s1[i] !== s2[k]) transpositions++;
288
+ k++;
289
+ }
290
+ return (matches / s1.length + matches / s2.length + (matches - transpositions / 2) / matches) / 3;
291
+ }
292
+ function jaroWinklerSimilarity(s1, s2, prefixScale = 0.1) {
293
+ const jaroSim = jaroSimilarity(s1, s2);
294
+ let prefixLength = 0;
295
+ const maxPrefix = Math.min(4, Math.min(s1.length, s2.length));
296
+ for (let i = 0; i < maxPrefix; i++) {
297
+ if (s1[i] === s2[i]) {
298
+ prefixLength++;
299
+ } else {
300
+ break;
301
+ }
302
+ }
303
+ return jaroSim + prefixLength * prefixScale * (1 - jaroSim);
304
+ }
305
+ function generateNgrams(s, n) {
306
+ const ngrams = /* @__PURE__ */ new Set();
307
+ if (s.length < n) {
308
+ ngrams.add(s);
309
+ return ngrams;
310
+ }
311
+ for (let i = 0; i <= s.length - n; i++) {
312
+ ngrams.add(s.substring(i, i + n));
313
+ }
314
+ return ngrams;
315
+ }
316
+ function ngramSimilarity(s1, s2, n = 2) {
317
+ if (s1.length === 0 && s2.length === 0) return 1;
318
+ if (s1.length === 0 || s2.length === 0) return 0;
319
+ const ngrams1 = generateNgrams(s1, n);
320
+ const ngrams2 = generateNgrams(s2, n);
321
+ let intersection = 0;
322
+ for (const ngram of ngrams1) {
323
+ if (ngrams2.has(ngram)) {
324
+ intersection++;
325
+ }
326
+ }
327
+ const union = ngrams1.size + ngrams2.size - intersection;
328
+ return union === 0 ? 0 : intersection / union;
329
+ }
330
+ function normalizeString(s, config = {}) {
331
+ let normalized = s;
332
+ if (!config.caseSensitive) {
333
+ normalized = normalized.toLowerCase();
334
+ }
335
+ if (config.ignoreWhitespace !== false) {
336
+ normalized = normalized.replace(/\s+/g, " ").trim();
337
+ }
338
+ return normalized;
339
+ }
340
+ function fuzzyMatch(source, target, config = {}) {
341
+ const finalConfig = { ...DEFAULT_FUZZY_CONFIG, ...config };
342
+ const normalizedSource = normalizeString(source, finalConfig);
343
+ const normalizedTarget = normalizeString(target, finalConfig);
344
+ const levenshteinScore = levenshteinSimilarity(normalizedSource, normalizedTarget);
345
+ const jaroWinklerScore = jaroWinklerSimilarity(normalizedSource, normalizedTarget);
346
+ const ngramScore = ngramSimilarity(normalizedSource, normalizedTarget, finalConfig.ngramSize);
347
+ const similarity = levenshteinScore * finalConfig.levenshteinWeight + jaroWinklerScore * finalConfig.jaroWinklerWeight + ngramScore * finalConfig.ngramWeight;
348
+ return {
349
+ similarity,
350
+ isMatch: similarity >= finalConfig.threshold,
351
+ scores: {
352
+ levenshtein: levenshteinScore,
353
+ jaroWinkler: jaroWinklerScore,
354
+ ngram: ngramScore
355
+ },
356
+ normalizedSource,
357
+ normalizedTarget
358
+ };
359
+ }
360
+ function findBestMatch(source, candidates, config = {}) {
361
+ if (candidates.length === 0) {
362
+ return { match: null, index: -1, result: null };
363
+ }
364
+ let bestMatch = null;
365
+ let bestIndex = -1;
366
+ let bestResult = null;
367
+ for (let i = 0; i < candidates.length; i++) {
368
+ const result = fuzzyMatch(source, candidates[i], config);
369
+ if (result.isMatch && (!bestResult || result.similarity > bestResult.similarity)) {
370
+ bestMatch = candidates[i];
371
+ bestIndex = i;
372
+ bestResult = result;
373
+ }
374
+ }
375
+ return { match: bestMatch, index: bestIndex, result: bestResult };
376
+ }
377
+ function findAllMatches(source, candidates, config = {}) {
378
+ const matches = [];
379
+ for (let i = 0; i < candidates.length; i++) {
380
+ const result = fuzzyMatch(source, candidates[i], config);
381
+ if (result.isMatch) {
382
+ matches.push({ candidate: candidates[i], index: i, result });
383
+ }
384
+ }
385
+ matches.sort((a, b) => b.result.similarity - a.result.similarity);
386
+ return matches;
387
+ }
388
+ function fuzzyContains(source, target, config = {}) {
389
+ const finalConfig = { ...DEFAULT_FUZZY_CONFIG, ...config };
390
+ const normalizedSource = normalizeString(source, finalConfig);
391
+ const normalizedTarget = normalizeString(target, finalConfig);
392
+ if (normalizedSource.includes(normalizedTarget)) {
393
+ return true;
394
+ }
395
+ const sourceWords = normalizedSource.split(/\s+/);
396
+ const targetWords = normalizedTarget.split(/\s+/);
397
+ for (const targetWord of targetWords) {
398
+ const hasMatch = sourceWords.some((sourceWord) => {
399
+ const result = fuzzyMatch(sourceWord, targetWord, { ...finalConfig, threshold: 0.8 });
400
+ return result.isMatch;
401
+ });
402
+ if (!hasMatch) {
403
+ return false;
404
+ }
405
+ }
406
+ return true;
407
+ }
408
+ function wordSimilarity(s1, s2, config = {}) {
409
+ const finalConfig = { ...DEFAULT_FUZZY_CONFIG, ...config };
410
+ const words1 = normalizeString(s1, finalConfig).split(/\s+/);
411
+ const words2 = normalizeString(s2, finalConfig).split(/\s+/);
412
+ if (words1.length === 0 && words2.length === 0) return 1;
413
+ if (words1.length === 0 || words2.length === 0) return 0;
414
+ let totalSimilarity = 0;
415
+ let matchCount = 0;
416
+ for (const word1 of words1) {
417
+ let bestSim = 0;
418
+ for (const word2 of words2) {
419
+ const result = fuzzyMatch(word1, word2, finalConfig);
420
+ if (result.similarity > bestSim) {
421
+ bestSim = result.similarity;
422
+ }
423
+ }
424
+ totalSimilarity += bestSim;
425
+ if (bestSim >= finalConfig.threshold) {
426
+ matchCount++;
427
+ }
428
+ }
429
+ const avgSimilarity = totalSimilarity / words1.length;
430
+ const matchRatio = matchCount / Math.max(words1.length, words2.length);
431
+ return avgSimilarity * 0.5 + matchRatio * 0.5;
432
+ }
433
+ function tokenize(s) {
434
+ return s.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase().split(" ").filter((token) => token.length > 0);
435
+ }
436
+ function tokenSimilarity(s1, s2) {
437
+ const tokens1 = tokenize(s1);
438
+ const tokens2 = tokenize(s2);
439
+ if (tokens1.length === 0 && tokens2.length === 0) return 1;
440
+ if (tokens1.length === 0 || tokens2.length === 0) return 0;
441
+ const set1 = new Set(tokens1);
442
+ const set2 = new Set(tokens2);
443
+ let intersection = 0;
444
+ for (const token of set1) {
445
+ if (set2.has(token)) {
446
+ intersection++;
447
+ }
448
+ }
449
+ const union = set1.size + set2.size - intersection;
450
+ return union === 0 ? 0 : intersection / union;
451
+ }
452
+
453
+ // src/ai/alias-generator.ts
454
+ var DEFAULT_ALIAS_CONFIG = {
455
+ includeText: true,
456
+ includeAriaLabel: true,
457
+ includePlaceholder: true,
458
+ includeTitle: true,
459
+ includeSynonyms: true,
460
+ maxAliases: 20,
461
+ minLength: 2,
462
+ maxLength: 50
463
+ };
464
+ var SYNONYMS = {
465
+ // Submit-related
466
+ submit: ["send", "go", "confirm", "ok", "apply", "save", "done", "finish"],
467
+ send: ["submit", "deliver", "post"],
468
+ save: ["submit", "store", "keep", "apply"],
469
+ cancel: ["close", "dismiss", "abort", "back", "exit", "quit", "nevermind"],
470
+ close: ["cancel", "dismiss", "exit", "x"],
471
+ delete: ["remove", "trash", "erase", "clear", "destroy"],
472
+ remove: ["delete", "clear", "discard"],
473
+ edit: ["modify", "change", "update", "alter"],
474
+ update: ["edit", "modify", "save", "refresh"],
475
+ add: ["create", "new", "plus", "insert"],
476
+ create: ["add", "new", "make"],
477
+ search: ["find", "lookup", "query", "filter"],
478
+ find: ["search", "locate", "lookup"],
479
+ login: ["signin", "sign in", "log in", "authenticate", "enter"],
480
+ logout: ["signout", "sign out", "log out", "exit"],
481
+ register: ["signup", "sign up", "join", "create account"],
482
+ next: ["continue", "forward", "proceed", "advance"],
483
+ previous: ["back", "backward", "return", "prior"],
484
+ back: ["previous", "return", "backward"],
485
+ start: ["begin", "launch", "initiate", "run", "execute"],
486
+ stop: ["end", "halt", "pause", "terminate"],
487
+ enable: ["activate", "turn on", "switch on"],
488
+ disable: ["deactivate", "turn off", "switch off"],
489
+ show: ["display", "reveal", "view", "open"],
490
+ hide: ["conceal", "collapse", "close"],
491
+ expand: ["open", "show", "unfold", "reveal"],
492
+ collapse: ["close", "hide", "fold", "minimize"],
493
+ yes: ["ok", "confirm", "agree", "accept"],
494
+ no: ["cancel", "decline", "reject", "deny"],
495
+ help: ["support", "assistance", "info", "information", "faq"],
496
+ settings: ["preferences", "options", "config", "configuration"],
497
+ profile: ["account", "user", "me"],
498
+ download: ["export", "save", "get"],
499
+ upload: ["import", "load", "attach"],
500
+ refresh: ["reload", "update", "sync"],
501
+ copy: ["duplicate", "clone"],
502
+ paste: ["insert"],
503
+ select: ["choose", "pick"],
504
+ toggle: ["switch", "flip"],
505
+ // Form fields
506
+ email: ["e-mail", "mail"],
507
+ password: ["pass", "pwd", "secret"],
508
+ username: ["user", "login", "account", "name"],
509
+ firstname: ["first name", "given name", "forename"],
510
+ lastname: ["last name", "surname", "family name"],
511
+ fullname: ["full name", "name", "complete name"],
512
+ phone: ["telephone", "tel", "mobile", "cell"],
513
+ address: ["location", "street"],
514
+ city: ["town"],
515
+ country: ["nation"],
516
+ zip: ["zipcode", "postal", "postal code", "postcode"],
517
+ // Navigation
518
+ home: ["main", "start", "dashboard"],
519
+ menu: ["navigation", "nav"],
520
+ sidebar: ["side bar", "side panel", "side menu"]
521
+ };
522
+ var ELEMENT_ACTION_WORDS = {
523
+ button: ["button", "btn", "click"],
524
+ input: ["input", "field", "textbox", "box"],
525
+ textarea: ["textarea", "text area", "text field", "multiline"],
526
+ select: ["select", "dropdown", "combo", "picker", "chooser"],
527
+ checkbox: ["checkbox", "check", "tick"],
528
+ radio: ["radio", "option", "choice"],
529
+ link: ["link", "anchor", "href"],
530
+ form: ["form"],
531
+ menu: ["menu"],
532
+ menuitem: ["menu item", "option"],
533
+ tab: ["tab"],
534
+ dialog: ["dialog", "modal", "popup"],
535
+ switch: ["switch", "toggle"],
536
+ slider: ["slider", "range"]
537
+ };
538
+ function normalizeAlias(text) {
539
+ return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
540
+ }
541
+ function extractWords(text) {
542
+ const tokens = tokenize(text);
543
+ return tokens.filter((t) => t.length >= 2);
544
+ }
545
+ function generateTextAliases(text, config) {
546
+ if (!text || !config.includeText) return [];
547
+ const aliases = [];
548
+ const normalized = normalizeAlias(text);
549
+ if (normalized.length >= config.minLength && normalized.length <= config.maxLength) {
550
+ aliases.push(normalized);
551
+ }
552
+ const words = extractWords(text);
553
+ for (const word of words) {
554
+ if (word.length >= config.minLength) {
555
+ aliases.push(word);
556
+ }
557
+ }
558
+ if (words.length >= 2 && words.length <= 4) {
559
+ const twoWords = words.slice(0, 2).join(" ");
560
+ if (twoWords.length <= config.maxLength) {
561
+ aliases.push(twoWords);
562
+ }
563
+ if (words.length > 2) {
564
+ const lastTwo = words.slice(-2).join(" ");
565
+ if (lastTwo.length <= config.maxLength) {
566
+ aliases.push(lastTwo);
567
+ }
568
+ }
569
+ }
570
+ return aliases;
571
+ }
572
+ function generateSynonyms(aliases, config) {
573
+ if (!config.includeSynonyms) return [];
574
+ const synonyms = [];
575
+ for (const alias of aliases) {
576
+ const words = alias.toLowerCase().split(/\s+/);
577
+ for (const word of words) {
578
+ if (SYNONYMS[word]) {
579
+ for (const synonym of SYNONYMS[word]) {
580
+ const newAlias = alias.toLowerCase().replace(word, synonym);
581
+ if (newAlias !== alias.toLowerCase()) {
582
+ synonyms.push(newAlias);
583
+ }
584
+ if (synonym.length >= config.minLength) {
585
+ synonyms.push(synonym);
586
+ }
587
+ }
588
+ }
589
+ }
590
+ }
591
+ return synonyms;
592
+ }
593
+ function generateTypeAliases(elementType) {
594
+ const type = elementType.toLowerCase();
595
+ return ELEMENT_ACTION_WORDS[type] || [type];
596
+ }
597
+ function generateAliases(input, config = {}) {
598
+ const finalConfig = { ...DEFAULT_ALIAS_CONFIG, ...config };
599
+ const aliasSet = /* @__PURE__ */ new Set();
600
+ const addAlias = (alias) => {
601
+ const normalized = normalizeAlias(alias);
602
+ if (normalized.length >= finalConfig.minLength && normalized.length <= finalConfig.maxLength) {
603
+ aliasSet.add(normalized);
604
+ }
605
+ };
606
+ const addAliases = (aliases2) => {
607
+ for (const alias of aliases2) {
608
+ addAlias(alias);
609
+ }
610
+ };
611
+ if (finalConfig.includeText && input.textContent) {
612
+ addAliases(generateTextAliases(input.textContent, finalConfig));
613
+ }
614
+ if (finalConfig.includeAriaLabel && input.ariaLabel) {
615
+ addAliases(generateTextAliases(input.ariaLabel, finalConfig));
616
+ }
617
+ if (finalConfig.includeAriaLabel && input.ariaLabelledBy) {
618
+ addAliases(generateTextAliases(input.ariaLabelledBy, finalConfig));
619
+ }
620
+ if (finalConfig.includePlaceholder && input.placeholder) {
621
+ addAliases(generateTextAliases(input.placeholder, finalConfig));
622
+ }
623
+ if (finalConfig.includeTitle && input.title) {
624
+ addAliases(generateTextAliases(input.title, finalConfig));
625
+ }
626
+ if (input.labelText) {
627
+ addAliases(generateTextAliases(input.labelText, finalConfig));
628
+ }
629
+ if (input.id) {
630
+ addAliases(extractWords(input.id));
631
+ }
632
+ if (input.name) {
633
+ addAliases(extractWords(input.name));
634
+ }
635
+ if (input.value && (input.elementType === "button" || input.inputType === "submit" || input.inputType === "button")) {
636
+ addAliases(generateTextAliases(input.value, finalConfig));
637
+ }
638
+ if (input.elementType) {
639
+ addAliases(generateTypeAliases(input.elementType));
640
+ }
641
+ if (input.inputType) {
642
+ addAlias(input.inputType);
643
+ if (input.inputType === "email") {
644
+ addAliases(["email", "e-mail", "email address"]);
645
+ } else if (input.inputType === "password") {
646
+ addAliases(["password", "pass", "pwd"]);
647
+ } else if (input.inputType === "tel") {
648
+ addAliases(["phone", "telephone", "mobile"]);
649
+ } else if (input.inputType === "url") {
650
+ addAliases(["url", "website", "link", "address"]);
651
+ } else if (input.inputType === "search") {
652
+ addAliases(["search", "find", "query"]);
653
+ }
654
+ }
655
+ if (finalConfig.includeSynonyms) {
656
+ const currentAliases = Array.from(aliasSet);
657
+ addAliases(generateSynonyms(currentAliases, finalConfig));
658
+ }
659
+ let aliases = Array.from(aliasSet);
660
+ aliases.sort((a, b) => a.length - b.length);
661
+ if (aliases.length > finalConfig.maxAliases) {
662
+ aliases = aliases.slice(0, finalConfig.maxAliases);
663
+ }
664
+ return aliases;
665
+ }
666
+ function generateDescription(input) {
667
+ const parts = [];
668
+ let name = input.ariaLabel || input.labelText || input.textContent || input.placeholder || input.title || input.id || input.name;
669
+ if (name) {
670
+ name = name.trim();
671
+ if (name.length > 30) {
672
+ name = name.substring(0, 27) + "...";
673
+ }
674
+ parts.push(`"${name}"`);
675
+ }
676
+ const typeWords = ELEMENT_ACTION_WORDS[input.elementType || ""] || [input.elementType || "element"];
677
+ parts.push(typeWords[0]);
678
+ if (input.inputType && input.inputType !== "text") {
679
+ parts.push(`(${input.inputType})`);
680
+ }
681
+ return parts.join(" ");
682
+ }
683
+ function generatePurpose(input) {
684
+ const text = (input.textContent || input.ariaLabel || input.title || "").toLowerCase();
685
+ const type = input.elementType?.toLowerCase() || "";
686
+ const inputType = input.inputType?.toLowerCase() || "";
687
+ if (type === "button" || inputType === "submit") {
688
+ if (text.match(/submit|send|save|confirm|ok|done|finish|apply/)) {
689
+ return "Submits the form";
690
+ }
691
+ if (text.match(/cancel|close|dismiss|back|exit/)) {
692
+ return "Cancels or closes the current action";
693
+ }
694
+ if (text.match(/delete|remove|trash|clear/)) {
695
+ return "Deletes or removes an item";
696
+ }
697
+ if (text.match(/edit|modify|change|update/)) {
698
+ return "Edits or modifies an item";
699
+ }
700
+ if (text.match(/add|create|new|\+/)) {
701
+ return "Creates or adds a new item";
702
+ }
703
+ if (text.match(/search|find|lookup/)) {
704
+ return "Performs a search";
705
+ }
706
+ if (text.match(/login|sign.?in/)) {
707
+ return "Signs the user in";
708
+ }
709
+ if (text.match(/logout|sign.?out/)) {
710
+ return "Signs the user out";
711
+ }
712
+ if (text.match(/register|sign.?up|join/)) {
713
+ return "Creates a new account";
714
+ }
715
+ if (text.match(/next|continue|proceed/)) {
716
+ return "Proceeds to the next step";
717
+ }
718
+ if (text.match(/previous|back|return/)) {
719
+ return "Returns to the previous step";
720
+ }
721
+ }
722
+ if (type === "input" || type === "textarea") {
723
+ if (inputType === "email") return "Accepts email address input";
724
+ if (inputType === "password") return "Accepts password input";
725
+ if (inputType === "search") return "Accepts search query input";
726
+ if (inputType === "tel") return "Accepts phone number input";
727
+ if (inputType === "url") return "Accepts URL input";
728
+ if (inputType === "number") return "Accepts numeric input";
729
+ if (inputType === "date") return "Accepts date input";
730
+ if (inputType === "file") return "Accepts file upload";
731
+ }
732
+ if (type === "checkbox") {
733
+ return "Toggles an option on or off";
734
+ }
735
+ if (type === "radio") {
736
+ return "Selects one option from a group";
737
+ }
738
+ if (type === "select") {
739
+ return "Selects an option from a dropdown";
740
+ }
741
+ if (type === "link") {
742
+ return "Navigates to another page";
743
+ }
744
+ return void 0;
745
+ }
746
+ function generateSuggestedActions(input) {
747
+ const type = input.elementType?.toLowerCase() || "";
748
+ const inputType = input.inputType?.toLowerCase() || "";
749
+ const text = (input.textContent || input.ariaLabel || "").toLowerCase();
750
+ const actions = [];
751
+ switch (type) {
752
+ case "button":
753
+ actions.push(`click "${text || "this button"}"`);
754
+ break;
755
+ case "input":
756
+ if (inputType === "checkbox") {
757
+ actions.push("check to enable", "uncheck to disable");
758
+ } else if (inputType === "radio") {
759
+ actions.push("select this option");
760
+ } else {
761
+ actions.push(`type into "${text || "this field"}"`);
762
+ actions.push("clear the field");
763
+ }
764
+ break;
765
+ case "textarea":
766
+ actions.push(`type into "${text || "this text area"}"`);
767
+ actions.push("clear the content");
768
+ break;
769
+ case "select":
770
+ actions.push(`select an option from "${text || "this dropdown"}"`);
771
+ break;
772
+ case "checkbox":
773
+ actions.push("check to enable", "uncheck to disable");
774
+ break;
775
+ case "radio":
776
+ actions.push("select this option");
777
+ break;
778
+ case "link":
779
+ actions.push(`click to navigate to "${text || "the linked page"}"`);
780
+ break;
781
+ case "switch":
782
+ actions.push("toggle on", "toggle off");
783
+ break;
784
+ default:
785
+ actions.push("click");
786
+ }
787
+ return actions;
788
+ }
789
+ function getSynonyms(word) {
790
+ const normalized = word.toLowerCase().trim();
791
+ return SYNONYMS[normalized] || [];
792
+ }
793
+ function areSynonyms(word1, word2) {
794
+ const w1 = word1.toLowerCase().trim();
795
+ const w2 = word2.toLowerCase().trim();
796
+ if (w1 === w2) return true;
797
+ const synonyms1 = SYNONYMS[w1] || [];
798
+ const synonyms2 = SYNONYMS[w2] || [];
799
+ return synonyms1.includes(w2) || synonyms2.includes(w1);
800
+ }
801
+
225
802
  // src/core/registry.ts
226
803
  function getElementState(element) {
227
804
  const rect = element.getBoundingClientRect();
@@ -420,9 +997,13 @@ var UIBridgeRegistry = class {
420
997
  registerElement(id, element, options = {}) {
421
998
  const type = options.type ?? inferElementType(element);
422
999
  const actions = options.actions ?? inferActions(type);
423
- element.setAttribute("data-ui-id", id);
1000
+ const existingUiId = element.getAttribute("data-ui-id");
1001
+ const actualId = existingUiId || id;
1002
+ if (!existingUiId) {
1003
+ element.setAttribute("data-ui-id", actualId);
1004
+ }
424
1005
  const registered = {
425
- id,
1006
+ id: actualId,
426
1007
  element,
427
1008
  type,
428
1009
  label: options.label,
@@ -433,8 +1014,8 @@ var UIBridgeRegistry = class {
433
1014
  registeredAt: Date.now(),
434
1015
  mounted: true
435
1016
  };
436
- this.elements.set(id, registered);
437
- this.emit("element:registered", { id, type, label: options.label });
1017
+ this.elements.set(actualId, registered);
1018
+ this.emit("element:registered", { id: actualId, type, label: options.label });
438
1019
  return registered;
439
1020
  }
440
1021
  /**
@@ -474,6 +1055,209 @@ var UIBridgeRegistry = class {
474
1055
  }
475
1056
  return void 0;
476
1057
  }
1058
+ /**
1059
+ * Search for elements using AI search criteria
1060
+ */
1061
+ searchElements(criteria) {
1062
+ const results = [];
1063
+ const threshold = criteria.fuzzyThreshold ?? 0.7;
1064
+ for (const element of this.elements.values()) {
1065
+ if (!element.mounted) continue;
1066
+ const state = element.getState();
1067
+ if (!criteria.fuzzy && !state.visible) continue;
1068
+ const aliases = element.aliases ?? this.generateElementAliases(element);
1069
+ const textContent = state.textContent?.trim() || "";
1070
+ const label = element.label || "";
1071
+ let maxScore = 0;
1072
+ const matchReasons = [];
1073
+ const scores = {};
1074
+ if (criteria.text) {
1075
+ if (textContent.toLowerCase() === criteria.text.toLowerCase() || label.toLowerCase() === criteria.text.toLowerCase()) {
1076
+ maxScore = 1;
1077
+ matchReasons.push("exact text match");
1078
+ scores.text = 1;
1079
+ } else if (criteria.fuzzy !== false) {
1080
+ const textResult = fuzzyMatch(criteria.text, textContent, { threshold });
1081
+ const labelResult = fuzzyMatch(criteria.text, label, { threshold });
1082
+ const bestResult = textResult.similarity > labelResult.similarity ? textResult : labelResult;
1083
+ if (bestResult.isMatch) {
1084
+ scores.text = bestResult.similarity;
1085
+ if (bestResult.similarity > maxScore) {
1086
+ maxScore = bestResult.similarity;
1087
+ matchReasons.push(`text similarity: ${(bestResult.similarity * 100).toFixed(0)}%`);
1088
+ }
1089
+ }
1090
+ }
1091
+ }
1092
+ if (criteria.textContains) {
1093
+ if (textContent.toLowerCase().includes(criteria.textContains.toLowerCase()) || label.toLowerCase().includes(criteria.textContains.toLowerCase())) {
1094
+ const containsScore = 0.85;
1095
+ scores.text = Math.max(scores.text ?? 0, containsScore);
1096
+ if (containsScore > maxScore) {
1097
+ maxScore = containsScore;
1098
+ matchReasons.push("text contains");
1099
+ }
1100
+ }
1101
+ }
1102
+ if (criteria.accessibleName) {
1103
+ const ariaLabel = element.element.getAttribute("aria-label") || "";
1104
+ const accessibleName = ariaLabel || label || textContent;
1105
+ if (accessibleName.toLowerCase() === criteria.accessibleName.toLowerCase()) {
1106
+ scores.accessibility = 1;
1107
+ if (1 > maxScore) {
1108
+ maxScore = 1;
1109
+ matchReasons.push("accessible name match");
1110
+ }
1111
+ } else if (criteria.fuzzy !== false) {
1112
+ const result = fuzzyMatch(criteria.accessibleName, accessibleName, { threshold });
1113
+ if (result.isMatch) {
1114
+ scores.accessibility = result.similarity;
1115
+ if (result.similarity > maxScore) {
1116
+ maxScore = result.similarity;
1117
+ matchReasons.push(`accessible name similarity: ${(result.similarity * 100).toFixed(0)}%`);
1118
+ }
1119
+ }
1120
+ }
1121
+ }
1122
+ if (criteria.role) {
1123
+ const role = element.element.getAttribute("role") || this.inferRole(element.type);
1124
+ if (role?.toLowerCase() === criteria.role.toLowerCase()) {
1125
+ scores.role = 1;
1126
+ if (1 > maxScore) {
1127
+ maxScore = 1;
1128
+ matchReasons.push(`role: ${criteria.role}`);
1129
+ }
1130
+ }
1131
+ }
1132
+ if (criteria.type) {
1133
+ if (element.type === criteria.type) {
1134
+ const typeScore = 0.9;
1135
+ scores.role = Math.max(scores.role ?? 0, typeScore);
1136
+ if (typeScore > maxScore) {
1137
+ maxScore = typeScore;
1138
+ matchReasons.push(`type: ${criteria.type}`);
1139
+ }
1140
+ }
1141
+ }
1142
+ for (const alias of aliases) {
1143
+ const searchText = criteria.text || criteria.textContains || criteria.accessibleName;
1144
+ if (searchText) {
1145
+ if (alias.toLowerCase() === searchText.toLowerCase()) {
1146
+ scores.fuzzy = 1;
1147
+ if (1 > maxScore) {
1148
+ maxScore = 1;
1149
+ matchReasons.push(`alias: "${alias}"`);
1150
+ }
1151
+ } else if (criteria.fuzzy !== false) {
1152
+ const result = fuzzyMatch(searchText, alias, { threshold });
1153
+ if (result.isMatch && result.similarity > (scores.fuzzy ?? 0)) {
1154
+ scores.fuzzy = result.similarity;
1155
+ if (result.similarity > maxScore) {
1156
+ maxScore = result.similarity;
1157
+ matchReasons.push(`fuzzy alias: "${alias}"`);
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+ }
1163
+ if (maxScore >= threshold) {
1164
+ const aiElement = {
1165
+ id: element.id,
1166
+ type: element.type,
1167
+ label: element.label,
1168
+ tagName: element.element.tagName.toLowerCase(),
1169
+ role: element.element.getAttribute("role") || void 0,
1170
+ accessibleName: element.element.getAttribute("aria-label") || element.label,
1171
+ actions: element.actions,
1172
+ state,
1173
+ registered: true,
1174
+ description: element.description || generateDescription({
1175
+ textContent,
1176
+ ariaLabel: element.element.getAttribute("aria-label"),
1177
+ elementType: element.type,
1178
+ id: element.id,
1179
+ labelText: element.label
1180
+ }),
1181
+ aliases,
1182
+ purpose: element.purpose,
1183
+ suggestedActions: [],
1184
+ semanticType: element.semanticType
1185
+ };
1186
+ results.push({
1187
+ element: aiElement,
1188
+ confidence: maxScore,
1189
+ matchReasons,
1190
+ scores
1191
+ });
1192
+ }
1193
+ }
1194
+ results.sort((a, b) => b.confidence - a.confidence);
1195
+ return results;
1196
+ }
1197
+ /**
1198
+ * Find element by visible text
1199
+ */
1200
+ findByText(text, fuzzy = true) {
1201
+ const results = this.searchElements({ text, fuzzy, fuzzyThreshold: fuzzy ? 0.7 : 1 });
1202
+ if (results.length > 0) {
1203
+ return this.elements.get(results[0].element.id);
1204
+ }
1205
+ return void 0;
1206
+ }
1207
+ /**
1208
+ * Find element by accessible name
1209
+ */
1210
+ findByAccessibleName(name) {
1211
+ const results = this.searchElements({ accessibleName: name, fuzzy: true });
1212
+ if (results.length > 0) {
1213
+ return this.elements.get(results[0].element.id);
1214
+ }
1215
+ return void 0;
1216
+ }
1217
+ /**
1218
+ * Generate aliases for an element
1219
+ */
1220
+ generateElementAliases(element) {
1221
+ const state = element.getState();
1222
+ return generateAliases({
1223
+ textContent: state.textContent,
1224
+ ariaLabel: element.element.getAttribute("aria-label"),
1225
+ placeholder: element.element.getAttribute("placeholder"),
1226
+ title: element.element.getAttribute("title"),
1227
+ elementType: element.type,
1228
+ tagName: element.element.tagName.toLowerCase(),
1229
+ id: element.id,
1230
+ labelText: element.label
1231
+ });
1232
+ }
1233
+ /**
1234
+ * Infer ARIA role from element type
1235
+ */
1236
+ inferRole(type) {
1237
+ const roleMap = {
1238
+ button: "button",
1239
+ input: "textbox",
1240
+ select: "combobox",
1241
+ checkbox: "checkbox",
1242
+ radio: "radio",
1243
+ link: "link",
1244
+ form: void 0,
1245
+ textarea: "textbox",
1246
+ menu: "menu",
1247
+ menuitem: "menuitem",
1248
+ tab: "tab",
1249
+ dialog: "dialog",
1250
+ custom: void 0,
1251
+ switch: "switch",
1252
+ slider: "slider",
1253
+ combobox: "combobox",
1254
+ listbox: "listbox",
1255
+ option: "option",
1256
+ textbox: "textbox",
1257
+ generic: void 0
1258
+ };
1259
+ return roleMap[type];
1260
+ }
477
1261
  /**
478
1262
  * Register a component
479
1263
  */
@@ -3495,7 +4279,7 @@ function useUIBridge() {
3495
4279
  },
3496
4280
  [context]
3497
4281
  );
3498
- const getElementState5 = react.useCallback(
4282
+ const getElementState6 = react.useCallback(
3499
4283
  (id) => {
3500
4284
  const element = context?.registry.getElement(id);
3501
4285
  return element?.getState();
@@ -3544,7 +4328,7 @@ function useUIBridge() {
3544
4328
  runWorkflow,
3545
4329
  getElement,
3546
4330
  getComponent,
3547
- getElementState: getElementState5,
4331
+ getElementState: getElementState6,
3548
4332
  registerWorkflow,
3549
4333
  unregisterWorkflow,
3550
4334
  captureRenderLog,
@@ -4186,24 +4970,24 @@ function useAutoRegister(options = {}) {
4186
4970
  const type = inferElementType2(element);
4187
4971
  const actions = inferActions2(type);
4188
4972
  const label = getAccessibleLabel(element);
4189
- bridge.registry.registerElement(uniqueId, element, {
4973
+ const registered = bridge.registry.registerElement(uniqueId, element, {
4190
4974
  type,
4191
4975
  actions,
4192
4976
  label
4193
4977
  });
4194
- registeredElementsRef.current.set(element, uniqueId);
4195
- onRegister?.(uniqueId, element);
4978
+ registeredElementsRef.current.set(element, registered.id);
4979
+ onRegister?.(registered.id, element);
4196
4980
  } else {
4197
4981
  const type = inferElementType2(element);
4198
4982
  const actions = inferActions2(type);
4199
4983
  const label = getAccessibleLabel(element);
4200
- bridge.registry.registerElement(id, element, {
4984
+ const registered = bridge.registry.registerElement(id, element, {
4201
4985
  type,
4202
4986
  actions,
4203
4987
  label
4204
4988
  });
4205
- registeredElementsRef.current.set(element, id);
4206
- onRegister?.(id, element);
4989
+ registeredElementsRef.current.set(element, registered.id);
4990
+ onRegister?.(registered.id, element);
4207
4991
  }
4208
4992
  },
4209
4993
  [bridge, idStrategy, customGenerateId, onRegister]
@@ -4662,39 +5446,3353 @@ function Inspector({ getRegisteredElement, initialActive }) {
4662
5446
  ] });
4663
5447
  }
4664
5448
 
5449
+ // src/ai/search-engine.ts
5450
+ var DEFAULT_SEARCH_CONFIG = {
5451
+ fuzzyThreshold: 0.7,
5452
+ textWeight: 0.35,
5453
+ accessibilityWeight: 0.25,
5454
+ roleWeight: 0.15,
5455
+ spatialWeight: 0.1,
5456
+ aliasWeight: 0.15,
5457
+ maxResults: 20,
5458
+ includeHidden: false
5459
+ };
5460
+ var SearchEngine = class {
5461
+ // Cache valid for 100ms
5462
+ constructor(config = {}) {
5463
+ this.cachedElements = [];
5464
+ this.cacheTimestamp = 0;
5465
+ this.cacheValidityMs = 100;
5466
+ this.config = { ...DEFAULT_SEARCH_CONFIG, ...config };
5467
+ }
5468
+ /**
5469
+ * Update cached elements from various sources
5470
+ */
5471
+ updateElements(elements, getState) {
5472
+ this.cachedElements = elements.map((el) => this.toSearchable(el, getState));
5473
+ this.cacheTimestamp = Date.now();
5474
+ }
5475
+ /**
5476
+ * Convert an element to searchable format
5477
+ */
5478
+ toSearchable(element, getState) {
5479
+ let state;
5480
+ let textContent;
5481
+ let tagName;
5482
+ let role;
5483
+ let ariaLabel;
5484
+ let placeholder;
5485
+ let title;
5486
+ let labelText;
5487
+ let value;
5488
+ if ("getState" in element && typeof element.getState === "function") {
5489
+ state = getState ? getState(element) : element.getState();
5490
+ textContent = state.textContent || void 0;
5491
+ tagName = element.element.tagName.toLowerCase();
5492
+ role = element.element.getAttribute("role") || void 0;
5493
+ ariaLabel = element.element.getAttribute("aria-label") || void 0;
5494
+ placeholder = element.element.getAttribute("placeholder") || void 0;
5495
+ title = element.element.getAttribute("title") || void 0;
5496
+ if (element.element.id) {
5497
+ const labelEl = document.querySelector(`label[for="${element.element.id}"]`);
5498
+ labelText = labelEl?.textContent?.trim() || void 0;
5499
+ }
5500
+ if (element.element instanceof HTMLInputElement || element.element instanceof HTMLTextAreaElement || element.element instanceof HTMLSelectElement) {
5501
+ value = element.element.value || void 0;
5502
+ }
5503
+ } else {
5504
+ const discovered = element;
5505
+ state = discovered.state;
5506
+ textContent = state.textContent || void 0;
5507
+ tagName = discovered.tagName;
5508
+ role = discovered.role || void 0;
5509
+ ariaLabel = discovered.accessibleName || void 0;
5510
+ }
5511
+ const aliases = generateAliases({
5512
+ textContent,
5513
+ ariaLabel,
5514
+ placeholder,
5515
+ title,
5516
+ elementType: element.type,
5517
+ id: element.id,
5518
+ labelText,
5519
+ value
5520
+ });
5521
+ const description = generateDescription({
5522
+ textContent,
5523
+ ariaLabel,
5524
+ placeholder,
5525
+ title,
5526
+ elementType: element.type,
5527
+ id: element.id,
5528
+ labelText
5529
+ });
5530
+ return {
5531
+ id: element.id,
5532
+ element,
5533
+ state,
5534
+ textContent,
5535
+ ariaLabel,
5536
+ placeholder,
5537
+ title,
5538
+ role,
5539
+ tagName,
5540
+ type: element.type,
5541
+ aliases,
5542
+ description,
5543
+ rect: state.rect,
5544
+ labelText,
5545
+ value
5546
+ };
5547
+ }
5548
+ /**
5549
+ * Search for elements matching the criteria
5550
+ */
5551
+ search(criteria, elements) {
5552
+ const startTime = performance.now();
5553
+ if (elements) {
5554
+ this.updateElements(elements);
5555
+ }
5556
+ let searchableElements = this.cachedElements;
5557
+ if (!this.config.includeHidden && !criteria.fuzzy) {
5558
+ searchableElements = searchableElements.filter((el) => el.state.visible);
5559
+ }
5560
+ const results = [];
5561
+ for (const searchable of searchableElements) {
5562
+ const result = this.scoreElement(searchable, criteria);
5563
+ if (result.confidence >= (criteria.fuzzyThreshold ?? this.config.fuzzyThreshold)) {
5564
+ results.push(result);
5565
+ }
5566
+ }
5567
+ results.sort((a, b) => b.confidence - a.confidence);
5568
+ const limitedResults = results.slice(0, this.config.maxResults);
5569
+ return {
5570
+ results: limitedResults,
5571
+ bestMatch: limitedResults.length > 0 ? limitedResults[0] : null,
5572
+ scannedCount: searchableElements.length,
5573
+ durationMs: performance.now() - startTime,
5574
+ criteria,
5575
+ timestamp: Date.now()
5576
+ };
5577
+ }
5578
+ /**
5579
+ * Find the best matching element
5580
+ */
5581
+ findBest(criteria, elements) {
5582
+ const response = this.search(criteria, elements);
5583
+ return response.bestMatch;
5584
+ }
5585
+ /**
5586
+ * Find elements by text content
5587
+ */
5588
+ findByText(text, fuzzy = true, elements) {
5589
+ return this.search({ text, fuzzy }, elements).results;
5590
+ }
5591
+ /**
5592
+ * Find elements by role
5593
+ */
5594
+ findByRole(role, name, elements) {
5595
+ const criteria = { role };
5596
+ if (name) {
5597
+ criteria.accessibleName = name;
5598
+ }
5599
+ return this.search(criteria, elements).results;
5600
+ }
5601
+ /**
5602
+ * Find elements by accessible name
5603
+ */
5604
+ findByAccessibleName(name, elements) {
5605
+ return this.search({ accessibleName: name, fuzzy: true }, elements).results;
5606
+ }
5607
+ /**
5608
+ * Find elements near another element
5609
+ */
5610
+ findNear(referenceId, criteria, elements) {
5611
+ return this.search({ ...criteria, near: referenceId }, elements).results;
5612
+ }
5613
+ /**
5614
+ * Find elements within a container
5615
+ */
5616
+ findWithin(containerId, criteria, elements) {
5617
+ return this.search({ ...criteria, within: containerId }, elements).results;
5618
+ }
5619
+ /**
5620
+ * Score an element against search criteria
5621
+ */
5622
+ scoreElement(searchable, criteria) {
5623
+ const scores = {};
5624
+ const matchReasons = [];
5625
+ let totalWeight = 0;
5626
+ let weightedScore = 0;
5627
+ const fuzzyConfig = {
5628
+ ...DEFAULT_FUZZY_CONFIG,
5629
+ threshold: criteria.fuzzyThreshold ?? this.config.fuzzyThreshold
5630
+ };
5631
+ if (criteria.text) {
5632
+ const textScore = this.scoreTextMatch(searchable, criteria.text, criteria.fuzzy !== false, fuzzyConfig.threshold);
5633
+ scores.text = textScore.score;
5634
+ if (textScore.score > 0) {
5635
+ matchReasons.push(...textScore.reasons);
5636
+ }
5637
+ weightedScore += textScore.score * this.config.textWeight;
5638
+ totalWeight += this.config.textWeight;
5639
+ }
5640
+ if (criteria.textContains) {
5641
+ const containsScore = this.scoreContainsMatch(searchable, criteria.textContains, criteria.fuzzy !== false);
5642
+ scores.text = Math.max(scores.text || 0, containsScore.score);
5643
+ if (containsScore.score > 0 && containsScore.reasons.length > 0) {
5644
+ matchReasons.push(...containsScore.reasons);
5645
+ }
5646
+ weightedScore += containsScore.score * this.config.textWeight;
5647
+ totalWeight += this.config.textWeight;
5648
+ }
5649
+ if (criteria.accessibleName) {
5650
+ const accessibilityScore = this.scoreAccessibilityMatch(
5651
+ searchable,
5652
+ criteria.accessibleName,
5653
+ criteria.fuzzy !== false,
5654
+ fuzzyConfig.threshold
5655
+ );
5656
+ scores.accessibility = accessibilityScore.score;
5657
+ if (accessibilityScore.score > 0) {
5658
+ matchReasons.push(...accessibilityScore.reasons);
5659
+ }
5660
+ weightedScore += accessibilityScore.score * this.config.accessibilityWeight;
5661
+ totalWeight += this.config.accessibilityWeight;
5662
+ }
5663
+ if (criteria.role) {
5664
+ const roleScore = this.scoreRoleMatch(searchable, criteria.role);
5665
+ scores.role = roleScore.score;
5666
+ if (roleScore.score > 0) {
5667
+ matchReasons.push(...roleScore.reasons);
5668
+ }
5669
+ weightedScore += roleScore.score * this.config.roleWeight;
5670
+ totalWeight += this.config.roleWeight;
5671
+ }
5672
+ if (criteria.type) {
5673
+ const typeMatch = searchable.type.toLowerCase() === criteria.type.toLowerCase();
5674
+ if (typeMatch) {
5675
+ matchReasons.push(`type: ${criteria.type}`);
5676
+ weightedScore += 1 * this.config.roleWeight;
5677
+ totalWeight += this.config.roleWeight;
5678
+ }
5679
+ }
5680
+ if (criteria.near) {
5681
+ const spatialScore = this.scoreSpatialMatch(searchable, criteria.near);
5682
+ scores.spatial = spatialScore.score;
5683
+ if (spatialScore.score > 0) {
5684
+ matchReasons.push(...spatialScore.reasons);
5685
+ }
5686
+ weightedScore += spatialScore.score * this.config.spatialWeight;
5687
+ totalWeight += this.config.spatialWeight;
5688
+ }
5689
+ if (criteria.placeholder && searchable.placeholder) {
5690
+ const placeholderResult = fuzzyMatch(searchable.placeholder, criteria.placeholder, fuzzyConfig);
5691
+ if (placeholderResult.isMatch) {
5692
+ matchReasons.push(`placeholder matches`);
5693
+ weightedScore += placeholderResult.similarity * this.config.textWeight;
5694
+ totalWeight += this.config.textWeight;
5695
+ }
5696
+ }
5697
+ if (criteria.title && searchable.title) {
5698
+ const titleResult = fuzzyMatch(searchable.title, criteria.title, fuzzyConfig);
5699
+ if (titleResult.isMatch) {
5700
+ matchReasons.push(`title matches`);
5701
+ weightedScore += titleResult.similarity * this.config.textWeight;
5702
+ totalWeight += this.config.textWeight;
5703
+ }
5704
+ }
5705
+ if (criteria.idPattern) {
5706
+ const idMatch = this.matchPattern(searchable.id, criteria.idPattern);
5707
+ if (idMatch) {
5708
+ matchReasons.push(`id matches pattern`);
5709
+ weightedScore += 1 * this.config.textWeight;
5710
+ totalWeight += this.config.textWeight;
5711
+ }
5712
+ }
5713
+ const aliasScore = this.scoreAliasMatch(searchable, criteria, fuzzyConfig.threshold);
5714
+ if (aliasScore.score > 0) {
5715
+ scores.fuzzy = aliasScore.score;
5716
+ matchReasons.push(...aliasScore.reasons);
5717
+ weightedScore += aliasScore.score * this.config.aliasWeight;
5718
+ totalWeight += this.config.aliasWeight;
5719
+ }
5720
+ const confidence = totalWeight > 0 ? weightedScore / totalWeight : 0;
5721
+ const aiElement = this.toAIDiscoveredElement(searchable);
5722
+ return {
5723
+ element: aiElement,
5724
+ confidence,
5725
+ matchReasons,
5726
+ scores
5727
+ };
5728
+ }
5729
+ /**
5730
+ * Score text match
5731
+ */
5732
+ scoreTextMatch(searchable, text, fuzzy, threshold) {
5733
+ const reasons = [];
5734
+ let maxScore = 0;
5735
+ const textsToMatch = [
5736
+ searchable.textContent,
5737
+ searchable.labelText,
5738
+ searchable.value
5739
+ ].filter(Boolean);
5740
+ for (const targetText of textsToMatch) {
5741
+ if (targetText.toLowerCase() === text.toLowerCase()) {
5742
+ maxScore = Math.max(maxScore, 1);
5743
+ reasons.push("exact text match");
5744
+ continue;
5745
+ }
5746
+ if (fuzzy) {
5747
+ const result = fuzzyMatch(targetText, text, { threshold });
5748
+ if (result.isMatch && result.similarity > maxScore) {
5749
+ maxScore = result.similarity;
5750
+ reasons.push(`text similarity: ${(result.similarity * 100).toFixed(0)}%`);
5751
+ }
5752
+ const wordSim = wordSimilarity(targetText, text, { threshold });
5753
+ if (wordSim > maxScore && wordSim >= threshold) {
5754
+ maxScore = wordSim;
5755
+ reasons.push(`word match: ${(wordSim * 100).toFixed(0)}%`);
5756
+ }
5757
+ }
5758
+ }
5759
+ return { score: maxScore, reasons };
5760
+ }
5761
+ /**
5762
+ * Score contains match
5763
+ */
5764
+ scoreContainsMatch(searchable, text, fuzzy) {
5765
+ const reasons = [];
5766
+ let maxScore = 0;
5767
+ const textsToMatch = [
5768
+ searchable.textContent,
5769
+ searchable.labelText,
5770
+ searchable.ariaLabel
5771
+ ].filter(Boolean);
5772
+ for (const targetText of textsToMatch) {
5773
+ if (targetText.toLowerCase().includes(text.toLowerCase())) {
5774
+ maxScore = Math.max(maxScore, 0.9);
5775
+ reasons.push("text contains match");
5776
+ continue;
5777
+ }
5778
+ if (fuzzy && fuzzyContains(targetText, text)) {
5779
+ maxScore = Math.max(maxScore, 0.7);
5780
+ reasons.push("fuzzy contains match");
5781
+ }
5782
+ }
5783
+ return { score: maxScore, reasons };
5784
+ }
5785
+ /**
5786
+ * Score accessibility match
5787
+ */
5788
+ scoreAccessibilityMatch(searchable, name, fuzzy, threshold) {
5789
+ const reasons = [];
5790
+ let maxScore = 0;
5791
+ const accessibleNames = [
5792
+ searchable.ariaLabel,
5793
+ searchable.ariaLabelledBy,
5794
+ searchable.labelText,
5795
+ searchable.title
5796
+ ].filter(Boolean);
5797
+ for (const accessibleName of accessibleNames) {
5798
+ if (accessibleName.toLowerCase() === name.toLowerCase()) {
5799
+ maxScore = Math.max(maxScore, 1);
5800
+ reasons.push("exact accessible name match");
5801
+ continue;
5802
+ }
5803
+ if (fuzzy) {
5804
+ const result = fuzzyMatch(accessibleName, name, { threshold });
5805
+ if (result.isMatch && result.similarity > maxScore) {
5806
+ maxScore = result.similarity;
5807
+ reasons.push(`accessible name similarity: ${(result.similarity * 100).toFixed(0)}%`);
5808
+ }
5809
+ }
5810
+ }
5811
+ return { score: maxScore, reasons };
5812
+ }
5813
+ /**
5814
+ * Score role match
5815
+ */
5816
+ scoreRoleMatch(searchable, role) {
5817
+ const reasons = [];
5818
+ const normalizedRole = role.toLowerCase();
5819
+ if (searchable.role?.toLowerCase() === normalizedRole) {
5820
+ return { score: 1, reasons: [`role: ${role}`] };
5821
+ }
5822
+ const tagRoleMap = {
5823
+ button: ["button", "input[type=button]", "input[type=submit]"],
5824
+ textbox: ["input", "textarea"],
5825
+ checkbox: ["input[type=checkbox]"],
5826
+ radio: ["input[type=radio]"],
5827
+ link: ["a"],
5828
+ listbox: ["select"],
5829
+ combobox: ["select", "input[list]"],
5830
+ navigation: ["nav"],
5831
+ main: ["main"],
5832
+ heading: ["h1", "h2", "h3", "h4", "h5", "h6"]
5833
+ };
5834
+ const inferredRoles = tagRoleMap[normalizedRole] || [];
5835
+ if (inferredRoles.some((r) => searchable.tagName === r || searchable.type.toLowerCase() === normalizedRole)) {
5836
+ return { score: 0.8, reasons: [`inferred role: ${role}`] };
5837
+ }
5838
+ return { score: 0, reasons };
5839
+ }
5840
+ /**
5841
+ * Score spatial match (proximity to another element)
5842
+ */
5843
+ scoreSpatialMatch(searchable, nearId) {
5844
+ const reference = this.cachedElements.find((el) => el.id === nearId);
5845
+ if (!reference) {
5846
+ return { score: 0, reasons: [] };
5847
+ }
5848
+ const distance = this.calculateDistance(searchable.rect, reference.rect);
5849
+ const nearThreshold = 200;
5850
+ if (distance > nearThreshold * 3) {
5851
+ return { score: 0, reasons: [] };
5852
+ }
5853
+ const score = Math.max(0, 1 - distance / (nearThreshold * 3));
5854
+ return {
5855
+ score,
5856
+ reasons: [`${distance.toFixed(0)}px from ${nearId}`]
5857
+ };
5858
+ }
5859
+ /**
5860
+ * Calculate distance between two element rectangles
5861
+ */
5862
+ calculateDistance(rect1, rect2) {
5863
+ const center1 = {
5864
+ x: rect1.x + rect1.width / 2,
5865
+ y: rect1.y + rect1.height / 2
5866
+ };
5867
+ const center2 = {
5868
+ x: rect2.x + rect2.width / 2,
5869
+ y: rect2.y + rect2.height / 2
5870
+ };
5871
+ return Math.sqrt(Math.pow(center1.x - center2.x, 2) + Math.pow(center1.y - center2.y, 2));
5872
+ }
5873
+ /**
5874
+ * Score alias match
5875
+ */
5876
+ scoreAliasMatch(searchable, criteria, threshold) {
5877
+ const reasons = [];
5878
+ let maxScore = 0;
5879
+ const searchTerms = [];
5880
+ if (criteria.text) searchTerms.push(criteria.text);
5881
+ if (criteria.textContains) searchTerms.push(criteria.textContains);
5882
+ if (criteria.accessibleName) searchTerms.push(criteria.accessibleName);
5883
+ for (const searchTerm of searchTerms) {
5884
+ const termLower = searchTerm.toLowerCase();
5885
+ for (const alias of searchable.aliases) {
5886
+ if (alias === termLower) {
5887
+ maxScore = Math.max(maxScore, 1);
5888
+ reasons.push(`alias match: "${alias}"`);
5889
+ continue;
5890
+ }
5891
+ const searchWords = termLower.split(/\s+/);
5892
+ const aliasWords = alias.split(/\s+/);
5893
+ for (const searchWord of searchWords) {
5894
+ for (const aliasWord of aliasWords) {
5895
+ if (areSynonyms(searchWord, aliasWord)) {
5896
+ maxScore = Math.max(maxScore, 0.85);
5897
+ reasons.push(`synonym match: "${searchWord}" ~ "${aliasWord}"`);
5898
+ }
5899
+ }
5900
+ }
5901
+ const result = fuzzyMatch(alias, termLower, { threshold });
5902
+ if (result.isMatch && result.similarity > maxScore) {
5903
+ maxScore = result.similarity;
5904
+ reasons.push(`fuzzy alias: "${alias}" (${(result.similarity * 100).toFixed(0)}%)`);
5905
+ }
5906
+ const tokenSim = tokenSimilarity(alias, termLower);
5907
+ if (tokenSim > maxScore && tokenSim >= threshold) {
5908
+ maxScore = tokenSim;
5909
+ reasons.push(`token match: "${alias}"`);
5910
+ }
5911
+ }
5912
+ }
5913
+ return { score: maxScore, reasons };
5914
+ }
5915
+ /**
5916
+ * Match a string against a pattern (supports * wildcard)
5917
+ */
5918
+ matchPattern(str, pattern) {
5919
+ const regexPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
5920
+ return new RegExp(`^${regexPattern}$`, "i").test(str);
5921
+ }
5922
+ /**
5923
+ * Convert searchable element to AI discovered element
5924
+ */
5925
+ toAIDiscoveredElement(searchable) {
5926
+ const discoveredBase = "getState" in searchable.element ? {
5927
+ id: searchable.id,
5928
+ type: searchable.type,
5929
+ label: searchable.element.label,
5930
+ tagName: searchable.tagName,
5931
+ role: searchable.role,
5932
+ accessibleName: searchable.ariaLabel,
5933
+ actions: searchable.element.actions,
5934
+ state: searchable.state,
5935
+ registered: true
5936
+ } : searchable.element;
5937
+ return {
5938
+ ...discoveredBase,
5939
+ description: searchable.description,
5940
+ aliases: searchable.aliases,
5941
+ purpose: generatePurpose({
5942
+ textContent: searchable.textContent,
5943
+ ariaLabel: searchable.ariaLabel,
5944
+ elementType: searchable.type,
5945
+ tagName: searchable.tagName
5946
+ }),
5947
+ parentContext: void 0,
5948
+ // Would need DOM traversal
5949
+ suggestedActions: generateSuggestedActions({
5950
+ textContent: searchable.textContent,
5951
+ ariaLabel: searchable.ariaLabel,
5952
+ elementType: searchable.type,
5953
+ tagName: searchable.tagName
5954
+ }),
5955
+ semanticType: this.inferSemanticType(searchable),
5956
+ labelText: searchable.labelText,
5957
+ placeholder: searchable.placeholder,
5958
+ title: searchable.title
5959
+ };
5960
+ }
5961
+ /**
5962
+ * Infer a semantic type for the element
5963
+ */
5964
+ inferSemanticType(searchable) {
5965
+ const text = (searchable.textContent || searchable.ariaLabel || "").toLowerCase();
5966
+ const type = searchable.type.toLowerCase();
5967
+ if (type === "input" || type === "textarea") {
5968
+ if (searchable.placeholder?.toLowerCase().includes("email") || text.includes("email")) {
5969
+ return "email-input";
5970
+ }
5971
+ if (searchable.placeholder?.toLowerCase().includes("password") || text.includes("password")) {
5972
+ return "password-input";
5973
+ }
5974
+ if (searchable.placeholder?.toLowerCase().includes("search") || text.includes("search")) {
5975
+ return "search-input";
5976
+ }
5977
+ return "text-input";
5978
+ }
5979
+ if (type === "button") {
5980
+ if (text.match(/submit|save|confirm|ok|done|apply/)) return "submit-button";
5981
+ if (text.match(/cancel|close|dismiss/)) return "cancel-button";
5982
+ if (text.match(/delete|remove|trash/)) return "delete-button";
5983
+ if (text.match(/add|create|new|\+/)) return "add-button";
5984
+ if (text.match(/edit|modify/)) return "edit-button";
5985
+ if (text.match(/next|continue/)) return "next-button";
5986
+ if (text.match(/back|previous/)) return "back-button";
5987
+ return "action-button";
5988
+ }
5989
+ if (type === "link") {
5990
+ if (text.match(/home|dashboard/)) return "home-link";
5991
+ if (text.match(/login|sign.?in/)) return "login-link";
5992
+ if (text.match(/logout|sign.?out/)) return "logout-link";
5993
+ return "navigation-link";
5994
+ }
5995
+ return type;
5996
+ }
5997
+ };
5998
+ function createSearchEngine(config) {
5999
+ return new SearchEngine(config);
6000
+ }
6001
+
6002
+ // src/ai/summary-generator.ts
6003
+ var DEFAULT_SUMMARY_CONFIG = {
6004
+ maxLength: 2e3,
6005
+ includeForms: true,
6006
+ includeElementCounts: true,
6007
+ includeModals: true,
6008
+ includeFocused: true,
6009
+ verbosity: "normal"
6010
+ };
6011
+ function generatePageSummary(elements, pageContext, config = {}) {
6012
+ const finalConfig = { ...DEFAULT_SUMMARY_CONFIG, ...config };
6013
+ const lines = [];
6014
+ if (pageContext) {
6015
+ if (pageContext.title) {
6016
+ lines.push(`Page: "${pageContext.title}"`);
6017
+ }
6018
+ if (pageContext.pageType && pageContext.pageType !== "unknown") {
6019
+ lines.push(`Type: ${formatPageType(pageContext.pageType)}`);
6020
+ }
6021
+ }
6022
+ if (finalConfig.includeElementCounts) {
6023
+ const counts = countElementTypes(elements);
6024
+ const countParts = [];
6025
+ if (counts.button > 0) countParts.push(`${counts.button} button${counts.button > 1 ? "s" : ""}`);
6026
+ if (counts.input > 0) countParts.push(`${counts.input} input${counts.input > 1 ? "s" : ""}`);
6027
+ if (counts.link > 0) countParts.push(`${counts.link} link${counts.link > 1 ? "s" : ""}`);
6028
+ if (counts.select > 0) countParts.push(`${counts.select} dropdown${counts.select > 1 ? "s" : ""}`);
6029
+ if (counts.checkbox > 0) countParts.push(`${counts.checkbox} checkbox${counts.checkbox > 1 ? "es" : ""}`);
6030
+ if (countParts.length > 0) {
6031
+ lines.push(`Contains: ${countParts.join(", ")}`);
6032
+ }
6033
+ }
6034
+ if (finalConfig.includeForms) {
6035
+ const forms = detectForms(elements);
6036
+ if (forms.length > 0) {
6037
+ lines.push("");
6038
+ lines.push("Forms:");
6039
+ for (const form of forms) {
6040
+ lines.push(generateFormSummary(form, finalConfig.verbosity));
6041
+ }
6042
+ }
6043
+ }
6044
+ if (finalConfig.includeModals && pageContext?.activeModals && pageContext.activeModals.length > 0) {
6045
+ lines.push("");
6046
+ lines.push(`Active modals: ${pageContext.activeModals.join(", ")}`);
6047
+ }
6048
+ if (finalConfig.includeFocused && pageContext?.focusedElement) {
6049
+ lines.push(`Focus: ${pageContext.focusedElement}`);
6050
+ }
6051
+ const keyElements = getKeyElements(elements);
6052
+ if (keyElements.length > 0) {
6053
+ lines.push("");
6054
+ lines.push("Key elements:");
6055
+ for (const el of keyElements) {
6056
+ lines.push(` - ${el.description}${el.state.enabled ? "" : " (disabled)"}`);
6057
+ }
6058
+ }
6059
+ let summary = lines.join("\n");
6060
+ if (summary.length > finalConfig.maxLength) {
6061
+ summary = summary.substring(0, finalConfig.maxLength - 3) + "...";
6062
+ }
6063
+ return summary;
6064
+ }
6065
+ function generateElementDescription(element) {
6066
+ const parts = [];
6067
+ const name = element.accessibleName || element.label || element.state.textContent?.trim();
6068
+ if (name) {
6069
+ parts.push(`"${truncate(name, 30)}"`);
6070
+ }
6071
+ parts.push(formatElementType(element.type));
6072
+ const stateIndicators = [];
6073
+ if (!element.state.visible) stateIndicators.push("hidden");
6074
+ if (!element.state.enabled) stateIndicators.push("disabled");
6075
+ if (element.state.focused) stateIndicators.push("focused");
6076
+ if (element.state.checked) stateIndicators.push("checked");
6077
+ if (stateIndicators.length > 0) {
6078
+ parts.push(`(${stateIndicators.join(", ")})`);
6079
+ }
6080
+ if (element.state.value && element.type !== "button") {
6081
+ const valuePreview = truncate(element.state.value, 20);
6082
+ parts.push(`value: "${valuePreview}"`);
6083
+ }
6084
+ return parts.join(" ");
6085
+ }
6086
+ function generateFormSummary(form, verbosity) {
6087
+ const lines = [];
6088
+ const formName = form.name || form.purpose || form.id;
6089
+ lines.push(` ${formName}:`);
6090
+ if (verbosity === "brief") {
6091
+ const fieldCount = form.fields.length;
6092
+ const filledCount = form.fields.filter((f) => f.value).length;
6093
+ lines.push(` ${filledCount}/${fieldCount} fields filled, ${form.isValid ? "valid" : "has errors"}`);
6094
+ } else {
6095
+ for (const field of form.fields) {
6096
+ let fieldLine = ` - ${field.label || field.id}`;
6097
+ if (field.value) {
6098
+ fieldLine += ` = "${truncate(field.value, 15)}"`;
6099
+ } else if (field.placeholder) {
6100
+ fieldLine += ` (${field.placeholder})`;
6101
+ } else {
6102
+ fieldLine += " (empty)";
6103
+ }
6104
+ if (!field.valid && field.error) {
6105
+ fieldLine += ` [ERROR: ${field.error}]`;
6106
+ } else if (field.required && !field.value) {
6107
+ fieldLine += " [required]";
6108
+ }
6109
+ lines.push(fieldLine);
6110
+ }
6111
+ if (form.submitButton) {
6112
+ lines.push(` Submit: ${form.submitButton}`);
6113
+ }
6114
+ }
6115
+ return lines.join("\n");
6116
+ }
6117
+ function generateSnapshotSummary(snapshot, config = {}) {
6118
+ const finalConfig = { ...DEFAULT_SUMMARY_CONFIG, ...config };
6119
+ const lines = [];
6120
+ lines.push(`Page: "${snapshot.page.title}"`);
6121
+ lines.push(`URL: ${snapshot.page.url}`);
6122
+ if (snapshot.page.pageType) {
6123
+ lines.push(`Type: ${formatPageType(snapshot.page.pageType)}`);
6124
+ }
6125
+ if (finalConfig.includeElementCounts) {
6126
+ const countParts = [];
6127
+ for (const [type, count] of Object.entries(snapshot.elementCounts)) {
6128
+ if (count > 0) {
6129
+ countParts.push(`${count} ${type}${count > 1 ? "s" : ""}`);
6130
+ }
6131
+ }
6132
+ if (countParts.length > 0) {
6133
+ lines.push(`Elements: ${countParts.join(", ")}`);
6134
+ }
6135
+ }
6136
+ if (finalConfig.includeForms && snapshot.forms.length > 0) {
6137
+ lines.push("");
6138
+ lines.push("Forms:");
6139
+ for (const form of snapshot.forms) {
6140
+ lines.push(generateFormStateSummary(form));
6141
+ }
6142
+ }
6143
+ if (finalConfig.includeModals && snapshot.activeModals.length > 0) {
6144
+ lines.push("");
6145
+ lines.push("Active dialogs:");
6146
+ for (const modal of snapshot.activeModals) {
6147
+ lines.push(` - ${modal.title || modal.id} (${modal.type})`);
6148
+ }
6149
+ }
6150
+ if (finalConfig.includeFocused && snapshot.focusedElement) {
6151
+ const focused = snapshot.elements.find((e) => e.id === snapshot.focusedElement);
6152
+ if (focused) {
6153
+ lines.push(`Focused: ${generateElementDescription(focused)}`);
6154
+ }
6155
+ }
6156
+ return lines.join("\n");
6157
+ }
6158
+ function generateFormStateSummary(form) {
6159
+ const lines = [];
6160
+ const formName = form.name || form.purpose || form.id;
6161
+ const filledCount = form.fields.filter((f) => f.value).length;
6162
+ const errorCount = form.fields.filter((f) => !f.valid).length;
6163
+ let statusLine = ` ${formName}: ${filledCount}/${form.fields.length} filled`;
6164
+ if (errorCount > 0) {
6165
+ statusLine += `, ${errorCount} error${errorCount > 1 ? "s" : ""}`;
6166
+ }
6167
+ if (form.isDirty) {
6168
+ statusLine += " (modified)";
6169
+ }
6170
+ lines.push(statusLine);
6171
+ for (const field of form.fields) {
6172
+ if (!field.valid && field.error) {
6173
+ lines.push(` ERROR: ${field.label}: ${field.error}`);
6174
+ }
6175
+ }
6176
+ return lines.join("\n");
6177
+ }
6178
+ function generateDiffSummary(appeared, disappeared, modified) {
6179
+ const lines = [];
6180
+ if (appeared.length > 0) {
6181
+ lines.push(`Appeared: ${appeared.join(", ")}`);
6182
+ }
6183
+ if (disappeared.length > 0) {
6184
+ lines.push(`Disappeared: ${disappeared.join(", ")}`);
6185
+ }
6186
+ if (modified.length > 0) {
6187
+ lines.push("Changed:");
6188
+ for (const mod of modified.slice(0, 5)) {
6189
+ lines.push(` - ${mod.description}: ${mod.property} changed from "${mod.from}" to "${mod.to}"`);
6190
+ }
6191
+ if (modified.length > 5) {
6192
+ lines.push(` ... and ${modified.length - 5} more changes`);
6193
+ }
6194
+ }
6195
+ if (lines.length === 0) {
6196
+ return "No changes detected";
6197
+ }
6198
+ return lines.join("\n");
6199
+ }
6200
+ function countElementTypes(elements) {
6201
+ const counts = {};
6202
+ for (const el of elements) {
6203
+ const type = el.type.toLowerCase();
6204
+ counts[type] = (counts[type] || 0) + 1;
6205
+ }
6206
+ return counts;
6207
+ }
6208
+ function detectForms(elements) {
6209
+ const formElements = elements.filter(
6210
+ (el) => el.type === "input" || el.type === "textarea" || el.type === "select" || el.type === "checkbox"
6211
+ );
6212
+ if (formElements.length === 0) return [];
6213
+ const forms = [];
6214
+ const submitButtons = elements.filter(
6215
+ (el) => el.type === "button" && (el.state.textContent?.toLowerCase().includes("submit") || el.state.textContent?.toLowerCase().includes("save") || el.state.textContent?.toLowerCase().includes("send") || el.semanticType === "submit-button")
6216
+ );
6217
+ const defaultForm = {
6218
+ id: "detected-form",
6219
+ purpose: inferFormPurpose(formElements),
6220
+ fields: formElements.map((el) => ({
6221
+ id: el.id,
6222
+ label: el.labelText || el.accessibleName || el.placeholder || el.id,
6223
+ type: el.type,
6224
+ value: el.state.value || "",
6225
+ valid: true,
6226
+ // Can't determine without validation state
6227
+ required: false,
6228
+ // Can't determine without DOM access
6229
+ placeholder: el.placeholder
6230
+ })),
6231
+ isValid: true,
6232
+ submitButton: submitButtons[0]?.id
6233
+ };
6234
+ if (defaultForm.fields.length > 0) {
6235
+ forms.push(defaultForm);
6236
+ }
6237
+ return forms;
6238
+ }
6239
+ function inferFormPurpose(fields) {
6240
+ const labels = fields.map(
6241
+ (f) => (f.labelText || f.accessibleName || f.placeholder || "").toLowerCase()
6242
+ );
6243
+ const allLabels = labels.join(" ");
6244
+ if (allLabels.includes("email") && allLabels.includes("password")) {
6245
+ if (allLabels.includes("confirm") || allLabels.includes("name")) {
6246
+ return "Registration form";
6247
+ }
6248
+ return "Login form";
6249
+ }
6250
+ if (allLabels.includes("search")) {
6251
+ return "Search form";
6252
+ }
6253
+ if (allLabels.includes("address") || allLabels.includes("city") || allLabels.includes("zip")) {
6254
+ return "Address form";
6255
+ }
6256
+ if (allLabels.includes("card") || allLabels.includes("cvv") || allLabels.includes("expir")) {
6257
+ return "Payment form";
6258
+ }
6259
+ if (allLabels.includes("contact") || allLabels.includes("message")) {
6260
+ return "Contact form";
6261
+ }
6262
+ return "Form";
6263
+ }
6264
+ function getKeyElements(elements) {
6265
+ const keyElements = [];
6266
+ const actionButtons = elements.filter(
6267
+ (el) => el.type === "button" && el.state.visible && (el.semanticType?.includes("submit") || el.semanticType?.includes("action") || el.semanticType?.includes("next"))
6268
+ );
6269
+ keyElements.push(...actionButtons.slice(0, 2));
6270
+ const primaryInputs = elements.filter(
6271
+ (el) => (el.type === "input" || el.type === "textarea") && el.state.visible
6272
+ );
6273
+ keyElements.push(...primaryInputs.slice(0, 3));
6274
+ const links = elements.filter((el) => el.type === "link" && el.state.visible);
6275
+ keyElements.push(...links.slice(0, 2));
6276
+ const unique = [...new Map(keyElements.map((e) => [e.id, e])).values()];
6277
+ return unique.slice(0, 8);
6278
+ }
6279
+ function formatPageType(pageType) {
6280
+ const typeLabels = {
6281
+ login: "Login page",
6282
+ dashboard: "Dashboard",
6283
+ form: "Form page",
6284
+ list: "List/table page",
6285
+ detail: "Detail page",
6286
+ search: "Search page",
6287
+ checkout: "Checkout page",
6288
+ settings: "Settings page",
6289
+ unknown: "Unknown"
6290
+ };
6291
+ return typeLabels[pageType || "unknown"] || "Page";
6292
+ }
6293
+ function formatElementType(type) {
6294
+ const typeLabels = {
6295
+ button: "button",
6296
+ input: "input field",
6297
+ textarea: "text area",
6298
+ select: "dropdown",
6299
+ checkbox: "checkbox",
6300
+ radio: "radio button",
6301
+ link: "link",
6302
+ form: "form",
6303
+ menu: "menu",
6304
+ menuitem: "menu item",
6305
+ tab: "tab",
6306
+ dialog: "dialog",
6307
+ switch: "switch",
6308
+ slider: "slider"
6309
+ };
6310
+ return typeLabels[type.toLowerCase()] || type;
6311
+ }
6312
+ function truncate(str, maxLength) {
6313
+ if (str.length <= maxLength) return str;
6314
+ return str.substring(0, maxLength - 3) + "...";
6315
+ }
6316
+ function inferPageType(url, title, elements) {
6317
+ const urlLower = url.toLowerCase();
6318
+ const titleLower = title.toLowerCase();
6319
+ if (urlLower.includes("login") || urlLower.includes("signin")) return "login";
6320
+ if (urlLower.includes("dashboard")) return "dashboard";
6321
+ if (urlLower.includes("search")) return "search";
6322
+ if (urlLower.includes("checkout") || urlLower.includes("payment")) return "checkout";
6323
+ if (urlLower.includes("settings") || urlLower.includes("preferences")) return "settings";
6324
+ if (titleLower.includes("login") || titleLower.includes("sign in")) return "login";
6325
+ if (titleLower.includes("dashboard")) return "dashboard";
6326
+ if (titleLower.includes("search")) return "search";
6327
+ const hasLoginForm = elements.some((el) => el.type === "input" && el.semanticType === "email-input") && elements.some((el) => el.type === "input" && el.semanticType === "password-input");
6328
+ if (hasLoginForm) return "login";
6329
+ const hasSearchInput = elements.some(
6330
+ (el) => el.type === "input" && el.semanticType === "search-input"
6331
+ );
6332
+ if (hasSearchInput) return "search";
6333
+ const inputCount = elements.filter(
6334
+ (el) => el.type === "input" || el.type === "textarea" || el.type === "select"
6335
+ ).length;
6336
+ if (inputCount >= 3) return "form";
6337
+ const hasTable = elements.some((el) => el.tagName === "table");
6338
+ const hasMany = elements.length > 20;
6339
+ if (hasTable || hasMany) return "list";
6340
+ return "unknown";
6341
+ }
6342
+
6343
+ // src/ai/nl-action-parser.ts
6344
+ var ACTION_PATTERNS = [
6345
+ // Click patterns
6346
+ {
6347
+ regex: /^click\s+(?:on\s+)?(?:the\s+)?(.+?)(?:\s+button)?$/i,
6348
+ action: "click",
6349
+ targetGroup: 1,
6350
+ confidence: 0.95
6351
+ },
6352
+ {
6353
+ regex: /^press\s+(?:the\s+)?(.+?)(?:\s+button)?$/i,
6354
+ action: "click",
6355
+ targetGroup: 1,
6356
+ confidence: 0.9
6357
+ },
6358
+ {
6359
+ regex: /^tap\s+(?:on\s+)?(?:the\s+)?(.+)$/i,
6360
+ action: "click",
6361
+ targetGroup: 1,
6362
+ confidence: 0.85
6363
+ },
6364
+ {
6365
+ regex: /^activate\s+(?:the\s+)?(.+)$/i,
6366
+ action: "click",
6367
+ targetGroup: 1,
6368
+ confidence: 0.8
6369
+ },
6370
+ // Double click patterns
6371
+ {
6372
+ regex: /^double[\s-]?click\s+(?:on\s+)?(?:the\s+)?(.+)$/i,
6373
+ action: "doubleClick",
6374
+ targetGroup: 1,
6375
+ confidence: 0.95
6376
+ },
6377
+ // Right click patterns
6378
+ {
6379
+ regex: /^right[\s-]?click\s+(?:on\s+)?(?:the\s+)?(.+)$/i,
6380
+ action: "rightClick",
6381
+ targetGroup: 1,
6382
+ confidence: 0.95
6383
+ },
6384
+ {
6385
+ regex: /^context\s+click\s+(?:on\s+)?(?:the\s+)?(.+)$/i,
6386
+ action: "rightClick",
6387
+ targetGroup: 1,
6388
+ confidence: 0.9
6389
+ },
6390
+ // Type patterns - "type X in Y"
6391
+ {
6392
+ regex: /^type\s+["'](.+?)["']\s+(?:in(?:to)?|on)\s+(?:the\s+)?(.+)$/i,
6393
+ action: "type",
6394
+ targetGroup: 2,
6395
+ valueGroup: 1,
6396
+ confidence: 0.95
6397
+ },
6398
+ {
6399
+ regex: /^type\s+(.+?)\s+(?:in(?:to)?|on)\s+(?:the\s+)?(.+)$/i,
6400
+ action: "type",
6401
+ targetGroup: 2,
6402
+ valueGroup: 1,
6403
+ confidence: 0.85
6404
+ },
6405
+ // Type patterns - "enter X in Y"
6406
+ {
6407
+ regex: /^enter\s+["'](.+?)["']\s+(?:in(?:to)?|on)\s+(?:the\s+)?(.+)$/i,
6408
+ action: "type",
6409
+ targetGroup: 2,
6410
+ valueGroup: 1,
6411
+ confidence: 0.95
6412
+ },
6413
+ {
6414
+ regex: /^enter\s+(.+?)\s+(?:in(?:to)?|on)\s+(?:the\s+)?(.+)$/i,
6415
+ action: "type",
6416
+ targetGroup: 2,
6417
+ valueGroup: 1,
6418
+ confidence: 0.85
6419
+ },
6420
+ // Type patterns - "input X into Y"
6421
+ {
6422
+ regex: /^input\s+["'](.+?)["']\s+(?:in(?:to)?)\s+(?:the\s+)?(.+)$/i,
6423
+ action: "type",
6424
+ targetGroup: 2,
6425
+ valueGroup: 1,
6426
+ confidence: 0.9
6427
+ },
6428
+ // Type patterns - "fill Y with X"
6429
+ {
6430
+ regex: /^fill\s+(?:in\s+)?(?:the\s+)?(.+?)\s+with\s+["'](.+?)["']$/i,
6431
+ action: "type",
6432
+ targetGroup: 1,
6433
+ valueGroup: 2,
6434
+ confidence: 0.95
6435
+ },
6436
+ {
6437
+ regex: /^fill\s+(?:in\s+)?(?:the\s+)?(.+?)\s+with\s+(.+)$/i,
6438
+ action: "type",
6439
+ targetGroup: 1,
6440
+ valueGroup: 2,
6441
+ confidence: 0.85
6442
+ },
6443
+ // Type patterns - "set Y to X"
6444
+ {
6445
+ regex: /^set\s+(?:the\s+)?(.+?)\s+to\s+["'](.+?)["']$/i,
6446
+ action: "type",
6447
+ targetGroup: 1,
6448
+ valueGroup: 2,
6449
+ confidence: 0.9
6450
+ },
6451
+ // Select patterns
6452
+ {
6453
+ regex: /^select\s+["'](.+?)["']\s+(?:from|in)\s+(?:the\s+)?(.+)$/i,
6454
+ action: "select",
6455
+ targetGroup: 2,
6456
+ valueGroup: 1,
6457
+ confidence: 0.95
6458
+ },
6459
+ {
6460
+ regex: /^choose\s+["'](.+?)["']\s+(?:from|in)\s+(?:the\s+)?(.+)$/i,
6461
+ action: "select",
6462
+ targetGroup: 2,
6463
+ valueGroup: 1,
6464
+ confidence: 0.9
6465
+ },
6466
+ {
6467
+ regex: /^pick\s+["'](.+?)["']\s+(?:from|in)\s+(?:the\s+)?(.+)$/i,
6468
+ action: "select",
6469
+ targetGroup: 2,
6470
+ valueGroup: 1,
6471
+ confidence: 0.85
6472
+ },
6473
+ // Check patterns
6474
+ {
6475
+ regex: /^check\s+(?:the\s+)?(.+?)(?:\s+checkbox)?$/i,
6476
+ action: "check",
6477
+ targetGroup: 1,
6478
+ confidence: 0.9
6479
+ },
6480
+ {
6481
+ regex: /^enable\s+(?:the\s+)?(.+)$/i,
6482
+ action: "check",
6483
+ targetGroup: 1,
6484
+ confidence: 0.8
6485
+ },
6486
+ {
6487
+ regex: /^tick\s+(?:the\s+)?(.+)$/i,
6488
+ action: "check",
6489
+ targetGroup: 1,
6490
+ confidence: 0.85
6491
+ },
6492
+ // Uncheck patterns
6493
+ {
6494
+ regex: /^uncheck\s+(?:the\s+)?(.+?)(?:\s+checkbox)?$/i,
6495
+ action: "uncheck",
6496
+ targetGroup: 1,
6497
+ confidence: 0.9
6498
+ },
6499
+ {
6500
+ regex: /^disable\s+(?:the\s+)?(.+)$/i,
6501
+ action: "uncheck",
6502
+ targetGroup: 1,
6503
+ confidence: 0.8
6504
+ },
6505
+ {
6506
+ regex: /^untick\s+(?:the\s+)?(.+)$/i,
6507
+ action: "uncheck",
6508
+ targetGroup: 1,
6509
+ confidence: 0.85
6510
+ },
6511
+ // Clear patterns
6512
+ {
6513
+ regex: /^clear\s+(?:the\s+)?(.+)$/i,
6514
+ action: "clear",
6515
+ targetGroup: 1,
6516
+ confidence: 0.9
6517
+ },
6518
+ {
6519
+ regex: /^erase\s+(?:the\s+)?(.+)$/i,
6520
+ action: "clear",
6521
+ targetGroup: 1,
6522
+ confidence: 0.85
6523
+ },
6524
+ {
6525
+ regex: /^empty\s+(?:the\s+)?(.+)$/i,
6526
+ action: "clear",
6527
+ targetGroup: 1,
6528
+ confidence: 0.8
6529
+ },
6530
+ // Hover patterns
6531
+ {
6532
+ regex: /^hover\s+(?:over\s+)?(?:the\s+)?(.+)$/i,
6533
+ action: "hover",
6534
+ targetGroup: 1,
6535
+ confidence: 0.9
6536
+ },
6537
+ {
6538
+ regex: /^mouse\s+over\s+(?:the\s+)?(.+)$/i,
6539
+ action: "hover",
6540
+ targetGroup: 1,
6541
+ confidence: 0.85
6542
+ },
6543
+ // Focus patterns
6544
+ {
6545
+ regex: /^focus\s+(?:on\s+)?(?:the\s+)?(.+)$/i,
6546
+ action: "focus",
6547
+ targetGroup: 1,
6548
+ confidence: 0.9
6549
+ },
6550
+ // Scroll patterns
6551
+ {
6552
+ regex: /^scroll\s+(up|down|left|right)$/i,
6553
+ action: "scroll",
6554
+ targetGroup: 1,
6555
+ confidence: 0.9
6556
+ },
6557
+ {
6558
+ regex: /^scroll\s+(?:the\s+)?(.+?)\s+(up|down|left|right)$/i,
6559
+ action: "scroll",
6560
+ targetGroup: 1,
6561
+ confidence: 0.85
6562
+ },
6563
+ {
6564
+ regex: /^scroll\s+to\s+(?:the\s+)?(.+)$/i,
6565
+ action: "scroll",
6566
+ targetGroup: 1,
6567
+ confidence: 0.85
6568
+ },
6569
+ // Wait patterns
6570
+ {
6571
+ regex: /^wait\s+(?:for\s+)?(?:the\s+)?(.+?)(?:\s+to\s+(?:be\s+)?(.+))?$/i,
6572
+ action: "wait",
6573
+ targetGroup: 1,
6574
+ confidence: 0.85
6575
+ },
6576
+ {
6577
+ regex: /^wait\s+until\s+(?:the\s+)?(.+?)(?:\s+(?:is|becomes)\s+(.+))?$/i,
6578
+ action: "wait",
6579
+ targetGroup: 1,
6580
+ confidence: 0.85
6581
+ },
6582
+ // Assert patterns
6583
+ {
6584
+ regex: /^(?:assert|verify|check)\s+(?:that\s+)?(?:the\s+)?(.+?)\s+(?:is\s+)?(visible|hidden|enabled|disabled|checked|unchecked|focused)$/i,
6585
+ action: "assert",
6586
+ targetGroup: 1,
6587
+ confidence: 0.9
6588
+ },
6589
+ {
6590
+ regex: /^(?:assert|verify|check)\s+(?:that\s+)?(?:the\s+)?(.+?)\s+(?:contains|has)\s+["'](.+?)["']$/i,
6591
+ action: "assert",
6592
+ targetGroup: 1,
6593
+ valueGroup: 2,
6594
+ confidence: 0.9
6595
+ },
6596
+ {
6597
+ regex: /^(?:the\s+)?(.+?)\s+should\s+(?:be\s+)?(visible|hidden|enabled|disabled|checked|unchecked|focused)$/i,
6598
+ action: "assert",
6599
+ targetGroup: 1,
6600
+ confidence: 0.85
6601
+ }
6602
+ ];
6603
+ var ASSERTION_TYPE_MAP = {
6604
+ visible: "visible",
6605
+ hidden: "hidden",
6606
+ enabled: "enabled",
6607
+ disabled: "disabled",
6608
+ checked: "checked",
6609
+ unchecked: "unchecked",
6610
+ focused: "focused",
6611
+ contains: "containsText",
6612
+ has: "hasText"
6613
+ };
6614
+ function parseNLInstruction(instruction) {
6615
+ const trimmed = instruction.trim();
6616
+ if (!trimmed) return null;
6617
+ for (const pattern of ACTION_PATTERNS) {
6618
+ const match = trimmed.match(pattern.regex);
6619
+ if (match) {
6620
+ const parsed = {
6621
+ action: pattern.action,
6622
+ targetDescription: cleanTargetDescription(match[pattern.targetGroup] || ""),
6623
+ rawInstruction: instruction,
6624
+ parseConfidence: pattern.confidence
6625
+ };
6626
+ if (pattern.valueGroup && match[pattern.valueGroup]) {
6627
+ parsed.value = match[pattern.valueGroup];
6628
+ }
6629
+ if (pattern.modifierExtractor) {
6630
+ parsed.modifiers = pattern.modifierExtractor(match);
6631
+ }
6632
+ if (pattern.action === "scroll") {
6633
+ const directionMatch = trimmed.match(/(up|down|left|right)/i);
6634
+ if (directionMatch) {
6635
+ parsed.scrollDirection = directionMatch[1].toLowerCase();
6636
+ }
6637
+ }
6638
+ if (pattern.action === "assert") {
6639
+ const assertMatch = trimmed.match(/(visible|hidden|enabled|disabled|checked|unchecked|focused|contains|has)/i);
6640
+ if (assertMatch) {
6641
+ parsed.assertionType = ASSERTION_TYPE_MAP[assertMatch[1].toLowerCase()];
6642
+ }
6643
+ }
6644
+ if (pattern.action === "wait") {
6645
+ const waitCondition = match[2];
6646
+ if (waitCondition) {
6647
+ parsed.waitCondition = waitCondition;
6648
+ }
6649
+ }
6650
+ return parsed;
6651
+ }
6652
+ }
6653
+ return inferAction(trimmed);
6654
+ }
6655
+ function cleanTargetDescription(target) {
6656
+ return target.trim().replace(/^(the|a|an)\s+/i, "").replace(/\s+(button|field|input|link|dropdown|checkbox|radio)$/i, "").trim();
6657
+ }
6658
+ function inferAction(instruction) {
6659
+ const lower = instruction.toLowerCase();
6660
+ if (lower.includes("click") || lower.includes("press") || lower.includes("tap")) {
6661
+ const target = instruction.replace(/click|press|tap|on|the/gi, "").trim();
6662
+ if (target) {
6663
+ return {
6664
+ action: "click",
6665
+ targetDescription: cleanTargetDescription(target),
6666
+ rawInstruction: instruction,
6667
+ parseConfidence: 0.6
6668
+ };
6669
+ }
6670
+ }
6671
+ if (lower.includes("type") || lower.includes("enter") || lower.includes("input")) {
6672
+ const quotedMatch = instruction.match(/["'](.+?)["']/);
6673
+ if (quotedMatch) {
6674
+ const target = instruction.replace(/type|enter|input|into|in|the|["'].*?["']/gi, "").trim();
6675
+ return {
6676
+ action: "type",
6677
+ targetDescription: cleanTargetDescription(target),
6678
+ value: quotedMatch[1],
6679
+ rawInstruction: instruction,
6680
+ parseConfidence: 0.5
6681
+ };
6682
+ }
6683
+ }
6684
+ return null;
6685
+ }
6686
+ function parseNLInstructions(instructions) {
6687
+ const parsed = [];
6688
+ for (const instruction of instructions) {
6689
+ const result = parseNLInstruction(instruction);
6690
+ if (result) {
6691
+ parsed.push(result);
6692
+ }
6693
+ }
6694
+ return parsed;
6695
+ }
6696
+ function splitCompoundInstruction(instruction) {
6697
+ const parts = instruction.split(/\s+(?:and|then|,\s*then|,\s*and|,)\s+/i);
6698
+ return parts.map((p) => p.trim()).filter((p) => p.length > 0);
6699
+ }
6700
+ function extractModifiers(instruction) {
6701
+ const modifiers = [];
6702
+ const lower = instruction.toLowerCase();
6703
+ if (lower.includes("shift") || lower.includes("with shift")) {
6704
+ modifiers.push("shift");
6705
+ }
6706
+ if (lower.includes("ctrl") || lower.includes("control") || lower.includes("with ctrl")) {
6707
+ modifiers.push("ctrl");
6708
+ }
6709
+ if (lower.includes("alt") || lower.includes("with alt") || lower.includes("option")) {
6710
+ modifiers.push("alt");
6711
+ }
6712
+ if (lower.includes("meta") || lower.includes("command") || lower.includes("cmd") || lower.includes("windows")) {
6713
+ modifiers.push("meta");
6714
+ }
6715
+ return modifiers.length > 0 ? modifiers : void 0;
6716
+ }
6717
+ function validateParsedAction(action) {
6718
+ const errors = [];
6719
+ if (!action.targetDescription && action.action !== "scroll") {
6720
+ errors.push("No target element specified");
6721
+ }
6722
+ if ((action.action === "type" || action.action === "select") && !action.value) {
6723
+ errors.push(`No value specified for ${action.action} action`);
6724
+ }
6725
+ if (action.parseConfidence < 0.5) {
6726
+ errors.push("Low confidence parsing - instruction may be ambiguous");
6727
+ }
6728
+ return {
6729
+ valid: errors.length === 0,
6730
+ errors
6731
+ };
6732
+ }
6733
+ function describeAction(action) {
6734
+ switch (action.action) {
6735
+ case "click":
6736
+ return `Click on "${action.targetDescription}"`;
6737
+ case "doubleClick":
6738
+ return `Double-click on "${action.targetDescription}"`;
6739
+ case "rightClick":
6740
+ return `Right-click on "${action.targetDescription}"`;
6741
+ case "type":
6742
+ return `Type "${action.value}" into "${action.targetDescription}"`;
6743
+ case "select":
6744
+ return `Select "${action.value}" from "${action.targetDescription}"`;
6745
+ case "check":
6746
+ return `Check "${action.targetDescription}"`;
6747
+ case "uncheck":
6748
+ return `Uncheck "${action.targetDescription}"`;
6749
+ case "clear":
6750
+ return `Clear "${action.targetDescription}"`;
6751
+ case "hover":
6752
+ return `Hover over "${action.targetDescription}"`;
6753
+ case "focus":
6754
+ return `Focus on "${action.targetDescription}"`;
6755
+ case "scroll":
6756
+ if (action.scrollDirection) {
6757
+ return `Scroll ${action.scrollDirection}`;
6758
+ }
6759
+ return `Scroll to "${action.targetDescription}"`;
6760
+ case "wait":
6761
+ return `Wait for "${action.targetDescription}"${action.waitCondition ? ` to be ${action.waitCondition}` : ""}`;
6762
+ case "assert":
6763
+ return `Assert "${action.targetDescription}" is ${action.assertionType || "valid"}`;
6764
+ default:
6765
+ return `${action.action} on "${action.targetDescription}"`;
6766
+ }
6767
+ }
6768
+
6769
+ // src/ai/error-context.ts
6770
+ function getElementState5(el) {
6771
+ if ("state" in el && el.state) {
6772
+ return el.state;
6773
+ }
6774
+ if ("getState" in el && typeof el.getState === "function") {
6775
+ try {
6776
+ return el.getState();
6777
+ } catch {
6778
+ return void 0;
6779
+ }
6780
+ }
6781
+ return void 0;
6782
+ }
6783
+ var ErrorCodes = {
6784
+ // Parsing errors
6785
+ PARSE_ERROR: "PARSE_ERROR",
6786
+ VALIDATION_ERROR: "VALIDATION_ERROR",
6787
+ // Element errors
6788
+ ELEMENT_NOT_FOUND: "ELEMENT_NOT_FOUND",
6789
+ ELEMENT_NOT_VISIBLE: "ELEMENT_NOT_VISIBLE",
6790
+ ELEMENT_DISABLED: "ELEMENT_DISABLED",
6791
+ ELEMENT_BLOCKED: "ELEMENT_BLOCKED",
6792
+ MULTIPLE_ELEMENTS: "MULTIPLE_ELEMENTS",
6793
+ // Search errors
6794
+ LOW_CONFIDENCE: "LOW_CONFIDENCE",
6795
+ AMBIGUOUS_MATCH: "AMBIGUOUS_MATCH",
6796
+ // Action errors
6797
+ ACTION_FAILED: "ACTION_FAILED",
6798
+ ACTION_TIMEOUT: "ACTION_TIMEOUT",
6799
+ UNSUPPORTED_ACTION: "UNSUPPORTED_ACTION",
6800
+ // State errors
6801
+ UNEXPECTED_STATE: "UNEXPECTED_STATE",
6802
+ STALE_ELEMENT: "STALE_ELEMENT",
6803
+ // Page errors
6804
+ PAGE_LOAD_ERROR: "PAGE_LOAD_ERROR",
6805
+ NAVIGATION_ERROR: "NAVIGATION_ERROR"
6806
+ };
6807
+ var ERROR_MESSAGES = {
6808
+ PARSE_ERROR: "Could not parse the natural language instruction",
6809
+ VALIDATION_ERROR: "The parsed action failed validation",
6810
+ ELEMENT_NOT_FOUND: "No element matching the description could be found",
6811
+ ELEMENT_NOT_VISIBLE: "The element exists but is not visible",
6812
+ ELEMENT_DISABLED: "The element is disabled and cannot be interacted with",
6813
+ ELEMENT_BLOCKED: "The element is blocked by another element",
6814
+ MULTIPLE_ELEMENTS: "Multiple elements match the description",
6815
+ LOW_CONFIDENCE: "The best match has low confidence",
6816
+ AMBIGUOUS_MATCH: "Multiple elements match with similar confidence",
6817
+ ACTION_FAILED: "The action could not be completed",
6818
+ ACTION_TIMEOUT: "The action timed out waiting for a condition",
6819
+ UNSUPPORTED_ACTION: "The requested action is not supported",
6820
+ UNEXPECTED_STATE: "The element is in an unexpected state",
6821
+ STALE_ELEMENT: "The element is no longer attached to the DOM",
6822
+ PAGE_LOAD_ERROR: "The page failed to load correctly",
6823
+ NAVIGATION_ERROR: "Navigation to the target page failed"
6824
+ };
6825
+ var ERROR_SUGGESTIONS = {
6826
+ PARSE_ERROR: [
6827
+ {
6828
+ action: 'Use a simpler instruction format like "click Submit button"',
6829
+ confidence: 0.8,
6830
+ priority: 1
6831
+ },
6832
+ {
6833
+ action: "Use specific element names visible on the page",
6834
+ confidence: 0.7,
6835
+ priority: 2
6836
+ }
6837
+ ],
6838
+ VALIDATION_ERROR: [
6839
+ {
6840
+ action: "Provide required parameters for the action",
6841
+ confidence: 0.9,
6842
+ priority: 1
6843
+ },
6844
+ {
6845
+ action: "Check the instruction format",
6846
+ confidence: 0.7,
6847
+ priority: 2
6848
+ }
6849
+ ],
6850
+ ELEMENT_NOT_FOUND: [
6851
+ {
6852
+ action: "Wait for the page to fully load",
6853
+ command: "wait for page to load",
6854
+ confidence: 0.7,
6855
+ priority: 1
6856
+ },
6857
+ {
6858
+ action: "Use a different description for the element",
6859
+ confidence: 0.8,
6860
+ priority: 2
6861
+ },
6862
+ {
6863
+ action: "Scroll the page to reveal the element",
6864
+ command: "scroll down",
6865
+ confidence: 0.6,
6866
+ priority: 3
6867
+ }
6868
+ ],
6869
+ ELEMENT_NOT_VISIBLE: [
6870
+ {
6871
+ action: "Scroll to make the element visible",
6872
+ command: "scroll to element",
6873
+ confidence: 0.9,
6874
+ priority: 1
6875
+ },
6876
+ {
6877
+ action: "Close any overlaying elements",
6878
+ confidence: 0.7,
6879
+ priority: 2
6880
+ },
6881
+ {
6882
+ action: "Wait for loading to complete",
6883
+ command: "wait for loading",
6884
+ confidence: 0.6,
6885
+ priority: 3
6886
+ }
6887
+ ],
6888
+ ELEMENT_DISABLED: [
6889
+ {
6890
+ action: "Fill in required fields first",
6891
+ confidence: 0.8,
6892
+ priority: 1
6893
+ },
6894
+ {
6895
+ action: "Complete prerequisite steps",
6896
+ confidence: 0.7,
6897
+ priority: 2
6898
+ },
6899
+ {
6900
+ action: "Wait for the element to become enabled",
6901
+ command: "wait for element to be enabled",
6902
+ confidence: 0.6,
6903
+ priority: 3
6904
+ }
6905
+ ],
6906
+ ELEMENT_BLOCKED: [
6907
+ {
6908
+ action: "Close the modal or popup",
6909
+ command: "click close button",
6910
+ confidence: 0.9,
6911
+ priority: 1
6912
+ },
6913
+ {
6914
+ action: "Dismiss the overlay",
6915
+ confidence: 0.8,
6916
+ priority: 2
6917
+ },
6918
+ {
6919
+ action: "Wait for the blocking element to disappear",
6920
+ confidence: 0.6,
6921
+ priority: 3
6922
+ }
6923
+ ],
6924
+ MULTIPLE_ELEMENTS: [
6925
+ {
6926
+ action: "Use a more specific description",
6927
+ confidence: 0.9,
6928
+ priority: 1
6929
+ },
6930
+ {
6931
+ action: "Include the element position (first, second, etc.)",
6932
+ confidence: 0.8,
6933
+ priority: 2
6934
+ },
6935
+ {
6936
+ action: "Use the element ID directly",
6937
+ confidence: 0.7,
6938
+ priority: 3
6939
+ }
6940
+ ],
6941
+ LOW_CONFIDENCE: [
6942
+ {
6943
+ action: "Use the exact text shown on the element",
6944
+ confidence: 0.9,
6945
+ priority: 1
6946
+ },
6947
+ {
6948
+ action: "Lower the confidence threshold if the match is correct",
6949
+ confidence: 0.7,
6950
+ priority: 2
6951
+ },
6952
+ {
6953
+ action: "Try a different way to describe the element",
6954
+ confidence: 0.6,
6955
+ priority: 3
6956
+ }
6957
+ ],
6958
+ AMBIGUOUS_MATCH: [
6959
+ {
6960
+ action: "Be more specific about which element you mean",
6961
+ confidence: 0.9,
6962
+ priority: 1
6963
+ },
6964
+ {
6965
+ action: "Include the section or form name",
6966
+ confidence: 0.8,
6967
+ priority: 2
6968
+ }
6969
+ ],
6970
+ ACTION_FAILED: [
6971
+ {
6972
+ action: "Check if the element is interactable",
6973
+ confidence: 0.7,
6974
+ priority: 1
6975
+ },
6976
+ {
6977
+ action: "Wait and retry the action",
6978
+ command: "wait 1 second then retry",
6979
+ confidence: 0.6,
6980
+ priority: 2
6981
+ }
6982
+ ],
6983
+ ACTION_TIMEOUT: [
6984
+ {
6985
+ action: "Increase the timeout duration",
6986
+ confidence: 0.8,
6987
+ priority: 1
6988
+ },
6989
+ {
6990
+ action: "Check if the condition can ever be met",
6991
+ confidence: 0.7,
6992
+ priority: 2
6993
+ }
6994
+ ],
6995
+ UNSUPPORTED_ACTION: [
6996
+ {
6997
+ action: "Use a different action type",
6998
+ confidence: 0.9,
6999
+ priority: 1
7000
+ },
7001
+ {
7002
+ action: "Break down into simpler actions",
7003
+ confidence: 0.7,
7004
+ priority: 2
7005
+ }
7006
+ ],
7007
+ UNEXPECTED_STATE: [
7008
+ {
7009
+ action: "Refresh the page state",
7010
+ command: "refresh",
7011
+ confidence: 0.7,
7012
+ priority: 1
7013
+ },
7014
+ {
7015
+ action: "Wait for state to stabilize",
7016
+ command: "wait 2 seconds",
7017
+ confidence: 0.6,
7018
+ priority: 2
7019
+ }
7020
+ ],
7021
+ STALE_ELEMENT: [
7022
+ {
7023
+ action: "Re-find the element",
7024
+ confidence: 0.9,
7025
+ priority: 1
7026
+ },
7027
+ {
7028
+ action: "Wait for page to stabilize",
7029
+ command: "wait 1 second",
7030
+ confidence: 0.7,
7031
+ priority: 2
7032
+ }
7033
+ ],
7034
+ PAGE_LOAD_ERROR: [
7035
+ {
7036
+ action: "Refresh the page",
7037
+ command: "refresh page",
7038
+ confidence: 0.8,
7039
+ priority: 1
7040
+ },
7041
+ {
7042
+ action: "Check network connectivity",
7043
+ confidence: 0.6,
7044
+ priority: 2
7045
+ }
7046
+ ],
7047
+ NAVIGATION_ERROR: [
7048
+ {
7049
+ action: "Try the navigation again",
7050
+ confidence: 0.7,
7051
+ priority: 1
7052
+ },
7053
+ {
7054
+ action: "Check if the URL is correct",
7055
+ confidence: 0.6,
7056
+ priority: 2
7057
+ }
7058
+ ]
7059
+ };
7060
+ function createErrorContext(errorCode, attemptedAction, availableElements, searchCriteria, nearestMatch) {
7061
+ const message = ERROR_MESSAGES[errorCode] || "An unknown error occurred";
7062
+ const baseSuggestions = ERROR_SUGGESTIONS[errorCode] || [];
7063
+ const possibleBlockers = detectPossibleBlockers(availableElements);
7064
+ const visibleElements = availableElements.filter((el) => {
7065
+ const state = getElementState5(el);
7066
+ return state?.visible ?? false;
7067
+ }).length;
7068
+ const suggestions = enhanceSuggestions(
7069
+ baseSuggestions,
7070
+ errorCode,
7071
+ nearestMatch,
7072
+ possibleBlockers
7073
+ );
7074
+ return {
7075
+ code: errorCode,
7076
+ message,
7077
+ attemptedAction,
7078
+ searchCriteria,
7079
+ searchResults: {
7080
+ candidatesFound: availableElements.length,
7081
+ nearestMatch: nearestMatch ? {
7082
+ element: nearestMatch.element,
7083
+ confidence: nearestMatch.confidence,
7084
+ whyNotSelected: determineWhyNotSelected(errorCode, nearestMatch)
7085
+ } : void 0
7086
+ },
7087
+ pageContext: {
7088
+ url: typeof window !== "undefined" ? window.location.href : "",
7089
+ title: typeof document !== "undefined" ? document.title : "",
7090
+ visibleElements,
7091
+ possibleBlockers
7092
+ },
7093
+ suggestions,
7094
+ timestamp: Date.now()
7095
+ };
7096
+ }
7097
+ function detectPossibleBlockers(elements) {
7098
+ const blockers = [];
7099
+ for (const el of elements) {
7100
+ const state = getElementState5(el);
7101
+ if (!state) continue;
7102
+ if (el.type === "dialog" && state.visible) {
7103
+ blockers.push(`Modal dialog: ${el.id}`);
7104
+ }
7105
+ if (state.computedStyles?.pointerEvents === "none") {
7106
+ continue;
7107
+ }
7108
+ }
7109
+ return blockers;
7110
+ }
7111
+ function enhanceSuggestions(baseSuggestions, errorCode, nearestMatch, possibleBlockers) {
7112
+ const suggestions = [...baseSuggestions];
7113
+ if (possibleBlockers && possibleBlockers.length > 0) {
7114
+ suggestions.unshift({
7115
+ action: `Close the blocking element: ${possibleBlockers[0]}`,
7116
+ command: "click close button",
7117
+ confidence: 0.85,
7118
+ priority: 0
7119
+ });
7120
+ }
7121
+ if (nearestMatch && errorCode === "LOW_CONFIDENCE") {
7122
+ suggestions.unshift({
7123
+ action: `Did you mean: "${nearestMatch.element.description}"?`,
7124
+ command: `click "${nearestMatch.element.description}"`,
7125
+ confidence: nearestMatch.confidence,
7126
+ priority: 0
7127
+ });
7128
+ }
7129
+ suggestions.sort((a, b) => a.priority - b.priority);
7130
+ return suggestions;
7131
+ }
7132
+ function determineWhyNotSelected(errorCode, nearestMatch) {
7133
+ switch (errorCode) {
7134
+ case "LOW_CONFIDENCE":
7135
+ return `Confidence (${(nearestMatch.confidence * 100).toFixed(0)}%) below threshold`;
7136
+ case "ELEMENT_NOT_VISIBLE":
7137
+ return "Element is not visible";
7138
+ case "ELEMENT_DISABLED":
7139
+ return "Element is disabled";
7140
+ case "AMBIGUOUS_MATCH":
7141
+ return "Multiple elements with similar confidence";
7142
+ default:
7143
+ return "Did not meet selection criteria";
7144
+ }
7145
+ }
7146
+ function formatErrorContext(context) {
7147
+ const lines = [];
7148
+ lines.push(`Error: ${context.code}`);
7149
+ lines.push(`Message: ${context.message}`);
7150
+ lines.push(`Attempted: ${context.attemptedAction}`);
7151
+ lines.push("");
7152
+ if (context.searchResults.nearestMatch) {
7153
+ const match = context.searchResults.nearestMatch;
7154
+ lines.push(`Nearest match: "${match.element.description}" (${(match.confidence * 100).toFixed(0)}% confidence)`);
7155
+ lines.push(`Why not used: ${match.whyNotSelected}`);
7156
+ lines.push("");
7157
+ }
7158
+ lines.push(`Page: ${context.pageContext.title || context.pageContext.url}`);
7159
+ lines.push(`Visible elements: ${context.pageContext.visibleElements}`);
7160
+ if (context.pageContext.possibleBlockers.length > 0) {
7161
+ lines.push(`Possible blockers: ${context.pageContext.possibleBlockers.join(", ")}`);
7162
+ }
7163
+ lines.push("");
7164
+ lines.push("Suggestions:");
7165
+ for (const suggestion of context.suggestions.slice(0, 3)) {
7166
+ lines.push(` - ${suggestion.action}`);
7167
+ if (suggestion.command) {
7168
+ lines.push(` Command: ${suggestion.command}`);
7169
+ }
7170
+ }
7171
+ return lines.join("\n");
7172
+ }
7173
+ function createSimpleError(code, message) {
7174
+ return {
7175
+ code,
7176
+ message: message || ERROR_MESSAGES[code] || "Unknown error"
7177
+ };
7178
+ }
7179
+ function isRecoverableError(code) {
7180
+ const unrecoverableErrors = [
7181
+ "UNSUPPORTED_ACTION",
7182
+ "PAGE_LOAD_ERROR",
7183
+ "NAVIGATION_ERROR"
7184
+ ];
7185
+ return !unrecoverableErrors.includes(code);
7186
+ }
7187
+ function getBestRecoverySuggestion(context) {
7188
+ if (context.suggestions.length === 0) return null;
7189
+ const sorted = [...context.suggestions].sort((a, b) => b.confidence - a.confidence);
7190
+ return sorted[0];
7191
+ }
7192
+
7193
+ // src/ai/nl-action-executor.ts
7194
+ var DEFAULT_EXECUTOR_CONFIG = {
7195
+ defaultConfidenceThreshold: 0.7,
7196
+ defaultTimeout: 5e3,
7197
+ maxAlternatives: 3,
7198
+ verbose: false
7199
+ };
7200
+ var NLActionExecutor = class {
7201
+ constructor(config = {}) {
7202
+ this.actionExecutor = null;
7203
+ this.elements = [];
7204
+ this.config = { ...DEFAULT_EXECUTOR_CONFIG, ...config };
7205
+ this.searchEngine = new SearchEngine(this.config.searchConfig);
7206
+ }
7207
+ /**
7208
+ * Set the action executor for performing DOM actions
7209
+ */
7210
+ setActionExecutor(executor) {
7211
+ this.actionExecutor = executor;
7212
+ }
7213
+ /**
7214
+ * Update available elements for search
7215
+ */
7216
+ updateElements(elements) {
7217
+ this.elements = elements;
7218
+ this.searchEngine.updateElements(elements);
7219
+ }
7220
+ /**
7221
+ * Execute a natural language instruction
7222
+ */
7223
+ async execute(request) {
7224
+ const startTime = performance.now();
7225
+ const threshold = request.confidenceThreshold ?? this.config.defaultConfidenceThreshold;
7226
+ const parsed = parseNLInstruction(request.instruction);
7227
+ if (!parsed) {
7228
+ return this.createFailureResponse(
7229
+ startTime,
7230
+ "PARSE_ERROR",
7231
+ `Could not parse instruction: "${request.instruction}"`,
7232
+ request.instruction,
7233
+ [],
7234
+ threshold
7235
+ );
7236
+ }
7237
+ const validation = validateParsedAction(parsed);
7238
+ if (!validation.valid) {
7239
+ return this.createFailureResponse(
7240
+ startTime,
7241
+ "VALIDATION_ERROR",
7242
+ validation.errors.join("; "),
7243
+ request.instruction,
7244
+ [],
7245
+ threshold
7246
+ );
7247
+ }
7248
+ const searchCriteria = this.buildSearchCriteria(parsed);
7249
+ const searchResponse = this.searchEngine.search(searchCriteria);
7250
+ if (!searchResponse.bestMatch) {
7251
+ return this.createFailureResponse(
7252
+ startTime,
7253
+ "ELEMENT_NOT_FOUND",
7254
+ `Could not find element matching: "${parsed.targetDescription}"`,
7255
+ request.instruction,
7256
+ searchResponse.results,
7257
+ threshold,
7258
+ searchCriteria
7259
+ );
7260
+ }
7261
+ if (searchResponse.bestMatch.confidence < threshold) {
7262
+ const alternatives = searchResponse.results.slice(0, this.config.maxAlternatives);
7263
+ return this.createFailureResponse(
7264
+ startTime,
7265
+ "LOW_CONFIDENCE",
7266
+ `Best match confidence (${(searchResponse.bestMatch.confidence * 100).toFixed(0)}%) is below threshold (${(threshold * 100).toFixed(0)}%)`,
7267
+ request.instruction,
7268
+ alternatives,
7269
+ threshold,
7270
+ searchCriteria,
7271
+ searchResponse.bestMatch
7272
+ );
7273
+ }
7274
+ try {
7275
+ const result = await this.performAction(
7276
+ parsed,
7277
+ searchResponse.bestMatch.element,
7278
+ request.timeout ?? this.config.defaultTimeout
7279
+ );
7280
+ return {
7281
+ success: true,
7282
+ executedAction: describeAction(parsed),
7283
+ elementUsed: searchResponse.bestMatch.element,
7284
+ confidence: searchResponse.bestMatch.confidence,
7285
+ elementState: result.elementState,
7286
+ durationMs: performance.now() - startTime,
7287
+ timestamp: Date.now()
7288
+ };
7289
+ } catch (error) {
7290
+ const errorMessage = error instanceof Error ? error.message : String(error);
7291
+ const alternatives = searchResponse.results.filter((r) => r !== searchResponse.bestMatch).slice(0, this.config.maxAlternatives);
7292
+ return this.createFailureResponse(
7293
+ startTime,
7294
+ "ACTION_FAILED",
7295
+ errorMessage,
7296
+ request.instruction,
7297
+ alternatives,
7298
+ threshold,
7299
+ searchCriteria,
7300
+ searchResponse.bestMatch
7301
+ );
7302
+ }
7303
+ }
7304
+ /**
7305
+ * Execute a parsed action directly (skip parsing)
7306
+ */
7307
+ async executeParsed(parsed, threshold) {
7308
+ const startTime = performance.now();
7309
+ const confidenceThreshold = threshold ?? this.config.defaultConfidenceThreshold;
7310
+ const searchCriteria = this.buildSearchCriteria(parsed);
7311
+ const searchResponse = this.searchEngine.search(searchCriteria);
7312
+ if (!searchResponse.bestMatch) {
7313
+ return this.createFailureResponse(
7314
+ startTime,
7315
+ "ELEMENT_NOT_FOUND",
7316
+ `Could not find element: "${parsed.targetDescription}"`,
7317
+ parsed.rawInstruction,
7318
+ [],
7319
+ confidenceThreshold,
7320
+ searchCriteria
7321
+ );
7322
+ }
7323
+ if (searchResponse.bestMatch.confidence < confidenceThreshold) {
7324
+ return this.createFailureResponse(
7325
+ startTime,
7326
+ "LOW_CONFIDENCE",
7327
+ `Best match confidence too low`,
7328
+ parsed.rawInstruction,
7329
+ searchResponse.results.slice(0, this.config.maxAlternatives),
7330
+ confidenceThreshold,
7331
+ searchCriteria,
7332
+ searchResponse.bestMatch
7333
+ );
7334
+ }
7335
+ try {
7336
+ const result = await this.performAction(
7337
+ parsed,
7338
+ searchResponse.bestMatch.element,
7339
+ this.config.defaultTimeout
7340
+ );
7341
+ return {
7342
+ success: true,
7343
+ executedAction: describeAction(parsed),
7344
+ elementUsed: searchResponse.bestMatch.element,
7345
+ confidence: searchResponse.bestMatch.confidence,
7346
+ elementState: result.elementState,
7347
+ durationMs: performance.now() - startTime,
7348
+ timestamp: Date.now()
7349
+ };
7350
+ } catch (error) {
7351
+ return this.createFailureResponse(
7352
+ startTime,
7353
+ "ACTION_FAILED",
7354
+ error instanceof Error ? error.message : String(error),
7355
+ parsed.rawInstruction,
7356
+ searchResponse.results.filter((r) => r !== searchResponse.bestMatch).slice(0, this.config.maxAlternatives),
7357
+ confidenceThreshold,
7358
+ searchCriteria,
7359
+ searchResponse.bestMatch
7360
+ );
7361
+ }
7362
+ }
7363
+ /**
7364
+ * Build search criteria from a parsed action
7365
+ */
7366
+ buildSearchCriteria(parsed) {
7367
+ const criteria = {
7368
+ text: parsed.targetDescription,
7369
+ fuzzy: true,
7370
+ fuzzyThreshold: this.config.defaultConfidenceThreshold
7371
+ };
7372
+ switch (parsed.action) {
7373
+ case "click":
7374
+ case "doubleClick":
7375
+ case "rightClick":
7376
+ break;
7377
+ case "type":
7378
+ case "clear":
7379
+ criteria.type = "input";
7380
+ break;
7381
+ case "select":
7382
+ criteria.type = "select";
7383
+ break;
7384
+ case "check":
7385
+ case "uncheck":
7386
+ criteria.type = "checkbox";
7387
+ break;
7388
+ }
7389
+ return criteria;
7390
+ }
7391
+ /**
7392
+ * Perform the actual action on an element
7393
+ */
7394
+ async performAction(parsed, element, timeout) {
7395
+ if (!this.actionExecutor) {
7396
+ throw new Error("No action executor configured");
7397
+ }
7398
+ const actionMap = {
7399
+ click: "click",
7400
+ doubleClick: "doubleClick",
7401
+ rightClick: "rightClick",
7402
+ type: "type",
7403
+ select: "select",
7404
+ check: "check",
7405
+ uncheck: "uncheck",
7406
+ scroll: "scroll",
7407
+ wait: null,
7408
+ // Special handling
7409
+ assert: null,
7410
+ // Special handling
7411
+ hover: "hover",
7412
+ focus: "focus",
7413
+ clear: "clear"
7414
+ };
7415
+ const standardAction = actionMap[parsed.action];
7416
+ if (!standardAction) {
7417
+ if (parsed.action === "wait") {
7418
+ const waitResult = await this.actionExecutor.waitFor(element.id, {
7419
+ visible: true,
7420
+ timeout
7421
+ });
7422
+ if (!waitResult.met) {
7423
+ throw new Error(waitResult.error || "Wait condition not met");
7424
+ }
7425
+ return { elementState: waitResult.state };
7426
+ }
7427
+ if (parsed.action === "assert") {
7428
+ throw new Error("Use the assertions module for assert actions");
7429
+ }
7430
+ throw new Error(`Unsupported action: ${parsed.action}`);
7431
+ }
7432
+ const actionRequest = {
7433
+ action: standardAction,
7434
+ waitOptions: {
7435
+ visible: true,
7436
+ enabled: true,
7437
+ timeout
7438
+ }
7439
+ };
7440
+ if (standardAction === "type" && parsed.value) {
7441
+ actionRequest.params = { text: parsed.value };
7442
+ } else if (standardAction === "select" && parsed.value) {
7443
+ actionRequest.params = { value: parsed.value };
7444
+ } else if (standardAction === "scroll" && parsed.scrollDirection) {
7445
+ actionRequest.params = { direction: parsed.scrollDirection };
7446
+ }
7447
+ const response = await this.actionExecutor.executeAction(element.id, actionRequest);
7448
+ if (!response.success) {
7449
+ throw new Error(response.error || "Action failed");
7450
+ }
7451
+ return { elementState: response.elementState };
7452
+ }
7453
+ /**
7454
+ * Create a failure response with suggestions
7455
+ */
7456
+ createFailureResponse(startTime, errorCode, errorMessage, instruction, alternatives, threshold, searchCriteria, nearestMatch) {
7457
+ const suggestions = this.generateSuggestions(
7458
+ errorCode,
7459
+ instruction,
7460
+ alternatives,
7461
+ nearestMatch
7462
+ );
7463
+ const dummyElement = nearestMatch?.element || {
7464
+ id: "not-found",
7465
+ type: "unknown",
7466
+ tagName: "unknown",
7467
+ actions: [],
7468
+ state: {
7469
+ visible: false,
7470
+ enabled: false,
7471
+ focused: false,
7472
+ rect: { x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0 }
7473
+ },
7474
+ registered: false,
7475
+ description: "Element not found",
7476
+ aliases: [],
7477
+ suggestedActions: []
7478
+ };
7479
+ return {
7480
+ success: false,
7481
+ executedAction: instruction,
7482
+ elementUsed: dummyElement,
7483
+ confidence: nearestMatch?.confidence || 0,
7484
+ elementState: dummyElement.state,
7485
+ durationMs: performance.now() - startTime,
7486
+ timestamp: Date.now(),
7487
+ error: errorMessage,
7488
+ errorCode,
7489
+ suggestions,
7490
+ alternatives: alternatives.slice(0, this.config.maxAlternatives)
7491
+ };
7492
+ }
7493
+ /**
7494
+ * Generate recovery suggestions
7495
+ */
7496
+ generateSuggestions(errorCode, instruction, alternatives, nearestMatch) {
7497
+ const suggestions = [];
7498
+ switch (errorCode) {
7499
+ case "PARSE_ERROR":
7500
+ suggestions.push('Try using a simpler phrase like "click Submit button"');
7501
+ suggestions.push('Ensure the instruction follows patterns like "click X" or "type Y into X"');
7502
+ break;
7503
+ case "ELEMENT_NOT_FOUND":
7504
+ if (alternatives.length > 0) {
7505
+ suggestions.push(`Did you mean: "${alternatives[0].element.description}"?`);
7506
+ }
7507
+ suggestions.push("Check if the element is visible on the page");
7508
+ suggestions.push("Try using a more specific description");
7509
+ break;
7510
+ case "LOW_CONFIDENCE":
7511
+ if (nearestMatch) {
7512
+ suggestions.push(
7513
+ `Found "${nearestMatch.element.description}" with ${(nearestMatch.confidence * 100).toFixed(0)}% confidence`
7514
+ );
7515
+ }
7516
+ suggestions.push("Try using the exact text shown on the element");
7517
+ suggestions.push("Lower the confidence threshold if this match is correct");
7518
+ break;
7519
+ case "ACTION_FAILED":
7520
+ suggestions.push("Check if the element is enabled");
7521
+ suggestions.push("Wait for any loading to complete");
7522
+ suggestions.push("Ensure no modal or overlay is blocking the element");
7523
+ break;
7524
+ default:
7525
+ suggestions.push("Try a different approach or check the page state");
7526
+ }
7527
+ return suggestions;
7528
+ }
7529
+ /**
7530
+ * Get rich error context for debugging
7531
+ */
7532
+ getErrorContext(errorCode, instruction, searchCriteria, nearestMatch) {
7533
+ return createErrorContext(
7534
+ errorCode,
7535
+ instruction,
7536
+ this.elements,
7537
+ searchCriteria,
7538
+ nearestMatch
7539
+ );
7540
+ }
7541
+ };
7542
+ function createNLActionExecutor(config) {
7543
+ return new NLActionExecutor(config);
7544
+ }
7545
+
7546
+ // src/ai/assertions.ts
7547
+ var DEFAULT_ASSERTION_CONFIG = {
7548
+ defaultTimeout: 5e3,
7549
+ pollInterval: 100,
7550
+ fuzzyThreshold: 0.7,
7551
+ includeSuggestions: true
7552
+ };
7553
+ var AssertionExecutor = class {
7554
+ constructor(config = {}) {
7555
+ this.elements = [];
7556
+ this.config = { ...DEFAULT_ASSERTION_CONFIG, ...config };
7557
+ this.searchEngine = new SearchEngine({ fuzzyThreshold: this.config.fuzzyThreshold });
7558
+ }
7559
+ /**
7560
+ * Update available elements for assertions
7561
+ */
7562
+ updateElements(elements) {
7563
+ this.elements = elements;
7564
+ this.searchEngine.updateElements(elements);
7565
+ }
7566
+ /**
7567
+ * Execute a single assertion
7568
+ */
7569
+ async assert(request) {
7570
+ const startTime = performance.now();
7571
+ const timeout = request.timeout ?? this.config.defaultTimeout;
7572
+ const element = await this.findElement(request.target, request.fuzzy !== false);
7573
+ if (!element && request.type !== "notExists") {
7574
+ return this.createResult(
7575
+ false,
7576
+ typeof request.target === "string" ? request.target : JSON.stringify(request.target),
7577
+ "element not found",
7578
+ request.type === "exists" ? true : request.expected,
7579
+ null,
7580
+ "Element could not be found",
7581
+ this.config.includeSuggestions ? "Check if the element exists and is properly labeled" : void 0,
7582
+ startTime
7583
+ );
7584
+ }
7585
+ return this.executeAssertion(request, element, timeout, startTime);
7586
+ }
7587
+ /**
7588
+ * Execute multiple assertions
7589
+ */
7590
+ async assertBatch(request) {
7591
+ const startTime = performance.now();
7592
+ const results = [];
7593
+ let passedCount = 0;
7594
+ let failedCount = 0;
7595
+ for (const assertion of request.assertions) {
7596
+ const result = await this.assert(assertion);
7597
+ results.push(result);
7598
+ if (result.passed) {
7599
+ passedCount++;
7600
+ } else {
7601
+ failedCount++;
7602
+ if (request.stopOnFailure) {
7603
+ break;
7604
+ }
7605
+ }
7606
+ }
7607
+ const passed = request.mode === "all" ? failedCount === 0 : passedCount > 0;
7608
+ return {
7609
+ passed,
7610
+ results,
7611
+ passedCount,
7612
+ failedCount,
7613
+ durationMs: performance.now() - startTime,
7614
+ timestamp: Date.now()
7615
+ };
7616
+ }
7617
+ /**
7618
+ * Convenience method: assert element is visible
7619
+ */
7620
+ async assertVisible(target, timeout) {
7621
+ return this.assert({ target, type: "visible", timeout });
7622
+ }
7623
+ /**
7624
+ * Convenience method: assert element is hidden
7625
+ */
7626
+ async assertHidden(target, timeout) {
7627
+ return this.assert({ target, type: "hidden", timeout });
7628
+ }
7629
+ /**
7630
+ * Convenience method: assert element is enabled
7631
+ */
7632
+ async assertEnabled(target, timeout) {
7633
+ return this.assert({ target, type: "enabled", timeout });
7634
+ }
7635
+ /**
7636
+ * Convenience method: assert element is disabled
7637
+ */
7638
+ async assertDisabled(target, timeout) {
7639
+ return this.assert({ target, type: "disabled", timeout });
7640
+ }
7641
+ /**
7642
+ * Convenience method: assert element has text
7643
+ */
7644
+ async assertHasText(target, text, timeout) {
7645
+ return this.assert({ target, type: "hasText", expected: text, timeout });
7646
+ }
7647
+ /**
7648
+ * Convenience method: assert element contains text
7649
+ */
7650
+ async assertContainsText(target, text, timeout) {
7651
+ return this.assert({ target, type: "containsText", expected: text, timeout });
7652
+ }
7653
+ /**
7654
+ * Convenience method: assert element has value
7655
+ */
7656
+ async assertHasValue(target, value, timeout) {
7657
+ return this.assert({ target, type: "hasValue", expected: value, timeout });
7658
+ }
7659
+ /**
7660
+ * Convenience method: assert element exists
7661
+ */
7662
+ async assertExists(target, timeout) {
7663
+ return this.assert({ target, type: "exists", timeout });
7664
+ }
7665
+ /**
7666
+ * Convenience method: assert element does not exist
7667
+ */
7668
+ async assertNotExists(target, timeout) {
7669
+ return this.assert({ target, type: "notExists", timeout });
7670
+ }
7671
+ /**
7672
+ * Convenience method: assert checkbox is checked
7673
+ */
7674
+ async assertChecked(target, timeout) {
7675
+ return this.assert({ target, type: "checked", timeout });
7676
+ }
7677
+ /**
7678
+ * Convenience method: assert checkbox is unchecked
7679
+ */
7680
+ async assertUnchecked(target, timeout) {
7681
+ return this.assert({ target, type: "unchecked", timeout });
7682
+ }
7683
+ /**
7684
+ * Convenience method: assert element count
7685
+ */
7686
+ async assertCount(target, expectedCount, timeout) {
7687
+ return this.assert({ target, type: "count", expected: expectedCount, timeout });
7688
+ }
7689
+ /**
7690
+ * Find element by target (string or criteria)
7691
+ */
7692
+ async findElement(target, fuzzy = true) {
7693
+ const criteria = typeof target === "string" ? { text: target, fuzzy } : { ...target, fuzzy };
7694
+ const searchResult = this.searchEngine.findBest(criteria);
7695
+ if (searchResult && searchResult.confidence >= this.config.fuzzyThreshold) {
7696
+ return searchResult.element;
7697
+ }
7698
+ return null;
7699
+ }
7700
+ /**
7701
+ * Execute the actual assertion
7702
+ */
7703
+ async executeAssertion(request, element, timeout, startTime) {
7704
+ const targetStr = typeof request.target === "string" ? request.target : JSON.stringify(request.target);
7705
+ const elementDescription = element?.description || targetStr;
7706
+ switch (request.type) {
7707
+ case "visible":
7708
+ return this.assertVisibility(element, true, elementDescription, request.message, startTime);
7709
+ case "hidden":
7710
+ return this.assertVisibility(element, false, elementDescription, request.message, startTime);
7711
+ case "enabled":
7712
+ return this.assertEnabledState(element, true, elementDescription, request.message, startTime);
7713
+ case "disabled":
7714
+ return this.assertEnabledState(element, false, elementDescription, request.message, startTime);
7715
+ case "focused":
7716
+ return this.assertFocused(element, elementDescription, request.message, startTime);
7717
+ case "checked":
7718
+ return this.assertCheckedState(element, true, elementDescription, request.message, startTime);
7719
+ case "unchecked":
7720
+ return this.assertCheckedState(element, false, elementDescription, request.message, startTime);
7721
+ case "hasText":
7722
+ return this.assertTextMatch(
7723
+ element,
7724
+ request.expected,
7725
+ true,
7726
+ elementDescription,
7727
+ request.message,
7728
+ startTime
7729
+ );
7730
+ case "containsText":
7731
+ return this.assertTextMatch(
7732
+ element,
7733
+ request.expected,
7734
+ false,
7735
+ elementDescription,
7736
+ request.message,
7737
+ startTime
7738
+ );
7739
+ case "hasValue":
7740
+ return this.assertValue(
7741
+ element,
7742
+ request.expected,
7743
+ elementDescription,
7744
+ request.message,
7745
+ startTime
7746
+ );
7747
+ case "exists":
7748
+ return this.createResult(
7749
+ element !== null,
7750
+ targetStr,
7751
+ elementDescription,
7752
+ true,
7753
+ element !== null,
7754
+ element === null ? "Element does not exist" : void 0,
7755
+ void 0,
7756
+ startTime,
7757
+ element?.state
7758
+ );
7759
+ case "notExists":
7760
+ return this.createResult(
7761
+ element === null,
7762
+ targetStr,
7763
+ elementDescription,
7764
+ false,
7765
+ element === null,
7766
+ element !== null ? "Element exists but should not" : void 0,
7767
+ void 0,
7768
+ startTime,
7769
+ element?.state
7770
+ );
7771
+ case "count":
7772
+ return this.assertElementCount(
7773
+ request.target,
7774
+ request.expected,
7775
+ targetStr,
7776
+ request.message,
7777
+ startTime
7778
+ );
7779
+ case "attribute":
7780
+ return this.assertAttribute(
7781
+ element,
7782
+ request.attributeName,
7783
+ request.expected,
7784
+ elementDescription,
7785
+ request.message,
7786
+ startTime
7787
+ );
7788
+ case "hasClass":
7789
+ return this.assertHasClass(
7790
+ element,
7791
+ request.expected,
7792
+ elementDescription,
7793
+ request.message,
7794
+ startTime
7795
+ );
7796
+ case "cssProperty":
7797
+ return this.assertCssProperty(
7798
+ element,
7799
+ request.propertyName,
7800
+ request.expected,
7801
+ elementDescription,
7802
+ request.message,
7803
+ startTime
7804
+ );
7805
+ default:
7806
+ return this.createResult(
7807
+ false,
7808
+ targetStr,
7809
+ elementDescription,
7810
+ void 0,
7811
+ void 0,
7812
+ `Unknown assertion type: ${request.type}`,
7813
+ void 0,
7814
+ startTime
7815
+ );
7816
+ }
7817
+ }
7818
+ /**
7819
+ * Assert visibility state
7820
+ */
7821
+ assertVisibility(element, expectedVisible, description, message, startTime = performance.now()) {
7822
+ const isVisible3 = element.state.visible;
7823
+ const passed = isVisible3 === expectedVisible;
7824
+ return this.createResult(
7825
+ passed,
7826
+ element.id,
7827
+ description,
7828
+ expectedVisible,
7829
+ isVisible3,
7830
+ passed ? void 0 : message || `Element is ${isVisible3 ? "visible" : "hidden"} but expected ${expectedVisible ? "visible" : "hidden"}`,
7831
+ passed ? void 0 : "Check if element is covered by another element or has display:none",
7832
+ startTime,
7833
+ element.state
7834
+ );
7835
+ }
7836
+ /**
7837
+ * Assert enabled state
7838
+ */
7839
+ assertEnabledState(element, expectedEnabled, description, message, startTime = performance.now()) {
7840
+ const isEnabled = element.state.enabled;
7841
+ const passed = isEnabled === expectedEnabled;
7842
+ return this.createResult(
7843
+ passed,
7844
+ element.id,
7845
+ description,
7846
+ expectedEnabled,
7847
+ isEnabled,
7848
+ passed ? void 0 : message || `Element is ${isEnabled ? "enabled" : "disabled"} but expected ${expectedEnabled ? "enabled" : "disabled"}`,
7849
+ passed ? void 0 : "Check if the element has a disabled attribute or aria-disabled",
7850
+ startTime,
7851
+ element.state
7852
+ );
7853
+ }
7854
+ /**
7855
+ * Assert focused state
7856
+ */
7857
+ assertFocused(element, description, message, startTime = performance.now()) {
7858
+ const isFocused = element.state.focused;
7859
+ return this.createResult(
7860
+ isFocused,
7861
+ element.id,
7862
+ description,
7863
+ true,
7864
+ isFocused,
7865
+ isFocused ? void 0 : message || "Element is not focused",
7866
+ isFocused ? void 0 : "Click or focus the element first",
7867
+ startTime,
7868
+ element.state
7869
+ );
7870
+ }
7871
+ /**
7872
+ * Assert checked state
7873
+ */
7874
+ assertCheckedState(element, expectedChecked, description, message, startTime = performance.now()) {
7875
+ const isChecked = element.state.checked ?? false;
7876
+ const passed = isChecked === expectedChecked;
7877
+ return this.createResult(
7878
+ passed,
7879
+ element.id,
7880
+ description,
7881
+ expectedChecked,
7882
+ isChecked,
7883
+ passed ? void 0 : message || `Element is ${isChecked ? "checked" : "unchecked"} but expected ${expectedChecked ? "checked" : "unchecked"}`,
7884
+ passed ? void 0 : "Click the checkbox to change its state",
7885
+ startTime,
7886
+ element.state
7887
+ );
7888
+ }
7889
+ /**
7890
+ * Assert text content
7891
+ */
7892
+ assertTextMatch(element, expectedText, exact, description, message, startTime = performance.now()) {
7893
+ const actualText = element.state.textContent || "";
7894
+ const passed = exact ? actualText === expectedText : actualText.includes(expectedText);
7895
+ return this.createResult(
7896
+ passed,
7897
+ element.id,
7898
+ description,
7899
+ expectedText,
7900
+ actualText,
7901
+ passed ? void 0 : message || (exact ? `Text "${actualText}" does not match expected "${expectedText}"` : `Text "${actualText}" does not contain "${expectedText}"`),
7902
+ passed ? void 0 : "Verify the element contains the expected text",
7903
+ startTime,
7904
+ element.state
7905
+ );
7906
+ }
7907
+ /**
7908
+ * Assert input value
7909
+ */
7910
+ assertValue(element, expectedValue, description, message, startTime = performance.now()) {
7911
+ const actualValue = element.state.value || "";
7912
+ const passed = actualValue === expectedValue;
7913
+ return this.createResult(
7914
+ passed,
7915
+ element.id,
7916
+ description,
7917
+ expectedValue,
7918
+ actualValue,
7919
+ passed ? void 0 : message || `Value "${actualValue}" does not match expected "${expectedValue}"`,
7920
+ passed ? void 0 : "Type the expected value into the input",
7921
+ startTime,
7922
+ element.state
7923
+ );
7924
+ }
7925
+ /**
7926
+ * Assert element count
7927
+ */
7928
+ assertElementCount(criteria, expectedCount, targetStr, message, startTime = performance.now()) {
7929
+ const searchResponse = this.searchEngine.search(criteria);
7930
+ const actualCount = searchResponse.results.length;
7931
+ const passed = actualCount === expectedCount;
7932
+ return this.createResult(
7933
+ passed,
7934
+ targetStr,
7935
+ `${actualCount} elements matching criteria`,
7936
+ expectedCount,
7937
+ actualCount,
7938
+ passed ? void 0 : message || `Found ${actualCount} elements but expected ${expectedCount}`,
7939
+ passed ? void 0 : "Adjust search criteria or wait for elements to load",
7940
+ startTime
7941
+ );
7942
+ }
7943
+ /**
7944
+ * Assert attribute value (placeholder for DOM attribute assertions)
7945
+ */
7946
+ assertAttribute(element, attributeName, expectedValue, description, message, startTime = performance.now()) {
7947
+ let actualValue;
7948
+ switch (attributeName.toLowerCase()) {
7949
+ case "placeholder":
7950
+ actualValue = element.placeholder;
7951
+ break;
7952
+ case "title":
7953
+ actualValue = element.title;
7954
+ break;
7955
+ default:
7956
+ return this.createResult(
7957
+ false,
7958
+ element.id,
7959
+ description,
7960
+ expectedValue,
7961
+ void 0,
7962
+ `Cannot check attribute "${attributeName}" without DOM access`,
7963
+ "Use the server API to check element attributes",
7964
+ startTime,
7965
+ element.state
7966
+ );
7967
+ }
7968
+ const passed = actualValue === expectedValue;
7969
+ return this.createResult(
7970
+ passed,
7971
+ element.id,
7972
+ description,
7973
+ expectedValue,
7974
+ actualValue,
7975
+ passed ? void 0 : message || `Attribute "${attributeName}" is "${actualValue}" but expected "${expectedValue}"`,
7976
+ void 0,
7977
+ startTime,
7978
+ element.state
7979
+ );
7980
+ }
7981
+ /**
7982
+ * Assert element has CSS class
7983
+ */
7984
+ assertHasClass(element, className, description, message, startTime = performance.now()) {
7985
+ return this.createResult(
7986
+ false,
7987
+ element.id,
7988
+ description,
7989
+ className,
7990
+ void 0,
7991
+ "Cannot check CSS classes without DOM access",
7992
+ "Use the server API to check element classes",
7993
+ startTime,
7994
+ element.state
7995
+ );
7996
+ }
7997
+ /**
7998
+ * Assert CSS property value
7999
+ */
8000
+ assertCssProperty(element, propertyName, expectedValue, description, message, startTime = performance.now()) {
8001
+ const computedStyles = element.state.computedStyles;
8002
+ if (!computedStyles) {
8003
+ return this.createResult(
8004
+ false,
8005
+ element.id,
8006
+ description,
8007
+ expectedValue,
8008
+ void 0,
8009
+ "Computed styles not available",
8010
+ "Request element state with computed styles",
8011
+ startTime,
8012
+ element.state
8013
+ );
8014
+ }
8015
+ const styleKey = propertyName;
8016
+ const actualValue = computedStyles[styleKey];
8017
+ const passed = actualValue === expectedValue;
8018
+ return this.createResult(
8019
+ passed,
8020
+ element.id,
8021
+ description,
8022
+ expectedValue,
8023
+ actualValue,
8024
+ passed ? void 0 : message || `CSS property "${propertyName}" is "${actualValue}" but expected "${expectedValue}"`,
8025
+ void 0,
8026
+ startTime,
8027
+ element.state
8028
+ );
8029
+ }
8030
+ /**
8031
+ * Create an assertion result
8032
+ */
8033
+ createResult(passed, target, targetDescription, expected, actual, failureReason, suggestion, startTime = performance.now(), elementState) {
8034
+ return {
8035
+ passed,
8036
+ target,
8037
+ targetDescription,
8038
+ expected,
8039
+ actual,
8040
+ failureReason,
8041
+ suggestion: this.config.includeSuggestions ? suggestion : void 0,
8042
+ elementState,
8043
+ durationMs: performance.now() - startTime,
8044
+ timestamp: Date.now()
8045
+ };
8046
+ }
8047
+ };
8048
+ function createAssertionExecutor(config) {
8049
+ return new AssertionExecutor(config);
8050
+ }
8051
+
8052
+ // src/ai/semantic-snapshot.ts
8053
+ var DEFAULT_SNAPSHOT_CONFIG = {
8054
+ analyzeForms: true,
8055
+ detectModals: true,
8056
+ inferPageType: true,
8057
+ generateDescriptions: true,
8058
+ maxElements: 500
8059
+ };
8060
+ var SemanticSnapshotManager = class {
8061
+ constructor(config = {}) {
8062
+ this.history = [];
8063
+ this.maxHistorySize = 10;
8064
+ this.snapshotCounter = 0;
8065
+ this.config = { ...DEFAULT_SNAPSHOT_CONFIG, ...config };
8066
+ this.searchEngine = new SearchEngine();
8067
+ }
8068
+ /**
8069
+ * Create a semantic snapshot from a control snapshot
8070
+ */
8071
+ createSnapshot(controlSnapshot, pageContext) {
8072
+ const snapshotId = `snapshot-${++this.snapshotCounter}-${Date.now()}`;
8073
+ const aiElements = this.convertElements(controlSnapshot.elements);
8074
+ this.searchEngine.updateElements(aiElements);
8075
+ const fullPageContext = this.buildPageContext(aiElements, pageContext);
8076
+ const forms = this.config.analyzeForms ? this.analyzeForms(aiElements) : [];
8077
+ const modals = this.config.detectModals ? this.detectModals(aiElements) : [];
8078
+ const elementCounts = this.countElementTypes(aiElements);
8079
+ const summary = generatePageSummary(aiElements, fullPageContext);
8080
+ const focusedElement = aiElements.find((el) => el.state.focused)?.id;
8081
+ const snapshot = {
8082
+ timestamp: Date.now(),
8083
+ snapshotId,
8084
+ page: fullPageContext,
8085
+ elements: aiElements.slice(0, this.config.maxElements),
8086
+ forms,
8087
+ activeModals: modals,
8088
+ focusedElement,
8089
+ summary,
8090
+ elementCounts
8091
+ };
8092
+ this.addToHistory(snapshot);
8093
+ return snapshot;
8094
+ }
8095
+ /**
8096
+ * Get the last snapshot
8097
+ */
8098
+ getLastSnapshot() {
8099
+ if (this.history.length === 0) return null;
8100
+ return this.history[this.history.length - 1].snapshot;
8101
+ }
8102
+ /**
8103
+ * Get snapshot by ID
8104
+ */
8105
+ getSnapshot(snapshotId) {
8106
+ const entry = this.history.find((h) => h.snapshot.snapshotId === snapshotId);
8107
+ return entry?.snapshot || null;
8108
+ }
8109
+ /**
8110
+ * Get snapshot history
8111
+ */
8112
+ getHistory() {
8113
+ return this.history.map((h) => h.snapshot);
8114
+ }
8115
+ /**
8116
+ * Clear history
8117
+ */
8118
+ clearHistory() {
8119
+ this.history = [];
8120
+ }
8121
+ /**
8122
+ * Convert control snapshot elements to AI elements
8123
+ */
8124
+ convertElements(elements) {
8125
+ return elements.map((el) => this.convertElement(el));
8126
+ }
8127
+ /**
8128
+ * Convert a single element to AI element
8129
+ */
8130
+ convertElement(element) {
8131
+ const aliases = generateAliases({
8132
+ textContent: element.state.textContent,
8133
+ elementType: element.type,
8134
+ id: element.id,
8135
+ labelText: element.label
8136
+ });
8137
+ const description = this.config.generateDescriptions ? generateDescription({
8138
+ textContent: element.state.textContent,
8139
+ elementType: element.type,
8140
+ id: element.id,
8141
+ labelText: element.label
8142
+ }) : element.label || element.id;
8143
+ const purpose = generatePurpose({
8144
+ textContent: element.state.textContent,
8145
+ elementType: element.type
8146
+ });
8147
+ const suggestedActions = generateSuggestedActions({
8148
+ textContent: element.state.textContent,
8149
+ elementType: element.type
8150
+ });
8151
+ return {
8152
+ id: element.id,
8153
+ type: element.type,
8154
+ label: element.label,
8155
+ tagName: this.inferTagName(element.type),
8156
+ role: this.inferRole(element.type),
8157
+ accessibleName: element.label || element.state.textContent?.trim(),
8158
+ actions: element.actions,
8159
+ state: element.state,
8160
+ registered: true,
8161
+ description,
8162
+ aliases,
8163
+ purpose,
8164
+ suggestedActions,
8165
+ semanticType: this.inferSemanticType(element)
8166
+ };
8167
+ }
8168
+ /**
8169
+ * Build full page context
8170
+ */
8171
+ buildPageContext(elements, partial) {
8172
+ const url = partial?.url || (typeof window !== "undefined" ? window.location.href : "");
8173
+ const title = partial?.title || (typeof document !== "undefined" ? document.title : "");
8174
+ const pageType = this.config.inferPageType ? inferPageType(url, title, elements) : partial?.pageType || "unknown";
8175
+ const activeModals = elements.filter((el) => el.type === "dialog" && el.state.visible).map((el) => el.id);
8176
+ return {
8177
+ url,
8178
+ title,
8179
+ pageType,
8180
+ activeModals: partial?.activeModals || activeModals,
8181
+ focusedElement: partial?.focusedElement || elements.find((el) => el.state.focused)?.id,
8182
+ navigation: partial?.navigation
8183
+ };
8184
+ }
8185
+ /**
8186
+ * Analyze forms in the snapshot
8187
+ */
8188
+ analyzeForms(elements) {
8189
+ const forms = [];
8190
+ const formElements = elements.filter((el) => el.type === "form");
8191
+ if (formElements.length === 0) {
8192
+ const implicitForm = this.detectImplicitForm(elements);
8193
+ if (implicitForm) {
8194
+ forms.push(implicitForm);
8195
+ }
8196
+ } else {
8197
+ for (const form of formElements) {
8198
+ const formState = this.analyzeForm(form, elements);
8199
+ if (formState) {
8200
+ forms.push(formState);
8201
+ }
8202
+ }
8203
+ }
8204
+ return forms;
8205
+ }
8206
+ /**
8207
+ * Detect implicit form from inputs
8208
+ */
8209
+ detectImplicitForm(elements) {
8210
+ const inputs = elements.filter(
8211
+ (el) => el.type === "input" || el.type === "textarea" || el.type === "select" || el.type === "checkbox"
8212
+ );
8213
+ if (inputs.length === 0) return null;
8214
+ const submitButton = elements.find(
8215
+ (el) => el.type === "button" && el.state.visible && (el.semanticType === "submit-button" || el.state.textContent?.toLowerCase().match(/submit|save|send|continue/))
8216
+ );
8217
+ const fields = this.analyzeFormFields(inputs);
8218
+ const hasErrors = fields.some((f) => !f.valid);
8219
+ return {
8220
+ id: "implicit-form",
8221
+ purpose: this.inferFormPurpose(inputs),
8222
+ fields,
8223
+ isValid: !hasErrors,
8224
+ submitButton: submitButton?.id,
8225
+ isDirty: fields.some((f) => f.value !== "" && f.touched)
8226
+ };
8227
+ }
8228
+ /**
8229
+ * Analyze a specific form
8230
+ */
8231
+ analyzeForm(form, allElements) {
8232
+ const inputs = allElements.filter(
8233
+ (el) => (el.type === "input" || el.type === "textarea" || el.type === "select") && el.state.visible
8234
+ );
8235
+ const fields = this.analyzeFormFields(inputs);
8236
+ const hasErrors = fields.some((f) => !f.valid);
8237
+ const submitButton = allElements.find(
8238
+ (el) => el.type === "button" && el.state.visible && el.semanticType === "submit-button"
8239
+ );
8240
+ return {
8241
+ id: form.id,
8242
+ name: form.label,
8243
+ purpose: form.purpose,
8244
+ fields,
8245
+ isValid: !hasErrors,
8246
+ submitButton: submitButton?.id,
8247
+ isDirty: fields.some((f) => f.value !== "")
8248
+ };
8249
+ }
8250
+ /**
8251
+ * Analyze form fields
8252
+ */
8253
+ analyzeFormFields(inputs) {
8254
+ return inputs.map((input) => ({
8255
+ id: input.id,
8256
+ label: input.accessibleName || input.label || input.id,
8257
+ type: input.type,
8258
+ value: input.state.value || "",
8259
+ valid: true,
8260
+ // Would need validation state
8261
+ required: false,
8262
+ // Would need DOM access
8263
+ touched: input.state.focused || (input.state.value?.length || 0) > 0
8264
+ }));
8265
+ }
8266
+ /**
8267
+ * Detect modal dialogs
8268
+ */
8269
+ detectModals(elements) {
8270
+ const modals = [];
8271
+ const dialogElements = elements.filter(
8272
+ (el) => el.type === "dialog" && el.state.visible
8273
+ );
8274
+ for (const dialog of dialogElements) {
8275
+ const closeButton = elements.find(
8276
+ (el) => el.type === "button" && el.state.visible && (el.semanticType === "cancel-button" || el.state.textContent?.toLowerCase().match(/close|cancel|x|dismiss/))
8277
+ );
8278
+ const primaryAction = elements.find(
8279
+ (el) => el.type === "button" && el.state.visible && el.semanticType === "submit-button"
8280
+ );
8281
+ modals.push({
8282
+ id: dialog.id,
8283
+ title: dialog.accessibleName || dialog.label,
8284
+ type: this.inferModalType(dialog),
8285
+ blocking: true,
8286
+ // Assume dialogs are blocking
8287
+ closeButton: closeButton?.id,
8288
+ primaryAction: primaryAction?.id
8289
+ });
8290
+ }
8291
+ return modals;
8292
+ }
8293
+ /**
8294
+ * Infer modal type
8295
+ */
8296
+ inferModalType(dialog) {
8297
+ const text = (dialog.accessibleName || dialog.state.textContent || "").toLowerCase();
8298
+ if (text.includes("alert") || text.includes("warning") || text.includes("error")) {
8299
+ return "alert";
8300
+ }
8301
+ if (text.includes("confirm") || text.includes("are you sure")) {
8302
+ return "confirm";
8303
+ }
8304
+ if (text.includes("prompt") || text.includes("enter")) {
8305
+ return "prompt";
8306
+ }
8307
+ return "dialog";
8308
+ }
8309
+ /**
8310
+ * Count elements by type
8311
+ */
8312
+ countElementTypes(elements) {
8313
+ const counts = {};
8314
+ for (const el of elements) {
8315
+ const type = el.type.toLowerCase();
8316
+ counts[type] = (counts[type] || 0) + 1;
8317
+ }
8318
+ return counts;
8319
+ }
8320
+ /**
8321
+ * Infer form purpose from fields
8322
+ */
8323
+ inferFormPurpose(fields) {
8324
+ const labels = fields.map(
8325
+ (f) => (f.accessibleName || f.label || "").toLowerCase()
8326
+ );
8327
+ const allLabels = labels.join(" ");
8328
+ if (allLabels.includes("email") && allLabels.includes("password")) {
8329
+ if (allLabels.includes("confirm") || allLabels.includes("name")) {
8330
+ return "Registration";
8331
+ }
8332
+ return "Login";
8333
+ }
8334
+ if (allLabels.includes("search")) return "Search";
8335
+ if (allLabels.includes("address") || allLabels.includes("city")) return "Address";
8336
+ if (allLabels.includes("card") || allLabels.includes("payment")) return "Payment";
8337
+ if (allLabels.includes("contact") || allLabels.includes("message")) return "Contact";
8338
+ return "Form";
8339
+ }
8340
+ /**
8341
+ * Infer tag name from element type
8342
+ */
8343
+ inferTagName(type) {
8344
+ const typeMap = {
8345
+ button: "button",
8346
+ input: "input",
8347
+ textarea: "textarea",
8348
+ select: "select",
8349
+ checkbox: "input",
8350
+ radio: "input",
8351
+ link: "a",
8352
+ form: "form",
8353
+ dialog: "dialog"
8354
+ };
8355
+ return typeMap[type] || "div";
8356
+ }
8357
+ /**
8358
+ * Infer ARIA role from element type
8359
+ */
8360
+ inferRole(type) {
8361
+ const roleMap = {
8362
+ button: "button",
8363
+ input: "textbox",
8364
+ textarea: "textbox",
8365
+ select: "combobox",
8366
+ checkbox: "checkbox",
8367
+ radio: "radio",
8368
+ link: "link",
8369
+ dialog: "dialog",
8370
+ menu: "menu",
8371
+ menuitem: "menuitem",
8372
+ tab: "tab"
8373
+ };
8374
+ return roleMap[type];
8375
+ }
8376
+ /**
8377
+ * Infer semantic type
8378
+ */
8379
+ inferSemanticType(element) {
8380
+ const text = (element.state.textContent || element.label || "").toLowerCase();
8381
+ const type = element.type.toLowerCase();
8382
+ if (type === "button") {
8383
+ if (text.match(/submit|save|confirm|ok|done|apply/)) return "submit-button";
8384
+ if (text.match(/cancel|close|dismiss/)) return "cancel-button";
8385
+ if (text.match(/delete|remove|trash/)) return "delete-button";
8386
+ if (text.match(/add|create|new|\+/)) return "add-button";
8387
+ if (text.match(/edit|modify/)) return "edit-button";
8388
+ if (text.match(/next|continue/)) return "next-button";
8389
+ if (text.match(/back|previous/)) return "back-button";
8390
+ return "action-button";
8391
+ }
8392
+ if (type === "input") {
8393
+ if (text.includes("email") || element.id.includes("email")) return "email-input";
8394
+ if (text.includes("password") || element.id.includes("password")) return "password-input";
8395
+ if (text.includes("search") || element.id.includes("search")) return "search-input";
8396
+ return "text-input";
8397
+ }
8398
+ return type;
8399
+ }
8400
+ /**
8401
+ * Add snapshot to history
8402
+ */
8403
+ addToHistory(snapshot) {
8404
+ this.history.push({
8405
+ snapshot,
8406
+ timestamp: Date.now()
8407
+ });
8408
+ if (this.history.length > this.maxHistorySize) {
8409
+ this.history = this.history.slice(-this.maxHistorySize);
8410
+ }
8411
+ }
8412
+ };
8413
+ function createSnapshotManager(config) {
8414
+ return new SemanticSnapshotManager(config);
8415
+ }
8416
+
8417
+ // src/ai/semantic-diff.ts
8418
+ var DEFAULT_DIFF_CONFIG = {
8419
+ ignoreInsignificant: true,
8420
+ trackedProperties: ["visible", "enabled", "focused", "checked", "value", "textContent"],
8421
+ generateSuggestions: true,
8422
+ maxModifications: 20
8423
+ };
8424
+ var INSIGNIFICANT_PROPERTIES = /* @__PURE__ */ new Set(["rect", "computedStyles", "innerHTML"]);
8425
+ function computeDiff(fromSnapshot, toSnapshot, config = {}) {
8426
+ const startTime = performance.now();
8427
+ const finalConfig = { ...DEFAULT_DIFF_CONFIG, ...config };
8428
+ const fromElements = new Map(fromSnapshot.elements.map((el) => [el.id, el]));
8429
+ const toElements = new Map(toSnapshot.elements.map((el) => [el.id, el]));
8430
+ const appeared = [];
8431
+ for (const [id, element] of toElements) {
8432
+ if (!fromElements.has(id)) {
8433
+ appeared.push({
8434
+ elementId: id,
8435
+ description: element.description,
8436
+ type: element.type,
8437
+ semanticType: element.semanticType
8438
+ });
8439
+ }
8440
+ }
8441
+ const disappeared = [];
8442
+ for (const [id, element] of fromElements) {
8443
+ if (!toElements.has(id)) {
8444
+ disappeared.push({
8445
+ elementId: id,
8446
+ description: element.description,
8447
+ type: element.type,
8448
+ semanticType: element.semanticType
8449
+ });
8450
+ }
8451
+ }
8452
+ const modified = [];
8453
+ for (const [id, toElement] of toElements) {
8454
+ const fromElement = fromElements.get(id);
8455
+ if (fromElement) {
8456
+ const modifications = compareElements(fromElement, toElement, finalConfig);
8457
+ modified.push(...modifications);
8458
+ }
8459
+ }
8460
+ const limitedModifications = modified.slice(0, finalConfig.maxModifications);
8461
+ const probableTrigger = detectTrigger(appeared, disappeared, limitedModifications);
8462
+ const suggestedActions = finalConfig.generateSuggestions ? generateSuggestedActionsFromDiff(appeared, disappeared, limitedModifications, probableTrigger) : void 0;
8463
+ const pageChanges = detectPageChanges(fromSnapshot, toSnapshot);
8464
+ const summary = generateDiffSummary(
8465
+ appeared.map((e) => e.description),
8466
+ disappeared.map((e) => e.description),
8467
+ limitedModifications
8468
+ );
8469
+ return {
8470
+ summary,
8471
+ fromSnapshotId: fromSnapshot.snapshotId,
8472
+ toSnapshotId: toSnapshot.snapshotId,
8473
+ changes: {
8474
+ appeared,
8475
+ disappeared,
8476
+ modified: limitedModifications
8477
+ },
8478
+ probableTrigger,
8479
+ suggestedActions,
8480
+ pageChanges,
8481
+ durationMs: performance.now() - startTime,
8482
+ timestamp: Date.now()
8483
+ };
8484
+ }
8485
+ function compareElements(fromElement, toElement, config) {
8486
+ const modifications = [];
8487
+ for (const property of config.trackedProperties) {
8488
+ const fromValue = getPropertyValue(fromElement, property);
8489
+ const toValue = getPropertyValue(toElement, property);
8490
+ if (fromValue !== toValue) {
8491
+ const isSignificant = isSignificantChange(property, fromValue, toValue);
8492
+ if (!config.ignoreInsignificant || isSignificant) {
8493
+ modifications.push({
8494
+ elementId: toElement.id,
8495
+ description: toElement.description,
8496
+ property,
8497
+ from: formatValue(fromValue),
8498
+ to: formatValue(toValue),
8499
+ significant: isSignificant
8500
+ });
8501
+ }
8502
+ }
8503
+ }
8504
+ return modifications;
8505
+ }
8506
+ function getPropertyValue(element, property) {
8507
+ if (property in element.state) {
8508
+ return element.state[property];
8509
+ }
8510
+ return element[property];
8511
+ }
8512
+ function isSignificantChange(property, fromValue, toValue) {
8513
+ if (INSIGNIFICANT_PROPERTIES.has(property)) {
8514
+ return false;
8515
+ }
8516
+ if (property === "visible") {
8517
+ return true;
8518
+ }
8519
+ if (property === "enabled") {
8520
+ return true;
8521
+ }
8522
+ if (property === "focused") {
8523
+ return true;
8524
+ }
8525
+ if (property === "checked") {
8526
+ return true;
8527
+ }
8528
+ if (property === "value") {
8529
+ return Boolean(fromValue) || Boolean(toValue);
8530
+ }
8531
+ if (property === "textContent") {
8532
+ const fromText = String(fromValue || "");
8533
+ const toText = String(toValue || "");
8534
+ return fromText.trim() !== toText.trim();
8535
+ }
8536
+ return true;
8537
+ }
8538
+ function formatValue(value) {
8539
+ if (value === void 0) return "undefined";
8540
+ if (value === null) return "null";
8541
+ if (typeof value === "boolean") return value ? "true" : "false";
8542
+ if (typeof value === "string") {
8543
+ if (value.length > 50) {
8544
+ return value.substring(0, 47) + "...";
8545
+ }
8546
+ return value;
8547
+ }
8548
+ if (typeof value === "object") {
8549
+ return JSON.stringify(value);
8550
+ }
8551
+ return String(value);
8552
+ }
8553
+ function detectTrigger(appeared, disappeared, modified) {
8554
+ const hasNewErrors = appeared.some(
8555
+ (e) => e.description.toLowerCase().includes("error") || e.type === "error"
8556
+ );
8557
+ if (hasNewErrors) {
8558
+ return "Form validation";
8559
+ }
8560
+ const hasNewModal = appeared.some(
8561
+ (e) => e.type === "dialog" || e.semanticType?.includes("dialog")
8562
+ );
8563
+ if (hasNewModal) {
8564
+ return "Modal opened";
8565
+ }
8566
+ const hasModalDismissed = disappeared.some(
8567
+ (e) => e.type === "dialog" || e.semanticType?.includes("dialog")
8568
+ );
8569
+ if (hasModalDismissed) {
8570
+ return "Modal closed";
8571
+ }
8572
+ const hasLoading = modified.some((m) => m.description.toLowerCase().includes("loading"));
8573
+ if (hasLoading) {
8574
+ return "Loading state change";
8575
+ }
8576
+ const hasFocusChange = modified.some((m) => m.property === "focused");
8577
+ if (hasFocusChange && modified.length <= 2) {
8578
+ return "Focus changed";
8579
+ }
8580
+ const hasValueChange = modified.some((m) => m.property === "value");
8581
+ if (hasValueChange && modified.length <= 2) {
8582
+ return "User input";
8583
+ }
8584
+ const visibilityChanges = modified.filter((m) => m.property === "visible");
8585
+ if (visibilityChanges.length > 0 && visibilityChanges.length <= 5) {
8586
+ return "UI expansion/collapse";
8587
+ }
8588
+ if (appeared.length > 5) {
8589
+ return "Page navigation";
8590
+ }
8591
+ return void 0;
8592
+ }
8593
+ function detectPageChanges(fromSnapshot, toSnapshot) {
8594
+ const urlChanged = fromSnapshot.page.url !== toSnapshot.page.url;
8595
+ const titleChanged = fromSnapshot.page.title !== toSnapshot.page.title;
8596
+ if (!urlChanged && !titleChanged) {
8597
+ return void 0;
8598
+ }
8599
+ return {
8600
+ urlChanged,
8601
+ titleChanged,
8602
+ newUrl: urlChanged ? toSnapshot.page.url : void 0,
8603
+ newTitle: titleChanged ? toSnapshot.page.title : void 0
8604
+ };
8605
+ }
8606
+ function generateSuggestedActionsFromDiff(appeared, disappeared, modified, trigger) {
8607
+ const suggestions = [];
8608
+ if (trigger === "Form validation") {
8609
+ suggestions.push("Fix the validation errors before submitting");
8610
+ }
8611
+ if (trigger === "Modal opened") {
8612
+ const modal = appeared.find(
8613
+ (e) => e.type === "dialog" || e.semanticType?.includes("dialog")
8614
+ );
8615
+ if (modal) {
8616
+ suggestions.push(`Interact with the "${modal.description}" dialog`);
8617
+ }
8618
+ }
8619
+ if (trigger === "Modal closed") {
8620
+ suggestions.push("Continue with the main page interaction");
8621
+ }
8622
+ for (const element of appeared.slice(0, 3)) {
8623
+ if (element.type === "button" && element.semanticType === "submit-button") {
8624
+ suggestions.push(`Click the "${element.description}" to proceed`);
8625
+ }
8626
+ if (element.description.toLowerCase().includes("error")) {
8627
+ suggestions.push(`Address the error: ${element.description}`);
8628
+ }
8629
+ }
8630
+ for (const mod of modified.slice(0, 3)) {
8631
+ if (mod.property === "enabled" && mod.to === "true") {
8632
+ suggestions.push(`"${mod.description}" is now enabled`);
8633
+ }
8634
+ if (mod.property === "visible" && mod.to === "true") {
8635
+ suggestions.push(`"${mod.description}" is now visible`);
8636
+ }
8637
+ }
8638
+ return suggestions.slice(0, 5);
8639
+ }
8640
+ var SemanticDiffManager = class {
8641
+ constructor(config = {}) {
8642
+ this.lastSnapshot = null;
8643
+ this.config = { ...DEFAULT_DIFF_CONFIG, ...config };
8644
+ }
8645
+ /**
8646
+ * Update with new snapshot and get diff
8647
+ */
8648
+ update(newSnapshot) {
8649
+ if (!this.lastSnapshot) {
8650
+ this.lastSnapshot = newSnapshot;
8651
+ return null;
8652
+ }
8653
+ const diff = computeDiff(this.lastSnapshot, newSnapshot, this.config);
8654
+ this.lastSnapshot = newSnapshot;
8655
+ return diff;
8656
+ }
8657
+ /**
8658
+ * Get diff from a specific snapshot to current
8659
+ */
8660
+ diffFrom(fromSnapshot) {
8661
+ if (!this.lastSnapshot) return null;
8662
+ return computeDiff(fromSnapshot, this.lastSnapshot, this.config);
8663
+ }
8664
+ /**
8665
+ * Reset the manager
8666
+ */
8667
+ reset() {
8668
+ this.lastSnapshot = null;
8669
+ }
8670
+ /**
8671
+ * Get the last known snapshot
8672
+ */
8673
+ getLastSnapshot() {
8674
+ return this.lastSnapshot;
8675
+ }
8676
+ };
8677
+ function createDiffManager(config) {
8678
+ return new SemanticDiffManager(config);
8679
+ }
8680
+ function hasSignificantChanges(diff) {
8681
+ if (diff.changes.appeared.length > 0) return true;
8682
+ if (diff.changes.disappeared.length > 0) return true;
8683
+ if (diff.changes.modified.some((m) => m.significant)) return true;
8684
+ if (diff.pageChanges?.urlChanged) return true;
8685
+ return false;
8686
+ }
8687
+ function describeDiff(diff) {
8688
+ const parts = [];
8689
+ if (diff.changes.appeared.length > 0) {
8690
+ parts.push(`${diff.changes.appeared.length} elements appeared`);
8691
+ }
8692
+ if (diff.changes.disappeared.length > 0) {
8693
+ parts.push(`${diff.changes.disappeared.length} elements disappeared`);
8694
+ }
8695
+ const significantMods = diff.changes.modified.filter((m) => m.significant);
8696
+ if (significantMods.length > 0) {
8697
+ parts.push(`${significantMods.length} elements modified`);
8698
+ }
8699
+ if (diff.pageChanges?.urlChanged) {
8700
+ parts.push("URL changed");
8701
+ }
8702
+ if (parts.length === 0) {
8703
+ return "No significant changes";
8704
+ }
8705
+ return parts.join(", ");
8706
+ }
8707
+
8708
+ exports.AssertionExecutor = AssertionExecutor;
4665
8709
  exports.AutoRegisterProvider = AutoRegisterProvider;
8710
+ exports.DEFAULT_ALIAS_CONFIG = DEFAULT_ALIAS_CONFIG;
8711
+ exports.DEFAULT_ASSERTION_CONFIG = DEFAULT_ASSERTION_CONFIG;
8712
+ exports.DEFAULT_DIFF_CONFIG = DEFAULT_DIFF_CONFIG;
8713
+ exports.DEFAULT_EXECUTOR_CONFIG = DEFAULT_EXECUTOR_CONFIG;
8714
+ exports.DEFAULT_FUZZY_CONFIG = DEFAULT_FUZZY_CONFIG;
8715
+ exports.DEFAULT_SEARCH_CONFIG = DEFAULT_SEARCH_CONFIG;
8716
+ exports.DEFAULT_SNAPSHOT_CONFIG = DEFAULT_SNAPSHOT_CONFIG;
4666
8717
  exports.DOMChangeObserver = DOMChangeObserver;
4667
8718
  exports.DefaultActionExecutor = DefaultActionExecutor;
4668
8719
  exports.DefaultWorkflowEngine = DefaultWorkflowEngine;
8720
+ exports.ErrorCodes = ErrorCodes;
4669
8721
  exports.ID_ATTRIBUTES = ID_ATTRIBUTES;
4670
8722
  exports.InMemoryRenderLogStorage = InMemoryRenderLogStorage;
4671
8723
  exports.InfoPanel = InfoPanel;
4672
8724
  exports.Inspector = Inspector;
4673
8725
  exports.InspectorOverlay = InspectorOverlay;
4674
8726
  exports.MetricsCollector = MetricsCollector;
8727
+ exports.NLActionExecutor = NLActionExecutor;
4675
8728
  exports.RenderLogManager = RenderLogManager;
8729
+ exports.SearchEngine = SearchEngine;
8730
+ exports.SemanticDiffManager = SemanticDiffManager;
8731
+ exports.SemanticSnapshotManager = SemanticSnapshotManager;
4676
8732
  exports.UIBridgeProvider = UIBridgeProvider;
4677
8733
  exports.UIBridgeRegistry = UIBridgeRegistry;
4678
8734
  exports.UIBridgeWSClient = UIBridgeWSClient;
8735
+ exports.areSynonyms = areSynonyms;
4679
8736
  exports.captureDOMSnapshot = captureDOMSnapshot;
4680
8737
  exports.captureInteractiveElements = captureInteractiveElements;
8738
+ exports.computeDiff = computeDiff;
4681
8739
  exports.createActionExecutor = createActionExecutor;
8740
+ exports.createAssertionExecutor = createAssertionExecutor;
8741
+ exports.createDiffManager = createDiffManager;
4682
8742
  exports.createElementIdentifier = createElementIdentifier;
8743
+ exports.createErrorContext = createErrorContext;
4683
8744
  exports.createMetricsCollector = createMetricsCollector;
8745
+ exports.createNLActionExecutor = createNLActionExecutor;
4684
8746
  exports.createRenderLogManager = createRenderLogManager;
8747
+ exports.createSearchEngine = createSearchEngine;
8748
+ exports.createSimpleError = createSimpleError;
8749
+ exports.createSnapshotManager = createSnapshotManager;
4685
8750
  exports.createWSClient = createWSClient;
4686
8751
  exports.createWorkflowEngine = createWorkflowEngine;
8752
+ exports.describeAction = describeAction;
8753
+ exports.describeDiff = describeDiff;
4687
8754
  exports.elementMatchesIdentifier = elementMatchesIdentifier;
8755
+ exports.extractModifiers = extractModifiers;
4688
8756
  exports.findAllElementsByIdentifier = findAllElementsByIdentifier;
8757
+ exports.findAllMatches = findAllMatches;
8758
+ exports.findBestMatch = findBestMatch;
4689
8759
  exports.findElementByIdentifier = findElementByIdentifier;
4690
8760
  exports.formatDuration = formatDuration;
8761
+ exports.formatErrorContext = formatErrorContext;
4691
8762
  exports.formatPercentage = formatPercentage;
8763
+ exports.fuzzyContains = fuzzyContains;
8764
+ exports.fuzzyMatch = fuzzyMatch;
8765
+ exports.generateAliases = generateAliases;
4692
8766
  exports.generateCSSSelector = generateCSSSelector;
8767
+ exports.generateDescription = generateDescription;
8768
+ exports.generateDiffSummary = generateDiffSummary;
8769
+ exports.generateElementDescription = generateElementDescription;
8770
+ exports.generateNgrams = generateNgrams;
8771
+ exports.generatePageSummary = generatePageSummary;
8772
+ exports.generatePurpose = generatePurpose;
8773
+ exports.generateSnapshotSummary = generateSnapshotSummary;
8774
+ exports.generateSuggestedActions = generateSuggestedActions;
4693
8775
  exports.generateXPath = generateXPath;
4694
8776
  exports.getBestIdentifier = getBestIdentifier;
8777
+ exports.getBestRecoverySuggestion = getBestRecoverySuggestion;
4695
8778
  exports.getGlobalRegistry = getGlobalRegistry;
8779
+ exports.getSynonyms = getSynonyms;
8780
+ exports.hasSignificantChanges = hasSignificantChanges;
8781
+ exports.inferPageType = inferPageType;
8782
+ exports.isRecoverableError = isRecoverableError;
8783
+ exports.jaroSimilarity = jaroSimilarity;
8784
+ exports.jaroWinklerSimilarity = jaroWinklerSimilarity;
8785
+ exports.levenshteinDistance = levenshteinDistance;
8786
+ exports.levenshteinSimilarity = levenshteinSimilarity;
8787
+ exports.ngramSimilarity = ngramSimilarity;
8788
+ exports.normalizeString = normalizeString;
8789
+ exports.parseNLInstruction = parseNLInstruction;
8790
+ exports.parseNLInstructions = parseNLInstructions;
4696
8791
  exports.resetGlobalRegistry = resetGlobalRegistry;
4697
8792
  exports.setGlobalRegistry = setGlobalRegistry;
8793
+ exports.splitCompoundInstruction = splitCompoundInstruction;
8794
+ exports.tokenSimilarity = tokenSimilarity;
8795
+ exports.tokenize = tokenize;
4698
8796
  exports.useActiveStates = useActiveStates;
4699
8797
  exports.useAutoRegister = useAutoRegister;
4700
8798
  exports.useAvailableTransitions = useAvailableTransitions;
@@ -4715,5 +8813,7 @@ exports.useUINavigation = useUINavigation;
4715
8813
  exports.useUIState = useUIState;
4716
8814
  exports.useUIStateGroup = useUIStateGroup;
4717
8815
  exports.useUITransition = useUITransition;
8816
+ exports.validateParsedAction = validateParsedAction;
8817
+ exports.wordSimilarity = wordSimilarity;
4718
8818
  //# sourceMappingURL=index.js.map
4719
8819
  //# sourceMappingURL=index.js.map