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

+15 -17
View File
@@ -193,19 +193,22 @@ async def _observe_foreground(
Fast path: the focused window's package equals the target. Otherwise the app also
counts as foreground when the top task belongs to it (see ``ForegroundTask``).
A ``None`` focused package means the window manager is mid-transition and is
reported as "not yet" without consulting the task stack.
With no focused window, a resumed activity in the app's task can still prove
readiness. A merely visible task without a resumed activity is not enough.
Returns (is_foreground, focused_package, foreground_task); the task is only read
when the fast path fails on a non-null focused package.
when the focused window does not already identify the target app.
"""
current_package = await get_current_foreground_package_async(ctx)
if current_package == app_package:
return True, current_package, None
if current_package is None:
return False, None, None
task = await get_foreground_task_async(ctx)
return task is not None and task.owns(app_package), current_package, task
ready = (
task is not None
and task.owns(app_package)
and (current_package is not None or task.resumed_package is not None)
)
return ready, current_package, task
async def _poll_for_app_ready(
@@ -216,8 +219,8 @@ async def _poll_for_app_ready(
) -> tuple[bool, str | None]:
"""Poll for app to be ready after launch.
Treats mCurrentFocus=null as a loading state and keeps polling.
Only fails if we get a different (non-null) package or timeout.
A focused window or resumed foreground task can confirm readiness. If neither
identifies the app, keep polling and return diagnostics on timeout.
Args:
ctx: Mobile use context
@@ -273,7 +276,10 @@ async def launch_app_with_retries(
max_retries: int = 3,
max_poll_seconds: int = 15,
) -> tuple[bool, str | None]:
"""Launch an app with retry logic and smart polling.
"""Launch or bring an app forward with bounded, non-destructive retries.
Stopping or restarting an app is a separate explicit operation; a failed
readiness check must not silently reset the app's running state.
Args:
ctx: Mobile use context
@@ -298,14 +304,6 @@ async def launch_app_with_retries(
span.payload = {"attempt": attempt, "app_package": app_package}
controller = UnifiedMobileController(ctx)
if attempt > 1:
logger.warning(
f"Attempt {attempt - 1} failed. Force stopping"
f" '{app_package}' to clear frozen state before retrying..."
)
await controller.terminate_app(app_package)
await asyncio.sleep(1.0)
launch_error: AppLaunchError | None = None
try:
launch_success = await controller.launch_app(app_package)