feat: release 2.2.0 with PC-only tests and execution reliability fixes

Unify task scheduling and queue state, fix Android input and observation recovery, preserve screenshot paths, and match final review criteria accurately. Add collapsible copyable results and publish the complete 2.1.0 upgrade notes. Validation: 3189 backend tests, 326 frontend tests, 24 client tests, production build and quality gates.
This commit is contained in:
chenmingxuan committed 2026-10-27 01:13:41 +08:00
1 parent fac4ccb760
commit be6e8c2abd
79 files changed
+2263 -277

No files matched your search

+45 -1
View File
@@ -18,6 +18,7 @@ from artemis_client import (
NotFoundError,
ProtocolError,
TaskRejectedError,
TaskResult,
TaskTimeoutError,
)
@@ -105,7 +106,50 @@ class ArtemisClientTests(unittest.IsolatedAsyncioTestCase):
assert body is not None
self.assertEqual(body["session_id"], task_id)
self.assertEqual(body["ingress"], "python_sdk")
self.assertEqual(body["options"], {"record_video": True})
self.assertEqual(body["runtime_overrides"], {"record_video": True})
async def test_pc_submission_ignores_saved_phone_and_uses_shared_options(self) -> None:
self.client.device_serial = "offline-phone"
options = {"device_serial": "old-phone", "plugins": [{"id": "cua", "profile": "default"}]}
self.transport.add(
"POST", "/api/run", {"tasks": [{"session_id": "pc", "status": "pending"}]}
)
await self.client.submit("Check desktop", execution_target="computer", options=options)
body = self.transport.calls[-1][2]
assert body is not None
self.assertNotIn("device_serial", body)
self.assertEqual(
body["runtime_overrides"],
{"execution_target": "computer", "plugins": options["plugins"]},
)
self.assertEqual(options["device_serial"], "old-phone")
async def test_pc_target_in_options_and_task_object_are_forwarded(self) -> None:
self.transport.add(
"POST", "/api/run", {"tasks": [{"session_id": "pc", "status": "pending"}]}
)
await self.client.submit("Check desktop", options={"execution_target": "computer"})
self.assertEqual(
self.transport.calls[-1][2]["runtime_overrides"], {"execution_target": "computer"}
)
from unittest.mock import AsyncMock
from types import SimpleNamespace
self.client.wait_for_task = AsyncMock(
return_value=TaskResult(task_id="pc", status="completed")
)
self.transport.add(
"POST", "/api/run", {"tasks": [{"session_id": "pc", "status": "pending"}]}
)
await self.client.run_task(SimpleNamespace(goal="Check PC", execution_target="computer"))
self.assertEqual(
self.transport.calls[-1][2]["runtime_overrides"], {"execution_target": "computer"}
)
async def test_invalid_execution_target_is_rejected_without_submission(self) -> None:
with self.assertRaisesRegex(ValueError, "execution_target"):
await self.client.submit("Check desktop", options={"execution_target": "phone"})
self.assertEqual(self.transport.calls, [])
async def test_submit_forwards_pro_tuning_knobs_normalised(self) -> None:
task_id = "00000000-0000-4000-8000-000000000321"