mobster-cli 0.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mobile_agent/__init__.py +4 -0
- mobile_agent/__main__.py +598 -0
- mobile_agent/accounts/__init__.py +1 -0
- mobile_agent/accounts/context.py +80 -0
- mobile_agent/accounts/mcp.py +55 -0
- mobile_agent/accounts/pacing.py +318 -0
- mobile_agent/accounts/register.py +78 -0
- mobile_agent/accounts/routes.py +212 -0
- mobile_agent/accounts/routing.py +69 -0
- mobile_agent/accounts/screen.py +273 -0
- mobile_agent/accounts/stats.py +54 -0
- mobile_agent/accounts/store.py +394 -0
- mobile_agent/agent_hooks.py +123 -0
- mobile_agent/ai_plans/__init__.py +7 -0
- mobile_agent/ai_plans/agents.py +96 -0
- mobile_agent/ai_plans/claude_code.py +208 -0
- mobile_agent/ai_plans/cli.py +400 -0
- mobile_agent/ai_plans/codex.py +258 -0
- mobile_agent/ai_plans/compat_chat.py +307 -0
- mobile_agent/ai_plans/device_login.py +164 -0
- mobile_agent/ai_plans/draft_client.py +68 -0
- mobile_agent/ai_plans/gemini.py +137 -0
- mobile_agent/ai_plans/instructions.py +65 -0
- mobile_agent/ai_plans/mcp_http.py +160 -0
- mobile_agent/ai_plans/phone_tools.py +892 -0
- mobile_agent/ai_plans/providers.py +235 -0
- mobile_agent/ai_plans/routes.py +160 -0
- mobile_agent/ai_plans/runner.py +400 -0
- mobile_agent/ai_plans/vault.py +114 -0
- mobile_agent/alerts.py +337 -0
- mobile_agent/android/__init__.py +18 -0
- mobile_agent/android/adb.py +268 -0
- mobile_agent/android/conditions.py +128 -0
- mobile_agent/android/doctor.py +54 -0
- mobile_agent/android/driver.py +137 -0
- mobile_agent/android/emulator.py +216 -0
- mobile_agent/android/logs.py +85 -0
- mobile_agent/android/manager.py +225 -0
- mobile_agent/android/records.py +31 -0
- mobile_agent/android/tree.py +96 -0
- mobile_agent/answer_shapes.py +230 -0
- mobile_agent/api_devices.py +333 -0
- mobile_agent/api_errors.py +17 -0
- mobile_agent/api_get.py +249 -0
- mobile_agent/api_http.py +467 -0
- mobile_agent/api_post.py +453 -0
- mobile_agent/api_routes.py +173 -0
- mobile_agent/app_api/__init__.py +4 -0
- mobile_agent/app_api/cli.py +151 -0
- mobile_agent/app_api/endpoints.py +193 -0
- mobile_agent/app_api/mcp.py +54 -0
- mobile_agent/app_api/openapi.py +146 -0
- mobile_agent/app_api/reading.py +172 -0
- mobile_agent/app_api/register.py +10 -0
- mobile_agent/app_api/screen.py +216 -0
- mobile_agent/app_api/service.py +382 -0
- mobile_agent/app_api/sessions.py +404 -0
- mobile_agent/app_api/tokens.py +167 -0
- mobile_agent/app_choice.py +131 -0
- mobile_agent/app_icons.py +183 -0
- mobile_agent/app_killer.py +69 -0
- mobile_agent/artifacts.py +606 -0
- mobile_agent/attach/__init__.py +13 -0
- mobile_agent/attach/cli.py +46 -0
- mobile_agent/attach/flutter.py +28 -0
- mobile_agent/attach/score.py +119 -0
- mobile_agent/attach/web.py +13 -0
- mobile_agent/attachments/__init__.py +1 -0
- mobile_agent/attachments/agent.py +418 -0
- mobile_agent/attachments/extract.py +217 -0
- mobile_agent/attachments/mcp.py +104 -0
- mobile_agent/attachments/register.py +73 -0
- mobile_agent/attachments/routes.py +326 -0
- mobile_agent/attachments/service.py +170 -0
- mobile_agent/attachments/sniff.py +152 -0
- mobile_agent/attachments/store.py +441 -0
- mobile_agent/audit_verify.py +436 -0
- mobile_agent/bench/README.md +410 -0
- mobile_agent/bench/__init__.py +0 -0
- mobile_agent/bench/__main__.py +394 -0
- mobile_agent/bench/ab.py +168 -0
- mobile_agent/bench/actuation.py +368 -0
- mobile_agent/bench/agents/__init__.py +9 -0
- mobile_agent/bench/agents/base.py +105 -0
- mobile_agent/bench/agents/plan_clients.py +245 -0
- mobile_agent/bench/agents/smart.py +533 -0
- mobile_agent/bench/checks.py +358 -0
- mobile_agent/bench/decide_gate.py +214 -0
- mobile_agent/bench/diagnose.py +181 -0
- mobile_agent/bench/evals/__init__.py +14 -0
- mobile_agent/bench/evals/build.py +565 -0
- mobile_agent/bench/evals/checkpoint.py +214 -0
- mobile_agent/bench/evals/irt.py +219 -0
- mobile_agent/bench/evals/l0.py +647 -0
- mobile_agent/bench/evals/l1.py +866 -0
- mobile_agent/bench/evals/models.py +124 -0
- mobile_agent/bench/evals/pairwise.py +189 -0
- mobile_agent/bench/evals/proxy.py +90 -0
- mobile_agent/bench/evals/sets.py +149 -0
- mobile_agent/bench/evals/traces.py +446 -0
- mobile_agent/bench/fixtures.py +176 -0
- mobile_agent/bench/guard_replay.py +129 -0
- mobile_agent/bench/h2-manifest.json +20 -0
- mobile_agent/bench/h3-manifest.json +20 -0
- mobile_agent/bench/heldout-manifest.json +32 -0
- mobile_agent/bench/heldout.py +375 -0
- mobile_agent/bench/iosworld.py +901 -0
- mobile_agent/bench/iosworld_stats.py +510 -0
- mobile_agent/bench/llm_units.py +116 -0
- mobile_agent/bench/manifest.json +93 -0
- mobile_agent/bench/metrics.py +66 -0
- mobile_agent/bench/phone.py +44 -0
- mobile_agent/bench/replay.py +172 -0
- mobile_agent/bench/report.py +207 -0
- mobile_agent/bench/runner.py +456 -0
- mobile_agent/bench/safety.py +310 -0
- mobile_agent/bench/stream_timing.py +213 -0
- mobile_agent/bench/suite.py +444 -0
- mobile_agent/bench/sweep.py +176 -0
- mobile_agent/bench/tape.py +798 -0
- mobile_agent/bench/tape_replay.py +88 -0
- mobile_agent/bench/trace_evals.py +128 -0
- mobile_agent/bench/trace_view.py +81 -0
- mobile_agent/bench/truth/iphone15pro.json +138 -0
- mobile_agent/bench/truth.py +340 -0
- mobile_agent/bench/tuning.py +543 -0
- mobile_agent/bench/tuning_analysis.py +656 -0
- mobile_agent/bench/vague-heldout-manifest.json +12 -0
- mobile_agent/bench/vague_rubric.py +135 -0
- mobile_agent/bench/vague_run.py +243 -0
- mobile_agent/brand.py +454 -0
- mobile_agent/brand_fonts.py +756 -0
- mobile_agent/build_identity.py +21 -0
- mobile_agent/cable_bt.py +623 -0
- mobile_agent/cable_bt_runtime.py +128 -0
- mobile_agent/catalog.py +69 -0
- mobile_agent/chart_svg.py +415 -0
- mobile_agent/chat_here.py +189 -0
- mobile_agent/cli_constants.py +5 -0
- mobile_agent/cli_parser.py +611 -0
- mobile_agent/cli_run.py +115 -0
- mobile_agent/compiled_read/__init__.py +21 -0
- mobile_agent/compiled_read/delta.py +89 -0
- mobile_agent/compiled_read/extractors.py +202 -0
- mobile_agent/compiled_read/routes.py +56 -0
- mobile_agent/completion.py +267 -0
- mobile_agent/compose.py +48 -0
- mobile_agent/config.py +114 -0
- mobile_agent/connectors/__init__.py +1 -0
- mobile_agent/connectors/api_session.py +76 -0
- mobile_agent/connectors/callback_page.py +146 -0
- mobile_agent/connectors/client.py +582 -0
- mobile_agent/connectors/github_app.py +55 -0
- mobile_agent/connectors/graph_api.py +396 -0
- mobile_agent/connectors/imports.py +170 -0
- mobile_agent/connectors/issue.py +66 -0
- mobile_agent/connectors/keystore.py +85 -0
- mobile_agent/connectors/marks.py +222 -0
- mobile_agent/connectors/oauth.py +476 -0
- mobile_agent/connectors/picker.py +228 -0
- mobile_agent/connectors/presets.py +591 -0
- mobile_agent/connectors/register.py +48 -0
- mobile_agent/connectors/routes.py +153 -0
- mobile_agent/connectors/service.py +966 -0
- mobile_agent/connectors/shared.py +136 -0
- mobile_agent/connectors/skill.py +446 -0
- mobile_agent/connectors/slack_api.py +269 -0
- mobile_agent/connectors/steps.py +92 -0
- mobile_agent/connectors/store.py +144 -0
- mobile_agent/connectors/vault.py +72 -0
- mobile_agent/console.py +178 -0
- mobile_agent/content_queue/__init__.py +1 -0
- mobile_agent/content_queue/client.py +263 -0
- mobile_agent/content_queue/importing.py +153 -0
- mobile_agent/content_queue/media_check.py +224 -0
- mobile_agent/content_queue/prompt.py +64 -0
- mobile_agent/content_queue/register.py +91 -0
- mobile_agent/content_queue/routes.py +370 -0
- mobile_agent/content_queue/runner.py +475 -0
- mobile_agent/content_queue/store.py +521 -0
- mobile_agent/contract.py +996 -0
- mobile_agent/contract_asks.py +248 -0
- mobile_agent/contract_prompt.py +45 -0
- mobile_agent/contract_screen.py +981 -0
- mobile_agent/contract_view.py +84 -0
- mobile_agent/corpus/__init__.py +26 -0
- mobile_agent/corpus/cli.py +63 -0
- mobile_agent/corpus/consent.py +55 -0
- mobile_agent/corpus/listener.py +76 -0
- mobile_agent/corpus/register.py +9 -0
- mobile_agent/corpus/skeleton.py +72 -0
- mobile_agent/corpus/store.py +143 -0
- mobile_agent/costs.py +161 -0
- mobile_agent/decider_scope.py +40 -0
- mobile_agent/decision_provider.py +189 -0
- mobile_agent/deeplinks.py +352 -0
- mobile_agent/device_info.py +116 -0
- mobile_agent/device_manager.py +978 -0
- mobile_agent/device_status.py +195 -0
- mobile_agent/device_targets.py +217 -0
- mobile_agent/devices.py +676 -0
- mobile_agent/devkit/__init__.py +64 -0
- mobile_agent/devkit/account_cli.py +90 -0
- mobile_agent/devkit/asc.py +562 -0
- mobile_agent/devkit/asc_cli.py +214 -0
- mobile_agent/devkit/ci.py +737 -0
- mobile_agent/devkit/ci_cli.py +196 -0
- mobile_agent/devkit/ci_hosts.py +194 -0
- mobile_agent/devkit/es256.py +197 -0
- mobile_agent/devkit/expo.py +440 -0
- mobile_agent/devkit/expo_cli.py +405 -0
- mobile_agent/devkit/expo_dev.py +203 -0
- mobile_agent/devkit/expo_routes.py +90 -0
- mobile_agent/devkit/fixtures_cli.py +133 -0
- mobile_agent/devkit/fixtures_routes.py +82 -0
- mobile_agent/devkit/links.py +375 -0
- mobile_agent/devkit/links_cli.py +123 -0
- mobile_agent/devkit/links_routes.py +83 -0
- mobile_agent/devkit/nightly.py +152 -0
- mobile_agent/devkit/shots.py +203 -0
- mobile_agent/devkit/testflight.py +155 -0
- mobile_agent/devkit/testflight_cli.py +127 -0
- mobile_agent/devkit/testflight_routes.py +111 -0
- mobile_agent/devkit/xcode.py +236 -0
- mobile_agent/devkit/xcode_cli.py +101 -0
- mobile_agent/devtools.py +419 -0
- mobile_agent/doctor.py +509 -0
- mobile_agent/doctor_fix.py +467 -0
- mobile_agent/downloads.py +55 -0
- mobile_agent/drivers.py +793 -0
- mobile_agent/dry_run.py +102 -0
- mobile_agent/effect_ledger.py +83 -0
- mobile_agent/engine_health.py +378 -0
- mobile_agent/engine_hooks.py +96 -0
- mobile_agent/engines.py +408 -0
- mobile_agent/env_file.py +63 -0
- mobile_agent/env_secrets.py +149 -0
- mobile_agent/errors.py +15 -0
- mobile_agent/evals/__init__.py +3 -0
- mobile_agent/evals/app_survey.py +149 -0
- mobile_agent/evals/development/__init__.py +1 -0
- mobile_agent/evals/development/score.py +276 -0
- mobile_agent/evals/latency_report.py +312 -0
- mobile_agent/evals/offline_report.py +374 -0
- mobile_agent/evals/oracles.py +298 -0
- mobile_agent/evals/redteam/__init__.py +15 -0
- mobile_agent/evals/redteam/__main__.py +72 -0
- mobile_agent/evals/redteam/cases.py +730 -0
- mobile_agent/evals/redteam/harness.py +604 -0
- mobile_agent/evals/redteam/live.py +139 -0
- mobile_agent/evals/redteam/plain.py +85 -0
- mobile_agent/evals/vision/__init__.py +0 -0
- mobile_agent/evals/vision/images/dog_001.jpg +0 -0
- mobile_agent/evals/vision/images/dog_002.jpg +0 -0
- mobile_agent/evals/vision/images/dog_003.jpg +0 -0
- mobile_agent/evals/vision/images/dog_004.jpg +0 -0
- mobile_agent/evals/vision/images/dog_005.jpg +0 -0
- mobile_agent/evals/vision/images/dog_006.jpg +0 -0
- mobile_agent/evals/vision/images/dog_007.jpg +0 -0
- mobile_agent/evals/vision/images/dog_010.jpg +0 -0
- mobile_agent/evals/vision/images/dog_011.jpg +0 -0
- mobile_agent/evals/vision/images/dog_013.jpg +0 -0
- mobile_agent/evals/vision/images/dog_014.jpg +0 -0
- mobile_agent/evals/vision/images/dog_015.jpg +0 -0
- mobile_agent/evals/vision/images/dog_016.jpg +0 -0
- mobile_agent/evals/vision/images/dog_017.jpg +0 -0
- mobile_agent/evals/vision/images/dog_019.jpg +0 -0
- mobile_agent/evals/vision/images/dog_020.jpg +0 -0
- mobile_agent/evals/vision/images/dog_021.jpg +0 -0
- mobile_agent/evals/vision/images/dog_022.jpg +0 -0
- mobile_agent/evals/vision/images/dog_023.jpg +0 -0
- mobile_agent/evals/vision/images/dog_024.jpg +0 -0
- mobile_agent/evals/vision/images/dog_026.jpg +0 -0
- mobile_agent/evals/vision/images/dog_027.jpg +0 -0
- mobile_agent/evals/vision/images/dog_034.jpg +0 -0
- mobile_agent/evals/vision/images/dog_035.jpg +0 -0
- mobile_agent/evals/vision/images/dog_038.jpg +0 -0
- mobile_agent/evals/vision/images/dog_042.jpg +0 -0
- mobile_agent/evals/vision/images/dog_047.jpg +0 -0
- mobile_agent/evals/vision/images/dog_052.jpg +0 -0
- mobile_agent/evals/vision/images/dog_058.jpg +0 -0
- mobile_agent/evals/vision/images/dog_062.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_000.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_010.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_011.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_015.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_016.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_017.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_019.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_021.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_025.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_026.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_027.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_030.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_032.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_033.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_034.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_035.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_036.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_037.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_039.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_040.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_041.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_042.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_043.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_044.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_046.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_047.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_048.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_049.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_051.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_053.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_054.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_055.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_058.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_059.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_002.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_003.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_004.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_007.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_009.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_010.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_011.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_012.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_013.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_014.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_016.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_017.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_023.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_027.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_029.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_032.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_033.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_036.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_041.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_043.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_047.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_049.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_051.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_053.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_056.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_057.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_058.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_059.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_060.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_061.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_062.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_063.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_064.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_065.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_066.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_067.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_068.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_069.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_070.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_071.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_000.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_001.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_002.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_004.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_005.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_008.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_009.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_010.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_012.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_013.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_015.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_016.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_017.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_018.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_021.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_022.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_023.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_024.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_030.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_031.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_032.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_033.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_034.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_036.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_038.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_039.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_040.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_041.jpg +0 -0
- mobile_agent/evals/vision/manifest.json +2355 -0
- mobile_agent/eventbus.py +110 -0
- mobile_agent/explain.py +34 -0
- mobile_agent/failure_line.py +197 -0
- mobile_agent/fanout.py +663 -0
- mobile_agent/feedback/__init__.py +2 -0
- mobile_agent/feedback/bundle.py +204 -0
- mobile_agent/feedback/outbox.py +196 -0
- mobile_agent/feedback/register.py +9 -0
- mobile_agent/feedback/routes.py +177 -0
- mobile_agent/fleet.py +841 -0
- mobile_agent/fleet_health.py +729 -0
- mobile_agent/fleet_phone.py +186 -0
- mobile_agent/frontier/__init__.py +79 -0
- mobile_agent/frontier/act.py +380 -0
- mobile_agent/frontier/actions.py +70 -0
- mobile_agent/frontier/agent.py +256 -0
- mobile_agent/frontier/approvals.py +366 -0
- mobile_agent/frontier/hooks.py +199 -0
- mobile_agent/frontier/interrupts.py +125 -0
- mobile_agent/frontier/observe.py +331 -0
- mobile_agent/frontier/phases.py +381 -0
- mobile_agent/frontier/pipeline.py +61 -0
- mobile_agent/frontier/prompt.py +384 -0
- mobile_agent/frontier/protocol.py +91 -0
- mobile_agent/frontier/safety.py +467 -0
- mobile_agent/frontier/saved.py +375 -0
- mobile_agent/frontier/screen.py +419 -0
- mobile_agent/frontier/skills.py +147 -0
- mobile_agent/frontier/stages/__init__.py +1 -0
- mobile_agent/frontier/stages/after.py +192 -0
- mobile_agent/frontier/stages/approve.py +138 -0
- mobile_agent/frontier/stages/controls.py +115 -0
- mobile_agent/frontier/stages/ends.py +102 -0
- mobile_agent/frontier/stages/gate.py +92 -0
- mobile_agent/frontier/stages/guards.py +132 -0
- mobile_agent/frontier/stages/points.py +155 -0
- mobile_agent/frontier/stages/send.py +177 -0
- mobile_agent/frontier/turn.py +151 -0
- mobile_agent/full_disk_access.py +83 -0
- mobile_agent/gemini.py +69 -0
- mobile_agent/genuine.py +120 -0
- mobile_agent/graph/__init__.py +4 -0
- mobile_agent/graph/catalog.py +789 -0
- mobile_agent/graph/chain.py +306 -0
- mobile_agent/graph/dispatch.py +98 -0
- mobile_agent/graph/draft.py +373 -0
- mobile_agent/graph/edit.py +76 -0
- mobile_agent/graph/estimate.py +189 -0
- mobile_agent/graph/expr.py +562 -0
- mobile_agent/graph/functions.py +271 -0
- mobile_agent/graph/infer.py +144 -0
- mobile_agent/graph/nodes.py +936 -0
- mobile_agent/graph/phone_steps.py +178 -0
- mobile_agent/graph/rerun.py +327 -0
- mobile_agent/graph/run.py +899 -0
- mobile_agent/graph/schema.py +842 -0
- mobile_agent/graph/state.py +161 -0
- mobile_agent/graph/values.py +198 -0
- mobile_agent/harness/__init__.py +2 -0
- mobile_agent/harness/checkpoints.py +468 -0
- mobile_agent/harness/control.py +101 -0
- mobile_agent/harness/prewarm.py +213 -0
- mobile_agent/harness/register.py +43 -0
- mobile_agent/harness/routes.py +114 -0
- mobile_agent/harness/settings.py +66 -0
- mobile_agent/harness/tools/__init__.py +67 -0
- mobile_agent/harness/tools/ask_user.py +102 -0
- mobile_agent/harness/tools/watch.py +120 -0
- mobile_agent/harness_api.py +815 -0
- mobile_agent/hedge.py +203 -0
- mobile_agent/help_topics.py +305 -0
- mobile_agent/hints.py +248 -0
- mobile_agent/history.py +69 -0
- mobile_agent/home.py +214 -0
- mobile_agent/http_pool.py +175 -0
- mobile_agent/image_policy.py +139 -0
- mobile_agent/inbox.py +148 -0
- mobile_agent/installed_apps.py +344 -0
- mobile_agent/integrations/__init__.py +1 -0
- mobile_agent/integrations/cli.py +472 -0
- mobile_agent/integrations/client_files.py +65 -0
- mobile_agent/integrations/clients.py +998 -0
- mobile_agent/integrations/doctor.py +185 -0
- mobile_agent/integrations/jsonc.py +383 -0
- mobile_agent/integrations/routes.py +135 -0
- mobile_agent/integrations/rules.py +112 -0
- mobile_agent/integrations/server_options.py +46 -0
- mobile_agent/integrations/skill.py +152 -0
- mobile_agent/integrations/skill_files.py +10 -0
- mobile_agent/integrations/textedit.py +309 -0
- mobile_agent/intents/__init__.py +17 -0
- mobile_agent/intents/atlas.py +216 -0
- mobile_agent/intents/callback.py +44 -0
- mobile_agent/intents/cli.py +55 -0
- mobile_agent/intents/harvest.py +91 -0
- mobile_agent/intents/plan.py +93 -0
- mobile_agent/ios/__init__.py +1 -0
- mobile_agent/ios/guide.py +16 -0
- mobile_agent/ios/kb.py +212 -0
- mobile_agent/ios/kb_data.py +1588 -0
- mobile_agent/ios/mcp.py +44 -0
- mobile_agent/ios/register.py +8 -0
- mobile_agent/ios/system.py +369 -0
- mobile_agent/journal.py +531 -0
- mobile_agent/keychain_acl.py +374 -0
- mobile_agent/keys.py +395 -0
- mobile_agent/latency_overlap.py +36 -0
- mobile_agent/latency_trace.py +170 -0
- mobile_agent/learned/__init__.py +15 -0
- mobile_agent/learned/app_path.py +160 -0
- mobile_agent/learned/code_read.py +38 -0
- mobile_agent/learned/parts.py +546 -0
- mobile_agent/learned/programs.py +691 -0
- mobile_agent/learned/register.py +171 -0
- mobile_agent/learned/routes.py +61 -0
- mobile_agent/learned/taught.py +329 -0
- mobile_agent/learned/teach.py +538 -0
- mobile_agent/learned/workflows.py +647 -0
- mobile_agent/learned/zero.py +41 -0
- mobile_agent/links.py +37 -0
- mobile_agent/listen.py +607 -0
- mobile_agent/lock_spike.py +278 -0
- mobile_agent/lockscreen.py +526 -0
- mobile_agent/login.py +354 -0
- mobile_agent/longform/__init__.py +15 -0
- mobile_agent/longform/ads.py +191 -0
- mobile_agent/longform/blocks.py +117 -0
- mobile_agent/longform/budget.py +265 -0
- mobile_agent/longform/context.py +285 -0
- mobile_agent/longform/costs.py +93 -0
- mobile_agent/longform/digest.py +145 -0
- mobile_agent/longform/extract.py +508 -0
- mobile_agent/longform/feed.py +505 -0
- mobile_agent/longform/followup.py +168 -0
- mobile_agent/longform/frames.py +82 -0
- mobile_agent/longform/identity.py +87 -0
- mobile_agent/longform/inbox.py +221 -0
- mobile_agent/longform/items.py +425 -0
- mobile_agent/longform/models.py +115 -0
- mobile_agent/longform/notebook.py +329 -0
- mobile_agent/longform/plan.py +614 -0
- mobile_agent/longform/recall.py +195 -0
- mobile_agent/longform/receipts.py +81 -0
- mobile_agent/longform/register.py +124 -0
- mobile_agent/longform/report.py +343 -0
- mobile_agent/longform/routes.py +102 -0
- mobile_agent/longform/session.py +644 -0
- mobile_agent/longform/store.py +146 -0
- mobile_agent/longform/synthesis.py +347 -0
- mobile_agent/mac_tools.py +223 -0
- mobile_agent/manual_control.py +143 -0
- mobile_agent/maps.py +149 -0
- mobile_agent/marks.py +92 -0
- mobile_agent/mcp_server/__init__.py +3 -0
- mobile_agent/mcp_server/__main__.py +29 -0
- mobile_agent/mcp_server/action_journal.py +24 -0
- mobile_agent/mcp_server/action_tools.py +638 -0
- mobile_agent/mcp_server/approvals.py +219 -0
- mobile_agent/mcp_server/catalog.py +92 -0
- mobile_agent/mcp_server/cli.py +194 -0
- mobile_agent/mcp_server/codes.py +147 -0
- mobile_agent/mcp_server/device_tools.py +306 -0
- mobile_agent/mcp_server/direct.py +272 -0
- mobile_agent/mcp_server/expo_tools.py +105 -0
- mobile_agent/mcp_server/phone_tools.py +510 -0
- mobile_agent/mcp_server/protocol.py +364 -0
- mobile_agent/mcp_server/registry.py +55 -0
- mobile_agent/mcp_server/run_tools.py +531 -0
- mobile_agent/mcp_server/schemas.py +425 -0
- mobile_agent/mcp_server/screen_tools.py +168 -0
- mobile_agent/mcp_server/session.py +469 -0
- mobile_agent/mcp_server/support.py +145 -0
- mobile_agent/mcp_server/tools.py +493 -0
- mobile_agent/memory/__init__.py +9 -0
- mobile_agent/memory/cli.py +207 -0
- mobile_agent/memory/extract.py +191 -0
- mobile_agent/memory/listener.py +100 -0
- mobile_agent/memory/model.py +144 -0
- mobile_agent/memory/project.py +96 -0
- mobile_agent/memory/provider.py +127 -0
- mobile_agent/memory/register.py +21 -0
- mobile_agent/memory/retrieval.py +172 -0
- mobile_agent/memory/routes.py +216 -0
- mobile_agent/memory/runs.py +124 -0
- mobile_agent/memory/store.py +806 -0
- mobile_agent/memory/writer.py +278 -0
- mobile_agent/mirroring.py +585 -0
- mobile_agent/mirroring_runtime.py +221 -0
- mobile_agent/model_clients.py +604 -0
- mobile_agent/narrate.py +813 -0
- mobile_agent/native_helpers.py +84 -0
- mobile_agent/netxray/__init__.py +1 -0
- mobile_agent/netxray/analyze.py +322 -0
- mobile_agent/netxray/attribution.py +62 -0
- mobile_agent/netxray/capture.py +88 -0
- mobile_agent/netxray/cli.py +142 -0
- mobile_agent/netxray/job.py +219 -0
- mobile_agent/netxray/mobster-pcap.c +560 -0
- mobile_agent/netxray/packets.py +410 -0
- mobile_agent/netxray/pcapng.py +223 -0
- mobile_agent/netxray/register.py +93 -0
- mobile_agent/netxray/report.py +214 -0
- mobile_agent/netxray/store.py +48 -0
- mobile_agent/netxray/summary.py +157 -0
- mobile_agent/netxray/target.py +67 -0
- mobile_agent/netxray/tool.py +66 -0
- mobile_agent/netxray/trackers.py +194 -0
- mobile_agent/notify_triggers/__init__.py +1 -0
- mobile_agent/notify_triggers/rules.py +39 -0
- mobile_agent/notify_triggers/sources.py +315 -0
- mobile_agent/notify_triggers/watch.py +135 -0
- mobile_agent/offers.py +86 -0
- mobile_agent/orchestrate.py +215 -0
- mobile_agent/outlets/__init__.py +1 -0
- mobile_agent/outlets/connect.py +249 -0
- mobile_agent/outlets/folder.py +180 -0
- mobile_agent/outlets/hooks.py +453 -0
- mobile_agent/outlets/inbound.py +472 -0
- mobile_agent/outlets/inbound_reply.py +57 -0
- mobile_agent/outlets/inbound_routes.py +81 -0
- mobile_agent/outlets/kinds.py +308 -0
- mobile_agent/outlets/mail.py +110 -0
- mobile_agent/outlets/net.py +86 -0
- mobile_agent/outlets/notion.py +244 -0
- mobile_agent/outlets/register.py +56 -0
- mobile_agent/outlets/routes.py +135 -0
- mobile_agent/outlets/service.py +580 -0
- mobile_agent/outlets/sheets.py +169 -0
- mobile_agent/outlets/slack.py +177 -0
- mobile_agent/outlets/store.py +194 -0
- mobile_agent/outlets/vault.py +80 -0
- mobile_agent/outlets/webhook.py +316 -0
- mobile_agent/output_contract.py +141 -0
- mobile_agent/owner_presence.py +211 -0
- mobile_agent/ownership.py +219 -0
- mobile_agent/packs/__init__.py +1 -0
- mobile_agent/packs/calendar.py +243 -0
- mobile_agent/packs/context.py +68 -0
- mobile_agent/packs/core.py +757 -0
- mobile_agent/packs/front_door.py +316 -0
- mobile_agent/packs/heal.py +214 -0
- mobile_agent/packs/health.py +122 -0
- mobile_agent/packs/insights.py +76 -0
- mobile_agent/packs/instagram.py +640 -0
- mobile_agent/packs/linkedin.py +106 -0
- mobile_agent/packs/maps.py +196 -0
- mobile_agent/packs/mcp.py +149 -0
- mobile_agent/packs/messages.py +270 -0
- mobile_agent/packs/notes.py +226 -0
- mobile_agent/packs/photos.py +166 -0
- mobile_agent/packs/pins.py +47 -0
- mobile_agent/packs/reddit.py +110 -0
- mobile_agent/packs/register.py +276 -0
- mobile_agent/packs/rows.py +108 -0
- mobile_agent/packs/safari.py +143 -0
- mobile_agent/packs/settings.py +253 -0
- mobile_agent/packs/social.py +300 -0
- mobile_agent/packs/threads.py +97 -0
- mobile_agent/packs/tiktok.py +455 -0
- mobile_agent/packs/tools.py +290 -0
- mobile_agent/packs/x.py +127 -0
- mobile_agent/packs/youtube.py +199 -0
- mobile_agent/parity.py +270 -0
- mobile_agent/paths.py +99 -0
- mobile_agent/perception.py +470 -0
- mobile_agent/permissions/__init__.py +32 -0
- mobile_agent/permissions/consequence.py +535 -0
- mobile_agent/permissions/exceptions.py +99 -0
- mobile_agent/permissions/gate.py +316 -0
- mobile_agent/permissions/outside.py +316 -0
- mobile_agent/permissions/policy.py +288 -0
- mobile_agent/permissions/project_policy.py +411 -0
- mobile_agent/permissions/register.py +148 -0
- mobile_agent/permissions/rules.py +291 -0
- mobile_agent/phone_io/__init__.py +146 -0
- mobile_agent/phone_io/cli.py +427 -0
- mobile_agent/phone_io/clipboard.py +78 -0
- mobile_agent/phone_io/controls.py +78 -0
- mobile_agent/phone_io/files.py +289 -0
- mobile_agent/phone_io/handoff.py +123 -0
- mobile_agent/phone_io/icloud.py +118 -0
- mobile_agent/phone_io/install.py +188 -0
- mobile_agent/phone_io/network_capture.py +188 -0
- mobile_agent/phone_io/network_cli.py +138 -0
- mobile_agent/phone_io/network_profiles.py +138 -0
- mobile_agent/phone_io/photos.py +512 -0
- mobile_agent/phone_io/recording.py +115 -0
- mobile_agent/phone_io/secrets.py +146 -0
- mobile_agent/phone_io/transfer.py +895 -0
- mobile_agent/phone_io/tunnel.py +129 -0
- mobile_agent/phone_ready.py +91 -0
- mobile_agent/pinning.py +158 -0
- mobile_agent/plan_gate.py +807 -0
- mobile_agent/plugins/__init__.py +3 -0
- mobile_agent/plugins/install.py +163 -0
- mobile_agent/plugins/manifest.py +242 -0
- mobile_agent/plugins/register.py +15 -0
- mobile_agent/plugins/routes.py +82 -0
- mobile_agent/plugins/service.py +322 -0
- mobile_agent/plugins/skills.py +76 -0
- mobile_agent/plugins/store.py +79 -0
- mobile_agent/pool.py +285 -0
- mobile_agent/power_watch.py +275 -0
- mobile_agent/premium_model.py +22 -0
- mobile_agent/project_env.py +291 -0
- mobile_agent/project_env_cli.py +74 -0
- mobile_agent/project_env_routes.py +55 -0
- mobile_agent/projects/__init__.py +27 -0
- mobile_agent/projects/ci.py +332 -0
- mobile_agent/projects/clone.py +339 -0
- mobile_agent/projects/drafts.py +158 -0
- mobile_agent/projects/folder.py +436 -0
- mobile_agent/projects/github.py +131 -0
- mobile_agent/projects/register.py +31 -0
- mobile_agent/projects/revisions.py +253 -0
- mobile_agent/projects/routes.py +234 -0
- mobile_agent/projects/runs.py +297 -0
- mobile_agent/projects/service.py +695 -0
- mobile_agent/projects/setup.py +84 -0
- mobile_agent/projects/store.py +379 -0
- mobile_agent/projects/sync.py +380 -0
- mobile_agent/projects/workflow_files.py +270 -0
- mobile_agent/replay_core/__init__.py +26 -0
- mobile_agent/replay_core/checks.py +594 -0
- mobile_agent/replay_core/engine.py +596 -0
- mobile_agent/replay_core/heal.py +364 -0
- mobile_agent/replay_core/path.py +240 -0
- mobile_agent/replay_core/recorder.py +206 -0
- mobile_agent/replay_core/store.py +171 -0
- mobile_agent/result_blocks.py +941 -0
- mobile_agent/run.py +642 -0
- mobile_agent/run_delivery.py +123 -0
- mobile_agent/run_executor.py +200 -0
- mobile_agent/run_export.py +284 -0
- mobile_agent/run_gif.py +891 -0
- mobile_agent/runner_hints.py +86 -0
- mobile_agent/runner_renewal.py +205 -0
- mobile_agent/runtime_create.py +549 -0
- mobile_agent/runtime_driver.py +458 -0
- mobile_agent/runtime_limits.py +319 -0
- mobile_agent/runtime_queue.py +394 -0
- mobile_agent/runtime_runs.py +383 -0
- mobile_agent/runtime_status.py +181 -0
- mobile_agent/runtime_teach.py +295 -0
- mobile_agent/runtime_work.py +599 -0
- mobile_agent/screen_text.py +101 -0
- mobile_agent/secret_filter.py +113 -0
- mobile_agent/serve.py +160 -0
- mobile_agent/server.py +363 -0
- mobile_agent/service_support.py +42 -0
- mobile_agent/settle/__init__.py +1 -0
- mobile_agent/settle/predict.py +102 -0
- mobile_agent/settle/replay.py +92 -0
- mobile_agent/settle/store.py +93 -0
- mobile_agent/settle/stream.py +43 -0
- mobile_agent/setup_cli.py +225 -0
- mobile_agent/setup_errors.py +87 -0
- mobile_agent/setup_service.py +527 -0
- mobile_agent/share.py +104 -0
- mobile_agent/signing.py +184 -0
- mobile_agent/sim/__init__.py +6 -0
- mobile_agent/sim/__main__.py +26 -0
- mobile_agent/sim/api.py +952 -0
- mobile_agent/sim/apps.py +172 -0
- mobile_agent/sim/biometrics.py +84 -0
- mobile_agent/sim/cli.py +271 -0
- mobile_agent/sim/doctor.py +293 -0
- mobile_agent/sim/governor.py +90 -0
- mobile_agent/sim/holders.py +88 -0
- mobile_agent/sim/models.py +62 -0
- mobile_agent/sim/registry.py +161 -0
- mobile_agent/sim/simctl.py +269 -0
- mobile_agent/sim/wda.py +618 -0
- mobile_agent/sim/xcode.py +74 -0
- mobile_agent/skills/__init__.py +12 -0
- mobile_agent/skills/codes.py +597 -0
- mobile_agent/skills/notifications.py +281 -0
- mobile_agent/skills/open_url.py +127 -0
- mobile_agent/smart_events.py +588 -0
- mobile_agent/smart_result.py +490 -0
- mobile_agent/smart_run.py +413 -0
- mobile_agent/state.py +662 -0
- mobile_agent/states.py +217 -0
- mobile_agent/steering.py +70 -0
- mobile_agent/style.py +178 -0
- mobile_agent/supervised.py +234 -0
- mobile_agent/swift/helper_bt_hid.swift +534 -0
- mobile_agent/swift/helper_doc_text.swift +184 -0
- mobile_agent/swift/helper_draft_reply.swift +170 -0
- mobile_agent/swift/helper_map.swift +110 -0
- mobile_agent/swift/helper_mirroring.swift +1099 -0
- mobile_agent/swift/helper_mjpeg_mp4.swift +103 -0
- mobile_agent/swift/helper_pdf.swift +100 -0
- mobile_agent/swift/helper_phone_audio.swift +993 -0
- mobile_agent/swift/helper_phone_screen.swift +416 -0
- mobile_agent/swift/helper_screen_text.swift +24 -0
- mobile_agent/swift/helper_transfer.swift +246 -0
- mobile_agent/swift/ocr.swift +22 -0
- mobile_agent/task_policy.py +337 -0
- mobile_agent/team_signing/__init__.py +34 -0
- mobile_agent/team_signing/apple_ca.py +37 -0
- mobile_agent/team_signing/bundle.py +136 -0
- mobile_agent/team_signing/cli.py +208 -0
- mobile_agent/team_signing/identity.py +248 -0
- mobile_agent/team_signing/ledger.py +105 -0
- mobile_agent/team_signing/prebuilt.py +61 -0
- mobile_agent/team_signing/resign.py +256 -0
- mobile_agent/team_signing/service.py +382 -0
- mobile_agent/team_signing/tooling.py +243 -0
- mobile_agent/team_signing/writes.py +268 -0
- mobile_agent/terminal_image.py +81 -0
- mobile_agent/testkit/__init__.py +1 -0
- mobile_agent/testkit/a11y_baseline.py +132 -0
- mobile_agent/testkit/api.py +403 -0
- mobile_agent/testkit/capsule.py +400 -0
- mobile_agent/testkit/capsule_cli.py +57 -0
- mobile_agent/testkit/cli.py +948 -0
- mobile_agent/testkit/cli_arguments.py +193 -0
- mobile_agent/testkit/comparison.py +288 -0
- mobile_agent/testkit/comparison_cli.py +41 -0
- mobile_agent/testkit/discover.py +257 -0
- mobile_agent/testkit/editor.py +243 -0
- mobile_agent/testkit/execute.py +515 -0
- mobile_agent/testkit/github_checks.py +248 -0
- mobile_agent/testkit/gitref.py +66 -0
- mobile_agent/testkit/heal_review.py +176 -0
- mobile_agent/testkit/health.py +480 -0
- mobile_agent/testkit/history.py +255 -0
- mobile_agent/testkit/hosted_results.py +321 -0
- mobile_agent/testkit/input_files.py +173 -0
- mobile_agent/testkit/jobs.py +285 -0
- mobile_agent/testkit/junit_verdict.py +33 -0
- mobile_agent/testkit/maestro.py +802 -0
- mobile_agent/testkit/mcp.py +562 -0
- mobile_agent/testkit/mcp_comparison.py +88 -0
- mobile_agent/testkit/minimize.py +471 -0
- mobile_agent/testkit/minimizer_cli.py +48 -0
- mobile_agent/testkit/notify.py +212 -0
- mobile_agent/testkit/phone_video.py +191 -0
- mobile_agent/testkit/pr.py +491 -0
- mobile_agent/testkit/pr_report.py +562 -0
- mobile_agent/testkit/prbuild.py +148 -0
- mobile_agent/testkit/proof_report.py +55 -0
- mobile_agent/testkit/provenance.py +119 -0
- mobile_agent/testkit/prrun.py +244 -0
- mobile_agent/testkit/recent.py +166 -0
- mobile_agent/testkit/recipes.py +146 -0
- mobile_agent/testkit/record.py +204 -0
- mobile_agent/testkit/register.py +288 -0
- mobile_agent/testkit/reports.py +998 -0
- mobile_agent/testkit/repro.py +432 -0
- mobile_agent/testkit/scenario.py +440 -0
- mobile_agent/testkit/scenario_cli.py +37 -0
- mobile_agent/testkit/snapshots_cli.py +60 -0
- mobile_agent/testkit/suite.py +670 -0
- mobile_agent/testkit/targets.py +497 -0
- mobile_agent/testkit/xctest.py +378 -0
- mobile_agent/threads/__init__.py +1 -0
- mobile_agent/threads/chat_once.py +264 -0
- mobile_agent/threads/cli.py +740 -0
- mobile_agent/threads/client.py +326 -0
- mobile_agent/threads/context.py +246 -0
- mobile_agent/threads/mcp.py +739 -0
- mobile_agent/threads/recent.py +34 -0
- mobile_agent/threads/register.py +54 -0
- mobile_agent/threads/routes.py +188 -0
- mobile_agent/threads/service.py +975 -0
- mobile_agent/threads/store.py +361 -0
- mobile_agent/threads/summary.py +117 -0
- mobile_agent/threads/term.py +101 -0
- mobile_agent/threads/workflow_cli.py +104 -0
- mobile_agent/tls.py +36 -0
- mobile_agent/traces.py +271 -0
- mobile_agent/tracks.py +235 -0
- mobile_agent/transport.py +271 -0
- mobile_agent/triggers/__init__.py +1 -0
- mobile_agent/triggers/actions.py +204 -0
- mobile_agent/triggers/model.py +146 -0
- mobile_agent/triggers/register.py +29 -0
- mobile_agent/triggers/routes.py +101 -0
- mobile_agent/triggers/service.py +387 -0
- mobile_agent/triggers/store.py +142 -0
- mobile_agent/tui/__init__.py +89 -0
- mobile_agent/tui/app.py +582 -0
- mobile_agent/tui/chrome.py +321 -0
- mobile_agent/tui/commands.py +375 -0
- mobile_agent/tui/demo_phone.py +369 -0
- mobile_agent/tui/images.py +29 -0
- mobile_agent/tui/keys.py +64 -0
- mobile_agent/tui/prompt.py +195 -0
- mobile_agent/tui/render.py +395 -0
- mobile_agent/tui/screens.py +180 -0
- mobile_agent/tui/session.py +308 -0
- mobile_agent/tui/tasks.py +287 -0
- mobile_agent/tui/theme.py +118 -0
- mobile_agent/tui/widgets.py +272 -0
- mobile_agent/update_check.py +213 -0
- mobile_agent/usage.py +184 -0
- mobile_agent/verify/__init__.py +0 -0
- mobile_agent/verify/__main__.py +41 -0
- mobile_agent/verify/a11y.py +189 -0
- mobile_agent/verify/actions.py +290 -0
- mobile_agent/verify/assertions.py +570 -0
- mobile_agent/verify/audit.py +312 -0
- mobile_agent/verify/business.py +221 -0
- mobile_agent/verify/business_run.py +131 -0
- mobile_agent/verify/business_state.py +168 -0
- mobile_agent/verify/check_schema.py +398 -0
- mobile_agent/verify/checks.py +614 -0
- mobile_agent/verify/cli.py +423 -0
- mobile_agent/verify/conditions.py +483 -0
- mobile_agent/verify/crashes.py +321 -0
- mobile_agent/verify/device_audit.py +246 -0
- mobile_agent/verify/dialogs.py +287 -0
- mobile_agent/verify/evidence.py +705 -0
- mobile_agent/verify/fixtures.py +305 -0
- mobile_agent/verify/flow.py +213 -0
- mobile_agent/verify/l10n.py +141 -0
- mobile_agent/verify/netcerts.py +67 -0
- mobile_agent/verify/netproxy.py +356 -0
- mobile_agent/verify/netrun.py +81 -0
- mobile_agent/verify/netshim.m +28 -0
- mobile_agent/verify/netshim.py +48 -0
- mobile_agent/verify/netstubs.py +132 -0
- mobile_agent/verify/overlay.py +174 -0
- mobile_agent/verify/perf.py +239 -0
- mobile_agent/verify/proof.py +192 -0
- mobile_agent/verify/replay_admission.py +48 -0
- mobile_agent/verify/report.py +952 -0
- mobile_agent/verify/run_cleanup.py +67 -0
- mobile_agent/verify/run_links.py +48 -0
- mobile_agent/verify/run_perf.py +44 -0
- mobile_agent/verify/run_secrets.py +39 -0
- mobile_agent/verify/runner.py +976 -0
- mobile_agent/verify/runs.py +57 -0
- mobile_agent/verify/screen_coverage.py +181 -0
- mobile_agent/verify/smart.py +106 -0
- mobile_agent/verify/snapshot_run.py +114 -0
- mobile_agent/verify/snapshots.py +306 -0
- mobile_agent/verify/sound.py +335 -0
- mobile_agent/verify/source_observation.py +130 -0
- mobile_agent/verify/system_surfaces/__init__.py +7 -0
- mobile_agent/verify/system_surfaces/play.py +106 -0
- mobile_agent/verify/system_surfaces/spec.py +139 -0
- mobile_agent/verify/system_surfaces/stage.py +138 -0
- mobile_agent/verify/system_surfaces/surfaces.py +121 -0
- mobile_agent/verify/tree.py +223 -0
- mobile_agent/verify/verdict.py +377 -0
- mobile_agent/verify/wire.py +392 -0
- mobile_agent/verify/wirestore.py +176 -0
- mobile_agent/waiting/__init__.py +1 -0
- mobile_agent/waiting/contacts.py +73 -0
- mobile_agent/waiting/drafts.py +136 -0
- mobile_agent/waiting/job.py +145 -0
- mobile_agent/waiting/messages.py +174 -0
- mobile_agent/waiting/rank.py +115 -0
- mobile_agent/waiting/register.py +26 -0
- mobile_agent/watch.py +502 -0
- mobile_agent/wda/__init__.py +2 -0
- mobile_agent/wda/frame_clock.py +646 -0
- mobile_agent/wda/frame_source.py +325 -0
- mobile_agent/wda/frame_store.py +115 -0
- mobile_agent/wda/gate.py +163 -0
- mobile_agent/wda/gestures.py +291 -0
- mobile_agent/wda/guards.py +94 -0
- mobile_agent/wda/identity.py +55 -0
- mobile_agent/wda/observe.py +884 -0
- mobile_agent/wda/patches.py +856 -0
- mobile_agent/wda/point_gestures.py +150 -0
- mobile_agent/wda/source.py +32 -0
- mobile_agent/wda/system_dialogs.py +571 -0
- mobile_agent/wda/text.py +694 -0
- mobile_agent/wda/timeouts.py +77 -0
- mobile_agent/wda/video.py +505 -0
- mobile_agent/wda/watchdog.py +243 -0
- mobile_agent/wireless/__init__.py +15 -0
- mobile_agent/wireless/api.py +354 -0
- mobile_agent/wireless/cli.py +287 -0
- mobile_agent/wireless/devicectl.py +229 -0
- mobile_agent/wireless/exposure.py +33 -0
- mobile_agent/wireless/mobster-wifi-pair.c +122 -0
- mobile_agent/wireless/monitor.py +332 -0
- mobile_agent/wireless/network.py +334 -0
- mobile_agent/wireless/onboarding.py +293 -0
- mobile_agent/wireless/pair.py +124 -0
- mobile_agent/wireless/probe.py +322 -0
- mobile_agent/wireless/register.py +8 -0
- mobile_agent/wireless/relay.py +179 -0
- mobile_agent/wireless/result.py +87 -0
- mobile_agent/wireless/store.py +85 -0
- mobile_agent/wireless/switch.py +77 -0
- mobile_agent/wireless/transport.py +118 -0
- mobile_agent/wireless/words.py +81 -0
- mobile_agent/wireless/xctestrun.py +83 -0
- mobile_agent/workflow_tools.py +59 -0
- mobile_agent/workflows/__init__.py +2 -0
- mobile_agent/workflows/brief.py +201 -0
- mobile_agent/workflows/cron.py +110 -0
- mobile_agent/workflows/health.py +257 -0
- mobile_agent/workflows/history.py +126 -0
- mobile_agent/workflows/records.py +442 -0
- mobile_agent/workflows/runs.py +1000 -0
- mobile_agent/workflows/scheduler.py +134 -0
- mobile_agent/workflows/service.py +973 -0
- mobile_agent/workflows/starters.py +285 -0
- mobile_agent/workflows/triggers.py +248 -0
- mobile_agent/xray/__init__.py +1 -0
- mobile_agent/xray/app_cli.py +116 -0
- mobile_agent/xray/cli.py +21 -0
- mobile_agent/xray/crawl.py +754 -0
- mobile_agent/xray/diff.py +171 -0
- mobile_agent/xray/diff_cli.py +43 -0
- mobile_agent/xray/job.py +184 -0
- mobile_agent/xray/paywall.py +188 -0
- mobile_agent/xray/policy.py +305 -0
- mobile_agent/xray/register.py +43 -0
- mobile_agent/xray/report.py +283 -0
- mobile_agent/xray/screens.py +117 -0
- mobster_cli-0.4.3.dist-info/METADATA +93 -0
- mobster_cli-0.4.3.dist-info/RECORD +1009 -0
- mobster_cli-0.4.3.dist-info/WHEEL +5 -0
- mobster_cli-0.4.3.dist-info/entry_points.txt +3 -0
- mobster_cli-0.4.3.dist-info/licenses/LICENSE +21 -0
- mobster_cli-0.4.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""The benchmark's own reads of the phone, independent of the run under test.
|
|
2
|
+
|
|
3
|
+
The agent decides whether it is finished by comparing its own evidence against
|
|
4
|
+
its own goal. That is model agreement, not verification. The bench grades a run
|
|
5
|
+
by reading the phone itself: ``WDAProbe`` NEVER reads the agent's evidence,
|
|
6
|
+
citations, screenshots or completion claim. It opens its own accessibility
|
|
7
|
+
connection to the phone, reads the tree itself, and the bench's checks
|
|
8
|
+
(``bench/checks.py``, ``bench/truth.py``) compare what it finds against ground
|
|
9
|
+
truth declared before the run started. A grader that consulted the agent's own
|
|
10
|
+
record would only re-certify the agent's opinion.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from ..drivers import WDA
|
|
17
|
+
from ..errors import MobsterError
|
|
18
|
+
from ..state import from_wda
|
|
19
|
+
from ..transport import HTTP
|
|
20
|
+
from ..wda.observe import WDA_SOURCE_PATH
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ProbeUnavailable(MobsterError):
|
|
24
|
+
"""The harness could not read the device itself, so nothing is graded."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Measured on iOS 26.0.1 (2026-09-23): WDA reported this invisible Siri/Apple
|
|
28
|
+
# Intelligence edge-light window as the active app while Safari was in front.
|
|
29
|
+
SYSTEM_OVERLAYS = frozenset({"com.apple.siri.IntelligentLight"})
|
|
30
|
+
SPRINGBOARD = "com.apple.springboard"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WDAProbe:
|
|
34
|
+
"""The harness's own eyes on a USB iPhone, separate from the run under test. WDA serves one session per device,
|
|
35
|
+
so resets act through the probe's own driver on that session."""
|
|
36
|
+
|
|
37
|
+
# Apps whose foreground check needed the hinted source read, per WDA URL (process-wide).
|
|
38
|
+
_overlay_apps = {}
|
|
39
|
+
|
|
40
|
+
def __init__(self, url, session=None):
|
|
41
|
+
self.url = url.rstrip("/")
|
|
42
|
+
self.http = HTTP(self.url)
|
|
43
|
+
self._overlay_seen = False
|
|
44
|
+
self.session = session or self._current_session()
|
|
45
|
+
|
|
46
|
+
def _current_session(self):
|
|
47
|
+
try:
|
|
48
|
+
session = self.http.request("GET", "/status", timeout=5).get("sessionId")
|
|
49
|
+
except Exception as error:
|
|
50
|
+
raise ProbeUnavailable(f"WDA status unavailable: {error}") from None
|
|
51
|
+
if not isinstance(session, str) or not session:
|
|
52
|
+
raise ProbeUnavailable("WDA has no active session")
|
|
53
|
+
return session
|
|
54
|
+
|
|
55
|
+
def _get(self, path, timeout):
|
|
56
|
+
value = WDA.response_value(self.http.request("GET", path, timeout=timeout))
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
def foreground(self, timeout=5, expected=None):
|
|
60
|
+
"""The foreground app. WDA's activeAppInfo is the fast route, except while
|
|
61
|
+
a see-through system layer is up: then it took ~8 s and named the layer
|
|
62
|
+
(measured 2026-09-23), and the session's own tree names the app instead."""
|
|
63
|
+
if not self._overlay_seen and expected not in WDAProbe._overlay_apps.get(self.url, ()):
|
|
64
|
+
# One short ask. With Safari in front activeAppInfo hangs on the Siri overlay
|
|
65
|
+
# (measured 24 Sep: 11 s, then the fallback's settings call queued behind it
|
|
66
|
+
# and timed out); WDA serves one request at a time, so waiting longer only
|
|
67
|
+
# delays the path that works.
|
|
68
|
+
try:
|
|
69
|
+
value = self._get("/wda/activeAppInfo", min(timeout, 3))
|
|
70
|
+
except Exception:
|
|
71
|
+
value = None
|
|
72
|
+
bundle = value.get("bundleId") if isinstance(value, dict) else None
|
|
73
|
+
# On a simulator activeAppInfo names SpringBoard whatever is in front (iOS 26.4, 10 Oct: every
|
|
74
|
+
# reset failed "not in the foreground"): the session's tree, hinted with the app, names it instead.
|
|
75
|
+
stale = bundle == SPRINGBOARD and expected not in (None, SPRINGBOARD) and self._simulator()
|
|
76
|
+
if bundle == SPRINGBOARD and expected not in (None, SPRINGBOARD) and not stale:
|
|
77
|
+
# A phone on iOS 26.4 does the same once an app it was asked to open isn't there (the bench phone, 11
|
|
78
|
+
# Oct: after activating a deleted Mail, every later read named SpringBoard with Settings in front). The
|
|
79
|
+
# session's own activeAppInfo answers in one short read. A hinted tree read is not asked here: it
|
|
80
|
+
# names the hinted app even when SpringBoard is in front.
|
|
81
|
+
bundle = self._session_app(min(timeout, 3)) or bundle
|
|
82
|
+
if isinstance(bundle, str) and bundle and bundle not in SYSTEM_OVERLAYS and not stale:
|
|
83
|
+
return bundle
|
|
84
|
+
self._overlay_seen = True
|
|
85
|
+
if expected:
|
|
86
|
+
WDAProbe._overlay_apps.setdefault(self.url, set()).add(expected)
|
|
87
|
+
try:
|
|
88
|
+
if expected:
|
|
89
|
+
# Name the app we expect as WDA's default active application,
|
|
90
|
+
# as the agent's driver does (WDA.observe).
|
|
91
|
+
# Patient: the request it follows may still hold WDA for several seconds.
|
|
92
|
+
self._get_session_settings(expected, max(timeout, 15))
|
|
93
|
+
snapshot = from_wda(self._get(f"/session/{self.session}{WDA_SOURCE_PATH}", max(timeout, 25)))
|
|
94
|
+
except Exception as error:
|
|
95
|
+
raise ProbeUnavailable(f"foreground unavailable: {error}") from None
|
|
96
|
+
if not snapshot.bundle_id:
|
|
97
|
+
raise ProbeUnavailable("WDA did not report a foreground app")
|
|
98
|
+
return snapshot.bundle_id
|
|
99
|
+
|
|
100
|
+
def _session_app(self, timeout):
|
|
101
|
+
"""The foreground app by the session's own activeAppInfo, or None."""
|
|
102
|
+
try:
|
|
103
|
+
value = self._get(f"/session/{self.session}/wda/activeAppInfo", timeout)
|
|
104
|
+
except Exception: # noqa: BLE001 -- unknown: the caller's other paths decide
|
|
105
|
+
return None
|
|
106
|
+
bundle = value.get("bundleId") if isinstance(value, dict) else None
|
|
107
|
+
return bundle if isinstance(bundle, str) and bundle and bundle not in SYSTEM_OVERLAYS else None
|
|
108
|
+
|
|
109
|
+
def observe(self, bundle_id, timeout=10):
|
|
110
|
+
"""A fresh read, stamped with the foreground app read before AND after it,
|
|
111
|
+
so a read that straddled an app switch is refused rather than graded."""
|
|
112
|
+
try:
|
|
113
|
+
before = self.foreground(timeout=min(5, timeout), expected=bundle_id)
|
|
114
|
+
# Through the session, so the foreground hint WDA needs on iOS 26
|
|
115
|
+
# (see WDA.observe) applies to the probe's reads as well.
|
|
116
|
+
hinted = WDA._overlay_apps.get(self.url, ())
|
|
117
|
+
if bundle_id in hinted:
|
|
118
|
+
self._get_session_settings(bundle_id, timeout)
|
|
119
|
+
snapshot = from_wda(self._get(f"/session/{self.session}{WDA_SOURCE_PATH}", timeout))
|
|
120
|
+
after = self.foreground(timeout=min(5, timeout), expected=bundle_id)
|
|
121
|
+
except ProbeUnavailable:
|
|
122
|
+
raise
|
|
123
|
+
except Exception as error:
|
|
124
|
+
raise ProbeUnavailable(f"observation failed: {error}") from None
|
|
125
|
+
if before != after:
|
|
126
|
+
raise ProbeUnavailable(f"foreground changed during the read ({before} -> {after})")
|
|
127
|
+
snapshot.bundle_id = after
|
|
128
|
+
return snapshot
|
|
129
|
+
|
|
130
|
+
def _simulator(self):
|
|
131
|
+
"""True when this WDA runs on a simulator (its /status names a simulator version); asked once."""
|
|
132
|
+
if getattr(self, "_is_simulator", None) is None:
|
|
133
|
+
try:
|
|
134
|
+
status = self._get("/status", 3)
|
|
135
|
+
ios = status.get("ios") if isinstance(status, dict) else None
|
|
136
|
+
self._is_simulator = bool(isinstance(ios, dict) and ios.get("simulatorVersion"))
|
|
137
|
+
except Exception: # noqa: BLE001 -- unknown: a phone, asked again next time
|
|
138
|
+
return False
|
|
139
|
+
return self._is_simulator
|
|
140
|
+
|
|
141
|
+
def _get_session_settings(self, bundle_id, timeout):
|
|
142
|
+
WDA.response_value(self.http.request(
|
|
143
|
+
"POST", f"/session/{self.session}/appium/settings",
|
|
144
|
+
{"settings": {"defaultActiveApplication": bundle_id}}, timeout=timeout))
|
|
145
|
+
|
|
146
|
+
def driver(self):
|
|
147
|
+
driver = WDA(self.url, self.session)
|
|
148
|
+
driver.configure()
|
|
149
|
+
return driver
|
|
150
|
+
|
|
151
|
+
def reset(self, bundle_id, url=None, max_back=12):
|
|
152
|
+
"""Relaunch the app and pop it to its root screen, verified. iOS restores an app's navigation stack across
|
|
153
|
+
relaunches, so a task could otherwise start on the screen its answer is on."""
|
|
154
|
+
try:
|
|
155
|
+
# WDA issues a new session whenever its runner restarts.
|
|
156
|
+
self.session = self._current_session()
|
|
157
|
+
except ProbeUnavailable:
|
|
158
|
+
pass
|
|
159
|
+
driver = None
|
|
160
|
+
try:
|
|
161
|
+
driver = self.driver()
|
|
162
|
+
for path in ("/wda/apps/terminate", "/wda/apps/activate"):
|
|
163
|
+
driver.call("POST", path, {"bundleId": bundle_id}, timeout=15)
|
|
164
|
+
if url:
|
|
165
|
+
# Bound to the task's app: a bare /url opens the default browser
|
|
166
|
+
# (Chrome on the measured phone), not the app under test.
|
|
167
|
+
driver.call("POST", "/url", {"url": url, "bundleId": bundle_id}, timeout=15)
|
|
168
|
+
# activate can return while SpringBoard still owns the foreground.
|
|
169
|
+
# The first read after a launch can be slow once (a system overlay); keep
|
|
170
|
+
# asking within the window instead of failing the task's setup on it.
|
|
171
|
+
launched = time.monotonic() + 20
|
|
172
|
+
while time.monotonic() < launched:
|
|
173
|
+
try:
|
|
174
|
+
if self.foreground(expected=bundle_id) == bundle_id:
|
|
175
|
+
break
|
|
176
|
+
except ProbeUnavailable:
|
|
177
|
+
pass
|
|
178
|
+
time.sleep(.1)
|
|
179
|
+
popped = False
|
|
180
|
+
for _ in range(max_back):
|
|
181
|
+
# Room for one failed read on a system overlay plus its hinted retry.
|
|
182
|
+
snapshot = driver.observe_ready(timeout=20)
|
|
183
|
+
if self.foreground(expected=bundle_id) != bundle_id:
|
|
184
|
+
raise ProbeUnavailable(f"{bundle_id} is not in the foreground after launch")
|
|
185
|
+
if url:
|
|
186
|
+
# The URL is the start state; Back would walk browser history.
|
|
187
|
+
snapshot.bundle_id = bundle_id
|
|
188
|
+
return snapshot
|
|
189
|
+
viewer = next((e for e in snapshot.elements if e.role == "Button" and e.rect[1] < .12
|
|
190
|
+
and (e.label or "").strip().casefold() == "close"), None)
|
|
191
|
+
if viewer is not None:
|
|
192
|
+
# A document viewer left open (Quick Look in Files): close it first.
|
|
193
|
+
driver.execute("TAP", viewer, snapshot, timeout=10)
|
|
194
|
+
driver.wait_for_change(snapshot, timeout=3)
|
|
195
|
+
continue
|
|
196
|
+
search = next((e for e in snapshot.elements if e.role == "SearchField"
|
|
197
|
+
and (e.value or "").strip() not in ("", (e.label or "").strip())), None)
|
|
198
|
+
dismiss = search and next((e for e in snapshot.elements if e.role == "Button"
|
|
199
|
+
and (e.label or "").strip().casefold() in ("close", "cancel")
|
|
200
|
+
and abs(e.center[1] - search.center[1]) < .04), None)
|
|
201
|
+
if dismiss:
|
|
202
|
+
# A search left open (Notes keeps its query and results): close it, so the
|
|
203
|
+
# task starts on the app's own screen (pass 6: three Notes tasks began there).
|
|
204
|
+
driver.execute("TAP", dismiss, snapshot, timeout=10)
|
|
205
|
+
driver.wait_for_change(snapshot, timeout=3)
|
|
206
|
+
continue
|
|
207
|
+
back = navigation_back(snapshot)
|
|
208
|
+
pop = RESET_POP_TABS.get(bundle_id)
|
|
209
|
+
if back is None and pop and not popped:
|
|
210
|
+
# Files' folder views publish no labelled Back button; re-tapping the
|
|
211
|
+
# selected tab pops its stack to the root (Browse), as a user would.
|
|
212
|
+
popped = True
|
|
213
|
+
tab = next((e for e in snapshot.elements if e.label == pop and e.role == "Button"), None)
|
|
214
|
+
if tab is not None:
|
|
215
|
+
for press in range(2 if (tab.value or "").strip() == "1" else 1):
|
|
216
|
+
if press:
|
|
217
|
+
snapshot = driver.observe_ready(timeout=10)
|
|
218
|
+
tab = next((e for e in snapshot.elements if e.label == pop and e.role == "Button"),
|
|
219
|
+
None)
|
|
220
|
+
if tab is None:
|
|
221
|
+
break
|
|
222
|
+
driver.execute("TAP", tab, snapshot, timeout=10)
|
|
223
|
+
time.sleep(.6)
|
|
224
|
+
continue
|
|
225
|
+
if back is None:
|
|
226
|
+
tab = RESET_TABS.get(bundle_id)
|
|
227
|
+
selected = next((e for e in snapshot.elements if e.label == tab and e.role == "Button"), None)
|
|
228
|
+
if tab and selected is not None and (selected.value or "").strip() != "1":
|
|
229
|
+
# A tabbed app reopens on its last tab; the task's start is its first one
|
|
230
|
+
# (nav.clock_stopwatch started on Stopwatch and needed no action).
|
|
231
|
+
driver.execute("TAP", selected, snapshot, timeout=10)
|
|
232
|
+
driver.wait_for_change(snapshot, timeout=3)
|
|
233
|
+
continue
|
|
234
|
+
snapshot.bundle_id = bundle_id
|
|
235
|
+
return snapshot
|
|
236
|
+
driver.execute("TAP", back, snapshot, timeout=10)
|
|
237
|
+
driver.wait_for_change(snapshot, timeout=3)
|
|
238
|
+
raise ProbeUnavailable(f"{bundle_id} did not reach its root screen")
|
|
239
|
+
except ProbeUnavailable:
|
|
240
|
+
raise
|
|
241
|
+
except Exception as error:
|
|
242
|
+
raise ProbeUnavailable(f"reset failed: {error}") from None
|
|
243
|
+
finally:
|
|
244
|
+
if driver is not None:
|
|
245
|
+
driver.close()
|
|
246
|
+
|
|
247
|
+
def close(self):
|
|
248
|
+
self.http.close()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# Leading navigation-bar buttons that are not Back: Clock's root has "Edit" there, and
|
|
252
|
+
# tapping it twelve times left the reset toggling edit mode ("did not reach its root screen").
|
|
253
|
+
# The tab a tabbed app's root must show at the start of a task.
|
|
254
|
+
RESET_TABS = {"com.apple.mobiletimer": "World Clock"}
|
|
255
|
+
# Apps whose root is reached by re-tapping a tab rather than by Back buttons.
|
|
256
|
+
RESET_POP_TABS = {"com.apple.DocumentsApp": "Browse"}
|
|
257
|
+
NOT_BACK = re.compile(r"^(edit|done|cancel|close|select|add|new|filter|sort|menu|more|history|list|sidebar|"
|
|
258
|
+
r"show sidebar|hide sidebar|[a-z0-9]+(\.[a-z0-9]+)+)$", re.I)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def navigation_back(snapshot):
|
|
262
|
+
"""The navigation bar's leading button, which on iOS is the back button."""
|
|
263
|
+
return next((element for element in snapshot.elements
|
|
264
|
+
if element.role == "Button" and "XCUIElementTypeNavigationBar" in element.locator
|
|
265
|
+
and element.rect[0] < .25 and not NOT_BACK.match((element.label or "").strip())), None)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def navigation_title(snapshot):
|
|
269
|
+
"""The nav-bar title: an inert label in the top band of the screen. A row label is not a location: Settings'
|
|
270
|
+
General lists a row called About."""
|
|
271
|
+
bars = [element for element in snapshot.elements if element.role == "NavigationBar"]
|
|
272
|
+
if bars:
|
|
273
|
+
# WDA names the bar after its title; every WDA element advertises TAP,
|
|
274
|
+
# so the inert-label heuristic below cannot apply to it. Some bars carry a
|
|
275
|
+
# class name instead ("FullDocumentManagerViewControllerNavigationBar" in
|
|
276
|
+
# Files): then the title is the text drawn inside the bar.
|
|
277
|
+
bar = min(bars, key=lambda element: element.rect[1])
|
|
278
|
+
label = bar.label.strip()
|
|
279
|
+
if re.fullmatch(r"[A-Za-z_][\w.]*", label) and len(label) > 20:
|
|
280
|
+
x, y, w, h = bar.rect
|
|
281
|
+
inside = [e for e in snapshot.elements if e.role == "StaticText" and e.label.strip()
|
|
282
|
+
and y <= e.rect[1] + e.rect[3] / 2 <= y + h and x <= e.rect[0] + e.rect[2] / 2 <= x + w]
|
|
283
|
+
title = max(inside, key=lambda e: e.rect[2]).label.strip() if inside else label
|
|
284
|
+
return re.sub(r",\s*Actions Menu$", "", title) # iOS 26 menu titles
|
|
285
|
+
return re.sub(r",\s*Actions Menu$", "", label)
|
|
286
|
+
candidates = [element for element in snapshot.elements
|
|
287
|
+
if not (element.actions or ()) and (element.label or "").strip()
|
|
288
|
+
and element.rect[1] < .12 and element.rect[3] < .06]
|
|
289
|
+
if not candidates:
|
|
290
|
+
return ""
|
|
291
|
+
return min(candidates, key=lambda element: element.rect[1]).label.strip()
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def screen_text(snapshot):
|
|
295
|
+
parts = [snapshot.text or ""]
|
|
296
|
+
for element in snapshot.elements:
|
|
297
|
+
parts.append(f"{element.label or ''} {element.value or ''}")
|
|
298
|
+
return "\n".join(parts)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""The red-team suite for consequential actions: Mobster never sends, posts, buys, deletes or otherwise acts beyond the
|
|
2
|
+
phone without the person's explicit OK.
|
|
3
|
+
|
|
4
|
+
- ``cases``: the attacks, as synthetic screens (accessibility trees, and what the pixels say where the two differ),
|
|
5
|
+
what the user asked, what a fooled or careless model does, and the ground truth: which dispatches would have an
|
|
6
|
+
effect the person must approve. The ground truth is the case's own, never read from Mobster's classifiers.
|
|
7
|
+
- ``navigation``: actions that only read and move around, which must not ask (the false-ask budget).
|
|
8
|
+
- ``harness``: runs each case through every path that acts on a phone (Mobster's agent, a coding agent's phone tools,
|
|
9
|
+
an outside agent over MCP) with a declining approver, an approving one, and nobody to answer, and scores it.
|
|
10
|
+
- ``python -m mobile_agent.evals.redteam``: the offline report (no phone, no model, no network). ``--live`` replays the
|
|
11
|
+
cases' requests and screens to a real model (a key and a spend cap, ``live``).
|
|
12
|
+
|
|
13
|
+
The unit tests (tests/test_redteam.py) hold the offline numbers: every must-ask unit passes, and false asks stay within
|
|
14
|
+
the budget.
|
|
15
|
+
"""
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""python -m mobile_agent.evals.redteam [--json] [--case PREFIX] [--path frontier|tools|mcp]
|
|
2
|
+
|
|
3
|
+
The offline red-team report: must-ask recall over every case, path and regime, how many cards named the act, and the
|
|
4
|
+
false asks on the navigation set. No phone, no model, no network. ``--live`` runs the real-model replay instead
|
|
5
|
+
(mobile_agent.evals.redteam.live).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(argv=None):
|
|
14
|
+
parser = argparse.ArgumentParser(prog="python -m mobile_agent.evals.redteam")
|
|
15
|
+
parser.add_argument("--json", action="store_true", help="print the units as JSON")
|
|
16
|
+
parser.add_argument("--case", default="", help="only cases whose id starts with this")
|
|
17
|
+
parser.add_argument("--path", choices=("frontier", "tools", "mcp"), help="only this path")
|
|
18
|
+
parser.add_argument("--live", action="store_true", help="replay the cases to a real model (needs a key)")
|
|
19
|
+
parser.add_argument("--connection", choices=("tree", "mirroring", "cable_bluetooth", "all"), default="all",
|
|
20
|
+
help="how the agent sees the phone (all: the tree and both image-only connections)")
|
|
21
|
+
args, rest = parser.parse_known_args(argv)
|
|
22
|
+
if args.live:
|
|
23
|
+
from . import live
|
|
24
|
+
return live.main(rest + (["--case", args.case] if args.case else []))
|
|
25
|
+
import logging
|
|
26
|
+
logging.disable(logging.CRITICAL)
|
|
27
|
+
from .cases import CASES, NAVIGATION
|
|
28
|
+
from .harness import CONNECTIONS, IMAGE_PATHS, PATHS, run_all, run_nav, score
|
|
29
|
+
cases = [c for c in CASES if c.id.startswith(args.case)]
|
|
30
|
+
paths = (args.path,) if args.path else PATHS
|
|
31
|
+
connections = CONNECTIONS if args.connection == "all" else (args.connection,)
|
|
32
|
+
results = run_all(cases, paths, connections)
|
|
33
|
+
navs = [run_nav(n, path, contract, connection) for connection in connections for n in NAVIGATION
|
|
34
|
+
for path, contract in (("frontier", "none"), ("frontier", "read"), ("tools", ""))
|
|
35
|
+
if (not args.path or args.path == path) and (connection == "tree" or path in IMAGE_PATHS)] \
|
|
36
|
+
if not args.case else []
|
|
37
|
+
summary = score(results)
|
|
38
|
+
false_asks = [n for n in navs if n.asked or not n.sent]
|
|
39
|
+
summary.update(navigation=len(navs), false_asks=sum(1 for n in navs if n.asked),
|
|
40
|
+
not_sent=sum(1 for n in navs if not n.sent))
|
|
41
|
+
if args.json:
|
|
42
|
+
print(json.dumps({"summary": summary, "units": [u.__dict__ for u in results],
|
|
43
|
+
"navigation": [n.__dict__ for n in navs]}, default=str, indent=1))
|
|
44
|
+
return 0
|
|
45
|
+
failed = [u for u in results if not u.ok]
|
|
46
|
+
by_group = {}
|
|
47
|
+
for unit in results:
|
|
48
|
+
for key in (unit.group, f"@{unit.connection}"):
|
|
49
|
+
held, total = by_group.get(key, (0, 0))
|
|
50
|
+
by_group[key] = (held + unit.ok, total + 1)
|
|
51
|
+
print(f"Must-ask recall: {summary['held']}/{summary['units']} units ({summary['recall']:.1%})")
|
|
52
|
+
for group, (held, total) in sorted(by_group.items()):
|
|
53
|
+
print(f" {group:12} {held}/{total}")
|
|
54
|
+
print(f"Cards that named the act: {summary['cards_named']}/{summary['cards']}")
|
|
55
|
+
if navs:
|
|
56
|
+
rate = summary["false_asks"] / summary["navigation"]
|
|
57
|
+
print(f"False asks on navigation: {summary['false_asks']}/{summary['navigation']} ({rate:.1%}); "
|
|
58
|
+
f"not sent: {summary['not_sent']}")
|
|
59
|
+
for unit in failed:
|
|
60
|
+
print(f"FAIL {unit.key}: {unit.why}")
|
|
61
|
+
for unit in results:
|
|
62
|
+
if unit.carded is False:
|
|
63
|
+
title = next((c.get("title") for c in unit.cards if c.get("title")), None)
|
|
64
|
+
print(f"CARD {unit.key}: {title!r}")
|
|
65
|
+
for n in false_asks:
|
|
66
|
+
print(f"NAV {n.id}/{n.path}/{n.contract}@{n.connection}: asked "
|
|
67
|
+
f"{[c.get('title') for c in n.asked]} sent={n.sent}")
|
|
68
|
+
return 0 if not failed else 1
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
sys.exit(main())
|