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

+66 -4
View File
@@ -361,7 +361,7 @@ async def test_launch_app_failure_when_top_task_is_another_app(
assert success is False
assert f"Failed to launch {SETTINGS}" in error_msg
assert mock_controller.launch_app.call_count == 2
assert mock_controller.terminate_app.call_count == 1
mock_controller.terminate_app.assert_not_called()
@pytest.mark.asyncio
@@ -399,12 +399,13 @@ async def test_launch_timeout_message_names_foreground_package_and_task(
@patch("artemis.utils.app_launch_utils.get_foreground_task")
@patch("artemis.utils.app_launch_utils.UnifiedMobileController")
@patch("artemis.utils.app_launch_utils.get_current_foreground_package_async")
async def test_launch_app_null_focus_keeps_polling_without_reading_task(
async def test_launch_app_null_focus_without_task_evidence_keeps_polling(
mock_get_foreground, mock_controller_cls, mock_get_task, mock_context
):
"""mCurrentFocus=null is a loading state: no task lookup, then success once focused."""
"""Unknown focus and task state remain loading until a focused window appears."""
_controller(mock_controller_cls)
mock_get_foreground.side_effect = [None, None, YOUTUBE]
mock_get_task.return_value = None
success, error_msg = await launch_app_with_retries(
mock_context, YOUTUBE, max_retries=1, max_poll_seconds=3
@@ -412,7 +413,67 @@ async def test_launch_app_null_focus_keeps_polling_without_reading_task(
assert success is True
assert error_msg is None
mock_get_task.assert_not_called()
assert mock_get_task.call_count == 2
@pytest.mark.asyncio
@pytest.mark.parametrize(
"dump, ready",
[
(DUMP_SETTINGS_TASK_WITH_SEARCH_ON_TOP, True),
(DUMP_NO_RESUMED, False),
(DUMP_HOME, False),
("", False),
],
ids=["resumed-app", "visible-only", "other-app", "unknown"],
)
async def test_null_focus_requires_resumed_foreground_task_evidence(mock_context, dump, ready):
with (
patch("artemis.utils.app_launch_utils.UnifiedMobileController") as controller_cls,
patch(
"artemis.utils.app_launch_utils.get_current_foreground_package_async",
new_callable=AsyncMock,
return_value=None,
),
patch(
"artemis.utils.app_launch_utils.get_foreground_task",
return_value=parse_foreground_task(dump),
),
patch("artemis.utils.app_launch_utils.asyncio.sleep", new_callable=AsyncMock),
):
controller = _controller(controller_cls)
success, detail = await launch_app_with_retries(
mock_context, SETTINGS, max_retries=2, max_poll_seconds=1
)
assert success is ready
if ready:
assert detail is None
controller.launch_app.assert_awaited_once_with(SETTINGS)
else:
assert "Current foreground: None" in detail
assert controller.launch_app.await_count == 2
controller.terminate_app.assert_not_called()
@pytest.mark.asyncio
async def test_initial_launch_skips_relaunch_with_null_focus_and_resumed_app_task(mock_context):
with (
patch(
"artemis.utils.app_launch_utils.get_current_foreground_package_async",
new_callable=AsyncMock,
return_value=None,
),
patch(
"artemis.utils.app_launch_utils.get_foreground_task",
return_value=parse_foreground_task(DUMP_SETTINGS_TASK_WITH_SEARCH_ON_TOP),
),
patch(
"artemis.utils.app_launch_utils.launch_app_with_retries", new_callable=AsyncMock
) as launch,
):
result = await _handle_initial_app_launch(mock_context, SETTINGS)
assert result.locked_app_initial_launch_success is True
launch.assert_not_awaited()
# ---------------------------------------------------------------------------
@@ -467,6 +528,7 @@ async def test_transient_launch_failure_is_bounded_and_keeps_cause(mock_context)
assert result is False
assert detail == "device offline"
assert controller.launch_app.await_count == 3
controller.terminate_app.assert_not_called()
poll.assert_not_called()