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:
1 parent
83bcfd54d5
commit
fac4ccb760
146 files changed
+5689
-2033
No files matched your search
@@ -123,11 +123,19 @@ async def test_validator_success(mock_mcp, mock_context, temp_screenshot):
|
||||
"""Test that ValidatorNode executes actions successfully."""
|
||||
decisions = json.dumps([{"action": "tap", "coordinates": [105, 205]}])
|
||||
state = DummyState(structured_decisions=decisions, latest_screenshot=temp_screenshot)
|
||||
state.initial_goal = "在真实审批表单上 Approve once,不要普通聊天代答或重复提交。"
|
||||
|
||||
with patch("artemis.utils.image_diff.check_ui_change", return_value=True):
|
||||
with (
|
||||
patch("artemis.utils.image_diff.check_ui_change", return_value=True),
|
||||
patch(
|
||||
"artemis.services.llm.get_llm",
|
||||
side_effect=AssertionError("Unexpected AI action review"),
|
||||
) as get_llm,
|
||||
):
|
||||
node = ValidatorNode(mock_context)
|
||||
result = await node(state)
|
||||
|
||||
get_llm.assert_not_called()
|
||||
assert "last_execution_result" in result
|
||||
report = result["last_execution_result"]
|
||||
assert "execution" in report
|
||||
@@ -144,10 +152,40 @@ async def test_validator_success(mock_mcp, mock_context, temp_screenshot):
|
||||
# tests/unit/mcp/test_action_session.py against the in-process ActionSession.
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pro_unconfirmed_input_stops_burst_before_submission(
|
||||
mock_mcp, mock_context, temp_screenshot
|
||||
):
|
||||
mock_mcp.hierarchy = [
|
||||
{
|
||||
"class": "android.widget.EditText",
|
||||
"resource_id": "fixture:input",
|
||||
"text": "Paste here",
|
||||
"hint": "Paste here",
|
||||
"showing_hint": True,
|
||||
"focused": True,
|
||||
"bounds": "[100,200][500,400]",
|
||||
}
|
||||
]
|
||||
decisions = [
|
||||
{"action": "focus_and_input_text", "coordinates": [300, 300], "text": "fixture value"},
|
||||
{"action": "tap", "coordinates": [300, 500]},
|
||||
]
|
||||
state = DummyState(
|
||||
structured_decisions=json.dumps(decisions), latest_screenshot=temp_screenshot
|
||||
)
|
||||
with patch("artemis.utils.image_diff.check_ui_change", return_value=False):
|
||||
result = await ValidatorNode(mock_context)(state)
|
||||
report = result["last_execution_result"]
|
||||
assert report["status"] == "failed"
|
||||
assert report["execution"][0]["input_verification"] == "empty"
|
||||
assert len(mock_mcp.calls_for("input_text")) == 1
|
||||
assert not mock_mcp.calls_for("click")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validator_exec_error_opens_incident(mock_mcp, mock_context, temp_screenshot):
|
||||
"""A vetted single action that the device rejects is retried once, then an
|
||||
execution incident is opened for the Operator (no repair agent)."""
|
||||
"""An uncertain device failure opens an incident without replaying the write."""
|
||||
mock_mcp.action_handler = lambda name, args: (
|
||||
(False, "Error: Element not found") if name == "click" else (True, "")
|
||||
)
|
||||
@@ -165,8 +203,8 @@ async def test_validator_exec_error_opens_incident(mock_mcp, mock_context, temp_
|
||||
assert len(report["execution"]) == 1
|
||||
assert report["execution"][0]["attempts"] == [
|
||||
"Error: Element not found",
|
||||
"Error: Element not found",
|
||||
]
|
||||
assert len(mock_mcp.calls_for("click")) == 1
|
||||
assert "repair" not in report["execution"][0]
|
||||
|
||||
incident = report["incident"]
|
||||
|
||||
Reference in new issue
Block a user