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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user