feat: prepare 2.2.0 with UI and execution fixes

Add dark theme, richer Markdown and readable plugin results.
Improve Android input evidence, response integrity and diagnostics.
Unify lifecycle, guidance receipts and evidence sources across clients.
Keep direct action dispatch without an added AI quota approval layer.
Align version metadata at 2.2.0 and bundle Helper 1.2.2.

Validation: 3063 backend tests before the version bump; 44 after.
Frontend: 317 tests and production build passed.
This commit is contained in:
chenmingxuan committed 2026-10-26 00:44:13 +08:00
1 parent 83bcfd54d5
commit fac4ccb760
146 files changed
+5689 -2033

No files matched your search

+93
View File
@@ -0,0 +1,93 @@
"""A hint, OCR label and real editable value are different kinds of proof."""
from types import SimpleNamespace
import pytest
from artemis.drivers.android.hierarchy import parse_ui_hierarchy
from artemis.utils.action_target import observation_anchor
from artemis.utils.input_state import input_state, verify_input
from artemis.utils.ui_filter import filter_ui_hierarchy
from artemis.utils.visualization import format_minimal_list_with_elements
@pytest.mark.parametrize(
("extra", "status"),
[
({"text": "example", "hint": "example", "showing-hint-text": "true"}, "empty"),
({"text": "example", "hint": "example"}, "unknown"),
({"text": "example", "hint": "example", "showing-hint-text": "false"}, "present"),
({"text": "", "hint": "example"}, "empty"),
({"hint": "example"}, "unknown"),
({"text": "actual"}, "present"),
],
)
def test_editable_value_evidence(extra, status):
evidence = input_state({"class": "android.widget.EditText", **extra})
assert evidence["input_value_state"] == status
if status != "present":
assert evidence["input_value"] in (None, "")
def test_static_json_and_ocr_cannot_prove_input():
static = {"class": "android.widget.TextView", "text": '{"pairing":"example"}'}
assert input_state(static) == {}
node = {
"class": "android.widget.EditText",
"text": "",
"hint": "example",
"bounds": "[0,0][100,100]",
"ocr_elements": [{"text": "example", "bounds": [0, 0, 100, 100]}],
}
lines, indexed, _ = format_minimal_list_with_elements([node], 100, 100)
assert len(indexed) == 1
assert indexed[0]["input_value_state"] == "empty"
assert "Hint:" in lines
def test_filter_keeps_empty_input_and_does_not_promote_child_hint():
node = {
"class": "android.widget.EditText",
"editable": True,
"text": "",
"bounds": "[0,0][100,100]",
"children": [
{"class": "android.widget.TextView", "text": "example", "bounds": "[0,0][100,100]"}
],
}
filtered = filter_ui_hierarchy([node])
assert filtered[0]["text"] == ""
assert input_state(filtered[0])["input_value_state"] == "empty"
def test_fresh_value_must_belong_to_the_actual_input():
node = {
"class": "android.widget.EditText",
"resource-id": "pair",
"text": "actual",
"focused": True,
}
assert verify_input([node], {"target_resource_id": "pair"}) == "present"
assert verify_input([node], {"target_resource_id": "other"}) == "unknown"
assert verify_input([{**node, "password": True}]) == "unknown"
def test_driver_parser_keeps_showing_hint_and_empty_editable_nodes():
nodes = parse_ui_hierarchy(
'<hierarchy><node class="android.widget.EditText" bounds="[0,0][100,100]" editable="true" text="example" hint="example" showing-hint-text="true" focused="true"/></hierarchy>'
)
assert len(nodes) == 1
assert nodes[0]["input_value"] == ""
assert nodes[0]["input_value_state"] == "empty"
assert nodes[0]["focused"] is True
def test_past_anchor_does_not_change_when_index_is_reused():
state = SimpleNamespace(
latest_screenshot="before.png", indexed_elements=[{"text": "Connect", "center": [1, 2]}]
)
old = observation_anchor(state)
state.latest_screenshot = "after.png"
state.indexed_elements = [{"text": "Keyboard key", "center": [3, 4]}]
assert old["target_image_name"] == "before"
assert observation_anchor(state)["target_snapshot_id"] != old["target_snapshot_id"]