Skip to content

Commit 7c18615

Browse files
committed
Add pre-action grounding guard (bounds check + snap-to-element)
1 parent 38e0679 commit 7c18615

15 files changed

Lines changed: 300 additions & 1 deletion

File tree

README/WHATS_NEW_zh-CN.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-23) — 动作前接地防护
4+
5+
拒绝越界点击;把接近偏离者吸附到真正的元素。完整参考:[`docs/source/Zh/doc/new_features/v153_features_doc.rst`](../docs/source/Zh/doc/new_features/v153_features_doc.rst)
6+
7+
- **`validate_action` / `snap_to_element` / `in_bounds`**(`AC_validate_action`):`guardrail` 扫文字、`loop_guard` 检测循环——两者都不在派发前验证坐标动作,所以幻觉 `(9999,-5)` 点击会打到空处、偏 5px 的点击会错过。本功能拒绝屏幕外坐标,并在提供 `targets` 时把接近偏离者吸附到最近元素中心,返回 `{ok, reason, snapped}`。纯标准库几何,作用于元素字典;执行器 `screen` 默认为实际屏幕。可无头测试;接在 agent 循环派发之前。
8+
39
## 本次更新 (2026-06-23) — 符记预算内的无障碍文字观测
410

511
把无障碍树转成 VLM 可操作的已编号文字区块。完整参考:[`docs/source/Zh/doc/new_features/v152_features_doc.rst`](../docs/source/Zh/doc/new_features/v152_features_doc.rst)

README/WHATS_NEW_zh-TW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-23) — 動作前接地防護
4+
5+
拒絕越界點擊;把接近偏離者吸附到真正的元素。完整參考:[`docs/source/Zh/doc/new_features/v153_features_doc.rst`](../docs/source/Zh/doc/new_features/v153_features_doc.rst)
6+
7+
- **`validate_action` / `snap_to_element` / `in_bounds`**(`AC_validate_action`):`guardrail` 掃文字、`loop_guard` 偵測迴圈——兩者都不在派發前驗證座標動作,所以幻覺 `(9999,-5)` 點擊會打到空處、偏 5px 的點擊會錯過。本功能拒絕螢幕外座標,並在提供 `targets` 時把接近偏離者吸附到最近元素中心,回傳 `{ok, reason, snapped}`。純標準函式庫幾何,作用於元素字典;執行器 `screen` 預設為實際螢幕。可無頭測試;接在 agent 迴圈派發之前。
8+
39
## 本次更新 (2026-06-23) — 符記預算內的無障礙文字觀測
410

511
把無障礙樹轉成 VLM 可操作的已編號文字區塊。完整參考:[`docs/source/Zh/doc/new_features/v152_features_doc.rst`](../docs/source/Zh/doc/new_features/v152_features_doc.rst)

WHATS_NEW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's New — AutoControl
22

3+
## What's new (2026-06-23) — Pre-Action Grounding Guard
4+
5+
Reject out-of-bounds clicks; snap near-misses onto the real element. Full reference: [`docs/source/Eng/doc/new_features/v153_features_doc.rst`](docs/source/Eng/doc/new_features/v153_features_doc.rst).
6+
7+
- **`validate_action` / `snap_to_element` / `in_bounds`** (`AC_validate_action`): `guardrail` scans text and `loop_guard` detects loops — neither validates a coordinate action before dispatch, so a hallucinated `(9999,-5)` click fires into nothing and a 5px-off click misses. This rejects off-screen coordinates and, given `targets`, snaps a near-miss onto the nearest element's centre, returning `{ok, reason, snapped}`. Pure-stdlib geometry over element dicts; the executor `screen` defaults to the live screen. Headless-testable; plugs in front of an agent loop's dispatch.
8+
39
## What's new (2026-06-23) — Token-Budgeted A11y Text Observation
410

511
Turn the a11y tree into an indexed text block a VLM can act on. Full reference: [`docs/source/Eng/doc/new_features/v152_features_doc.rst`](docs/source/Eng/doc/new_features/v152_features_doc.rst).
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Pre-Action Grounding Guard
2+
==========================
3+
4+
``guardrail`` scans text for prompt-injection and ``loop_guard`` detects stuck loops —
5+
but neither validates a *coordinate action* before it is dispatched. An agent loop
6+
executes whatever the model returns with no bounds or target check, so a hallucinated
7+
``(9999, -5)`` click fires into nothing and a 5-pixel-off click misses the button.
8+
``validate_action`` adds the "detect misaligned actions before execution" guard: reject
9+
clicks outside the screen and snap a near-miss coordinate onto the nearest known
10+
element's centre.
11+
12+
Pure-stdlib geometry over plain element dicts (``x`` / ``y`` / ``width`` / ``height``),
13+
so it is fully unit-testable. Imports no ``PySide6``.
14+
15+
Headless API
16+
------------
17+
18+
.. code-block:: python
19+
20+
from je_auto_control import validate_action, snap_to_element, in_bounds
21+
22+
check = validate_action(model_action, screen_size=(1920, 1080), targets=elements)
23+
if not check["ok"]:
24+
print("rejected:", check["reason"]) # e.g. "out of bounds"
25+
else:
26+
x, y = check["snapped"] or (model_action["x"], model_action["y"])
27+
click(x, y) # snapped onto the real button
28+
29+
``in_bounds(x, y, screen_size)`` is the screen-bounds predicate; ``snap_to_element``
30+
returns the centre of the element at (or nearest within ``max_dist`` of) a point, or
31+
``None``; ``validate_action`` combines them, returning ``{ok, reason, snapped}`` —
32+
rejecting out-of-bounds coordinates and snapping near-misses when ``targets`` are
33+
supplied. Actions without a coordinate always pass.
34+
35+
Executor command
36+
----------------
37+
38+
``AC_validate_action`` (``action`` / ``screen`` / ``targets`` → ``{ok, reason,
39+
snapped}``; ``screen`` defaults to the live screen). It is exposed as the MCP tool
40+
``ac_validate_action`` and as a Script Builder command under **Native UI**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ Comprehensive guides for all AutoControl features.
175175
doc/new_features/v150_features_doc
176176
doc/new_features/v151_features_doc
177177
doc/new_features/v152_features_doc
178+
doc/new_features/v153_features_doc
178179
doc/ocr_backends/ocr_backends_doc
179180
doc/observability/observability_doc
180181
doc/operations_layer/operations_layer_doc
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
動作前接地防護
2+
==============
3+
4+
``guardrail`` 掃描文字找提示注入、``loop_guard`` 偵測卡住的迴圈——但兩者都不在派發前驗證*座標動作*。agent 迴圈會
5+
執行模型回傳的任何東西,毫無邊界或目標檢查,因此幻覺出的 ``(9999, -5)`` 點擊會打到空處,而偏 5 像素的點擊會錯過
6+
按鈕。``validate_action`` 加入「執行前偵測錯位動作」防護:拒絕螢幕外點擊,並把接近但偏離的座標吸附到最近已知元素
7+
的中心。
8+
9+
純標準函式庫幾何,作用於純元素字典(``x`` / ``y`` / ``width`` / ``height``),因此完全可單元測試。不匯入 ``PySide6``。
10+
11+
無頭 API
12+
--------
13+
14+
.. code-block:: python
15+
16+
from je_auto_control import validate_action, snap_to_element, in_bounds
17+
18+
check = validate_action(model_action, screen_size=(1920, 1080), targets=elements)
19+
if not check["ok"]:
20+
print("rejected:", check["reason"]) # 例如 "out of bounds"
21+
else:
22+
x, y = check["snapped"] or (model_action["x"], model_action["y"])
23+
click(x, y) # 已吸附到真正的按鈕
24+
25+
``in_bounds(x, y, screen_size)`` 是螢幕邊界判斷式;``snap_to_element`` 回傳某點所在(或在 ``max_dist`` 內最近)
26+
元素的中心,否則 ``None``;``validate_action`` 結合兩者,回傳 ``{ok, reason, snapped}``——拒絕越界座標,並在提供
27+
``targets`` 時吸附接近偏離者。沒有座標的動作一律通過。
28+
29+
執行器命令
30+
----------
31+
32+
``AC_validate_action``(``action`` / ``screen`` / ``targets`` → ``{ok, reason, snapped}``;``screen`` 預設為實際
33+
螢幕)。它以 MCP 工具 ``ac_validate_action`` 以及 Script Builder 中 **Native UI** 分類下的命令提供。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ AutoControl 所有功能的完整使用指南。
175175
doc/new_features/v150_features_doc
176176
doc/new_features/v151_features_doc
177177
doc/new_features/v152_features_doc
178+
doc/new_features/v153_features_doc
178179
doc/ocr_backends/ocr_backends_doc
179180
doc/observability/observability_doc
180181
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@
369369
from je_auto_control.utils.observation import (
370370
flatten_tree, observation_index, serialize_observation,
371371
)
372+
# Pre-action grounding guard (bounds check + snap-to-element)
373+
from je_auto_control.utils.action_grounding import (
374+
in_bounds, snap_to_element, validate_action,
375+
)
372376
# CI workflow annotations (GitHub Actions)
373377
from je_auto_control.utils.ci_annotations import (
374378
emit_annotations, format_annotation,
@@ -1250,6 +1254,9 @@ def start_autocontrol_gui(*args, **kwargs):
12501254
"flatten_tree",
12511255
"observation_index",
12521256
"serialize_observation",
1257+
"in_bounds",
1258+
"snap_to_element",
1259+
"validate_action",
12531260
"emit_annotations", "format_annotation",
12541261
"ClipboardHistory", "default_clipboard_history",
12551262
"analyze_heal_log", "heal_stats", "scan_secrets",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2942,6 +2942,19 @@ def _add_set_of_marks_specs(specs: List[CommandSpec]) -> None:
29422942
),
29432943
description="Reading-ordered, viewport-clipped, indexed element list.",
29442944
))
2945+
specs.append(CommandSpec(
2946+
"AC_validate_action", "Native UI", "Validate / Snap Action",
2947+
fields=(
2948+
FieldSpec("action", FieldType.STRING,
2949+
placeholder='{"type":"click","x":..,"y":..}'),
2950+
FieldSpec("screen", FieldType.STRING, optional=True,
2951+
placeholder="[width, height]"),
2952+
FieldSpec("targets", FieldType.STRING, optional=True,
2953+
placeholder='[{"x":..,"y":..,"width":..,"height":..}]'),
2954+
),
2955+
description="Reject out-of-bounds clicks; snap a near-miss to the nearest "
2956+
"element.",
2957+
))
29452958
specs.append(CommandSpec(
29462959
"AC_mark_screen", "Native UI", "Set-of-Marks: Number Elements",
29472960
fields=(
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Pre-action grounding guard (bounds check + snap-to-element)."""
2+
from je_auto_control.utils.action_grounding.action_grounding import (
3+
in_bounds, snap_to_element, validate_action,
4+
)
5+
6+
__all__ = ["in_bounds", "snap_to_element", "validate_action"]

0 commit comments

Comments
 (0)