agent-web-interface 4.2.0 → 4.4.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 (265) hide show
  1. package/dist/src/browser/connection-utils.d.ts +48 -0
  2. package/dist/src/browser/connection-utils.d.ts.map +1 -0
  3. package/dist/src/browser/connection-utils.js +129 -0
  4. package/dist/src/browser/connection-utils.js.map +1 -0
  5. package/dist/src/browser/index.d.ts +3 -1
  6. package/dist/src/browser/index.d.ts.map +1 -1
  7. package/dist/src/browser/index.js +2 -1
  8. package/dist/src/browser/index.js.map +1 -1
  9. package/dist/src/browser/session-manager.d.ts +1 -89
  10. package/dist/src/browser/session-manager.d.ts.map +1 -1
  11. package/dist/src/browser/session-manager.js +1 -116
  12. package/dist/src/browser/session-manager.js.map +1 -1
  13. package/dist/src/browser/session-manager.types.d.ts +90 -0
  14. package/dist/src/browser/session-manager.types.d.ts.map +1 -0
  15. package/dist/src/browser/session-manager.types.js +7 -0
  16. package/dist/src/browser/session-manager.types.js.map +1 -0
  17. package/dist/src/form/constraint-extraction.d.ts +31 -0
  18. package/dist/src/form/constraint-extraction.d.ts.map +1 -0
  19. package/dist/src/form/constraint-extraction.js +110 -0
  20. package/dist/src/form/constraint-extraction.js.map +1 -0
  21. package/dist/src/form/field-extractor.d.ts.map +1 -1
  22. package/dist/src/form/field-extractor.js +3 -444
  23. package/dist/src/form/field-extractor.js.map +1 -1
  24. package/dist/src/form/field-state-extractor.d.ts +22 -0
  25. package/dist/src/form/field-state-extractor.d.ts.map +1 -0
  26. package/dist/src/form/field-state-extractor.js +55 -0
  27. package/dist/src/form/field-state-extractor.js.map +1 -0
  28. package/dist/src/form/form-actions.d.ts +45 -0
  29. package/dist/src/form/form-actions.d.ts.map +1 -0
  30. package/dist/src/form/form-actions.js +108 -0
  31. package/dist/src/form/form-actions.js.map +1 -0
  32. package/dist/src/form/form-detector.d.ts +0 -36
  33. package/dist/src/form/form-detector.d.ts.map +1 -1
  34. package/dist/src/form/form-detector.js +11 -376
  35. package/dist/src/form/form-detector.js.map +1 -1
  36. package/dist/src/form/input-clustering.d.ts +15 -0
  37. package/dist/src/form/input-clustering.d.ts.map +1 -0
  38. package/dist/src/form/input-clustering.js +61 -0
  39. package/dist/src/form/input-clustering.js.map +1 -0
  40. package/dist/src/form/intent-inference.d.ts +28 -0
  41. package/dist/src/form/intent-inference.d.ts.map +1 -0
  42. package/dist/src/form/intent-inference.js +137 -0
  43. package/dist/src/form/intent-inference.js.map +1 -0
  44. package/dist/src/form/purpose-inference.d.ts +50 -0
  45. package/dist/src/form/purpose-inference.d.ts.map +1 -0
  46. package/dist/src/form/purpose-inference.js +313 -0
  47. package/dist/src/form/purpose-inference.js.map +1 -0
  48. package/dist/src/form/submit-detection.d.ts +36 -0
  49. package/dist/src/form/submit-detection.d.ts.map +1 -0
  50. package/dist/src/form/submit-detection.js +101 -0
  51. package/dist/src/form/submit-detection.js.map +1 -0
  52. package/dist/src/form/types.d.ts +2 -2
  53. package/dist/src/index.js +65 -48
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/observation/observation-accumulator.d.ts +1 -1
  56. package/dist/src/observation/observation-accumulator.js +1 -1
  57. package/dist/src/observation/observer-script.d.ts +1 -1
  58. package/dist/src/observation/observer-script.d.ts.map +1 -1
  59. package/dist/src/observation/observer-script.js +129 -7
  60. package/dist/src/observation/observer-script.js.map +1 -1
  61. package/dist/src/query/disambiguation.d.ts +18 -0
  62. package/dist/src/query/disambiguation.d.ts.map +1 -0
  63. package/dist/src/query/disambiguation.js +123 -0
  64. package/dist/src/query/disambiguation.js.map +1 -0
  65. package/dist/src/query/fuzzy-match.d.ts +17 -0
  66. package/dist/src/query/fuzzy-match.d.ts.map +1 -0
  67. package/dist/src/query/fuzzy-match.js +34 -0
  68. package/dist/src/query/fuzzy-match.js.map +1 -0
  69. package/dist/src/query/index.d.ts +3 -0
  70. package/dist/src/query/index.d.ts.map +1 -1
  71. package/dist/src/query/index.js +6 -0
  72. package/dist/src/query/index.js.map +1 -1
  73. package/dist/src/query/query-engine.d.ts +0 -35
  74. package/dist/src/query/query-engine.d.ts.map +1 -1
  75. package/dist/src/query/query-engine.js +9 -309
  76. package/dist/src/query/query-engine.js.map +1 -1
  77. package/dist/src/query/scoring.d.ts +52 -0
  78. package/dist/src/query/scoring.d.ts.map +1 -0
  79. package/dist/src/query/scoring.js +162 -0
  80. package/dist/src/query/scoring.js.map +1 -0
  81. package/dist/src/server/mcp-server.d.ts.map +1 -1
  82. package/dist/src/server/mcp-server.js +29 -1
  83. package/dist/src/server/mcp-server.js.map +1 -1
  84. package/dist/src/snapshot/element-resolver.d.ts +50 -18
  85. package/dist/src/snapshot/element-resolver.d.ts.map +1 -1
  86. package/dist/src/snapshot/element-resolver.js +180 -101
  87. package/dist/src/snapshot/element-resolver.js.map +1 -1
  88. package/dist/src/snapshot/extractors/ax-extractor.d.ts +1 -1
  89. package/dist/src/snapshot/extractors/ax-extractor.d.ts.map +1 -1
  90. package/dist/src/snapshot/extractors/ax-extractor.js +4 -1
  91. package/dist/src/snapshot/extractors/ax-extractor.js.map +1 -1
  92. package/dist/src/snapshot/extractors/index.d.ts +1 -1
  93. package/dist/src/snapshot/extractors/index.d.ts.map +1 -1
  94. package/dist/src/snapshot/extractors/index.js +1 -1
  95. package/dist/src/snapshot/extractors/index.js.map +1 -1
  96. package/dist/src/snapshot/extractors/region-resolver.d.ts.map +1 -1
  97. package/dist/src/snapshot/extractors/region-resolver.js +8 -0
  98. package/dist/src/snapshot/extractors/region-resolver.js.map +1 -1
  99. package/dist/src/snapshot/extractors/types.d.ts +8 -0
  100. package/dist/src/snapshot/extractors/types.d.ts.map +1 -1
  101. package/dist/src/snapshot/extractors/types.js +16 -0
  102. package/dist/src/snapshot/extractors/types.js.map +1 -1
  103. package/dist/src/snapshot/frame-context.d.ts +68 -0
  104. package/dist/src/snapshot/frame-context.d.ts.map +1 -0
  105. package/dist/src/snapshot/frame-context.js +131 -0
  106. package/dist/src/snapshot/frame-context.js.map +1 -0
  107. package/dist/src/snapshot/heading-index.d.ts +28 -0
  108. package/dist/src/snapshot/heading-index.d.ts.map +1 -0
  109. package/dist/src/snapshot/heading-index.js +108 -0
  110. package/dist/src/snapshot/heading-index.js.map +1 -0
  111. package/dist/src/snapshot/index.d.ts +5 -3
  112. package/dist/src/snapshot/index.d.ts.map +1 -1
  113. package/dist/src/snapshot/index.js +3 -2
  114. package/dist/src/snapshot/index.js.map +1 -1
  115. package/dist/src/snapshot/kind-mapping.d.ts +30 -0
  116. package/dist/src/snapshot/kind-mapping.d.ts.map +1 -0
  117. package/dist/src/snapshot/kind-mapping.js +114 -0
  118. package/dist/src/snapshot/kind-mapping.js.map +1 -0
  119. package/dist/src/snapshot/node-filter.d.ts +31 -0
  120. package/dist/src/snapshot/node-filter.d.ts.map +1 -0
  121. package/dist/src/snapshot/node-filter.js +137 -0
  122. package/dist/src/snapshot/node-filter.js.map +1 -0
  123. package/dist/src/snapshot/node-synthesizer.d.ts +62 -0
  124. package/dist/src/snapshot/node-synthesizer.d.ts.map +1 -0
  125. package/dist/src/snapshot/node-synthesizer.js +185 -0
  126. package/dist/src/snapshot/node-synthesizer.js.map +1 -0
  127. package/dist/src/snapshot/snapshot-compiler.d.ts +2 -36
  128. package/dist/src/snapshot/snapshot-compiler.d.ts.map +1 -1
  129. package/dist/src/snapshot/snapshot-compiler.js +28 -520
  130. package/dist/src/snapshot/snapshot-compiler.js.map +1 -1
  131. package/dist/src/snapshot/snapshot.types.d.ts +7 -2
  132. package/dist/src/snapshot/snapshot.types.d.ts.map +1 -1
  133. package/dist/src/snapshot/snapshot.types.js +9 -0
  134. package/dist/src/snapshot/snapshot.types.js.map +1 -1
  135. package/dist/src/state/actionables-filter.d.ts +5 -0
  136. package/dist/src/state/actionables-filter.d.ts.map +1 -1
  137. package/dist/src/state/actionables-filter.js +22 -3
  138. package/dist/src/state/actionables-filter.js.map +1 -1
  139. package/dist/src/state/diff-engine.js +3 -3
  140. package/dist/src/state/diff-engine.js.map +1 -1
  141. package/dist/src/state/element-registry.d.ts.map +1 -1
  142. package/dist/src/state/element-registry.js +6 -4
  143. package/dist/src/state/element-registry.js.map +1 -1
  144. package/dist/src/state/hash-utils.d.ts +24 -0
  145. package/dist/src/state/hash-utils.d.ts.map +1 -0
  146. package/dist/src/state/hash-utils.js +41 -0
  147. package/dist/src/state/hash-utils.js.map +1 -0
  148. package/dist/src/state/layer-detector.d.ts.map +1 -1
  149. package/dist/src/state/layer-detector.js +15 -286
  150. package/dist/src/state/layer-detector.js.map +1 -1
  151. package/dist/src/state/layer-detectors/drawer-detector.d.ts +32 -0
  152. package/dist/src/state/layer-detectors/drawer-detector.d.ts.map +1 -0
  153. package/dist/src/state/layer-detectors/drawer-detector.js +96 -0
  154. package/dist/src/state/layer-detectors/drawer-detector.js.map +1 -0
  155. package/dist/src/state/layer-detectors/index.d.ts +10 -0
  156. package/dist/src/state/layer-detectors/index.d.ts.map +1 -0
  157. package/dist/src/state/layer-detectors/index.js +10 -0
  158. package/dist/src/state/layer-detectors/index.js.map +1 -0
  159. package/dist/src/state/layer-detectors/modal-detector.d.ts +30 -0
  160. package/dist/src/state/layer-detectors/modal-detector.d.ts.map +1 -0
  161. package/dist/src/state/layer-detectors/modal-detector.js +127 -0
  162. package/dist/src/state/layer-detectors/modal-detector.js.map +1 -0
  163. package/dist/src/state/layer-detectors/popover-detector.d.ts +20 -0
  164. package/dist/src/state/layer-detectors/popover-detector.d.ts.map +1 -0
  165. package/dist/src/state/layer-detectors/popover-detector.js +76 -0
  166. package/dist/src/state/layer-detectors/popover-detector.js.map +1 -0
  167. package/dist/src/state/layer-detectors/toast-detector.d.ts +24 -0
  168. package/dist/src/state/layer-detectors/toast-detector.d.ts.map +1 -0
  169. package/dist/src/state/layer-detectors/toast-detector.js +48 -0
  170. package/dist/src/state/layer-detectors/toast-detector.js.map +1 -0
  171. package/dist/src/state/region-mapping.d.ts +13 -0
  172. package/dist/src/state/region-mapping.d.ts.map +1 -0
  173. package/dist/src/state/region-mapping.js +25 -0
  174. package/dist/src/state/region-mapping.js.map +1 -0
  175. package/dist/src/state/state-manager.d.ts.map +1 -1
  176. package/dist/src/state/state-manager.js +8 -192
  177. package/dist/src/state/state-manager.js.map +1 -1
  178. package/dist/src/state/state-renderer.d.ts.map +1 -1
  179. package/dist/src/state/state-renderer.js +16 -2
  180. package/dist/src/state/state-renderer.js.map +1 -1
  181. package/dist/src/state/types.d.ts +8 -4
  182. package/dist/src/state/types.d.ts.map +1 -1
  183. package/dist/src/state/url-sanitization.d.ts +22 -0
  184. package/dist/src/state/url-sanitization.d.ts.map +1 -0
  185. package/dist/src/state/url-sanitization.js +60 -0
  186. package/dist/src/state/url-sanitization.js.map +1 -0
  187. package/dist/src/state/value-masking.d.ts +36 -0
  188. package/dist/src/state/value-masking.d.ts.map +1 -0
  189. package/dist/src/state/value-masking.js +86 -0
  190. package/dist/src/state/value-masking.js.map +1 -0
  191. package/dist/src/tools/action-context.d.ts +60 -0
  192. package/dist/src/tools/action-context.d.ts.map +1 -0
  193. package/dist/src/tools/action-context.js +78 -0
  194. package/dist/src/tools/action-context.js.map +1 -0
  195. package/dist/src/tools/action-stabilization.d.ts +48 -0
  196. package/dist/src/tools/action-stabilization.d.ts.map +1 -0
  197. package/dist/src/tools/action-stabilization.js +87 -0
  198. package/dist/src/tools/action-stabilization.js.map +1 -0
  199. package/dist/src/tools/browser-tools.d.ts +8 -146
  200. package/dist/src/tools/browser-tools.d.ts.map +1 -1
  201. package/dist/src/tools/browser-tools.js +13 -689
  202. package/dist/src/tools/browser-tools.js.map +1 -1
  203. package/dist/src/tools/canvas-tools.d.ts +32 -0
  204. package/dist/src/tools/canvas-tools.d.ts.map +1 -0
  205. package/dist/src/tools/canvas-tools.js +370 -0
  206. package/dist/src/tools/canvas-tools.js.map +1 -0
  207. package/dist/src/tools/effect-tracker.d.ts +25 -0
  208. package/dist/src/tools/effect-tracker.d.ts.map +1 -0
  209. package/dist/src/tools/effect-tracker.js +69 -0
  210. package/dist/src/tools/effect-tracker.js.map +1 -0
  211. package/dist/src/tools/execute-action.d.ts +1 -31
  212. package/dist/src/tools/execute-action.d.ts.map +1 -1
  213. package/dist/src/tools/execute-action.js +7 -276
  214. package/dist/src/tools/execute-action.js.map +1 -1
  215. package/dist/src/tools/form-tools.d.ts +4 -6
  216. package/dist/src/tools/form-tools.d.ts.map +1 -1
  217. package/dist/src/tools/form-tools.js +10 -42
  218. package/dist/src/tools/form-tools.js.map +1 -1
  219. package/dist/src/tools/index.d.ts +6 -4
  220. package/dist/src/tools/index.d.ts.map +1 -1
  221. package/dist/src/tools/index.js +21 -10
  222. package/dist/src/tools/index.js.map +1 -1
  223. package/dist/src/tools/interaction-tools.d.ts +46 -0
  224. package/dist/src/tools/interaction-tools.d.ts.map +1 -0
  225. package/dist/src/tools/interaction-tools.js +138 -0
  226. package/dist/src/tools/interaction-tools.js.map +1 -0
  227. package/dist/src/tools/navigation-detection.d.ts +31 -0
  228. package/dist/src/tools/navigation-detection.d.ts.map +1 -0
  229. package/dist/src/tools/navigation-detection.js +46 -0
  230. package/dist/src/tools/navigation-detection.js.map +1 -0
  231. package/dist/src/tools/navigation-tools.d.ts +57 -0
  232. package/dist/src/tools/navigation-tools.d.ts.map +1 -0
  233. package/dist/src/tools/navigation-tools.js +178 -0
  234. package/dist/src/tools/navigation-tools.js.map +1 -0
  235. package/dist/src/tools/observation-tools.d.ts +53 -0
  236. package/dist/src/tools/observation-tools.d.ts.map +1 -0
  237. package/dist/src/tools/observation-tools.js +247 -0
  238. package/dist/src/tools/observation-tools.js.map +1 -0
  239. package/dist/src/tools/response-builder.js +2 -2
  240. package/dist/src/tools/response-builder.js.map +1 -1
  241. package/dist/src/tools/stale-element-retry.d.ts +37 -0
  242. package/dist/src/tools/stale-element-retry.d.ts.map +1 -0
  243. package/dist/src/tools/stale-element-retry.js +68 -0
  244. package/dist/src/tools/stale-element-retry.js.map +1 -0
  245. package/dist/src/tools/state-manager-registry.d.ts +26 -0
  246. package/dist/src/tools/state-manager-registry.d.ts.map +1 -0
  247. package/dist/src/tools/state-manager-registry.js +39 -0
  248. package/dist/src/tools/state-manager-registry.js.map +1 -0
  249. package/dist/src/tools/tool-context.d.ts +53 -0
  250. package/dist/src/tools/tool-context.d.ts.map +1 -0
  251. package/dist/src/tools/tool-context.js +119 -0
  252. package/dist/src/tools/tool-context.js.map +1 -0
  253. package/dist/src/tools/tool-result.types.d.ts +16 -1
  254. package/dist/src/tools/tool-result.types.d.ts.map +1 -1
  255. package/dist/src/tools/tool-result.types.js +11 -0
  256. package/dist/src/tools/tool-result.types.js.map +1 -1
  257. package/dist/src/tools/tool-schemas.d.ts +358 -146
  258. package/dist/src/tools/tool-schemas.d.ts.map +1 -1
  259. package/dist/src/tools/tool-schemas.js +142 -19
  260. package/dist/src/tools/tool-schemas.js.map +1 -1
  261. package/dist/src/tools/viewport-tools.d.ts +36 -0
  262. package/dist/src/tools/viewport-tools.d.ts.map +1 -0
  263. package/dist/src/tools/viewport-tools.js +105 -0
  264. package/dist/src/tools/viewport-tools.js.map +1 -0
  265. package/package.json +1 -1
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Disambiguation
3
+ *
4
+ * Generate suggestions to narrow ambiguous query results.
5
+ * Analyzes matched nodes and suggests refinements.
6
+ */
7
+ import { normalizeText } from '../lib/text-utils.js';
8
+ import { normalizeLabelFilter } from './scoring.js';
9
+ /**
10
+ * Generate disambiguation suggestions when query matches multiple elements.
11
+ * Suggests refinements that would narrow down the results.
12
+ */
13
+ export function generateSuggestions(matches, request) {
14
+ const suggestions = [];
15
+ const nodes = matches.map((m) => m.node);
16
+ // Only generate suggestions if we have multiple matches
17
+ if (matches.length < 2)
18
+ return suggestions;
19
+ // 1. Suggest refining by kind if matches have different kinds
20
+ if (request.kind === undefined) {
21
+ const kindCounts = countByAttribute(nodes, (n) => n.kind);
22
+ if (kindCounts.size > 1) {
23
+ for (const [kind, count] of kindCounts) {
24
+ if (count < matches.length) {
25
+ suggestions.push({
26
+ type: 'refine_kind',
27
+ message: `Add kind: "${kind}" to narrow to ${count} result(s)`,
28
+ refinement: { kind },
29
+ expected_matches: count,
30
+ });
31
+ }
32
+ }
33
+ }
34
+ }
35
+ // 2. Suggest refining by region if matches span multiple regions
36
+ if (request.region === undefined) {
37
+ const regionCounts = countByAttribute(nodes, (n) => n.where.region);
38
+ if (regionCounts.size > 1) {
39
+ for (const [region, count] of regionCounts) {
40
+ if (count < matches.length && region !== 'unknown') {
41
+ suggestions.push({
42
+ type: 'refine_region',
43
+ message: `Add region: "${region}" to narrow to ${count} result(s)`,
44
+ refinement: { region },
45
+ expected_matches: count,
46
+ });
47
+ }
48
+ }
49
+ }
50
+ }
51
+ // 3. Suggest refining by group_id if matches have different groups
52
+ if (request.group_id === undefined) {
53
+ const groupCounts = countByAttribute(nodes, (n) => n.where.group_id);
54
+ groupCounts.delete(undefined); // Remove nodes without groups
55
+ if (groupCounts.size >= 1) {
56
+ for (const [groupId, count] of groupCounts) {
57
+ if (groupId !== undefined) {
58
+ suggestions.push({
59
+ type: 'refine_group',
60
+ message: `Add group_id: "${groupId}" to narrow to ${count} result(s)`,
61
+ refinement: { group_id: groupId },
62
+ expected_matches: count,
63
+ });
64
+ }
65
+ }
66
+ }
67
+ }
68
+ // 4. Suggest adding state filters
69
+ if (request.state === undefined) {
70
+ const enabledCount = nodes.filter((n) => n.state?.enabled).length;
71
+ if (enabledCount > 0 && enabledCount < matches.length) {
72
+ suggestions.push({
73
+ type: 'add_state',
74
+ message: `Add state: { enabled: true } to narrow to ${enabledCount} result(s)`,
75
+ refinement: { state: { enabled: true } },
76
+ expected_matches: enabledCount,
77
+ });
78
+ }
79
+ const visibleCount = nodes.filter((n) => n.state?.visible).length;
80
+ if (visibleCount > 0 && visibleCount < matches.length) {
81
+ suggestions.push({
82
+ type: 'add_state',
83
+ message: `Add state: { visible: true } to narrow to ${visibleCount} result(s)`,
84
+ refinement: { state: { visible: true } },
85
+ expected_matches: visibleCount,
86
+ });
87
+ }
88
+ }
89
+ // 5. Suggest refining label to exact match if using contains/fuzzy
90
+ if (request.label !== undefined) {
91
+ const { mode, text } = normalizeLabelFilter(request.label);
92
+ if (mode !== 'exact') {
93
+ const normalizedText = normalizeText(text.toLowerCase());
94
+ const exactCount = nodes.filter((n) => normalizeText(n.label.toLowerCase()) === normalizedText).length;
95
+ if (exactCount > 0 && exactCount < matches.length) {
96
+ suggestions.push({
97
+ type: 'refine_label',
98
+ message: `Use exact label match to narrow to ${exactCount} result(s)`,
99
+ refinement: { label: { text, mode: 'exact' } },
100
+ expected_matches: exactCount,
101
+ });
102
+ }
103
+ }
104
+ }
105
+ // Sort by expected_matches (prefer suggestions that narrow most effectively)
106
+ // and limit to top 5
107
+ return suggestions
108
+ .filter((s) => s.expected_matches > 0 && s.expected_matches < matches.length)
109
+ .sort((a, b) => a.expected_matches - b.expected_matches)
110
+ .slice(0, 5);
111
+ }
112
+ /**
113
+ * Count nodes by a given attribute.
114
+ */
115
+ export function countByAttribute(nodes, getter) {
116
+ const counts = new Map();
117
+ for (const node of nodes) {
118
+ const value = getter(node);
119
+ counts.set(value, (counts.get(value) ?? 0) + 1);
120
+ }
121
+ return counts;
122
+ }
123
+ //# sourceMappingURL=disambiguation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disambiguation.js","sourceRoot":"","sources":["../../../src/query/disambiguation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Fuzzy Match
3
+ *
4
+ * Fuzzy label matching for query engine.
5
+ * Uses token-based matching with configurable thresholds.
6
+ */
7
+ import type { ReadableNode } from '../snapshot/snapshot.types.js';
8
+ import type { FuzzyMatchOptions } from './types/query.types.js';
9
+ /**
10
+ * Filter nodes by fuzzy label matching.
11
+ * Returns matched nodes and a map of node_id -> match score for relevance calculation.
12
+ */
13
+ export declare function filterByLabelFuzzy(nodes: ReadableNode[], text: string, caseSensitive: boolean, options?: FuzzyMatchOptions): {
14
+ nodes: ReadableNode[];
15
+ scores: Map<string, number>;
16
+ };
17
+ //# sourceMappingURL=fuzzy-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy-match.d.ts","sourceRoot":"","sources":["../../../src/query/fuzzy-match.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EAAE,EACrB,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,OAAO,EACtB,OAAO,GAAE,iBAAsB,GAC9B;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CA0BxD"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Fuzzy Match
3
+ *
4
+ * Fuzzy label matching for query engine.
5
+ * Uses token-based matching with configurable thresholds.
6
+ */
7
+ import { normalizeText, tokenizeForMatching, fuzzyTokensMatch } from '../lib/text-utils.js';
8
+ /**
9
+ * Filter nodes by fuzzy label matching.
10
+ * Returns matched nodes and a map of node_id -> match score for relevance calculation.
11
+ */
12
+ export function filterByLabelFuzzy(nodes, text, caseSensitive, options = {}) {
13
+ const scores = new Map();
14
+ const normalizedQuery = normalizeText(caseSensitive ? text : text.toLowerCase());
15
+ const queryTokens = tokenizeForMatching(normalizedQuery, 10, 2);
16
+ if (queryTokens.length === 0) {
17
+ return { nodes: [], scores };
18
+ }
19
+ const matched = nodes.filter((n) => {
20
+ const normalizedLabel = normalizeText(caseSensitive ? n.label : n.label.toLowerCase());
21
+ const labelTokens = tokenizeForMatching(normalizedLabel, 10, 2);
22
+ const result = fuzzyTokensMatch(labelTokens, queryTokens, {
23
+ minTokenOverlap: options.minTokenOverlap ?? 0.5,
24
+ prefixMatch: options.prefixMatch ?? true,
25
+ minSimilarity: options.minSimilarity ?? 0.8,
26
+ });
27
+ if (result.isMatch) {
28
+ scores.set(n.node_id, result.score);
29
+ }
30
+ return result.isMatch;
31
+ });
32
+ return { nodes: matched, scores };
33
+ }
34
+ //# sourceMappingURL=fuzzy-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy-match.js","sourceRoot":"","sources":["../../../src/query/fuzzy-match.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE5F;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAqB,EACrB,IAAY,EACZ,aAAsB,EACtB,UAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACjF,MAAM,WAAW,GAAG,mBAAmB,CAAC,eAAe,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAEhE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QACvF,MAAM,WAAW,GAAG,mBAAmB,CAAC,eAAe,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAEhE,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE;YACxD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,GAAG;YAC/C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;YACxC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,GAAG;SAC5C,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC"}
@@ -6,4 +6,7 @@
6
6
  export * from './types/query.types.js';
7
7
  export { QueryEngine } from './query-engine.js';
8
8
  export type { QueryEngineOptions } from './query-engine.js';
9
+ export { scoreMatch, calculateMaxPossibleScore, normalizeLabelFilter, SCORING_WEIGHTS, } from './scoring.js';
10
+ export { generateSuggestions, countByAttribute } from './disambiguation.js';
11
+ export { filterByLabelFuzzy } from './fuzzy-match.js';
9
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,wBAAwB,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,wBAAwB,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EACL,UAAU,EACV,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -7,4 +7,10 @@
7
7
  export * from './types/query.types.js';
8
8
  // Query Engine
9
9
  export { QueryEngine } from './query-engine.js';
10
+ // Scoring
11
+ export { scoreMatch, calculateMaxPossibleScore, normalizeLabelFilter, SCORING_WEIGHTS, } from './scoring.js';
12
+ // Disambiguation
13
+ export { generateSuggestions, countByAttribute } from './disambiguation.js';
14
+ // Fuzzy matching
15
+ export { filterByLabelFuzzy } from './fuzzy-match.js';
10
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,wBAAwB,CAAC;AAEvC,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,wBAAwB,CAAC;AAEvC,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,UAAU;AACV,OAAO,EACL,UAAU,EACV,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,iBAAiB;AACjB,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5E,iBAAiB;AACjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -72,40 +72,5 @@ export declare class QueryEngine {
72
72
  * Check if a node matches all state constraints
73
73
  */
74
74
  private matchState;
75
- /**
76
- * Normalize a label filter to its constituent parts
77
- */
78
- private normalizeLabelFilter;
79
- /**
80
- * Filter nodes by fuzzy label matching.
81
- * Returns matched nodes and a map of node_id -> match score for relevance calculation.
82
- */
83
- private filterByLabelFuzzy;
84
- /**
85
- * Calculate relevance score and match reasons for a node.
86
- *
87
- * @param node - The node to score
88
- * @param request - The original query request
89
- * @param labelMatchScore - Pre-computed label match score (for fuzzy matching)
90
- * @returns Relevance score (0-1) and list of reasons
91
- */
92
- private scoreMatch;
93
- /**
94
- * Calculate the maximum possible score given the request filters.
95
- */
96
- private calculateMaxPossibleScore;
97
- /**
98
- * Truncate a label for display in reasons.
99
- */
100
- private truncateLabel;
101
- /**
102
- * Generate disambiguation suggestions when query matches multiple elements.
103
- * Suggests refinements that would narrow down the results.
104
- */
105
- private generateSuggestions;
106
- /**
107
- * Count nodes by a given attribute.
108
- */
109
- private countByAttribute;
110
75
  }
111
76
  //# sourceMappingURL=query-engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-engine.d.ts","sourceRoot":"","sources":["../../../src/query/query-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAGb,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAQrB,MAAM,wBAAwB,CAAC;AAGhC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;CAOvB;AAoBD;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC;;OAEG;gBACS,QAAQ,EAAE,YAAY,EAAE,OAAO,GAAE,kBAAuB;IAMpE;;OAEG;IACH,IAAI,CAAC,OAAO,GAAE,mBAAwB,GAAG,oBAAoB;IAwF7D;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIjD;;OAEG;IACH,eAAe,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAO9D;;OAEG;IACH,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IAS3C;;OAEG;IACH,OAAO,CAAC,YAAY;IAKpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,OAAO,CAAC,UAAU;IAgClB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAqC1B;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAqGlB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAqBjC;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAgH3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAWzB"}
1
+ {"version":3,"file":"query-engine.d.ts","sourceRoot":"","sources":["../../../src/query/query-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAGb,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAMrB,MAAM,wBAAwB,CAAC;AAMhC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;CAOvB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC;;OAEG;gBACS,QAAQ,EAAE,YAAY,EAAE,OAAO,GAAE,kBAAuB;IAMpE;;OAEG;IACH,IAAI,CAAC,OAAO,GAAE,mBAAwB,GAAG,oBAAoB;IAwF7D;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIjD;;OAEG;IACH,eAAe,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAO9D;;OAEG;IACH,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IAS3C;;OAEG;IACH,OAAO,CAAC,YAAY;IAKpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,OAAO,CAAC,UAAU;CA+BnB"}
@@ -9,24 +9,10 @@
9
9
  * - Relevance scoring
10
10
  * - Disambiguation suggestions
11
11
  */
12
- import { normalizeText, tokenizeForMatching, fuzzyTokensMatch } from '../lib/text-utils.js';
13
- /**
14
- * Scoring weights for relevance calculation.
15
- * Values represent the maximum contribution each signal can add to the score.
16
- */
17
- const SCORING_WEIGHTS = {
18
- labelMatch: {
19
- exact: 0.4,
20
- contains: 0.3,
21
- fuzzy: 0.25, // Base, multiplied by fuzzy match quality
22
- },
23
- kindMatch: 0.15,
24
- regionMatch: 0.12,
25
- stateMatch: 0.03, // Per matching state property (max ~0.27 for 9 props)
26
- groupMatch: 0.08,
27
- headingMatch: 0.08,
28
- visibility: 0.02,
29
- };
12
+ import { normalizeText } from '../lib/text-utils.js';
13
+ import { scoreMatch, normalizeLabelFilter } from './scoring.js';
14
+ import { generateSuggestions } from './disambiguation.js';
15
+ import { filterByLabelFuzzy } from './fuzzy-match.js';
30
16
  /**
31
17
  * Query engine for BaseSnapshot data
32
18
  */
@@ -57,9 +43,9 @@ export class QueryEngine {
57
43
  }
58
44
  // Filter by label (with fuzzy support)
59
45
  if (request.label !== undefined) {
60
- const { mode, text, caseSensitive, fuzzyOptions } = this.normalizeLabelFilter(request.label);
46
+ const { mode, text, caseSensitive, fuzzyOptions } = normalizeLabelFilter(request.label);
61
47
  if (mode === 'fuzzy') {
62
- const result = this.filterByLabelFuzzy(candidates, text, caseSensitive, fuzzyOptions);
48
+ const result = filterByLabelFuzzy(candidates, text, caseSensitive, fuzzyOptions);
63
49
  candidates = result.nodes;
64
50
  labelScores = result.scores;
65
51
  }
@@ -85,7 +71,7 @@ export class QueryEngine {
85
71
  }
86
72
  // Score all candidates
87
73
  const scoredMatches = candidates.map((node) => {
88
- const { relevance, reasons } = this.scoreMatch(node, request, labelScores?.get(node.node_id));
74
+ const { relevance, reasons } = scoreMatch(node, request, labelScores?.get(node.node_id));
89
75
  return { node, relevance, match_reasons: reasons };
90
76
  });
91
77
  // Apply min_score filter
@@ -104,7 +90,7 @@ export class QueryEngine {
104
90
  // Generate suggestions if requested and results are ambiguous
105
91
  let suggestions;
106
92
  if (request.include_suggestions && totalMatched > 1) {
107
- suggestions = this.generateSuggestions(limitedMatches, request);
93
+ suggestions = generateSuggestions(limitedMatches, request);
108
94
  if (suggestions.length === 0)
109
95
  suggestions = undefined;
110
96
  }
@@ -154,7 +140,7 @@ export class QueryEngine {
154
140
  * Filter nodes by label text
155
141
  */
156
142
  filterByLabel(nodes, filter) {
157
- const { text, mode, caseSensitive } = this.normalizeLabelFilter(filter);
143
+ const { text, mode, caseSensitive } = normalizeLabelFilter(filter);
158
144
  if (!text) {
159
145
  return nodes;
160
146
  }
@@ -219,291 +205,5 @@ export class QueryEngine {
219
205
  }
220
206
  return true;
221
207
  }
222
- /**
223
- * Normalize a label filter to its constituent parts
224
- */
225
- normalizeLabelFilter(filter) {
226
- if (typeof filter === 'string') {
227
- return { text: filter, mode: 'contains', caseSensitive: false };
228
- }
229
- return {
230
- text: filter.text,
231
- mode: filter.mode ?? 'contains',
232
- caseSensitive: filter.caseSensitive ?? false,
233
- fuzzyOptions: filter.fuzzyOptions,
234
- };
235
- }
236
- // ===========================================================================
237
- // Fuzzy Matching
238
- // ===========================================================================
239
- /**
240
- * Filter nodes by fuzzy label matching.
241
- * Returns matched nodes and a map of node_id -> match score for relevance calculation.
242
- */
243
- filterByLabelFuzzy(nodes, text, caseSensitive, options = {}) {
244
- const scores = new Map();
245
- const normalizedQuery = normalizeText(caseSensitive ? text : text.toLowerCase());
246
- const queryTokens = tokenizeForMatching(normalizedQuery, 10, 2);
247
- if (queryTokens.length === 0) {
248
- return { nodes: [], scores };
249
- }
250
- const matched = nodes.filter((n) => {
251
- const normalizedLabel = normalizeText(caseSensitive ? n.label : n.label.toLowerCase());
252
- const labelTokens = tokenizeForMatching(normalizedLabel, 10, 2);
253
- const result = fuzzyTokensMatch(labelTokens, queryTokens, {
254
- minTokenOverlap: options.minTokenOverlap ?? 0.5,
255
- prefixMatch: options.prefixMatch ?? true,
256
- minSimilarity: options.minSimilarity ?? 0.8,
257
- });
258
- if (result.isMatch) {
259
- scores.set(n.node_id, result.score);
260
- }
261
- return result.isMatch;
262
- });
263
- return { nodes: matched, scores };
264
- }
265
- // ===========================================================================
266
- // Relevance Scoring
267
- // ===========================================================================
268
- /**
269
- * Calculate relevance score and match reasons for a node.
270
- *
271
- * @param node - The node to score
272
- * @param request - The original query request
273
- * @param labelMatchScore - Pre-computed label match score (for fuzzy matching)
274
- * @returns Relevance score (0-1) and list of reasons
275
- */
276
- scoreMatch(node, request, labelMatchScore) {
277
- let relevance = 0;
278
- const reasons = [];
279
- // Label scoring
280
- if (request.label !== undefined) {
281
- const { mode } = this.normalizeLabelFilter(request.label);
282
- let labelContribution;
283
- if (mode === 'fuzzy' && labelMatchScore !== undefined) {
284
- // Fuzzy: base weight * match quality
285
- labelContribution = SCORING_WEIGHTS.labelMatch.fuzzy * labelMatchScore;
286
- }
287
- else if (mode === 'exact') {
288
- labelContribution = SCORING_WEIGHTS.labelMatch.exact;
289
- }
290
- else {
291
- labelContribution = SCORING_WEIGHTS.labelMatch.contains;
292
- }
293
- relevance += labelContribution;
294
- reasons.push({
295
- type: 'label',
296
- description: `Label "${this.truncateLabel(node.label)}" matches query`,
297
- score_contribution: labelContribution,
298
- });
299
- }
300
- // Kind scoring
301
- if (request.kind !== undefined) {
302
- relevance += SCORING_WEIGHTS.kindMatch;
303
- reasons.push({
304
- type: 'kind',
305
- description: `Kind "${node.kind}" matches filter`,
306
- score_contribution: SCORING_WEIGHTS.kindMatch,
307
- });
308
- }
309
- // Region scoring
310
- if (request.region !== undefined) {
311
- relevance += SCORING_WEIGHTS.regionMatch;
312
- reasons.push({
313
- type: 'region',
314
- description: `Region "${node.where.region}" matches filter`,
315
- score_contribution: SCORING_WEIGHTS.regionMatch,
316
- });
317
- }
318
- // State scoring (per matched property)
319
- if (request.state !== undefined && node.state) {
320
- const matchedStates = Object.keys(request.state).filter((k) => request.state[k] !== undefined &&
321
- request.state[k] === node.state[k]);
322
- const stateContribution = matchedStates.length * SCORING_WEIGHTS.stateMatch;
323
- if (stateContribution > 0) {
324
- relevance += stateContribution;
325
- reasons.push({
326
- type: 'state',
327
- description: `States match: ${matchedStates.join(', ')}`,
328
- score_contribution: stateContribution,
329
- });
330
- }
331
- }
332
- // Group scoring
333
- if (request.group_id !== undefined) {
334
- relevance += SCORING_WEIGHTS.groupMatch;
335
- reasons.push({
336
- type: 'group',
337
- description: `Group "${node.where.group_id}" matches`,
338
- score_contribution: SCORING_WEIGHTS.groupMatch,
339
- });
340
- }
341
- // Heading context scoring
342
- if (request.heading_context !== undefined) {
343
- relevance += SCORING_WEIGHTS.headingMatch;
344
- reasons.push({
345
- type: 'heading',
346
- description: `Heading context "${node.where.heading_context}" matches`,
347
- score_contribution: SCORING_WEIGHTS.headingMatch,
348
- });
349
- }
350
- // Visibility bonus (always applied if node is visible)
351
- if (node.state?.visible) {
352
- relevance += SCORING_WEIGHTS.visibility;
353
- }
354
- // Normalize to 0-1 range based on what was actually queried
355
- // This gives higher scores when fewer filters are used but all match
356
- const maxPossible = this.calculateMaxPossibleScore(request);
357
- const normalizedRelevance = maxPossible > 0 ? Math.min(1, relevance / maxPossible) : 0;
358
- return { relevance: normalizedRelevance, reasons };
359
- }
360
- /**
361
- * Calculate the maximum possible score given the request filters.
362
- */
363
- calculateMaxPossibleScore(request) {
364
- let max = SCORING_WEIGHTS.visibility; // Always possible
365
- if (request.label !== undefined) {
366
- const { mode } = this.normalizeLabelFilter(request.label);
367
- max += SCORING_WEIGHTS.labelMatch[mode];
368
- }
369
- if (request.kind !== undefined)
370
- max += SCORING_WEIGHTS.kindMatch;
371
- if (request.region !== undefined)
372
- max += SCORING_WEIGHTS.regionMatch;
373
- if (request.state !== undefined) {
374
- const stateCount = Object.keys(request.state).filter((k) => request.state[k] !== undefined).length;
375
- max += stateCount * SCORING_WEIGHTS.stateMatch;
376
- }
377
- if (request.group_id !== undefined)
378
- max += SCORING_WEIGHTS.groupMatch;
379
- if (request.heading_context !== undefined)
380
- max += SCORING_WEIGHTS.headingMatch;
381
- return max;
382
- }
383
- /**
384
- * Truncate a label for display in reasons.
385
- */
386
- truncateLabel(label, maxLength = 30) {
387
- if (label.length <= maxLength)
388
- return label;
389
- return label.slice(0, maxLength - 1) + '…';
390
- }
391
- // ===========================================================================
392
- // Disambiguation Suggestions
393
- // ===========================================================================
394
- /**
395
- * Generate disambiguation suggestions when query matches multiple elements.
396
- * Suggests refinements that would narrow down the results.
397
- */
398
- generateSuggestions(matches, request) {
399
- const suggestions = [];
400
- const nodes = matches.map((m) => m.node);
401
- // Only generate suggestions if we have multiple matches
402
- if (matches.length < 2)
403
- return suggestions;
404
- // 1. Suggest refining by kind if matches have different kinds
405
- if (request.kind === undefined) {
406
- const kindCounts = this.countByAttribute(nodes, (n) => n.kind);
407
- if (kindCounts.size > 1) {
408
- for (const [kind, count] of kindCounts) {
409
- if (count < matches.length) {
410
- suggestions.push({
411
- type: 'refine_kind',
412
- message: `Add kind: "${kind}" to narrow to ${count} result(s)`,
413
- refinement: { kind },
414
- expected_matches: count,
415
- });
416
- }
417
- }
418
- }
419
- }
420
- // 2. Suggest refining by region if matches span multiple regions
421
- if (request.region === undefined) {
422
- const regionCounts = this.countByAttribute(nodes, (n) => n.where.region);
423
- if (regionCounts.size > 1) {
424
- for (const [region, count] of regionCounts) {
425
- if (count < matches.length && region !== 'unknown') {
426
- suggestions.push({
427
- type: 'refine_region',
428
- message: `Add region: "${region}" to narrow to ${count} result(s)`,
429
- refinement: { region },
430
- expected_matches: count,
431
- });
432
- }
433
- }
434
- }
435
- }
436
- // 3. Suggest refining by group_id if matches have different groups
437
- if (request.group_id === undefined) {
438
- const groupCounts = this.countByAttribute(nodes, (n) => n.where.group_id);
439
- groupCounts.delete(undefined); // Remove nodes without groups
440
- if (groupCounts.size >= 1) {
441
- for (const [groupId, count] of groupCounts) {
442
- if (groupId !== undefined) {
443
- suggestions.push({
444
- type: 'refine_group',
445
- message: `Add group_id: "${groupId}" to narrow to ${count} result(s)`,
446
- refinement: { group_id: groupId },
447
- expected_matches: count,
448
- });
449
- }
450
- }
451
- }
452
- }
453
- // 4. Suggest adding state filters
454
- if (request.state === undefined) {
455
- const enabledCount = nodes.filter((n) => n.state?.enabled).length;
456
- if (enabledCount > 0 && enabledCount < matches.length) {
457
- suggestions.push({
458
- type: 'add_state',
459
- message: `Add state: { enabled: true } to narrow to ${enabledCount} result(s)`,
460
- refinement: { state: { enabled: true } },
461
- expected_matches: enabledCount,
462
- });
463
- }
464
- const visibleCount = nodes.filter((n) => n.state?.visible).length;
465
- if (visibleCount > 0 && visibleCount < matches.length) {
466
- suggestions.push({
467
- type: 'add_state',
468
- message: `Add state: { visible: true } to narrow to ${visibleCount} result(s)`,
469
- refinement: { state: { visible: true } },
470
- expected_matches: visibleCount,
471
- });
472
- }
473
- }
474
- // 5. Suggest refining label to exact match if using contains/fuzzy
475
- if (request.label !== undefined) {
476
- const { mode, text } = this.normalizeLabelFilter(request.label);
477
- if (mode !== 'exact') {
478
- const normalizedText = normalizeText(text.toLowerCase());
479
- const exactCount = nodes.filter((n) => normalizeText(n.label.toLowerCase()) === normalizedText).length;
480
- if (exactCount > 0 && exactCount < matches.length) {
481
- suggestions.push({
482
- type: 'refine_label',
483
- message: `Use exact label match to narrow to ${exactCount} result(s)`,
484
- refinement: { label: { text, mode: 'exact' } },
485
- expected_matches: exactCount,
486
- });
487
- }
488
- }
489
- }
490
- // Sort by expected_matches (prefer suggestions that narrow most effectively)
491
- // and limit to top 5
492
- return suggestions
493
- .filter((s) => s.expected_matches > 0 && s.expected_matches < matches.length)
494
- .sort((a, b) => a.expected_matches - b.expected_matches)
495
- .slice(0, 5);
496
- }
497
- /**
498
- * Count nodes by a given attribute.
499
- */
500
- countByAttribute(nodes, getter) {
501
- const counts = new Map();
502
- for (const node of nodes) {
503
- const value = getter(node);
504
- counts.set(value, (counts.get(value) ?? 0) + 1);
505
- }
506
- return counts;
507
- }
508
208
  }
509
209
  //# sourceMappingURL=query-engine.js.map