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.
- package/dist/src/browser/connection-utils.d.ts +48 -0
- package/dist/src/browser/connection-utils.d.ts.map +1 -0
- package/dist/src/browser/connection-utils.js +129 -0
- package/dist/src/browser/connection-utils.js.map +1 -0
- package/dist/src/browser/index.d.ts +3 -1
- package/dist/src/browser/index.d.ts.map +1 -1
- package/dist/src/browser/index.js +2 -1
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/browser/session-manager.d.ts +1 -89
- package/dist/src/browser/session-manager.d.ts.map +1 -1
- package/dist/src/browser/session-manager.js +1 -116
- package/dist/src/browser/session-manager.js.map +1 -1
- package/dist/src/browser/session-manager.types.d.ts +90 -0
- package/dist/src/browser/session-manager.types.d.ts.map +1 -0
- package/dist/src/browser/session-manager.types.js +7 -0
- package/dist/src/browser/session-manager.types.js.map +1 -0
- package/dist/src/form/constraint-extraction.d.ts +31 -0
- package/dist/src/form/constraint-extraction.d.ts.map +1 -0
- package/dist/src/form/constraint-extraction.js +110 -0
- package/dist/src/form/constraint-extraction.js.map +1 -0
- package/dist/src/form/field-extractor.d.ts.map +1 -1
- package/dist/src/form/field-extractor.js +3 -444
- package/dist/src/form/field-extractor.js.map +1 -1
- package/dist/src/form/field-state-extractor.d.ts +22 -0
- package/dist/src/form/field-state-extractor.d.ts.map +1 -0
- package/dist/src/form/field-state-extractor.js +55 -0
- package/dist/src/form/field-state-extractor.js.map +1 -0
- package/dist/src/form/form-actions.d.ts +45 -0
- package/dist/src/form/form-actions.d.ts.map +1 -0
- package/dist/src/form/form-actions.js +108 -0
- package/dist/src/form/form-actions.js.map +1 -0
- package/dist/src/form/form-detector.d.ts +0 -36
- package/dist/src/form/form-detector.d.ts.map +1 -1
- package/dist/src/form/form-detector.js +11 -376
- package/dist/src/form/form-detector.js.map +1 -1
- package/dist/src/form/input-clustering.d.ts +15 -0
- package/dist/src/form/input-clustering.d.ts.map +1 -0
- package/dist/src/form/input-clustering.js +61 -0
- package/dist/src/form/input-clustering.js.map +1 -0
- package/dist/src/form/intent-inference.d.ts +28 -0
- package/dist/src/form/intent-inference.d.ts.map +1 -0
- package/dist/src/form/intent-inference.js +137 -0
- package/dist/src/form/intent-inference.js.map +1 -0
- package/dist/src/form/purpose-inference.d.ts +50 -0
- package/dist/src/form/purpose-inference.d.ts.map +1 -0
- package/dist/src/form/purpose-inference.js +313 -0
- package/dist/src/form/purpose-inference.js.map +1 -0
- package/dist/src/form/submit-detection.d.ts +36 -0
- package/dist/src/form/submit-detection.d.ts.map +1 -0
- package/dist/src/form/submit-detection.js +101 -0
- package/dist/src/form/submit-detection.js.map +1 -0
- package/dist/src/form/types.d.ts +2 -2
- package/dist/src/index.js +65 -48
- package/dist/src/index.js.map +1 -1
- package/dist/src/observation/observation-accumulator.d.ts +1 -1
- package/dist/src/observation/observation-accumulator.js +1 -1
- package/dist/src/observation/observer-script.d.ts +1 -1
- package/dist/src/observation/observer-script.d.ts.map +1 -1
- package/dist/src/observation/observer-script.js +129 -7
- package/dist/src/observation/observer-script.js.map +1 -1
- package/dist/src/query/disambiguation.d.ts +18 -0
- package/dist/src/query/disambiguation.d.ts.map +1 -0
- package/dist/src/query/disambiguation.js +123 -0
- package/dist/src/query/disambiguation.js.map +1 -0
- package/dist/src/query/fuzzy-match.d.ts +17 -0
- package/dist/src/query/fuzzy-match.d.ts.map +1 -0
- package/dist/src/query/fuzzy-match.js +34 -0
- package/dist/src/query/fuzzy-match.js.map +1 -0
- package/dist/src/query/index.d.ts +3 -0
- package/dist/src/query/index.d.ts.map +1 -1
- package/dist/src/query/index.js +6 -0
- package/dist/src/query/index.js.map +1 -1
- package/dist/src/query/query-engine.d.ts +0 -35
- package/dist/src/query/query-engine.d.ts.map +1 -1
- package/dist/src/query/query-engine.js +9 -309
- package/dist/src/query/query-engine.js.map +1 -1
- package/dist/src/query/scoring.d.ts +52 -0
- package/dist/src/query/scoring.d.ts.map +1 -0
- package/dist/src/query/scoring.js +162 -0
- package/dist/src/query/scoring.js.map +1 -0
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/mcp-server.js +29 -1
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/snapshot/element-resolver.d.ts +50 -18
- package/dist/src/snapshot/element-resolver.d.ts.map +1 -1
- package/dist/src/snapshot/element-resolver.js +180 -101
- package/dist/src/snapshot/element-resolver.js.map +1 -1
- package/dist/src/snapshot/extractors/ax-extractor.d.ts +1 -1
- package/dist/src/snapshot/extractors/ax-extractor.d.ts.map +1 -1
- package/dist/src/snapshot/extractors/ax-extractor.js +4 -1
- package/dist/src/snapshot/extractors/ax-extractor.js.map +1 -1
- package/dist/src/snapshot/extractors/index.d.ts +1 -1
- package/dist/src/snapshot/extractors/index.d.ts.map +1 -1
- package/dist/src/snapshot/extractors/index.js +1 -1
- package/dist/src/snapshot/extractors/index.js.map +1 -1
- package/dist/src/snapshot/extractors/region-resolver.d.ts.map +1 -1
- package/dist/src/snapshot/extractors/region-resolver.js +8 -0
- package/dist/src/snapshot/extractors/region-resolver.js.map +1 -1
- package/dist/src/snapshot/extractors/types.d.ts +8 -0
- package/dist/src/snapshot/extractors/types.d.ts.map +1 -1
- package/dist/src/snapshot/extractors/types.js +16 -0
- package/dist/src/snapshot/extractors/types.js.map +1 -1
- package/dist/src/snapshot/frame-context.d.ts +68 -0
- package/dist/src/snapshot/frame-context.d.ts.map +1 -0
- package/dist/src/snapshot/frame-context.js +131 -0
- package/dist/src/snapshot/frame-context.js.map +1 -0
- package/dist/src/snapshot/heading-index.d.ts +28 -0
- package/dist/src/snapshot/heading-index.d.ts.map +1 -0
- package/dist/src/snapshot/heading-index.js +108 -0
- package/dist/src/snapshot/heading-index.js.map +1 -0
- package/dist/src/snapshot/index.d.ts +5 -3
- package/dist/src/snapshot/index.d.ts.map +1 -1
- package/dist/src/snapshot/index.js +3 -2
- package/dist/src/snapshot/index.js.map +1 -1
- package/dist/src/snapshot/kind-mapping.d.ts +30 -0
- package/dist/src/snapshot/kind-mapping.d.ts.map +1 -0
- package/dist/src/snapshot/kind-mapping.js +114 -0
- package/dist/src/snapshot/kind-mapping.js.map +1 -0
- package/dist/src/snapshot/node-filter.d.ts +31 -0
- package/dist/src/snapshot/node-filter.d.ts.map +1 -0
- package/dist/src/snapshot/node-filter.js +137 -0
- package/dist/src/snapshot/node-filter.js.map +1 -0
- package/dist/src/snapshot/node-synthesizer.d.ts +62 -0
- package/dist/src/snapshot/node-synthesizer.d.ts.map +1 -0
- package/dist/src/snapshot/node-synthesizer.js +185 -0
- package/dist/src/snapshot/node-synthesizer.js.map +1 -0
- package/dist/src/snapshot/snapshot-compiler.d.ts +2 -36
- package/dist/src/snapshot/snapshot-compiler.d.ts.map +1 -1
- package/dist/src/snapshot/snapshot-compiler.js +28 -520
- package/dist/src/snapshot/snapshot-compiler.js.map +1 -1
- package/dist/src/snapshot/snapshot.types.d.ts +7 -2
- package/dist/src/snapshot/snapshot.types.d.ts.map +1 -1
- package/dist/src/snapshot/snapshot.types.js +9 -0
- package/dist/src/snapshot/snapshot.types.js.map +1 -1
- package/dist/src/state/actionables-filter.d.ts +5 -0
- package/dist/src/state/actionables-filter.d.ts.map +1 -1
- package/dist/src/state/actionables-filter.js +22 -3
- package/dist/src/state/actionables-filter.js.map +1 -1
- package/dist/src/state/diff-engine.js +3 -3
- package/dist/src/state/diff-engine.js.map +1 -1
- package/dist/src/state/element-registry.d.ts.map +1 -1
- package/dist/src/state/element-registry.js +6 -4
- package/dist/src/state/element-registry.js.map +1 -1
- package/dist/src/state/hash-utils.d.ts +24 -0
- package/dist/src/state/hash-utils.d.ts.map +1 -0
- package/dist/src/state/hash-utils.js +41 -0
- package/dist/src/state/hash-utils.js.map +1 -0
- package/dist/src/state/layer-detector.d.ts.map +1 -1
- package/dist/src/state/layer-detector.js +15 -286
- package/dist/src/state/layer-detector.js.map +1 -1
- package/dist/src/state/layer-detectors/drawer-detector.d.ts +32 -0
- package/dist/src/state/layer-detectors/drawer-detector.d.ts.map +1 -0
- package/dist/src/state/layer-detectors/drawer-detector.js +96 -0
- package/dist/src/state/layer-detectors/drawer-detector.js.map +1 -0
- package/dist/src/state/layer-detectors/index.d.ts +10 -0
- package/dist/src/state/layer-detectors/index.d.ts.map +1 -0
- package/dist/src/state/layer-detectors/index.js +10 -0
- package/dist/src/state/layer-detectors/index.js.map +1 -0
- package/dist/src/state/layer-detectors/modal-detector.d.ts +30 -0
- package/dist/src/state/layer-detectors/modal-detector.d.ts.map +1 -0
- package/dist/src/state/layer-detectors/modal-detector.js +127 -0
- package/dist/src/state/layer-detectors/modal-detector.js.map +1 -0
- package/dist/src/state/layer-detectors/popover-detector.d.ts +20 -0
- package/dist/src/state/layer-detectors/popover-detector.d.ts.map +1 -0
- package/dist/src/state/layer-detectors/popover-detector.js +76 -0
- package/dist/src/state/layer-detectors/popover-detector.js.map +1 -0
- package/dist/src/state/layer-detectors/toast-detector.d.ts +24 -0
- package/dist/src/state/layer-detectors/toast-detector.d.ts.map +1 -0
- package/dist/src/state/layer-detectors/toast-detector.js +48 -0
- package/dist/src/state/layer-detectors/toast-detector.js.map +1 -0
- package/dist/src/state/region-mapping.d.ts +13 -0
- package/dist/src/state/region-mapping.d.ts.map +1 -0
- package/dist/src/state/region-mapping.js +25 -0
- package/dist/src/state/region-mapping.js.map +1 -0
- package/dist/src/state/state-manager.d.ts.map +1 -1
- package/dist/src/state/state-manager.js +8 -192
- package/dist/src/state/state-manager.js.map +1 -1
- package/dist/src/state/state-renderer.d.ts.map +1 -1
- package/dist/src/state/state-renderer.js +16 -2
- package/dist/src/state/state-renderer.js.map +1 -1
- package/dist/src/state/types.d.ts +8 -4
- package/dist/src/state/types.d.ts.map +1 -1
- package/dist/src/state/url-sanitization.d.ts +22 -0
- package/dist/src/state/url-sanitization.d.ts.map +1 -0
- package/dist/src/state/url-sanitization.js +60 -0
- package/dist/src/state/url-sanitization.js.map +1 -0
- package/dist/src/state/value-masking.d.ts +36 -0
- package/dist/src/state/value-masking.d.ts.map +1 -0
- package/dist/src/state/value-masking.js +86 -0
- package/dist/src/state/value-masking.js.map +1 -0
- package/dist/src/tools/action-context.d.ts +60 -0
- package/dist/src/tools/action-context.d.ts.map +1 -0
- package/dist/src/tools/action-context.js +78 -0
- package/dist/src/tools/action-context.js.map +1 -0
- package/dist/src/tools/action-stabilization.d.ts +48 -0
- package/dist/src/tools/action-stabilization.d.ts.map +1 -0
- package/dist/src/tools/action-stabilization.js +87 -0
- package/dist/src/tools/action-stabilization.js.map +1 -0
- package/dist/src/tools/browser-tools.d.ts +8 -146
- package/dist/src/tools/browser-tools.d.ts.map +1 -1
- package/dist/src/tools/browser-tools.js +13 -689
- package/dist/src/tools/browser-tools.js.map +1 -1
- package/dist/src/tools/canvas-tools.d.ts +32 -0
- package/dist/src/tools/canvas-tools.d.ts.map +1 -0
- package/dist/src/tools/canvas-tools.js +370 -0
- package/dist/src/tools/canvas-tools.js.map +1 -0
- package/dist/src/tools/effect-tracker.d.ts +25 -0
- package/dist/src/tools/effect-tracker.d.ts.map +1 -0
- package/dist/src/tools/effect-tracker.js +69 -0
- package/dist/src/tools/effect-tracker.js.map +1 -0
- package/dist/src/tools/execute-action.d.ts +1 -31
- package/dist/src/tools/execute-action.d.ts.map +1 -1
- package/dist/src/tools/execute-action.js +7 -276
- package/dist/src/tools/execute-action.js.map +1 -1
- package/dist/src/tools/form-tools.d.ts +4 -6
- package/dist/src/tools/form-tools.d.ts.map +1 -1
- package/dist/src/tools/form-tools.js +10 -42
- package/dist/src/tools/form-tools.js.map +1 -1
- package/dist/src/tools/index.d.ts +6 -4
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +21 -10
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/interaction-tools.d.ts +46 -0
- package/dist/src/tools/interaction-tools.d.ts.map +1 -0
- package/dist/src/tools/interaction-tools.js +138 -0
- package/dist/src/tools/interaction-tools.js.map +1 -0
- package/dist/src/tools/navigation-detection.d.ts +31 -0
- package/dist/src/tools/navigation-detection.d.ts.map +1 -0
- package/dist/src/tools/navigation-detection.js +46 -0
- package/dist/src/tools/navigation-detection.js.map +1 -0
- package/dist/src/tools/navigation-tools.d.ts +57 -0
- package/dist/src/tools/navigation-tools.d.ts.map +1 -0
- package/dist/src/tools/navigation-tools.js +178 -0
- package/dist/src/tools/navigation-tools.js.map +1 -0
- package/dist/src/tools/observation-tools.d.ts +53 -0
- package/dist/src/tools/observation-tools.d.ts.map +1 -0
- package/dist/src/tools/observation-tools.js +247 -0
- package/dist/src/tools/observation-tools.js.map +1 -0
- package/dist/src/tools/response-builder.js +2 -2
- package/dist/src/tools/response-builder.js.map +1 -1
- package/dist/src/tools/stale-element-retry.d.ts +37 -0
- package/dist/src/tools/stale-element-retry.d.ts.map +1 -0
- package/dist/src/tools/stale-element-retry.js +68 -0
- package/dist/src/tools/stale-element-retry.js.map +1 -0
- package/dist/src/tools/state-manager-registry.d.ts +26 -0
- package/dist/src/tools/state-manager-registry.d.ts.map +1 -0
- package/dist/src/tools/state-manager-registry.js +39 -0
- package/dist/src/tools/state-manager-registry.js.map +1 -0
- package/dist/src/tools/tool-context.d.ts +53 -0
- package/dist/src/tools/tool-context.d.ts.map +1 -0
- package/dist/src/tools/tool-context.js +119 -0
- package/dist/src/tools/tool-context.js.map +1 -0
- package/dist/src/tools/tool-result.types.d.ts +16 -1
- package/dist/src/tools/tool-result.types.d.ts.map +1 -1
- package/dist/src/tools/tool-result.types.js +11 -0
- package/dist/src/tools/tool-result.types.js.map +1 -1
- package/dist/src/tools/tool-schemas.d.ts +358 -146
- package/dist/src/tools/tool-schemas.d.ts.map +1 -1
- package/dist/src/tools/tool-schemas.js +142 -19
- package/dist/src/tools/tool-schemas.js.map +1 -1
- package/dist/src/tools/viewport-tools.d.ts +36 -0
- package/dist/src/tools/viewport-tools.d.ts.map +1 -0
- package/dist/src/tools/viewport-tools.js +105 -0
- package/dist/src/tools/viewport-tools.js.map +1 -0
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -29,11 +29,11 @@ export function getSessionStore() {
|
|
|
29
29
|
export function getSessionBinding() {
|
|
30
30
|
return sessionBinding;
|
|
31
31
|
}
|
|
32
|
-
import {
|
|
32
|
+
import { initializeToolContext,
|
|
33
33
|
// Tool handlers
|
|
34
|
-
listPages, closePage, closeSession, navigate, goBack, goForward, reload, captureSnapshot, findElements, getNodeDetails, scrollElementIntoView, scrollPage, click, type, press, select, hover, getFormUnderstanding, getFieldContext, takeScreenshot,
|
|
34
|
+
listPages, closePage, closeSession, navigate, goBack, goForward, reload, captureSnapshot, findElements, getNodeDetails, scrollElementIntoView, scrollPage, click, type, press, select, hover, drag, wheel, getFormUnderstanding, getFieldContext, takeScreenshot, inspectCanvas,
|
|
35
35
|
// Input schemas only (all outputs are XML strings now)
|
|
36
|
-
ListPagesInputSchema, ClosePageInputSchema, CloseSessionInputSchema, NavigateInputSchema, GoBackInputSchema, GoForwardInputSchema, ReloadInputSchema, CaptureSnapshotInputSchema, FindElementsInputSchema, GetNodeDetailsInputSchema, ScrollElementIntoViewInputSchemaBase, ScrollPageInputSchema, ClickInputSchemaBase, TypeInputSchemaBase, PressInputSchema, SelectInputSchemaBase, HoverInputSchemaBase, GetFormUnderstandingInputSchema, GetFieldContextInputSchema, TakeScreenshotInputSchemaBase, } from './tools/index.js';
|
|
36
|
+
ListPagesInputSchema, ClosePageInputSchema, CloseSessionInputSchema, NavigateInputSchema, GoBackInputSchema, GoForwardInputSchema, ReloadInputSchema, CaptureSnapshotInputSchema, FindElementsInputSchema, GetNodeDetailsInputSchema, ScrollElementIntoViewInputSchemaBase, ScrollPageInputSchema, ClickInputSchemaBase, TypeInputSchemaBase, PressInputSchema, SelectInputSchemaBase, HoverInputSchemaBase, DragInputSchemaBase, WheelInputSchemaBase, GetFormUnderstandingInputSchema, GetFieldContextInputSchema, TakeScreenshotInputSchemaBase, InspectCanvasInputSchemaBase, } from './tools/index.js';
|
|
37
37
|
/**
|
|
38
38
|
* Wrap a tool handler with lazy browser initialization.
|
|
39
39
|
* Works with both sync and async handlers - sync return values are automatically
|
|
@@ -107,14 +107,13 @@ function initializeServer() {
|
|
|
107
107
|
});
|
|
108
108
|
// Initialize session manager and tools
|
|
109
109
|
const session = getSessionManager();
|
|
110
|
-
|
|
111
|
-
initializeFormTools(session);
|
|
110
|
+
initializeToolContext(session);
|
|
112
111
|
// ============================================================================
|
|
113
112
|
// SESSION TOOLS
|
|
114
113
|
// ============================================================================
|
|
115
114
|
server.registerTool('list_pages', {
|
|
116
115
|
title: 'List Pages',
|
|
117
|
-
description: 'List all open browser pages with their page_id, URL, and title.
|
|
116
|
+
description: 'List all open browser pages with their page_id, URL, and title.',
|
|
118
117
|
inputSchema: ListPagesInputSchema.shape,
|
|
119
118
|
}, withLazyInit(listPages, 'list_pages'));
|
|
120
119
|
server.registerTool('close_page', {
|
|
@@ -124,7 +123,7 @@ function initializeServer() {
|
|
|
124
123
|
}, withLazyInit(closePage, 'close_page'));
|
|
125
124
|
server.registerTool('close_session', {
|
|
126
125
|
title: 'Close Session',
|
|
127
|
-
description: 'Close the entire browser and clear all state.
|
|
126
|
+
description: 'Close the entire browser and clear all state.',
|
|
128
127
|
inputSchema: CloseSessionInputSchema.shape,
|
|
129
128
|
}, withLazyInit(closeSession, 'close_session'));
|
|
130
129
|
// ============================================================================
|
|
@@ -132,98 +131,116 @@ function initializeServer() {
|
|
|
132
131
|
// ============================================================================
|
|
133
132
|
server.registerTool('navigate', {
|
|
134
133
|
title: 'Navigate',
|
|
135
|
-
description: 'Go to a URL. Returns page snapshot with interactive elements.
|
|
134
|
+
description: 'Go to a URL. Returns page snapshot with interactive elements.',
|
|
136
135
|
inputSchema: NavigateInputSchema.shape,
|
|
137
136
|
}, withLazyInit(navigate, 'navigate'));
|
|
138
137
|
server.registerTool('go_back', {
|
|
139
138
|
title: 'Go Back',
|
|
140
|
-
description: 'Go back one page in browser history
|
|
139
|
+
description: 'Go back one page in browser history.',
|
|
141
140
|
inputSchema: GoBackInputSchema.shape,
|
|
142
141
|
}, withLazyInit(goBack, 'go_back'));
|
|
143
142
|
server.registerTool('go_forward', {
|
|
144
143
|
title: 'Go Forward',
|
|
145
|
-
description: 'Go forward one page in browser history.
|
|
144
|
+
description: 'Go forward one page in browser history.',
|
|
146
145
|
inputSchema: GoForwardInputSchema.shape,
|
|
147
146
|
}, withLazyInit(goForward, 'go_forward'));
|
|
148
147
|
server.registerTool('reload', {
|
|
149
148
|
title: 'Reload',
|
|
150
|
-
description: 'Refresh the current page.
|
|
149
|
+
description: 'Refresh the current page.',
|
|
151
150
|
inputSchema: ReloadInputSchema.shape,
|
|
152
151
|
}, withLazyInit(reload, 'reload'));
|
|
153
|
-
server.registerTool('
|
|
154
|
-
title: '
|
|
155
|
-
description: 'Re-capture the page state without performing any action. Use when the page may have changed on its own (timers, live updates, animations
|
|
152
|
+
server.registerTool('snapshot', {
|
|
153
|
+
title: 'Snapshot',
|
|
154
|
+
description: 'Re-capture the page state without performing any action. Use when the page may have changed on its own (timers, live updates, animations).',
|
|
156
155
|
inputSchema: CaptureSnapshotInputSchema.shape,
|
|
157
|
-
}, withLazyInit(captureSnapshot, '
|
|
156
|
+
}, withLazyInit(captureSnapshot, 'snapshot'));
|
|
158
157
|
// ============================================================================
|
|
159
158
|
// OBSERVATION TOOLS
|
|
160
159
|
// ============================================================================
|
|
161
|
-
server.registerTool('
|
|
162
|
-
title: 'Find
|
|
163
|
-
description: 'Search for interactive elements OR read page text content. Filter by `kind` (button, link, textbox), `label` (case-insensitive substring match), or `region` (header, main, footer).
|
|
160
|
+
server.registerTool('find', {
|
|
161
|
+
title: 'Find',
|
|
162
|
+
description: 'Search for interactive elements OR read page text content. Filter by `kind` (button, link, textbox, canvas), `label` (case-insensitive substring match), or `region` (header, main, footer).',
|
|
164
163
|
inputSchema: FindElementsInputSchema.shape,
|
|
165
|
-
}, withLazyInit(findElements, '
|
|
166
|
-
server.registerTool('
|
|
167
|
-
title: 'Get Element
|
|
168
|
-
description: 'Get complete details for one element: exact position, size, state, attributes.
|
|
164
|
+
}, withLazyInit(findElements, 'find'));
|
|
165
|
+
server.registerTool('get_element', {
|
|
166
|
+
title: 'Get Element',
|
|
167
|
+
description: 'Get complete details for one element: exact position, size, state, attributes.',
|
|
169
168
|
inputSchema: GetNodeDetailsInputSchema.shape,
|
|
170
|
-
}, withLazyInit(getNodeDetails, '
|
|
171
|
-
server.registerTool('
|
|
172
|
-
title: '
|
|
173
|
-
description: 'Capture a screenshot of the current page or a specific element.
|
|
169
|
+
}, withLazyInit(getNodeDetails, 'get_element'));
|
|
170
|
+
server.registerTool('screenshot', {
|
|
171
|
+
title: 'Screenshot',
|
|
172
|
+
description: 'Capture a screenshot of the current page or a specific element.',
|
|
174
173
|
inputSchema: TakeScreenshotInputSchemaBase.shape,
|
|
175
|
-
}, withLazyInit(takeScreenshot, '
|
|
174
|
+
}, withLazyInit(takeScreenshot, 'screenshot'));
|
|
176
175
|
// ============================================================================
|
|
177
176
|
// INTERACTION TOOLS
|
|
178
177
|
// ============================================================================
|
|
179
|
-
server.registerTool('
|
|
180
|
-
title: 'Scroll
|
|
181
|
-
description: 'Scroll until a specific element is visible in the viewport.
|
|
178
|
+
server.registerTool('scroll_to', {
|
|
179
|
+
title: 'Scroll To',
|
|
180
|
+
description: 'Scroll until a specific element is visible in the viewport.',
|
|
182
181
|
inputSchema: ScrollElementIntoViewInputSchemaBase.shape,
|
|
183
|
-
}, withLazyInit(scrollElementIntoView, '
|
|
184
|
-
server.registerTool('
|
|
185
|
-
title: 'Scroll
|
|
186
|
-
description: 'Scroll the viewport up or down by pixels.
|
|
182
|
+
}, withLazyInit(scrollElementIntoView, 'scroll_to'));
|
|
183
|
+
server.registerTool('scroll', {
|
|
184
|
+
title: 'Scroll',
|
|
185
|
+
description: 'Scroll the viewport up or down by pixels.',
|
|
187
186
|
inputSchema: ScrollPageInputSchema.shape,
|
|
188
|
-
}, withLazyInit(scrollPage, '
|
|
187
|
+
}, withLazyInit(scrollPage, 'scroll'));
|
|
189
188
|
server.registerTool('click', {
|
|
190
189
|
title: 'Click Element',
|
|
191
|
-
description: 'Click an element
|
|
190
|
+
description: 'Click an element or at viewport coordinates.',
|
|
192
191
|
inputSchema: ClickInputSchemaBase.shape,
|
|
193
192
|
}, withLazyInit(click, 'click'));
|
|
194
193
|
server.registerTool('type', {
|
|
195
194
|
title: 'Type Text',
|
|
196
|
-
description: 'Type text into an input field
|
|
195
|
+
description: 'Type text into an input field or text area.',
|
|
197
196
|
inputSchema: TypeInputSchemaBase.shape,
|
|
198
197
|
}, withLazyInit(type, 'type'));
|
|
199
198
|
server.registerTool('press', {
|
|
200
199
|
title: 'Press Key',
|
|
201
|
-
description: 'Press a keyboard key
|
|
200
|
+
description: 'Press a keyboard key with optional modifiers.',
|
|
202
201
|
inputSchema: PressInputSchema.shape,
|
|
203
202
|
}, withLazyInit(press, 'press'));
|
|
204
203
|
server.registerTool('select', {
|
|
205
204
|
title: 'Select Option',
|
|
206
|
-
description: 'Choose an option from a dropdown menu
|
|
205
|
+
description: 'Choose an option from a dropdown menu by value or visible text.',
|
|
207
206
|
inputSchema: SelectInputSchemaBase.shape,
|
|
208
207
|
}, withLazyInit(select, 'select'));
|
|
209
208
|
server.registerTool('hover', {
|
|
210
209
|
title: 'Hover Element',
|
|
211
|
-
description: 'Move mouse over an element without clicking.
|
|
210
|
+
description: 'Move mouse over an element without clicking. Triggers hover menus and tooltips.',
|
|
212
211
|
inputSchema: HoverInputSchemaBase.shape,
|
|
213
212
|
}, withLazyInit(hover, 'hover'));
|
|
213
|
+
server.registerTool('drag', {
|
|
214
|
+
title: 'Drag',
|
|
215
|
+
description: 'Drag from one point to another.',
|
|
216
|
+
inputSchema: DragInputSchemaBase.shape,
|
|
217
|
+
}, withLazyInit(drag, 'drag'));
|
|
218
|
+
server.registerTool('wheel', {
|
|
219
|
+
title: 'Wheel',
|
|
220
|
+
description: 'Dispatch a mouse wheel event at specific coordinates. Use for scroll-to-zoom (with Control modifier) or horizontal scrolling.',
|
|
221
|
+
inputSchema: WheelInputSchemaBase.shape,
|
|
222
|
+
}, withLazyInit(wheel, 'wheel'));
|
|
223
|
+
// ============================================================================
|
|
224
|
+
// CANVAS INSPECTION TOOLS
|
|
225
|
+
// ============================================================================
|
|
226
|
+
server.registerTool('inspect_canvas', {
|
|
227
|
+
title: 'Inspect Canvas',
|
|
228
|
+
description: 'Analyze a canvas element: auto-detect the rendering library, query its scene graph, and return an annotated screenshot with coordinate grid overlay.',
|
|
229
|
+
inputSchema: InspectCanvasInputSchemaBase.shape,
|
|
230
|
+
}, withLazyInit(inspectCanvas, 'inspect_canvas'));
|
|
214
231
|
// ============================================================================
|
|
215
232
|
// FORM UNDERSTANDING TOOLS
|
|
216
233
|
// ============================================================================
|
|
217
|
-
server.registerTool('
|
|
218
|
-
title: 'Get Form
|
|
219
|
-
description: 'Analyze all forms on the page: fields, required inputs, validation rules, and field dependencies.
|
|
234
|
+
server.registerTool('get_form', {
|
|
235
|
+
title: 'Get Form',
|
|
236
|
+
description: 'Analyze all forms on the page: fields, required inputs, validation rules, and field dependencies.',
|
|
220
237
|
inputSchema: GetFormUnderstandingInputSchema.shape,
|
|
221
|
-
}, withLazyInit(getFormUnderstanding, '
|
|
222
|
-
server.registerTool('
|
|
223
|
-
title: 'Get Field
|
|
224
|
-
description: 'Get detailed info about one form field:
|
|
238
|
+
}, withLazyInit(getFormUnderstanding, 'get_form'));
|
|
239
|
+
server.registerTool('get_field', {
|
|
240
|
+
title: 'Get Field',
|
|
241
|
+
description: 'Get detailed info about one form field: purpose, valid input formats, dependencies, and suggested values.',
|
|
225
242
|
inputSchema: GetFieldContextInputSchema.shape,
|
|
226
|
-
}, withLazyInit(getFieldContext, '
|
|
243
|
+
}, withLazyInit(getFieldContext, 'get_field'));
|
|
227
244
|
return server;
|
|
228
245
|
}
|
|
229
246
|
/**
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG;AAEH,OAAO,EAAE,uBAAuB,EAA0B,MAAM,wBAAwB,CAAC;AACzF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAsB,MAAM,qCAAqC,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;AAE3B,sDAAsD;AACtD,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;AAExC,2DAA2D;AAC3D,MAAM,aAAa,GAChB,OAAO,CAAC,GAAG,CAAC,cAAgC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAEtF,wCAAwC;AACxC,MAAM,cAAc,GAAG,IAAI,oBAAoB,CAAC,aAAa,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,cAAc,CAAC;AACxB,CAAC;AACD,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,7 +34,7 @@ export declare class ObservationAccumulator {
|
|
|
34
34
|
getObservations(page: Page, actionStartTime: number): Promise<ObservationGroups>;
|
|
35
35
|
/**
|
|
36
36
|
* Get accumulated observations without action context.
|
|
37
|
-
* Used by
|
|
37
|
+
* Used by snapshot tool to report accumulated changes.
|
|
38
38
|
*/
|
|
39
39
|
getAccumulatedObservations(page: Page): Promise<ObservationGroups>;
|
|
40
40
|
/**
|
|
@@ -102,7 +102,7 @@ export class ObservationAccumulator {
|
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Get accumulated observations without action context.
|
|
105
|
-
* Used by
|
|
105
|
+
* Used by snapshot tool to report accumulated changes.
|
|
106
106
|
*/
|
|
107
107
|
async getAccumulatedObservations(page) {
|
|
108
108
|
try {
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The script is returned as a string for page.evaluate().
|
|
17
17
|
*/
|
|
18
|
-
export declare const OBSERVATION_OBSERVER_SCRIPT = "\n(function() {\n // Prevent double-injection\n if (window.__observationAccumulator) return;\n\n const MAX_ENTRIES = 500;\n const MAX_TEXT_LENGTH = 200;\n const MAX_SHADOW_OBSERVERS = 50; // Limit to prevent performance issues\n // IMPORTANT: Must match SIGNIFICANCE_THRESHOLD in observation.types.ts\n const SIGNIFICANCE_THRESHOLD = 4;\n // Node type constant for shadow root parent check\n const DOCUMENT_FRAGMENT_NODE = 11;\n\n // Significance weights (must match server-side observation.types.ts)\n const WEIGHTS = {\n // Semantic signals (strongest)\n hasAlertRole: 3,\n hasAriaLive: 3,\n isDialog: 3,\n\n // Visual signals\n isFixedOrSticky: 2,\n hasHighZIndex: 1,\n coversSignificantViewport: 2,\n\n // Structural signals\n isBodyDirectChild: 1,\n containsInteractiveElements: 1,\n\n // New universal signals - work without ARIA\n isVisibleInViewport: 2, // Element is visible in viewport\n hasNonTrivialText: 1, // Has meaningful text content\n // Temporal signals computed later\n };\n\n // Shadow DOM tracking\n // Map: shadowRoot -> { observer, hostPath }\n const shadowObservers = new Map();\n\n // Track processed elements to avoid duplicate observations\n // WeakSet allows garbage collection of removed elements\n let processedElements = new WeakSet();\n\n /**\n * Generate a stable identifier for an element (for shadow path tracking).\n * Format: TAG#id or TAG.className or TAG[index]\n */\n function getElementIdentifier(el) {\n const tag = el.tagName.toLowerCase();\n if (el.id) {\n return tag + '#' + el.id;\n }\n // For custom elements, use the tag name (usually descriptive like 'my-toast')\n if (tag.includes('-')) {\n return tag;\n }\n // Fallback: use first class or just tag\n const className = el.className && typeof el.className === 'string'\n ? el.className.split(' ')[0]\n : '';\n if (className) {\n return tag + '.' + className;\n }\n return tag;\n }\n\n // Tags whose text content should be excluded from extraction\n const EXCLUDED_TEXT_TAGS = new Set(['STYLE', 'SCRIPT', 'NOSCRIPT', 'TEMPLATE', 'SVG']);\n\n /**\n * Get clean text content from an element, excluding CSS/JS content.\n * Uses TreeWalker to iterate only text nodes, skipping those inside\n * excluded tags (STYLE, SCRIPT, NOSCRIPT, TEMPLATE, SVG).\n *\n * @param {Element} el - The DOM element to extract text from\n * @param {number} maxLength - Maximum text length (truncates result)\n * @returns {string} Clean text content, space-joined, truncated to maxLength\n */\n function getCleanTextContent(el, maxLength) {\n // If element itself is an excluded tag, return empty\n if (EXCLUDED_TEXT_TAGS.has(el.tagName.toUpperCase())) {\n return '';\n }\n\n const walker = document.createTreeWalker(el, 4, { // NodeFilter.SHOW_TEXT = 4\n acceptNode: function(node) {\n let parent = node.parentElement;\n while (parent && parent !== el) {\n if (EXCLUDED_TEXT_TAGS.has(parent.tagName.toUpperCase())) {\n return 2; // FILTER_REJECT\n }\n parent = parent.parentElement;\n }\n return 1; // FILTER_ACCEPT\n }\n });\n\n const textParts = [];\n let totalLength = 0;\n let node;\n\n while ((node = walker.nextNode()) && totalLength < maxLength) {\n const text = node.nodeValue;\n if (text) {\n const trimmed = text.trim();\n if (trimmed) {\n textParts.push(trimmed);\n totalLength += trimmed.length;\n }\n }\n }\n\n return textParts.join(' ').substring(0, maxLength);\n }\n\n /**\n * Compute significance signals from element.\n * Uses universal web standards (ARIA, CSS positioning, DOM structure, visibility).\n */\n function computeSignals(el, shadowPath) {\n const role = el.getAttribute('role');\n const ariaLive = el.getAttribute('aria-live');\n const ariaModal = el.getAttribute('aria-modal');\n const tagName = el.tagName.toLowerCase();\n\n // Get computed style (may fail for detached elements)\n let style = null;\n let rect = null;\n try {\n style = getComputedStyle(el);\n rect = el.getBoundingClientRect();\n } catch (e) {\n // Element may be detached\n }\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Check if element is visible in viewport\n const isVisibleInViewport = rect && style &&\n rect.width > 0 && rect.height > 0 &&\n rect.bottom > 0 && rect.top < vh &&\n rect.right > 0 && rect.left < vw &&\n style.display !== 'none' &&\n style.visibility !== 'hidden' &&\n style.opacity !== '0';\n\n // Check for non-trivial text (at least 3 chars, not just whitespace)\n // Use short sample for signal check - excludes style/script content\n const text = getCleanTextContent(el, 100);\n const hasNonTrivialText = text.length >= 3;\n\n // For shadow DOM elements, isBodyDirectChild is false but we still want to capture them\n // Check if parent is a shadow root (which indicates top-level in shadow DOM)\n const isTopLevelInShadow = shadowPath && shadowPath.length > 0 &&\n el.parentNode && el.parentNode.nodeType === DOCUMENT_FRAGMENT_NODE;\n\n return {\n // Semantic signals\n hasAlertRole: ['alert', 'status', 'log', 'alertdialog'].includes(role),\n hasAriaLive: ariaLive === 'polite' || ariaLive === 'assertive',\n isDialog: role === 'dialog' || tagName === 'dialog' || ariaModal === 'true',\n\n // Visual signals\n isFixedOrSticky: style && (style.position === 'fixed' || style.position === 'sticky'),\n // Note: parseInt returns NaN for non-numeric values like \"auto\", which correctly fails the > 1000 check\n hasHighZIndex: style && parseInt(style.zIndex, 10) > 1000,\n coversSignificantViewport: rect && ((rect.width > vw * 0.5) || (rect.height > vh * 0.3)),\n\n // Structural signals - consider top-level shadow DOM elements as equivalent to body children\n isBodyDirectChild: el.parentElement === document.body || isTopLevelInShadow,\n containsInteractiveElements: el.querySelector('button, a, input, select, textarea') !== null,\n\n // Universal signals (work without ARIA)\n isVisibleInViewport: !!isVisibleInViewport,\n hasNonTrivialText: hasNonTrivialText,\n\n // Temporal signals (set by accumulator later)\n appearedAfterDelay: false,\n wasShortLived: false,\n };\n }\n\n /**\n * Calculate significance score from signals using weighted sum.\n * @param signals - The computed significance signals for an element\n * @returns The total significance score\n */\n function computeSignificance(signals) {\n let score = 0;\n for (const [key, weight] of Object.entries(WEIGHTS)) {\n if (signals[key]) score += weight;\n }\n return score;\n }\n\n /**\n * Capture a mutation entry from an element.\n * @param node - The DOM node\n * @param type - 'added' or 'removed'\n * @param shadowPath - Optional array of shadow host identifiers\n */\n function captureEntry(node, type, shadowPath) {\n if (node.nodeType !== 1) return null; // Element nodes only\n\n const el = node;\n const signals = computeSignals(el, shadowPath);\n const significance = computeSignificance(signals);\n\n // Only capture if meets threshold\n if (significance < SIGNIFICANCE_THRESHOLD) return null;\n\n // Capture content - excludes style/script content\n const text = getCleanTextContent(el, MAX_TEXT_LENGTH);\n const hasInteractives = signals.containsInteractiveElements;\n\n // Get viewport coverage for later analysis\n let viewportCoverage = { widthPct: 0, heightPct: 0 };\n try {\n const rect = el.getBoundingClientRect();\n viewportCoverage = {\n widthPct: Math.round((rect.width / window.innerWidth) * 100),\n heightPct: Math.round((rect.height / window.innerHeight) * 100),\n };\n } catch (e) {\n // Element may be detached\n }\n\n // Get z-index\n let zIndex = 0;\n try {\n zIndex = parseInt(getComputedStyle(el).zIndex, 10) || 0;\n } catch (e) {\n // Element may be detached\n }\n\n const entry = {\n type: type,\n timestamp: Date.now(),\n tag: el.tagName.toLowerCase(),\n id: el.id || undefined,\n\n // Semantic attributes\n role: el.getAttribute('role') || undefined,\n ariaLive: el.getAttribute('aria-live') || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n ariaModal: el.getAttribute('aria-modal') || undefined,\n\n // Content\n text: text,\n hasInteractives: hasInteractives,\n\n // Visual signals\n isFixedOrSticky: signals.isFixedOrSticky,\n zIndex: zIndex,\n viewportCoverage: viewportCoverage,\n\n // Structural\n isBodyDirectChild: signals.isBodyDirectChild,\n\n // Universal signals\n isVisibleInViewport: signals.isVisibleInViewport,\n hasNonTrivialText: signals.hasNonTrivialText,\n\n // Shadow DOM context\n shadowPath: shadowPath && shadowPath.length > 0 ? shadowPath : undefined,\n\n // Significance\n significance: significance,\n };\n\n return entry;\n }\n\n const log = [];\n const pageLoadTime = Date.now();\n\n /**\n * Process added nodes - capture entries and check for shadow roots.\n * @param node - The added node\n * @param shadowPath - Current shadow path context\n */\n function processAddedNode(node, shadowPath) {\n // Skip if already processed (prevents duplicates from nested shadow DOM)\n if (node.nodeType === 1 && processedElements.has(node)) {\n return;\n }\n\n const entry = captureEntry(node, 'added', shadowPath);\n if (entry) {\n entry.appearedAfterDelay = (entry.timestamp - pageLoadTime) > 100;\n log.push(entry);\n // Mark as processed\n if (node.nodeType === 1) {\n processedElements.add(node);\n }\n }\n\n // Check significant children - both ARIA-attributed and visible text elements\n if (node.nodeType === 1) {\n // First: ARIA-attributed elements (high confidence)\n const ariaChildren = node.querySelectorAll(\n '[role=\"alert\"], [role=\"status\"], [role=\"dialog\"], [role=\"alertdialog\"], [aria-live], [aria-modal], dialog'\n );\n for (const child of ariaChildren) {\n // Skip if already processed\n if (processedElements.has(child)) continue;\n\n const childEntry = captureEntry(child, 'added', shadowPath);\n if (childEntry) {\n childEntry.appearedAfterDelay = (childEntry.timestamp - pageLoadTime) > 100;\n log.push(childEntry);\n processedElements.add(child);\n }\n }\n\n // Second: Any visible element with text (broader capture for sites without ARIA)\n const textChildren = node.querySelectorAll('span, div, p, small, strong, em, label, li');\n for (const child of textChildren) {\n // Skip if already processed\n if (processedElements.has(child)) continue;\n\n // Skip if already captured via ARIA query\n if (child.hasAttribute('role') || child.hasAttribute('aria-live')) continue;\n\n // Only capture leaf-ish elements (minimal nested structure)\n const hasDeepNesting = child.querySelector('div, p, ul, ol, table');\n if (hasDeepNesting) continue;\n\n const childEntry = captureEntry(child, 'added', shadowPath);\n if (childEntry) {\n childEntry.appearedAfterDelay = (childEntry.timestamp - pageLoadTime) > 100;\n log.push(childEntry);\n processedElements.add(child);\n }\n }\n\n // Check for shadow roots in this element and its descendants\n checkAndObserveShadowRoots(node, shadowPath || []);\n }\n }\n\n /**\n * Process removed nodes - capture entries and cleanup shadow observers.\n * @param node - The removed node\n * @param shadowPath - Current shadow path context\n */\n function processRemovedNode(node, shadowPath) {\n const entry = captureEntry(node, 'removed', shadowPath);\n if (entry) {\n log.push(entry);\n }\n\n // Cleanup shadow observers for removed elements\n if (node.nodeType === 1) {\n cleanupShadowObservers(node);\n }\n }\n\n /**\n * Create a mutation callback for observing a specific context (main DOM or shadow root).\n * @param shadowPath - The shadow path context for this observer\n */\n function createMutationCallback(shadowPath) {\n return function(mutations) {\n for (const m of mutations) {\n // Capture added nodes\n for (const node of m.addedNodes) {\n processAddedNode(node, shadowPath);\n }\n\n // Capture removed nodes\n for (const node of m.removedNodes) {\n processRemovedNode(node, shadowPath);\n }\n }\n\n // Trim if over limit (FIFO)\n if (log.length > MAX_ENTRIES) {\n const excess = log.length - MAX_ENTRIES;\n log.splice(0, excess);\n }\n };\n }\n\n /**\n * Observe a shadow root for mutations.\n * @param shadowRoot - The shadow root to observe\n * @param shadowPath - The path of shadow host identifiers leading to this shadow root\n */\n function observeShadowRoot(shadowRoot, shadowPath) {\n // Already observing this shadow root\n if (shadowObservers.has(shadowRoot)) return;\n\n // Limit number of shadow observers for performance\n if (shadowObservers.size >= MAX_SHADOW_OBSERVERS) {\n console.warn('[ObservationAccumulator] Max shadow observers reached, skipping:', shadowPath);\n return;\n }\n\n const observer = new MutationObserver(createMutationCallback(shadowPath));\n observer.observe(shadowRoot, {\n childList: true,\n subtree: true,\n });\n\n shadowObservers.set(shadowRoot, { observer, hostPath: shadowPath });\n }\n\n /**\n * Recursively check element and descendants for open shadow roots.\n * @param element - The element to check\n * @param currentShadowPath - The current shadow path context\n * @param visited - Set of already-visited elements to prevent infinite recursion\n */\n function checkAndObserveShadowRoots(element, currentShadowPath, visited) {\n if (!element || element.nodeType !== 1) return;\n\n // Initialize visited set on first call\n if (!visited) {\n visited = new Set();\n }\n\n // Prevent infinite recursion from circular references\n if (visited.has(element)) return;\n visited.add(element);\n\n // Check if this element has an open shadow root\n if (element.shadowRoot) {\n const newPath = [...currentShadowPath, getElementIdentifier(element)];\n observeShadowRoot(element.shadowRoot, newPath);\n\n // Check shadow root's children for nested shadow roots\n const shadowChildren = element.shadowRoot.querySelectorAll('*');\n for (const child of shadowChildren) {\n if (child.shadowRoot) {\n checkAndObserveShadowRoots(child, newPath, visited);\n }\n }\n }\n\n // Check light DOM children for shadow roots\n const children = element.querySelectorAll('*');\n for (const child of children) {\n if (child.shadowRoot) {\n checkAndObserveShadowRoots(child, currentShadowPath, visited);\n }\n }\n }\n\n /**\n * Cleanup shadow observers for a removed element and its descendants.\n * @param element - The element being removed\n */\n function cleanupShadowObservers(element) {\n if (!element || element.nodeType !== 1) return;\n\n // If element is a shadow host, cleanup its observer\n if (element.shadowRoot && shadowObservers.has(element.shadowRoot)) {\n const { observer } = shadowObservers.get(element.shadowRoot);\n observer.disconnect();\n shadowObservers.delete(element.shadowRoot);\n }\n\n // Recursively cleanup descendant shadow hosts\n try {\n const descendants = element.querySelectorAll('*');\n for (const child of descendants) {\n if (child.shadowRoot && shadowObservers.has(child.shadowRoot)) {\n const { observer } = shadowObservers.get(child.shadowRoot);\n observer.disconnect();\n shadowObservers.delete(child.shadowRoot);\n }\n }\n } catch (e) {\n // Element may be detached, ignore errors\n }\n }\n\n // Create main observer for document.body\n const observer = new MutationObserver(createMutationCallback(null));\n\n // Start observing\n if (document.body) {\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n // Initial scan for existing shadow roots in the DOM\n checkAndObserveShadowRoots(document.body, []);\n }\n\n window.__observationAccumulator = {\n log: log,\n observer: observer,\n shadowObservers: shadowObservers, // Expose for debugging/testing\n observedBody: document.body, // Track which body we're observing for staleness detection\n pageLoadTime: pageLoadTime,\n lastReportedIndex: 0, // Track what's been reported\n\n // Get all entries since timestamp\n getSince: function(timestamp) {\n return this.log.filter(e => e.timestamp >= timestamp);\n },\n\n // Get significant entries since timestamp\n getSignificant: function(timestamp, threshold) {\n threshold = threshold || SIGNIFICANCE_THRESHOLD;\n return this.log.filter(e => e.timestamp >= timestamp && e.significance >= threshold);\n },\n\n // Get unreported entries (for accumulation between tool calls)\n getUnreported: function() {\n const unreported = this.log.slice(this.lastReportedIndex);\n return unreported.filter(e => e.significance >= SIGNIFICANCE_THRESHOLD);\n },\n\n // Mark entries as reported\n markReported: function() {\n this.lastReportedIndex = this.log.length;\n },\n\n // Reset on navigation\n reset: function() {\n this.log.length = 0;\n this.lastReportedIndex = 0;\n this.pageLoadTime = Date.now();\n // Cleanup all shadow observers\n for (const [shadowRoot, { observer }] of this.shadowObservers) {\n observer.disconnect();\n }\n this.shadowObservers.clear();\n // Clear processed elements tracking (create fresh WeakSet)\n processedElements = new WeakSet();\n },\n\n // Re-scan for shadow roots (useful after dynamic content load)\n rescanShadowRoots: function() {\n if (document.body) {\n checkAndObserveShadowRoots(document.body, []);\n }\n },\n\n // Get shadow observer count (for debugging)\n getShadowObserverCount: function() {\n return this.shadowObservers.size;\n },\n };\n})();\n";
|
|
18
|
+
export declare const OBSERVATION_OBSERVER_SCRIPT = "\n(function() {\n // Prevent double-injection\n if (window.__observationAccumulator) return;\n\n const MAX_ENTRIES = 500;\n const MAX_TEXT_LENGTH = 200;\n const MAX_SHADOW_OBSERVERS = 50; // Limit to prevent performance issues\n // IMPORTANT: Must match SIGNIFICANCE_THRESHOLD in observation.types.ts\n const SIGNIFICANCE_THRESHOLD = 4;\n // Node type constant for shadow root parent check\n const DOCUMENT_FRAGMENT_NODE = 11;\n // ARIA roles that indicate live regions (text mutations are significant)\n const LIVE_REGION_ROLES = new Set(['alert', 'status', 'log', 'marquee', 'timer']);\n // Attributes whose changes can reveal hidden elements (visibility tracking)\n const VISIBILITY_ATTRS = ['style', 'class', 'hidden', 'aria-hidden'];\n\n // Known toast library selectors (unsemantic \u2014 no ARIA roles).\n // Keep in sync with TOAST_DATA_ATTRS / TOAST_CLASS_PATTERNS in snapshot-compiler.ts.\n const TOAST_LIBRARY_PARTS = [\n '[data-sonner-toaster]',\n '[data-sonner-toast]',\n '[data-hot-toast]',\n '.Toastify__toast-container',\n '.Toastify__toast',\n '.ant-message',\n '.ant-message-notice',\n ];\n const TOAST_LIBRARY_SELECTOR = TOAST_LIBRARY_PARTS.join(', ');\n\n // Significance weights (must match server-side observation.types.ts)\n const WEIGHTS = {\n // Semantic signals (strongest)\n hasAlertRole: 3,\n hasAriaLive: 3,\n isDialog: 3,\n\n // Visual signals\n isFixedOrSticky: 2,\n hasHighZIndex: 1,\n coversSignificantViewport: 2,\n\n // Structural signals\n isBodyDirectChild: 1,\n containsInteractiveElements: 1,\n\n // Known toast library patterns (unsemantic but significant)\n isKnownToastLibrary: 3, // Equivalent to hasAlertRole\n\n // New universal signals - work without ARIA\n isVisibleInViewport: 2, // Element is visible in viewport\n hasNonTrivialText: 1, // Has meaningful text content\n // Temporal signals computed later\n };\n\n // Shadow DOM tracking\n // Map: shadowRoot -> { observer, hostPath }\n const shadowObservers = new Map();\n\n // Track processed elements to avoid duplicate observations\n // WeakSet allows garbage collection of removed elements\n let processedElements = new WeakSet();\n\n /**\n * Generate a stable identifier for an element (for shadow path tracking).\n * Format: TAG#id or TAG.className or TAG[index]\n */\n function getElementIdentifier(el) {\n const tag = el.tagName.toLowerCase();\n if (el.id) {\n return tag + '#' + el.id;\n }\n // For custom elements, use the tag name (usually descriptive like 'my-toast')\n if (tag.includes('-')) {\n return tag;\n }\n // Fallback: use first class or just tag\n const className = el.className && typeof el.className === 'string'\n ? el.className.split(' ')[0]\n : '';\n if (className) {\n return tag + '.' + className;\n }\n return tag;\n }\n\n // Tags whose text content should be excluded from extraction\n const EXCLUDED_TEXT_TAGS = new Set(['STYLE', 'SCRIPT', 'NOSCRIPT', 'TEMPLATE', 'SVG']);\n\n /**\n * Get clean text content from an element, excluding CSS/JS content.\n * Uses TreeWalker to iterate only text nodes, skipping those inside\n * excluded tags (STYLE, SCRIPT, NOSCRIPT, TEMPLATE, SVG).\n *\n * @param {Element} el - The DOM element to extract text from\n * @param {number} maxLength - Maximum text length (truncates result)\n * @returns {string} Clean text content, space-joined, truncated to maxLength\n */\n function getCleanTextContent(el, maxLength) {\n // If element itself is an excluded tag, return empty\n if (EXCLUDED_TEXT_TAGS.has(el.tagName.toUpperCase())) {\n return '';\n }\n\n const walker = document.createTreeWalker(el, 4, { // NodeFilter.SHOW_TEXT = 4\n acceptNode: function(node) {\n let parent = node.parentElement;\n while (parent && parent !== el) {\n if (EXCLUDED_TEXT_TAGS.has(parent.tagName.toUpperCase())) {\n return 2; // FILTER_REJECT\n }\n parent = parent.parentElement;\n }\n return 1; // FILTER_ACCEPT\n }\n });\n\n const textParts = [];\n let totalLength = 0;\n let node;\n\n while ((node = walker.nextNode()) && totalLength < maxLength) {\n const text = node.nodeValue;\n if (text) {\n const trimmed = text.trim();\n if (trimmed) {\n textParts.push(trimmed);\n totalLength += trimmed.length;\n }\n }\n }\n\n return textParts.join(' ').substring(0, maxLength);\n }\n\n /**\n * Compute significance signals from element.\n * Uses universal web standards (ARIA, CSS positioning, DOM structure, visibility).\n */\n function computeSignals(el, shadowPath) {\n const role = el.getAttribute('role');\n const ariaLive = el.getAttribute('aria-live');\n const ariaModal = el.getAttribute('aria-modal');\n const tagName = el.tagName.toLowerCase();\n\n // Get computed style (may fail for detached elements)\n let style = null;\n let rect = null;\n try {\n style = getComputedStyle(el);\n rect = el.getBoundingClientRect();\n } catch (e) {\n // Element may be detached\n }\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Check if element is visible in viewport\n const isVisibleInViewport = rect && style &&\n rect.width > 0 && rect.height > 0 &&\n rect.bottom > 0 && rect.top < vh &&\n rect.right > 0 && rect.left < vw &&\n style.display !== 'none' &&\n style.visibility !== 'hidden' &&\n style.opacity !== '0';\n\n // Check for non-trivial text (at least 3 chars, not just whitespace)\n // Use short sample for signal check - excludes style/script content\n const text = getCleanTextContent(el, 100);\n const hasNonTrivialText = text.length >= 3;\n\n // For shadow DOM elements, isBodyDirectChild is false but we still want to capture them\n // Check if parent is a shadow root (which indicates top-level in shadow DOM)\n const isTopLevelInShadow = shadowPath && shadowPath.length > 0 &&\n el.parentNode && el.parentNode.nodeType === DOCUMENT_FRAGMENT_NODE;\n\n return {\n // Semantic signals\n hasAlertRole: ['alert', 'status', 'log', 'alertdialog'].includes(role),\n hasAriaLive: ariaLive === 'polite' || ariaLive === 'assertive',\n isDialog: role === 'dialog' || tagName === 'dialog' || ariaModal === 'true',\n\n // Visual signals\n isFixedOrSticky: style && (style.position === 'fixed' || style.position === 'sticky'),\n // Note: parseInt returns NaN for non-numeric values like \"auto\", which correctly fails the > 1000 check\n hasHighZIndex: style && parseInt(style.zIndex, 10) > 1000,\n coversSignificantViewport: rect && ((rect.width > vw * 0.5) || (rect.height > vh * 0.3)),\n\n // Structural signals - consider top-level shadow DOM elements as equivalent to body children\n isBodyDirectChild: el.parentElement === document.body || isTopLevelInShadow,\n containsInteractiveElements: el.querySelector('button, a, input, select, textarea') !== null,\n\n // Known toast library detection (unsemantic but significant)\n isKnownToastLibrary: el.matches(TOAST_LIBRARY_SELECTOR) || (typeof el.className === 'string' && el.className.includes('chakra-toast')),\n\n // Universal signals (work without ARIA)\n isVisibleInViewport: !!isVisibleInViewport,\n hasNonTrivialText: hasNonTrivialText,\n\n // Temporal signals (set by accumulator later)\n appearedAfterDelay: false,\n wasShortLived: false,\n };\n }\n\n /**\n * Calculate significance score from signals using weighted sum.\n * @param signals - The computed significance signals for an element\n * @returns The total significance score\n */\n function computeSignificance(signals) {\n let score = 0;\n for (const [key, weight] of Object.entries(WEIGHTS)) {\n if (signals[key]) score += weight;\n }\n return score;\n }\n\n /**\n * Capture a mutation entry from an element.\n * @param node - The DOM node\n * @param type - 'added' or 'removed'\n * @param shadowPath - Optional array of shadow host identifiers\n */\n function captureEntry(node, type, shadowPath) {\n if (node.nodeType !== 1) return null; // Element nodes only\n\n const el = node;\n const signals = computeSignals(el, shadowPath);\n const significance = computeSignificance(signals);\n\n // Only capture if meets threshold\n if (significance < SIGNIFICANCE_THRESHOLD) return null;\n\n // Capture content - excludes style/script content\n const text = getCleanTextContent(el, MAX_TEXT_LENGTH);\n const hasInteractives = signals.containsInteractiveElements;\n\n // Get viewport coverage for later analysis\n let viewportCoverage = { widthPct: 0, heightPct: 0 };\n try {\n const rect = el.getBoundingClientRect();\n viewportCoverage = {\n widthPct: Math.round((rect.width / window.innerWidth) * 100),\n heightPct: Math.round((rect.height / window.innerHeight) * 100),\n };\n } catch (e) {\n // Element may be detached\n }\n\n // Get z-index\n let zIndex = 0;\n try {\n zIndex = parseInt(getComputedStyle(el).zIndex, 10) || 0;\n } catch (e) {\n // Element may be detached\n }\n\n const entry = {\n type: type,\n timestamp: Date.now(),\n tag: el.tagName.toLowerCase(),\n id: el.id || undefined,\n\n // Semantic attributes\n role: el.getAttribute('role') || undefined,\n ariaLive: el.getAttribute('aria-live') || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n ariaModal: el.getAttribute('aria-modal') || undefined,\n\n // Content\n text: text,\n hasInteractives: hasInteractives,\n\n // Visual signals\n isFixedOrSticky: signals.isFixedOrSticky,\n zIndex: zIndex,\n viewportCoverage: viewportCoverage,\n\n // Structural\n isBodyDirectChild: signals.isBodyDirectChild,\n\n // Universal signals\n isVisibleInViewport: signals.isVisibleInViewport,\n hasNonTrivialText: signals.hasNonTrivialText,\n\n // Shadow DOM context\n shadowPath: shadowPath && shadowPath.length > 0 ? shadowPath : undefined,\n\n // Significance\n significance: significance,\n };\n\n return entry;\n }\n\n const log = [];\n const pageLoadTime = Date.now();\n\n /**\n * Process added nodes - capture entries and check for shadow roots.\n * @param node - The added node\n * @param shadowPath - Current shadow path context\n */\n function processAddedNode(node, shadowPath) {\n // Skip if already processed (prevents duplicates from nested shadow DOM)\n if (node.nodeType === 1 && processedElements.has(node)) {\n return;\n }\n\n const entry = captureEntry(node, 'added', shadowPath);\n if (entry) {\n entry.appearedAfterDelay = (entry.timestamp - pageLoadTime) > 100;\n log.push(entry);\n // Mark as processed\n if (node.nodeType === 1) {\n processedElements.add(node);\n }\n }\n\n // Check significant children - both ARIA-attributed and visible text elements\n if (node.nodeType === 1) {\n // First: ARIA-attributed elements (high confidence)\n const ariaChildren = node.querySelectorAll(\n '[role=\"alert\"], [role=\"status\"], [role=\"dialog\"], [role=\"alertdialog\"], [aria-live], [aria-modal], dialog'\n );\n for (const child of ariaChildren) {\n // Skip if already processed\n if (processedElements.has(child)) continue;\n\n const childEntry = captureEntry(child, 'added', shadowPath);\n if (childEntry) {\n childEntry.appearedAfterDelay = (childEntry.timestamp - pageLoadTime) > 100;\n log.push(childEntry);\n processedElements.add(child);\n }\n }\n\n // Second: Any visible element with text (broader capture for sites without ARIA)\n const textChildren = node.querySelectorAll('span, div, p, small, strong, em, label, li');\n for (const child of textChildren) {\n // Skip if already processed\n if (processedElements.has(child)) continue;\n\n // Skip if already captured via ARIA query\n if (child.hasAttribute('role') || child.hasAttribute('aria-live')) continue;\n\n // Only capture leaf-ish elements (minimal nested structure)\n const hasDeepNesting = child.querySelector('div, p, ul, ol, table');\n if (hasDeepNesting) continue;\n\n const childEntry = captureEntry(child, 'added', shadowPath);\n if (childEntry) {\n childEntry.appearedAfterDelay = (childEntry.timestamp - pageLoadTime) > 100;\n log.push(childEntry);\n processedElements.add(child);\n }\n }\n\n // Check for shadow roots in this element and its descendants\n checkAndObserveShadowRoots(node, shadowPath || []);\n }\n }\n\n /**\n * Process removed nodes - capture entries and cleanup shadow observers.\n * @param node - The removed node\n * @param shadowPath - Current shadow path context\n */\n function processRemovedNode(node, shadowPath) {\n const entry = captureEntry(node, 'removed', shadowPath);\n if (entry) {\n log.push(entry);\n }\n\n // Cleanup shadow observers for removed elements\n if (node.nodeType === 1) {\n cleanupShadowObservers(node);\n }\n }\n\n /**\n * Find the nearest ancestor (or self) with a live region role.\n * Returns the element if found, null otherwise.\n */\n function findLiveRegionAncestor(node) {\n let el = node.nodeType === 1 ? node : node.parentElement;\n while (el && el !== document.body) {\n const role = el.getAttribute && el.getAttribute('role');\n if (role && LIVE_REGION_ROLES.has(role)) return el;\n const ariaLive = el.getAttribute && el.getAttribute('aria-live');\n if (ariaLive === 'polite' || ariaLive === 'assertive') return el;\n el = el.parentElement;\n }\n return null;\n }\n\n /**\n * Handle characterData mutations (text-only changes in existing elements).\n * Captures the live region container when its text content changes.\n */\n function processCharacterDataMutation(mutation, shadowPath) {\n const textNode = mutation.target;\n const liveRegion = findLiveRegionAncestor(textNode);\n if (!liveRegion) return;\n\n // Avoid re-capturing the same live region for rapid successive text changes\n // Use a short debounce window (same element within 100ms)\n const now = Date.now();\n if (liveRegion.__lastTextCapture && (now - liveRegion.__lastTextCapture) < 100) return;\n liveRegion.__lastTextCapture = now;\n\n const entry = captureEntry(liveRegion, 'added', shadowPath);\n if (entry) {\n entry.appearedAfterDelay = (entry.timestamp - pageLoadTime) > 100;\n log.push(entry);\n }\n }\n\n /**\n * Handle attribute mutations that may reveal hidden elements.\n * Detects visibility changes (display:none \u2192 block, hidden removal, etc.)\n */\n function processAttributeMutation(mutation, shadowPath) {\n const el = mutation.target;\n if (el.nodeType !== 1) return;\n\n // Skip if already processed in this cycle\n if (processedElements.has(el)) return;\n\n // Check if this attribute change made the element visible\n const attrName = mutation.attributeName;\n\n // For 'hidden' attribute: element becomes visible when hidden is removed\n if (attrName === 'hidden' && el.hasAttribute('hidden')) return; // Still hidden\n\n // For 'aria-hidden': element becomes visible when set to false or removed\n if (attrName === 'aria-hidden') {\n const val = el.getAttribute('aria-hidden');\n if (val === 'true') return; // Still hidden\n }\n\n // For 'style' or 'class': check computed visibility\n if (attrName === 'style' || attrName === 'class') {\n try {\n const style = getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {\n return; // Still hidden\n }\n } catch (e) {\n return; // Detached element\n }\n }\n\n // Check if this element (or a live region ancestor) is significant\n // Only capture if the element has meaningful content and meets threshold\n const entry = captureEntry(el, 'added', shadowPath);\n if (entry) {\n entry.appearedAfterDelay = (entry.timestamp - pageLoadTime) > 100;\n log.push(entry);\n processedElements.add(el);\n }\n }\n\n /**\n * Create a mutation callback for observing a specific context (main DOM or shadow root).\n * @param shadowPath - The shadow path context for this observer\n */\n function createMutationCallback(shadowPath) {\n return function(mutations) {\n for (const m of mutations) {\n if (m.type === 'childList') {\n // Capture added nodes\n for (const node of m.addedNodes) {\n processAddedNode(node, shadowPath);\n }\n\n // Capture removed nodes\n for (const node of m.removedNodes) {\n processRemovedNode(node, shadowPath);\n }\n } else if (m.type === 'characterData') {\n // Text content changed in an existing node (live region updates)\n processCharacterDataMutation(m, shadowPath);\n } else if (m.type === 'attributes') {\n // Attribute changed \u2014 may reveal hidden elements (visibility tracking)\n processAttributeMutation(m, shadowPath);\n }\n }\n\n // Trim if over limit (FIFO)\n if (log.length > MAX_ENTRIES) {\n const excess = log.length - MAX_ENTRIES;\n log.splice(0, excess);\n }\n };\n }\n\n /**\n * Observe a shadow root for mutations.\n * @param shadowRoot - The shadow root to observe\n * @param shadowPath - The path of shadow host identifiers leading to this shadow root\n */\n function observeShadowRoot(shadowRoot, shadowPath) {\n // Already observing this shadow root\n if (shadowObservers.has(shadowRoot)) return;\n\n // Limit number of shadow observers for performance\n if (shadowObservers.size >= MAX_SHADOW_OBSERVERS) {\n console.warn('[ObservationAccumulator] Max shadow observers reached, skipping:', shadowPath);\n return;\n }\n\n const observer = new MutationObserver(createMutationCallback(shadowPath));\n observer.observe(shadowRoot, {\n childList: true,\n subtree: true,\n characterData: true,\n characterDataOldValue: true,\n attributes: true,\n attributeFilter: VISIBILITY_ATTRS,\n });\n\n shadowObservers.set(shadowRoot, { observer, hostPath: shadowPath });\n }\n\n /**\n * Recursively check element and descendants for open shadow roots.\n * @param element - The element to check\n * @param currentShadowPath - The current shadow path context\n * @param visited - Set of already-visited elements to prevent infinite recursion\n */\n function checkAndObserveShadowRoots(element, currentShadowPath, visited) {\n if (!element || element.nodeType !== 1) return;\n\n // Initialize visited set on first call\n if (!visited) {\n visited = new Set();\n }\n\n // Prevent infinite recursion from circular references\n if (visited.has(element)) return;\n visited.add(element);\n\n // Check if this element has an open shadow root\n if (element.shadowRoot) {\n const newPath = [...currentShadowPath, getElementIdentifier(element)];\n observeShadowRoot(element.shadowRoot, newPath);\n\n // Check shadow root's children for nested shadow roots\n const shadowChildren = element.shadowRoot.querySelectorAll('*');\n for (const child of shadowChildren) {\n if (child.shadowRoot) {\n checkAndObserveShadowRoots(child, newPath, visited);\n }\n }\n }\n\n // Check light DOM children for shadow roots\n const children = element.querySelectorAll('*');\n for (const child of children) {\n if (child.shadowRoot) {\n checkAndObserveShadowRoots(child, currentShadowPath, visited);\n }\n }\n }\n\n /**\n * Cleanup shadow observers for a removed element and its descendants.\n * @param element - The element being removed\n */\n function cleanupShadowObservers(element) {\n if (!element || element.nodeType !== 1) return;\n\n // If element is a shadow host, cleanup its observer\n if (element.shadowRoot && shadowObservers.has(element.shadowRoot)) {\n const { observer } = shadowObservers.get(element.shadowRoot);\n observer.disconnect();\n shadowObservers.delete(element.shadowRoot);\n }\n\n // Recursively cleanup descendant shadow hosts\n try {\n const descendants = element.querySelectorAll('*');\n for (const child of descendants) {\n if (child.shadowRoot && shadowObservers.has(child.shadowRoot)) {\n const { observer } = shadowObservers.get(child.shadowRoot);\n observer.disconnect();\n shadowObservers.delete(child.shadowRoot);\n }\n }\n } catch (e) {\n // Element may be detached, ignore errors\n }\n }\n\n // Create main observer for document.body\n const observer = new MutationObserver(createMutationCallback(null));\n\n // Start observing\n if (document.body) {\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n characterDataOldValue: true,\n attributes: true,\n attributeFilter: VISIBILITY_ATTRS,\n });\n\n // Initial scan for existing shadow roots in the DOM\n checkAndObserveShadowRoots(document.body, []);\n }\n\n window.__observationAccumulator = {\n log: log,\n observer: observer,\n shadowObservers: shadowObservers, // Expose for debugging/testing\n observedBody: document.body, // Track which body we're observing for staleness detection\n pageLoadTime: pageLoadTime,\n lastReportedIndex: 0, // Track what's been reported\n\n // Get all entries since timestamp\n getSince: function(timestamp) {\n return this.log.filter(e => e.timestamp >= timestamp);\n },\n\n // Get significant entries since timestamp\n getSignificant: function(timestamp, threshold) {\n threshold = threshold || SIGNIFICANCE_THRESHOLD;\n return this.log.filter(e => e.timestamp >= timestamp && e.significance >= threshold);\n },\n\n // Get unreported entries (for accumulation between tool calls)\n getUnreported: function() {\n const unreported = this.log.slice(this.lastReportedIndex);\n return unreported.filter(e => e.significance >= SIGNIFICANCE_THRESHOLD);\n },\n\n // Mark entries as reported\n markReported: function() {\n this.lastReportedIndex = this.log.length;\n },\n\n // Reset on navigation\n reset: function() {\n this.log.length = 0;\n this.lastReportedIndex = 0;\n this.pageLoadTime = Date.now();\n // Cleanup all shadow observers\n for (const [shadowRoot, { observer }] of this.shadowObservers) {\n observer.disconnect();\n }\n this.shadowObservers.clear();\n // Clear processed elements tracking (create fresh WeakSet)\n processedElements = new WeakSet();\n },\n\n // Re-scan for shadow roots (useful after dynamic content load)\n rescanShadowRoots: function() {\n if (document.body) {\n checkAndObserveShadowRoots(document.body, []);\n }\n },\n\n // Get shadow observer count (for debugging)\n getShadowObserverCount: function() {\n return this.shadowObservers.size;\n },\n };\n})();\n";
|
|
19
19
|
//# sourceMappingURL=observer-script.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observer-script.d.ts","sourceRoot":"","sources":["../../../src/observation/observer-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,2BAA2B,
|
|
1
|
+
{"version":3,"file":"observer-script.d.ts","sourceRoot":"","sources":["../../../src/observation/observer-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,2BAA2B,wuuBAgqBvC,CAAC"}
|
|
@@ -27,6 +27,23 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
27
27
|
const SIGNIFICANCE_THRESHOLD = 4;
|
|
28
28
|
// Node type constant for shadow root parent check
|
|
29
29
|
const DOCUMENT_FRAGMENT_NODE = 11;
|
|
30
|
+
// ARIA roles that indicate live regions (text mutations are significant)
|
|
31
|
+
const LIVE_REGION_ROLES = new Set(['alert', 'status', 'log', 'marquee', 'timer']);
|
|
32
|
+
// Attributes whose changes can reveal hidden elements (visibility tracking)
|
|
33
|
+
const VISIBILITY_ATTRS = ['style', 'class', 'hidden', 'aria-hidden'];
|
|
34
|
+
|
|
35
|
+
// Known toast library selectors (unsemantic — no ARIA roles).
|
|
36
|
+
// Keep in sync with TOAST_DATA_ATTRS / TOAST_CLASS_PATTERNS in snapshot-compiler.ts.
|
|
37
|
+
const TOAST_LIBRARY_PARTS = [
|
|
38
|
+
'[data-sonner-toaster]',
|
|
39
|
+
'[data-sonner-toast]',
|
|
40
|
+
'[data-hot-toast]',
|
|
41
|
+
'.Toastify__toast-container',
|
|
42
|
+
'.Toastify__toast',
|
|
43
|
+
'.ant-message',
|
|
44
|
+
'.ant-message-notice',
|
|
45
|
+
];
|
|
46
|
+
const TOAST_LIBRARY_SELECTOR = TOAST_LIBRARY_PARTS.join(', ');
|
|
30
47
|
|
|
31
48
|
// Significance weights (must match server-side observation.types.ts)
|
|
32
49
|
const WEIGHTS = {
|
|
@@ -44,6 +61,9 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
44
61
|
isBodyDirectChild: 1,
|
|
45
62
|
containsInteractiveElements: 1,
|
|
46
63
|
|
|
64
|
+
// Known toast library patterns (unsemantic but significant)
|
|
65
|
+
isKnownToastLibrary: 3, // Equivalent to hasAlertRole
|
|
66
|
+
|
|
47
67
|
// New universal signals - work without ARIA
|
|
48
68
|
isVisibleInViewport: 2, // Element is visible in viewport
|
|
49
69
|
hasNonTrivialText: 1, // Has meaningful text content
|
|
@@ -188,6 +208,9 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
188
208
|
isBodyDirectChild: el.parentElement === document.body || isTopLevelInShadow,
|
|
189
209
|
containsInteractiveElements: el.querySelector('button, a, input, select, textarea') !== null,
|
|
190
210
|
|
|
211
|
+
// Known toast library detection (unsemantic but significant)
|
|
212
|
+
isKnownToastLibrary: el.matches(TOAST_LIBRARY_SELECTOR) || (typeof el.className === 'string' && el.className.includes('chakra-toast')),
|
|
213
|
+
|
|
191
214
|
// Universal signals (work without ARIA)
|
|
192
215
|
isVisibleInViewport: !!isVisibleInViewport,
|
|
193
216
|
hasNonTrivialText: hasNonTrivialText,
|
|
@@ -374,6 +397,89 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
374
397
|
}
|
|
375
398
|
}
|
|
376
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Find the nearest ancestor (or self) with a live region role.
|
|
402
|
+
* Returns the element if found, null otherwise.
|
|
403
|
+
*/
|
|
404
|
+
function findLiveRegionAncestor(node) {
|
|
405
|
+
let el = node.nodeType === 1 ? node : node.parentElement;
|
|
406
|
+
while (el && el !== document.body) {
|
|
407
|
+
const role = el.getAttribute && el.getAttribute('role');
|
|
408
|
+
if (role && LIVE_REGION_ROLES.has(role)) return el;
|
|
409
|
+
const ariaLive = el.getAttribute && el.getAttribute('aria-live');
|
|
410
|
+
if (ariaLive === 'polite' || ariaLive === 'assertive') return el;
|
|
411
|
+
el = el.parentElement;
|
|
412
|
+
}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Handle characterData mutations (text-only changes in existing elements).
|
|
418
|
+
* Captures the live region container when its text content changes.
|
|
419
|
+
*/
|
|
420
|
+
function processCharacterDataMutation(mutation, shadowPath) {
|
|
421
|
+
const textNode = mutation.target;
|
|
422
|
+
const liveRegion = findLiveRegionAncestor(textNode);
|
|
423
|
+
if (!liveRegion) return;
|
|
424
|
+
|
|
425
|
+
// Avoid re-capturing the same live region for rapid successive text changes
|
|
426
|
+
// Use a short debounce window (same element within 100ms)
|
|
427
|
+
const now = Date.now();
|
|
428
|
+
if (liveRegion.__lastTextCapture && (now - liveRegion.__lastTextCapture) < 100) return;
|
|
429
|
+
liveRegion.__lastTextCapture = now;
|
|
430
|
+
|
|
431
|
+
const entry = captureEntry(liveRegion, 'added', shadowPath);
|
|
432
|
+
if (entry) {
|
|
433
|
+
entry.appearedAfterDelay = (entry.timestamp - pageLoadTime) > 100;
|
|
434
|
+
log.push(entry);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Handle attribute mutations that may reveal hidden elements.
|
|
440
|
+
* Detects visibility changes (display:none → block, hidden removal, etc.)
|
|
441
|
+
*/
|
|
442
|
+
function processAttributeMutation(mutation, shadowPath) {
|
|
443
|
+
const el = mutation.target;
|
|
444
|
+
if (el.nodeType !== 1) return;
|
|
445
|
+
|
|
446
|
+
// Skip if already processed in this cycle
|
|
447
|
+
if (processedElements.has(el)) return;
|
|
448
|
+
|
|
449
|
+
// Check if this attribute change made the element visible
|
|
450
|
+
const attrName = mutation.attributeName;
|
|
451
|
+
|
|
452
|
+
// For 'hidden' attribute: element becomes visible when hidden is removed
|
|
453
|
+
if (attrName === 'hidden' && el.hasAttribute('hidden')) return; // Still hidden
|
|
454
|
+
|
|
455
|
+
// For 'aria-hidden': element becomes visible when set to false or removed
|
|
456
|
+
if (attrName === 'aria-hidden') {
|
|
457
|
+
const val = el.getAttribute('aria-hidden');
|
|
458
|
+
if (val === 'true') return; // Still hidden
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// For 'style' or 'class': check computed visibility
|
|
462
|
+
if (attrName === 'style' || attrName === 'class') {
|
|
463
|
+
try {
|
|
464
|
+
const style = getComputedStyle(el);
|
|
465
|
+
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
|
|
466
|
+
return; // Still hidden
|
|
467
|
+
}
|
|
468
|
+
} catch (e) {
|
|
469
|
+
return; // Detached element
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Check if this element (or a live region ancestor) is significant
|
|
474
|
+
// Only capture if the element has meaningful content and meets threshold
|
|
475
|
+
const entry = captureEntry(el, 'added', shadowPath);
|
|
476
|
+
if (entry) {
|
|
477
|
+
entry.appearedAfterDelay = (entry.timestamp - pageLoadTime) > 100;
|
|
478
|
+
log.push(entry);
|
|
479
|
+
processedElements.add(el);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
377
483
|
/**
|
|
378
484
|
* Create a mutation callback for observing a specific context (main DOM or shadow root).
|
|
379
485
|
* @param shadowPath - The shadow path context for this observer
|
|
@@ -381,14 +487,22 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
381
487
|
function createMutationCallback(shadowPath) {
|
|
382
488
|
return function(mutations) {
|
|
383
489
|
for (const m of mutations) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
490
|
+
if (m.type === 'childList') {
|
|
491
|
+
// Capture added nodes
|
|
492
|
+
for (const node of m.addedNodes) {
|
|
493
|
+
processAddedNode(node, shadowPath);
|
|
494
|
+
}
|
|
388
495
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
496
|
+
// Capture removed nodes
|
|
497
|
+
for (const node of m.removedNodes) {
|
|
498
|
+
processRemovedNode(node, shadowPath);
|
|
499
|
+
}
|
|
500
|
+
} else if (m.type === 'characterData') {
|
|
501
|
+
// Text content changed in an existing node (live region updates)
|
|
502
|
+
processCharacterDataMutation(m, shadowPath);
|
|
503
|
+
} else if (m.type === 'attributes') {
|
|
504
|
+
// Attribute changed — may reveal hidden elements (visibility tracking)
|
|
505
|
+
processAttributeMutation(m, shadowPath);
|
|
392
506
|
}
|
|
393
507
|
}
|
|
394
508
|
|
|
@@ -419,6 +533,10 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
419
533
|
observer.observe(shadowRoot, {
|
|
420
534
|
childList: true,
|
|
421
535
|
subtree: true,
|
|
536
|
+
characterData: true,
|
|
537
|
+
characterDataOldValue: true,
|
|
538
|
+
attributes: true,
|
|
539
|
+
attributeFilter: VISIBILITY_ATTRS,
|
|
422
540
|
});
|
|
423
541
|
|
|
424
542
|
shadowObservers.set(shadowRoot, { observer, hostPath: shadowPath });
|
|
@@ -502,6 +620,10 @@ export const OBSERVATION_OBSERVER_SCRIPT = `
|
|
|
502
620
|
observer.observe(document.body, {
|
|
503
621
|
childList: true,
|
|
504
622
|
subtree: true,
|
|
623
|
+
characterData: true,
|
|
624
|
+
characterDataOldValue: true,
|
|
625
|
+
attributes: true,
|
|
626
|
+
attributeFilter: VISIBILITY_ATTRS,
|
|
505
627
|
});
|
|
506
628
|
|
|
507
629
|
// Initial scan for existing shadow roots in the DOM
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observer-script.js","sourceRoot":"","sources":["../../../src/observation/observer-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG
|
|
1
|
+
{"version":3,"file":"observer-script.js","sourceRoot":"","sources":["../../../src/observation/observer-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgqB1C,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disambiguation
|
|
3
|
+
*
|
|
4
|
+
* Generate suggestions to narrow ambiguous query results.
|
|
5
|
+
* Analyzes matched nodes and suggests refinements.
|
|
6
|
+
*/
|
|
7
|
+
import type { ReadableNode } from '../snapshot/snapshot.types.js';
|
|
8
|
+
import type { FindElementsRequest, MatchedNode, DisambiguationSuggestion } from './types/query.types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Generate disambiguation suggestions when query matches multiple elements.
|
|
11
|
+
* Suggests refinements that would narrow down the results.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateSuggestions(matches: MatchedNode[], request: FindElementsRequest): DisambiguationSuggestion[];
|
|
14
|
+
/**
|
|
15
|
+
* Count nodes by a given attribute.
|
|
16
|
+
*/
|
|
17
|
+
export declare function countByAttribute<T>(nodes: ReadableNode[], getter: (node: ReadableNode) => T): Map<T, number>;
|
|
18
|
+
//# sourceMappingURL=disambiguation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disambiguation.d.ts","sourceRoot":"","sources":["../../../src/query/disambiguation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACX,wBAAwB,EACzB,MAAM,wBAAwB,CAAC;AAIhC;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,WAAW,EAAE,EACtB,OAAO,EAAE,mBAAmB,GAC3B,wBAAwB,EAAE,CA2G5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,KAAK,EAAE,YAAY,EAAE,EACrB,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,CAAC,GAChC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAOhB"}
|