@mseep/clawdcursor 1.5.5
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/CHANGELOG.md +2264 -0
- package/LICENSE +21 -0
- package/README.md +385 -0
- package/SECURITY.md +44 -0
- package/SKILL.md +503 -0
- package/dist/core/agent-loop/agent.d.ts +42 -0
- package/dist/core/agent-loop/agent.js +1023 -0
- package/dist/core/agent-loop/agent.js.map +1 -0
- package/dist/core/agent-loop/batch-tool.d.ts +25 -0
- package/dist/core/agent-loop/batch-tool.js +218 -0
- package/dist/core/agent-loop/batch-tool.js.map +1 -0
- package/dist/core/agent-loop/coord-scale.d.ts +72 -0
- package/dist/core/agent-loop/coord-scale.js +89 -0
- package/dist/core/agent-loop/coord-scale.js.map +1 -0
- package/dist/core/agent-loop/focus-guard.d.ts +24 -0
- package/dist/core/agent-loop/focus-guard.js +29 -0
- package/dist/core/agent-loop/focus-guard.js.map +1 -0
- package/dist/core/agent-loop/project-mcp.d.ts +97 -0
- package/dist/core/agent-loop/project-mcp.js +253 -0
- package/dist/core/agent-loop/project-mcp.js.map +1 -0
- package/dist/core/agent-loop/prompt.d.ts +45 -0
- package/dist/core/agent-loop/prompt.js +426 -0
- package/dist/core/agent-loop/prompt.js.map +1 -0
- package/dist/core/agent-loop/tool-meta.d.ts +93 -0
- package/dist/core/agent-loop/tool-meta.js +651 -0
- package/dist/core/agent-loop/tool-meta.js.map +1 -0
- package/dist/core/agent-loop/tools.d.ts +38 -0
- package/dist/core/agent-loop/tools.js +2134 -0
- package/dist/core/agent-loop/tools.js.map +1 -0
- package/dist/core/agent-loop/types.d.ts +170 -0
- package/dist/core/agent-loop/types.js +12 -0
- package/dist/core/agent-loop/types.js.map +1 -0
- package/dist/core/agent.d.ts +51 -0
- package/dist/core/agent.js +245 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/app-categories.d.ts +67 -0
- package/dist/core/app-categories.js +108 -0
- package/dist/core/app-categories.js.map +1 -0
- package/dist/core/banner.d.ts +70 -0
- package/dist/core/banner.js +245 -0
- package/dist/core/banner.js.map +1 -0
- package/dist/core/classify/capability.d.ts +45 -0
- package/dist/core/classify/capability.js +78 -0
- package/dist/core/classify/capability.js.map +1 -0
- package/dist/core/decompose/llm-decomposer.d.ts +35 -0
- package/dist/core/decompose/llm-decomposer.js +156 -0
- package/dist/core/decompose/llm-decomposer.js.map +1 -0
- package/dist/core/decompose/parser.d.ts +27 -0
- package/dist/core/decompose/parser.js +101 -0
- package/dist/core/decompose/parser.js.map +1 -0
- package/dist/core/observability/correlation.d.ts +19 -0
- package/dist/core/observability/correlation.js +36 -0
- package/dist/core/observability/correlation.js.map +1 -0
- package/dist/core/observability/cost-meter.d.ts +51 -0
- package/dist/core/observability/cost-meter.js +134 -0
- package/dist/core/observability/cost-meter.js.map +1 -0
- package/dist/core/observability/logger.d.ts +61 -0
- package/dist/core/observability/logger.js +550 -0
- package/dist/core/observability/logger.js.map +1 -0
- package/dist/core/router/aliases.d.ts +50 -0
- package/dist/core/router/aliases.js +104 -0
- package/dist/core/router/aliases.js.map +1 -0
- package/dist/core/router/normalize.d.ts +41 -0
- package/dist/core/router/normalize.js +80 -0
- package/dist/core/router/normalize.js.map +1 -0
- package/dist/core/safety.d.ts +126 -0
- package/dist/core/safety.js +568 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/sense/a11y-resolver.d.ts +73 -0
- package/dist/core/sense/a11y-resolver.js +76 -0
- package/dist/core/sense/a11y-resolver.js.map +1 -0
- package/dist/core/sense/fingerprint.d.ts +41 -0
- package/dist/core/sense/fingerprint.js +123 -0
- package/dist/core/sense/fingerprint.js.map +1 -0
- package/dist/core/sense/rank.d.ts +70 -0
- package/dist/core/sense/rank.js +192 -0
- package/dist/core/sense/rank.js.map +1 -0
- package/dist/core/sense/reactive-check.d.ts +40 -0
- package/dist/core/sense/reactive-check.js +48 -0
- package/dist/core/sense/reactive-check.js.map +1 -0
- package/dist/core/sense/snapshot.d.ts +19 -0
- package/dist/core/sense/snapshot.js +100 -0
- package/dist/core/sense/snapshot.js.map +1 -0
- package/dist/core/sense/types.d.ts +66 -0
- package/dist/core/sense/types.js +9 -0
- package/dist/core/sense/types.js.map +1 -0
- package/dist/core/sense/ui-map-anchors.d.ts +7 -0
- package/dist/core/sense/ui-map-anchors.js +24 -0
- package/dist/core/sense/ui-map-anchors.js.map +1 -0
- package/dist/core/sense/ui-map-elements.d.ts +5 -0
- package/dist/core/sense/ui-map-elements.js +33 -0
- package/dist/core/sense/ui-map-elements.js.map +1 -0
- package/dist/core/sense/ui-map-find.d.ts +56 -0
- package/dist/core/sense/ui-map-find.js +153 -0
- package/dist/core/sense/ui-map-find.js.map +1 -0
- package/dist/core/sense/ui-map-fuse.d.ts +4 -0
- package/dist/core/sense/ui-map-fuse.js +44 -0
- package/dist/core/sense/ui-map-fuse.js.map +1 -0
- package/dist/core/sense/ui-map-geom.d.ts +3 -0
- package/dist/core/sense/ui-map-geom.js +16 -0
- package/dist/core/sense/ui-map-geom.js.map +1 -0
- package/dist/core/sense/ui-map-holder.d.ts +58 -0
- package/dist/core/sense/ui-map-holder.js +87 -0
- package/dist/core/sense/ui-map-holder.js.map +1 -0
- package/dist/core/sense/ui-map-normalize.d.ts +19 -0
- package/dist/core/sense/ui-map-normalize.js +65 -0
- package/dist/core/sense/ui-map-normalize.js.map +1 -0
- package/dist/core/sense/ui-map-render.d.ts +4 -0
- package/dist/core/sense/ui-map-render.js +34 -0
- package/dist/core/sense/ui-map-render.js.map +1 -0
- package/dist/core/sense/ui-map-resolve.d.ts +41 -0
- package/dist/core/sense/ui-map-resolve.js +59 -0
- package/dist/core/sense/ui-map-resolve.js.map +1 -0
- package/dist/core/sense/ui-map-types.d.ts +66 -0
- package/dist/core/sense/ui-map-types.js +11 -0
- package/dist/core/sense/ui-map-types.js.map +1 -0
- package/dist/core/sense/ui-map.d.ts +29 -0
- package/dist/core/sense/ui-map.js +113 -0
- package/dist/core/sense/ui-map.js.map +1 -0
- package/dist/core/verify/assertions.d.ts +132 -0
- package/dist/core/verify/assertions.js +284 -0
- package/dist/core/verify/assertions.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/browser-config.d.ts +36 -0
- package/dist/llm/browser-config.js +83 -0
- package/dist/llm/browser-config.js.map +1 -0
- package/dist/llm/client.d.ts +268 -0
- package/dist/llm/client.js +1094 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/config.d.ts +79 -0
- package/dist/llm/config.js +375 -0
- package/dist/llm/config.js.map +1 -0
- package/dist/llm/credentials.d.ts +35 -0
- package/dist/llm/credentials.js +491 -0
- package/dist/llm/credentials.js.map +1 -0
- package/dist/llm/external-creds.d.ts +42 -0
- package/dist/llm/external-creds.js +169 -0
- package/dist/llm/external-creds.js.map +1 -0
- package/dist/llm/providers.d.ts +123 -0
- package/dist/llm/providers.js +717 -0
- package/dist/llm/providers.js.map +1 -0
- package/dist/paths.d.ts +31 -0
- package/dist/paths.js +147 -0
- package/dist/paths.js.map +1 -0
- package/dist/platform/accessibility.d.ts +139 -0
- package/dist/platform/accessibility.js +670 -0
- package/dist/platform/accessibility.js.map +1 -0
- package/dist/platform/cdp-driver.d.ts +318 -0
- package/dist/platform/cdp-driver.js +1179 -0
- package/dist/platform/cdp-driver.js.map +1 -0
- package/dist/platform/index.d.ts +11 -0
- package/dist/platform/index.js +69 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/keys.d.ts +17 -0
- package/dist/platform/keys.js +129 -0
- package/dist/platform/keys.js.map +1 -0
- package/dist/platform/launch-poll.d.ts +101 -0
- package/dist/platform/launch-poll.js +177 -0
- package/dist/platform/launch-poll.js.map +1 -0
- package/dist/platform/linux.d.ts +173 -0
- package/dist/platform/linux.js +1253 -0
- package/dist/platform/linux.js.map +1 -0
- package/dist/platform/macos.d.ts +136 -0
- package/dist/platform/macos.js +976 -0
- package/dist/platform/macos.js.map +1 -0
- package/dist/platform/native-desktop.d.ts +145 -0
- package/dist/platform/native-desktop.js +936 -0
- package/dist/platform/native-desktop.js.map +1 -0
- package/dist/platform/native-helper.d.ts +130 -0
- package/dist/platform/native-helper.js +592 -0
- package/dist/platform/native-helper.js.map +1 -0
- package/dist/platform/ocr-engine.d.ts +78 -0
- package/dist/platform/ocr-engine.js +363 -0
- package/dist/platform/ocr-engine.js.map +1 -0
- package/dist/platform/ps-runner.d.ts +28 -0
- package/dist/platform/ps-runner.js +228 -0
- package/dist/platform/ps-runner.js.map +1 -0
- package/dist/platform/types.d.ts +397 -0
- package/dist/platform/types.js +15 -0
- package/dist/platform/types.js.map +1 -0
- package/dist/platform/uri-handler.d.ts +75 -0
- package/dist/platform/uri-handler.js +273 -0
- package/dist/platform/uri-handler.js.map +1 -0
- package/dist/platform/wayland-backend.d.ts +53 -0
- package/dist/platform/wayland-backend.js +348 -0
- package/dist/platform/wayland-backend.js.map +1 -0
- package/dist/platform/windows.d.ts +232 -0
- package/dist/platform/windows.js +1210 -0
- package/dist/platform/windows.js.map +1 -0
- package/dist/postbuild.d.ts +10 -0
- package/dist/postbuild.js +98 -0
- package/dist/postbuild.js.map +1 -0
- package/dist/schema/snapshot.d.ts +33 -0
- package/dist/schema/snapshot.js +90 -0
- package/dist/schema/snapshot.js.map +1 -0
- package/dist/shortcuts.d.ts +30 -0
- package/dist/shortcuts.js +261 -0
- package/dist/shortcuts.js.map +1 -0
- package/dist/surface/cli.d.ts +7 -0
- package/dist/surface/cli.js +1556 -0
- package/dist/surface/cli.js.map +1 -0
- package/dist/surface/dashboard.d.ts +8 -0
- package/dist/surface/dashboard.js +1193 -0
- package/dist/surface/dashboard.js.map +1 -0
- package/dist/surface/doctor.d.ts +29 -0
- package/dist/surface/doctor.js +1514 -0
- package/dist/surface/doctor.js.map +1 -0
- package/dist/surface/format.d.ts +10 -0
- package/dist/surface/format.js +37 -0
- package/dist/surface/format.js.map +1 -0
- package/dist/surface/http-utility.d.ts +65 -0
- package/dist/surface/http-utility.js +336 -0
- package/dist/surface/http-utility.js.map +1 -0
- package/dist/surface/mcp-server.d.ts +91 -0
- package/dist/surface/mcp-server.js +280 -0
- package/dist/surface/mcp-server.js.map +1 -0
- package/dist/surface/onboarding.d.ts +15 -0
- package/dist/surface/onboarding.js +184 -0
- package/dist/surface/onboarding.js.map +1 -0
- package/dist/surface/pidfile.d.ts +79 -0
- package/dist/surface/pidfile.js +263 -0
- package/dist/surface/pidfile.js.map +1 -0
- package/dist/surface/readiness.d.ts +45 -0
- package/dist/surface/readiness.js +230 -0
- package/dist/surface/readiness.js.map +1 -0
- package/dist/surface/report.d.ts +68 -0
- package/dist/surface/report.js +341 -0
- package/dist/surface/report.js.map +1 -0
- package/dist/surface/skill-register.d.ts +14 -0
- package/dist/surface/skill-register.js +150 -0
- package/dist/surface/skill-register.js.map +1 -0
- package/dist/surface/version.d.ts +6 -0
- package/dist/surface/version.js +27 -0
- package/dist/surface/version.js.map +1 -0
- package/dist/tools/a11y.d.ts +8 -0
- package/dist/tools/a11y.js +545 -0
- package/dist/tools/a11y.js.map +1 -0
- package/dist/tools/a11y_depth.d.ts +19 -0
- package/dist/tools/a11y_depth.js +455 -0
- package/dist/tools/a11y_depth.js.map +1 -0
- package/dist/tools/agent.d.ts +15 -0
- package/dist/tools/agent.js +248 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/batch.d.ts +46 -0
- package/dist/tools/batch.js +230 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/cdp.d.ts +8 -0
- package/dist/tools/cdp.js +233 -0
- package/dist/tools/cdp.js.map +1 -0
- package/dist/tools/compact.d.ts +63 -0
- package/dist/tools/compact.js +418 -0
- package/dist/tools/compact.js.map +1 -0
- package/dist/tools/cost-class.d.ts +38 -0
- package/dist/tools/cost-class.js +117 -0
- package/dist/tools/cost-class.js.map +1 -0
- package/dist/tools/desktop.d.ts +9 -0
- package/dist/tools/desktop.js +346 -0
- package/dist/tools/desktop.js.map +1 -0
- package/dist/tools/electron_bridge.d.ts +41 -0
- package/dist/tools/electron_bridge.js +261 -0
- package/dist/tools/electron_bridge.js.map +1 -0
- package/dist/tools/extras.d.ts +22 -0
- package/dist/tools/extras.js +942 -0
- package/dist/tools/extras.js.map +1 -0
- package/dist/tools/favorites.d.ts +13 -0
- package/dist/tools/favorites.js +137 -0
- package/dist/tools/favorites.js.map +1 -0
- package/dist/tools/introspection.d.ts +13 -0
- package/dist/tools/introspection.js +55 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/tools/ocr.d.ts +8 -0
- package/dist/tools/ocr.js +66 -0
- package/dist/tools/ocr.js.map +1 -0
- package/dist/tools/orchestration.d.ts +7 -0
- package/dist/tools/orchestration.js +377 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/playbooks/extract-compose.d.ts +22 -0
- package/dist/tools/playbooks/extract-compose.js +85 -0
- package/dist/tools/playbooks/extract-compose.js.map +1 -0
- package/dist/tools/playbooks/find-replace.d.ts +11 -0
- package/dist/tools/playbooks/find-replace.js +56 -0
- package/dist/tools/playbooks/find-replace.js.map +1 -0
- package/dist/tools/playbooks/index.d.ts +63 -0
- package/dist/tools/playbooks/index.js +70 -0
- package/dist/tools/playbooks/index.js.map +1 -0
- package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
- package/dist/tools/playbooks/keys-blocklist.js +89 -0
- package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
- package/dist/tools/registry.d.ts +40 -0
- package/dist/tools/registry.js +560 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/safety-gate.d.ts +16 -0
- package/dist/tools/safety-gate.js +70 -0
- package/dist/tools/safety-gate.js.map +1 -0
- package/dist/tools/scheduler.d.ts +76 -0
- package/dist/tools/scheduler.js +413 -0
- package/dist/tools/scheduler.js.map +1 -0
- package/dist/tools/shortcuts.d.ts +13 -0
- package/dist/tools/shortcuts.js +205 -0
- package/dist/tools/shortcuts.js.map +1 -0
- package/dist/tools/smart.d.ts +15 -0
- package/dist/tools/smart.js +785 -0
- package/dist/tools/smart.js.map +1 -0
- package/dist/tools/types.d.ts +174 -0
- package/dist/tools/types.js +67 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/window-text.d.ts +15 -0
- package/dist/tools/window-text.js +39 -0
- package/dist/tools/window-text.js.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.js +41 -0
- package/dist/types.js.map +1 -0
- package/native/Package.swift +38 -0
- package/native/README.md +113 -0
- package/native/Sources/ClawdCursorHelper/main.swift +602 -0
- package/native/Sources/ClawdCursorHost/main.swift +182 -0
- package/native/Sources/PermissionCheck/main.swift +53 -0
- package/native/Sources/ScreenshotHelper/main.swift +219 -0
- package/native/build.sh +139 -0
- package/native/entitlements.plist +12 -0
- package/package.json +115 -0
- package/scripts/banner.ps1 +112 -0
- package/scripts/coord-accuracy.ps1 +140 -0
- package/scripts/coord-uwp.ps1 +80 -0
- package/scripts/edge-glow.ps1 +180 -0
- package/scripts/find-element.ps1 +198 -0
- package/scripts/get-foreground-window.ps1 +71 -0
- package/scripts/get-screen-context.ps1 +183 -0
- package/scripts/get-windows.ps1 +66 -0
- package/scripts/install-panic-hotkey.ps1 +46 -0
- package/scripts/interact-element.ps1 +431 -0
- package/scripts/invoke-element.ps1 +314 -0
- package/scripts/linux/atspi-bridge.py +356 -0
- package/scripts/linux/ocr-recognize.py +154 -0
- package/scripts/mac/_window-picker.jxa +163 -0
- package/scripts/mac/find-element.jxa +0 -0
- package/scripts/mac/find-element.sh +161 -0
- package/scripts/mac/focus-window.jxa +284 -0
- package/scripts/mac/get-focused-element.jxa +102 -0
- package/scripts/mac/get-foreground-window.jxa +173 -0
- package/scripts/mac/get-screen-context.jxa +197 -0
- package/scripts/mac/get-ui-tree.sh +141 -0
- package/scripts/mac/get-windows.jxa +117 -0
- package/scripts/mac/interact-element.sh +235 -0
- package/scripts/mac/invoke-element.jxa +408 -0
- package/scripts/mac/ocr-recognize.swift +124 -0
- package/scripts/ocr-recognize.ps1 +102 -0
- package/scripts/postinstall-native.js +48 -0
- package/scripts/ps-bridge.ps1 +830 -0
- package/scripts/smoke-mcp.ps1 +119 -0
- package/scripts/sync-version.ts +178 -0
- package/scripts/verify-install.js +81 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A11y bounds resolver — math-only, zero LLM, zero vision.
|
|
3
|
+
*
|
|
4
|
+
* Ported from src/a11y-click-resolver.ts. Decoupled from the legacy
|
|
5
|
+
* `AccessibilityBridge` class — the unified pipeline hands in a resolver
|
|
6
|
+
* function that returns UI element bounds (typically from
|
|
7
|
+
* `PlatformAdapter.findElements`).
|
|
8
|
+
*
|
|
9
|
+
* The critical invariant v0.6.3 earned through real-world bug hunting:
|
|
10
|
+
* reject bounds with `y:-29503` and similar absurd values that some a11y
|
|
11
|
+
* APIs return for hidden/off-screen elements. This file's `isValidBounds`
|
|
12
|
+
* is the guard.
|
|
13
|
+
*/
|
|
14
|
+
export interface Bounds {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}
|
|
20
|
+
export interface UiElementWithBounds {
|
|
21
|
+
name?: string;
|
|
22
|
+
controlType?: string;
|
|
23
|
+
bounds?: Bounds;
|
|
24
|
+
automationId?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Bounds sanity check. Rejects:
|
|
28
|
+
* - zero/negative width or height
|
|
29
|
+
* - off-screen coordinates that indicate hidden elements
|
|
30
|
+
* (e.g. macOS AX returns y:-29503 for some cached-but-invisible items)
|
|
31
|
+
* - out-of-range coordinates > 10k (no real screen is that large; 8K is ~8000)
|
|
32
|
+
*/
|
|
33
|
+
export declare function isValidBounds(b: Bounds | undefined): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Return the integer center of a bounds rect.
|
|
36
|
+
*/
|
|
37
|
+
export declare function centerOf(b: Bounds): {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Resolve an element name/automationId to its center coords.
|
|
43
|
+
*
|
|
44
|
+
* `lookup` is a caller-provided function so this module is decoupled from any
|
|
45
|
+
* specific `AccessibilityBridge` or `PlatformAdapter` instance — tests can
|
|
46
|
+
* stub it, and the pipeline can wire whatever adapter it has.
|
|
47
|
+
*
|
|
48
|
+
* Returns null if the element isn't found or its bounds fail the sanity
|
|
49
|
+
* check. Callers should fall back to OCR-based coord resolution on null.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveByName(name: string, lookup: (q: {
|
|
52
|
+
name?: string;
|
|
53
|
+
controlType?: string;
|
|
54
|
+
processId?: number;
|
|
55
|
+
}) => Promise<UiElementWithBounds[]>, opts?: {
|
|
56
|
+
controlType?: string;
|
|
57
|
+
processId?: number;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
} | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Resolve by a11y automationId (Windows UIA only today; noop elsewhere).
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveById(automationId: string, lookup: (q: {
|
|
66
|
+
automationId?: string;
|
|
67
|
+
processId?: number;
|
|
68
|
+
}) => Promise<UiElementWithBounds[]>, opts?: {
|
|
69
|
+
processId?: number;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
} | null>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A11y bounds resolver — math-only, zero LLM, zero vision.
|
|
4
|
+
*
|
|
5
|
+
* Ported from src/a11y-click-resolver.ts. Decoupled from the legacy
|
|
6
|
+
* `AccessibilityBridge` class — the unified pipeline hands in a resolver
|
|
7
|
+
* function that returns UI element bounds (typically from
|
|
8
|
+
* `PlatformAdapter.findElements`).
|
|
9
|
+
*
|
|
10
|
+
* The critical invariant v0.6.3 earned through real-world bug hunting:
|
|
11
|
+
* reject bounds with `y:-29503` and similar absurd values that some a11y
|
|
12
|
+
* APIs return for hidden/off-screen elements. This file's `isValidBounds`
|
|
13
|
+
* is the guard.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.isValidBounds = isValidBounds;
|
|
17
|
+
exports.centerOf = centerOf;
|
|
18
|
+
exports.resolveByName = resolveByName;
|
|
19
|
+
exports.resolveById = resolveById;
|
|
20
|
+
/**
|
|
21
|
+
* Bounds sanity check. Rejects:
|
|
22
|
+
* - zero/negative width or height
|
|
23
|
+
* - off-screen coordinates that indicate hidden elements
|
|
24
|
+
* (e.g. macOS AX returns y:-29503 for some cached-but-invisible items)
|
|
25
|
+
* - out-of-range coordinates > 10k (no real screen is that large; 8K is ~8000)
|
|
26
|
+
*/
|
|
27
|
+
function isValidBounds(b) {
|
|
28
|
+
if (!b || b.width <= 0 || b.height <= 0)
|
|
29
|
+
return false;
|
|
30
|
+
if (b.x < -100 || b.y < -100)
|
|
31
|
+
return false;
|
|
32
|
+
if (b.x > 10_000 || b.y > 10_000)
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Return the integer center of a bounds rect.
|
|
38
|
+
*/
|
|
39
|
+
function centerOf(b) {
|
|
40
|
+
return {
|
|
41
|
+
x: b.x + Math.floor(b.width / 2),
|
|
42
|
+
y: b.y + Math.floor(b.height / 2),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve an element name/automationId to its center coords.
|
|
47
|
+
*
|
|
48
|
+
* `lookup` is a caller-provided function so this module is decoupled from any
|
|
49
|
+
* specific `AccessibilityBridge` or `PlatformAdapter` instance — tests can
|
|
50
|
+
* stub it, and the pipeline can wire whatever adapter it has.
|
|
51
|
+
*
|
|
52
|
+
* Returns null if the element isn't found or its bounds fail the sanity
|
|
53
|
+
* check. Callers should fall back to OCR-based coord resolution on null.
|
|
54
|
+
*/
|
|
55
|
+
async function resolveByName(name, lookup, opts) {
|
|
56
|
+
const results = await lookup({ name, ...opts });
|
|
57
|
+
if (!results?.length)
|
|
58
|
+
return null;
|
|
59
|
+
const first = results[0];
|
|
60
|
+
if (!isValidBounds(first.bounds))
|
|
61
|
+
return null;
|
|
62
|
+
return centerOf(first.bounds);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolve by a11y automationId (Windows UIA only today; noop elsewhere).
|
|
66
|
+
*/
|
|
67
|
+
async function resolveById(automationId, lookup, opts) {
|
|
68
|
+
const results = await lookup({ automationId, ...opts });
|
|
69
|
+
if (!results?.length)
|
|
70
|
+
return null;
|
|
71
|
+
const first = results[0];
|
|
72
|
+
if (!isValidBounds(first.bounds))
|
|
73
|
+
return null;
|
|
74
|
+
return centerOf(first.bounds);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=a11y-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11y-resolver.js","sourceRoot":"","sources":["../../../src/core/sense/a11y-resolver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAuBH,sCAKC;AAKD,4BAKC;AAYD,sCAUC;AAKD,kCAUC;AA3DD;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,CAAqB;IACjD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAgB,QAAQ,CAAC,CAAS;IAChC,OAAO;QACL,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAChC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;KAClC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,aAAa,CACjC,IAAY,EACZ,MAA0G,EAC1G,IAAmD;IAEnD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAO,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,WAAW,CAC/B,YAAoB,EACpB,MAA4F,EAC5F,IAA6B;IAE7B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAO,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot fingerprint — stagnation detection.
|
|
3
|
+
*
|
|
4
|
+
* Ported from src/snapshot-builder.ts. The idea: after every agent action,
|
|
5
|
+
* compute a stable hash of "what's on screen" (structured elements only —
|
|
6
|
+
* not pixels). If the fingerprint doesn't change across N consecutive
|
|
7
|
+
* actions, the agent is stuck doing the same thing.
|
|
8
|
+
*
|
|
9
|
+
* The orchestrator uses this to short-circuit infinite loops. A 2-iteration
|
|
10
|
+
* duplicate fingerprint + a verifier that says "not done" = abort, try a
|
|
11
|
+
* different layer (text-agent → vision-agent → retry).
|
|
12
|
+
*/
|
|
13
|
+
import type { SnapshotElement } from './types';
|
|
14
|
+
/**
|
|
15
|
+
* Produce a deterministic short hash of the screen state.
|
|
16
|
+
*
|
|
17
|
+
* Order-insensitive (elements sorted before hash), position-quantized (to 8px
|
|
18
|
+
* buckets) so that sub-pixel jitter from OCR doesn't break the equality.
|
|
19
|
+
*
|
|
20
|
+
* Intentionally NOT based on pixel screenshots — that's what the verifier's
|
|
21
|
+
* pixel-diff signal handles. This is the a11y/OCR-level "same set of named
|
|
22
|
+
* buttons at roughly the same places" fingerprint.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fingerprint(elements: SnapshotElement[], activeTitle?: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* A sliding history of recent fingerprints. Reports stagnation when the
|
|
27
|
+
* last `n` fingerprints are identical.
|
|
28
|
+
*/
|
|
29
|
+
export declare class FingerprintHistory {
|
|
30
|
+
private readonly maxSize;
|
|
31
|
+
private history;
|
|
32
|
+
constructor(maxSize?: number);
|
|
33
|
+
/** Append a new fingerprint. */
|
|
34
|
+
push(fp: string): void;
|
|
35
|
+
/** Stagnant if the last N entries are all equal and ≥ 2. */
|
|
36
|
+
isStagnant(n?: number): boolean;
|
|
37
|
+
/** Reset after a successful action so the agent gets a fresh window. */
|
|
38
|
+
reset(): void;
|
|
39
|
+
/** Internal — for tests and telemetry. */
|
|
40
|
+
getHistory(): string[];
|
|
41
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Snapshot fingerprint — stagnation detection.
|
|
4
|
+
*
|
|
5
|
+
* Ported from src/snapshot-builder.ts. The idea: after every agent action,
|
|
6
|
+
* compute a stable hash of "what's on screen" (structured elements only —
|
|
7
|
+
* not pixels). If the fingerprint doesn't change across N consecutive
|
|
8
|
+
* actions, the agent is stuck doing the same thing.
|
|
9
|
+
*
|
|
10
|
+
* The orchestrator uses this to short-circuit infinite loops. A 2-iteration
|
|
11
|
+
* duplicate fingerprint + a verifier that says "not done" = abort, try a
|
|
12
|
+
* different layer (text-agent → vision-agent → retry).
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.FingerprintHistory = void 0;
|
|
49
|
+
exports.fingerprint = fingerprint;
|
|
50
|
+
const crypto = __importStar(require("crypto"));
|
|
51
|
+
// NOTE on the SHA-1 below: these are NON-credential state checksums for
|
|
52
|
+
// stagnation detection — not password/credential storage. The digests are
|
|
53
|
+
// truncated, held only in-memory for the duration of one task run, and never
|
|
54
|
+
// persisted or transmitted. Secure fields are already excluded upstream
|
|
55
|
+
// (value === undefined). CodeQL's js/insufficient-password-hash flags the
|
|
56
|
+
// `e.value` → SHA-1 flow as a "password hash"; that is a false positive here
|
|
57
|
+
// (a KDF like bcrypt/scrypt would be the wrong tool for a per-frame checksum).
|
|
58
|
+
/**
|
|
59
|
+
* Produce a deterministic short hash of the screen state.
|
|
60
|
+
*
|
|
61
|
+
* Order-insensitive (elements sorted before hash), position-quantized (to 8px
|
|
62
|
+
* buckets) so that sub-pixel jitter from OCR doesn't break the equality.
|
|
63
|
+
*
|
|
64
|
+
* Intentionally NOT based on pixel screenshots — that's what the verifier's
|
|
65
|
+
* pixel-diff signal handles. This is the a11y/OCR-level "same set of named
|
|
66
|
+
* buttons at roughly the same places" fingerprint.
|
|
67
|
+
*/
|
|
68
|
+
function fingerprint(elements, activeTitle) {
|
|
69
|
+
// Quantize coords to a grid to absorb OCR jitter.
|
|
70
|
+
const QUANT = 8;
|
|
71
|
+
const quant = (n) => Math.round(n / QUANT) * QUANT;
|
|
72
|
+
const tokens = elements.map(e => {
|
|
73
|
+
const cx = quant(e.x);
|
|
74
|
+
const cy = quant(e.y);
|
|
75
|
+
const role = e.role ?? e.source;
|
|
76
|
+
// Normalize name: trim + collapse whitespace + lowercase
|
|
77
|
+
const name = (e.name ?? '').trim().toLowerCase().replace(/\s+/g, ' ');
|
|
78
|
+
// Include the field VALUE (hashed short, never the raw text — it may be
|
|
79
|
+
// sensitive): typing changes value but not name/role/bounds, so a
|
|
80
|
+
// value-blind fingerprint made every successful type/fill look like "no
|
|
81
|
+
// observable change" and fed false stagnation nudges (audit 2026-06-10,
|
|
82
|
+
// finding D2). Secure fields carry value=undefined and stay excluded.
|
|
83
|
+
const val = e.value ? crypto.createHash('sha1').update(e.value).digest('hex').slice(0, 8) : '';
|
|
84
|
+
return `${role}|${name}|${cx}|${cy}|${val}`;
|
|
85
|
+
});
|
|
86
|
+
tokens.sort(); // order-insensitive
|
|
87
|
+
const payload = (activeTitle ?? '') + '\n' + tokens.join('\n');
|
|
88
|
+
return crypto.createHash('sha1').update(payload).digest('hex').slice(0, 16);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A sliding history of recent fingerprints. Reports stagnation when the
|
|
92
|
+
* last `n` fingerprints are identical.
|
|
93
|
+
*/
|
|
94
|
+
class FingerprintHistory {
|
|
95
|
+
maxSize;
|
|
96
|
+
history = [];
|
|
97
|
+
constructor(maxSize = 8) {
|
|
98
|
+
this.maxSize = maxSize;
|
|
99
|
+
}
|
|
100
|
+
/** Append a new fingerprint. */
|
|
101
|
+
push(fp) {
|
|
102
|
+
this.history.push(fp);
|
|
103
|
+
if (this.history.length > this.maxSize)
|
|
104
|
+
this.history.shift();
|
|
105
|
+
}
|
|
106
|
+
/** Stagnant if the last N entries are all equal and ≥ 2. */
|
|
107
|
+
isStagnant(n = 2) {
|
|
108
|
+
if (this.history.length < n)
|
|
109
|
+
return false;
|
|
110
|
+
const tail = this.history.slice(-n);
|
|
111
|
+
return tail.every(fp => fp === tail[0]);
|
|
112
|
+
}
|
|
113
|
+
/** Reset after a successful action so the agent gets a fresh window. */
|
|
114
|
+
reset() {
|
|
115
|
+
this.history = [];
|
|
116
|
+
}
|
|
117
|
+
/** Internal — for tests and telemetry. */
|
|
118
|
+
getHistory() {
|
|
119
|
+
return [...this.history];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.FingerprintHistory = FingerprintHistory;
|
|
123
|
+
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../../src/core/sense/fingerprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rank-before-truncate for a11y snapshots.
|
|
3
|
+
*
|
|
4
|
+
* Problem: Paint's a11y tree had "Pencil" at index 20 (it was NOT truncated),
|
|
5
|
+
* but Explorer / Outlook / Teams can easily spew 200+ a11y nodes where the
|
|
6
|
+
* buttons the agent actually needs sit at indices 80–150. The previous
|
|
7
|
+
* `slice(0, 80)` strategy threw those overboard.
|
|
8
|
+
*
|
|
9
|
+
* This module ranks elements by how likely the agent is to act on them, then
|
|
10
|
+
* the snapshot renderer truncates from the bottom. No app-specific rules —
|
|
11
|
+
* the signals come from accessibility roles and bounding-box geometry,
|
|
12
|
+
* nothing encoded per-app.
|
|
13
|
+
*
|
|
14
|
+
* Signals (additive, all non-app-specific):
|
|
15
|
+
* + Interactive roles (Button, MenuItem, Hyperlink, Edit, ComboBox, Tab,
|
|
16
|
+
* CheckBox, RadioButton, ListItem, TreeItem) get a strong boost.
|
|
17
|
+
* + Named elements (non-empty `name`) beat anonymous ones.
|
|
18
|
+
* + Focused element gets a big boost.
|
|
19
|
+
* + Elements marked `interactive: true` get a small boost.
|
|
20
|
+
* + Smaller elements (typical for buttons / icons) slightly preferred
|
|
21
|
+
* over full-window panes / group containers.
|
|
22
|
+
* + Top-edge and right-edge toolbars get a modest boost (Paint palette,
|
|
23
|
+
* Office ribbon live there).
|
|
24
|
+
* - Containers (Pane, Group, Document, ScrollBar) get a penalty — they
|
|
25
|
+
* hold other interactive nodes but aren't themselves clickable.
|
|
26
|
+
* - Huge elements (> 80% of screen) get a penalty — usually the window
|
|
27
|
+
* body, not a target.
|
|
28
|
+
*
|
|
29
|
+
* The ranker is a pure function of the snapshot element list. It never
|
|
30
|
+
* consults app name / window title / anything app-specific. A new LOB app
|
|
31
|
+
* with exotic roles will rank cleanly because it obeys the same a11y
|
|
32
|
+
* contract every Windows/macOS/Linux UI toolkit does.
|
|
33
|
+
*/
|
|
34
|
+
import type { SnapshotElement } from './types';
|
|
35
|
+
export interface RankOpts {
|
|
36
|
+
/** Physical screen width for "huge element" detection. */
|
|
37
|
+
screenWidth?: number;
|
|
38
|
+
/** Physical screen height for "huge element" detection. */
|
|
39
|
+
screenHeight?: number;
|
|
40
|
+
/** Active window processId — elements outside this process get penalized. */
|
|
41
|
+
focusProcessId?: number;
|
|
42
|
+
/** Explicitly-focused element coords, if the adapter reports one. */
|
|
43
|
+
focusPoint?: {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Score one element. Higher = more likely the agent wants to act on it.
|
|
50
|
+
* Pure: no I/O, no app-specific tables.
|
|
51
|
+
*/
|
|
52
|
+
export declare function scoreElement(el: SnapshotElement, opts?: RankOpts): number;
|
|
53
|
+
/**
|
|
54
|
+
* Rank elements from most-likely-to-act-on to least. Stable: equal scores
|
|
55
|
+
* preserve document order (often the left-to-right, top-to-bottom order
|
|
56
|
+
* Windows UIA returns, which tends to match visual scan order).
|
|
57
|
+
*
|
|
58
|
+
* Does NOT truncate — callers decide the cap based on their token budget.
|
|
59
|
+
* The snapshot renderer typically calls rank(), then slices the top N.
|
|
60
|
+
*/
|
|
61
|
+
export declare function rankElements(elements: SnapshotElement[], opts?: RankOpts): SnapshotElement[];
|
|
62
|
+
/**
|
|
63
|
+
* Debug-friendly: return the top-N with their scores. Used by the
|
|
64
|
+
* observability logger so the user can see WHY "Pencil" ranked 3rd when
|
|
65
|
+
* the tree had 200 elements.
|
|
66
|
+
*/
|
|
67
|
+
export declare function rankWithScores(elements: SnapshotElement[], opts?: RankOpts): Array<{
|
|
68
|
+
el: SnapshotElement;
|
|
69
|
+
score: number;
|
|
70
|
+
}>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rank-before-truncate for a11y snapshots.
|
|
4
|
+
*
|
|
5
|
+
* Problem: Paint's a11y tree had "Pencil" at index 20 (it was NOT truncated),
|
|
6
|
+
* but Explorer / Outlook / Teams can easily spew 200+ a11y nodes where the
|
|
7
|
+
* buttons the agent actually needs sit at indices 80–150. The previous
|
|
8
|
+
* `slice(0, 80)` strategy threw those overboard.
|
|
9
|
+
*
|
|
10
|
+
* This module ranks elements by how likely the agent is to act on them, then
|
|
11
|
+
* the snapshot renderer truncates from the bottom. No app-specific rules —
|
|
12
|
+
* the signals come from accessibility roles and bounding-box geometry,
|
|
13
|
+
* nothing encoded per-app.
|
|
14
|
+
*
|
|
15
|
+
* Signals (additive, all non-app-specific):
|
|
16
|
+
* + Interactive roles (Button, MenuItem, Hyperlink, Edit, ComboBox, Tab,
|
|
17
|
+
* CheckBox, RadioButton, ListItem, TreeItem) get a strong boost.
|
|
18
|
+
* + Named elements (non-empty `name`) beat anonymous ones.
|
|
19
|
+
* + Focused element gets a big boost.
|
|
20
|
+
* + Elements marked `interactive: true` get a small boost.
|
|
21
|
+
* + Smaller elements (typical for buttons / icons) slightly preferred
|
|
22
|
+
* over full-window panes / group containers.
|
|
23
|
+
* + Top-edge and right-edge toolbars get a modest boost (Paint palette,
|
|
24
|
+
* Office ribbon live there).
|
|
25
|
+
* - Containers (Pane, Group, Document, ScrollBar) get a penalty — they
|
|
26
|
+
* hold other interactive nodes but aren't themselves clickable.
|
|
27
|
+
* - Huge elements (> 80% of screen) get a penalty — usually the window
|
|
28
|
+
* body, not a target.
|
|
29
|
+
*
|
|
30
|
+
* The ranker is a pure function of the snapshot element list. It never
|
|
31
|
+
* consults app name / window title / anything app-specific. A new LOB app
|
|
32
|
+
* with exotic roles will rank cleanly because it obeys the same a11y
|
|
33
|
+
* contract every Windows/macOS/Linux UI toolkit does.
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scoreElement = scoreElement;
|
|
37
|
+
exports.rankElements = rankElements;
|
|
38
|
+
exports.rankWithScores = rankWithScores;
|
|
39
|
+
/**
|
|
40
|
+
* Score weights. Tuned to be monotonic: the sum for a typical interactive
|
|
41
|
+
* button exceeds the sum for a container pane by a healthy margin. Exact
|
|
42
|
+
* numbers matter less than the ordering.
|
|
43
|
+
*/
|
|
44
|
+
const ROLE_BOOSTS = {
|
|
45
|
+
// ─── Clickable/commandable ────────────────────────────────
|
|
46
|
+
'button': 40,
|
|
47
|
+
'menuitem': 38,
|
|
48
|
+
'hyperlink': 38,
|
|
49
|
+
'link': 38,
|
|
50
|
+
'tab': 32,
|
|
51
|
+
'checkbox': 30,
|
|
52
|
+
'radiobutton': 30,
|
|
53
|
+
'splitbutton': 30,
|
|
54
|
+
'togglebutton': 30,
|
|
55
|
+
'listitem': 24,
|
|
56
|
+
'treeitem': 24,
|
|
57
|
+
'menubaritem': 30,
|
|
58
|
+
// ─── Editable ─────────────────────────────────────────────
|
|
59
|
+
'edit': 35,
|
|
60
|
+
'text': 18, // generic; raised only slightly
|
|
61
|
+
'combobox': 32,
|
|
62
|
+
'spinner': 26,
|
|
63
|
+
'slider': 26,
|
|
64
|
+
'datepicker': 26,
|
|
65
|
+
// ─── Informational but often targeted ─────────────────────
|
|
66
|
+
'image': 6,
|
|
67
|
+
'statictext': 5,
|
|
68
|
+
'statusbar': 8,
|
|
69
|
+
// ─── Containers (penalized, but not eliminated) ───────────
|
|
70
|
+
'pane': -12,
|
|
71
|
+
'group': -8,
|
|
72
|
+
'document': -10,
|
|
73
|
+
'scrollbar': -20,
|
|
74
|
+
'titlebar': -4,
|
|
75
|
+
'menubar': 0,
|
|
76
|
+
'toolbar': 6, // toolbars themselves aren't clicked, but they
|
|
77
|
+
// geographically concentrate buttons — keep them
|
|
78
|
+
// visible so the agent has orientation
|
|
79
|
+
'window': -18,
|
|
80
|
+
};
|
|
81
|
+
/** Extra boost when the element sits along a toolbar edge (top/right). */
|
|
82
|
+
const TOOLBAR_EDGE_BOOST = 6;
|
|
83
|
+
/** Boost for an explicitly focused element. */
|
|
84
|
+
const FOCUSED_BOOST = 25;
|
|
85
|
+
/** Boost for having a readable name. */
|
|
86
|
+
const NAMED_BOOST = 10;
|
|
87
|
+
/** Boost for interactive flag (set by the platform adapter). */
|
|
88
|
+
const INTERACTIVE_FLAG_BOOST = 6;
|
|
89
|
+
/** Penalty for being >= 80% of the screen area. */
|
|
90
|
+
const HUGE_ELEMENT_PENALTY = -25;
|
|
91
|
+
/** Penalty for being 0 bounds (invisible). */
|
|
92
|
+
const ZERO_BOUNDS_PENALTY = -80;
|
|
93
|
+
/**
|
|
94
|
+
* Score one element. Higher = more likely the agent wants to act on it.
|
|
95
|
+
* Pure: no I/O, no app-specific tables.
|
|
96
|
+
*/
|
|
97
|
+
function scoreElement(el, opts = {}) {
|
|
98
|
+
let score = 0;
|
|
99
|
+
// Role signal — the single biggest ranker.
|
|
100
|
+
const role = (el.role || '').toLowerCase().replace(/[^a-z]/g, '');
|
|
101
|
+
if (role && role in ROLE_BOOSTS)
|
|
102
|
+
score += ROLE_BOOSTS[role];
|
|
103
|
+
else if (role)
|
|
104
|
+
score += 4; // unknown role gets a small positive (don't zero-weight new a11y roles)
|
|
105
|
+
// Named elements beat anonymous.
|
|
106
|
+
const name = (el.name || '').trim();
|
|
107
|
+
if (name)
|
|
108
|
+
score += NAMED_BOOST;
|
|
109
|
+
// Longer readable names (up to ~30 chars) get a small additional weight.
|
|
110
|
+
if (name.length > 2)
|
|
111
|
+
score += Math.min(name.length, 20) * 0.15;
|
|
112
|
+
// Interactive flag from adapter.
|
|
113
|
+
if (el.interactive)
|
|
114
|
+
score += INTERACTIVE_FLAG_BOOST;
|
|
115
|
+
// A11y source beats OCR — a11y tree entries come with stable roles and
|
|
116
|
+
// names; OCR is often noisy text labels.
|
|
117
|
+
if (el.source === 'a11y')
|
|
118
|
+
score += 4;
|
|
119
|
+
// Geometric sanity:
|
|
120
|
+
const screenW = opts.screenWidth ?? 1920;
|
|
121
|
+
const screenH = opts.screenHeight ?? 1080;
|
|
122
|
+
const screenArea = screenW * screenH;
|
|
123
|
+
const elArea = Math.max(0, el.width) * Math.max(0, el.height);
|
|
124
|
+
if (el.width <= 0 || el.height <= 0) {
|
|
125
|
+
score += ZERO_BOUNDS_PENALTY;
|
|
126
|
+
}
|
|
127
|
+
else if (screenArea > 0 && elArea / screenArea > 0.8) {
|
|
128
|
+
score += HUGE_ELEMENT_PENALTY;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Smaller elements (typical of buttons) get a modest positive. Weighted
|
|
132
|
+
// so a 40×40 button scores +6, a 200×200 control scores ~+2, a big pane
|
|
133
|
+
// scores near 0.
|
|
134
|
+
const normalized = 1 - Math.min(1, elArea / (screenArea / 50));
|
|
135
|
+
score += normalized * 6;
|
|
136
|
+
}
|
|
137
|
+
// Toolbar-edge boost — elements near the top (y < 140) or right
|
|
138
|
+
// (x > screenW - 200) are often command buttons (Paint palette, Office
|
|
139
|
+
// ribbon, sidebar buttons). No app-specific code needed.
|
|
140
|
+
if (el.y < 140)
|
|
141
|
+
score += TOOLBAR_EDGE_BOOST;
|
|
142
|
+
else if (el.x > screenW - 200)
|
|
143
|
+
score += TOOLBAR_EDGE_BOOST * 0.7;
|
|
144
|
+
// Focused element wins big.
|
|
145
|
+
if (opts.focusPoint) {
|
|
146
|
+
const cx = el.x + el.width / 2;
|
|
147
|
+
const cy = el.y + el.height / 2;
|
|
148
|
+
const dx = cx - opts.focusPoint.x;
|
|
149
|
+
const dy = cy - opts.focusPoint.y;
|
|
150
|
+
const dist = Math.hypot(dx, dy);
|
|
151
|
+
// Within 40px — definitely focused.
|
|
152
|
+
if (dist < 40)
|
|
153
|
+
score += FOCUSED_BOOST;
|
|
154
|
+
// Within 200 — nearby / probably related.
|
|
155
|
+
else if (dist < 200)
|
|
156
|
+
score += FOCUSED_BOOST * 0.3;
|
|
157
|
+
}
|
|
158
|
+
// Prefer elements in the focused window's process.
|
|
159
|
+
if (opts.focusProcessId != null && el.processId != null && el.processId !== opts.focusProcessId) {
|
|
160
|
+
score -= 30;
|
|
161
|
+
}
|
|
162
|
+
return score;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Rank elements from most-likely-to-act-on to least. Stable: equal scores
|
|
166
|
+
* preserve document order (often the left-to-right, top-to-bottom order
|
|
167
|
+
* Windows UIA returns, which tends to match visual scan order).
|
|
168
|
+
*
|
|
169
|
+
* Does NOT truncate — callers decide the cap based on their token budget.
|
|
170
|
+
* The snapshot renderer typically calls rank(), then slices the top N.
|
|
171
|
+
*/
|
|
172
|
+
function rankElements(elements, opts = {}) {
|
|
173
|
+
// Attach indices to preserve stable order on ties.
|
|
174
|
+
const scored = elements.map((el, i) => ({ el, i, s: scoreElement(el, opts) }));
|
|
175
|
+
scored.sort((a, b) => {
|
|
176
|
+
if (b.s !== a.s)
|
|
177
|
+
return b.s - a.s;
|
|
178
|
+
return a.i - b.i;
|
|
179
|
+
});
|
|
180
|
+
return scored.map(x => x.el);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Debug-friendly: return the top-N with their scores. Used by the
|
|
184
|
+
* observability logger so the user can see WHY "Pencil" ranked 3rd when
|
|
185
|
+
* the tree had 200 elements.
|
|
186
|
+
*/
|
|
187
|
+
function rankWithScores(elements, opts = {}) {
|
|
188
|
+
return elements
|
|
189
|
+
.map(el => ({ el, score: scoreElement(el, opts) }))
|
|
190
|
+
.sort((a, b) => b.score - a.score);
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=rank.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../../src/core/sense/rank.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer C — reactive step discipline. After a consequential action, verify the
|
|
3
|
+
* agent-stated expected effect (hard) or a tolerant "did anything change" net
|
|
4
|
+
* (soft), so the agent never blindly proceeds on an action that didn't take.
|
|
5
|
+
* Pure over its inputs + PlatformAdapter reads (OS-agnostic, unit-testable).
|
|
6
|
+
* See docs/superpowers/specs/2026-06-07-ui-state-compiler-layer-c-design.md.
|
|
7
|
+
*/
|
|
8
|
+
import type { PlatformAdapter } from '../../platform/types';
|
|
9
|
+
export interface ReactiveInput {
|
|
10
|
+
/** Raw `expect` arg from the tool call (assertions array), or undefined. */
|
|
11
|
+
expect: unknown;
|
|
12
|
+
/** The tool's own result text + success, to fold the note into. */
|
|
13
|
+
toolText: string;
|
|
14
|
+
toolSuccess: boolean;
|
|
15
|
+
/** Whether the tool is screen-changing (consequential). */
|
|
16
|
+
changesScreen: boolean;
|
|
17
|
+
/** Whether the loop observed a change after the action (fingerprint/pixel). */
|
|
18
|
+
observedChange: boolean;
|
|
19
|
+
adapter: PlatformAdapter;
|
|
20
|
+
/** Lazy OCR reader for ocr_contains assertions; omit when unavailable. */
|
|
21
|
+
ocrText?: () => Promise<string>;
|
|
22
|
+
/** Settle budget for hard checks (ms). Defaults to SETTLE_BUDGET_MS. */
|
|
23
|
+
settleMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* How long a failing hard check re-polls before declaring a DEVIATION. UIs
|
|
27
|
+
* are asynchronous: a recipient chip resolves via a directory lookup
|
|
28
|
+
* (0.5–3s), window titles update lazily after a save. Declaring DEVIATION
|
|
29
|
+
* off a single immediate check told the model to RETRY actions that had
|
|
30
|
+
* actually taken — a duplicate-send risk on Send/Submit (audit 2026-06-10,
|
|
31
|
+
* finding D1).
|
|
32
|
+
*/
|
|
33
|
+
export declare const SETTLE_BUDGET_MS = 2000;
|
|
34
|
+
export declare const SETTLE_INTERVAL_MS = 250;
|
|
35
|
+
/** A modified result (success/text) to replace the tool's, or null = leave as-is. */
|
|
36
|
+
export interface ReactiveOutcome {
|
|
37
|
+
success: boolean;
|
|
38
|
+
text: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function reactiveCheck(input: ReactiveInput): Promise<ReactiveOutcome | null>;
|