@mercuryo-ai/agentbrowse 0.2.50

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 (301) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +335 -0
  3. package/dist/assistive-runtime.d.ts +110 -0
  4. package/dist/assistive-runtime.d.ts.map +1 -0
  5. package/dist/assistive-runtime.js +79 -0
  6. package/dist/assistive-runtime.test-support.d.ts +7 -0
  7. package/dist/assistive-runtime.test-support.d.ts.map +1 -0
  8. package/dist/assistive-runtime.test-support.js +106 -0
  9. package/dist/assistive-stagehand.d.ts +12 -0
  10. package/dist/assistive-stagehand.d.ts.map +1 -0
  11. package/dist/assistive-stagehand.js +10 -0
  12. package/dist/browser-session-state.d.ts +95 -0
  13. package/dist/browser-session-state.d.ts.map +1 -0
  14. package/dist/browser-session-state.js +279 -0
  15. package/dist/client-bindings.d.ts +10 -0
  16. package/dist/client-bindings.d.ts.map +1 -0
  17. package/dist/client-bindings.js +18 -0
  18. package/dist/client.d.ts +49 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +63 -0
  21. package/dist/command-api-tracing.d.ts +20 -0
  22. package/dist/command-api-tracing.d.ts.map +1 -0
  23. package/dist/command-api-tracing.js +149 -0
  24. package/dist/command-name.d.ts +3 -0
  25. package/dist/command-name.d.ts.map +1 -0
  26. package/dist/command-name.js +11 -0
  27. package/dist/commands/act.d.ts +43 -0
  28. package/dist/commands/act.d.ts.map +1 -0
  29. package/dist/commands/act.js +1107 -0
  30. package/dist/commands/action-acceptance.d.ts +93 -0
  31. package/dist/commands/action-acceptance.d.ts.map +1 -0
  32. package/dist/commands/action-acceptance.js +1938 -0
  33. package/dist/commands/action-artifacts.d.ts +33 -0
  34. package/dist/commands/action-artifacts.d.ts.map +1 -0
  35. package/dist/commands/action-artifacts.js +104 -0
  36. package/dist/commands/action-execution-guards.d.ts +5 -0
  37. package/dist/commands/action-execution-guards.d.ts.map +1 -0
  38. package/dist/commands/action-execution-guards.js +3 -0
  39. package/dist/commands/action-executor-helpers.d.ts +21 -0
  40. package/dist/commands/action-executor-helpers.d.ts.map +1 -0
  41. package/dist/commands/action-executor-helpers.js +265 -0
  42. package/dist/commands/action-executor.d.ts +14 -0
  43. package/dist/commands/action-executor.d.ts.map +1 -0
  44. package/dist/commands/action-executor.js +46 -0
  45. package/dist/commands/action-fallbacks.d.ts +6 -0
  46. package/dist/commands/action-fallbacks.d.ts.map +1 -0
  47. package/dist/commands/action-fallbacks.js +43 -0
  48. package/dist/commands/action-result-resolution.d.ts +17 -0
  49. package/dist/commands/action-result-resolution.d.ts.map +1 -0
  50. package/dist/commands/action-result-resolution.js +132 -0
  51. package/dist/commands/action-value-projection.d.ts +32 -0
  52. package/dist/commands/action-value-projection.d.ts.map +1 -0
  53. package/dist/commands/action-value-projection.js +151 -0
  54. package/dist/commands/attach.d.ts +41 -0
  55. package/dist/commands/attach.d.ts.map +1 -0
  56. package/dist/commands/attach.js +103 -0
  57. package/dist/commands/browse-actions.d.ts +4 -0
  58. package/dist/commands/browse-actions.d.ts.map +1 -0
  59. package/dist/commands/browse-actions.js +4 -0
  60. package/dist/commands/browser-status.d.ts +57 -0
  61. package/dist/commands/browser-status.d.ts.map +1 -0
  62. package/dist/commands/browser-status.js +243 -0
  63. package/dist/commands/click-action-executor.d.ts +12 -0
  64. package/dist/commands/click-action-executor.d.ts.map +1 -0
  65. package/dist/commands/click-action-executor.js +111 -0
  66. package/dist/commands/click-activation-policy.d.ts +5 -0
  67. package/dist/commands/click-activation-policy.d.ts.map +1 -0
  68. package/dist/commands/click-activation-policy.js +13 -0
  69. package/dist/commands/close.d.ts +26 -0
  70. package/dist/commands/close.d.ts.map +1 -0
  71. package/dist/commands/close.js +124 -0
  72. package/dist/commands/datepicker-action-executor.d.ts +12 -0
  73. package/dist/commands/datepicker-action-executor.d.ts.map +1 -0
  74. package/dist/commands/datepicker-action-executor.js +218 -0
  75. package/dist/commands/descriptor-validation.d.ts +27 -0
  76. package/dist/commands/descriptor-validation.d.ts.map +1 -0
  77. package/dist/commands/descriptor-validation.js +192 -0
  78. package/dist/commands/extract-scope-resolution.d.ts +20 -0
  79. package/dist/commands/extract-scope-resolution.d.ts.map +1 -0
  80. package/dist/commands/extract-scope-resolution.js +109 -0
  81. package/dist/commands/extract-scoped-dialog-text.d.ts +3 -0
  82. package/dist/commands/extract-scoped-dialog-text.d.ts.map +1 -0
  83. package/dist/commands/extract-scoped-dialog-text.js +210 -0
  84. package/dist/commands/extract-snapshot-sanitizer.d.ts +5 -0
  85. package/dist/commands/extract-snapshot-sanitizer.d.ts.map +1 -0
  86. package/dist/commands/extract-snapshot-sanitizer.js +98 -0
  87. package/dist/commands/extract-stagehand-executor.d.ts +17 -0
  88. package/dist/commands/extract-stagehand-executor.d.ts.map +1 -0
  89. package/dist/commands/extract-stagehand-executor.js +112 -0
  90. package/dist/commands/extract.d.ts +57 -0
  91. package/dist/commands/extract.d.ts.map +1 -0
  92. package/dist/commands/extract.js +668 -0
  93. package/dist/commands/interaction-kernel.d.ts +46 -0
  94. package/dist/commands/interaction-kernel.d.ts.map +1 -0
  95. package/dist/commands/interaction-kernel.js +215 -0
  96. package/dist/commands/launch.d.ts +41 -0
  97. package/dist/commands/launch.d.ts.map +1 -0
  98. package/dist/commands/launch.js +182 -0
  99. package/dist/commands/navigate.d.ts +31 -0
  100. package/dist/commands/navigate.d.ts.map +1 -0
  101. package/dist/commands/navigate.js +202 -0
  102. package/dist/commands/observe-accessibility.d.ts +22 -0
  103. package/dist/commands/observe-accessibility.d.ts.map +1 -0
  104. package/dist/commands/observe-accessibility.js +566 -0
  105. package/dist/commands/observe-display-label.d.ts +4 -0
  106. package/dist/commands/observe-display-label.d.ts.map +1 -0
  107. package/dist/commands/observe-display-label.js +26 -0
  108. package/dist/commands/observe-dom-label-contract.d.ts +2 -0
  109. package/dist/commands/observe-dom-label-contract.d.ts.map +1 -0
  110. package/dist/commands/observe-dom-label-contract.js +564 -0
  111. package/dist/commands/observe-fallback-semantics.d.ts +6 -0
  112. package/dist/commands/observe-fallback-semantics.d.ts.map +1 -0
  113. package/dist/commands/observe-fallback-semantics.js +86 -0
  114. package/dist/commands/observe-inventory.d.ts +149 -0
  115. package/dist/commands/observe-inventory.d.ts.map +1 -0
  116. package/dist/commands/observe-inventory.js +3545 -0
  117. package/dist/commands/observe-label-policy.d.ts +8 -0
  118. package/dist/commands/observe-label-policy.d.ts.map +1 -0
  119. package/dist/commands/observe-label-policy.js +21 -0
  120. package/dist/commands/observe-page-state.d.ts +11 -0
  121. package/dist/commands/observe-page-state.d.ts.map +1 -0
  122. package/dist/commands/observe-page-state.js +89 -0
  123. package/dist/commands/observe-persistence.d.ts +15 -0
  124. package/dist/commands/observe-persistence.d.ts.map +1 -0
  125. package/dist/commands/observe-persistence.js +238 -0
  126. package/dist/commands/observe-projection.d.ts +119 -0
  127. package/dist/commands/observe-projection.d.ts.map +1 -0
  128. package/dist/commands/observe-projection.js +726 -0
  129. package/dist/commands/observe-protected.d.ts +6 -0
  130. package/dist/commands/observe-protected.d.ts.map +1 -0
  131. package/dist/commands/observe-protected.js +31 -0
  132. package/dist/commands/observe-semantics.d.ts +10 -0
  133. package/dist/commands/observe-semantics.d.ts.map +1 -0
  134. package/dist/commands/observe-semantics.js +535 -0
  135. package/dist/commands/observe-signals.d.ts +48 -0
  136. package/dist/commands/observe-signals.d.ts.map +1 -0
  137. package/dist/commands/observe-signals.js +461 -0
  138. package/dist/commands/observe-stagehand.d.ts +49 -0
  139. package/dist/commands/observe-stagehand.d.ts.map +1 -0
  140. package/dist/commands/observe-stagehand.js +94 -0
  141. package/dist/commands/observe-surfaces.d.ts +11 -0
  142. package/dist/commands/observe-surfaces.d.ts.map +1 -0
  143. package/dist/commands/observe-surfaces.js +290 -0
  144. package/dist/commands/observe.d.ts +113 -0
  145. package/dist/commands/observe.d.ts.map +1 -0
  146. package/dist/commands/observe.js +556 -0
  147. package/dist/commands/screenshot.d.ts +37 -0
  148. package/dist/commands/screenshot.d.ts.map +1 -0
  149. package/dist/commands/screenshot.js +269 -0
  150. package/dist/commands/select-action-executor.d.ts +10 -0
  151. package/dist/commands/select-action-executor.d.ts.map +1 -0
  152. package/dist/commands/select-action-executor.js +156 -0
  153. package/dist/commands/semantic-observe-lexical.d.ts +31 -0
  154. package/dist/commands/semantic-observe-lexical.d.ts.map +1 -0
  155. package/dist/commands/semantic-observe-lexical.js +186 -0
  156. package/dist/commands/semantic-observe.d.ts +37 -0
  157. package/dist/commands/semantic-observe.d.ts.map +1 -0
  158. package/dist/commands/semantic-observe.js +1339 -0
  159. package/dist/commands/structured-grid-action-executor.d.ts +3 -0
  160. package/dist/commands/structured-grid-action-executor.d.ts.map +1 -0
  161. package/dist/commands/structured-grid-action-executor.js +4 -0
  162. package/dist/commands/target-resolution.d.ts +4 -0
  163. package/dist/commands/target-resolution.d.ts.map +1 -0
  164. package/dist/commands/target-resolution.js +33 -0
  165. package/dist/commands/text-input-action-executor.d.ts +5 -0
  166. package/dist/commands/text-input-action-executor.d.ts.map +1 -0
  167. package/dist/commands/text-input-action-executor.js +116 -0
  168. package/dist/commands/user-actionable.d.ts +4 -0
  169. package/dist/commands/user-actionable.d.ts.map +1 -0
  170. package/dist/commands/user-actionable.js +216 -0
  171. package/dist/control-semantics.d.ts +30 -0
  172. package/dist/control-semantics.d.ts.map +1 -0
  173. package/dist/control-semantics.js +419 -0
  174. package/dist/diagnostics.d.ts +132 -0
  175. package/dist/diagnostics.d.ts.map +1 -0
  176. package/dist/diagnostics.js +120 -0
  177. package/dist/index.d.ts +5 -0
  178. package/dist/index.d.ts.map +1 -0
  179. package/dist/index.js +350 -0
  180. package/dist/library.d.ts +17 -0
  181. package/dist/library.d.ts.map +1 -0
  182. package/dist/library.js +14 -0
  183. package/dist/output.d.ts +32 -0
  184. package/dist/output.d.ts.map +1 -0
  185. package/dist/output.js +33 -0
  186. package/dist/owned-browser.d.ts +12 -0
  187. package/dist/owned-browser.d.ts.map +1 -0
  188. package/dist/owned-browser.js +69 -0
  189. package/dist/owned-process.d.ts +19 -0
  190. package/dist/owned-process.d.ts.map +1 -0
  191. package/dist/owned-process.js +145 -0
  192. package/dist/playwright-runtime.d.ts +43 -0
  193. package/dist/playwright-runtime.d.ts.map +1 -0
  194. package/dist/playwright-runtime.js +339 -0
  195. package/dist/protected-fill-browser.d.ts +22 -0
  196. package/dist/protected-fill-browser.d.ts.map +1 -0
  197. package/dist/protected-fill-browser.js +52 -0
  198. package/dist/protected-fill.d.ts +82 -0
  199. package/dist/protected-fill.d.ts.map +1 -0
  200. package/dist/protected-fill.js +20 -0
  201. package/dist/runtime-metrics.d.ts +27 -0
  202. package/dist/runtime-metrics.d.ts.map +1 -0
  203. package/dist/runtime-metrics.js +66 -0
  204. package/dist/runtime-page-state.d.ts +11 -0
  205. package/dist/runtime-page-state.d.ts.map +1 -0
  206. package/dist/runtime-page-state.js +62 -0
  207. package/dist/runtime-protected-state.d.ts +14 -0
  208. package/dist/runtime-protected-state.d.ts.map +1 -0
  209. package/dist/runtime-protected-state.js +148 -0
  210. package/dist/runtime-resolution.d.ts +9 -0
  211. package/dist/runtime-resolution.d.ts.map +1 -0
  212. package/dist/runtime-resolution.js +19 -0
  213. package/dist/runtime-state.d.ts +251 -0
  214. package/dist/runtime-state.d.ts.map +1 -0
  215. package/dist/runtime-state.js +599 -0
  216. package/dist/secrets/catalog-applicability.d.ts +5 -0
  217. package/dist/secrets/catalog-applicability.d.ts.map +1 -0
  218. package/dist/secrets/catalog-applicability.js +59 -0
  219. package/dist/secrets/field-policy.d.ts +3 -0
  220. package/dist/secrets/field-policy.d.ts.map +1 -0
  221. package/dist/secrets/field-policy.js +3 -0
  222. package/dist/secrets/fill-ordering.d.ts +10 -0
  223. package/dist/secrets/fill-ordering.d.ts.map +1 -0
  224. package/dist/secrets/fill-ordering.js +41 -0
  225. package/dist/secrets/form-matcher.d.ts +60 -0
  226. package/dist/secrets/form-matcher.d.ts.map +1 -0
  227. package/dist/secrets/form-matcher.js +948 -0
  228. package/dist/secrets/protected-artifact-guard.d.ts +39 -0
  229. package/dist/secrets/protected-artifact-guard.d.ts.map +1 -0
  230. package/dist/secrets/protected-artifact-guard.js +44 -0
  231. package/dist/secrets/protected-bindings.d.ts +14 -0
  232. package/dist/secrets/protected-bindings.d.ts.map +1 -0
  233. package/dist/secrets/protected-bindings.js +29 -0
  234. package/dist/secrets/protected-exact-value-redaction.d.ts +14 -0
  235. package/dist/secrets/protected-exact-value-redaction.d.ts.map +1 -0
  236. package/dist/secrets/protected-exact-value-redaction.js +360 -0
  237. package/dist/secrets/protected-field-semantics.d.ts +9 -0
  238. package/dist/secrets/protected-field-semantics.d.ts.map +1 -0
  239. package/dist/secrets/protected-field-semantics.js +154 -0
  240. package/dist/secrets/protected-field-values.d.ts +15 -0
  241. package/dist/secrets/protected-field-values.d.ts.map +1 -0
  242. package/dist/secrets/protected-field-values.js +131 -0
  243. package/dist/secrets/protected-fill.d.ts +47 -0
  244. package/dist/secrets/protected-fill.d.ts.map +1 -0
  245. package/dist/secrets/protected-fill.js +446 -0
  246. package/dist/secrets/protected-value-adapters.d.ts +4 -0
  247. package/dist/secrets/protected-value-adapters.d.ts.map +1 -0
  248. package/dist/secrets/protected-value-adapters.js +118 -0
  249. package/dist/secrets/types.d.ts +70 -0
  250. package/dist/secrets/types.d.ts.map +1 -0
  251. package/dist/secrets/types.js +30 -0
  252. package/dist/session.d.ts +19 -0
  253. package/dist/session.d.ts.map +1 -0
  254. package/dist/session.js +120 -0
  255. package/dist/solver/browser-launcher.d.ts +14 -0
  256. package/dist/solver/browser-launcher.d.ts.map +1 -0
  257. package/dist/solver/browser-launcher.js +799 -0
  258. package/dist/solver/config.d.ts +18 -0
  259. package/dist/solver/config.d.ts.map +1 -0
  260. package/dist/solver/config.js +67 -0
  261. package/dist/solver/fingerprint.d.ts +9 -0
  262. package/dist/solver/fingerprint.d.ts.map +1 -0
  263. package/dist/solver/fingerprint.js +96 -0
  264. package/dist/solver/profile-manager.d.ts +8 -0
  265. package/dist/solver/profile-manager.d.ts.map +1 -0
  266. package/dist/solver/profile-manager.js +74 -0
  267. package/dist/solver/turnstile-challenge.d.ts +3 -0
  268. package/dist/solver/turnstile-challenge.d.ts.map +1 -0
  269. package/dist/solver/turnstile-challenge.js +173 -0
  270. package/dist/solver/types.d.ts +67 -0
  271. package/dist/solver/types.d.ts.map +1 -0
  272. package/dist/solver/types.js +1 -0
  273. package/dist/stagehand-runtime.d.ts +4 -0
  274. package/dist/stagehand-runtime.d.ts.map +1 -0
  275. package/dist/stagehand-runtime.js +10 -0
  276. package/dist/stagehand.d.ts +15 -0
  277. package/dist/stagehand.d.ts.map +1 -0
  278. package/dist/stagehand.js +19 -0
  279. package/dist/testing.d.ts +5 -0
  280. package/dist/testing.d.ts.map +1 -0
  281. package/dist/testing.js +4 -0
  282. package/dist/update-check.d.ts +14 -0
  283. package/dist/update-check.d.ts.map +1 -0
  284. package/dist/update-check.js +182 -0
  285. package/dist/workflow-session-state.d.ts +30 -0
  286. package/dist/workflow-session-state.d.ts.map +1 -0
  287. package/dist/workflow-session-state.js +74 -0
  288. package/docs/README.md +25 -0
  289. package/docs/api-reference.md +242 -0
  290. package/docs/assistive-runtime.md +148 -0
  291. package/docs/configuration.md +287 -0
  292. package/docs/getting-started.md +237 -0
  293. package/docs/integration-checklist.md +36 -0
  294. package/docs/protected-fill.md +112 -0
  295. package/docs/testing.md +50 -0
  296. package/docs/troubleshooting.md +71 -0
  297. package/examples/README.md +18 -0
  298. package/examples/attach.ts +27 -0
  299. package/examples/basic.ts +36 -0
  300. package/examples/extract.ts +50 -0
  301. package/package.json +83 -0
@@ -0,0 +1,1339 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ import { tryCreateAgentbrowseAssistiveLlmClient } from '../assistive-runtime.js';
4
+ import { recordLlmUsage, recordPayloadBudget } from '../runtime-metrics.js';
5
+ import { analyzeSemanticObserveText, buildSemanticObserveBm25CorpusStats, buildSemanticObserveLexicalDocument, normalizeSemanticObserveText, scoreSemanticObserveBm25, } from './semantic-observe-lexical.js';
6
+ const rerankSchema = z.object({
7
+ matches: z
8
+ .array(z.object({
9
+ candidateId: z.string(),
10
+ }))
11
+ .max(8),
12
+ });
13
+ const RERANK_CANDIDATE_LIMIT = 120;
14
+ const GOAL_RETRIEVAL_ENTITY_LIMIT = 64;
15
+ const FORM_BUCKET_RESERVE_LIMIT = 48;
16
+ const FORM_BUCKET_RESERVE_PER_BUCKET = 8;
17
+ const SCOPE_BUCKET_RESERVE_LIMIT = 24;
18
+ const SCOPE_BUCKET_RESERVE_PER_BUCKET = 2;
19
+ const IFRAME_BUCKET_RESERVE_LIMIT = 24;
20
+ const IFRAME_BUCKET_RESERVE_PER_BUCKET = 2;
21
+ const RETRIEVAL_FIELD_WEIGHTS = {
22
+ entityLabel: 5.5,
23
+ representativeLabel: 5,
24
+ representativeLabels: 4,
25
+ surfaceLabel: 2.25,
26
+ placeholder: 2.5,
27
+ title: 1.25,
28
+ itemLabel: 2.5,
29
+ itemText: 1.5,
30
+ groupLabel: 1.75,
31
+ groupText: 1,
32
+ containerLabel: 1.5,
33
+ containerText: 0.9,
34
+ landmarkLabel: 1.25,
35
+ landmarkText: 0.75,
36
+ hintText: 1.5,
37
+ kind: 0.6,
38
+ role: 0.6,
39
+ surfaceKind: 0.75,
40
+ controlFamily: 0.9,
41
+ acceptancePolicy: 1,
42
+ allowedActions: 0.9,
43
+ structure: 1.25,
44
+ latentIntent: 1.2,
45
+ };
46
+ const HIGH_SIGNAL_SCOPE_KINDS = new Set([
47
+ 'dialog',
48
+ 'listbox',
49
+ 'menu',
50
+ 'grid',
51
+ 'tabpanel',
52
+ 'popover',
53
+ 'dropdown',
54
+ 'datepicker',
55
+ 'card',
56
+ 'form',
57
+ ]);
58
+ const SEMANTIC_OBSERVE_SNAPSHOT_CACHE_VERSION = 1;
59
+ const SEMANTIC_OBSERVE_SNAPSHOT_CACHE_KEY_VERSION = 'semantic-observe-snapshot-v1';
60
+ const SEMANTIC_OBSERVE_GOAL_CACHE_KEY_VERSION = 'semantic-observe-goal-v1';
61
+ const SEMANTIC_OBSERVE_MAX_SNAPSHOT_CACHE_ENTRIES = 4;
62
+ const SEMANTIC_OBSERVE_MAX_GOAL_SHORTLISTS_PER_SNAPSHOT = 6;
63
+ const semanticObserveSnapshotCacheBySession = new WeakMap();
64
+ function isFieldLikeTarget(target) {
65
+ const kind = (target.kind ?? '').trim().toLowerCase();
66
+ const role = (target.role ?? '').trim().toLowerCase();
67
+ return (['input', 'textarea', 'select', 'combobox'].includes(kind) ||
68
+ ['textbox', 'combobox'].includes(role));
69
+ }
70
+ function isScopeLikeCandidate(target) {
71
+ return ((target.goalInventoryType ?? '').trim().toLowerCase() === 'scope' ||
72
+ (target.capability ?? '').trim().toLowerCase() === 'scope');
73
+ }
74
+ function isActionLikeTargetCandidate(target) {
75
+ if (isScopeLikeCandidate(target) || isFieldLikeTarget(target)) {
76
+ return false;
77
+ }
78
+ const kind = (target.kind ?? '').trim().toLowerCase();
79
+ const role = (target.role ?? '').trim().toLowerCase();
80
+ return (target.allowedActions?.includes('click') ||
81
+ target.allowedActions?.includes('press') ||
82
+ kind === 'button' ||
83
+ role === 'button' ||
84
+ kind === 'link' ||
85
+ role === 'link');
86
+ }
87
+ function semanticFormContextKey(context) {
88
+ for (const node of [context?.landmark, context?.container, context?.group, context?.item]) {
89
+ const kind = (node?.kind ?? '').trim().toLowerCase();
90
+ if (kind !== 'form') {
91
+ continue;
92
+ }
93
+ const label = (node?.label ?? node?.text ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
94
+ return `${kind}|${label}`;
95
+ }
96
+ return undefined;
97
+ }
98
+ function formBucketKey(target) {
99
+ const formSelector = target.formSelector?.trim();
100
+ if (formSelector) {
101
+ return `selector:${formSelector}`;
102
+ }
103
+ const contextKey = semanticFormContextKey(target.context);
104
+ return contextKey ? `context:${contextKey}` : undefined;
105
+ }
106
+ function candidateBucketKey(target, options = {}) {
107
+ const frameKey = target.framePath?.join('>') ?? 'top';
108
+ const formKey = formBucketKey(target);
109
+ const surfaceKey = (options.preferFormBucket ? formKey : undefined) ?? target.surfaceRef ?? formKey ?? 'page-root';
110
+ return `${frameKey}|${surfaceKey}`;
111
+ }
112
+ function surfaceIdentityOf(target) {
113
+ const explicitSurfaceId = target.goalSurfaceId?.trim();
114
+ if (explicitSurfaceId) {
115
+ return explicitSurfaceId;
116
+ }
117
+ const surfaceRef = target.surfaceRef?.trim();
118
+ if (!surfaceRef) {
119
+ return undefined;
120
+ }
121
+ return surfaceRef.startsWith('scope:') ? surfaceRef.slice('scope:'.length) : surfaceRef;
122
+ }
123
+ function isPrimaryFormControlTarget(target) {
124
+ const kind = (target.kind ?? '').trim().toLowerCase();
125
+ const role = (target.role ?? '').trim().toLowerCase();
126
+ const controlFamily = (target.controlFamily ?? '').trim().toLowerCase();
127
+ const acceptancePolicy = (target.acceptancePolicy ?? '').trim().toLowerCase();
128
+ if (target.allowedActions?.includes('fill') || target.allowedActions?.includes('type')) {
129
+ return true;
130
+ }
131
+ if (target.allowedActions?.includes('select')) {
132
+ return true;
133
+ }
134
+ if (['text-input', 'select', 'datepicker'].includes(controlFamily)) {
135
+ return true;
136
+ }
137
+ if (acceptancePolicy === 'submit' || acceptancePolicy === 'date-selection') {
138
+ return true;
139
+ }
140
+ return (Boolean(formBucketKey(target)) &&
141
+ acceptancePolicy === 'disclosure' &&
142
+ (kind === 'button' || role === 'button'));
143
+ }
144
+ function isConcreteFormChoiceTarget(target) {
145
+ if (!formBucketKey(target) ||
146
+ isScopeLikeCandidate(target) ||
147
+ isFieldLikeTarget(target) ||
148
+ !isActionLikeTargetCandidate(target) ||
149
+ isPrimaryFormControlTarget(target)) {
150
+ return false;
151
+ }
152
+ const acceptancePolicy = (target.acceptancePolicy ?? '').trim().toLowerCase();
153
+ const controlFamily = (target.controlFamily ?? '').trim().toLowerCase();
154
+ const surfaceKind = (target.surfaceKind ?? '').trim().toLowerCase();
155
+ const hasGoalIdentity = Boolean(goalLabelOf(target));
156
+ if (!hasGoalIdentity) {
157
+ return false;
158
+ }
159
+ return (controlFamily === 'trigger' ||
160
+ acceptancePolicy === 'selection' ||
161
+ ['listbox', 'menu', 'dropdown', 'popover'].includes(surfaceKind));
162
+ }
163
+ function shouldPreserveStandaloneFormActionEntity(target) {
164
+ return (Boolean(formBucketKey(target)) &&
165
+ !isScopeLikeCandidate(target) &&
166
+ !isFieldLikeTarget(target) &&
167
+ isActionLikeTargetCandidate(target) &&
168
+ (isPrimaryFormControlTarget(target) || isConcreteFormChoiceTarget(target)));
169
+ }
170
+ function primaryFormTargetPriority(target) {
171
+ const acceptancePolicy = (target.acceptancePolicy ?? '').trim().toLowerCase();
172
+ if (isFieldLikeTarget(target)) {
173
+ return 0;
174
+ }
175
+ if (acceptancePolicy === 'submit') {
176
+ return 1;
177
+ }
178
+ if (acceptancePolicy === 'selection' || acceptancePolicy === 'date-selection') {
179
+ return 2;
180
+ }
181
+ if (acceptancePolicy === 'disclosure') {
182
+ return 3;
183
+ }
184
+ return 4;
185
+ }
186
+ function isHighSignalScopeCandidate(target) {
187
+ if ((target.capability ?? '').trim().toLowerCase() !== 'scope') {
188
+ return false;
189
+ }
190
+ const kind = (target.kind ?? '').trim().toLowerCase();
191
+ return HIGH_SIGNAL_SCOPE_KINDS.has(kind);
192
+ }
193
+ function contentItemBucketKey(target) {
194
+ if (isScopeLikeCandidate(target) ||
195
+ !isActionLikeTargetCandidate(target) ||
196
+ Boolean(formBucketKey(target)) ||
197
+ isPrimaryFormControlTarget(target)) {
198
+ return undefined;
199
+ }
200
+ const surfaceKind = (target.surfaceKind ?? '').trim().toLowerCase();
201
+ if (['form', 'dialog', 'listbox', 'menu', 'popover', 'dropdown', 'datepicker'].includes(surfaceKind)) {
202
+ return undefined;
203
+ }
204
+ const surfaceIdentity = surfaceIdentityOf(target);
205
+ if (surfaceIdentity) {
206
+ return `surface:${surfaceIdentity}`;
207
+ }
208
+ const itemKey = normalizeSemanticObserveText(target.context?.item?.label ?? target.context?.item?.text);
209
+ if (itemKey) {
210
+ return `item:${itemKey}`;
211
+ }
212
+ const containerKey = normalizeSemanticObserveText(target.context?.container?.label ?? target.context?.container?.text);
213
+ if (containerKey) {
214
+ return `container:${containerKey}`;
215
+ }
216
+ return undefined;
217
+ }
218
+ function cachedContextNodeValue(node) {
219
+ if (!node) {
220
+ return undefined;
221
+ }
222
+ return {
223
+ ...(node.kind ? { kind: node.kind } : {}),
224
+ ...(node.label ? { label: node.label } : {}),
225
+ ...(node.text ? { text: node.text } : {}),
226
+ };
227
+ }
228
+ function cachedStateEntries(states) {
229
+ if (!states) {
230
+ return undefined;
231
+ }
232
+ const entries = Object.entries(states).sort(([left], [right]) => left.localeCompare(right));
233
+ return entries.length > 0 ? entries : undefined;
234
+ }
235
+ function cachedStructureValue(structure) {
236
+ if (!structure) {
237
+ return undefined;
238
+ }
239
+ return {
240
+ ...(structure.family ? { family: structure.family } : {}),
241
+ ...(structure.variant ? { variant: structure.variant } : {}),
242
+ ...(structure.row ? { row: structure.row } : {}),
243
+ ...(structure.column ? { column: structure.column } : {}),
244
+ ...(structure.zone ? { zone: structure.zone } : {}),
245
+ ...(structure.cellLabel ? { cellLabel: structure.cellLabel } : {}),
246
+ };
247
+ }
248
+ function normalizeGoalIdentityValue(value, maxLength = 180) {
249
+ const normalized = value?.replace(/\s+/g, ' ').trim();
250
+ if (!normalized) {
251
+ return undefined;
252
+ }
253
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized;
254
+ }
255
+ function appendGoalIdentityAlias(values, value) {
256
+ const normalized = normalizeGoalIdentityValue(value);
257
+ if (!normalized) {
258
+ return;
259
+ }
260
+ const normalizedKey = normalized.toLowerCase();
261
+ if (values.some((existing) => existing.toLowerCase() === normalizedKey)) {
262
+ return;
263
+ }
264
+ values.push(normalized);
265
+ }
266
+ function goalIdentityValuesOf(target) {
267
+ const values = [];
268
+ appendGoalIdentityAlias(values, target.goalLabel);
269
+ for (const value of target.goalAliases ?? []) {
270
+ appendGoalIdentityAlias(values, value);
271
+ }
272
+ if (values.length > 0) {
273
+ return values;
274
+ }
275
+ if (isFieldLikeTarget(target)) {
276
+ appendGoalIdentityAlias(values, target.label);
277
+ appendGoalIdentityAlias(values, target.displayLabel);
278
+ appendGoalIdentityAlias(values, target.placeholder);
279
+ appendGoalIdentityAlias(values, target.title);
280
+ return values;
281
+ }
282
+ appendGoalIdentityAlias(values, target.label);
283
+ appendGoalIdentityAlias(values, target.displayLabel);
284
+ appendGoalIdentityAlias(values, target.text);
285
+ appendGoalIdentityAlias(values, target.title);
286
+ return values;
287
+ }
288
+ function goalLabelOf(target) {
289
+ return goalIdentityValuesOf(target)[0];
290
+ }
291
+ function goalAliasesOf(target) {
292
+ return goalIdentityValuesOf(target).slice(1, 5);
293
+ }
294
+ function serializeObserveSnapshotTarget(target) {
295
+ const itemContext = cachedContextNodeValue(target.context?.item);
296
+ const groupContext = cachedContextNodeValue(target.context?.group);
297
+ const containerContext = cachedContextNodeValue(target.context?.container);
298
+ const landmarkContext = cachedContextNodeValue(target.context?.landmark);
299
+ const stateEntries = cachedStateEntries(target.states);
300
+ const structure = cachedStructureValue(target.structure);
301
+ const goalLabel = goalLabelOf(target);
302
+ const goalAliases = goalAliasesOf(target);
303
+ return {
304
+ goalInventoryType: target.goalInventoryType,
305
+ ...(target.goalInventoryType === 'scope' && target.goalSurfaceId
306
+ ? { goalSurfaceId: target.goalSurfaceId }
307
+ : {}),
308
+ ...(goalLabel ? { goalLabel } : {}),
309
+ ...(goalAliases.length ? { goalAliases } : {}),
310
+ ...(target.kind ? { kind: target.kind } : {}),
311
+ ...(target.label ? { label: target.label } : {}),
312
+ ...(target.displayLabel ? { displayLabel: target.displayLabel } : {}),
313
+ ...(target.role ? { role: target.role } : {}),
314
+ ...(target.interactionHint ? { interactionHint: target.interactionHint } : {}),
315
+ ...(target.text ? { text: target.text } : {}),
316
+ ...(target.placeholder ? { placeholder: target.placeholder } : {}),
317
+ ...(target.title ? { title: target.title } : {}),
318
+ ...(target.capability ? { capability: target.capability } : {}),
319
+ ...(target.allowedActions?.length ? { allowedActions: [...target.allowedActions].sort() } : {}),
320
+ ...(target.acceptancePolicy ? { acceptancePolicy: target.acceptancePolicy } : {}),
321
+ ...(target.controlFamily ? { controlFamily: target.controlFamily } : {}),
322
+ ...(target.surfaceRef ? { surfaceRef: target.surfaceRef } : {}),
323
+ ...(target.surfaceKind ? { surfaceKind: target.surfaceKind } : {}),
324
+ ...(target.surfaceLabel ? { surfaceLabel: target.surfaceLabel } : {}),
325
+ ...(typeof target.surfacePriority === 'number'
326
+ ? { surfacePriority: target.surfacePriority }
327
+ : {}),
328
+ ...(target.framePath?.length ? { framePath: [...target.framePath] } : {}),
329
+ ...(target.frameUrl ? { frameUrl: target.frameUrl } : {}),
330
+ ...(target.formSelector ? { formSelector: target.formSelector } : {}),
331
+ ...(target.pageSignature ? { pageSignature: target.pageSignature } : {}),
332
+ ...(target.controlsSurfaceSelector
333
+ ? { controlsSurfaceSelector: target.controlsSurfaceSelector }
334
+ : {}),
335
+ ...(stateEntries ? { states: stateEntries } : {}),
336
+ ...(target.context
337
+ ? {
338
+ context: {
339
+ ...(itemContext ? { item: itemContext } : {}),
340
+ ...(groupContext ? { group: groupContext } : {}),
341
+ ...(containerContext ? { container: containerContext } : {}),
342
+ ...(landmarkContext ? { landmark: landmarkContext } : {}),
343
+ ...(target.context.hintText ? { hintText: target.context.hintText } : {}),
344
+ },
345
+ }
346
+ : {}),
347
+ ...(structure ? { structure } : {}),
348
+ };
349
+ }
350
+ function semanticObserveSnapshotKey(targets) {
351
+ const payload = JSON.stringify(targets.map((target) => serializeObserveSnapshotTarget(target)));
352
+ return createHash('sha256')
353
+ .update(SEMANTIC_OBSERVE_SNAPSHOT_CACHE_KEY_VERSION)
354
+ .update(payload)
355
+ .digest('hex');
356
+ }
357
+ function semanticObserveGoalKey(goal) {
358
+ return createHash('sha256')
359
+ .update(SEMANTIC_OBSERVE_GOAL_CACHE_KEY_VERSION)
360
+ .update(normalizedGoalText(goal))
361
+ .digest('hex');
362
+ }
363
+ function serializeLexicalDocument(lexicalDocument) {
364
+ return {
365
+ analyzerKey: lexicalDocument.analyzerKey,
366
+ weightedLength: lexicalDocument.weightedLength,
367
+ weightedTermFrequencies: [...lexicalDocument.weightedTermFrequencies.entries()],
368
+ };
369
+ }
370
+ function deserializeLexicalDocument(lexicalDocument) {
371
+ return {
372
+ analyzerKey: lexicalDocument.analyzerKey,
373
+ weightedLength: lexicalDocument.weightedLength,
374
+ weightedTermFrequencies: new Map(lexicalDocument.weightedTermFrequencies),
375
+ };
376
+ }
377
+ function ensureSemanticObserveSnapshotCache(session) {
378
+ const existing = semanticObserveSnapshotCacheBySession.get(session);
379
+ if (existing &&
380
+ existing.version === SEMANTIC_OBSERVE_SNAPSHOT_CACHE_VERSION &&
381
+ Array.isArray(existing.order) &&
382
+ existing.snapshotsByKey &&
383
+ typeof existing.snapshotsByKey === 'object') {
384
+ return existing;
385
+ }
386
+ const nextCache = {
387
+ version: SEMANTIC_OBSERVE_SNAPSHOT_CACHE_VERSION,
388
+ order: [],
389
+ snapshotsByKey: {},
390
+ };
391
+ semanticObserveSnapshotCacheBySession.set(session, nextCache);
392
+ return nextCache;
393
+ }
394
+ function touchSemanticObserveSnapshotCacheEntry(cache, snapshotKey) {
395
+ cache.order = [snapshotKey, ...cache.order.filter((key) => key !== snapshotKey)].slice(0, SEMANTIC_OBSERVE_MAX_SNAPSHOT_CACHE_ENTRIES);
396
+ for (const key of Object.keys(cache.snapshotsByKey)) {
397
+ if (!cache.order.includes(key)) {
398
+ delete cache.snapshotsByKey[key];
399
+ }
400
+ }
401
+ }
402
+ function materializeCachedRetrievalEntities(targets, cachedEntities) {
403
+ const materialized = [];
404
+ for (const cachedEntity of cachedEntities) {
405
+ const representative = targets[cachedEntity.representativeIndex];
406
+ if (!representative) {
407
+ return null;
408
+ }
409
+ if (cachedEntity.memberIndexes.some((index) => !targets[index])) {
410
+ return null;
411
+ }
412
+ materialized.push({
413
+ entityKind: cachedEntity.entityKind,
414
+ entityKey: cachedEntity.entityKey,
415
+ firstIndex: cachedEntity.firstIndex,
416
+ memberIndexes: [...cachedEntity.memberIndexes],
417
+ representative,
418
+ label: pickEntityLabel(cachedEntity.entityKind, representative),
419
+ kind: cachedEntity.entityKind === 'form'
420
+ ? 'form'
421
+ : cachedEntity.entityKind === 'item'
422
+ ? (representative.surfaceKind ?? representative.kind)
423
+ : representative.kind,
424
+ surfaceKind: representative.surfaceKind,
425
+ surfaceLabel: representative.surfaceLabel,
426
+ surfacePriority: representative.surfacePriority,
427
+ framePath: representative.framePath,
428
+ frameUrl: representative.frameUrl,
429
+ context: representative.context,
430
+ structure: representative.structure,
431
+ representativeLabels: [],
432
+ analyzerKey: cachedEntity.analyzerKey,
433
+ lexicalDocument: deserializeLexicalDocument(cachedEntity.lexicalDocument),
434
+ });
435
+ }
436
+ return materialized;
437
+ }
438
+ function loadCachedRetrievalEntities(session, snapshotKey, targets) {
439
+ if (!session) {
440
+ return null;
441
+ }
442
+ const cache = ensureSemanticObserveSnapshotCache(session);
443
+ const entry = cache.snapshotsByKey[snapshotKey];
444
+ if (!entry || entry.targetCount !== targets.length || !entry.retrievalEntities) {
445
+ return null;
446
+ }
447
+ const materialized = materializeCachedRetrievalEntities(targets, entry.retrievalEntities);
448
+ if (!materialized) {
449
+ delete cache.snapshotsByKey[snapshotKey];
450
+ cache.order = cache.order.filter((key) => key !== snapshotKey);
451
+ return null;
452
+ }
453
+ touchSemanticObserveSnapshotCacheEntry(cache, snapshotKey);
454
+ return materialized;
455
+ }
456
+ function saveCachedRetrievalEntities(session, snapshotKey, targets, entities) {
457
+ if (!session) {
458
+ return;
459
+ }
460
+ const cache = ensureSemanticObserveSnapshotCache(session);
461
+ const indexByTarget = new Map(targets.map((target, index) => [target, index]));
462
+ const existingEntry = cache.snapshotsByKey[snapshotKey];
463
+ cache.snapshotsByKey[snapshotKey] = {
464
+ snapshotKey,
465
+ targetCount: targets.length,
466
+ cachedAt: new Date().toISOString(),
467
+ retrievalEntities: entities.map((entity) => ({
468
+ entityKind: entity.entityKind,
469
+ entityKey: entity.entityKey,
470
+ firstIndex: entity.firstIndex,
471
+ representativeIndex: indexByTarget.get(entity.representative) ?? entity.firstIndex,
472
+ memberIndexes: [...entity.memberIndexes],
473
+ analyzerKey: entity.analyzerKey,
474
+ lexicalDocument: serializeLexicalDocument(entity.lexicalDocument),
475
+ })),
476
+ goalShortlists: existingEntry?.goalShortlists ?? {},
477
+ goalOrder: existingEntry?.goalOrder ?? [],
478
+ };
479
+ touchSemanticObserveSnapshotCacheEntry(cache, snapshotKey);
480
+ }
481
+ function loadCachedGoalShortlist(session, snapshotKey, goalKey, targets) {
482
+ if (!session || !goalKey) {
483
+ return null;
484
+ }
485
+ const cache = ensureSemanticObserveSnapshotCache(session);
486
+ const entry = cache.snapshotsByKey[snapshotKey];
487
+ const shortlist = entry?.goalShortlists?.[goalKey];
488
+ if (!entry || entry.targetCount !== targets.length || !shortlist) {
489
+ return null;
490
+ }
491
+ const candidates = shortlist.candidateIndexes.map((index) => targets[index]).filter(Boolean);
492
+ if (candidates.length !== shortlist.candidateIndexes.length) {
493
+ delete entry.goalShortlists?.[goalKey];
494
+ entry.goalOrder = (entry.goalOrder ?? []).filter((key) => key !== goalKey);
495
+ return null;
496
+ }
497
+ entry.goalOrder = [goalKey, ...(entry.goalOrder ?? []).filter((key) => key !== goalKey)].slice(0, SEMANTIC_OBSERVE_MAX_GOAL_SHORTLISTS_PER_SNAPSHOT);
498
+ touchSemanticObserveSnapshotCacheEntry(cache, snapshotKey);
499
+ return candidates;
500
+ }
501
+ function saveCachedGoalShortlist(session, snapshotKey, goalKey, targets, shortlist) {
502
+ if (!session || !goalKey) {
503
+ return;
504
+ }
505
+ const cache = ensureSemanticObserveSnapshotCache(session);
506
+ const existingEntry = cache.snapshotsByKey[snapshotKey];
507
+ const entry = existingEntry ?? {
508
+ snapshotKey,
509
+ targetCount: targets.length,
510
+ cachedAt: new Date().toISOString(),
511
+ goalShortlists: {},
512
+ goalOrder: [],
513
+ };
514
+ const indexByTarget = new Map(targets.map((target, index) => [target, index]));
515
+ entry.targetCount = targets.length;
516
+ entry.cachedAt = new Date().toISOString();
517
+ entry.goalShortlists ??= {};
518
+ entry.goalOrder = [goalKey, ...(entry.goalOrder ?? []).filter((key) => key !== goalKey)].slice(0, SEMANTIC_OBSERVE_MAX_GOAL_SHORTLISTS_PER_SNAPSHOT);
519
+ entry.goalShortlists[goalKey] = {
520
+ goalKey,
521
+ candidateIndexes: shortlist
522
+ .map((candidate) => indexByTarget.get(candidate))
523
+ .filter((index) => index !== undefined),
524
+ cachedAt: entry.cachedAt,
525
+ };
526
+ for (const cachedGoalKey of Object.keys(entry.goalShortlists)) {
527
+ if (!entry.goalOrder.includes(cachedGoalKey)) {
528
+ delete entry.goalShortlists[cachedGoalKey];
529
+ }
530
+ }
531
+ cache.snapshotsByKey[snapshotKey] = entry;
532
+ touchSemanticObserveSnapshotCacheEntry(cache, snapshotKey);
533
+ }
534
+ function scopeCandidatePriority(target) {
535
+ const kind = (target.kind ?? '').trim().toLowerCase();
536
+ if (kind === 'dialog' || kind === 'listbox' || kind === 'menu') {
537
+ return 0;
538
+ }
539
+ if (kind === 'card') {
540
+ return 1;
541
+ }
542
+ if (kind === 'grid' || kind === 'tabpanel' || kind === 'datepicker') {
543
+ return 2;
544
+ }
545
+ if (kind === 'form') {
546
+ return 3;
547
+ }
548
+ return 4;
549
+ }
550
+ function representativeCandidateScore(target) {
551
+ const normalizedLabel = normalizeSemanticObserveText(target.label);
552
+ const kind = (target.kind ?? '').trim().toLowerCase();
553
+ const role = (target.role ?? '').trim().toLowerCase();
554
+ let score = (target.surfacePriority ?? 0) * 10;
555
+ if (isPrimaryFormControlTarget(target)) {
556
+ score += 1_500 - primaryFormTargetPriority(target) * 100;
557
+ }
558
+ else if (isFieldLikeTarget(target)) {
559
+ score += 1_250;
560
+ }
561
+ else if (isActionLikeTargetCandidate(target)) {
562
+ score += 1_000;
563
+ }
564
+ else if (isScopeLikeCandidate(target)) {
565
+ score += 700;
566
+ }
567
+ if (target.allowedActions?.includes('fill') || target.allowedActions?.includes('select')) {
568
+ score += 120;
569
+ }
570
+ if (target.allowedActions?.includes('click') || target.allowedActions?.includes('press')) {
571
+ score += 80;
572
+ }
573
+ if (target.acceptancePolicy === 'submit') {
574
+ score += 120;
575
+ }
576
+ if (target.acceptancePolicy === 'navigation') {
577
+ score += 60;
578
+ }
579
+ if (kind === 'link' || role === 'link') {
580
+ score += 40;
581
+ }
582
+ if (normalizedLabel) {
583
+ score += Math.min(normalizedLabel.length, 100);
584
+ if (normalizedLabel === 'button' || normalizedLabel === 'link') {
585
+ score -= 300;
586
+ }
587
+ if (normalizedLabel.includes('opens in new window')) {
588
+ score -= 120;
589
+ }
590
+ if (normalizedLabel.includes('save this item')) {
591
+ score -= 80;
592
+ }
593
+ }
594
+ return score;
595
+ }
596
+ function entityMemberPriority(entityKind, target) {
597
+ if (entityKind === 'form' && isPrimaryFormControlTarget(target)) {
598
+ return 5_000 - primaryFormTargetPriority(target) * 100;
599
+ }
600
+ if (entityKind === 'scope' && isScopeLikeCandidate(target)) {
601
+ return representativeCandidateScore(target) - 200;
602
+ }
603
+ return representativeCandidateScore(target);
604
+ }
605
+ function compareEntityMembers(entityKind, left, right) {
606
+ const scoreDelta = entityMemberPriority(entityKind, right.target) - entityMemberPriority(entityKind, left.target);
607
+ if (scoreDelta !== 0) {
608
+ return scoreDelta;
609
+ }
610
+ return left.index - right.index;
611
+ }
612
+ function pickEntityLabel(entityKind, representative) {
613
+ if (entityKind === 'form') {
614
+ return (representative.context?.landmark?.label ??
615
+ representative.context?.container?.label ??
616
+ representative.context?.group?.label ??
617
+ representative.surfaceLabel ??
618
+ representative.label);
619
+ }
620
+ return (goalLabelOf(representative) ??
621
+ representative.context?.container?.label ??
622
+ representative.context?.item?.label ??
623
+ representative.surfaceLabel ??
624
+ representative.context?.group?.label ??
625
+ representative.context?.landmark?.label);
626
+ }
627
+ function collectRepresentativeLabels(targets) {
628
+ const labels = [];
629
+ for (const target of targets) {
630
+ for (const label of [goalLabelOf(target), ...goalAliasesOf(target)]) {
631
+ if (!label || label === 'Button' || label === 'Link' || labels.includes(label)) {
632
+ continue;
633
+ }
634
+ labels.push(label);
635
+ if (labels.length >= 4) {
636
+ return labels;
637
+ }
638
+ }
639
+ }
640
+ return labels;
641
+ }
642
+ function structureLexicalValue(structure) {
643
+ if (!structure) {
644
+ return undefined;
645
+ }
646
+ return [
647
+ structure.family,
648
+ structure.variant,
649
+ structure.row,
650
+ structure.column,
651
+ structure.zone,
652
+ structure.cellLabel,
653
+ ]
654
+ .filter(Boolean)
655
+ .join(' ');
656
+ }
657
+ function latentActionHintText(target) {
658
+ const acceptancePolicy = (target.acceptancePolicy ?? '').trim().toLowerCase();
659
+ const controlFamily = (target.controlFamily ?? '').trim().toLowerCase();
660
+ const surfaceKind = (target.surfaceKind ?? '').trim().toLowerCase();
661
+ const kind = (target.kind ?? '').trim().toLowerCase();
662
+ const role = (target.role ?? '').trim().toLowerCase();
663
+ const popupBacked = acceptancePolicy === 'disclosure' ||
664
+ Boolean(target.controlsSurfaceSelector) ||
665
+ target.states?.expanded !== undefined;
666
+ if (!popupBacked) {
667
+ return undefined;
668
+ }
669
+ const hints = new Set();
670
+ hints.add('open menu options choices');
671
+ if (!isFieldLikeTarget(target)) {
672
+ hints.add('sort sorting filter filters view switch picker');
673
+ }
674
+ if (controlFamily === 'select' ||
675
+ ['menu', 'listbox', 'dropdown', 'popover'].includes(surfaceKind)) {
676
+ hints.add('select choose option options menu dropdown listbox');
677
+ }
678
+ if (controlFamily === 'datepicker' || surfaceKind === 'datepicker') {
679
+ hints.add('open calendar datepicker date picker choose date calendar');
680
+ }
681
+ const evidenceText = [
682
+ target.label,
683
+ target.placeholder,
684
+ target.title,
685
+ target.surfaceLabel,
686
+ target.context?.group?.label,
687
+ target.context?.container?.label,
688
+ target.context?.landmark?.label,
689
+ target.context?.hintText,
690
+ ]
691
+ .filter((value) => Boolean(value))
692
+ .join(' ')
693
+ .toLowerCase();
694
+ if (/(?:sort|sorting|show first|ordered by|order by|popular|relevance|recommended|price|cheap|cheapest|expensive|сорт|сначала|популяр|релевант|рекоменд|цене|дешев|дорог)/i.test(evidenceText)) {
695
+ hints.add('sort sorting order order by relevance popular recommended price cheapest cheapest first');
696
+ }
697
+ if (/(?:filter|filters|refine|brand|airline|amenit|фильтр|фильтры|бренд|авиакомпан|удобств)/i.test(evidenceText)) {
698
+ hints.add('filter filters refine refine results brand airline amenities');
699
+ }
700
+ if (/(?:view|layout|grid|list|map|calendar view|вид|список|сетка|карта|раскладк)/i.test(evidenceText)) {
701
+ hints.add('view layout grid list map switch');
702
+ }
703
+ if (kind === 'button' &&
704
+ role === 'button' &&
705
+ (acceptancePolicy === 'disclosure' ||
706
+ controlFamily === 'select' ||
707
+ controlFamily === 'datepicker')) {
708
+ hints.add('open selector choose mode');
709
+ }
710
+ return hints.size > 0 ? [...hints].join(' ') : undefined;
711
+ }
712
+ function buildEntityLexicalFields(entityLabel, representative, representativeLabels) {
713
+ const representativeGoalLabel = goalLabelOf(representative);
714
+ return [
715
+ { value: entityLabel, weight: RETRIEVAL_FIELD_WEIGHTS.entityLabel },
716
+ { value: representativeGoalLabel, weight: RETRIEVAL_FIELD_WEIGHTS.representativeLabel },
717
+ ...representativeLabels.map((value) => ({
718
+ value,
719
+ weight: RETRIEVAL_FIELD_WEIGHTS.representativeLabels,
720
+ })),
721
+ { value: representative.surfaceLabel, weight: RETRIEVAL_FIELD_WEIGHTS.surfaceLabel },
722
+ { value: representative.placeholder, weight: RETRIEVAL_FIELD_WEIGHTS.placeholder },
723
+ { value: representative.title, weight: RETRIEVAL_FIELD_WEIGHTS.title },
724
+ { value: representative.context?.item?.label, weight: RETRIEVAL_FIELD_WEIGHTS.itemLabel },
725
+ { value: representative.context?.item?.text, weight: RETRIEVAL_FIELD_WEIGHTS.itemText },
726
+ { value: representative.context?.group?.label, weight: RETRIEVAL_FIELD_WEIGHTS.groupLabel },
727
+ { value: representative.context?.group?.text, weight: RETRIEVAL_FIELD_WEIGHTS.groupText },
728
+ {
729
+ value: representative.context?.container?.label,
730
+ weight: RETRIEVAL_FIELD_WEIGHTS.containerLabel,
731
+ },
732
+ {
733
+ value: representative.context?.container?.text,
734
+ weight: RETRIEVAL_FIELD_WEIGHTS.containerText,
735
+ },
736
+ {
737
+ value: representative.context?.landmark?.label,
738
+ weight: RETRIEVAL_FIELD_WEIGHTS.landmarkLabel,
739
+ },
740
+ {
741
+ value: representative.context?.landmark?.text,
742
+ weight: RETRIEVAL_FIELD_WEIGHTS.landmarkText,
743
+ },
744
+ { value: representative.context?.hintText, weight: RETRIEVAL_FIELD_WEIGHTS.hintText },
745
+ { value: representative.kind, weight: RETRIEVAL_FIELD_WEIGHTS.kind },
746
+ { value: representative.role, weight: RETRIEVAL_FIELD_WEIGHTS.role },
747
+ { value: representative.surfaceKind, weight: RETRIEVAL_FIELD_WEIGHTS.surfaceKind },
748
+ {
749
+ value: representative.controlFamily,
750
+ weight: RETRIEVAL_FIELD_WEIGHTS.controlFamily,
751
+ },
752
+ {
753
+ value: representative.acceptancePolicy,
754
+ weight: RETRIEVAL_FIELD_WEIGHTS.acceptancePolicy,
755
+ },
756
+ {
757
+ value: representative.allowedActions?.join(' '),
758
+ weight: RETRIEVAL_FIELD_WEIGHTS.allowedActions,
759
+ },
760
+ {
761
+ value: structureLexicalValue(representative.structure),
762
+ weight: RETRIEVAL_FIELD_WEIGHTS.structure,
763
+ },
764
+ {
765
+ value: latentActionHintText(representative),
766
+ weight: RETRIEVAL_FIELD_WEIGHTS.latentIntent,
767
+ },
768
+ ];
769
+ }
770
+ function buildGoalRetrievalEntity(entityKind, entityKey, memberIndexes, targets) {
771
+ const orderedMembers = [...memberIndexes]
772
+ .map((index) => ({ index, target: targets[index] }))
773
+ .sort((left, right) => compareEntityMembers(entityKind, left, right));
774
+ const representative = orderedMembers[0].target;
775
+ const representativeLabels = collectRepresentativeLabels(orderedMembers.map((entry) => entry.target));
776
+ const label = pickEntityLabel(entityKind, representative);
777
+ const lexicalFields = buildEntityLexicalFields(label, representative, representativeLabels);
778
+ const lexicalDocument = buildSemanticObserveLexicalDocument(lexicalFields);
779
+ return {
780
+ entityKind,
781
+ entityKey,
782
+ firstIndex: Math.min(...memberIndexes),
783
+ memberIndexes: orderedMembers.map((entry) => entry.index),
784
+ representative,
785
+ label,
786
+ kind: entityKind === 'form'
787
+ ? 'form'
788
+ : entityKind === 'item'
789
+ ? (representative.surfaceKind ?? representative.kind)
790
+ : representative.kind,
791
+ surfaceKind: representative.surfaceKind,
792
+ surfaceLabel: representative.surfaceLabel,
793
+ surfacePriority: representative.surfacePriority,
794
+ framePath: representative.framePath,
795
+ frameUrl: representative.frameUrl,
796
+ context: representative.context,
797
+ structure: representative.structure,
798
+ representativeLabels,
799
+ analyzerKey: lexicalDocument.analyzerKey,
800
+ lexicalDocument,
801
+ };
802
+ }
803
+ function buildGoalRetrievalEntities(targets) {
804
+ const entities = [];
805
+ const groupedTargetIndexes = new Set();
806
+ const nonScopeTargetsBySurface = new Map();
807
+ for (const [index, target] of targets.entries()) {
808
+ if (isScopeLikeCandidate(target)) {
809
+ continue;
810
+ }
811
+ const surfaceIdentity = surfaceIdentityOf(target);
812
+ if (!surfaceIdentity) {
813
+ continue;
814
+ }
815
+ const linked = nonScopeTargetsBySurface.get(surfaceIdentity) ?? [];
816
+ linked.push(index);
817
+ nonScopeTargetsBySurface.set(surfaceIdentity, linked);
818
+ }
819
+ const formGroups = new Map();
820
+ for (const [index, target] of targets.entries()) {
821
+ if (isScopeLikeCandidate(target)) {
822
+ continue;
823
+ }
824
+ const bucketKey = formBucketKey(target);
825
+ if (!bucketKey) {
826
+ continue;
827
+ }
828
+ const members = formGroups.get(bucketKey) ?? [];
829
+ members.push(index);
830
+ formGroups.set(bucketKey, members);
831
+ }
832
+ for (const [key, memberIndexes] of formGroups.entries()) {
833
+ entities.push(buildGoalRetrievalEntity('form', `form:${key}`, memberIndexes, targets));
834
+ memberIndexes.forEach((index) => {
835
+ if (shouldPreserveStandaloneFormActionEntity(targets[index])) {
836
+ return;
837
+ }
838
+ groupedTargetIndexes.add(index);
839
+ });
840
+ }
841
+ const itemGroups = new Map();
842
+ for (const [index, target] of targets.entries()) {
843
+ const bucketKey = contentItemBucketKey(target);
844
+ if (!bucketKey) {
845
+ continue;
846
+ }
847
+ const members = itemGroups.get(bucketKey) ?? [];
848
+ members.push(index);
849
+ itemGroups.set(bucketKey, members);
850
+ }
851
+ for (const [key, memberIndexes] of itemGroups.entries()) {
852
+ entities.push(buildGoalRetrievalEntity('item', `item:${key}`, memberIndexes, targets));
853
+ memberIndexes.forEach((index) => groupedTargetIndexes.add(index));
854
+ }
855
+ for (const [index, target] of targets.entries()) {
856
+ if (!isHighSignalScopeCandidate(target)) {
857
+ continue;
858
+ }
859
+ const kind = (target.kind ?? '').trim().toLowerCase();
860
+ const surfaceIdentity = surfaceIdentityOf(target);
861
+ if (!surfaceIdentity) {
862
+ continue;
863
+ }
864
+ if (kind === 'form') {
865
+ continue;
866
+ }
867
+ if (kind === 'card' && itemGroups.has(`surface:${surfaceIdentity}`)) {
868
+ continue;
869
+ }
870
+ const linkedTargets = nonScopeTargetsBySurface.get(surfaceIdentity) ?? [];
871
+ const memberIndexes = [index, ...linkedTargets];
872
+ entities.push(buildGoalRetrievalEntity('scope', `scope:${surfaceIdentity}`, memberIndexes, targets));
873
+ linkedTargets.forEach((targetIndex) => groupedTargetIndexes.add(targetIndex));
874
+ }
875
+ for (const [index, target] of targets.entries()) {
876
+ if (isScopeLikeCandidate(target) || groupedTargetIndexes.has(index)) {
877
+ continue;
878
+ }
879
+ if (!isActionLikeTargetCandidate(target) &&
880
+ !isFieldLikeTarget(target) &&
881
+ !isPrimaryFormControlTarget(target)) {
882
+ continue;
883
+ }
884
+ entities.push(buildGoalRetrievalEntity('standalone', `target:${index}`, [index], targets));
885
+ }
886
+ return entities.sort((left, right) => left.firstIndex - right.firstIndex);
887
+ }
888
+ function retrievalEntityPriority(entity) {
889
+ let score = (entity.surfacePriority ?? 0) * 10;
890
+ switch (entity.entityKind) {
891
+ case 'form':
892
+ score += 900;
893
+ break;
894
+ case 'item':
895
+ score += 820;
896
+ break;
897
+ case 'scope':
898
+ score += 720;
899
+ break;
900
+ case 'standalone':
901
+ score += 600;
902
+ break;
903
+ }
904
+ score += Math.min(entity.memberIndexes.length, 6) * 25;
905
+ score += representativeCandidateScore(entity.representative);
906
+ return score;
907
+ }
908
+ function scoreRetrievalEntityAgainstGoal(analyzedGoal, entity, corpusStats) {
909
+ if (!analyzedGoal.normalizedText) {
910
+ return 0;
911
+ }
912
+ if (entity.lexicalDocument.weightedLength <= 0) {
913
+ return 0;
914
+ }
915
+ const bm25Score = scoreSemanticObserveBm25(analyzedGoal, entity.lexicalDocument, corpusStats);
916
+ const analyzerAlignmentScore = analyzedGoal.analyzerKey !== 'neutral' && analyzedGoal.analyzerKey === entity.analyzerKey
917
+ ? 15
918
+ : 0;
919
+ return bm25Score * 100 + analyzerAlignmentScore;
920
+ }
921
+ function preselectRetrievalEntities(goal, entities) {
922
+ if (entities.length <= GOAL_RETRIEVAL_ENTITY_LIMIT) {
923
+ return [...entities];
924
+ }
925
+ const analyzedGoal = analyzeSemanticObserveText(goal);
926
+ const corpusStats = buildSemanticObserveBm25CorpusStats(entities.map((entity) => entity.lexicalDocument));
927
+ const scored = entities.map((entity) => ({
928
+ entity,
929
+ lexicalScore: scoreRetrievalEntityAgainstGoal(analyzedGoal, entity, corpusStats),
930
+ priority: retrievalEntityPriority(entity),
931
+ }));
932
+ const selected = [];
933
+ const seenKeys = new Set();
934
+ for (const entry of scored
935
+ .filter((candidate) => candidate.lexicalScore > 0)
936
+ .sort((left, right) => right.lexicalScore - left.lexicalScore ||
937
+ right.priority - left.priority ||
938
+ left.entity.firstIndex - right.entity.firstIndex)) {
939
+ if (seenKeys.has(entry.entity.entityKey)) {
940
+ continue;
941
+ }
942
+ seenKeys.add(entry.entity.entityKey);
943
+ selected.push(entry.entity);
944
+ if (selected.length >= GOAL_RETRIEVAL_ENTITY_LIMIT) {
945
+ return selected;
946
+ }
947
+ }
948
+ for (const entry of scored.sort((left, right) => right.priority - left.priority || left.entity.firstIndex - right.entity.firstIndex)) {
949
+ if (seenKeys.has(entry.entity.entityKey)) {
950
+ continue;
951
+ }
952
+ seenKeys.add(entry.entity.entityKey);
953
+ selected.push(entry.entity);
954
+ if (selected.length >= GOAL_RETRIEVAL_ENTITY_LIMIT) {
955
+ break;
956
+ }
957
+ }
958
+ return selected;
959
+ }
960
+ function expandRetrievalEntitiesToCandidates(targets, entities) {
961
+ const orderedIndexes = [];
962
+ const seenIndexes = new Set();
963
+ for (const entity of entities) {
964
+ for (const index of entity.memberIndexes) {
965
+ if (seenIndexes.has(index)) {
966
+ continue;
967
+ }
968
+ seenIndexes.add(index);
969
+ orderedIndexes.push(index);
970
+ }
971
+ }
972
+ return orderedIndexes.map((index) => targets[index]).filter(Boolean);
973
+ }
974
+ function collectBucketedCandidateIndexes(targets, options) {
975
+ const bucketEntries = new Map();
976
+ for (const [index, target] of targets.entries()) {
977
+ if (!options.predicate(target)) {
978
+ continue;
979
+ }
980
+ const bucketKey = options.bucketKeyOf(target);
981
+ if (!bucketKey) {
982
+ continue;
983
+ }
984
+ const entries = bucketEntries.get(bucketKey) ?? [];
985
+ entries.push({ index, target });
986
+ bucketEntries.set(bucketKey, entries);
987
+ }
988
+ const bucketQueues = [...bucketEntries.values()]
989
+ .map((entries) => entries
990
+ .sort((left, right) => {
991
+ const leftPriority = options.priorityOf?.(left.target) ?? 0;
992
+ const rightPriority = options.priorityOf?.(right.target) ?? 0;
993
+ if (leftPriority !== rightPriority) {
994
+ return leftPriority - rightPriority;
995
+ }
996
+ return left.index - right.index;
997
+ })
998
+ .slice(0, options.perBucketLimit))
999
+ .sort((left, right) => left[0].index - right[0].index);
1000
+ const selectedIndexes = [];
1001
+ while (selectedIndexes.length < options.totalLimit) {
1002
+ let progressed = false;
1003
+ for (const queue of bucketQueues) {
1004
+ const next = queue.shift();
1005
+ if (!next) {
1006
+ continue;
1007
+ }
1008
+ selectedIndexes.push(next.index);
1009
+ progressed = true;
1010
+ if (selectedIndexes.length >= options.totalLimit) {
1011
+ break;
1012
+ }
1013
+ }
1014
+ if (!progressed) {
1015
+ break;
1016
+ }
1017
+ }
1018
+ return selectedIndexes;
1019
+ }
1020
+ function diversifyCandidates(targets) {
1021
+ if (targets.length <= RERANK_CANDIDATE_LIMIT) {
1022
+ return [...targets];
1023
+ }
1024
+ const orderedIndexes = [];
1025
+ const selectedIndexes = new Set();
1026
+ const pushSelectedIndexes = (indexes) => {
1027
+ for (const index of indexes) {
1028
+ if (selectedIndexes.has(index)) {
1029
+ continue;
1030
+ }
1031
+ selectedIndexes.add(index);
1032
+ orderedIndexes.push(index);
1033
+ if (orderedIndexes.length >= RERANK_CANDIDATE_LIMIT) {
1034
+ break;
1035
+ }
1036
+ }
1037
+ };
1038
+ pushSelectedIndexes(collectBucketedCandidateIndexes(targets, {
1039
+ totalLimit: FORM_BUCKET_RESERVE_LIMIT,
1040
+ perBucketLimit: FORM_BUCKET_RESERVE_PER_BUCKET,
1041
+ bucketKeyOf: (target) => candidateBucketKey(target, { preferFormBucket: true }),
1042
+ predicate: (target) => Boolean(formBucketKey(target)) && isPrimaryFormControlTarget(target),
1043
+ priorityOf: primaryFormTargetPriority,
1044
+ }));
1045
+ pushSelectedIndexes(collectBucketedCandidateIndexes(targets, {
1046
+ totalLimit: SCOPE_BUCKET_RESERVE_LIMIT,
1047
+ perBucketLimit: SCOPE_BUCKET_RESERVE_PER_BUCKET,
1048
+ bucketKeyOf: (target) => candidateBucketKey(target),
1049
+ predicate: isHighSignalScopeCandidate,
1050
+ priorityOf: scopeCandidatePriority,
1051
+ }));
1052
+ pushSelectedIndexes(collectBucketedCandidateIndexes(targets, {
1053
+ totalLimit: IFRAME_BUCKET_RESERVE_LIMIT,
1054
+ perBucketLimit: IFRAME_BUCKET_RESERVE_PER_BUCKET,
1055
+ bucketKeyOf: (target) => candidateBucketKey(target),
1056
+ predicate: (target) => Boolean(target.framePath?.length) && isFieldLikeTarget(target),
1057
+ }));
1058
+ for (let index = 0; index < targets.length && orderedIndexes.length < RERANK_CANDIDATE_LIMIT; index += 1) {
1059
+ if (selectedIndexes.has(index)) {
1060
+ continue;
1061
+ }
1062
+ selectedIndexes.add(index);
1063
+ orderedIndexes.push(index);
1064
+ }
1065
+ return orderedIndexes.map((index) => targets[index]).slice(0, RERANK_CANDIDATE_LIMIT);
1066
+ }
1067
+ function compactContextValue(value, maxLength = 80) {
1068
+ const normalized = value?.replace(/\s+/g, ' ').trim();
1069
+ if (!normalized) {
1070
+ return undefined;
1071
+ }
1072
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized;
1073
+ }
1074
+ function compactCandidateContext(target) {
1075
+ const context = target.context;
1076
+ if (!context) {
1077
+ return undefined;
1078
+ }
1079
+ const compacted = {};
1080
+ const push = (key, value) => {
1081
+ const compactedValue = compactContextValue(value);
1082
+ if (!compactedValue) {
1083
+ return;
1084
+ }
1085
+ compacted[key] = compactedValue;
1086
+ };
1087
+ push('item', context.item?.label ?? context.item?.text);
1088
+ push('group', context.group?.label ?? context.group?.text);
1089
+ push('container', context.container?.label ?? context.container?.text);
1090
+ push('landmark', context.landmark?.label ?? context.landmark?.text);
1091
+ push('hint', context.hintText);
1092
+ return Object.keys(compacted).length > 0 ? compacted : undefined;
1093
+ }
1094
+ function normalizedGoalText(value) {
1095
+ return value.replace(/\s+/g, ' ').trim().toLowerCase();
1096
+ }
1097
+ function goalNeedsScopeCandidates(goal) {
1098
+ const normalizedGoal = normalizedGoalText(goal);
1099
+ if (!normalizedGoal) {
1100
+ return true;
1101
+ }
1102
+ const scopeHints = [
1103
+ 'form',
1104
+ 'forms',
1105
+ 'field',
1106
+ 'fields',
1107
+ 'control',
1108
+ 'controls',
1109
+ 'dialog',
1110
+ 'modal',
1111
+ 'popup',
1112
+ 'widget',
1113
+ 'section',
1114
+ 'panel',
1115
+ 'menu',
1116
+ 'listbox',
1117
+ 'dropdown',
1118
+ 'calendar',
1119
+ 'datepicker',
1120
+ 'card',
1121
+ 'results',
1122
+ 'scope',
1123
+ 'region',
1124
+ 'surface',
1125
+ 'форма',
1126
+ 'форму',
1127
+ 'поля',
1128
+ 'поля ввода',
1129
+ 'контрол',
1130
+ 'диалог',
1131
+ 'модал',
1132
+ 'попап',
1133
+ 'виджет',
1134
+ 'секци',
1135
+ 'панел',
1136
+ 'меню',
1137
+ 'список',
1138
+ 'выпада',
1139
+ 'календар',
1140
+ 'карточ',
1141
+ 'результат',
1142
+ 'область',
1143
+ 'регион',
1144
+ ];
1145
+ return scopeHints.some((hint) => normalizedGoal.includes(hint));
1146
+ }
1147
+ function relevantTargetsForGoal(goal, targets) {
1148
+ if (goalNeedsScopeCandidates(goal)) {
1149
+ return [...targets];
1150
+ }
1151
+ const directTargets = targets.filter((target) => !isScopeLikeCandidate(target));
1152
+ return directTargets.length > 0 ? directTargets : [...targets];
1153
+ }
1154
+ function buildSurfaceSummaryLine(target, id) {
1155
+ const parts = [`${id}`];
1156
+ const context = compactCandidateContext(target);
1157
+ const surfaceKind = target.surfaceKind ??
1158
+ target.context?.container?.kind ??
1159
+ target.context?.group?.kind ??
1160
+ target.kind;
1161
+ const surfaceLabel = target.surfaceLabel ??
1162
+ target.context?.container?.label ??
1163
+ target.context?.group?.label ??
1164
+ target.context?.landmark?.label ??
1165
+ target.label;
1166
+ if (surfaceKind) {
1167
+ parts.push(`kind=${JSON.stringify(surfaceKind)}`);
1168
+ }
1169
+ if (surfaceLabel) {
1170
+ parts.push(`label=${JSON.stringify(surfaceLabel)}`);
1171
+ }
1172
+ if (typeof target.surfacePriority === 'number') {
1173
+ parts.push(`priority=${JSON.stringify(target.surfacePriority)}`);
1174
+ }
1175
+ if (target.framePath?.length) {
1176
+ parts.push(`framePath=${JSON.stringify(target.framePath)}`);
1177
+ }
1178
+ if (context) {
1179
+ parts.push(`context=${JSON.stringify(context)}`);
1180
+ }
1181
+ return parts.join(' | ');
1182
+ }
1183
+ function buildSurfaceSummaries(targets) {
1184
+ const summaries = [];
1185
+ const summaryIdBySurfaceKey = new Map();
1186
+ for (const target of targets) {
1187
+ const surfaceKey = surfaceIdentityOf(target);
1188
+ if (!surfaceKey || summaryIdBySurfaceKey.has(surfaceKey)) {
1189
+ continue;
1190
+ }
1191
+ const id = `s${summaries.length + 1}`;
1192
+ summaryIdBySurfaceKey.set(surfaceKey, id);
1193
+ summaries.push({
1194
+ id,
1195
+ line: buildSurfaceSummaryLine(target, id),
1196
+ });
1197
+ }
1198
+ return { summaries, summaryIdBySurfaceKey };
1199
+ }
1200
+ function buildCompactCandidateSummary(target, index, surfaceSummaryIdBySurfaceKey) {
1201
+ const parts = [`id=c${index + 1}`];
1202
+ const compactContext = compactCandidateContext(target);
1203
+ const goalLabel = goalLabelOf(target);
1204
+ const goalAliases = goalAliasesOf(target).map((value) => compactContextValue(value, 120) ?? value);
1205
+ const surfaceSummaryId = surfaceIdentityOf(target)
1206
+ ? surfaceSummaryIdBySurfaceKey?.get(surfaceIdentityOf(target))
1207
+ : undefined;
1208
+ if (target.kind)
1209
+ parts.push(`kind=${JSON.stringify(target.kind)}`);
1210
+ if (target.role)
1211
+ parts.push(`role=${JSON.stringify(target.role)}`);
1212
+ if (goalLabel)
1213
+ parts.push(`label=${JSON.stringify(goalLabel)}`);
1214
+ if (goalAliases.length)
1215
+ parts.push(`aliases=${JSON.stringify(goalAliases)}`);
1216
+ if (target.allowedActions?.length) {
1217
+ parts.push(`actions=${JSON.stringify(target.allowedActions)}`);
1218
+ }
1219
+ if (target.acceptancePolicy) {
1220
+ parts.push(`policy=${JSON.stringify(target.acceptancePolicy)}`);
1221
+ }
1222
+ if (target.controlFamily) {
1223
+ parts.push(`family=${JSON.stringify(target.controlFamily)}`);
1224
+ }
1225
+ if (target.controlsSurfaceSelector) {
1226
+ parts.push('controlsPopup=true');
1227
+ }
1228
+ if (target.capability && target.capability !== 'actionable') {
1229
+ parts.push(`capability=${JSON.stringify(target.capability)}`);
1230
+ }
1231
+ if (surfaceSummaryId) {
1232
+ parts.push(`surface=${surfaceSummaryId}`);
1233
+ }
1234
+ if (target.structure)
1235
+ parts.push(`structure=${JSON.stringify(target.structure)}`);
1236
+ if (target.placeholder)
1237
+ parts.push(`placeholder=${JSON.stringify(target.placeholder)}`);
1238
+ if (target.states)
1239
+ parts.push(`state=${JSON.stringify(target.states)}`);
1240
+ if (target.framePath?.length)
1241
+ parts.push(`framePath=${JSON.stringify(target.framePath)}`);
1242
+ if (compactContext)
1243
+ parts.push(`context=${JSON.stringify(compactContext)}`);
1244
+ return parts.join(' | ');
1245
+ }
1246
+ function normalizeCandidateId(value) {
1247
+ return value.trim().toLowerCase();
1248
+ }
1249
+ export async function rerankDomTargetsForGoal(instruction, targets, options = {}) {
1250
+ if (targets.length === 0) {
1251
+ return [];
1252
+ }
1253
+ const relevantTargets = relevantTargetsForGoal(instruction, targets);
1254
+ options.session &&
1255
+ recordPayloadBudget(options.session, {
1256
+ observeRerankCandidatesSeen: targets.length,
1257
+ });
1258
+ const snapshotKey = options.session ? semanticObserveSnapshotKey(relevantTargets) : undefined;
1259
+ const goalKey = snapshotKey ? semanticObserveGoalKey(instruction) : undefined;
1260
+ const cachedCandidates = snapshotKey && goalKey
1261
+ ? loadCachedGoalShortlist(options.session, snapshotKey, goalKey, relevantTargets)
1262
+ : null;
1263
+ let retrievedCandidates;
1264
+ if (cachedCandidates) {
1265
+ retrievedCandidates = cachedCandidates;
1266
+ }
1267
+ else if (relevantTargets.length > RERANK_CANDIDATE_LIMIT) {
1268
+ let retrievalEntities = snapshotKey
1269
+ ? loadCachedRetrievalEntities(options.session, snapshotKey, relevantTargets)
1270
+ : null;
1271
+ if (!retrievalEntities) {
1272
+ retrievalEntities = buildGoalRetrievalEntities(relevantTargets);
1273
+ if (snapshotKey) {
1274
+ saveCachedRetrievalEntities(options.session, snapshotKey, relevantTargets, retrievalEntities);
1275
+ }
1276
+ }
1277
+ retrievedCandidates = expandRetrievalEntitiesToCandidates(relevantTargets, preselectRetrievalEntities(instruction, retrievalEntities));
1278
+ }
1279
+ else {
1280
+ retrievedCandidates = [...relevantTargets];
1281
+ }
1282
+ const candidates = diversifyCandidates(retrievedCandidates.length > 0 ? retrievedCandidates : [...relevantTargets]);
1283
+ if (!cachedCandidates && snapshotKey && goalKey) {
1284
+ saveCachedGoalShortlist(options.session, snapshotKey, goalKey, relevantTargets, candidates);
1285
+ }
1286
+ const { summaries: surfaceSummaries, summaryIdBySurfaceKey } = buildSurfaceSummaries(candidates);
1287
+ options.session &&
1288
+ recordPayloadBudget(options.session, {
1289
+ observeRerankCandidatesSent: candidates.length,
1290
+ });
1291
+ let client = null;
1292
+ try {
1293
+ client = tryCreateAgentbrowseAssistiveLlmClient({ session: options.session });
1294
+ }
1295
+ catch {
1296
+ client = null;
1297
+ }
1298
+ if (!client) {
1299
+ return candidates.slice(0, Math.min(8, candidates.length));
1300
+ }
1301
+ const prompt = [
1302
+ 'You are choosing from already discovered visible webpage candidates.',
1303
+ 'Select only candidate IDs that directly satisfy the goal.',
1304
+ 'A disclosure trigger with a current-state label can still directly satisfy sort, filter, view-switch, or picker goals when opening it is the immediate next step.',
1305
+ 'Prefer direct actionable controls over surrounding regions unless the goal explicitly asks for a form, region, widget, or set of controls.',
1306
+ 'Use owning surface, compact context, explicit state, and structure metadata to disambiguate similar labels.',
1307
+ 'For input/value goals, prefer the directly editable field or primary picker trigger over wrappers or mirrored summary content.',
1308
+ 'For structured-grid targets such as dates or seats, use row/column/zone/cell metadata and state.',
1309
+ 'An exact disabled or readonly field can still be relevant when the goal refers to that specific field.',
1310
+ 'Do not invent IDs. Return an empty list if nothing clearly matches.',
1311
+ '',
1312
+ `Goal: ${instruction}`,
1313
+ '',
1314
+ ...(surfaceSummaries.length > 0
1315
+ ? ['Surfaces:', ...surfaceSummaries.map((entry) => entry.line), '']
1316
+ : []),
1317
+ 'Candidates:',
1318
+ ...candidates.map((target, index) => buildCompactCandidateSummary(target, index, summaryIdBySurfaceKey)),
1319
+ ].join('\n');
1320
+ const result = await client.createChatCompletion({
1321
+ logger: () => { },
1322
+ options: {
1323
+ messages: [{ role: 'user', content: prompt }],
1324
+ response_model: {
1325
+ name: 'Observation',
1326
+ schema: rerankSchema,
1327
+ },
1328
+ },
1329
+ });
1330
+ if (options.session) {
1331
+ recordLlmUsage(options.session, {
1332
+ purpose: 'browse.observe',
1333
+ usage: result.usage,
1334
+ inputChars: prompt.length,
1335
+ });
1336
+ }
1337
+ const selectedIds = new Set(result.data.matches.map((match) => normalizeCandidateId(match.candidateId)));
1338
+ return candidates.filter((_, index) => selectedIds.has(`c${index + 1}`));
1339
+ }